import type { Metadata } from "next"; import { CalendarPlus, LogIn } from "lucide-react"; import Link from "next/link"; import { auth } from "@/auth"; import { AppointmentCard } from "@/components/ui/appointment-card"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { EmptyState } from "@/components/feedback/empty-state"; import { TopNav } from "@/components/navigation/top-nav"; import { getPrimaryBusiness } from "@/lib/business/get-primary-business"; import { prisma } from "@/lib/db/prisma"; import { listAppointments } from "@/services/scheduling/appointment.service"; import { getUnreadNotificationCount } from "@/services/notifications/notification.service"; import { BookAgainButton } from "@/features/booking/components/book-again-button"; import { CancelAppointmentDialog } from "@/features/booking/components/cancel-appointment-dialog"; import { InlineNotificationBell } from "@/features/notifications/components/notification-bell"; import type { AppointmentStatus } from "@/types/domain"; export const metadata: Metadata = { title: "Appointments" }; export const dynamic = "force-dynamic"; const STATUS_MAP: Record = { PENDING: "pending_approval", CONFIRMED: "confirmed", CHECKED_IN: "arrived", IN_PROGRESS: "in_progress", COMPLETED: "completed", CANCELLED: "cancelled", NO_SHOW: "no_show", }; const UPCOMING_STATUSES = ["PENDING", "CONFIRMED", "CHECKED_IN", "IN_PROGRESS"] as const; const CANCELLED_STATUSES = ["CANCELLED", "NO_SHOW"] as const; const RESCHEDULABLE_STATUSES = new Set(["PENDING", "CONFIRMED", "CHECKED_IN"]); const CANCELLABLE_STATUSES = new Set(["PENDING", "CONFIRMED", "CHECKED_IN"]); export default async function AppointmentsPage() { const session = await auth(); if (!session?.user) { return (
Sign In } />
); } const [business, unreadNotificationCount] = await Promise.all([ getPrimaryBusiness(), getUnreadNotificationCount(prisma, session.user.id), ]); const customerProfile = await prisma.customerProfile.findUnique({ where: { userId: session.user.id }, }); const bell = ; if (!customerProfile) { return (
Book Now } />
); } const [upcoming, past, cancelled] = await Promise.all([ listAppointments(prisma, { customerId: customerProfile.id, statuses: [...UPCOMING_STATUSES] }), listAppointments(prisma, { customerId: customerProfile.id, statuses: ["COMPLETED"] }), listAppointments(prisma, { customerId: customerProfile.id, statuses: [...CANCELLED_STATUSES] }), ]); const renderList = (appointments: typeof upcoming, kind: "upcoming" | "past" | "cancelled") => { if (appointments.length === 0) { return ( Book Now ) : undefined } /> ); } return (
{appointments.map((appointment) => ( s.service.name).join(" + ")} person={{ id: appointment.staffId, name: appointment.staff.user.name ?? "Barber" }} formattedDate={appointment.startAt.toLocaleDateString("en-GB", { weekday: "short", day: "numeric", month: "short", timeZone: business.timezone, })} formattedTime={appointment.startAt.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", timeZone: business.timezone, })} actions={
View {kind === "upcoming" && RESCHEDULABLE_STATUSES.has(appointment.status) && ( Reschedule )} {kind === "upcoming" && CANCELLABLE_STATUSES.has(appointment.status) && ( )} {kind !== "upcoming" && }
} /> ))}
); }; return (
Upcoming Past Cancelled {renderList(upcoming, "upcoming")} {renderList(past, "past")} {renderList(cancelled, "cancelled")}
); }