Fix toast hydration

This commit is contained in:
chark1es 2025-03-01 04:37:33 -08:00
parent eb77c00540
commit ef3e8f38d6
9 changed files with 855 additions and 931 deletions

View file

@ -2,6 +2,9 @@
import FilePreview from "./universal/FilePreview";
import EventCheckIn from "./EventsSection/EventCheckIn";
import EventLoad from "./EventsSection/EventLoad";
import { Icon } from "astro-icon/components";
import { Get } from "../../scripts/pocketbase/Get";
import { toast } from "react-hot-toast";
---
<div id="" class="">
@ -28,7 +31,8 @@ import EventLoad from "./EventsSection/EventLoad";
<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 text-sm sm:text-base"
<span
class="text-base-content font-medium text-sm sm:text-base"
>Coming Soon</span
>
</div>
@ -48,8 +52,12 @@ import EventLoad from "./EventsSection/EventLoad";
disabled
>
<option disabled selected>Pick an event</option>
<option>Technical Workshop - Web Development</option>
<option>Professional Development Workshop</option>
<option
>Technical Workshop - Web Development</option
>
<option
>Professional Development Workshop</option
>
<option>Social Event - Game Night</option>
</select>
<button
@ -89,8 +97,9 @@ import EventLoad from "./EventsSection/EventLoad";
class="btn btn-circle btn-ghost btn-sm sm:btn-md"
onclick="window.closeEventDetailsModal()"
>
<iconify-icon icon="heroicons:x-mark" className="h-4 w-4 sm:h-6 sm:w-6"
></iconify-icon>
<iconify-icon
icon="heroicons:x-mark"
className="h-4 w-4 sm:h-6 sm:w-6"></iconify-icon>
</button>
</div>
@ -124,7 +133,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-md sm: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} />
@ -137,109 +147,26 @@ import EventLoad from "./EventsSection/EventLoad";
</dialog>
<script>
import { toast } from "react-hot-toast";
import JSZip from "jszip";
// Toast management system
const createToast = (
message: string,
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";
document.body.appendChild(toastContainer);
}
const existingToasts = document.querySelectorAll(".toast-container .toast");
if (existingToasts.length >= 2) {
const oldestToast = existingToasts[0];
oldestToast.classList.add("toast-exit");
setTimeout(() => oldestToast.remove(), 150);
}
// Update positions of existing toasts
existingToasts.forEach((t) => {
const toast = t as HTMLElement;
const currentIndex = parseInt(toast.getAttribute("data-index") || "0");
toast.setAttribute("data-index", (currentIndex + 1).toString());
});
const toast = document.createElement("div");
toast.className = "toast translate-x-full";
toast.setAttribute("data-index", "0");
// Update alert styling based on type
const alertClass =
type === "success"
? "alert-success bg-success text-success-content"
: type === "error"
? "alert-error bg-error text-error-content"
: "alert-warning bg-warning text-warning-content";
toast.innerHTML = `
<div class="alert ${alertClass} shadow-lg min-w-[300px]">
<div class="flex items-center gap-2">
${
type === "success"
? '<iconify-icon icon="heroicons:check-circle" class="stroke-current shrink-0 h-6 w-6"></iconify-icon>'
: type === "error"
? '<iconify-icon icon="heroicons:x-circle" class="stroke-current shrink-0 h-6 w-6"></iconify-icon>'
: '<iconify-icon icon="heroicons:exclamation-triangle" class="stroke-current shrink-0 w-6"></iconify-icon>'
}
<span>${message}</span>
</div>
</div>
`;
toastContainer.appendChild(toast);
// Force a reflow to ensure the animation triggers
toast.offsetHeight;
// Add the transition class and remove transform
toast.classList.add("transition-all", "duration-300", "ease-out");
requestAnimationFrame(() => {
toast.classList.remove("translate-x-full");
});
// Setup exit animation
setTimeout(() => {
toast.classList.add("toast-exit");
setTimeout(() => toast.remove(), 150);
}, 3000);
};
// Add styles to the document
const style = document.createElement("style");
style.textContent = `
.toast-container {
display: flex;
flex-direction: column;
pointer-events: none;
/* Custom styles for the event details modal */
.event-details-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.toast {
pointer-events: auto;
transform: translateX(0);
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
@media (max-width: 640px) {
.event-details-grid {
grid-template-columns: 1fr;
}
.toast-exit {
transform: translateX(100%);
opacity: 0;
}
.toast.translate-x-full {
transform: translateX(100%);
}
.toast-container .toast {
transform: translateY(calc((1 - attr(data-index number)) * -0.25rem));
}
.toast-container .toast[data-index="0"] {
transform: translateY(0);
}
.toast-container .toast[data-index="1"] {
transform: translateY(-0.025rem);
}
/* Remove custom toast styles since we're using react-hot-toast */
`;
document.head.appendChild(style);
@ -269,7 +196,7 @@ import EventLoad from "./EventsSection/EventLoad";
window.previewFileEvents = function (url: string, filename: string) {
console.log("previewFileEvents called with:", { url, filename });
const modal = document.getElementById(
"filePreviewModal",
"filePreviewModal"
) as HTMLDialogElement;
const previewFileName = document.getElementById("previewFileName");
const previewContent = document.getElementById("previewContent");
@ -286,7 +213,7 @@ import EventLoad from "./EventsSection/EventLoad";
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url, filename },
}),
})
);
}
};
@ -295,7 +222,7 @@ import EventLoad from "./EventsSection/EventLoad";
window.closeFilePreviewEvents = function () {
console.log("closeFilePreviewEvents called");
const modal = document.getElementById(
"filePreviewModal",
"filePreviewModal"
) as HTMLDialogElement;
const previewFileName = document.getElementById("previewFileName");
const previewContent = document.getElementById("previewContent");
@ -306,7 +233,7 @@ import EventLoad from "./EventsSection/EventLoad";
window.dispatchEvent(
new CustomEvent("filePreviewStateChange", {
detail: { url: "", filename: "" },
}),
})
);
// Reset the UI
@ -329,10 +256,10 @@ import EventLoad from "./EventsSection/EventLoad";
// Update the openDetailsModal function to use the events-specific preview
window.openDetailsModal = function (event: any) {
const modal = document.getElementById(
"eventDetailsModal",
"eventDetailsModal"
) as HTMLDialogElement;
const filesContent = document.getElementById(
"filesContent",
"filesContent"
) as HTMLDivElement;
// Check if event has ended
@ -340,10 +267,14 @@ import EventLoad from "./EventsSection/EventLoad";
const now = new Date();
if (eventEndDate > now) {
createToast(
"Files are only available after the event has ended.",
"warning",
);
toast("Files are only available after the event has ended.", {
icon: "⚠️",
style: {
borderRadius: "10px",
background: "#FFC107",
color: "#000",
},
});
return;
}
@ -352,7 +283,11 @@ import EventLoad from "./EventsSection/EventLoad";
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;
@ -409,7 +344,7 @@ import EventLoad from "./EventsSection/EventLoad";
// Add downloadAllFiles function
window.downloadAllFiles = async function () {
const downloadBtn = document.getElementById(
"downloadAllBtn",
"downloadAllBtn"
) as HTMLButtonElement;
if (!downloadBtn) return;
const originalBtnContent = downloadBtn.innerHTML;
@ -460,12 +395,11 @@ import EventLoad from "./EventsSection/EventLoad";
URL.revokeObjectURL(downloadUrl);
// Show success message
createToast("Files downloaded successfully!", "success");
toast.success("Files downloaded successfully!");
} catch (error: any) {
console.error("Failed to download files:", error);
createToast(
error?.message || "Failed to download files. Please try again.",
"error",
toast.error(
error?.message || "Failed to download files. Please try again."
);
} finally {
// Reset button state
@ -477,7 +411,7 @@ import EventLoad from "./EventsSection/EventLoad";
// Close event details modal
window.closeEventDetailsModal = function () {
const modal = document.getElementById(
"eventDetailsModal",
"eventDetailsModal"
) as HTMLDialogElement;
const filesContent = document.getElementById("filesContent");

View file

@ -6,6 +6,7 @@ import { SendLog } from "../../../scripts/pocketbase/SendLog";
import { DataSyncService } from "../../../scripts/database/DataSyncService";
import { Collections } from "../../../schemas/pocketbase/schema";
import { Icon } from "@iconify/react";
import toast from "react-hot-toast";
import type { Event, AttendeeEntry } from "../../../schemas/pocketbase";
// Extended Event interface with additional properties needed for this component
@ -17,77 +18,6 @@ interface ExtendedEvent extends Event {
// When fetching events, UTC dates are converted to local time.
// When saving events, local dates are converted back to UTC.
// Toast management system
const createToast = (
message: string,
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";
document.body.appendChild(toastContainer);
}
const existingToasts = document.querySelectorAll(".toast-container .toast");
if (existingToasts.length >= 2) {
const oldestToast = existingToasts[0];
oldestToast.classList.add("toast-exit");
setTimeout(() => oldestToast.remove(), 150);
}
// Update positions of existing toasts
existingToasts.forEach((t) => {
const toast = t as HTMLElement;
const currentIndex = parseInt(toast.getAttribute("data-index") || "0");
toast.setAttribute("data-index", (currentIndex + 1).toString());
});
const toast = document.createElement("div");
toast.className = "toast translate-x-full";
toast.setAttribute("data-index", "0");
// Update alert styling based on type
const alertClass =
type === "success"
? "alert-success bg-success text-success-content"
: type === "error"
? "alert-error bg-error text-error-content"
: "alert-warning bg-warning text-warning-content";
const iconName = type === "success"
? "heroicons:check-circle"
: type === "error"
? "heroicons:x-circle"
: "heroicons:exclamation-triangle";
toast.innerHTML = `
<div class="alert ${alertClass} shadow-lg min-w-[300px]">
<div class="flex items-center gap-2">
<iconify-icon icon="${iconName}" width="20" height="20"></iconify-icon>
<span>${message}</span>
</div>
</div>
`;
toastContainer.appendChild(toast);
// Force a reflow to ensure the animation triggers
toast.offsetHeight;
// Add the transition class and remove transform
toast.classList.add("transition-all", "duration-300", "ease-out");
requestAnimationFrame(() => {
toast.classList.remove("translate-x-full");
});
// Setup exit animation
setTimeout(() => {
toast.classList.add("toast-exit");
setTimeout(() => toast.remove(), 150);
}, 3000);
};
const EventCheckIn = () => {
const [currentCheckInEvent, setCurrentCheckInEvent] = useState<Event | null>(null);
const [isLoading, setIsLoading] = useState(false);
@ -101,7 +31,7 @@ const EventCheckIn = () => {
const currentUser = auth.getCurrentUser();
if (!currentUser) {
createToast("You must be logged in to check in to events", "error");
toast.error("You must be logged in to check in to events");
return;
}
@ -151,7 +81,7 @@ const EventCheckIn = () => {
await completeCheckIn(event, null);
}
} catch (error: any) {
createToast(error?.message || "Failed to check in to event", "error");
toast.error(error?.message || "Failed to check in to event");
}
}
@ -171,7 +101,7 @@ const EventCheckIn = () => {
const userId = auth.getUserId();
if (!userId) {
createToast("You must be logged in to check in to an event", "error");
toast.error("You must be logged in to check in to an event");
return;
}
@ -184,7 +114,14 @@ const EventCheckIn = () => {
);
if (isAlreadyCheckedIn) {
createToast("You are already checked in to this event", "warning");
toast("You are already checked in to this event", {
icon: '⚠️',
style: {
borderRadius: '10px',
background: '#FFC107',
color: '#000',
},
});
return;
}
@ -243,12 +180,11 @@ const EventCheckIn = () => {
}
// Show success message with points if awarded
createToast(
toast.success(
`Successfully checked in to ${event.event_name}${event.points_to_reward > 0
? ` (+${event.points_to_reward} points!)`
: ""
}`,
"success"
}`
);
// Log the check-in
@ -265,7 +201,7 @@ const EventCheckIn = () => {
setFoodInput("");
}
} catch (error: any) {
createToast(error?.message || "Failed to check in to event", "error");
toast.error(error?.message || "Failed to check in to event");
}
}
@ -296,7 +232,14 @@ const EventCheckIn = () => {
input.value = "";
});
} else {
createToast("Please enter an event code", "warning");
toast("Please enter an event code", {
icon: '⚠️',
style: {
borderRadius: '10px',
background: '#FFC107',
color: '#000',
},
});
}
}}>
<div className="flex flex-col sm:flex-row gap-2">

View file

@ -4,7 +4,10 @@ import { Get } from "../../scripts/pocketbase/Get";
import EventRequestForm from "./Officer_EventRequestForm/EventRequestForm";
import UserEventRequests from "./Officer_EventRequestForm/UserEventRequests";
import { Collections } from "../../schemas/pocketbase/schema";
import { Toaster } from "react-hot-toast";
import { Icon } from "astro-icon/components";
import { Create } from "../../scripts/pocketbase/Create";
import { Update } from "../../scripts/pocketbase/Update";
import { toast } from "react-hot-toast";
// Import the EventRequest type from UserEventRequests to ensure consistency
import type { EventRequest as UserEventRequest } from "./Officer_EventRequestForm/UserEventRequests";
@ -118,8 +121,9 @@ if (auth.isAuthenticated()) {
</div>
</div>
<!-- Toast container for notifications -->
<Toaster client:load position="bottom-right" />
<div class="dashboard-section hidden" id="eventRequestFormSection">
<!-- ... existing code ... -->
</div>
<script>
// Import the DataSyncService for client-side use

View file

@ -1,10 +1,11 @@
---
import { Authentication } from "../../scripts/pocketbase/Authentication";
import { Get } from "../../scripts/pocketbase/Get";
import { Toaster } from "react-hot-toast";
import { toast } from "react-hot-toast";
import EventRequestManagementTable from "./Officer_EventRequestManagement/EventRequestManagementTable";
import type { EventRequest } from "../../schemas/pocketbase";
import { Collections } from "../../schemas/pocketbase/schema";
import { Icon } from "astro-icon/components";
// Get instances
const get = Get.getInstance();
@ -152,9 +153,6 @@ try {
</div>
)
}
<!-- Toast container for notifications -->
<Toaster client:load position="bottom-right" />
</div>
<script>

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Icon } from '@iconify/react';
import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { DataSyncService } from '../../../scripts/database/DataSyncService';
@ -7,7 +7,6 @@ import ReceiptForm from './ReceiptForm';
import { toast } from 'react-hot-toast';
import { motion, AnimatePresence } from 'framer-motion';
import FilePreview from '../universal/FilePreview';
import ToastProvider from './ToastProvider';
import type { ItemizedExpense, Reimbursement, Receipt } from '../../../schemas/pocketbase';
interface ReceiptFormData {
@ -310,7 +309,6 @@ export default function ReimbursementForm() {
return (
<>
<ToastProvider />
<motion.div
variants={containerVariants}
initial="hidden"

View file

@ -6,7 +6,6 @@ import { FileManager } from '../../../scripts/pocketbase/FileManager';
import FilePreview from '../universal/FilePreview';
import { toast } from 'react-hot-toast';
import { motion, AnimatePresence } from 'framer-motion';
import ToastProvider from './ToastProvider';
import type { ItemizedExpense, Reimbursement, Receipt } from '../../../schemas/pocketbase';
import { DataSyncService } from '../../../scripts/database/DataSyncService';
import { Collections } from '../../../schemas/pocketbase/schema';
@ -355,7 +354,6 @@ export default function ReimbursementList() {
return (
<>
<ToastProvider />
<motion.div
variants={containerVariants}
initial="hidden"

View file

@ -1,19 +0,0 @@
import { Toaster } from 'react-hot-toast';
export default function ToastProvider() {
return (
<Toaster
position="top-center"
reverseOrder={false}
toastOptions={{
duration: 5000,
style: {
background: '#333',
color: '#fff',
padding: '16px',
borderRadius: '8px',
},
}}
/>
);
}

View file

@ -0,0 +1,31 @@
import React from 'react';
import { Toaster } from 'react-hot-toast';
// Centralized toast provider to ensure consistent rendering
export default function ToastProvider() {
return (
<Toaster
position="bottom-right"
toastOptions={{
duration: 4000,
style: {
background: '#333',
color: '#fff',
borderRadius: '8px',
padding: '12px',
},
success: {
style: {
background: 'green',
},
},
error: {
style: {
background: 'red',
},
duration: 5000,
},
}}
/>
);
}

View file

@ -9,6 +9,7 @@ import { SendLog } from "../scripts/pocketbase/SendLog";
import { hasAccess, type OfficerStatus } from "../utils/roleAccess";
import { OfficerTypes } from "../schemas/pocketbase/schema";
import { initAuthSync } from "../scripts/database/initAuthSync";
import ToastProvider from "../components/dashboard/universal/ToastProvider";
const title = "Dashboard";
@ -365,10 +366,11 @@ console.log("Available components:", Object.keys(components)); // Debug log
</div>
</main>
</div>
</body>
</html>
<script>
<!-- Centralized Toast Provider -->
<ToastProvider client:load />
<script>
import { Authentication } from "../scripts/pocketbase/Authentication";
import { Get } from "../scripts/pocketbase/Get";
import { SendLog } from "../scripts/pocketbase/SendLog";
@ -381,7 +383,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
const logger = SendLog.getInstance();
// Initialize page state
const pageLoadingState = document.getElementById("pageLoadingState");
const pageLoadingState =
document.getElementById("pageLoadingState");
const pageErrorState = document.getElementById("pageErrorState");
const notAuthenticatedState = document.getElementById(
"notAuthenticatedState"
@ -415,7 +418,9 @@ console.log("Available components:", Object.keys(components)); // Debug log
}
// For non-sponsor roles, handle normally
document.querySelectorAll("[data-role-required]").forEach((element) => {
document
.querySelectorAll("[data-role-required]")
.forEach((element) => {
const requiredRole = element.getAttribute(
"data-role-required"
) as OfficerStatus;
@ -427,7 +432,10 @@ console.log("Available components:", Object.keys(components)); // Debug log
}
// Check if user has permission for this role
const hasPermission = hasAccess(officerStatus, requiredRole);
const hasPermission = hasAccess(
officerStatus,
requiredRole
);
// Only show elements if user has permission
element.classList.toggle("hidden", !hasPermission);
@ -436,8 +444,10 @@ console.log("Available components:", Object.keys(components)); // Debug log
// Handle navigation
const handleNavigation = () => {
const navButtons = document.querySelectorAll(".dashboard-nav-btn");
const sections = document.querySelectorAll(".dashboard-section");
const navButtons =
document.querySelectorAll(".dashboard-nav-btn");
const sections =
document.querySelectorAll(".dashboard-section");
const mainContentDiv = document.getElementById("mainContent");
// Ensure mainContent is visible
@ -471,7 +481,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
// Show selected section
const sectionId = `${sectionKey}Section`;
const targetSection = document.getElementById(sectionId);
const targetSection =
document.getElementById(sectionId);
if (targetSection) {
targetSection.classList.remove("hidden");
console.log(`Showing section: ${sectionId}`); // Debug log
@ -481,7 +492,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
if (window.innerWidth < 1024 && sidebar) {
sidebar.classList.add("-translate-x-full");
document.body.classList.remove("overflow-hidden");
const overlay = document.getElementById("sidebarOverlay");
const overlay =
document.getElementById("sidebarOverlay");
overlay?.remove();
}
});
@ -581,15 +593,19 @@ console.log("Available components:", Object.keys(components)); // Debug log
};
// Mobile sidebar toggle
const mobileSidebarToggle = document.getElementById("mobileSidebarToggle");
const mobileSidebarToggle = document.getElementById(
"mobileSidebarToggle"
);
if (mobileSidebarToggle && sidebar) {
const toggleSidebar = () => {
const isOpen = !sidebar.classList.contains("-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");
const overlay = document.getElementById("sidebarOverlay");
const overlay =
document.getElementById("sidebarOverlay");
overlay?.remove();
} else {
sidebar.classList.remove("-translate-x-full");
@ -615,13 +631,15 @@ console.log("Available components:", Object.keys(components)); // Debug log
// Check if user is authenticated
if (!auth.isAuthenticated()) {
console.log("User not authenticated");
if (pageLoadingState) pageLoadingState.classList.add("hidden");
if (pageLoadingState)
pageLoadingState.classList.add("hidden");
if (notAuthenticatedState)
notAuthenticatedState.classList.remove("hidden");
return;
}
if (pageLoadingState) pageLoadingState.classList.remove("hidden");
if (pageLoadingState)
pageLoadingState.classList.remove("hidden");
if (pageErrorState) pageErrorState.classList.add("hidden");
if (notAuthenticatedState)
notAuthenticatedState.classList.add("hidden");
@ -642,7 +660,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
if (userProfileSkeleton)
userProfileSkeleton.classList.remove("hidden");
if (userProfileSummary) userProfileSummary.classList.add("hidden");
if (userProfileSummary)
userProfileSummary.classList.add("hidden");
if (userProfileSignedOut)
userProfileSignedOut.classList.add("hidden");
if (menuLoadingSkeleton)
@ -705,16 +724,23 @@ console.log("Available components:", Object.keys(components)); // Debug log
officerStatus = "none";
}
} else {
const extendedUser = await get.getOne("users", user.id, {
const extendedUser = await get.getOne(
"users",
user.id,
{
fields: ["member_type"],
});
}
);
if (extendedUser.member_type === "Sponsor") {
officerStatus = "sponsor";
}
}
} catch (error) {
console.error("Error determining officer status:", error);
console.error(
"Error determining officer status:",
error
);
officerStatus = "none";
}
@ -736,7 +762,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
'[data-section="adminDashboard"]'
);
} else {
defaultSection = document.getElementById("profileSection");
defaultSection =
document.getElementById("profileSection");
defaultButton = document.querySelector(
'[data-section="profile"]'
);
@ -759,11 +786,14 @@ console.log("Available components:", Object.keys(components)); // Debug log
// Show main content and hide loading
if (mainContent) mainContent.classList.remove("hidden");
if (pageLoadingState) pageLoadingState.classList.add("hidden");
if (pageLoadingState)
pageLoadingState.classList.add("hidden");
} catch (error) {
console.error("Error initializing dashboard:", error);
if (pageLoadingState) pageLoadingState.classList.add("hidden");
if (pageErrorState) pageErrorState.classList.remove("hidden");
if (pageLoadingState)
pageLoadingState.classList.add("hidden");
if (pageErrorState)
pageErrorState.classList.remove("hidden");
}
};
@ -782,13 +812,17 @@ console.log("Available components:", Object.keys(components)); // Debug log
await auth.login();
} catch (error) {
console.error("Login error:", error);
if (pageLoadingState) pageLoadingState.classList.add("hidden");
if (pageErrorState) pageErrorState.classList.remove("hidden");
if (pageLoadingState)
pageLoadingState.classList.add("hidden");
if (pageErrorState)
pageErrorState.classList.remove("hidden");
}
});
// Handle logout button click
document.getElementById("logoutButton")?.addEventListener("click", () => {
document
.getElementById("logoutButton")
?.addEventListener("click", () => {
auth.logout();
window.location.reload();
});
@ -801,7 +835,8 @@ console.log("Available components:", Object.keys(components)); // Debug log
window.addEventListener("resize", () => {
if (window.innerWidth >= 1024) {
const overlay = document.getElementById("sidebarOverlay");
const overlay =
document.getElementById("sidebarOverlay");
if (overlay) {
overlay.remove();
document.body.classList.remove("overflow-hidden");
@ -810,4 +845,6 @@ console.log("Available components:", Object.keys(components)); // Debug log
}
});
}
</script>
</script>
</body>
</html>