filters only the published events
This commit is contained in:
parent
4e1f3f6720
commit
6327ca498a
1 changed files with 815 additions and 868 deletions
|
@ -16,9 +16,7 @@ import JSZip from "jszip";
|
|||
<h3 class="card-title text-lg mb-4">Event Check-in</h3>
|
||||
<div class="form-control w-full">
|
||||
<label class="label">
|
||||
<span class="label-text"
|
||||
>Enter event code to check in</span
|
||||
>
|
||||
<span class="label-text">Enter event code to check in</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
|
@ -26,9 +24,7 @@ import JSZip from "jszip";
|
|||
placeholder="Enter code"
|
||||
class="input input-bordered flex-1"
|
||||
/>
|
||||
<button class="btn btn-primary min-w-[90px]"
|
||||
>Check In</button
|
||||
>
|
||||
<button class="btn btn-primary min-w-[90px]">Check In</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,9 +37,7 @@ import JSZip from "jszip";
|
|||
<form id="foodSelectionForm" class="space-y-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text"
|
||||
>What food would you like?</span
|
||||
>
|
||||
<span class="label-text">What food would you like?</span>
|
||||
<span class="label-text-alt text-error">*</span>
|
||||
</label>
|
||||
<input
|
||||
|
@ -61,9 +55,7 @@ import JSZip from "jszip";
|
|||
</label>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<button type="submit" class="btn btn-primary"
|
||||
>Submit</button
|
||||
>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
|
@ -83,15 +75,12 @@ import JSZip from "jszip";
|
|||
<h3 class="card-title text-lg mb-4">Event Registration</h3>
|
||||
<div class="form-control w-full">
|
||||
<label class="label">
|
||||
<span class="label-text"
|
||||
>Select an event to register</span
|
||||
>
|
||||
<span class="label-text">Select an event to register</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<select class="select select-bordered flex-1">
|
||||
<option disabled selected>Pick an event</option>
|
||||
<option>Technical Workshop - Web Development</option
|
||||
>
|
||||
<option>Technical Workshop - Web Development</option>
|
||||
<option>Professional Development Workshop</option>
|
||||
<option>Social Event - Game Night</option>
|
||||
</select>
|
||||
|
@ -186,14 +175,10 @@ import JSZip from "jszip";
|
|||
<div id="filePreviewSection" class="hidden">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick="backToFileList()"
|
||||
>
|
||||
<button class="btn btn-ghost btn-sm" onclick="backToFileList()">
|
||||
← Back
|
||||
</button>
|
||||
<h3 class="font-bold text-lg truncate" id="previewFileName">
|
||||
</h3>
|
||||
<h3 class="font-bold text-lg truncate" id="previewFileName"></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative" id="previewContainer">
|
||||
|
@ -222,19 +207,16 @@ import JSZip from "jszip";
|
|||
// Toast management system
|
||||
const createToast = (
|
||||
message: string,
|
||||
type: "success" | "error" | "warning" = "success"
|
||||
type: "success" | "error" | "warning" = "success",
|
||||
) => {
|
||||
let toastContainer = document.querySelector(".toast-container");
|
||||
if (!toastContainer) {
|
||||
toastContainer = document.createElement("div");
|
||||
toastContainer.className =
|
||||
"toast-container fixed bottom-4 right-4 z-50";
|
||||
toastContainer.className = "toast-container fixed bottom-4 right-4 z-50";
|
||||
document.body.appendChild(toastContainer);
|
||||
}
|
||||
|
||||
const existingToasts = document.querySelectorAll(
|
||||
".toast-container .toast"
|
||||
);
|
||||
const existingToasts = document.querySelectorAll(".toast-container .toast");
|
||||
if (existingToasts.length >= 2) {
|
||||
const oldestToast = existingToasts[0];
|
||||
oldestToast.classList.add("toast-exit");
|
||||
|
@ -244,9 +226,7 @@ import JSZip from "jszip";
|
|||
// Update positions of existing toasts
|
||||
existingToasts.forEach((t) => {
|
||||
const toast = t as HTMLElement;
|
||||
const currentIndex = parseInt(
|
||||
toast.getAttribute("data-index") || "0"
|
||||
);
|
||||
const currentIndex = parseInt(toast.getAttribute("data-index") || "0");
|
||||
toast.setAttribute("data-index", (currentIndex + 1).toString());
|
||||
});
|
||||
|
||||
|
@ -345,18 +325,14 @@ import JSZip from "jszip";
|
|||
// Find the event with the given code
|
||||
const event = await get.getFirst<Event>(
|
||||
"events",
|
||||
`event_code = "${eventCode}"`
|
||||
`event_code = "${eventCode}"`,
|
||||
);
|
||||
if (!event) {
|
||||
throw new Error("Invalid event code");
|
||||
}
|
||||
|
||||
// Check if user is already checked in
|
||||
if (
|
||||
event.attendees.some(
|
||||
(entry) => entry.user_id === currentUser.id
|
||||
)
|
||||
) {
|
||||
if (event.attendees.some((entry) => entry.user_id === currentUser.id)) {
|
||||
throw new Error("You have already checked in to this event");
|
||||
}
|
||||
|
||||
|
@ -370,7 +346,7 @@ import JSZip from "jszip";
|
|||
if (event.has_food) {
|
||||
currentCheckInEvent = event;
|
||||
const modal = document.getElementById(
|
||||
"foodSelectionModal"
|
||||
"foodSelectionModal",
|
||||
) as HTMLDialogElement;
|
||||
modal.showModal();
|
||||
} else {
|
||||
|
@ -378,49 +354,40 @@ import JSZip from "jszip";
|
|||
await completeCheckIn(event, null);
|
||||
}
|
||||
} catch (error: any) {
|
||||
createToast(
|
||||
error?.message || "Failed to check in to event",
|
||||
"error"
|
||||
);
|
||||
createToast(error?.message || "Failed to check in to event", "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Add food selection form handler
|
||||
const foodSelectionForm = document.getElementById(
|
||||
"foodSelectionForm"
|
||||
"foodSelectionForm",
|
||||
) as HTMLFormElement;
|
||||
if (foodSelectionForm) {
|
||||
foodSelectionForm.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const modal = document.getElementById(
|
||||
"foodSelectionModal"
|
||||
"foodSelectionModal",
|
||||
) as HTMLDialogElement;
|
||||
const foodInput = document.getElementById(
|
||||
"foodInput"
|
||||
"foodInput",
|
||||
) as HTMLInputElement;
|
||||
|
||||
try {
|
||||
if (currentCheckInEvent) {
|
||||
await completeCheckIn(
|
||||
currentCheckInEvent,
|
||||
foodInput.value.trim()
|
||||
);
|
||||
await completeCheckIn(currentCheckInEvent, foodInput.value.trim());
|
||||
modal.close();
|
||||
foodInput.value = ""; // Reset input
|
||||
currentCheckInEvent = null;
|
||||
}
|
||||
} catch (error: any) {
|
||||
createToast(
|
||||
error?.message || "Failed to check in to event",
|
||||
"error"
|
||||
);
|
||||
createToast(error?.message || "Failed to check in to event", "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function completeCheckIn(
|
||||
event: Event,
|
||||
foodSelection: string | null
|
||||
foodSelection: string | null,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const auth = Authentication.getInstance();
|
||||
|
@ -443,11 +410,7 @@ import JSZip from "jszip";
|
|||
const existingAttendees = event.attendees || [];
|
||||
|
||||
// Check if user is already checked in
|
||||
if (
|
||||
existingAttendees.some(
|
||||
(entry) => entry.user_id === currentUser.id
|
||||
)
|
||||
) {
|
||||
if (existingAttendees.some((entry) => entry.user_id === currentUser.id)) {
|
||||
throw new Error("You have already checked in to this event");
|
||||
}
|
||||
|
||||
|
@ -459,7 +422,7 @@ import JSZip from "jszip";
|
|||
"events",
|
||||
event.id,
|
||||
"attendees",
|
||||
updatedAttendees
|
||||
updatedAttendees,
|
||||
);
|
||||
|
||||
// If food selection was made, log it
|
||||
|
@ -467,7 +430,7 @@ import JSZip from "jszip";
|
|||
await logger.send(
|
||||
"update",
|
||||
"event check-in",
|
||||
`Food selection for ${event.event_name}: ${foodSelection}`
|
||||
`Food selection for ${event.event_name}: ${foodSelection}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -478,14 +441,14 @@ import JSZip from "jszip";
|
|||
"users",
|
||||
currentUser.id,
|
||||
"points",
|
||||
userPoints + event.points_to_reward
|
||||
userPoints + event.points_to_reward,
|
||||
);
|
||||
|
||||
// Log the points award
|
||||
await logger.send(
|
||||
"update",
|
||||
"event check-in",
|
||||
`Awarded ${event.points_to_reward} points for checking in to ${event.event_name}`
|
||||
`Awarded ${event.points_to_reward} points for checking in to ${event.event_name}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -496,20 +459,17 @@ import JSZip from "jszip";
|
|||
? ` (+${event.points_to_reward} points!)`
|
||||
: ""
|
||||
}`,
|
||||
"success"
|
||||
"success",
|
||||
);
|
||||
|
||||
// Log the check-in
|
||||
await logger.send(
|
||||
"check_in",
|
||||
"events",
|
||||
`User ${currentUser.name} (${currentUser.graduation_year}) checked in to event ${event.event_name}`
|
||||
`User ${currentUser.name} (${currentUser.graduation_year}) checked in to event ${event.event_name}`,
|
||||
);
|
||||
} catch (error: any) {
|
||||
createToast(
|
||||
error?.message || "Failed to check in to event",
|
||||
"error"
|
||||
);
|
||||
createToast(error?.message || "Failed to check in to event", "error");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,18 +516,17 @@ import JSZip from "jszip";
|
|||
try {
|
||||
// Show skeletons first
|
||||
const upcomingEventsContainer = document.getElementById(
|
||||
"upcomingEventsContainer"
|
||||
"upcomingEventsContainer",
|
||||
);
|
||||
const pastEventsContainer = document.getElementById(
|
||||
"pastEventsContainer"
|
||||
"pastEventsContainer",
|
||||
);
|
||||
if (!upcomingEventsContainer || !pastEventsContainer) return;
|
||||
|
||||
// Add 3 skeleton cards to each container initially
|
||||
const createSkeletonCard = () => {
|
||||
const skeletonCard = document.createElement("div");
|
||||
skeletonCard.className =
|
||||
"card bg-base-200 shadow-lg animate-pulse";
|
||||
skeletonCard.className = "card bg-base-200 shadow-lg animate-pulse";
|
||||
skeletonCard.innerHTML = `
|
||||
<div class="card-body p-5">
|
||||
<div class="flex flex-col h-full">
|
||||
|
@ -605,8 +564,8 @@ import JSZip from "jszip";
|
|||
const get = Get.getInstance();
|
||||
const events = await get.getAll<Event>(
|
||||
"events",
|
||||
undefined,
|
||||
"-start_date"
|
||||
"published = true",
|
||||
"-start_date",
|
||||
); // Sort by start date descending
|
||||
|
||||
// Clear skeletons
|
||||
|
@ -627,21 +586,21 @@ import JSZip from "jszip";
|
|||
startDate.getMonth(),
|
||||
startDate.getDate(),
|
||||
startDate.getHours(),
|
||||
startDate.getMinutes()
|
||||
startDate.getMinutes(),
|
||||
);
|
||||
const endLocal = new Date(
|
||||
endDate.getFullYear(),
|
||||
endDate.getMonth(),
|
||||
endDate.getDate(),
|
||||
endDate.getHours(),
|
||||
endDate.getMinutes()
|
||||
endDate.getMinutes(),
|
||||
);
|
||||
const nowLocal = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
now.getHours(),
|
||||
now.getMinutes()
|
||||
now.getMinutes(),
|
||||
);
|
||||
|
||||
if (startLocal > nowLocal) {
|
||||
|
@ -656,21 +615,19 @@ import JSZip from "jszip";
|
|||
}
|
||||
return acc;
|
||||
},
|
||||
{ upcoming: [] as Event[], past: [] as Event[] }
|
||||
{ upcoming: [] as Event[], past: [] as Event[] },
|
||||
);
|
||||
|
||||
// Sort upcoming events by start date (closest first)
|
||||
upcoming.sort(
|
||||
(a, b) =>
|
||||
new Date(a.start_date).getTime() -
|
||||
new Date(b.start_date).getTime()
|
||||
new Date(a.start_date).getTime() - new Date(b.start_date).getTime(),
|
||||
);
|
||||
|
||||
// Sort past events by end date (most recent first)
|
||||
past.sort(
|
||||
(a, b) =>
|
||||
new Date(b.end_date).getTime() -
|
||||
new Date(a.end_date).getTime()
|
||||
new Date(b.end_date).getTime() - new Date(a.end_date).getTime(),
|
||||
);
|
||||
|
||||
upcoming.forEach((event) => {
|
||||
|
@ -698,7 +655,7 @@ import JSZip from "jszip";
|
|||
weekday: "short",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div class="text-xs mt-0.5 opacity-75">
|
||||
|
@ -707,7 +664,7 @@ import JSZip from "jszip";
|
|||
{
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -724,7 +681,7 @@ import JSZip from "jszip";
|
|||
</div>
|
||||
${(() => {
|
||||
const endDate = Get.isUTCDateString(
|
||||
event.end_date
|
||||
event.end_date,
|
||||
)
|
||||
? new Date(event.end_date)
|
||||
: new Date();
|
||||
|
@ -780,7 +737,7 @@ import JSZip from "jszip";
|
|||
weekday: "short",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div class="text-xs mt-0.5 opacity-75">
|
||||
|
@ -789,7 +746,7 @@ import JSZip from "jszip";
|
|||
{
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -891,8 +848,7 @@ import JSZip from "jszip";
|
|||
}
|
||||
|
||||
function backToFileList() {
|
||||
const filePreviewSection =
|
||||
document.getElementById("filePreviewSection");
|
||||
const filePreviewSection = document.getElementById("filePreviewSection");
|
||||
const filesContent = document.getElementById("filesContent");
|
||||
const modalTitle = document.getElementById("modalTitle");
|
||||
|
||||
|
@ -901,13 +857,8 @@ import JSZip from "jszip";
|
|||
if (modalTitle) modalTitle.textContent = "Event Files";
|
||||
}
|
||||
|
||||
function showFilePreview(file: {
|
||||
url: string;
|
||||
type: string;
|
||||
name: string;
|
||||
}) {
|
||||
const filePreviewSection =
|
||||
document.getElementById("filePreviewSection");
|
||||
function showFilePreview(file: { url: string; type: string; name: string }) {
|
||||
const filePreviewSection = document.getElementById("filePreviewSection");
|
||||
const filesContent = document.getElementById("filesContent");
|
||||
const previewContent = document.getElementById("previewContent");
|
||||
const previewFileName = document.getElementById("previewFileName");
|
||||
|
@ -1023,13 +974,13 @@ import JSZip from "jszip";
|
|||
// Add openDetailsModal function
|
||||
window.openDetailsModal = function (event: any) {
|
||||
const modal = document.getElementById(
|
||||
"eventDetailsModal"
|
||||
"eventDetailsModal",
|
||||
) as HTMLDialogElement;
|
||||
const filesContent = document.getElementById(
|
||||
"filesContent"
|
||||
"filesContent",
|
||||
) as HTMLDivElement;
|
||||
const filePreviewSection = document.getElementById(
|
||||
"filePreviewSection"
|
||||
"filePreviewSection",
|
||||
) as HTMLDivElement;
|
||||
|
||||
// Reset state
|
||||
|
@ -1038,11 +989,7 @@ import JSZip from "jszip";
|
|||
if (filesContent) filesContent.classList.remove("hidden");
|
||||
|
||||
// Populate files content
|
||||
if (
|
||||
event.files &&
|
||||
Array.isArray(event.files) &&
|
||||
event.files.length > 0
|
||||
) {
|
||||
if (event.files && Array.isArray(event.files) && event.files.length > 0) {
|
||||
const baseUrl = "https://pocketbase.ieeeucsd.org";
|
||||
const collectionId = "events";
|
||||
const recordId = event.id;
|
||||
|
@ -1070,7 +1017,7 @@ import JSZip from "jszip";
|
|||
url: fileUrl,
|
||||
type: fileType,
|
||||
name: file,
|
||||
}
|
||||
},
|
||||
)})'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
|
||||
|
@ -1108,7 +1055,7 @@ import JSZip from "jszip";
|
|||
// Add downloadAllFiles function
|
||||
window.downloadAllFiles = async function () {
|
||||
const downloadBtn = document.getElementById(
|
||||
"downloadAllBtn"
|
||||
"downloadAllBtn",
|
||||
) as HTMLButtonElement;
|
||||
if (!downloadBtn) return;
|
||||
const originalBtnContent = downloadBtn.innerHTML;
|
||||
|
@ -1164,7 +1111,7 @@ import JSZip from "jszip";
|
|||
console.error("Failed to download files:", error);
|
||||
createToast(
|
||||
error?.message || "Failed to download files. Please try again.",
|
||||
"error"
|
||||
"error",
|
||||
);
|
||||
} finally {
|
||||
// Reset button state
|
||||
|
|
Loading…
Reference in a new issue