"use client"; import { useState, useEffect, useRef } from "react"; import { buscarPacientePorId } from "@/lib/api"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Calendar, Search, ChevronDown } from "lucide-react"; interface FormData { patientName?: string; cpf?: string; rg?: string; birthDate?: string; phoneCode?: string; phoneNumber?: string; email?: string; convenio?: string; matricula?: string; validade?: string; documentos?: string; professionalName?: string; unit?: string; appointmentDate?: string; startTime?: string; endTime?: string; requestingProfessional?: string; appointmentType?: string; notes?: string; // API-editable appointment fields status?: string; duration_minutes?: number; chief_complaint?: string; patient_notes?: string; insurance_provider?: string; checked_in_at?: string; // ISO datetime completed_at?: string; // ISO datetime cancelled_at?: string; // ISO datetime cancellation_reason?: string; } interface CalendarRegistrationFormProperties { formData: FormData; onFormChange: (data: FormData) => void; } const formatValidityDate = (value: string) => { const cleaned = value.replaceAll(/\D/g, ""); if (cleaned.length > 4) { return `${cleaned.slice(0, 2)}/${cleaned.slice(2, 4)}/${cleaned.slice(4, 8)}`; } if (cleaned.length > 2) { return `${cleaned.slice(0, 2)}/${cleaned.slice(2, 4)}`; } return cleaned; }; export function CalendarRegistrationForm({ formData, onFormChange }: CalendarRegistrationFormProperties) { const [isAdditionalInfoOpen, setIsAdditionalInfoOpen] = useState(false); const [patientDetails, setPatientDetails] = useState(null); const [loadingPatient, setLoadingPatient] = useState(false); // Helpers to convert between ISO (server) and input[type=datetime-local] value const isoToDatetimeLocal = (iso?: string | null) => { if (!iso) return ""; try { let s = String(iso).trim(); // normalize common variants: space between date/time -> T s = s.replace(" ", "T"); // If no timezone info (no Z or +/-), try treating as UTC by appending Z if (!/[zZ]$/.test(s) && !/[+-]\d{2}:?\d{2}$/.test(s)) { // try parse first; if invalid, append Z const tryParse = Date.parse(s); if (isNaN(tryParse)) { s = s + "Z"; } } const d = new Date(s); if (isNaN(d.getTime())) return ""; const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); const hh = String(d.getHours()).padStart(2, "0"); const min = String(d.getMinutes()).padStart(2, "0"); return `${yyyy}-${mm}-${dd}T${hh}:${min}`; } catch (e) { return ""; } }; const datetimeLocalToIso = (value: string) => { if (!value) return null; // value expected: YYYY-MM-DDTHH:MM or with seconds try { // If the browser gives a value without seconds, Date constructor will treat as local when we split const [datePart, timePart] = value.split("T"); if (!datePart || !timePart) return null; const [y, m, d] = datePart.split("-").map((s) => parseInt(s, 10)); const timeParts = timePart.split(":"); const hh = parseInt(timeParts[0], 10); const min = parseInt(timeParts[1] || "0", 10); const sec = parseInt(timeParts[2] || "0", 10); if ([y, m, d, hh, min, sec].some((n) => Number.isNaN(n))) return null; const dt = new Date(y, m - 1, d, hh, min, sec, 0); return dt.toISOString(); } catch (e) { return null; } }; // Automatically fetch patient details when the form receives a patientId useEffect(() => { const maybeId = (formData as any).patientId || (formData as any).patient_id || null; if (!maybeId) { setPatientDetails(null); return; } let mounted = true; setLoadingPatient(true); buscarPacientePorId(maybeId) .then((p) => { if (!mounted) return; setPatientDetails(p); }) .catch((e) => { if (!mounted) return; setPatientDetails({ error: String(e) }); }) .finally(() => { if (!mounted) return; setLoadingPatient(false); }); return () => { mounted = false; }; }, [(formData as any).patientId, (formData as any).patient_id]); const handleChange = (event: React.ChangeEvent) => { const { name, value } = event.target; // map datetime-local fields to ISO before sending up if (name === 'checked_in_at' || name === 'completed_at' || name === 'cancelled_at') { const iso = datetimeLocalToIso(value as string); onFormChange({ ...formData, [name]: iso }); return; } if (name === 'validade') { const formattedValue = formatValidityDate(value); onFormChange({ ...formData, [name]: formattedValue }); return; } // ensure duration is stored as a number if (name === 'duration_minutes') { const n = Number(value); onFormChange({ ...formData, duration_minutes: Number.isNaN(n) ? undefined : n }); return; } // If user edits endTime manually, accept the value and clear lastAutoEndRef so auto-calc won't overwrite if (name === 'endTime') { // store as-is (HH:MM) try { // clear auto flag so user edits persist (lastAutoEndRef as any).current = null; } catch (e) {} onFormChange({ ...formData, endTime: value }); return; } onFormChange({ ...formData, [name]: value }); }; // Auto-calculate endTime from startTime + duration_minutes const lastAutoEndRef = useRef(null); useEffect(() => { const start = (formData as any).startTime; const dur = (formData as any).duration_minutes; const date = (formData as any).appointmentDate; // YYYY-MM-DD if (!start) return; // if duration is not a finite number, don't compute const minutes = typeof dur === 'number' && Number.isFinite(dur) ? dur : 0; // build a Date from appointmentDate + startTime; fall back to today if appointmentDate missing const datePart = date || new Date().toISOString().slice(0, 10); const [y, m, d] = String(datePart).split('-').map((s) => parseInt(s, 10)); const [hh, mm] = String(start).split(':').map((s) => parseInt(s, 10)); if ([y, m, d, hh, mm].some((n) => Number.isNaN(n))) return; const dt = new Date(y, m - 1, d, hh, mm, 0, 0); const dt2 = new Date(dt.getTime() + minutes * 60000); const newEnd = `${String(dt2.getHours()).padStart(2, '0')}:${String(dt2.getMinutes()).padStart(2, '0')}`; const currentEnd = (formData as any).endTime; // Only overwrite if endTime is empty or it was the previously auto-calculated value if (!currentEnd || currentEnd === lastAutoEndRef.current) { lastAutoEndRef.current = newEnd; onFormChange({ ...formData, endTime: newEnd }); } }, [(formData as any).startTime, (formData as any).duration_minutes, (formData as any).appointmentDate]); return (

Informações do paciente

{loadingPatient ? (
Carregando dados do paciente...
) : patientDetails ? ( patientDetails.error ? (
Erro ao carregar paciente: {String(patientDetails.error)}
) : (
CPF: {patientDetails.cpf || '-'}
Telefone: {patientDetails.phone_mobile || patientDetails.telefone || '-'}
E-mail: {patientDetails.email || '-'}
Data de nascimento: {patientDetails.birth_date || '-'}
) ) : (
Paciente não vinculado
)}
Para editar os dados do paciente, acesse a ficha do paciente.

Informações do atendimento

{/* Profissional solicitante removed per user request */}