'use client' import { useState, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { AppointmentCalendar } from '@/components/admin/appointment-calendar' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Badge } from '@/components/ui/badge' import { CalendarIcon, Plus, Users, Clock, CheckCircle, XCircle } from 'lucide-react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { toast } from 'sonner' import moment from 'moment' const appointmentSchema = z.object({ artistId: z.string().min(1, 'Artist is required'), clientName: z.string().min(1, 'Client name is required'), clientEmail: z.string().email('Valid email is required'), title: z.string().min(1, 'Title is required'), description: z.string().optional(), startTime: z.string().min(1, 'Start time is required'), endTime: z.string().min(1, 'End time is required'), depositAmount: z.number().optional(), totalAmount: z.number().optional(), notes: z.string().optional(), }) type AppointmentFormData = z.infer export default function CalendarPage() { const [isNewAppointmentOpen, setIsNewAppointmentOpen] = useState(false) const [selectedSlot, setSelectedSlot] = useState<{ start: Date; end: Date } | null>(null) const queryClient = useQueryClient() const form = useForm({ resolver: zodResolver(appointmentSchema), defaultValues: { artistId: '', clientName: '', clientEmail: '', title: '', description: '', startTime: '', endTime: '', depositAmount: undefined, totalAmount: undefined, notes: '', }, }) // Fetch appointments const { data: appointmentsData, isLoading: appointmentsLoading } = useQuery({ queryKey: ['appointments'], queryFn: async () => { const response = await fetch('/api/appointments') if (!response.ok) throw new Error('Failed to fetch appointments') return response.json() }, }) // Fetch artists const { data: artistsData, isLoading: artistsLoading } = useQuery({ queryKey: ['artists'], queryFn: async () => { const response = await fetch('/api/artists') if (!response.ok) throw new Error('Failed to fetch artists') return response.json() }, }) // Create appointment mutation const createAppointmentMutation = useMutation({ mutationFn: async (data: AppointmentFormData) => { // First, create or find the client user const clientResponse = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: data.clientName, email: data.clientEmail, role: 'CLIENT', }), }) let clientId if (clientResponse.ok) { const client = await clientResponse.json() clientId = client.user.id } else { // If user already exists, try to find them const existingUserResponse = await fetch(`/api/users?email=${encodeURIComponent(data.clientEmail)}`) if (existingUserResponse.ok) { const existingUser = await existingUserResponse.json() clientId = existingUser.user.id } else { throw new Error('Failed to create or find client') } } // Create the appointment const appointmentResponse = await fetch('/api/appointments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...data, clientId, startTime: new Date(data.startTime).toISOString(), endTime: new Date(data.endTime).toISOString(), }), }) if (!appointmentResponse.ok) { const error = await appointmentResponse.json() throw new Error(error.error || 'Failed to create appointment') } return appointmentResponse.json() }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['appointments'] }) setIsNewAppointmentOpen(false) form.reset() toast.success('Appointment created successfully') }, onError: (error: Error) => { toast.error(error.message) }, }) // Update appointment mutation const updateAppointmentMutation = useMutation({ mutationFn: async ({ id, updates }: { id: string; updates: any }) => { const response = await fetch('/api/appointments', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, ...updates }), }) if (!response.ok) { const error = await response.json() throw new Error(error.error || 'Failed to update appointment') } return response.json() }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['appointments'] }) toast.success('Appointment updated successfully') }, onError: (error: Error) => { toast.error(error.message) }, }) // Handle slot selection for new appointment const handleSlotSelect = (slotInfo: { start: Date; end: Date; slots: Date[] }) => { setSelectedSlot({ start: slotInfo.start, end: slotInfo.end }) form.setValue('startTime', moment(slotInfo.start).format('YYYY-MM-DDTHH:mm')) form.setValue('endTime', moment(slotInfo.end).format('YYYY-MM-DDTHH:mm')) setIsNewAppointmentOpen(true) } // Handle event update const handleEventUpdate = (eventId: string, updates: any) => { updateAppointmentMutation.mutate({ id: eventId, updates }) } const onSubmit = (data: AppointmentFormData) => { createAppointmentMutation.mutate(data) } const appointments = appointmentsData?.appointments || [] const artists = artistsData?.artists || [] // Calculate stats const stats = { total: appointments.length, pending: appointments.filter((apt: any) => apt.status === 'PENDING').length, confirmed: appointments.filter((apt: any) => apt.status === 'CONFIRMED').length, completed: appointments.filter((apt: any) => apt.status === 'COMPLETED').length, } if (appointmentsLoading || artistsLoading) { return (

Loading calendar...

) } return (
{/* Header */}

Appointment Calendar

Manage studio appointments and scheduling

Create New Appointment
( Artist )} />
( Client Name )} /> ( Client Email )} />
( Appointment Title )} /> ( Description