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,79 +7,104 @@ import EventLoad from "./EventsSection/EventLoad";
---
<div id="eventsSection" class="dashboard-section hidden">
<div class="mb-6">
<h2 class="text-2xl font-bold">Events</h2>
<p class="opacity-70">View and manage your IEEE UCSD events</p>
<div class="mb-4 sm:mb-6 px-4 sm:px-6">
<h2 class="text-xl sm:text-2xl font-bold">Events</h2>
<p class="opacity-70 text-sm sm:text-base">
View and manage your IEEE UCSD events
</p>
</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 -->
<div class="w-full">
<EventCheckIn client:load />
</div>
<!-- Event Registration Card -->
<div class="w-full">
<div
class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group"
class="card bg-base-100 shadow-xl border border-base-200 opacity-50 cursor-not-allowed relative group h-full"
>
<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"
>
<span class="text-base-content font-medium">Coming Soon</span>
<span
class="text-base-content font-medium text-sm sm:text-base"
>Coming Soon</span
>
</div>
<div class="card-body">
<h3 class="card-title text-lg mb-4">Event Registration</h3>
<div class="card-body p-4 sm:p-6">
<h3 class="card-title text-base sm:text-lg mb-3 sm:mb-4">
Event Registration
</h3>
<div class="form-control w-full">
<label class="label">
<span class="label-text"
<span class="label-text text-sm sm:text-base"
>Select an event to register</span
>
</label>
<div class="flex gap-2">
<select class="select select-bordered flex-1" disabled>
<option disabled selected>Pick an event</option>
<option>Technical Workshop - Web Development</option
<div class="flex flex-col sm:flex-row gap-2">
<select
class="select select-bordered flex-1 text-sm sm:text-base h-10 min-h-[2.5rem] w-full"
disabled
>
<option disabled selected>Pick an event</option>
<option
>Technical Workshop - Web Development</option
>
<option
>Professional Development Workshop</option
>
<option>Professional Development Workshop</option>
<option>Social Event - Game Night</option>
</select>
<button class="btn btn-primary" disabled
>Register</button
<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>
<EventLoad client:load />
</div>
<!-- Event Details Modal -->
<dialog id="eventDetailsModal" class="modal">
<div class="modal-box max-w-4xl">
<div class="flex justify-between items-center mb-4">
<div class="flex items-center gap-3">
<h3 class="font-bold text-lg" id="modalTitle">Event Files</h3>
<div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-2 sm:gap-3">
<h3 class="font-bold text-base sm:text-lg" id="modalTitle">
Event Files
</h3>
<button
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()"
>
<Icon
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
</button>
</div>
<button
class="btn btn-circle btn-ghost"
class="btn btn-circle btn-ghost btn-sm sm:btn-md"
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>
</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 -->
</div>
</div>
@ -90,14 +115,17 @@ import EventLoad from "./EventsSection/EventLoad";
<!-- Universal File Preview Modal -->
<dialog id="filePreviewModal" class="modal">
<div class="modal-box max-w-4xl">
<div class="flex justify-between items-center mb-4">
<div class="flex items-center gap-3">
<div class="modal-box max-w-[90vw] sm:max-w-4xl p-4 sm:p-6">
<div class="flex justify-between items-center mb-3 sm:mb-4">
<div class="flex items-center gap-2 sm:gap-3">
<button
class="btn btn-ghost btn-sm"
class="btn btn-ghost btn-sm text-xs sm:text-sm"
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>
</div>
</div>
@ -106,7 +134,8 @@ import EventLoad from "./EventsSection/EventLoad";
id="previewLoadingSpinner"
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 id="previewContent" class="w-full">
<FilePreview client:load isModal={true} />

View file

@ -240,12 +240,12 @@ const EventCheckIn = () => {
return (
<>
<div className="card bg-base-100 shadow-xl border border-base-200">
<div className="card-body">
<h3 className="card-title text-lg mb-4">Event Check-in</h3>
<div className="card bg-base-100 shadow-xl border border-base-200 h-full">
<div className="card-body p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Event Check-in</h3>
<div className="form-control w-full">
<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>
<form onSubmit={(e) => {
e.preventDefault();
@ -260,11 +260,11 @@ const EventCheckIn = () => {
createToast("Please enter an event code", "warning");
}
}}>
<div className="flex gap-2">
<div className="flex flex-col sm:flex-row gap-2">
<input
type="password"
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) => {
if (e.key === "Enter") {
e.preventDefault();
@ -273,7 +273,7 @@ const EventCheckIn = () => {
/>
<button
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}
>
{isLoading ? (
@ -289,35 +289,35 @@ const EventCheckIn = () => {
</div>
<dialog id="foodSelectionModal" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg mb-4">Food Selection</h3>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="modal-box max-w-[90vw] sm:max-w-lg p-4 sm:p-6">
<h3 className="font-bold text-base sm:text-lg mb-3 sm:mb-4">Food Selection</h3>
<form onSubmit={handleSubmit} className="space-y-3 sm:space-y-4">
<div className="form-control">
<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>
</label>
<input
type="text"
value={foodInput}
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'"
required
/>
<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
</span>
</label>
</div>
<div className="modal-action">
<button type="submit" className="btn btn-primary">
<div className="modal-action flex flex-col sm:flex-row gap-2 sm:gap-3">
<button type="submit" className="btn btn-primary text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-auto">
Submit
</button>
<button
type="button"
className="btn"
className="btn text-sm sm:text-base h-10 min-h-[2.5rem] w-full sm:w-auto"
onClick={() => {
const modal = document.getElementById("foodSelectionModal") as HTMLDialogElement;
modal.close();

View file

@ -88,24 +88,20 @@ const EventLoad = () => {
return (
<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 items-start justify-between gap-3 mb-2">
<div className="flex flex-col gap-2">
<div className="flex-1">
<h3 className="card-title 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">
<h3 className="card-title text-base sm:text-lg font-semibold mb-1 line-clamp-2">{event.event_name}</h3>
<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>
</div>
<div className="text-right shrink-0 text-base-content/80">
<div className="text-sm font-medium">
<div className="text-xs sm:text-sm opacity-75">
{startDate.toLocaleDateString("en-US", {
weekday: "short",
month: "short",
day: "numeric",
})}
</div>
<div className="text-xs mt-0.5 opacity-75">
{" • "}
{startDate.toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
@ -113,27 +109,28 @@ const EventLoad = () => {
</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"}
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 text-base-content/80">
<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 && (
<div className="flex flex-wrap items-center gap-2 mt-auto pt-2">
{event.files && event.files.length > 0 && (
<button
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="mdi:file-document-outline" className="w-4 h-4" />
<span>Files</span>
</div>
<Icon icon="heroicons:document-duplicate" className="h-3 w-3 sm:h-4 sm:w-4" />
Files ({event.files.length})
</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>
@ -147,7 +144,11 @@ const EventLoad = () => {
const allEvents = await get.getAll<Event>(
"events",
"published = true",
"-start_date"
"-start_date",
{
fields: ["*"],
disableAutoCancellation: true
}
);
// 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());
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);
} catch (error) {
console.error("Failed to load events:", error);
@ -214,10 +219,10 @@ const EventLoad = () => {
return (
<>
{/* 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-body">
<h3 className="card-title mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => (
<div key={`ongoing-skeleton-${i}`}>{createSkeletonCard()}</div>
))}
@ -226,10 +231,10 @@ const EventLoad = () => {
</div>
{/* 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-body">
<h3 className="card-title mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => (
<div key={`upcoming-skeleton-${i}`}>{createSkeletonCard()}</div>
))}
@ -238,10 +243,10 @@ const EventLoad = () => {
</div>
{/* 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-body">
<h3 className="card-title mb-4">Past Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 mx-4 sm:mx-6">
<div className="card-body p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Past Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{[...Array(3)].map((_, i) => (
<div key={`past-skeleton-${i}`}>{createSkeletonCard()}</div>
))}
@ -256,10 +261,10 @@ const EventLoad = () => {
<>
{/* Ongoing Events */}
{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-body">
<h3 className="card-title mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Ongoing Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.ongoing.map(renderEventCard)}
</div>
</div>
@ -268,10 +273,10 @@ const EventLoad = () => {
{/* Upcoming Events */}
{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-body">
<h3 className="card-title mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Upcoming Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.upcoming.map(renderEventCard)}
</div>
</div>
@ -280,10 +285,10 @@ const EventLoad = () => {
{/* Past Events */}
{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-body">
<h3 className="card-title mb-4">Past Events</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-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 mx-4 sm:mx-6">
<div className="card-body p-4 sm:p-6">
<h3 className="card-title text-base sm:text-lg mb-3 sm:mb-4">Past Events</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
{events.past.map(renderEventCard)}
</div>
</div>

View file

@ -71,23 +71,36 @@ const currentPage = eventResponse.page;
---
<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>
<h2 class="text-2xl font-bold">Event Management</h2>
<p class="opacity-70">Manage and create IEEE UCSD events</p>
<h2 class="text-xl md:text-2xl font-bold">Event Management</h2>
<p class="text-sm md:text-base opacity-70">
Manage and create IEEE UCSD events
</p>
</div>
</div>
<!-- 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
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-title font-medium opacity-80">
<div class="stat p-4 md:p-6">
<div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
Total Events
</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="badge badge-primary badge-sm" id="quarterLabel">
Current Academic Term
@ -98,11 +111,16 @@ const currentPage = eventResponse.page;
<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"
>
<div class="stat">
<div class="stat-title font-medium opacity-80">
<div class="stat p-4 md:p-6">
<div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
Unique Attendees
</div>
<div class="stat-value text-secondary" id="uniqueAttendees">
<div
class="stat-value text-secondary text-2xl md:text-3xl"
id="uniqueAttendees"
>
-
</div>
<div class="stat-desc flex items-center gap-2 mt-1">
@ -113,13 +131,18 @@ const currentPage = eventResponse.page;
</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 p-4 md:p-6">
<div
class="stat-title text-sm md:text-base font-medium opacity-80"
>
<div class="stat">
<div class="stat-title font-medium opacity-80">
Recurring Attendees
</div>
<div class="stat-value text-accent" id="recurringAttendees">
<div
class="stat-value text-accent text-2xl md:text-3xl"
id="recurringAttendees"
>
-
</div>
<div class="stat-desc flex items-center gap-2 mt-1">
@ -135,39 +158,50 @@ const currentPage = eventResponse.page;
<div
class="card bg-base-100 shadow-lg border border-base-200 hover:border-primary transition-all duration-300"
>
<div class="card-body">
<h3 class="card-title text-xl font-bold flex items-center gap-3">
<div class="card-body p-4 md:p-6">
<h3
class="card-title text-lg md:text-xl font-bold flex flex-col md:flex-row md:items-center gap-3"
>
<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 class="ml-auto justify-end flex gap-2">
</div>
<div class="flex-1 flex flex-wrap gap-2 md:justify-end">
<button
class="btn btn-ghost gap-2"
class="btn btn-ghost btn-sm md:btn-md gap-2"
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
</button>
<button
class="btn btn-primary gap-2"
class="btn btn-primary btn-sm md:btn-md gap-2"
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
</button>
</div>
</h3>
<div class="divider"></div>
<div class="divider my-2 md:my-4"></div>
<!-- Filter Controls -->
<div class="mb-4">
<!-- All Filters in One Line -->
<div class="flex flex-wrap items-end gap-4">
<div class="form-control">
<!-- All Filters -->
<div class="flex flex-wrap gap-4">
<div class="form-control w-full sm:w-auto">
<label class="label">
<span class="label-text font-medium"
<span
class="label-text text-sm md:text-base font-medium"
>Time Filter</span
>
</label>
@ -176,7 +210,7 @@ const currentPage = eventResponse.page;
type="radio"
name="timeFilter"
value="all"
class="join-item btn btn-sm"
class="join-item btn btn-xs md:btn-sm"
checked
aria-label="All Events"
/>
@ -184,29 +218,33 @@ const currentPage = eventResponse.page;
type="radio"
name="timeFilter"
value="ongoing"
class="join-item btn btn-sm"
class="join-item btn btn-xs md:btn-sm"
aria-label="Ongoing"
/>
<input
type="radio"
name="timeFilter"
value="upcoming"
class="join-item btn btn-sm"
class="join-item btn btn-xs md:btn-sm"
aria-label="Upcoming"
/>
<input
type="radio"
name="timeFilter"
value="past"
class="join-item btn btn-sm"
class="join-item btn btn-xs md:btn-sm"
aria-label="Past"
/>
</div>
</div>
<div class="form-control">
<!-- Other filters with similar responsive adjustments -->
<div class="form-control w-full sm:w-auto">
<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>
<div class="dropdown">
<label
@ -251,9 +289,12 @@ const currentPage = eventResponse.page;
</div>
</div>
<div class="form-control">
<div class="form-control w-full sm:w-auto">
<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>
<div class="dropdown">
<label
@ -278,8 +319,8 @@ const currentPage = eventResponse.page;
</svg>
</label>
<div
tabindex="0"
id="quarterDropdownContent"
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52"
>
<div class="form-control">
@ -339,9 +380,11 @@ const currentPage = eventResponse.page;
</div>
</div>
<div class="form-control">
<div class="form-control w-full sm:w-auto">
<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>
<div class="dropdown">
@ -404,9 +447,11 @@ const currentPage = eventResponse.page;
</div>
</div>
<div class="form-control">
<div class="form-control w-full sm:w-auto">
<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>
<div class="dropdown">
@ -469,9 +514,12 @@ const currentPage = eventResponse.page;
</div>
</div>
<div class="form-control">
<div class="form-control w-full sm:w-auto">
<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>
<div class="dropdown">
<label
@ -536,7 +584,7 @@ const currentPage = eventResponse.page;
</div>
<!-- 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="join w-full">
<div
@ -544,7 +592,7 @@ const currentPage = eventResponse.page;
>
<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"
fill="currentColor"
>
@ -558,20 +606,20 @@ const currentPage = eventResponse.page;
type="text"
id="searchInput"
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 class="form-control w-full md:w-auto">
<div class="form-control w-full sm:w-auto">
<div class="join">
<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
</div>
<select
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="10">10</option>
@ -583,33 +631,34 @@ const currentPage = eventResponse.page;
</div>
<!-- Event Items -->
<div class="space-y-4" id="eventsList">
<div class="text-center py-8 text-base-content/70">
<Icon
name="heroicons:calendar"
class="h-12 w-12 mx-auto mb-4 opacity-50"
/>
<p>Loading events...</p>
</div>
<div class="space-y-3 md:space-y-4" id="eventsList">
<!-- Event items will be populated here with responsive classes -->
</div>
<!-- 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">
<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
>
<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>
@ -1188,37 +1237,37 @@ const currentPage = eventResponse.page;
window[eventDataId] = event;
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 items-center gap-4">
<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-start sm:items-center gap-3 md:gap-4">
<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" />
</svg>
</div>
<div>
<h4 class="font-semibold">${event.event_name}</h4>
<p class="text-sm opacity-70">${dateStr}${detailsStr ? ` • ${detailsStr}` : ""}</p>
<div class="min-w-0">
<h4 class="font-semibold text-sm md:text-base truncate">${event.event_name}</h4>
<p class="text-xs md:text-sm opacity-70 truncate">${dateStr}${detailsStr ? ` • ${detailsStr}` : ""}</p>
</div>
</div>
<div class="flex gap-2">
<button class="btn btn-ghost 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">
<div class="flex flex-wrap gap-2 justify-end">
<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-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" />
<polyline points="14 2 14 8 20 8" />
</svg>
</button>
<button class="btn btn-ghost 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">
<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-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" />
</svg>
</button>
<button class="btn btn-ghost 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">
<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-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" />
</svg>
</button>
<button class="btn btn-ghost 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">
<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-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" />
</svg>
</button>

View file

@ -21,7 +21,7 @@ const title = "Dashboard";
<div class="flex h-screen">
<!-- Sidebar -->
<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 -->
<div class="p-6 border-b border-base-200">
@ -152,9 +152,13 @@ const title = "Dashboard";
</aside>
<!-- 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 -->
<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 gap-2">
<button
@ -169,11 +173,11 @@ const title = "Dashboard";
</header>
<!-- 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 -->
<div id="pageLoadingState" class="w-full">
<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>
@ -183,7 +187,7 @@ const title = "Dashboard";
<!-- Error State -->
<div id="pageErrorState" class="hidden w-full">
<div class="alert alert-error">
<div class="alert alert-error mx-2 sm:mx-0">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
@ -201,12 +205,14 @@ const title = "Dashboard";
<!-- Not Authenticated State -->
<div id="notAuthenticatedState" class="hidden w-full">
<div class="card bg-base-100 shadow-xl">
<div class="card-body items-center text-center">
<div class="mb-6">
<div class="card bg-base-100 shadow-xl mx-2 sm:mx-0">
<div
class="card-body items-center text-center p-4 sm:p-8"
>
<div class="mb-4 sm:mb-6">
<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"
fill="currentColor"
>
@ -216,15 +222,17 @@ const title = "Dashboard";
clip-rule="evenodd"></path>
</svg>
</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
</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
to access the dashboard.
</p>
<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
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"
clip-rule="evenodd"></path>
</svg>
Sign in with IEEEUCSD SSO
<span class="whitespace-nowrap"
>Sign in with IEEEUCSD SSO</span
>
</button>
</div>
</div>
</div>
<!-- Main Content -->
<div id="mainContent" class="hidden">
<div id="mainContent" class="hidden space-y-4 sm:space-y-6">
<ProfileSection />
<EventsSection />
<ReimbursementSection />
@ -291,37 +301,75 @@ const title = "Dashboard";
});
// Update display elements
if (userName) {
userName.textContent = extendedUser.name || "Unknown User";
}
if (userRole) {
userRole.textContent = extendedUser.member_type || "Member";
}
if (userInitials) {
const displayName = extendedUser.name || "Unknown User";
const displayRole = extendedUser.member_type || "Member";
const initials = (extendedUser.name || "U")
.split(" ")
.map((n: string) => n[0])
.join("")
.toUpperCase();
userInitials.textContent = initials;
}
// Update elements
if (userName) userName.textContent = displayName;
if (userRole) userRole.textContent = displayRole;
if (userInitials) userInitials.textContent = initials;
} catch (error) {
console.error("Error fetching user profile:", error);
if (userName) userName.textContent = "Unknown User";
if (userRole) userRole.textContent = "Member";
if (userInitials) userInitials.textContent = "?";
const fallbackValues = {
name: "Unknown User",
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");
if (mobileSidebarToggle && sidebar) {
mobileSidebarToggle.addEventListener("click", () => {
sidebar.classList.toggle("-translate-x-full");
});
const toggleSidebar = () => {
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
const handleNavigation = () => {
@ -343,9 +391,12 @@ const title = "Dashboard";
const sectionId = `${button.getAttribute("data-section")}Section`;
document.getElementById(sectionId)?.classList.remove("hidden");
// Close sidebar on mobile after selection
// Close sidebar and cleanup overlay on mobile
if (window.innerWidth < 1024 && sidebar) {
sidebar.classList.add("-translate-x-full");
document.body.classList.remove("overflow-hidden");
const overlay = document.getElementById("sidebarOverlay");
overlay?.remove();
}
});
});