"use client"; import { useEffect, useState, useCallback } from "react"; import { Permission } from "../../auth-context"; import { api } from "../../api"; import { sharedStyles } from "../../styles/shared"; import { Header } from "../../components/Header"; import { useRequireAuth } from "../../hooks/useRequireAuth"; import { components } from "../../generated/api"; type AppointmentResponse = components["schemas"]["AppointmentResponse"]; // Helper to format datetime function formatDateTime(isoString: string): string { const d = new Date(isoString); return d.toLocaleString("en-US", { weekday: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: false, }); } // Helper to get status display function getStatusDisplay(status: string): { text: string; color: string } { switch (status) { case "booked": return { text: "Booked", color: "#28a745" }; case "cancelled_by_user": return { text: "Cancelled by user", color: "#dc3545" }; case "cancelled_by_admin": return { text: "Cancelled by admin", color: "#dc3545" }; default: return { text: status, color: "#666" }; } } export default function AdminAppointmentsPage() { const { user, isLoading, isAuthorized } = useRequireAuth({ requiredPermission: Permission.VIEW_ALL_APPOINTMENTS, fallbackRedirect: "/", }); const [appointments, setAppointments] = useState([]); const [isLoadingAppointments, setIsLoadingAppointments] = useState(true); const [cancellingId, setCancellingId] = useState(null); const [confirmCancelId, setConfirmCancelId] = useState(null); const [error, setError] = useState(null); const [statusFilter, setStatusFilter] = useState("all"); const fetchAppointments = useCallback(async () => { try { const data = await api.get("/api/admin/appointments"); setAppointments(data); } catch (err) { console.error("Failed to fetch appointments:", err); setError("Failed to load appointments"); } finally { setIsLoadingAppointments(false); } }, []); useEffect(() => { if (user && isAuthorized) { fetchAppointments(); } }, [user, isAuthorized, fetchAppointments]); const handleCancel = async (appointmentId: number) => { setCancellingId(appointmentId); setError(null); try { await api.post(`/api/admin/appointments/${appointmentId}/cancel`, {}); await fetchAppointments(); setConfirmCancelId(null); } catch (err) { if (err instanceof Error) { setError(err.message); } else { setError("Failed to cancel appointment"); } } finally { setCancellingId(null); } }; if (isLoading) { return (

Loading...

); } if (!isAuthorized) { return null; } const filteredAppointments = appointments.filter((apt) => { if (statusFilter === "all") return true; return apt.status === statusFilter; }); const bookedCount = appointments.filter((a) => a.status === "booked").length; const cancelledCount = appointments.filter((a) => a.status !== "booked").length; return (

All Appointments

View and manage all user appointments

{error && (
{error}
)} {/* Status Filter */}
{isLoadingAppointments ? (

Loading appointments...

) : appointments.length === 0 ? (

No appointments yet.

) : filteredAppointments.length === 0 ? (

No appointments match the filter.

) : (
{filteredAppointments.map((apt) => { const status = getStatusDisplay(apt.status); const isPast = new Date(apt.slot_start) <= new Date(); return (
{formatDateTime(apt.slot_start)}
User: {apt.user_email}
{apt.note && (
"{apt.note}"
)} {status.text}
{apt.status === "booked" && (
{confirmCancelId === apt.id ? (
) : ( )}
)}
); })}
)}
); }