"use client" import { useState, useEffect, useMemo } from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogFooter, AlertDialogAction, AlertDialogCancel } from '@/components/ui/alert-dialog' import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { criarDisponibilidade, atualizarDisponibilidade, listarExcecoes, DoctorAvailabilityCreate, DoctorAvailability, DoctorAvailabilityUpdate, DoctorException } from '@/lib/api' import { useToast } from '@/hooks/use-toast' export interface AvailabilityFormProps { open: boolean onOpenChange: (open: boolean) => void doctorId?: string | null onSaved?: (saved: any) => void // when editing, pass the existing availability and set mode to 'edit' availability?: DoctorAvailability | null mode?: 'create' | 'edit' // existing availabilities to prevent duplicate weekday selection existingAvailabilities?: DoctorAvailability[] } export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved, availability = null, mode = 'create', existingAvailabilities = [] }: AvailabilityFormProps) { const [weekday, setWeekday] = useState('segunda') const [startTime, setStartTime] = useState('09:00') const [endTime, setEndTime] = useState('17:00') const [slotMinutes, setSlotMinutes] = useState(30) const [appointmentType, setAppointmentType] = useState<'presencial'|'telemedicina'>('presencial') const [active, setActive] = useState(true) const [submitting, setSubmitting] = useState(false) const { toast } = useToast() const [blockedException, setBlockedException] = useState(null) // Normalize weekday to standard format for comparison const normalizeWeekdayForComparison = (w?: string) => { if (!w) return w; const k = String(w).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '').replace(/[^a-z0-9]/g, ''); const map: Record = { 'segunda':'segunda','terca':'terca','quarta':'quarta','quinta':'quinta','sexta':'sexta','sabado':'sabado','domingo':'domingo', 'monday':'segunda','tuesday':'terca','wednesday':'quarta','thursday':'quinta','friday':'sexta','saturday':'sabado','sunday':'domingo', '1':'segunda','2':'terca','3':'quarta','4':'quinta','5':'sexta','6':'sabado','0':'domingo','7':'domingo' }; return map[k] ?? k; }; // Get list of already used weekdays (excluding current one in edit mode) const usedWeekdays = useMemo(() => { return new Set( (existingAvailabilities || []) .filter(a => mode === 'edit' ? a.id !== availability?.id : true) .map(a => normalizeWeekdayForComparison(a.weekday)) .filter(Boolean) ); }, [existingAvailabilities, mode, availability?.id]); // When editing, populate state from availability prop useEffect(() => { if (mode === 'edit' && availability) { // weekday may be 'monday' or 'segunda' — keep original string setWeekday(String(availability.weekday ?? 'segunda')) // strip seconds for time inputs (HH:MM) const st = String(availability.start_time ?? '09:00:00').replace(/:00$/,'') const et = String(availability.end_time ?? '17:00:00').replace(/:00$/,'') setStartTime(st) setEndTime(et) setSlotMinutes(Number(availability.slot_minutes ?? 30)) setAppointmentType((availability.appointment_type ?? 'presencial') as any) setActive(!!availability.active) } }, [mode, availability]) // When creating and modal opens, set the first available weekday useEffect(() => { if (mode === 'create' && open) { const allWeekdays = ['segunda', 'terca', 'quarta', 'quinta', 'sexta', 'sabado', 'domingo']; const firstAvailable = allWeekdays.find(day => !usedWeekdays.has(day)); if (firstAvailable) { setWeekday(firstAvailable); } } }, [mode, open, usedWeekdays]) async function handleSubmit(e?: React.FormEvent) { e?.preventDefault() if (!doctorId) { toast({ title: 'Erro', description: 'ID do médico não informado', variant: 'destructive' }) return } setSubmitting(true) try { // Pre-check exceptions for this doctor to avoid creating an availability // that is blocked by an existing exception. If a blocking exception is // found we show a specific toast and abort the creation request. try { const exceptions: DoctorException[] = await listarExcecoes({ doctorId: String(doctorId) }); const today = new Date(); const oneYearAhead = new Date(); oneYearAhead.setFullYear(oneYearAhead.getFullYear() + 1); const parseTimeToMinutes = (t?: string | null) => { if (!t) return null; const parts = String(t).split(':').map((p) => Number(p)); if (parts.length >= 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) { return parts[0] * 60 + parts[1]; } return null; }; const reqStart = parseTimeToMinutes(`${startTime}:00`); const reqEnd = parseTimeToMinutes(`${endTime}:00`); const normalizeWeekday = (w?: string) => { if (!w) return w; const k = String(w).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '').replace(/[^a-z0-9]/g, ''); const map: Record = { 'segunda':'monday','terca':'tuesday','quarta':'wednesday','quinta':'thursday','sexta':'friday','sabado':'saturday','domingo':'sunday', 'monday':'monday','tuesday':'tuesday','wednesday':'wednesday','thursday':'thursday','friday':'friday','saturday':'saturday','sunday':'sunday' }; return map[k] ?? k; }; const reqWeekday = normalizeWeekday(weekday); for (const ex of exceptions || []) { if (!ex || !ex.date) continue; const exDate = new Date(ex.date + 'T00:00:00'); if (isNaN(exDate.getTime())) continue; if (exDate < today || exDate > oneYearAhead) continue; if (ex.kind !== 'bloqueio') continue; const exWeekday = normalizeWeekday(exDate.toLocaleDateString('en-US', { weekday: 'long' })); if (exWeekday !== reqWeekday) continue; // whole-day block if (!ex.start_time && !ex.end_time) { setBlockedException({ date: ex.date, reason: ex.reason ?? undefined, times: undefined }) setSubmitting(false); return; } const exStart = parseTimeToMinutes(ex.start_time ?? undefined); const exEnd = parseTimeToMinutes(ex.end_time ?? undefined); if (reqStart != null && reqEnd != null && exStart != null && exEnd != null) { if (reqStart < exEnd && exStart < reqEnd) { setBlockedException({ date: ex.date, reason: ex.reason ?? undefined, times: `${ex.start_time}–${ex.end_time}` }) setSubmitting(false); return; } } } } catch (e) { // If checking exceptions fails, continue and let the API handle it. We // intentionally do not block the flow here because failure to fetch // exceptions shouldn't completely prevent admins from creating slots. console.warn('Falha ao verificar exceções antes da criação:', e); } if (mode === 'create') { const payload: DoctorAvailabilityCreate = { doctor_id: String(doctorId), weekday: weekday as any, start_time: `${startTime}:00`, end_time: `${endTime}:00`, slot_minutes: slotMinutes, appointment_type: appointmentType, active, } const saved = await criarDisponibilidade(payload) const labelMap: Record = { 'segunda':'Segunda','terca':'Terça','quarta':'Quarta','quinta':'Quinta','sexta':'Sexta','sabado':'Sábado','domingo':'Domingo', 'monday':'Segunda','tuesday':'Terça','wednesday':'Quarta','thursday':'Quinta','friday':'Sexta','saturday':'Sábado','sunday':'Domingo' } const label = labelMap[weekday as string] ?? String(weekday) toast({ title: 'Disponibilidade criada', description: `${label} ${startTime}–${endTime}`, variant: 'default' }) onSaved?.(saved) onOpenChange(false) } else { // edit mode: update existing availability if (!availability || !availability.id) { throw new Error('Disponibilidade inválida para edição') } const payload: DoctorAvailabilityUpdate = { weekday: weekday as any, start_time: `${startTime}:00`, end_time: `${endTime}:00`, slot_minutes: slotMinutes, appointment_type: appointmentType, active, } const updated = await atualizarDisponibilidade(String(availability.id), payload) const labelMap: Record = { 'segunda':'Segunda','terca':'Terça','quarta':'Quarta','quinta':'Quinta','sexta':'Sexta','sabado':'Sábado','domingo':'Domingo', 'monday':'Segunda','tuesday':'Terça','wednesday':'Quarta','thursday':'Quinta','friday':'Sexta','saturday':'Sábado','sunday':'Domingo' } const label = labelMap[weekday as string] ?? String(weekday) toast({ title: 'Disponibilidade atualizada', description: `${label} ${startTime}–${endTime}`, variant: 'default' }) onSaved?.(updated) onOpenChange(false) } } catch (err: any) { console.error('Erro ao criar disponibilidade:', err) toast({ title: 'Erro', description: err?.message || String(err), variant: 'destructive' }) } finally { setSubmitting(false) } } const be = blockedException return ( <> {mode === 'edit' ? 'Editar disponibilidade' : 'Criar disponibilidade'}
setStartTime(e.target.value)} />
setEndTime(e.target.value)} />
setSlotMinutes(Number(e.target.value || 30))} />
{ if (!open) setBlockedException(null) }}> Data bloqueada
{be ? (

Não é possível criar disponibilidade para o dia {be!.date}.

{be!.times ?

Horário bloqueado: {be!.times}

: null} {be!.reason ?

Motivo: {be!.reason}

: null}
) : null}
setBlockedException(null)}>OK
) } export default AvailabilityForm