new profile look!
This commit is contained in:
parent
7cd50bea6c
commit
eea944fcd2
4 changed files with 1008 additions and 1068 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,179 +1,192 @@
|
|||
<div>
|
||||
<!-- Loading Skeleton (shown by default) -->
|
||||
<div id="loadingSkeleton" class="card bg-base-200 shadow-xl">
|
||||
<div class="card-body p-0">
|
||||
<div class="px-6 pt-6">
|
||||
<!-- Title -->
|
||||
<h2 class="skeleton h-8 w-40 card-title"></h2>
|
||||
<!-- Loading Skeleton -->
|
||||
<div id="loadingSkeleton" class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body p-6">
|
||||
<!-- Avatar and Name Section -->
|
||||
<div class="flex flex-col items-center mb-6">
|
||||
<div class="skeleton w-24 h-24 rounded-full mb-4"></div>
|
||||
<div class="skeleton h-8 w-48"></div>
|
||||
<div class="skeleton h-4 w-32 mt-2"></div>
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div class="px-6 pb-6">
|
||||
<div class="space-y-3">
|
||||
<!-- Name -->
|
||||
<div class="space-y-1">
|
||||
<div class="skeleton h-3 w-16 opacity-70"></div>
|
||||
<div class="skeleton h-[1.75rem] w-48"></div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="space-y-1">
|
||||
<div class="skeleton h-3 w-16 opacity-70"></div>
|
||||
<div class="skeleton h-[1.75rem] w-64"></div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Member Status -->
|
||||
<div class="space-y-1">
|
||||
<div class="skeleton h-3 w-24 opacity-70"></div>
|
||||
<div class="skeleton h-[1.75rem] w-32"></div>
|
||||
<div class="flex justify-center mb-6">
|
||||
<div class="skeleton h-6 w-32"></div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Member ID -->
|
||||
<div class="space-y-1">
|
||||
<div class="skeleton h-3 w-20 opacity-70"></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="skeleton h-8 flex-1"></div>
|
||||
<div class="skeleton h-8 w-16"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Last Login -->
|
||||
<div class="space-y-1">
|
||||
<div class="skeleton h-3 w-20 opacity-70"></div>
|
||||
<div class="skeleton h-[1.25rem] w-32"></div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Event Check-in -->
|
||||
<div class="space-y-2">
|
||||
<div class="skeleton h-3 w-24 opacity-70"></div>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="skeleton h-8 flex-1"></div>
|
||||
<!-- Stats Grid -->
|
||||
<div class="stats stats-vertical shadow bg-base-200 mb-6">
|
||||
<div class="stat px-6 py-2">
|
||||
<div class="skeleton h-4 w-20 mb-1"></div>
|
||||
<div class="skeleton h-8 w-24"></div>
|
||||
</div>
|
||||
<div class="skeleton h-3 w-48 opacity-70"></div>
|
||||
<div class="stat px-6 py-2">
|
||||
<div class="skeleton h-4 w-24 mb-1"></div>
|
||||
<div class="skeleton h-8 w-32"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Resume -->
|
||||
<!-- Member Details -->
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<div class="skeleton h-3 w-16 opacity-70"></div>
|
||||
<div class="skeleton h-4 w-24"></div>
|
||||
<div class="skeleton h-10 w-full"></div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="skeleton h-5 flex-1"></div>
|
||||
<div class="skeleton h-5 w-24"></div>
|
||||
</div>
|
||||
<div class="skeleton h-8 w-full"></div>
|
||||
<div class="skeleton h-3 w-48 opacity-70"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
|
||||
<!-- Auth Buttons -->
|
||||
<div class="pt-2">
|
||||
<div class="skeleton h-4 w-20"></div>
|
||||
<div class="skeleton h-10 w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actual Content -->
|
||||
<div id="userInfo" class="card bg-base-100 shadow-xl opacity-0 hidden">
|
||||
<div class="card-body p-6">
|
||||
<!-- Avatar and Name Section -->
|
||||
<div class="flex flex-col items-center mb-6">
|
||||
<div class="avatar online placeholder mb-4">
|
||||
<div
|
||||
class="bg-gradient-to-br from-primary to-secondary text-primary-content rounded-full w-24 ring ring-primary ring-offset-base-100 ring-offset-2"
|
||||
>
|
||||
<span id="userInitials" class="text-3xl"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h2 id="userName" class="text-2xl font-bold">
|
||||
Not signed in
|
||||
</h2>
|
||||
<p id="userEmail" class="text-base-content/70">
|
||||
Not signed in
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actual Content (hidden by default) -->
|
||||
<div id="userInfo" class="card bg-base-200 shadow-xl opacity-0 hidden">
|
||||
<div class="card-body p-0">
|
||||
<div class="px-6 pt-6">
|
||||
<h2 class="card-title text-2xl">User Profile</h2>
|
||||
</div>
|
||||
<div class="px-6 pb-6">
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm opacity-70">Name</label>
|
||||
<p id="userName" class="h-[1.75rem] font-medium">
|
||||
Not signed in
|
||||
</p>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm opacity-70">Email</label>
|
||||
<p id="userEmail" class="h-[1.75rem] font-medium">
|
||||
Not signed in
|
||||
</p>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm opacity-70">Member Status</label>
|
||||
<div class="flex h-[1.75rem] items-center">
|
||||
<div id="memberStatus" class="badge badge-neutral">
|
||||
<!-- Member Status -->
|
||||
<div class="flex justify-center mb-6">
|
||||
<div id="memberStatus" class="badge badge-lg gap-2">
|
||||
<span class="loading loading-ring loading-xs"></span>
|
||||
Not verified
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<div class="stats stats-vertical shadow bg-base-200 mb-6">
|
||||
<div class="stat px-6 py-2">
|
||||
<div class="stat-title text-xs">Last Login</div>
|
||||
<div id="lastLogin" class="stat-value text-lg">Never</div>
|
||||
</div>
|
||||
<div class="stat px-6 py-2">
|
||||
<div class="stat-title text-xs">Member Since</div>
|
||||
<div class="stat-value text-lg" id="memberSince">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Toggles -->
|
||||
<div class="space-y-2 mb-6">
|
||||
<div id="officerViewToggle" class="hidden">
|
||||
<label
|
||||
class="flex items-center justify-between w-full px-1 bg-base-200 rounded-lg"
|
||||
class="flex cursor-pointer gap-2 justify-between items-center bg-gradient-to-r from-primary/10 to-primary/5 rounded-lg px-4 py-2 hover:from-primary/20 hover:to-primary/10 transition-all"
|
||||
>
|
||||
<span class="text-sm">Officer View</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary"
|
||||
/>
|
||||
<span class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Officer View</span>
|
||||
</span>
|
||||
<input type="checkbox" class="toggle toggle-primary" />
|
||||
</label>
|
||||
</div>
|
||||
<div id="sponsorViewToggle" class="hidden">
|
||||
<label
|
||||
class="flex items-center justify-between w-full px-1 bg-base-200 rounded-lg"
|
||||
class="flex cursor-pointer gap-2 justify-between items-center bg-gradient-to-r from-warning/10 to-warning/5 rounded-lg px-4 py-2 hover:from-warning/20 hover:to-warning/10 transition-all"
|
||||
>
|
||||
<span class="text-sm">Sponsor View</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-warning"
|
||||
/>
|
||||
<span class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 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>
|
||||
<span>Sponsor View</span>
|
||||
</span>
|
||||
<input type="checkbox" class="toggle toggle-warning" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm opacity-70">IEEE Member ID</label>
|
||||
<div class="flex items-center gap-2 h-8">
|
||||
</div>
|
||||
|
||||
<!-- Member Details -->
|
||||
<div class="space-y-4">
|
||||
<!-- IEEE Member ID -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
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>
|
||||
IEEE Member ID
|
||||
</span>
|
||||
</label>
|
||||
<div class="join w-full">
|
||||
<input
|
||||
type="text"
|
||||
id="memberIdInput"
|
||||
placeholder="Enter your IEEE Member ID"
|
||||
class="input input-bordered w-full h-8 min-h-[2rem] disabled:bg-base-300 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
||||
class="join-item input input-bordered flex-1 disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
||||
disabled
|
||||
/>
|
||||
<button
|
||||
id="saveMemberId"
|
||||
class="btn h-8 min-h-[2rem] disabled:bg-base-300 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed enabled:btn-primary hover:enabled:bg-primary-focus"
|
||||
class="join-item btn disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed enabled:btn-primary hover:enabled:bg-primary-focus"
|
||||
disabled>Save</button
|
||||
>
|
||||
</div>
|
||||
<p id="memberIdStatus" class="text-xs mt-1 opacity-70">
|
||||
</p>
|
||||
<label class="label">
|
||||
<span id="memberIdStatus" class="label-text-alt"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="space-y-1">
|
||||
<label class="text-sm opacity-70">Last Login</label>
|
||||
<p
|
||||
id="lastLogin"
|
||||
class="text-sm h-[1.25rem] opacity-80"
|
||||
|
||||
<!-- Resume Upload -->
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
Never
|
||||
</p>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm opacity-70">Resume</label>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
Resume
|
||||
</span>
|
||||
</label>
|
||||
<div id="resumeSection" class="space-y-2">
|
||||
<div class="flex items-center gap-2 h-[1.25rem]">
|
||||
<p
|
||||
id="resumeName"
|
||||
class="text-sm truncate flex-1"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<p id="resumeName" class="text-sm truncate flex-1">
|
||||
No resume uploaded
|
||||
</p>
|
||||
<div id="resumeActions" class="flex gap-2">
|
||||
|
@ -195,31 +208,54 @@
|
|||
type="file"
|
||||
id="resumeUpload"
|
||||
accept=".pdf,.doc,.docx"
|
||||
class="file-input file-input-bordered file-input-sm w-full disabled:bg-base-300 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
||||
class="file-input file-input-bordered file-input-sm w-full disabled:bg-base-200 disabled:border-2 disabled:border-opacity-50 disabled:cursor-not-allowed"
|
||||
disabled
|
||||
/>
|
||||
<p
|
||||
id="uploadStatus"
|
||||
class="text-xs mt-1 opacity-70"
|
||||
>
|
||||
</p>
|
||||
<label class="label">
|
||||
<span id="uploadStatus" class="label-text-alt"
|
||||
></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider my-0.5"></div>
|
||||
<div class="pt-2">
|
||||
</div>
|
||||
|
||||
<!-- Auth Buttons -->
|
||||
<div class="pt-4">
|
||||
<button
|
||||
id="contentLoginButton"
|
||||
class="login-button btn btn-primary w-full"
|
||||
>Sign in with IEEEUCSD SSO</button
|
||||
class="login-button btn btn-primary w-full gap-2"
|
||||
>
|
||||
<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="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
Sign in with IEEEUCSD SSO
|
||||
</button>
|
||||
<button
|
||||
id="contentLogoutButton"
|
||||
class="logout-button btn btn-error w-full"
|
||||
>Sign Out</button
|
||||
class="logout-button btn btn-error w-full gap-2"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<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="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -228,7 +264,7 @@
|
|||
<!-- Profile Editor Dialog -->
|
||||
<dialog id="profileEditor" class="modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg mb-4">Edit Profile</h3>
|
||||
<h3 class="font-bold text-2xl mb-6">Edit Profile</h3>
|
||||
<form class="space-y-4" onsubmit="return false" novalidate>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
|
@ -356,7 +392,7 @@
|
|||
display: none;
|
||||
}
|
||||
#userInfo {
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
.modal {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
|
@ -369,6 +405,82 @@
|
|||
<script>
|
||||
import { StoreAuth } from "./StoreAuth";
|
||||
import { EventCheckIn } from "./EventCheckIn";
|
||||
|
||||
// Initialize auth and event check-in
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
try {
|
||||
new StoreAuth();
|
||||
new EventCheckIn();
|
||||
|
||||
// Add error handling for failed initialization
|
||||
window.addEventListener("unhandledrejection", (event) => {
|
||||
console.error("Profile loading error:", event.reason);
|
||||
const userInfo = document.getElementById("userInfo");
|
||||
const loadingSkeleton =
|
||||
document.getElementById("loadingSkeleton");
|
||||
const errorMessage = document.createElement("div");
|
||||
|
||||
errorMessage.className = "alert alert-error";
|
||||
errorMessage.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="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span>Failed to load profile. Please refresh the page.</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (loadingSkeleton) loadingSkeleton.style.display = "none";
|
||||
if (userInfo) {
|
||||
userInfo.innerHTML = "";
|
||||
userInfo.appendChild(errorMessage);
|
||||
userInfo.classList.remove("hidden");
|
||||
userInfo.style.opacity = "1";
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize profile:", error);
|
||||
}
|
||||
});
|
||||
|
||||
// Add user initials generation
|
||||
const userNameElement = document.getElementById("userName");
|
||||
const userInitialsElement = document.getElementById("userInitials");
|
||||
const memberSinceElement = document.getElementById("memberSince");
|
||||
|
||||
if (userNameElement && userInitialsElement && memberSinceElement) {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (
|
||||
mutation.type === "characterData" ||
|
||||
mutation.type === "childList"
|
||||
) {
|
||||
const name = userNameElement.textContent || "";
|
||||
if (name && name !== "Not signed in") {
|
||||
const initials = name
|
||||
.split(" ")
|
||||
.map((n) => n[0])
|
||||
.join("")
|
||||
.toUpperCase()
|
||||
.slice(0, 2);
|
||||
userInitialsElement.textContent = initials;
|
||||
} else {
|
||||
userInitialsElement.textContent = "?";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(userNameElement, {
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Set member since date (you'll need to modify StoreAuth.ts to include this)
|
||||
if (memberSinceElement) {
|
||||
const created = new Date(); // Replace with actual user creation date
|
||||
memberSinceElement.textContent = created.toLocaleDateString();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -8,51 +8,47 @@
|
|||
<h2 class="card-title text-2xl">Events</h2>
|
||||
</div>
|
||||
|
||||
<!-- Event Check-in -->
|
||||
<div
|
||||
id="eventCheckInSkeleton"
|
||||
class="card bg-base-100 shadow-sm mb-4 animate-pulse"
|
||||
>
|
||||
<div class="card-body p-4">
|
||||
<div class="h-6 bg-base-300 rounded w-2/3 mb-4"></div>
|
||||
<!-- Event Check-in Section -->
|
||||
<div class="card bg-base-200 shadow-xl mb-8">
|
||||
<div class="card-body p-6">
|
||||
<h2 class="card-title text-2xl mb-4">Quick Check-in</h2>
|
||||
|
||||
<!-- Event Check-in Skeleton -->
|
||||
<div id="eventCheckInSkeleton" class="animate-pulse">
|
||||
<div class="h-6 bg-base-300 rounded w-2/3 mb-4">
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-8 bg-base-300 rounded flex-1"></div>
|
||||
<div class="h-8 bg-base-300 rounded flex-1">
|
||||
</div>
|
||||
<div class="h-8 bg-base-300 rounded w-24"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="eventCheckInContent"
|
||||
class="card bg-base-100 shadow-sm mb-4 hidden"
|
||||
>
|
||||
<div class="card-body p-4">
|
||||
<h3 class="font-medium text-lg mb-2">
|
||||
Enter your event code to check in
|
||||
</h3>
|
||||
<!-- Event Check-in Content -->
|
||||
<div id="eventCheckInContent" class="hidden">
|
||||
<div id="eventCheckInSection" class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
id="eventCodeInput"
|
||||
placeholder="Enter event code"
|
||||
class="input input-bordered input-sm flex-1"
|
||||
class="input input-bordered flex-1"
|
||||
value=""
|
||||
/>
|
||||
<button
|
||||
id="checkInButton"
|
||||
class="btn btn-sm btn-primary"
|
||||
>Check In</button
|
||||
class="btn btn-primary">Check In</button
|
||||
>
|
||||
</div>
|
||||
<p
|
||||
id="checkInStatus"
|
||||
class="text-xs mt-1 opacity-70"
|
||||
class="text-sm mt-1 opacity-70"
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider mt-0 mb-4"></div>
|
||||
</div>
|
||||
|
@ -210,6 +206,7 @@
|
|||
"eventCheckInSkeleton"
|
||||
);
|
||||
const eventCheckInContent = document.getElementById("eventCheckInContent");
|
||||
const pastEventsCount = document.getElementById("pastEventsCount");
|
||||
|
||||
// Function to show content and hide skeleton
|
||||
function showEventCheckIn() {
|
||||
|
@ -325,6 +322,7 @@
|
|||
const hasFiles =
|
||||
event.files && Array.isArray(event.files) && event.files.length > 0;
|
||||
const isPastEvent = new Date(event.end_date) < new Date();
|
||||
const { status, badge } = getEventStatus(event, isAttended);
|
||||
|
||||
// Only show files button for past events
|
||||
const filesButton =
|
||||
|
@ -343,31 +341,34 @@
|
|||
: "";
|
||||
|
||||
return `
|
||||
<div class="card bg-base-100 shadow-sm">
|
||||
<div class="card bg-base-100 shadow-sm hover:shadow-md transition-shadow">
|
||||
<div class="card-body p-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h3 class="font-medium text-lg">${event.event_name}</h3>
|
||||
<div class="flex justify-between items-start gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="font-medium text-lg truncate">${event.event_name}</h3>
|
||||
<div class="badge ${badge} gap-1">
|
||||
${getStatusIcon(status)}
|
||||
${status}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm opacity-70 space-y-1">
|
||||
<p>Starts: ${formatDate(event.start_date)}</p>
|
||||
<p>Ends: ${formatDate(event.end_date)}</p>
|
||||
${event.location ? `<p class="text-xs">📍 ${event.location}</p>` : ""}
|
||||
</div>
|
||||
${hasFiles && isPastEvent ? `<div class="mt-2">${filesButton}</div>` : ""}
|
||||
</div>
|
||||
${
|
||||
isAttended
|
||||
? `
|
||||
<div class="badge badge-success gap-1">
|
||||
event.location
|
||||
? `<p class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
Attended
|
||||
</div>
|
||||
`
|
||||
${event.location}
|
||||
</p>`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
${hasFiles && isPastEvent ? `<div class="mt-3">${filesButton}</div>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -427,14 +428,12 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Sort upcoming events by start date and limit to 2
|
||||
const nextTwoEvents = upcomingEvents
|
||||
.sort(
|
||||
// Sort upcoming events by start date
|
||||
const sortedUpcomingEvents = upcomingEvents.sort(
|
||||
(a, b) =>
|
||||
new Date(a.start_date).getTime() -
|
||||
new Date(b.start_date).getTime()
|
||||
)
|
||||
.slice(0, 2);
|
||||
);
|
||||
|
||||
// Sort past events by date descending (most recent first)
|
||||
const sortedPastEvents = pastEvents.sort(
|
||||
|
@ -443,50 +442,34 @@
|
|||
new Date(a.end_date).getTime()
|
||||
);
|
||||
|
||||
// Function to render section
|
||||
function renderSection(
|
||||
title: string,
|
||||
events: Event[],
|
||||
showDivider: boolean = true
|
||||
): string {
|
||||
if (events.length === 0) return "";
|
||||
return `
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-lg font-medium text-base-content/70">${title}</h3>
|
||||
<div class="space-y-4">
|
||||
${events.map((event) => renderEventCard(event, attendedEvents)).join("")}
|
||||
</div>
|
||||
${showDivider ? '<div class="divider"></div>' : ""}
|
||||
</div>
|
||||
`;
|
||||
// Update past events count
|
||||
if (pastEventsCount) {
|
||||
pastEventsCount.textContent =
|
||||
sortedPastEvents.length.toString();
|
||||
}
|
||||
|
||||
// Update main events list (left column)
|
||||
eventsList.innerHTML = `
|
||||
${renderSection("Upcoming Events", nextTwoEvents, nextTwoEvents.length > 0)}
|
||||
${renderSection("Currently Happening", currentEvents, false)}
|
||||
`;
|
||||
|
||||
// Update past events list (right column)
|
||||
pastEventsList.innerHTML =
|
||||
sortedPastEvents.length > 0
|
||||
? sortedPastEvents
|
||||
.map((event) =>
|
||||
renderEventCard(event, attendedEvents)
|
||||
)
|
||||
.join("")
|
||||
: `<div class="text-center py-8 opacity-70">
|
||||
<p>No past events found</p>
|
||||
</div>`;
|
||||
|
||||
// If no events at all
|
||||
if (events.items.length === 0) {
|
||||
eventsList.innerHTML = `
|
||||
// Function to render section
|
||||
function renderSection(events: Event[]): string {
|
||||
if (events.length === 0) {
|
||||
return `
|
||||
<div class="text-center py-8 opacity-70">
|
||||
<p>No events found</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return events
|
||||
.map((event) => renderEventCard(event, attendedEvents))
|
||||
.join("");
|
||||
}
|
||||
|
||||
// Update main events list (current & upcoming)
|
||||
eventsList.innerHTML = renderSection([
|
||||
...currentEvents,
|
||||
...sortedUpcomingEvents,
|
||||
]);
|
||||
|
||||
// Update past events list
|
||||
pastEventsList.innerHTML = renderSection(sortedPastEvents);
|
||||
} catch (err) {
|
||||
console.error("Failed to render events:", err);
|
||||
const errorMessage = `
|
||||
|
|
|
@ -7,16 +7,213 @@ const title = "User Profile";
|
|||
---
|
||||
|
||||
<Layout {title}>
|
||||
<main class="mx-auto pb-12 md:pt-[5vh] pt-[5vw] min-h-screen">
|
||||
<h1 class="text-4xl font-bold mb-12">Profile Management</h1>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
<!-- Left Column - User Info -->
|
||||
<div class="lg:col-span-2 2xl:col-span-1 h-fit">
|
||||
<UserProfile />
|
||||
<main class="min-h-screen bg-base-100/50">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<!-- Header Section with Breadcrumbs -->
|
||||
<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>
|
||||
<p class="text-base-content/70 mt-2">
|
||||
Manage your IEEE UCSD membership and activities
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Right Column - Store Items -->
|
||||
<div id="storeContent" class="lg:col-span-2 2xl:col-span-3">
|
||||
<!-- Loading State -->
|
||||
<div id="pageLoadingState" class="w-full">
|
||||
<div class="flex flex-col items-center justify-center p-8">
|
||||
<div class="loading loading-spinner loading-lg"></div>
|
||||
<p class="mt-4 text-base-content/70">
|
||||
Loading your profile...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div id="pageErrorState" class="hidden w-full">
|
||||
<div class="alert alert-error">
|
||||
<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="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span
|
||||
>Failed to load profile data. Please try refreshing
|
||||
the page.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Grid -->
|
||||
<div
|
||||
id="mainContent"
|
||||
class="hidden grid grid-cols-1 xl:grid-cols-12 gap-8"
|
||||
>
|
||||
<!-- Left Column - User Info -->
|
||||
<div class="xl:col-span-4 2xl:col-span-3">
|
||||
<div class="sticky top-8">
|
||||
<UserProfile />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column - Events & Activities -->
|
||||
<div class="xl:col-span-8 2xl:col-span-9">
|
||||
<!-- Quick Actions -->
|
||||
<div class="flex flex-wrap gap-4 mb-8">
|
||||
<button
|
||||
class="btn btn-primary gap-2"
|
||||
id="quickCheckInBtn"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM14 11a1 1 0 011 1v1h1a1 1 0 110 2h-1v1a1 1 0 11-2 0v-1h-1a1 1 0 110-2h1v-1a1 1 0 011-1z"
|
||||
></path>
|
||||
</svg>
|
||||
Quick Check-in
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost gap-2"
|
||||
id="viewCalendarBtn"
|
||||
>
|
||||
<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="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
View Calendar
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost gap-2"
|
||||
id="uploadResumeBtn"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z"
|
||||
></path>
|
||||
</svg>
|
||||
Upload Resume
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats Overview -->
|
||||
<div class="stats shadow w-full mb-8 bg-base-100">
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-title">Events Attended</div>
|
||||
<div
|
||||
class="stat-value text-primary"
|
||||
id="eventsAttendedValue"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-desc">Since joining</div>
|
||||
</div>
|
||||
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-secondary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 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>
|
||||
</div>
|
||||
<div class="stat-title">Loyalty Points</div>
|
||||
<div
|
||||
class="stat-value text-secondary"
|
||||
id="loyaltyPointsValue"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-desc" id="loyaltyPointsChange">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-accent">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V5a2 2 0 00-2-2H5zm9 4a1 1 0 10-2 0v6a1 1 0 102 0V7zm-3 2a1 1 0 10-2 0v4a1 1 0 102 0V9zm-3 3a1 1 0 10-2 0v1a1 1 0 102 0v-1z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-title">Activity Level</div>
|
||||
<div
|
||||
class="stat-value text-accent"
|
||||
id="activityLevelValue"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
<div class="stat-desc" id="activityLevelDesc">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Tabs -->
|
||||
<div class="tabs tabs-boxed mb-6">
|
||||
<button class="tab tab-active" data-tab="events"
|
||||
>Events & Activities</button
|
||||
>
|
||||
<button class="tab" data-tab="settings">Settings</button
|
||||
>
|
||||
<button class="tab" data-tab="officer" id="officerTab"
|
||||
>Officer Panel</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Content Areas -->
|
||||
<div id="defaultView">
|
||||
<DefaultProfileView />
|
||||
</div>
|
||||
|
@ -25,26 +222,177 @@ const title = "User Profile";
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
import { StoreAuth } from "../components/auth/StoreAuth";
|
||||
new StoreAuth();
|
||||
const auth = new StoreAuth();
|
||||
|
||||
// Handle view toggling
|
||||
const officerViewToggle = document.getElementById("officerViewToggle");
|
||||
const officerViewCheckbox = officerViewToggle?.querySelector(
|
||||
'input[type="checkbox"]'
|
||||
);
|
||||
// Initialize page state
|
||||
const pageLoadingState = document.getElementById("pageLoadingState");
|
||||
const pageErrorState = document.getElementById("pageErrorState");
|
||||
const mainContent = document.getElementById("mainContent");
|
||||
const tabs = document.querySelectorAll(".tab");
|
||||
const defaultView = document.getElementById("defaultView");
|
||||
const officerView = document.getElementById("officerView");
|
||||
const officerTab = document.getElementById("officerTab");
|
||||
|
||||
if (officerViewCheckbox && defaultView && officerView) {
|
||||
officerViewCheckbox.addEventListener("change", (e) => {
|
||||
const isChecked = (e.target as HTMLInputElement).checked;
|
||||
defaultView.style.display = isChecked ? "none" : "block";
|
||||
officerView.style.display = isChecked ? "block" : "none";
|
||||
// Stats elements
|
||||
const eventsAttendedValue = document.getElementById("eventsAttendedValue");
|
||||
const loyaltyPointsValue = document.getElementById("loyaltyPointsValue");
|
||||
const loyaltyPointsChange = document.getElementById("loyaltyPointsChange");
|
||||
const activityLevelValue = document.getElementById("activityLevelValue");
|
||||
const activityLevelDesc = document.getElementById("activityLevelDesc");
|
||||
|
||||
// Quick action buttons
|
||||
const quickCheckInBtn = document.getElementById("quickCheckInBtn");
|
||||
const viewCalendarBtn = document.getElementById("viewCalendarBtn");
|
||||
const uploadResumeBtn = document.getElementById("uploadResumeBtn");
|
||||
|
||||
// Hide officer tab by default
|
||||
if (officerTab) {
|
||||
officerTab.style.display = "none";
|
||||
}
|
||||
|
||||
// Show officer tab if user is an officer
|
||||
const showOfficerTab = () => {
|
||||
if (officerTab) {
|
||||
const authState = auth.getAuthState();
|
||||
const isOfficer =
|
||||
authState.model?.member_type === "officer" ||
|
||||
authState.model?.member_type === "administrator";
|
||||
officerTab.style.display = isOfficer ? "inline-flex" : "none";
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize page
|
||||
const initializePage = async () => {
|
||||
try {
|
||||
// Show loading state
|
||||
if (pageLoadingState) pageLoadingState.classList.remove("hidden");
|
||||
if (pageErrorState) pageErrorState.classList.add("hidden");
|
||||
if (mainContent) mainContent.classList.add("hidden");
|
||||
|
||||
// Check auth state
|
||||
const authState = auth.getAuthState();
|
||||
if (!authState.isValid || !authState.model) {
|
||||
throw new Error("User not authenticated");
|
||||
}
|
||||
|
||||
const user = authState.model;
|
||||
|
||||
// Update stats
|
||||
if (eventsAttendedValue) {
|
||||
const eventsAttended = user.events_attended?.length || 0;
|
||||
eventsAttendedValue.textContent = eventsAttended.toString();
|
||||
}
|
||||
|
||||
if (loyaltyPointsValue && loyaltyPointsChange) {
|
||||
const points = user.points || 0;
|
||||
loyaltyPointsValue.textContent = points.toString();
|
||||
// Calculate points change (example logic)
|
||||
const pointsChange = 10; // Replace with actual calculation
|
||||
loyaltyPointsChange.textContent =
|
||||
pointsChange > 0
|
||||
? `↗︎ ${pointsChange} points (30 days)`
|
||||
: `↘︎ ${Math.abs(pointsChange)} points (30 days)`;
|
||||
}
|
||||
|
||||
if (activityLevelValue && activityLevelDesc) {
|
||||
// Calculate activity level based on events and points
|
||||
const eventsAttended = user.events_attended?.length || 0;
|
||||
const points = user.points || 0;
|
||||
let activityLevel = "Low";
|
||||
let description = "Occasional Member";
|
||||
|
||||
if (eventsAttended > 5 || points > 50) {
|
||||
activityLevel = "High";
|
||||
description = "Active Member";
|
||||
} else if (eventsAttended > 2 || points > 20) {
|
||||
activityLevel = "Medium";
|
||||
description = "Regular Member";
|
||||
}
|
||||
|
||||
activityLevelValue.textContent = activityLevel;
|
||||
activityLevelDesc.textContent = description;
|
||||
}
|
||||
|
||||
// Show officer tab if applicable
|
||||
showOfficerTab();
|
||||
|
||||
// Hide loading state and show content
|
||||
if (pageLoadingState) pageLoadingState.classList.add("hidden");
|
||||
if (mainContent) mainContent.classList.remove("hidden");
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize page:", error);
|
||||
if (pageLoadingState) pageLoadingState.classList.add("hidden");
|
||||
if (pageErrorState) pageErrorState.classList.remove("hidden");
|
||||
if (mainContent) mainContent.classList.add("hidden");
|
||||
}
|
||||
};
|
||||
|
||||
// Check on load and auth changes
|
||||
initializePage();
|
||||
window.addEventListener("storage", (e) => {
|
||||
if (e.key === "pocketbase_auth") {
|
||||
initializePage();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle tab switching
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("click", () => {
|
||||
// Update tab styles
|
||||
tabs.forEach((t) => t.classList.remove("tab-active"));
|
||||
tab.classList.add("tab-active");
|
||||
|
||||
// Update content visibility
|
||||
const tabId = (tab as HTMLElement).dataset.tab;
|
||||
if (tabId === "events") {
|
||||
defaultView!.style.display = "block";
|
||||
officerView!.style.display = "none";
|
||||
} else if (tabId === "officer") {
|
||||
defaultView!.style.display = "none";
|
||||
officerView!.style.display = "block";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Quick action button handlers
|
||||
if (quickCheckInBtn) {
|
||||
quickCheckInBtn.addEventListener("click", () => {
|
||||
const eventCheckInInput = document.getElementById(
|
||||
"eventCheckInInput"
|
||||
) as HTMLInputElement;
|
||||
if (eventCheckInInput) {
|
||||
eventCheckInInput.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (viewCalendarBtn) {
|
||||
viewCalendarBtn.addEventListener("click", () => {
|
||||
// Implement calendar view logic
|
||||
console.log("Calendar view not implemented yet");
|
||||
});
|
||||
}
|
||||
|
||||
if (uploadResumeBtn) {
|
||||
uploadResumeBtn.addEventListener("click", () => {
|
||||
const resumeUpload = document.getElementById(
|
||||
"resumeUpload"
|
||||
) as HTMLInputElement;
|
||||
if (resumeUpload) {
|
||||
resumeUpload.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue