Add authentication #17
5 changed files with 340 additions and 122 deletions
|
@ -299,10 +299,32 @@ export class StoreAuth {
|
||||||
? new Date(user.last_login).toLocaleString()
|
? new Date(user.last_login).toLocaleString()
|
||||||
: this.config.ui.messages.auth.never;
|
: this.config.ui.messages.auth.never;
|
||||||
|
|
||||||
// Show/hide view toggles
|
// Show/hide view toggles and update view visibility
|
||||||
officerViewToggle.style.display = isOfficer ? "block" : "none";
|
officerViewToggle.style.display = isOfficer ? "block" : "none";
|
||||||
sponsorViewToggle.style.display = isSponsor ? "block" : "none";
|
sponsorViewToggle.style.display = isSponsor ? "block" : "none";
|
||||||
|
|
||||||
|
// If not an officer, ensure default view is shown and officer view is hidden
|
||||||
|
if (!isOfficer) {
|
||||||
|
const defaultView = document.getElementById("defaultView");
|
||||||
|
const officerView = document.getElementById("officerView");
|
||||||
|
const mainTabs = document.querySelector(".tabs.tabs-boxed");
|
||||||
|
const officerContent = document.getElementById("officerContent");
|
||||||
|
|
||||||
|
if (defaultView && officerView && mainTabs && officerContent) {
|
||||||
|
// Show default view and its tabs
|
||||||
|
defaultView.classList.remove("hidden");
|
||||||
|
mainTabs.classList.remove("hidden");
|
||||||
|
// Hide officer view
|
||||||
|
officerView.classList.add("hidden");
|
||||||
|
officerContent.classList.add("hidden");
|
||||||
|
// Also uncheck the toggle if it exists
|
||||||
|
const officerViewCheckbox = officerViewToggle.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
||||||
|
if (officerViewCheckbox) {
|
||||||
|
officerViewCheckbox.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// After everything is updated, show the content
|
// After everything is updated, show the content
|
||||||
loadingSkeleton.style.display = "none";
|
loadingSkeleton.style.display = "none";
|
||||||
userInfo.classList.remove("hidden");
|
userInfo.classList.remove("hidden");
|
||||||
|
|
|
@ -319,6 +319,46 @@
|
||||||
const auth = new StoreAuth();
|
const auth = new StoreAuth();
|
||||||
new EventCheckIn();
|
new EventCheckIn();
|
||||||
|
|
||||||
|
// Add officer view toggle handler
|
||||||
|
const officerViewToggle =
|
||||||
|
document.getElementById("officerViewToggle");
|
||||||
|
const officerViewCheckbox = officerViewToggle?.querySelector(
|
||||||
|
'input[type="checkbox"]'
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
|
if (officerViewCheckbox) {
|
||||||
|
officerViewCheckbox.addEventListener("change", () => {
|
||||||
|
const defaultView = document.getElementById("defaultView");
|
||||||
|
const officerView = document.getElementById("officerView");
|
||||||
|
const mainTabs = document.querySelector(".tabs.tabs-boxed");
|
||||||
|
const officerContent =
|
||||||
|
document.getElementById("officerContent");
|
||||||
|
|
||||||
|
if (
|
||||||
|
defaultView &&
|
||||||
|
officerView &&
|
||||||
|
mainTabs &&
|
||||||
|
officerContent
|
||||||
|
) {
|
||||||
|
if (officerViewCheckbox.checked) {
|
||||||
|
// Hide default view and its tabs
|
||||||
|
defaultView.classList.add("hidden");
|
||||||
|
mainTabs.classList.add("hidden");
|
||||||
|
// Show officer view
|
||||||
|
officerView.classList.remove("hidden");
|
||||||
|
officerContent.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
// Show default view and its tabs
|
||||||
|
defaultView.classList.remove("hidden");
|
||||||
|
mainTabs.classList.remove("hidden");
|
||||||
|
// Hide officer view
|
||||||
|
officerView.classList.add("hidden");
|
||||||
|
officerContent.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Add login button event listener
|
// Add login button event listener
|
||||||
const loginButtons = document.querySelectorAll(".login-button");
|
const loginButtons = document.querySelectorAll(".login-button");
|
||||||
loginButtons.forEach((button) => {
|
loginButtons.forEach((button) => {
|
||||||
|
|
|
@ -4,43 +4,43 @@ import EventManagement from "./EventManagement.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="officerContent" class="hidden">
|
<div id="officerContent" class="hidden">
|
||||||
<div class="tabs tabs-boxed mb-6">
|
<div class="tabs tabs-boxed mb-6" id="officerViewTabs">
|
||||||
<button class="tab tab-active" data-tab="members"
|
<button class="tab tab-active" data-officer-tab="members"
|
||||||
>Member Management</button
|
>Member Management</button
|
||||||
>
|
>
|
||||||
<button class="tab" data-tab="events">Event Management</button>
|
<button class="tab" data-officer-tab="events">Event Management</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-contents">
|
<div class="tab-contents">
|
||||||
<div id="membersTab" style="display: block;">
|
<div id="officerMembersTab" style="display: block;">
|
||||||
<MemberManagement />
|
<MemberManagement />
|
||||||
</div>
|
</div>
|
||||||
<div id="eventsTab" style="display: none;">
|
<div id="officerEventsTab" style="display: none;">
|
||||||
<EventManagement />
|
<EventManagement />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const tabs = document.querySelectorAll("[data-tab]");
|
const officerTabs = document.querySelectorAll("[data-officer-tab]");
|
||||||
const membersTab = document.getElementById("membersTab");
|
const officerMembersTab = document.getElementById("officerMembersTab");
|
||||||
const eventsTab = document.getElementById("eventsTab");
|
const officerEventsTab = document.getElementById("officerEventsTab");
|
||||||
|
|
||||||
if (tabs && membersTab && eventsTab) {
|
if (officerTabs && officerMembersTab && officerEventsTab) {
|
||||||
tabs.forEach((tab) => {
|
officerTabs.forEach((tab) => {
|
||||||
tab.addEventListener("click", () => {
|
tab.addEventListener("click", () => {
|
||||||
// Update tab styles
|
// Update tab styles
|
||||||
tabs.forEach((t) => t.classList.remove("tab-active"));
|
officerTabs.forEach((t) => t.classList.remove("tab-active"));
|
||||||
tab.classList.add("tab-active");
|
tab.classList.add("tab-active");
|
||||||
|
|
||||||
// Update content visibility
|
// Update content visibility
|
||||||
const tabId = (tab as HTMLElement).dataset.tab;
|
const tabId = (tab as HTMLElement).dataset.officerTab;
|
||||||
if (tabId === "members") {
|
if (tabId === "members") {
|
||||||
membersTab.style.display = "block";
|
officerMembersTab.style.display = "block";
|
||||||
eventsTab.style.display = "none";
|
officerEventsTab.style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
membersTab.style.display = "none";
|
officerMembersTab.style.display = "none";
|
||||||
eventsTab.style.display = "block";
|
officerEventsTab.style.display = "block";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,132 @@
|
||||||
<div class="space-y-6">
|
---
|
||||||
<div class="card bg-base-200 shadow-xl">
|
// Import the majors list
|
||||||
<div class="card-body">
|
import allMajors from "../../data/allUCSDMajors.txt?raw";
|
||||||
<h2 class="card-title text-2xl mb-4">Account Settings</h2>
|
const majorsList: string[] = allMajors
|
||||||
|
.split("\n")
|
||||||
|
.filter((major: string) => major.trim());
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body p-6">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex items-center gap-4 mb-8">
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="card-title text-2xl">Profile Settings</h2>
|
||||||
|
<p class="text-base-content/70 mt-1">
|
||||||
|
Manage your academic information and preferences
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
|
<button class="btn btn-primary" id="saveSettings">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5 mr-2"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings Sections -->
|
||||||
|
<div class="space-y-8">
|
||||||
|
<!-- Academic Information Section -->
|
||||||
|
<div class="bg-base-200 rounded-box p-6">
|
||||||
|
<div class="flex items-center gap-2 mb-6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.394 2.08a1 1 0 00-.788 0l-7 3a1 1 0 000 1.84L5.25 8.051a.999.999 0 01.356-.257l4-1.714a1 1 0 11.788 1.838L7.667 9.088l1.94.831a1 1 0 00.787 0l7-3a1 1 0 000-1.838l-7-3zM3.31 9.397L5 10.12v4.102a8.969 8.969 0 00-1.05-.174 1 1 0 01-.89-.89 11.115 11.115 0 01.25-3.762zM9.3 16.573A9.026 9.026 0 007 14.935v-3.957l1.818.78a3 3 0 002.364 0l5.508-2.361a11.026 11.026 0 01.25 3.762 1 1 0 01-.89.89 8.968 8.968 0 00-5.35 2.524 1 1 0 01-1.4 0zM6 18a1 1 0 001-1v-2.065a8.935 8.935 0 00-2-.712V17a1 1 0 001 1z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<h3 class="text-xl font-medium">Academic Information</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-6">
|
||||||
|
<!-- Major Selection -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium">Major</span>
|
||||||
|
<span class="label-text-alt text-base-content/70"
|
||||||
|
>Select your current major</span
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="majorSelect"
|
||||||
|
class="select select-bordered w-full"
|
||||||
|
>
|
||||||
|
<option value="">Select your major</option>
|
||||||
|
{
|
||||||
|
majorsList.map((major: string) => (
|
||||||
|
<option value={major}>{major}</option>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Graduation Year -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text font-medium"
|
||||||
|
>Expected Graduation</span
|
||||||
|
>
|
||||||
|
<span class="label-text-alt text-base-content/70"
|
||||||
|
>When do you plan to graduate?</span
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="gradYearSelect"
|
||||||
|
class="select select-bordered w-full"
|
||||||
|
>
|
||||||
|
<option value="">Select graduation year</option>
|
||||||
|
{
|
||||||
|
Array.from({ length: 6 }, (_, i) => {
|
||||||
|
const year = new Date().getFullYear() + i;
|
||||||
|
return <option value={year}>{year}</option>;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- IEEE Information Section -->
|
||||||
|
<div class="bg-base-200 rounded-box p-6">
|
||||||
|
<div class="flex items-center gap-2 mb-6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 2a1 1 0 00-1 1v1a1 1 0 002 0V3a1 1 0 00-1-1zM4 4h3a3 3 0 006 0h3a2 2 0 012 2v9a2 2 0 01-2 2H4a2 2 0 01-2-2V6a2 2 0 012-2zm2.5 7a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm2.45 4a2.5 2.5 0 10-4.9 0h4.9zM12 9a1 1 0 100 2h3a1 1 0 100-2h-3zm-1 4a1 1 0 011-1h2a1 1 0 110 2h-2a1 1 0 01-1-1z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<h3 class="text-xl font-medium">IEEE Membership</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-6">
|
||||||
<!-- IEEE Member ID -->
|
<!-- IEEE Member ID -->
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text">IEEE Member ID</span>
|
<span class="label-text font-medium"
|
||||||
|
>IEEE Member ID</span
|
||||||
|
>
|
||||||
|
<span class="label-text-alt text-base-content/70"
|
||||||
|
>Your IEEE membership number</span
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -16,50 +136,19 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Major -->
|
<!-- Resume Upload -->
|
||||||
<div class="form-control mt-4">
|
<div class="form-control">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text">Major</span>
|
<span class="label-text font-medium">Resume</span>
|
||||||
</label>
|
<span class="label-text-alt text-base-content/70"
|
||||||
<select id="majorSelect" class="select select-bordered w-full">
|
>Upload your latest resume</span
|
||||||
<option disabled selected>Select your major</option>
|
|
||||||
<option value="ECE"
|
|
||||||
>Electrical & Computer Engineering</option
|
|
||||||
>
|
>
|
||||||
<option value="CSE">Computer Science & Engineering</option>
|
|
||||||
<option value="MAE"
|
|
||||||
>Mechanical & Aerospace Engineering</option
|
|
||||||
>
|
|
||||||
<option value="BE">Bioengineering</option>
|
|
||||||
<option value="SE">Structural Engineering</option>
|
|
||||||
<option value="NANO">Nanoengineering</option>
|
|
||||||
<option value="CENG">Chemical Engineering</option>
|
|
||||||
<option value="OTHER">Other</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Expected Graduation Year -->
|
|
||||||
<div class="form-control mt-4">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Expected Graduation Year</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
id="gradYearInput"
|
|
||||||
min="2024"
|
|
||||||
max="2030"
|
|
||||||
placeholder="Enter graduation year"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Resume -->
|
|
||||||
<div class="form-control mt-4">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Resume</span>
|
|
||||||
</label>
|
</label>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p id="currentResume" class="text-sm opacity-70">
|
<p
|
||||||
|
id="currentResume"
|
||||||
|
class="text-sm text-base-content/70"
|
||||||
|
>
|
||||||
No resume uploaded
|
No resume uploaded
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
|
@ -69,16 +158,62 @@
|
||||||
class="file-input file-input-bordered file-input-sm w-full"
|
class="file-input file-input-bordered file-input-sm w-full"
|
||||||
/>
|
/>
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span id="uploadStatus" class="label-text-alt"></span>
|
<span id="uploadStatus" class="label-text-alt"
|
||||||
|
></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Save Button -->
|
<!-- Notification Preferences Section -->
|
||||||
<div class="flex justify-end">
|
<div class="bg-base-200 rounded-box p-6">
|
||||||
<button class="btn btn-primary" id="saveSettings">Save Changes</button>
|
<div class="flex items-center gap-2 mb-6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<h3 class="text-xl font-medium">
|
||||||
|
Notification Preferences
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="font-medium">Event Reminders</h3>
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
Get notified about upcoming events
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="font-medium">Newsletter</h3>
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
Receive our monthly newsletter
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -93,9 +228,9 @@
|
||||||
const majorSelect = document.getElementById(
|
const majorSelect = document.getElementById(
|
||||||
"majorSelect"
|
"majorSelect"
|
||||||
) as HTMLSelectElement;
|
) as HTMLSelectElement;
|
||||||
const gradYearInput = document.getElementById(
|
const gradYearSelect = document.getElementById(
|
||||||
"gradYearInput"
|
"gradYearSelect"
|
||||||
) as HTMLInputElement;
|
) as HTMLSelectElement;
|
||||||
const resumeUpload = document.getElementById(
|
const resumeUpload = document.getElementById(
|
||||||
"resumeUpload"
|
"resumeUpload"
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
|
@ -108,11 +243,10 @@
|
||||||
const authState = auth.getAuthState();
|
const authState = auth.getAuthState();
|
||||||
if (authState.isValid && authState.model) {
|
if (authState.isValid && authState.model) {
|
||||||
const user = authState.model;
|
const user = authState.model;
|
||||||
|
|
||||||
// Set current values
|
|
||||||
if (memberIdInput) memberIdInput.value = user.member_id || "";
|
if (memberIdInput) memberIdInput.value = user.member_id || "";
|
||||||
if (majorSelect) majorSelect.value = user.major || "";
|
if (majorSelect) majorSelect.value = user.major || "";
|
||||||
if (gradYearInput) gradYearInput.value = user.graduation_year || "";
|
if (gradYearSelect)
|
||||||
|
gradYearSelect.value = user.graduation_year || "";
|
||||||
|
|
||||||
// Update resume display
|
// Update resume display
|
||||||
if (currentResume && user.resume) {
|
if (currentResume && user.resume) {
|
||||||
|
@ -143,19 +277,53 @@
|
||||||
// Handle save settings
|
// Handle save settings
|
||||||
if (saveSettings) {
|
if (saveSettings) {
|
||||||
saveSettings.addEventListener("click", async () => {
|
saveSettings.addEventListener("click", async () => {
|
||||||
|
const button = saveSettings as HTMLButtonElement;
|
||||||
|
button.classList.add("loading");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
member_id: memberIdInput?.value,
|
member_id: memberIdInput?.value,
|
||||||
major: majorSelect?.value,
|
major: majorSelect?.value,
|
||||||
graduation_year: gradYearInput?.value,
|
graduation_year: gradYearSelect?.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
await auth.updateProfileSettings(data);
|
await auth.updateProfileSettings(data);
|
||||||
alert("Settings saved successfully");
|
|
||||||
loadUserData(); // Reload the data to show updated values
|
// Show success toast
|
||||||
|
const toast = document.createElement("div");
|
||||||
|
toast.className =
|
||||||
|
"alert alert-success fixed bottom-4 right-4 w-auto z-50";
|
||||||
|
toast.innerHTML = `
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span>Settings saved successfully!</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
setTimeout(() => toast.remove(), 3000);
|
||||||
|
|
||||||
|
loadUserData();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to save settings:", err);
|
console.error("Failed to save settings:", err);
|
||||||
alert("Failed to save settings");
|
|
||||||
|
// Show error toast
|
||||||
|
const toast = document.createElement("div");
|
||||||
|
toast.className =
|
||||||
|
"alert alert-error fixed bottom-4 right-4 w-auto z-50";
|
||||||
|
toast.innerHTML = `
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span>Failed to save settings. Please try again.</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
setTimeout(() => toast.remove(), 3000);
|
||||||
|
} finally {
|
||||||
|
button.classList.remove("loading");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,10 @@ const title = "User Profile";
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout {title}>
|
<Layout {title}>
|
||||||
<main class="min-h-screen bg-base-100/50">
|
<main class="min-h-screen bg-base-100/50 rounded-[1.5rem]">
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-6 py-6 mt-10">
|
||||||
<!-- Header Section with Breadcrumbs -->
|
<!-- Header Section with Breadcrumbs -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="text-sm breadcrumbs opacity-70 mb-2">
|
|
||||||
<ul>
|
|
||||||
<li><a href="/">Home</a></li>
|
|
||||||
<li>Profile</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<h1 class="text-4xl font-bold">Profile Dashboard</h1>
|
<h1 class="text-4xl font-bold">Profile Dashboard</h1>
|
||||||
<p class="text-base-content/70 mt-2">
|
<p class="text-base-content/70 mt-2">
|
||||||
Manage your IEEE UCSD membership and activities
|
Manage your IEEE UCSD membership and activities
|
||||||
|
@ -108,7 +102,7 @@ const title = "User Profile";
|
||||||
>
|
>
|
||||||
<!-- Left Column - User Info -->
|
<!-- Left Column - User Info -->
|
||||||
<div class="xl:col-span-4 2xl:col-span-3">
|
<div class="xl:col-span-4 2xl:col-span-3">
|
||||||
<div class="sticky top-8">
|
<div class="sticky top-[100px]">
|
||||||
<UserProfile />
|
<UserProfile />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -194,14 +188,12 @@ const title = "User Profile";
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main Content Tabs -->
|
<!-- Main Content Tabs -->
|
||||||
<div class="tabs tabs-boxed mb-6">
|
<div class="tabs tabs-boxed mb-6" id="defaultViewTabs">
|
||||||
<button class="tab tab-active" data-tab="events"
|
<button class="tab tab-active" data-default-tab="events"
|
||||||
>Events & Activities</button
|
>Events & Activities</button
|
||||||
>
|
>
|
||||||
<button class="tab" data-tab="settings">Settings</button
|
<button class="tab" data-default-tab="settings"
|
||||||
>
|
>Settings</button
|
||||||
<button class="tab" data-tab="officer" id="officerTab"
|
|
||||||
>Officer Panel</button
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -340,23 +332,22 @@ const title = "User Profile";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle tab switching
|
// Handle default view tab switching
|
||||||
tabs.forEach((tab) => {
|
const defaultViewTabs = document.querySelectorAll("[data-default-tab]");
|
||||||
|
defaultViewTabs.forEach((tab) => {
|
||||||
tab.addEventListener("click", () => {
|
tab.addEventListener("click", () => {
|
||||||
// Update tab styles
|
// Update tab styles
|
||||||
tabs.forEach((t) => t.classList.remove("tab-active"));
|
defaultViewTabs.forEach((t) => t.classList.remove("tab-active"));
|
||||||
tab.classList.add("tab-active");
|
tab.classList.add("tab-active");
|
||||||
|
|
||||||
// Update content visibility
|
// Update content visibility
|
||||||
const tabId = (tab as HTMLElement).dataset.tab;
|
const tabId = (tab as HTMLElement).dataset.defaultTab;
|
||||||
const defaultView = document.getElementById("defaultView");
|
const defaultView = document.getElementById("defaultView");
|
||||||
const settingsView = document.getElementById("settingsView");
|
const settingsView = document.getElementById("settingsView");
|
||||||
const officerView = document.getElementById("officerView");
|
|
||||||
|
|
||||||
if (defaultView && settingsView && officerView) {
|
if (defaultView && settingsView) {
|
||||||
defaultView.classList.add("hidden");
|
defaultView.classList.add("hidden");
|
||||||
settingsView.classList.add("hidden");
|
settingsView.classList.add("hidden");
|
||||||
officerView.classList.add("hidden");
|
|
||||||
|
|
||||||
switch (tabId) {
|
switch (tabId) {
|
||||||
case "events":
|
case "events":
|
||||||
|
@ -365,9 +356,6 @@ const title = "User Profile";
|
||||||
case "settings":
|
case "settings":
|
||||||
settingsView.classList.remove("hidden");
|
settingsView.classList.remove("hidden");
|
||||||
break;
|
break;
|
||||||
case "officer":
|
|
||||||
officerView.classList.remove("hidden");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue