"use client"; import type React from "react"; import { useState, useMemo, useEffect } from "react"; import { toast } from "sonner"; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Calendar, Clock, User, Mail, Phone, CheckCircle, Stethoscope, AlertCircle, FileText, ArrowLeft, ArrowRight, Loader2, CreditCard, Shield, Check, MapPin, Heart, } from "lucide-react"; import { Field, FieldLabel, FieldContent } from "@/components/ui/field"; import { InputGroup, InputGroupAddon, InputGroupInput, } from "@/components/ui/input-group"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { cn } from "@/lib/utils"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; interface BookingFormProps { services?: Array<{ id: string; name: string; price: string | number; duration: string | number; category: string; description?: string; }>; dentists?: Array<{ id: string; name: string; specialization?: string; image?: string; }>; patientId?: string; } const MEDICAL_CONDITIONS = [ "Diabetes", "Heart Disease", "High Blood Pressure", "Bleeding Disorder", "Asthma", "Allergies (Drug/Food)", "Thyroid Disease", "Kidney Disease", "Liver Disease", "Cancer", "Osteoporosis", "Arthritis", ]; const MEDICATIONS_COMMON = [ "Aspirin", "Ibuprofen", "Warfarin (Blood Thinner)", "Antibiotics", "Blood Pressure Medication", "Diabetes Medication", "Thyroid Medication", "Antidepressants", ]; const DENTAL_HISTORY = [ "Regular Checkups", "Root Canal", "Extractions", "Crowns/Bridges", "Implants", "Orthodontics (Braces)", "Gum Disease Treatment", "Teeth Whitening", ]; const STEP_CONFIG = [ { id: 0, title: "Services", shortTitle: "Services", description: "Choose your dental service", icon: Stethoscope, }, { id: 1, title: "Date & Time", shortTitle: "Schedule", description: "Select appointment date and time", icon: Calendar, }, { id: 2, title: "Patient Info", shortTitle: "Info", description: "Your contact information", icon: User, }, { id: 3, title: "Medical History", shortTitle: "Medical", description: "Health information (optional)", icon: Heart, }, { id: 4, title: "Review & Confirm", shortTitle: "Review", description: "Confirm your appointment", icon: Check, }, ]; type ServiceSelection = { id: string; name: string; qty: number }; export default function BookingForm({ services: propServices = [], dentists: propDentists = [], patientId, }: BookingFormProps) { const [currentStep, setCurrentStep] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const [isNewPatient, setIsNewPatient] = useState(true); const [showMedicalHistory, setShowMedicalHistory] = useState(false); // Auto-save to localStorage - memoize to prevent unnecessary re-renders const STORAGE_KEY = useMemo(() => `booking-form-${patientId}`, [patientId]); const services = useMemo( () => propServices && propServices.length > 0 ? propServices.map((service) => ({ ...service, price: service.price, duration: typeof service.duration === "string" ? Number.parseInt(service.duration) : service.duration, })) : [], [propServices] ); const dentists = propDentists && propDentists.length > 0 ? propDentists : []; const [formData, setFormData] = useState<{ services: ServiceSelection[]; preferredDate: string; preferredTime: string; dentistId: string; firstName: string; lastName: string; dateOfBirth: string; gender: string; email: string; contactNumber: string; preferredContact: string; address: string; city: string; postalCode: string; medicalConditions: string[]; otherConditions: string; currentMedications: string[]; otherMedications: string; allergies: string; lastDentalVisit: string; dentalHistory: string[]; hasMedicalUpdates: string; specialRequests: string; insuranceProvider: string; insurancePolicyNumber: string; emergencyContactName: string; emergencyContactPhone: string; smsReminders: boolean; consentToTreatment: boolean; }>({ services: [], preferredDate: "", preferredTime: "", dentistId: "", firstName: "", lastName: "", dateOfBirth: "", gender: "", email: "", contactNumber: "", preferredContact: "email", address: "", city: "", postalCode: "", medicalConditions: [], otherConditions: "", currentMedications: [], otherMedications: "", allergies: "", lastDentalVisit: "", dentalHistory: [], hasMedicalUpdates: "no", specialRequests: "", insuranceProvider: "", insurancePolicyNumber: "", emergencyContactName: "", emergencyContactPhone: "", smsReminders: true, consentToTreatment: false, }); const handleInputChange = (field: string, value: string | boolean) => { setFormData((prev) => ({ ...prev, [field]: value, })); }; const handleArrayToggle = (field: string, value: string) => { setFormData((prev) => { const array = prev[field as keyof typeof prev] as string[]; if (array.includes(value)) { return { ...prev, [field]: array.filter((item) => item !== value), }; } else { return { ...prev, [field]: [...array, value], }; } }); }; const handleServiceChange = (serviceId: string, qty: number) => { setFormData((prev) => { const existing = prev.services.find((s) => s.id === serviceId); let newServices; if (existing) { if (qty === 0) { newServices = prev.services.filter((s) => s.id !== serviceId); } else { newServices = prev.services.map((s) => s.id === serviceId ? { ...s, qty } : s ); } } else { const service = services.find((s) => s.id === serviceId); if (!service) return prev; newServices = [ ...prev.services, { id: serviceId, name: service.name, qty }, ]; } return { ...prev, services: newServices }; }); }; const parsePrice = (price: string | number): number => { if (!price) return 0; if (typeof price === "number") return price; if (price.toLowerCase().includes("contact")) return 0; const match = price.match(/₱?([\d,]+)/); if (match) { return Number.parseFloat(match[1].replace(/,/g, "")); } return 0; }; const { itemizedServices, subtotal, tax, totalDue } = useMemo(() => { const itemizedServices = formData.services .filter((s) => s.qty > 0) .map((s) => { const service = services.find((svc) => svc.id === s.id); const price = service ? parsePrice(service.price) : 0; const total = price * s.qty; return { description: service ? service.name : "", qty: s.qty, unitPrice: price, total, }; }); const subtotal = itemizedServices.reduce( (sum: number, s) => sum + s.total, 0 ); const tax = Math.round(subtotal * 0.12 * 100) / 100; const totalDue = subtotal + tax; return { itemizedServices, subtotal, tax, totalDue }; }, [formData.services, services]); const selectedDentist = dentists.find((d) => d.id === formData.dentistId); const validateStep = (step: number): boolean => { switch (step) { case 0: return formData.services.filter((s) => s.qty > 0).length > 0; case 1: return !!( formData.preferredDate && formData.preferredTime && formData.dentistId ); case 2: return !!( formData.firstName && formData.lastName && formData.email && formData.contactNumber && formData.dateOfBirth && formData.gender ); case 3: return true; case 4: return formData.consentToTreatment; default: return false; } }; const canProceed = validateStep(currentStep); const handleNext = () => { if (canProceed && currentStep < STEP_CONFIG.length - 1) { if ( currentStep === 2 && !showMedicalHistory && formData.hasMedicalUpdates === "no" ) { setCurrentStep(4); } else { setCurrentStep(currentStep + 1); } } }; const handleBack = () => { if (currentStep > 0) { if ( currentStep === 4 && !showMedicalHistory && formData.hasMedicalUpdates === "no" ) { setCurrentStep(2); } else { setCurrentStep(currentStep - 1); } } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formData.consentToTreatment) { toast.error("Please agree to the consent to treatment"); return; } const selectedServices = formData.services.filter((s) => s.qty > 0); if (selectedServices.length === 0) { toast.error("Please select at least one service"); return; } if (totalDue <= 0) { toast.error("Invalid total amount"); return; } setIsSubmitting(true); const appointmentData = { patientId, personalInfo: { firstName: formData.firstName, lastName: formData.lastName, dateOfBirth: formData.dateOfBirth, gender: formData.gender, email: formData.email, contactNumber: formData.contactNumber, preferredContact: formData.preferredContact, address: formData.address, city: formData.city, postalCode: formData.postalCode, }, medicalHistory: showMedicalHistory || formData.hasMedicalUpdates === "yes" ? { conditions: formData.medicalConditions, otherConditions: formData.otherConditions, medications: formData.currentMedications, otherMedications: formData.otherMedications, allergies: formData.allergies, lastDentalVisit: formData.lastDentalVisit, dentalHistory: formData.dentalHistory, } : null, appointment: { date: formData.preferredDate, time: formData.preferredTime, dentistId: formData.dentistId, dentistName: selectedDentist?.name, }, services: itemizedServices, insurance: { provider: formData.insuranceProvider, policyNumber: formData.insurancePolicyNumber, }, emergency: { contactName: formData.emergencyContactName, contactPhone: formData.emergencyContactPhone, }, preferences: { smsReminders: formData.smsReminders, }, specialRequests: formData.specialRequests, totals: { subtotal, tax, totalDue, }, }; try { const response = await fetch("/api/appointments/book", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ appointmentData, }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Failed to book appointment"); } localStorage.removeItem(STORAGE_KEY); toast.success( data.message || "Appointment booked successfully! Check your email for confirmation.", { description: "You will be redirected to your appointments page.", duration: 3000, } ); setTimeout(() => { window.location.href = "/patient/appointments"; }, 2000); } catch (error) { console.error("Error booking appointment:", error); toast.error("Failed to book appointment", { description: error instanceof Error ? error.message : "Unknown error. Please try again.", }); setIsSubmitting(false); } }; useEffect(() => { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { try { const parsed = JSON.parse(saved); setFormData((prev) => ({ ...prev, ...parsed })); } catch (e) { console.error("Failed to load saved form data", e); } } }, [STORAGE_KEY]); useEffect(() => { const timer = setTimeout(() => { localStorage.setItem(STORAGE_KEY, JSON.stringify(formData)); }, 1000); return () => clearTimeout(timer); }, [formData, STORAGE_KEY]); const progressPercent = ((currentStep + 1) / STEP_CONFIG.length) * 100; return (