import React from "react"; import { Avatar, AvatarFallback } from "./avatar"; import { Badge } from "./badge"; import { Button } from "./button"; import { Separator } from "./separator"; import { Sheet, SheetContent } from "./sheet"; import { AppointmentConfirmAction, AppointmentConfirmDialog, AppointmentRescheduleDialog, } from "./appointment-action-dialogs"; import type { AppointmentMeetingFormat, AppointmentStatus, AppointmentTimeSlot, } from "./appointment-time-slot-picker"; import { AlertCircle, Calendar, CalendarClock, Check, Circle, CircleCheck, CircleUser, Clock, ExternalLink, FileText, Mail, MapPin, Phone, RefreshCw, Sparkles, Users, Video, X, } from "lucide-react"; // Re-export so consumers who import these types from this module still work export type { AppointmentStatus, AppointmentMeetingFormat, } from "./appointment-time-slot-picker"; export interface AppointmentMeetingActionItem { label: string; done?: boolean; } /** AI-generated recap of a completed meeting, stored on the booking. */ export interface AppointmentMeetingSummary { /** Short recap of what was discussed. */ summary?: string; /** Follow-up items, optionally marked done. */ actionItems?: AppointmentMeetingActionItem[]; } export interface AppointmentDetailItem { id: string; status: AppointmentStatus; clientName: string; clientAvatarInitials: string; /** Client ID — used to pre-select client when rebooking after cancellation */ clientId?: string; date: string; timeStart: string; timeEnd: string; notes?: string; /** Reason provided when the appointment was cancelled */ cancelReason?: string; /** How the meeting is conducted */ meetingFormat?: AppointmentMeetingFormat; /** Formatted location string — shown for offline meetings */ meetingLocation?: string; /** Phone number — shown for call meetings */ callPhone?: string; /** Join URL — shown for google-meet and microsoft-teams meetings */ meetingLink?: string; /** AI meeting summary — shown once the meeting has been recorded and summarised */ meetingSummary?: AppointmentMeetingSummary; } export type LoanApplicationStatus = | "finished" | "in-progress" | "sent-request" | "not-start"; /** Extra client profile fields not stored on the appointment itself */ export interface AppointmentClientProfile { phone?: string; email?: string; accountType?: "Individual" | "Joint"; loanApplicationStatus?: LoanApplicationStatus; } export interface AppointmentDetailSheetProps { appointment: AppointmentDetailItem | undefined; open: boolean; onOpenChange: (v: boolean) => void; clientProfile?: AppointmentClientProfile; amSlots: AppointmentTimeSlot[]; pmSlots: AppointmentTimeSlot[]; /** * Fired when the user selects a different date in the reschedule dialog. * Use this to fetch fresh `amSlots`/`pmSlots` for the new date. */ onDateChange?: (date: Date) => void; /** * Advisor's weekly availability schedule. Days with `enabled: false` are * disabled in the reschedule date picker. */ schedule?: { day: string; enabled: boolean }[]; /** True while slots are being fetched for the selected date. */ isLoadingSlots?: boolean; onAccept?: (id: string) => void; onDecline?: (id: string) => void; onReschedule?: ( id: string, date: Date, slot: AppointmentTimeSlot, note: string ) => void; /** Called when "Book a New Appointment" is clicked on a cancelled appointment */ onBookNew?: (clientId?: string) => void; } // --------------------------------------------------------------------------- // Status config // --------------------------------------------------------------------------- const STATUS_CONFIG: Record< AppointmentStatus, { variant: "warning" | "success" | "destructive" | "info"; label: string; icon: React.ReactNode; } > = { pending: { variant: "warning", label: "Pending", icon: , }, confirmed: { variant: "success", label: "Confirmed", icon: , }, cancelled: { variant: "destructive", label: "Cancelled", icon: , }, rescheduled: { variant: "info", label: "Rescheduled", icon: , }, }; // --------------------------------------------------------------------------- // Meeting format config // --------------------------------------------------------------------------- const ICON_CLASS = "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground"; const MEETING_FORMAT_META: Record< AppointmentMeetingFormat, { icon: React.ReactNode; label: string } > = { call: { icon: , label: "Phone Call" }, "google-meet": { icon: