add dynamic view

This commit is contained in:
chark1es 2025-02-18 04:35:41 -08:00
parent b5cdc7464b
commit 45b278a2fc
5 changed files with 392 additions and 258 deletions

View file

@ -7,43 +7,63 @@ import EventLoad from "./EventsSection/EventLoad";
--- ---
<div id="eventsSection" class="dashboard-section hidden"> <div id="eventsSection" class="dashboard-section hidden">
<div class="mb-6"> <div class="mb-4 sm:mb-6 px-4 sm:px-6">
<h2 class="text-2xl font-bold">Events</h2> <h2 class="text-xl sm:text-2xl font-bold">Events</h2>
<p class="opacity-70">View and manage your IEEE UCSD events</p> <p class="opacity-70 text-sm sm:text-base">
View and manage your IEEE UCSD events
</p>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> <div
class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-4 sm:mb-6 px-4 sm:px-6"
>
<!-- Event Check-in Card --> <!-- Event Check-in Card -->
<EventCheckIn client:load /> <div class="w-full">
<EventCheckIn client:load />
</div>
<!-- Event Registration Card --> <!-- Event Registration Card -->
<div <div class="w-full">
class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group"
>
<div <div
class="absolute inset-0 bg-base-100 opacity-0 group-hover:opacity-90 transition-opacity duration-300 flex items-center justify-center z-10" class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group h-full"
> >
<span class="text-base-content font-medium">Coming Soon</span> <div
</div> class="absolute inset-0 bg-base-100 opacity-0 group-hover:opacity-90 transition-opacity duration-300 flex items-center justify-center z-10"
<div class="card-body"> >
<h3 class="card-title text-lg mb-4">Event Registration</h3> <span
<div class="form-control w-full"> class="text-base-content font-medium text-sm sm:text-base"
<label class="label"> >Coming Soon</span
<span class="label-text" >
>Select an event to register</span </div>
> <div class="card-body p-4 sm:p-6">
</label> <h3 class="card-title text-base sm:text-lg mb-3 sm:mb-4">
<div class="flex gap-2"> Event Registration
<select class="select select-bordered flex-1" disabled> </h3>
<option disabled selected>Pick an event</option> <div class="form-control w-full">
<option>Technical Workshop - Web Development</option <label class="label">
<span class="label-text text-sm sm:text-base"
>Select an event to register</span
> >
<option>Professional Development Workshop</option> </label>
<option>Social Event - Game Night</option> <div class="flex flex-col sm:flex-row gap-2">
</select> <select
<button class="btn btn-primary" disabled class="select select-bordered flex-1 text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
>Register</button disabled
> >
<option disabled selected>Pick an event</option>
<option
>Technical Workshop - Web Development</option
>
<option
>Professional Development Workshop</option
>
<option>Social Event - Game Night</option>
</select>
<button
class="btn btn-primary text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-[100px]"
disabled>Register</button
>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -55,31 +75,36 @@ import EventLoad from "./EventsSection/EventLoad";
<!-- Event Details Modal --> <!-- Event Details Modal -->
<dialog id="eventDetailsModal" class="modal"> <dialog id="eventDetailsModal" class="modal">
<div class="modal-box max-w-4xl"> <div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-2 sm:gap-3">
<h3 class="font-bold text-lg" id="modalTitle">Event Files</h3> <h3 class="font-bold text-base sm:text-lg" id="modalTitle">
Event Files
</h3>
<button <button
id="downloadAllBtn" id="downloadAllBtn"
class="btn btn-primary btn-sm gap-1" class="btn btn-primary btn-sm gap-1 text-xs sm:text-sm"
onclick="window.downloadAllFiles()" onclick="window.downloadAllFiles()"
> >
<Icon <Icon
icon="heroicons:arrow-down-tray-20-solid" icon="heroicons:arrow-down-tray-20-solid"
className="h-4 w-4" className="h-3 w-3 sm:h-4 sm:w-4"
/> />
Download All Download All
</button> </button>
</div> </div>
<button <button
class="btn btn-circle btn-ghost" class="btn btn-circle btn-ghost btn-sm sm:btn-md"
onclick="window.closeEventDetailsModal()" onclick="window.closeEventDetailsModal()"
> >
<Icon icon="heroicons:x-mark" className="h-6 w-6" /> <Icon
icon="heroicons:x-mark"
className="h-4 w-4 sm:h-6 sm:w-6"
/>
</button> </button>
</div> </div>
<div id="filesContent" class="space-y-4"> <div id="filesContent" class="space-y-3 sm:space-y-4">
<!-- Files list will be populated here --> <!-- Files list will be populated here -->
</div> </div>
</div> </div>
@ -90,14 +115,17 @@ import EventLoad from "./EventsSection/EventLoad";
<!-- Universal File Preview Modal --> <!-- Universal File Preview Modal -->
<dialog id="filePreviewModal" class="modal"> <dialog id="filePreviewModal" class="modal">
<div class="modal-box max-w-4xl"> <div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-2 sm:gap-3">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm text-xs sm:text-sm"
onclick="window.closeFilePreviewEvents()">Close</button onclick="window.closeFilePreviewEvents()">Close</button
> >
<h3 class="font-bold text-lg truncate" id="previewFileName"> <h3
class="font-bold text-base sm:text-lg truncate"
id="previewFileName"
>
</h3> </h3>
</div> </div>
</div> </div>
@ -106,7 +134,8 @@ import EventLoad from "./EventsSection/EventLoad";
id="previewLoadingSpinner" id="previewLoadingSpinner"
class="absolute inset-0 flex items-center justify-center bg-base-200 bg-opacity-50 hidden" class="absolute inset-0 flex items-center justify-center bg-base-200 bg-opacity-50 hidden"
> >
<span class="loading loading-spinner loading-lg"></span> <span class="loading loading-spinner loading-md sm:loading-lg"
></span>
</div> </div>
<div id="previewContent" class="w-full"> <div id="previewContent" class="w-full">
<FilePreview client:load isModal={true} /> <FilePreview client:load isModal={true} />

View file

@ -240,12 +240,12 @@ const EventCheckIn = () => {
return ( return (
<> <>
<div className="card bg-base-100 shadow-xl border border-base-200"> <div className="card bg-base-100 shadow-xl border border-base-200 h-full">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title text-lg mb-4">Event Check-in</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Event Check-in</h3>
<div className="form-control w-full"> <div className="form-control w-full">
<label className="label"> <label className="label">
<span className="label-text">Enter event code to check in</span> <span className="label-text text-sm sm:text-base">Enter event code to check in</span>
</label> </label>
<form onSubmit={(e) => { <form onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
@ -260,11 +260,11 @@ const EventCheckIn = () => {
createToast("Please enter an event code", "warning"); createToast("Please enter an event code", "warning");
} }
}}> }}>
<div className="flex gap-2"> <div className="flex flex-col sm:flex-row gap-2">
<input <input
type="password" type="password"
placeholder="Enter code" placeholder="Enter code"
className="input input-bordered flex-1" className="input input-bordered flex-1 text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
onKeyPress={(e) => { onKeyPress={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
@ -273,7 +273,7 @@ const EventCheckIn = () => {
/> />
<button <button
type="submit" type="submit"
className={`btn btn-primary min-w-[90px] ${isLoading ? "loading" : ""}`} className={`btn btn-primary h-10 min-h-[2.5rem] text-sm sm:text-base w-full sm:w-auto ${isLoading ? "loading" : ""}`}
disabled={isLoading} disabled={isLoading}
> >
{isLoading ? ( {isLoading ? (
@ -289,35 +289,35 @@ const EventCheckIn = () => {
</div> </div>
<dialog id="foodSelectionModal" className="modal"> <dialog id="foodSelectionModal" className="modal">
<div className="modal-box"> <div className="modal-box max-w-[90vw] sm:max-w-lg p-4 sm:p-6">
<h3 className="font-bold text-lg mb-4">Food Selection</h3> <h3 className="font-bold text-base sm:text-lg mb-3 sm:mb-4">Food Selection</h3>
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-3 sm:space-y-4">
<div className="form-control"> <div className="form-control">
<label className="label"> <label className="label">
<span className="label-text">What food would you like?</span> <span className="label-text text-sm sm:text-base">What food would you like?</span>
<span className="label-text-alt text-error">*</span> <span className="label-text-alt text-error">*</span>
</label> </label>
<input <input
type="text" type="text"
value={foodInput} value={foodInput}
onChange={(e) => setFoodInput(e.target.value)} onChange={(e) => setFoodInput(e.target.value)}
className="input input-bordered" className="input input-bordered text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
placeholder="Enter your food choice or 'none'" placeholder="Enter your food choice or 'none'"
required required
/> />
<label className="label"> <label className="label">
<span className="label-text-alt text-info"> <span className="label-text-alt text-info text-xs sm:text-sm">
Enter 'none' if you don't want any food Enter 'none' if you don't want any food
</span> </span>
</label> </label>
</div> </div>
<div className="modal-action"> <div className="modal-action flex flex-col sm:flex-row gap-2 sm:gap-3">
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-auto">
Submit Submit
</button> </button>
<button <button
type="button" type="button"
className="btn" className="btn text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-auto"
onClick={() => { onClick={() => {
const modal = document.getElementById("foodSelectionModal") as HTMLDialogElement; const modal = document.getElementById("foodSelectionModal") as HTMLDialogElement;
modal.close(); modal.close();

View file

@ -88,52 +88,49 @@ const EventLoad = () => {
return ( return (
<div key={event.id} className="card bg-base-200 shadow-lg hover:shadow-xl transition-all duration-300 relative overflow-hidden"> <div key={event.id} className="card bg-base-200 shadow-lg hover:shadow-xl transition-all duration-300 relative overflow-hidden">
<div className="card-body p-5"> <div className="card-body p-3 sm:p-4">
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
<div className="flex items-start justify-between gap-3 mb-2"> <div className="flex flex-col gap-2">
<div className="flex-1"> <div className="flex-1">
<h3 className="card-title text-lg font-semibold mb-1 line-clamp-2">{event.event_name}</h3> <h3 className="card-title text-base sm:text-lg font-semibold mb-1 line-clamp-2">{event.event_name}</h3>
<div className="flex items-center gap-2 text-sm text-base-content/70"> <div className="flex flex-wrap items-center gap-2 text-xs sm:text-sm text-base-content/70">
<div className="badge badge-primary badge-sm">{event.points_to_reward} pts</div> <div className="badge badge-primary badge-sm">{event.points_to_reward} pts</div>
</div> <div className="text-xs sm:text-sm opacity-75">
</div> {startDate.toLocaleDateString("en-US", {
<div className="text-right shrink-0 text-base-content/80"> weekday: "short",
<div className="text-sm font-medium"> month: "short",
{startDate.toLocaleDateString("en-US", { day: "numeric",
weekday: "short", })}
month: "short", {" • "}
day: "numeric", {startDate.toLocaleTimeString("en-US", {
})} hour: "numeric",
</div> minute: "2-digit",
<div className="text-xs mt-0.5 opacity-75"> })}
{startDate.toLocaleTimeString("en-US", { </div>
hour: "numeric",
minute: "2-digit",
})}
</div> </div>
</div> </div>
</div> </div>
<div className="text-sm text-base-content/70 mb-3 line-clamp-2"> <div className="text-xs sm:text-sm text-base-content/70 my-2 line-clamp-2">
{event.description || "No description available"} {event.description || "No description available"}
</div> </div>
<div className="flex items-center justify-between"> <div className="flex flex-wrap items-center gap-2 mt-auto pt-2">
<div className="flex items-center gap-2 text-base-content/80"> {event.files && event.files.length > 0 && (
<Icon icon="mdi:map-marker" className="w-4 h-4 text-primary shrink-0" />
<span className="text-sm">{event.location}</span>
</div>
{isPastEvent && event.files && event.files.length > 0 && (
<button <button
onClick={() => window.openDetailsModal(event)} onClick={() => window.openDetailsModal(event)}
className="btn btn-sm btn-primary w-[90px] inline-flex items-center justify-center" className="btn btn-ghost btn-sm text-xs sm:text-sm gap-1 h-8 min-h-0 px-2"
> >
<div className="flex items-center gap-1"> <Icon icon="heroicons:document-duplicate" className="h-3 w-3 sm:h-4 sm:w-4" />
<Icon icon="mdi:file-document-outline" className="w-4 h-4" /> Files ({event.files.length})
<span>Files</span>
</div>
</button> </button>
)} )}
{isPastEvent && (
<div className="badge badge-ghost text-xs">Past Event</div>
)}
<div className="text-xs sm:text-sm opacity-75 ml-auto">
{event.location}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -147,7 +144,11 @@ const EventLoad = () => {
const allEvents = await get.getAll<Event>( const allEvents = await get.getAll<Event>(
"events", "events",
"published = true", "published = true",
"-start_date" "-start_date",
{
fields: ["*"],
disableAutoCancellation: true
}
); );
// Split events into upcoming, ongoing, and past based on start and end dates // Split events into upcoming, ongoing, and past based on start and end dates
@ -202,7 +203,11 @@ const EventLoad = () => {
ongoing.sort((a, b) => new Date(a.end_date).getTime() - new Date(b.end_date).getTime()); ongoing.sort((a, b) => new Date(a.end_date).getTime() - new Date(b.end_date).getTime());
past.sort((a, b) => new Date(b.end_date).getTime() - new Date(a.end_date).getTime()); past.sort((a, b) => new Date(b.end_date).getTime() - new Date(a.end_date).getTime());
setEvents({ upcoming, ongoing, past }); setEvents({
upcoming: upcoming.slice(0, 50), // Limit to 50 events per section
ongoing: ongoing.slice(0, 50),
past: past.slice(0, 50)
});
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
console.error("Failed to load events:", error); console.error("Failed to load events:", error);
@ -214,10 +219,10 @@ const EventLoad = () => {
return ( return (
<> <>
{/* Ongoing Events */} {/* Ongoing Events */}
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-4 sm:mb-6 mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Ongoing Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => ( {[...Array(3)].map((_, i) => (
<div key={`ongoing-skeleton-${i}`}>{createSkeletonCard()}</div> <div key={`ongoing-skeleton-${i}`}>{createSkeletonCard()}</div>
))} ))}
@ -226,10 +231,10 @@ const EventLoad = () => {
</div> </div>
{/* Upcoming Events */} {/* Upcoming Events */}
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-4 sm:mb-6 mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Upcoming Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => ( {[...Array(3)].map((_, i) => (
<div key={`upcoming-skeleton-${i}`}>{createSkeletonCard()}</div> <div key={`upcoming-skeleton-${i}`}>{createSkeletonCard()}</div>
))} ))}
@ -238,10 +243,10 @@ const EventLoad = () => {
</div> </div>
{/* Past Events */} {/* Past Events */}
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Past Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Past Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => ( {[...Array(3)].map((_, i) => (
<div key={`past-skeleton-${i}`}>{createSkeletonCard()}</div> <div key={`past-skeleton-${i}`}>{createSkeletonCard()}</div>
))} ))}
@ -256,10 +261,10 @@ const EventLoad = () => {
<> <>
{/* Ongoing Events */} {/* Ongoing Events */}
{events.ongoing.length > 0 && ( {events.ongoing.length > 0 && (
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-4 sm:mb-6 mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Ongoing Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.ongoing.map(renderEventCard)} {events.ongoing.map(renderEventCard)}
</div> </div>
</div> </div>
@ -268,10 +273,10 @@ const EventLoad = () => {
{/* Upcoming Events */} {/* Upcoming Events */}
{events.upcoming.length > 0 && ( {events.upcoming.length > 0 && (
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-4 sm:mb-6 mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Upcoming Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.upcoming.map(renderEventCard)} {events.upcoming.map(renderEventCard)}
</div> </div>
</div> </div>
@ -280,10 +285,10 @@ const EventLoad = () => {
{/* Past Events */} {/* Past Events */}
{events.past.length > 0 && ( {events.past.length > 0 && (
<div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform"> <div className="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mx-4 sm:mx-6">
<div className="card-body"> <div className="card-body p-4 sm:p-6">
<h3 className="card-title mb-4">Past Events</h3> <h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Past Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.past.map(renderEventCard)} {events.past.map(renderEventCard)}
</div> </div>
</div> </div>

View file

@ -71,23 +71,36 @@ const currentPage = eventResponse.page;
--- ---
<div id="eventManagementSection" class="dashboard-section hidden"> <div id="eventManagementSection" class="dashboard-section hidden">
<div class="mb-6 flex justify-between items-center"> <div
class="mb-4 md:mb-6 flex flex-col md:flex-row md:justify-between md:items-center gap-2"
>
<div> <div>
<h2 class="text-2xl font-bold">Event Management</h2> <h2 class="text-xl md:text-2xl font-bold">Event Management</h2>
<p class="opacity-70">Manage and create IEEE UCSD events</p> <p class="text-sm md:text-base opacity-70">
Manage and create IEEE UCSD events
</p>
</div> </div>
</div> </div>
<!-- Stats Cards --> <!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"> <div
class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 md:gap-6 mb-6 md:mb-8"
>
<div <div
class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform" class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform"
> >
<div class="stat"> <div class="stat p-4 md:p-6">
<div class="stat-title font-medium opacity-80"> <div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
Total Events Total Events
</div> </div>
<div class="stat-value text-primary" id="totalEvents">-</div> <div
class="stat-value text-primary text-2xl md:text-3xl"
id="totalEvents"
>
-
</div>
<div class="stat-desc flex items-center gap-2 mt-1"> <div class="stat-desc flex items-center gap-2 mt-1">
<div class="badge badge-primary badge-sm" id="quarterLabel"> <div class="badge badge-primary badge-sm" id="quarterLabel">
Current Academic Term Current Academic Term
@ -98,11 +111,16 @@ const currentPage = eventResponse.page;
<div <div
class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-secondary transition-all duration-300 hover:-translate-y-1 transform" class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-secondary transition-all duration-300 hover:-translate-y-1 transform"
> >
<div class="stat"> <div class="stat p-4 md:p-6">
<div class="stat-title font-medium opacity-80"> <div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
Unique Attendees Unique Attendees
</div> </div>
<div class="stat-value text-secondary" id="uniqueAttendees"> <div
class="stat-value text-secondary text-2xl md:text-3xl"
id="uniqueAttendees"
>
- -
</div> </div>
<div class="stat-desc flex items-center gap-2 mt-1"> <div class="stat-desc flex items-center gap-2 mt-1">
@ -113,13 +131,18 @@ const currentPage = eventResponse.page;
</div> </div>
</div> </div>
<div <div
class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-accent transition-all duration-300 hover:-translate-y-1 transform" class="stats shadow-lg bg-base-100 rounded-2xl border border-base-200 hover:border-accent transition-all duration-300 hover:-translate-y-1 transform sm:col-span-2 md:col-span-1"
> >
<div class="stat"> <div class="stat p-4 md:p-6">
<div class="stat-title font-medium opacity-80"> <div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
Recurring Attendees Recurring Attendees
</div> </div>
<div class="stat-value text-accent" id="recurringAttendees"> <div
class="stat-value text-accent text-2xl md:text-3xl"
id="recurringAttendees"
>
- -
</div> </div>
<div class="stat-desc flex items-center gap-2 mt-1"> <div class="stat-desc flex items-center gap-2 mt-1">
@ -135,39 +158,50 @@ const currentPage = eventResponse.page;
<div <div
class="card bg-base-100 shadow-lg border border-base-200 hover:border-primary transition-all duration-300" class="card bg-base-100 shadow-lg border border-base-200 hover:border-primary transition-all duration-300"
> >
<div class="card-body"> <div class="card-body p-4 md:p-6">
<h3 class="card-title text-xl font-bold flex items-center gap-3"> <h3
<div class="badge badge-primary p-3"> class="card-title text-lg md:text-xl font-bold flex flex-col md:flex-row md:items-center gap-3"
<Icon name="heroicons:calendar" class="h-5 w-5" /> >
<div class="flex items-center gap-2">
<div class="badge badge-primary p-3">
<Icon name="heroicons:calendar" class="h-5 w-5" />
</div>
Events List
</div> </div>
Events List <div class="flex-1 flex flex-wrap gap-2 md:justify-end">
<div class="ml-auto justify-end flex gap-2">
<button <button
class="btn btn-ghost gap-2" class="btn btn-ghost btn-sm md:btn-md gap-2"
onclick="window.refreshEvents()" onclick="window.refreshEvents()"
> >
<Icon name="heroicons:arrow-path" class="h-5 w-5" /> <Icon
name="heroicons:arrow-path"
class="h-4 w-4 md:h-5 md:w-5"
/>
Refresh Refresh
</button> </button>
<button <button
class="btn btn-primary gap-2" class="btn btn-primary btn-sm md:btn-md gap-2"
onclick="window.openEditModal()" onclick="window.openEditModal()"
> >
<Icon name="heroicons:plus" class="h-5 w-5" /> <Icon
name="heroicons:plus"
class="h-4 w-4 md:h-5 md:w-5"
/>
Add New Event Add New Event
</button> </button>
</div> </div>
</h3> </h3>
<div class="divider"></div> <div class="divider my-2 md:my-4"></div>
<!-- Filter Controls --> <!-- Filter Controls -->
<div class="mb-4"> <div class="mb-4">
<!-- All Filters in One Line --> <!-- All Filters -->
<div class="flex flex-wrap items-end gap-4"> <div class="flex flex-wrap gap-4">
<div class="form-control"> <div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium" <span
class="label-text text-sm md:text-base font-medium"
>Time Filter</span >Time Filter</span
> >
</label> </label>
@ -176,7 +210,7 @@ const currentPage = eventResponse.page;
type="radio" type="radio"
name="timeFilter" name="timeFilter"
value="all" value="all"
class="join-item btn btn-sm" class="join-item btn btn-xs md:btn-sm"
checked checked
aria-label="All Events" aria-label="All Events"
/> />
@ -184,29 +218,33 @@ const currentPage = eventResponse.page;
type="radio" type="radio"
name="timeFilter" name="timeFilter"
value="ongoing" value="ongoing"
class="join-item btn btn-sm" class="join-item btn btn-xs md:btn-sm"
aria-label="Ongoing" aria-label="Ongoing"
/> />
<input <input
type="radio" type="radio"
name="timeFilter" name="timeFilter"
value="upcoming" value="upcoming"
class="join-item btn btn-sm" class="join-item btn btn-xs md:btn-sm"
aria-label="Upcoming" aria-label="Upcoming"
/> />
<input <input
type="radio" type="radio"
name="timeFilter" name="timeFilter"
value="past" value="past"
class="join-item btn btn-sm" class="join-item btn btn-xs md:btn-sm"
aria-label="Past" aria-label="Past"
/> />
</div> </div>
</div> </div>
<div class="form-control"> <!-- Other filters with similar responsive adjustments -->
<div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium">Year</span> <span
class="label-text text-sm md:text-base font-medium"
>Year</span
>
</label> </label>
<div class="dropdown"> <div class="dropdown">
<label <label
@ -251,9 +289,12 @@ const currentPage = eventResponse.page;
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium">Quarter</span> <span
class="label-text text-sm md:text-base font-medium"
>Quarter</span
>
</label> </label>
<div class="dropdown"> <div class="dropdown">
<label <label
@ -278,8 +319,8 @@ const currentPage = eventResponse.page;
</svg> </svg>
</label> </label>
<div <div
tabindex="0"
id="quarterDropdownContent" id="quarterDropdownContent"
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52"
> >
<div class="form-control"> <div class="form-control">
@ -339,9 +380,11 @@ const currentPage = eventResponse.page;
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium">Published</span <span
class="label-text text-sm md:text-base font-medium"
>Published</span
> >
</label> </label>
<div class="dropdown"> <div class="dropdown">
@ -404,9 +447,11 @@ const currentPage = eventResponse.page;
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium">Has Files</span <span
class="label-text text-sm md:text-base font-medium"
>Has Files</span
> >
</label> </label>
<div class="dropdown"> <div class="dropdown">
@ -469,9 +514,12 @@ const currentPage = eventResponse.page;
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control w-full sm:w-auto">
<label class="label"> <label class="label">
<span class="label-text font-medium">Has Food</span> <span
class="label-text text-sm md:text-base font-medium"
>Has Food</span
>
</label> </label>
<div class="dropdown"> <div class="dropdown">
<label <label
@ -536,7 +584,7 @@ const currentPage = eventResponse.page;
</div> </div>
<!-- Search and Per Page Controls --> <!-- Search and Per Page Controls -->
<div class="flex flex-col md:flex-row gap-4 mb-4"> <div class="flex flex-col sm:flex-row gap-4 mb-4">
<div class="form-control flex-1"> <div class="form-control flex-1">
<div class="join w-full"> <div class="join w-full">
<div <div
@ -544,7 +592,7 @@ const currentPage = eventResponse.page;
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 opacity-70" class="h-4 w-4 md:h-5 md:w-5 opacity-70"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
> >
@ -558,20 +606,20 @@ const currentPage = eventResponse.page;
type="text" type="text"
id="searchInput" id="searchInput"
placeholder="Search events..." placeholder="Search events..."
class="input input-bordered join-item w-full" class="input input-bordered join-item w-full text-sm md:text-base"
/> />
</div> </div>
</div> </div>
<div class="form-control w-full md:w-auto"> <div class="form-control w-full sm:w-auto">
<div class="join"> <div class="join">
<div <div
class="join-item bg-base-200 flex items-center px-3" class="join-item bg-base-200 flex items-center px-3 text-sm md:text-base"
> >
Per Page Per Page
</div> </div>
<select <select
id="perPageSelect" id="perPageSelect"
class="select select-bordered join-item" class="select select-bordered join-item text-sm md:text-base"
> >
<option value="5">5</option> <option value="5">5</option>
<option value="10">10</option> <option value="10">10</option>
@ -583,33 +631,34 @@ const currentPage = eventResponse.page;
</div> </div>
<!-- Event Items --> <!-- Event Items -->
<div class="space-y-4" id="eventsList"> <div class="space-y-3 md:space-y-4" id="eventsList">
<div class="text-center py-8 text-base-content/70"> <!-- Event items will be populated here with responsive classes -->
<Icon
name="heroicons:calendar"
class="h-12 w-12 mx-auto mb-4 opacity-50"
/>
<p>Loading events...</p>
</div>
</div> </div>
<!-- Pagination --> <!-- Pagination -->
<div class="flex justify-center mt-6" id="paginationContainer"> <div
class="flex justify-center mt-4 md:mt-6"
id="paginationContainer"
>
<div class="join"> <div class="join">
<button class="join-item btn btn-sm" id="firstPageBtn" <button
>«</button class="join-item btn btn-xs md:btn-sm"
id="firstPageBtn">«</button
> >
<button class="join-item btn btn-sm" id="prevPageBtn" <button
></button class="join-item btn btn-xs md:btn-sm"
id="prevPageBtn"></button
> >
<button class="join-item btn btn-sm" <button class="join-item btn btn-xs md:btn-sm"
>Page <span id="currentPageNumber">1</span></button >Page <span id="currentPageNumber">1</span></button
> >
<button class="join-item btn btn-sm" id="nextPageBtn" <button
></button class="join-item btn btn-xs md:btn-sm"
id="nextPageBtn"></button
> >
<button class="join-item btn btn-sm" id="lastPageBtn" <button
>»</button class="join-item btn btn-xs md:btn-sm"
id="lastPageBtn">»</button
> >
</div> </div>
</div> </div>
@ -1188,43 +1237,43 @@ const currentPage = eventResponse.page;
window[eventDataId] = event; window[eventDataId] = event;
return ` return `
<div class="flex items-center justify-between p-4 bg-base-200 rounded-xl hover:bg-base-300 transition-all duration-300"> <div class="flex flex-col sm:flex-row sm:items-center justify-between p-3 md:p-4 bg-base-200 rounded-xl hover:bg-base-300 transition-all duration-300 gap-3">
<div class="flex items-center gap-4"> <div class="flex items-start sm:items-center gap-3 md:gap-4">
<div class="badge badge-lg p-3 badge-primary"> <div class="badge badge-lg p-3 badge-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 md:h-5 md: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 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" />
</svg> </svg>
</div> </div>
<div> <div class="min-w-0">
<h4 class="font-semibold">${event.event_name}</h4> <h4 class="font-semibold text-sm md:text-base truncate">${event.event_name}</h4>
<p class="text-sm opacity-70">${dateStr}${detailsStr ? ` • ${detailsStr}` : ""}</p> <p class="text-xs md:text-sm opacity-70 truncate">${dateStr}${detailsStr ? ` • ${detailsStr}` : ""}</p>
</div> </div>
</div> </div>
<div class="flex gap-2"> <div class="flex flex-wrap gap-2 justify-end">
<button class="btn btn-ghost btn-sm" onclick="window.openDetailsModal(window['${eventDataId}'])"> <button class="btn btn-ghost btn-xs md:btn-sm" onclick="window.openDetailsModal(window['${eventDataId}'])">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 md:h-5 md:w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" /> <polyline points="14 2 14 8 20 8" />
</svg> </svg>
</button> </button>
<button class="btn btn-ghost btn-sm" onclick="window.openAttendeesModal(window['${eventDataId}'])"> <button class="btn btn-ghost btn-xs md:btn-sm" onclick="window.openAttendeesModal(window['${eventDataId}'])">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 md:h-5 md:w-5" 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 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" />
</svg> </svg>
</button> </button>
<button class="btn btn-ghost btn-sm" onclick="window.openEditModal(window['${eventDataId}'])"> <button class="btn btn-ghost btn-xs md:btn-sm" onclick="window.openEditModal(window['${eventDataId}'])">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 md:h-5 md:w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" /> <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
</svg> </svg>
</button> </button>
<button class="btn btn-ghost btn-sm text-error" onclick="window.deleteEvent('${event.id}', '${event.event_name.replace(/'/g, "\\'")}')"> <button class="btn btn-ghost btn-xs md:btn-sm text-error" onclick="window.deleteEvent('${event.id}', '${event.event_name.replace(/'/g, "\\'")}')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 md:h-5 md:w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg> </svg>
</button> </button>
</div> </div>
</div> </div>
`; `;
}) })
.join(""); .join("");

View file

@ -21,7 +21,7 @@ const title = "Dashboard";
<div class="flex h-screen"> <div class="flex h-screen">
<!-- Sidebar --> <!-- Sidebar -->
<aside <aside
class="bg-base-100 w-80 flex flex-col shadow-xl border-r border-base-200 transition-all duration-300" class="bg-base-100 w-80 flex flex-col shadow-xl border-r border-base-200 transition-all duration-300 fixed lg:relative h-full z-50 -translate-x-full lg:translate-x-0"
> >
<!-- Logo --> <!-- Logo -->
<div class="p-6 border-b border-base-200"> <div class="p-6 border-b border-base-200">
@ -152,9 +152,13 @@ const title = "Dashboard";
</aside> </aside>
<!-- Main Content --> <!-- Main Content -->
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-base-200"> <main
class="flex-1 overflow-x-hidden overflow-y-auto bg-base-200 w-full lg:w-[calc(100%-20rem)]"
>
<!-- Mobile Header --> <!-- Mobile Header -->
<header class="bg-base-100 p-4 shadow-md lg:hidden"> <header
class="bg-base-100 p-4 shadow-md lg:hidden sticky top-0 z-40"
>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
@ -169,11 +173,11 @@ const title = "Dashboard";
</header> </header>
<!-- Page Content --> <!-- Page Content -->
<div class="p-6 max-w-[1600px] mx-auto"> <div class="p-4 md:p-6 max-w-[1600px] mx-auto">
<!-- Loading State --> <!-- Loading State -->
<div id="pageLoadingState" class="w-full"> <div id="pageLoadingState" class="w-full">
<div <div
class="flex flex-col items-center justify-center p-8" class="flex flex-col items-center justify-center p-4 sm:p-8"
> >
<div class="loading loading-spinner loading-lg"> <div class="loading loading-spinner loading-lg">
</div> </div>
@ -183,7 +187,7 @@ const title = "Dashboard";
<!-- Error State --> <!-- Error State -->
<div id="pageErrorState" class="hidden w-full"> <div id="pageErrorState" class="hidden w-full">
<div class="alert alert-error"> <div class="alert alert-error mx-2 sm:mx-0">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6" class="h-6 w-6"
@ -201,12 +205,14 @@ const title = "Dashboard";
<!-- Not Authenticated State --> <!-- Not Authenticated State -->
<div id="notAuthenticatedState" class="hidden w-full"> <div id="notAuthenticatedState" class="hidden w-full">
<div class="card bg-base-100 shadow-xl"> <div class="card bg-base-100 shadow-xl mx-2 sm:mx-0">
<div class="card-body items-center text-center"> <div
<div class="mb-6"> class="card-body items-center text-center p-4 sm:p-8"
>
<div class="mb-4 sm:mb-6">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="h-16 w-16 opacity-30" class="h-12 w-12 sm:h-16 sm:w-16 opacity-30"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
> >
@ -216,15 +222,17 @@ const title = "Dashboard";
clip-rule="evenodd"></path> clip-rule="evenodd"></path>
</svg> </svg>
</div> </div>
<h2 class="card-title text-2xl mb-2"> <h2 class="card-title text-xl sm:text-2xl mb-2">
Sign in to Access Dashboard Sign in to Access Dashboard
</h2> </h2>
<p class="opacity-70 mb-6"> <p
class="opacity-70 mb-4 sm:mb-6 text-sm sm:text-base"
>
Please sign in with your IEEE UCSD account Please sign in with your IEEE UCSD account
to access the dashboard. to access the dashboard.
</p> </p>
<button <button
class="login-button btn btn-primary btn-lg gap-2" class="login-button btn btn-primary btn-lg gap-2 w-full sm:w-auto"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -237,14 +245,16 @@ const title = "Dashboard";
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" 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> clip-rule="evenodd"></path>
</svg> </svg>
Sign in with IEEEUCSD SSO <span class="whitespace-nowrap"
>Sign in with IEEEUCSD SSO</span
>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div id="mainContent" class="hidden"> <div id="mainContent" class="hidden space-y-4 sm:space-y-6">
<ProfileSection /> <ProfileSection />
<EventsSection /> <EventsSection />
<ReimbursementSection /> <ReimbursementSection />
@ -291,38 +301,76 @@ const title = "Dashboard";
}); });
// Update display elements // Update display elements
if (userName) { const displayName = extendedUser.name || "Unknown User";
userName.textContent = extendedUser.name || "Unknown User"; const displayRole = extendedUser.member_type || "Member";
} const initials = (extendedUser.name || "U")
.split(" ")
.map((n: string) => n[0])
.join("")
.toUpperCase();
if (userRole) { // Update elements
userRole.textContent = extendedUser.member_type || "Member"; if (userName) userName.textContent = displayName;
} if (userRole) userRole.textContent = displayRole;
if (userInitials) userInitials.textContent = initials;
if (userInitials) {
const initials = (extendedUser.name || "U")
.split(" ")
.map((n: string) => n[0])
.join("")
.toUpperCase();
userInitials.textContent = initials;
}
} catch (error) { } catch (error) {
console.error("Error fetching user profile:", error); console.error("Error fetching user profile:", error);
if (userName) userName.textContent = "Unknown User"; const fallbackValues = {
if (userRole) userRole.textContent = "Member"; name: "Unknown User",
if (userInitials) userInitials.textContent = "?"; role: "Member",
initials: "?",
};
// Update elements with fallback values
if (userName) userName.textContent = fallbackValues.name;
if (userRole) userRole.textContent = fallbackValues.role;
if (userInitials)
userInitials.textContent = fallbackValues.initials;
} }
}; };
// Mobile sidebar toggle // Mobile sidebar toggle with overlay
const mobileSidebarToggle = document.getElementById("mobileSidebarToggle"); const mobileSidebarToggle = document.getElementById("mobileSidebarToggle");
if (mobileSidebarToggle && sidebar) { if (mobileSidebarToggle && sidebar) {
mobileSidebarToggle.addEventListener("click", () => { const toggleSidebar = () => {
sidebar.classList.toggle("-translate-x-full"); const isOpen = !sidebar.classList.contains("-translate-x-full");
});
if (isOpen) {
sidebar.classList.add("-translate-x-full");
document.body.classList.remove("overflow-hidden");
// Remove overlay if it exists
const overlay = document.getElementById("sidebarOverlay");
overlay?.remove();
} else {
sidebar.classList.remove("-translate-x-full");
document.body.classList.add("overflow-hidden");
// Add overlay
const overlay = document.createElement("div");
overlay.id = "sidebarOverlay";
overlay.className =
"fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden";
overlay.addEventListener("click", toggleSidebar);
document.body.appendChild(overlay);
}
};
mobileSidebarToggle.addEventListener("click", toggleSidebar);
} }
// Close sidebar on window resize if screen becomes larger
window.addEventListener("resize", () => {
if (window.innerWidth >= 1024) {
const overlay = document.getElementById("sidebarOverlay");
if (overlay) {
overlay.remove();
document.body.classList.remove("overflow-hidden");
}
if (sidebar) {
sidebar.classList.remove("-translate-x-full");
}
}
});
// Handle navigation // Handle navigation
const handleNavigation = () => { const handleNavigation = () => {
const navButtons = document.querySelectorAll(".dashboard-nav-btn"); const navButtons = document.querySelectorAll(".dashboard-nav-btn");
@ -343,9 +391,12 @@ const title = "Dashboard";
const sectionId = `${button.getAttribute("data-section")}Section`; const sectionId = `${button.getAttribute("data-section")}Section`;
document.getElementById(sectionId)?.classList.remove("hidden"); document.getElementById(sectionId)?.classList.remove("hidden");
// Close sidebar on mobile after selection // Close sidebar and cleanup overlay on mobile
if (window.innerWidth < 1024 && sidebar) { if (window.innerWidth < 1024 && sidebar) {
sidebar.classList.add("-translate-x-full"); sidebar.classList.add("-translate-x-full");
document.body.classList.remove("overflow-hidden");
const overlay = document.getElementById("sidebarOverlay");
overlay?.remove();
} }
}); });
}); });