import React, { useState, useEffect } from "react"; import { Clock, Plus, Trash2, Save, Copy, Calendar as CalendarIcon, AlertCircle, } from "lucide-react"; import toast from "react-hot-toast"; import { format, addDays, startOfWeek } from "date-fns"; import { ptBR } from "date-fns/locale"; import availabilityService from "../services/availabilityService"; import exceptionService from "../services/exceptionService"; import { useAuth } from "../hooks/useAuth"; interface TimeSlot { id: string; inicio: string; fim: string; ativo: boolean; } interface DaySchedule { day: string; dayOfWeek: number; enabled: boolean; slots: TimeSlot[]; } const daysOfWeek = [ { key: 0, label: "Domingo", dbKey: "domingo" }, { key: 1, label: "Segunda-feira", dbKey: "segunda" }, { key: 2, label: "Terça-feira", dbKey: "terca" }, { key: 3, label: "Quarta-feira", dbKey: "quarta" }, { key: 4, label: "Quinta-feira", dbKey: "quinta" }, { key: 5, label: "Sexta-feira", dbKey: "sexta" }, { key: 6, label: "Sábado", dbKey: "sabado" }, ]; const DisponibilidadeMedico: React.FC = () => { const { user } = useAuth(); const medicoId = user?.id || ""; const [schedule, setSchedule] = useState>({}); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [activeTab, setActiveTab] = useState<"weekly" | "blocked" | "settings">( "weekly" ); // States for adding slots const [showAddSlotDialog, setShowAddSlotDialog] = useState(false); const [selectedDay, setSelectedDay] = useState(null); const [newSlot, setNewSlot] = useState({ inicio: "09:00", fim: "10:00" }); // States for blocked dates const [selectedDate, setSelectedDate] = useState( new Date() ); const [blockedDates, setBlockedDates] = useState([]); const [exceptions, setExceptions] = useState([]); // Settings const [consultationDuration, setConsultationDuration] = useState("60"); const [breakTime, setBreakTime] = useState("0"); useEffect(() => { if (medicoId) { loadAvailability(); loadExceptions(); } }, [medicoId]); const loadAvailability = async () => { try { setLoading(true); const response = await availabilityService.getAvailability(medicoId); if (response.success && response.data && response.data.length > 0) { const availabilityData = response.data[0]; const newSchedule: Record = {}; daysOfWeek.forEach(({ key, label, dbKey }) => { const dayData = availabilityData[dbKey]; newSchedule[key] = { day: label, dayOfWeek: key, enabled: dayData?.ativo || false, slots: dayData?.horarios?.map((h: any, index: number) => ({ id: `${key}-${index}`, inicio: h.inicio, fim: h.fim, ativo: h.ativo !== false, })) || [], }; }); setSchedule(newSchedule); } else { // Initialize empty schedule const newSchedule: Record = {}; daysOfWeek.forEach(({ key, label }) => { newSchedule[key] = { day: label, dayOfWeek: key, enabled: false, slots: [], }; }); setSchedule(newSchedule); } } catch (error) { console.error("Erro ao carregar disponibilidade:", error); toast.error("Erro ao carregar disponibilidade"); } finally { setLoading(false); } }; const loadExceptions = async () => { try { const response = await exceptionService.listExceptions(medicoId); if (response.success && response.data) { setExceptions(response.data); const blocked = response.data .filter((exc: any) => exc.tipo === "bloqueio") .map((exc: any) => new Date(exc.data)); setBlockedDates(blocked); } } catch (error) { console.error("Erro ao carregar exceções:", error); } }; const toggleDay = (dayKey: number) => { setSchedule((prev) => ({ ...prev, [dayKey]: { ...prev[dayKey], enabled: !prev[dayKey].enabled, }, })); }; const addTimeSlot = () => { if (selectedDay !== null) { const newSlotId = `${selectedDay}-${Date.now()}`; setSchedule((prev) => ({ ...prev, [selectedDay]: { ...prev[selectedDay], slots: [ ...prev[selectedDay].slots, { id: newSlotId, inicio: newSlot.inicio, fim: newSlot.fim, ativo: true, }, ], }, })); setShowAddSlotDialog(false); setNewSlot({ inicio: "09:00", fim: "10:00" }); setSelectedDay(null); } }; const removeTimeSlot = (dayKey: number, slotId: string) => { setSchedule((prev) => ({ ...prev, [dayKey]: { ...prev[dayKey], slots: prev[dayKey].slots.filter((slot) => slot.id !== slotId), }, })); }; const toggleSlotAvailability = (dayKey: number, slotId: string) => { setSchedule((prev) => ({ ...prev, [dayKey]: { ...prev[dayKey], slots: prev[dayKey].slots.map((slot) => slot.id === slotId ? { ...slot, ativo: !slot.ativo } : slot ), }, })); }; const copySchedule = (fromDay: number) => { const sourceSchedule = schedule[fromDay]; if (!sourceSchedule.enabled || sourceSchedule.slots.length === 0) { toast.error("Dia não tem horários configurados"); return; } const updatedSchedule = { ...schedule }; Object.keys(updatedSchedule).forEach((key) => { const dayKey = Number(key); if (dayKey !== fromDay && updatedSchedule[dayKey].enabled) { updatedSchedule[dayKey].slots = sourceSchedule.slots.map((slot) => ({ ...slot, id: `${dayKey}-${slot.id}`, })); } }); setSchedule(updatedSchedule); toast.success("Horários copiados com sucesso!"); }; const handleSaveSchedule = async () => { try { setSaving(true); // Build availability object const availabilityData: any = { medico_id: medicoId, }; daysOfWeek.forEach(({ key, dbKey }) => { const daySchedule = schedule[key]; availabilityData[dbKey] = { ativo: daySchedule.enabled, horarios: daySchedule.slots.map((slot) => ({ inicio: slot.inicio, fim: slot.fim, ativo: slot.ativo, })), }; }); const response = await availabilityService.createAvailability( availabilityData ); if (response.success) { toast.success("Disponibilidade salva com sucesso!"); loadAvailability(); } else { throw new Error(response.message || "Erro ao salvar"); } } catch (error: any) { console.error("Erro ao salvar disponibilidade:", error); toast.error(error.message || "Erro ao salvar disponibilidade"); } finally { setSaving(false); } }; const toggleBlockedDate = async () => { if (!selectedDate) return; const dateString = format(selectedDate, "yyyy-MM-dd"); const dateExists = blockedDates.some( (d) => format(d, "yyyy-MM-dd") === dateString ); try { if (dateExists) { // Remove block const exception = exceptions.find( (exc) => format(new Date(exc.data), "yyyy-MM-dd") === dateString ); if (exception) { await exceptionService.deleteException(exception._id); setBlockedDates( blockedDates.filter((d) => format(d, "yyyy-MM-dd") !== dateString) ); toast.success("Data desbloqueada"); } } else { // Add block const response = await exceptionService.createException({ medicoId, data: dateString, tipo: "bloqueio", motivo: "Data bloqueada pelo médico", }); if (response.success) { setBlockedDates([...blockedDates, selectedDate]); toast.success("Data bloqueada"); } } loadExceptions(); } catch (error) { console.error("Erro ao alternar bloqueio de data:", error); toast.error("Erro ao bloquear/desbloquear data"); } }; if (loading) { return (
); } return (
{/* Header */}

Gerenciar Disponibilidade

Configure seus horários de atendimento

{/* Tabs */}
{/* Tab Content - Weekly Schedule */} {activeTab === "weekly" && (

Horários por Dia da Semana

Defina seus horários de atendimento para cada dia da semana

{daysOfWeek.map(({ key, label }) => (
{label} {schedule[key]?.enabled && ( {schedule[key]?.slots.length || 0} horário(s) )}
{schedule[key]?.enabled && (
)}
{schedule[key]?.enabled && (
{schedule[key]?.slots.length === 0 ? (

Nenhum horário configurado

) : ( schedule[key]?.slots.map((slot) => (
{slot.inicio} - {slot.fim} {!slot.ativo && ( Bloqueado )}
)) )}
)}
))}
)} {/* Tab Content - Blocked Dates */} {activeTab === "blocked" && (

Selecionar Datas

Clique em uma data no calendário e depois no botão para bloquear/desbloquear

setSelectedDate(new Date(e.target.value))} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white" />

Datas Bloqueadas

{blockedDates.length} data(s) bloqueada(s)

{blockedDates.length === 0 ? (

Nenhuma data bloqueada

) : (
{blockedDates.map((date, index) => (
{format(date, "EEEE, dd 'de' MMMM 'de' yyyy", { locale: ptBR, })}
))}
)}
)} {/* Tab Content - Settings */} {activeTab === "settings" && (

Configurações de Consulta

Defina as configurações padrão para suas consultas

Esta duração será usada para calcular os horários disponíveis

Tempo de descanso entre consultas

Aceitar consultas online

Permitir agendamento de teleconsultas

Confirmação automática

Aprovar agendamentos automaticamente

)} {/* Add Time Slot Dialog */} {showAddSlotDialog && (

Adicionar Horário

Defina o período de atendimento para{" "} {selectedDay !== null ? schedule[selectedDay]?.day : ""}

setNewSlot({ ...newSlot, inicio: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white" />
setNewSlot({ ...newSlot, fim: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:text-white" />
)}
); }; export default DisponibilidadeMedico;