diff --git a/app/context/AppointmentsContext.tsx b/app/context/AppointmentsContext.tsx new file mode 100644 index 0000000..f5ce2db --- /dev/null +++ b/app/context/AppointmentsContext.tsx @@ -0,0 +1,112 @@ +"use client"; + +import React, { createContext, useContext, useState, ReactNode } from 'react'; + +// A interface Appointment permanece a mesma +export interface Appointment { + id: string; + doctorName: string; + specialty: string; + date: string; + time: string; + location: string; + phone: string; + status: 'Agendada' | 'Realizada' | 'Cancelada'; + observations?: string; +} + +export interface AppointmentsContextType { + appointments: Appointment[]; + addAppointment: (appointmentData: Omit) => void; + updateAppointment: (appointmentId: string, updatedData: Partial>) => void; + // [NOVA FUNÇÃO] Adicionando a assinatura da função de exclusão ao nosso contrato + deleteAppointment: (appointmentId: string) => void; +} + +const AppointmentsContext = createContext(undefined); + +// Os dados iniciais permanecem os mesmos +const initialAppointments: Appointment[] = [ + { + id: '1', + doctorName: "Dr. João Silva", + specialty: "Cardiologia", + date: "2024-08-15", + time: "14:30", + status: "Agendada", + location: "Consultório A - 2º andar", + phone: "(11) 3333-4444", + observations: "Paciente relata dor no peito.", + }, + { + id: '2', + doctorName: "Dra. Maria Santos", + specialty: "Dermatologia", + date: "2024-09-10", + time: "10:00", + status: "Agendada", + location: "Consultório B - 1º andar", + phone: "(11) 3333-5555", + }, + { + id: '3', + doctorName: "Dr. Pedro Costa", + specialty: "Ortopedia", + date: "2024-07-08", + time: "16:00", + status: "Realizada", + location: "Consultório C - 3º andar", + phone: "(11) 3333-6666", + }, +]; + +export function AppointmentsProvider({ children }: { children: ReactNode }) { + const [appointments, setAppointments] = useState(initialAppointments); + + const addAppointment = (appointmentData: Omit) => { + const newAppointment: Appointment = { + id: Date.now().toString(), + status: 'Agendada', + ...appointmentData, + }; + setAppointments((prev) => [...prev, newAppointment]); + }; + + const updateAppointment = (appointmentId: string, updatedData: Partial>) => { + setAppointments((prev) => + prev.map((apt) => + apt.id === appointmentId ? { ...apt, ...updatedData } : apt + ) + ); + }; + + // [NOVA FUNÇÃO] Implementando a lógica de exclusão real + const deleteAppointment = (appointmentId: string) => { + setAppointments((prev) => + // O método 'filter' cria um novo array com todos os itens + // EXCETO aquele cujo ID corresponde ao que queremos excluir. + prev.filter((apt) => apt.id !== appointmentId) + ); + }; + + const value = { + appointments, + addAppointment, + updateAppointment, + deleteAppointment, // Disponibilizando a nova função para os componentes + }; + + return ( + + {children} + + ); +} + +export function useAppointments() { + const context = useContext(AppointmentsContext); + if (context === undefined) { + throw new Error('useAppointments deve ser usado dentro de um AppointmentsProvider'); + } + return context; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index d54ce03..3becb5e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,6 +3,8 @@ import { GeistSans } from 'geist/font/sans' import { GeistMono } from 'geist/font/mono' import { Analytics } from '@vercel/analytics/next' import './globals.css' +// [PASSO 1.2] - Importando o nosso provider +import { AppointmentsProvider } from './context/AppointmentsContext' export const metadata: Metadata = { title: 'Clinic App', @@ -18,9 +20,12 @@ export default function RootLayout({ return ( - {children} + {/* [PASSO 1.2] - Envolvendo a aplicação com o provider */} + + {children} + ) -} +} \ No newline at end of file diff --git a/app/patient/appointments/page.tsx b/app/patient/appointments/page.tsx index 0cf7649..1152076 100644 --- a/app/patient/appointments/page.tsx +++ b/app/patient/appointments/page.tsx @@ -1,139 +1,125 @@ "use client"; import { useState } from "react"; +import Link from "next/link"; +import { toast } from "sonner"; +import { useAppointments, Appointment } from "../../context/AppointmentsContext"; + +// Componentes de UI e Ícones import PatientLayout from "@/components/patient-layout"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogClose } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Calendar, Clock, MapPin, Phone, CalendarDays, X } from "lucide-react"; -import { toast } from "sonner"; -import Link from "next/link"; +import { Calendar, Clock, MapPin, Phone, CalendarDays, X, Trash2 } from "lucide-react"; -export default function PatientAppointments() { - const [appointments, setAppointments] = useState([ - { - id: 1, - doctor: "Dr. João Silva", - specialty: "Cardiologia", - date: "2024-01-15", - time: "14:30", - status: "agendada", - location: "Consultório A - 2º andar", - phone: "(11) 3333-4444", - }, - { - id: 2, - doctor: "Dra. Maria Santos", - specialty: "Dermatologia", - date: "2024-01-22", - time: "10:00", - status: "agendada", - location: "Consultório B - 1º andar", - phone: "(11) 3333-5555", - }, - { - id: 3, - doctor: "Dr. Pedro Costa", - specialty: "Ortopedia", - date: "2024-01-08", - time: "16:00", - status: "realizada", - location: "Consultório C - 3º andar", - phone: "(11) 3333-6666", - }, - { - id: 4, - doctor: "Dra. Ana Lima", - specialty: "Ginecologia", - date: "2024-01-05", - time: "09:30", - status: "realizada", - location: "Consultório D - 2º andar", - phone: "(11) 3333-7777", - }, - ]); +export default function PatientAppointmentsPage() { + const { appointments, updateAppointment, deleteAppointment } = useAppointments(); - const [rescheduleModal, setRescheduleModal] = useState(false); - const [cancelModal, setCancelModal] = useState(false); - const [selectedAppointment, setSelectedAppointment] = useState(null); - const [rescheduleData, setRescheduleData] = useState({ - date: "", - time: "", - reason: "", - }); + // Estados para controlar os modais e os dados do formulário + const [isRescheduleModalOpen, setRescheduleModalOpen] = useState(false); + const [isCancelModalOpen, setCancelModalOpen] = useState(false); + const [selectedAppointment, setSelectedAppointment] = useState(null); + + const [rescheduleData, setRescheduleData] = useState({ date: "", time: "", reason: "" }); const [cancelReason, setCancelReason] = useState(""); - const handleReschedule = (appointment: any) => { + // --- MANIPULADORES DE EVENTOS --- + + const handleRescheduleClick = (appointment: Appointment) => { setSelectedAppointment(appointment); - setRescheduleData({ date: "", time: "", reason: "" }); - setRescheduleModal(true); + // Preenche o formulário com os dados atuais da consulta + setRescheduleData({ date: appointment.date, time: appointment.time, reason: appointment.observations || "" }); + setRescheduleModalOpen(true); }; - const handleCancel = (appointment: any) => { + const handleCancelClick = (appointment: Appointment) => { setSelectedAppointment(appointment); - setCancelReason(""); - setCancelModal(true); + setCancelReason(""); // Limpa o motivo ao abrir + setCancelModalOpen(true); }; - + const confirmReschedule = () => { if (!rescheduleData.date || !rescheduleData.time) { toast.error("Por favor, selecione uma nova data e horário"); return; } - - setAppointments((prev) => prev.map((apt) => (apt.id === selectedAppointment.id ? { ...apt, date: rescheduleData.date, time: rescheduleData.time } : apt))); - - setRescheduleModal(false); - toast.success("Consulta reagendada com sucesso!"); + if (selectedAppointment) { + updateAppointment(selectedAppointment.id, { + date: rescheduleData.date, + time: rescheduleData.time, + observations: rescheduleData.reason, // Atualiza as observações com o motivo + }); + toast.success("Consulta reagendada com sucesso!"); + setRescheduleModalOpen(false); + } }; const confirmCancel = () => { - if (!cancelReason.trim()) { - toast.error("O motivo do cancelamento é obrigatório"); - return; - } - if (cancelReason.trim().length < 10) { - toast.error("Por favor, forneça um motivo mais detalhado (mínimo 10 caracteres)"); + toast.error("Por favor, forneça um motivo com pelo menos 10 caracteres."); return; } - - setAppointments((prev) => prev.map((apt) => (apt.id === selectedAppointment.id ? { ...apt, status: "cancelada" } : apt))); - - setCancelModal(false); - toast.success("Consulta cancelada com sucesso!"); - }; - - const getStatusBadge = (status: string) => { - switch (status) { - case "agendada": - return Agendada; - case "realizada": - return Realizada; - case "cancelada": - return Cancelada; - default: - return {status}; + if (selectedAppointment) { + // Apenas atualiza o status e adiciona o motivo do cancelamento nas observações + updateAppointment(selectedAppointment.id, { + status: "Cancelada", + observations: `Motivo do cancelamento: ${cancelReason}` + }); + toast.success("Consulta cancelada com sucesso!"); + setCancelModalOpen(false); } }; - const timeSlots = ["08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30"]; + const handleDeleteClick = (appointmentId: string) => { + if (window.confirm("Tem certeza que deseja excluir permanentemente esta consulta? Esta ação não pode ser desfeita.")) { + deleteAppointment(appointmentId); + toast.success("Consulta excluída do histórico."); + } + }; + + // --- LÓGICA AUXILIAR --- + + const getStatusBadge = (status: Appointment['status']) => { + switch (status) { + case "Agendada": return Agendada; + case "Realizada": return Realizada; + case "Cancelada": return Cancelada; + } + }; + + const timeSlots = ["08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "14:00", "14:30", "15:00", "15:30"]; + const today = new Date(); + today.setHours(0, 0, 0, 0); // Zera o horário para comparar apenas o dia + + // ETAPA 1: ORDENAÇÃO DAS CONSULTAS + // Cria uma cópia do array e o ordena + const sortedAppointments = [...appointments].sort((a, b) => { + const statusWeight = { 'Agendada': 1, 'Realizada': 2, 'Cancelada': 3 }; + + // Primeiro, ordena por status (Agendada vem primeiro) + if (statusWeight[a.status] !== statusWeight[b.status]) { + return statusWeight[a.status] - statusWeight[b.status]; + } + + // Se o status for o mesmo, ordena por data (mais recente/futura no topo) + return new Date(b.date).getTime() - new Date(a.date).getTime(); + }); return ( -
+

Minhas Consultas

Histórico e consultas agendadas

- @@ -141,124 +127,162 @@ export default function PatientAppointments() {
- {appointments.map((appointment) => ( - - -
-
- {appointment.doctor} - {appointment.specialty} -
- {getStatusBadge(appointment.status)} -
-
- -
-
-
- - {new Date(appointment.date).toLocaleDateString("pt-BR")} -
-
- - {appointment.time} -
-
-
-
- - {appointment.location} -
-
- - {appointment.phone} -
-
-
+ {/* Utiliza o array ORDENADO para a renderização */} + {sortedAppointments.map((appointment) => { + const appointmentDate = new Date(appointment.date); + let displayStatus = appointment.status; - {appointment.status === "agendada" && ( -
- - + + + )} + + {(displayStatus === "Realizada" || displayStatus === "Cancelada") && ( + + )}
- )} -
-
- ))} + + + ); + })}
+ + {/* ETAPA 2: CONSTRUÇÃO DOS MODAIS */} - - + {/* Modal de Reagendamento */} + + Reagendar Consulta - Reagendar consulta com {selectedAppointment?.doctor} + + Reagendar consulta com {selectedAppointment?.doctorName}. +
-
- - setRescheduleData((prev) => ({ ...prev, date: e.target.value }))} min={new Date().toISOString().split("T")[0]} /> +
+ + setRescheduleData({...rescheduleData, date: e.target.value})} + className="col-span-3" + />
-
- - setRescheduleData({...rescheduleData, time: value})} + > + - {timeSlots.map((time) => ( - - {time} - - ))} + {timeSlots.map(time => {time})}
-
- -