diff --git a/app/doctor/dashboard/page.tsx b/app/doctor/dashboard/page.tsx index 5d90d6b..c8a6c2c 100644 --- a/app/doctor/dashboard/page.tsx +++ b/app/doctor/dashboard/page.tsx @@ -3,10 +3,13 @@ import DoctorLayout from "@/components/doctor-layout"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Calendar, Clock, User, Plus } from "lucide-react"; +import { Calendar, Clock, User, Trash2 } from "lucide-react"; import Link from "next/link"; import { useEffect, useState } from "react"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { AvailabilityService } from "@/services/availabilityApi.mjs"; +import { exceptionsService } from "@/services/exceptionApi.mjs"; +import { toast } from "@/hooks/use-toast"; type Availability = { id: string; @@ -29,10 +32,15 @@ type Schedule = { export default function PatientDashboard() { const userInfo = JSON.parse(localStorage.getItem("user_info") || "{}"); - const doctorId = "58ea5330-5cfe-4433-a218-2749844aee89"; //userInfo.id; + const doctorId = "3bb9ee4a-cfdd-4d81-b628-383907dfa225"; //userInfo.id; const [availability, setAvailability] = useState(null); + const [exceptions, setExceptions] = useState(null); const [schedule, setSchedule] = useState>({}); const formatTime = (time: string) => time.split(":").slice(0, 2).join(":"); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [patientToDelete, setPatientToDelete] = useState(null); + const [error, setError] = useState(null); + // Mapa de tradução const weekdaysPT: Record = { sunday: "Domingo", @@ -47,9 +55,14 @@ export default function PatientDashboard() { useEffect(() => { const fetchData = async () => { try { + // fetch para disponibilidade const response = await AvailabilityService.list(); const filteredResponse = response.filter((disp: { doctor_id: any }) => disp.doctor_id == doctorId); setAvailability(filteredResponse); + // fetch para exceções + const res = await exceptionsService.list(); + const filteredRes = res.filter((disp: { doctor_id: any }) => disp.doctor_id == doctorId); + setExceptions(filteredRes); } catch (e: any) { alert(`${e?.error} ${e?.message}`); } @@ -57,6 +70,41 @@ export default function PatientDashboard() { fetchData(); }, []); + const openDeleteDialog = (patientId: string) => { + setPatientToDelete(patientId); + setDeleteDialogOpen(true); + }; + + const handleDeletePatient = async (patientId: string) => { + // Remove from current list (client-side deletion) + try { + const res = await exceptionsService.delete(patientId); + + let message = "Exceção deletada com sucesso"; + try { + if (res) { + throw new Error(`${res.error} ${res.message}` || "A API retornou erro"); + } else { + console.log(message); + } + } catch {} + + toast({ + title: "Sucesso", + description: message, + }); + + setExceptions((prev: any[]) => prev.filter((p) => String(p.id) !== String(patientId))); + } catch (e: any) { + toast({ + title: "Erro", + description: e?.message || "Não foi possível deletar a exceção", + }); + } + setDeleteDialogOpen(false); + setPatientToDelete(null); + }; + function formatAvailability(data: Availability[]) { // Agrupar os horários por dia da semana const schedule = data.reduce((acc: any, item) => { @@ -199,6 +247,68 @@ export default function PatientDashboard() { +
+ + + Exceções + Bloqueios e liberações eventuais de agenda + + + + {exceptions && exceptions.length > 0 ? ( + exceptions.map((ex: any) => { + // Formata data e hora + const date = new Date(ex.date).toLocaleDateString("pt-BR", { + weekday: "long", + day: "2-digit", + month: "long", + }); + + const startTime = formatTime(ex.start_time); + const endTime = formatTime(ex.end_time); + + return ( +
+
+
+

{date}

+

+ {startTime} - {endTime} +

+
+
+

{ex.kind === "bloqueio" ? "Bloqueio" : "Liberação"}

+

{ex.reason || "Sem motivo especificado"}

+
+
+ +
+
+
+ ); + }) + ) : ( +

Nenhuma exceção registrada.

+ )} +
+
+
+ + + + Confirmar exclusão + Tem certeza que deseja excluir este paciente? Esta ação não pode ser desfeita. + + + Cancelar + patientToDelete && handleDeletePatient(patientToDelete)} className="bg-red-600 hover:bg-red-700"> + Excluir + + + + ); diff --git a/app/doctor/disponibilidade/excecoes/page.tsx b/app/doctor/disponibilidade/excecoes/page.tsx new file mode 100644 index 0000000..6eafdde --- /dev/null +++ b/app/doctor/disponibilidade/excecoes/page.tsx @@ -0,0 +1,219 @@ +"use client"; + +import type React from "react"; +import Link from "next/link"; +import { useState, useEffect } from "react"; +import DoctorLayout from "@/components/doctor-layout"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Clock, Calendar as CalendarIcon, MapPin, Phone, User, X, RefreshCw } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { useRouter } from "next/navigation"; +import { toast } from "@/hooks/use-toast"; +import { exceptionsService } from "@/services/exceptionApi.mjs"; + +// IMPORTAR O COMPONENTE CALENDÁRIO DA SHADCN +import { Calendar } from "@/components/ui/calendar"; +import { format } from "date-fns"; // Usaremos o date-fns para formatação e comparação de datas + +const APPOINTMENTS_STORAGE_KEY = "clinic-appointments"; + +// --- TIPAGEM DA CONSULTA SALVA NO LOCALSTORAGE --- +interface LocalStorageAppointment { + id: number; + patientName: string; + doctor: string; + specialty: string; + date: string; // Data no formato YYYY-MM-DD + time: string; // Hora no formato HH:MM + status: "agendada" | "confirmada" | "cancelada" | "realizada"; + location: string; + phone: string; +} + +const LOGGED_IN_DOCTOR_NAME = "Dr. João Santos"; + +// Função auxiliar para comparar se duas datas (Date objects) são o mesmo dia +const isSameDay = (date1: Date, date2: Date) => { + return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate(); +}; + +// --- COMPONENTE PRINCIPAL --- + +export default function ExceptionPage() { + const [allAppointments, setAllAppointments] = useState([]); + const router = useRouter(); + const [filteredAppointments, setFilteredAppointments] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const userInfo = JSON.parse(localStorage.getItem("user_info") || "{}"); + const doctorIdTemp = "3bb9ee4a-cfdd-4d81-b628-383907dfa225"; + const [tipo, setTipo] = useState(""); + + // NOVO ESTADO 1: Armazena os dias com consultas (para o calendário) + const [bookedDays, setBookedDays] = useState([]); + + // NOVO ESTADO 2: Armazena a data selecionada no calendário + const [selectedCalendarDate, setSelectedCalendarDate] = useState(new Date()); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (isLoading) return; + //setIsLoading(true); + const form = e.currentTarget; + const formData = new FormData(form); + + const apiPayload = { + doctor_id: doctorIdTemp, + created_by: doctorIdTemp, + date: selectedCalendarDate ? format(selectedCalendarDate, "yyyy-MM-dd") : "", + start_time: ((formData.get("horarioEntrada") + ":00") as string) || undefined, + end_time: ((formData.get("horarioSaida") + ":00") as string) || undefined, + kind: tipo || undefined, + reason: formData.get("reason"), + }; + console.log(apiPayload); + try { + const res = await exceptionsService.create(apiPayload); + console.log(res); + + let message = "Exceção cadastrada com sucesso"; + try { + if (!res[0].id) { + throw new Error(`${res.error} ${res.message}` || "A API retornou erro"); + } else { + console.log(message); + } + } catch {} + + toast({ + title: "Sucesso", + description: message, + }); + router.push("/doctor/dashboard"); // adicionar página para listar a disponibilidade + } catch (err: any) { + toast({ + title: "Erro", + description: err?.message || "Não foi possível cadastrar a exceção", + }); + } finally { + setIsLoading(false); + } + }; + + const displayDate = selectedCalendarDate ? new Date(selectedCalendarDate).toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long" }) : "Selecione uma data"; + + return ( + +
+
+

Adicione exceções

+

Altere a disponibilidade em casos especiais para o Dr. {userInfo.user_metadata.full_name}

+
+ +
+

Consultas para: {displayDate}

+ +
+ + {/* NOVO LAYOUT DE DUAS COLUNAS */} +
+ {/* COLUNA 1: CALENDÁRIO */} +
+ + + + + Calendário + +

Selecione a data desejada.

+
+ + + +
+
+ + {/* COLUNA 2: FORM PARA ADICIONAR EXCEÇÃO */} +
+ {isLoading ? ( +

Carregando a agenda...

+ ) : !selectedCalendarDate ? ( +

Selecione uma data.

+ ) : ( +
+
+

Dados

+

{selectedCalendarDate?.toLocaleDateString("pt-BR", { weekday: "long" })}

+
+
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+
+
+ +
+ + + + +
+
+ )} +
+
+
+
+ ); +} diff --git a/app/doctor/disponibilidade/page.tsx b/app/doctor/disponibilidade/page.tsx index 55c3cac..4a2beb0 100644 --- a/app/doctor/disponibilidade/page.tsx +++ b/app/doctor/disponibilidade/page.tsx @@ -16,6 +16,7 @@ export default function AvailabilityPage() { const router = useRouter(); const [isLoading, setIsLoading] = useState(false); const userInfo = JSON.parse(localStorage.getItem("user_info") || "{}"); + const doctorIdTemp = "3bb9ee4a-cfdd-4d81-b628-383907dfa225"; const [modalidadeConsulta, setModalidadeConsulta] = useState(""); useEffect(() => { @@ -39,8 +40,8 @@ export default function AvailabilityPage() { const formData = new FormData(form); const apiPayload = { - doctor_id: userInfo.id, - created_by: userInfo.id, + doctor_id: doctorIdTemp, + created_by: doctorIdTemp, weekday: (formData.get("weekday") as string) || undefined, start_time: (formData.get("horarioEntrada") as string) || undefined, end_time: (formData.get("horarioSaida") as string) || undefined, @@ -159,7 +160,7 @@ export default function AvailabilityPage() { - Presencial + Presencial Telemedicina @@ -168,7 +169,10 @@ export default function AvailabilityPage() {
- + + + +