diff --git a/susconecta/app/(main-routes)/consultas/page.tsx b/susconecta/app/(main-routes)/consultas/page.tsx new file mode 100644 index 0000000..68e07d3 --- /dev/null +++ b/susconecta/app/(main-routes)/consultas/page.tsx @@ -0,0 +1,368 @@ +"use client"; + +import Link from "next/link"; +import { useState } from "react"; +import { + MoreHorizontal, + PlusCircle, + Search, + Eye, + Edit, + Trash2, + ArrowLeft, +} from "lucide-react"; + +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +import { mockAppointments, mockProfessionals } from "@/lib/mocks/appointment-mocks"; +import { CalendarRegistrationForm } from "@/components/forms/calendar-registration-form"; + +// --- Helper Functions --- +const formatDate = (date: string | Date) => { + if (!date) return ""; + return new Date(date).toLocaleDateString("pt-BR", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + }); +}; + +const capitalize = (s: string) => { + if (typeof s !== 'string' || s.length === 0) return ''; + return s.charAt(0).toUpperCase() + s.slice(1); +}; + +// --- Main Page Component --- +export default function ConsultasPage() { + const [appointments, setAppointments] = useState(mockAppointments); + const [showForm, setShowForm] = useState(false); + const [editingAppointment, setEditingAppointment] = useState(null); + const [viewingAppointment, setViewingAppointment] = useState(null); + + // Converte o objeto da consulta para o formato esperado pelo formulário + const mapAppointmentToFormData = (appointment: any) => { + const professional = mockProfessionals.find(p => p.id === appointment.professional); + const appointmentDate = new Date(appointment.time); + + return { + id: appointment.id, + patientName: appointment.patient, + professionalName: professional ? professional.name : '', + appointmentDate: appointmentDate.toISOString().split('T')[0], // Formato YYYY-MM-DD + startTime: appointmentDate.toTimeString().split(' ')[0].substring(0, 5), // Formato HH:MM + endTime: new Date(appointmentDate.getTime() + appointment.duration * 60000).toTimeString().split(' ')[0].substring(0, 5), + status: appointment.status, + appointmentType: appointment.type, + notes: appointment.notes, + // Adicione outros campos do paciente aqui se necessário (cpf, rg, etc.) + // Eles não existem no mock de agendamento, então virão vazios + cpf: '', + rg: '', + birthDate: '', + phoneCode: '+55', + phoneNumber: '', + email: '', + unit: 'nei', + }; + }; + + const handleDelete = (appointmentId: string) => { + if (window.confirm("Tem certeza que deseja excluir esta consulta?")) { + setAppointments((prev) => prev.filter((a) => a.id !== appointmentId)); + } + }; + + const handleEdit = (appointment: any) => { + const formData = mapAppointmentToFormData(appointment); + setEditingAppointment(formData); + setShowForm(true); + }; + + const handleView = (appointment: any) => { + setViewingAppointment(appointment); + }; + + const handleCancel = () => { + setEditingAppointment(null); + setShowForm(false); + }; + + const handleSave = (formData: any) => { + // Como o formulário edita campos que não estão na tabela, + // precisamos mapear de volta para o formato original do agendamento. + // Para a simulação, vamos atualizar apenas os campos que existem no mock. + const updatedAppointment = { + id: formData.id, + patient: formData.patientName, + time: new Date(`${formData.appointmentDate}T${formData.startTime}`).toISOString(), + duration: 30, // Duração não está no form, então mantemos um valor fixo + type: formData.appointmentType as any, + status: formData.status as any, + professional: appointments.find(a => a.id === formData.id)?.professional || '', // Mantém o ID do profissional + notes: formData.notes, + }; + + setAppointments(prev => + prev.map(a => a.id === updatedAppointment.id ? updatedAppointment : a) + ); + handleCancel(); // Fecha o formulário + }; + + if (showForm && editingAppointment) { + return ( +
+
+ +

Editar Consulta

+
+ +
+ ) + } + + return ( +
+
+
+

Gerenciamento de Consultas

+

Visualize, filtre e gerencie todas as consultas da clínica.

+
+
+ + + +
+
+ + + + Consultas Agendadas + + Visualize, filtre e gerencie todas as consultas da clínica. + +
+
+ + +
+ + +
+
+ + + + + Paciente + Médico + Status + Data e Hora + Ações + + + + {appointments.map((appointment) => { + const professional = mockProfessionals.find( + (p) => p.id === appointment.professional + ); + return ( + + + {appointment.patient} + + + {professional ? professional.name : "Não encontrado"} + + + + {capitalize(appointment.status)} + + + {formatDate(appointment.time)} + + + + + + + handleView(appointment)} + > + + Ver + + handleEdit(appointment)}> + + Editar + + handleDelete(appointment.id)} + className="text-destructive" + > + + Excluir + + + + + + ); + })} + +
+
+
+ + {viewingAppointment && ( + setViewingAppointment(null)}> + + + Detalhes da Consulta + + Informações detalhadas da consulta de {viewingAppointment?.patient}. + + +
+
+ + {viewingAppointment?.patient} +
+
+ + + {mockProfessionals.find(p => p.id === viewingAppointment?.professional)?.name || "Não encontrado"} + +
+
+ + {viewingAppointment?.time ? formatDate(viewingAppointment.time) : ''} +
+
+ + + + {capitalize(viewingAppointment?.status || '')} + + +
+
+ + {capitalize(viewingAppointment?.type || '')} +
+
+ + {viewingAppointment?.notes || "Nenhuma"} +
+
+ + + +
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/susconecta/app/(main-routes)/dashboard/doutores/page.tsx b/susconecta/app/(main-routes)/doutores/page.tsx similarity index 74% rename from susconecta/app/(main-routes)/dashboard/doutores/page.tsx rename to susconecta/app/(main-routes)/doutores/page.tsx index 243c4ac..501e40d 100644 --- a/susconecta/app/(main-routes)/dashboard/doutores/page.tsx +++ b/susconecta/app/(main-routes)/doutores/page.tsx @@ -5,6 +5,8 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; import { MoreHorizontal, Plus, Search, Edit, Trash2, ArrowLeft, Eye } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { DoctorRegistrationForm } from "@/components/forms/doctor-registration-form"; @@ -18,6 +20,7 @@ export default function DoutoresPage() { const [search, setSearch] = useState(""); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); + const [viewingDoctor, setViewingDoctor] = useState(null); // Carrega da API async function load() { @@ -55,6 +58,10 @@ export default function DoutoresPage() { setShowForm(true); } + function handleView(doctor: Medico) { + setViewingDoctor(doctor); + } + // Excluir via API e recarregar async function handleDelete(id: string) { if (!confirm("Excluir este médico?")) return; @@ -70,7 +77,7 @@ export default function DoutoresPage() { if (showForm) { return ( -
+
- alert(JSON.stringify(doctor, null, 2))}> + handleView(doctor)}> Ver @@ -182,6 +189,47 @@ export default function DoutoresPage() {
+ + {viewingDoctor && ( + setViewingDoctor(null)}> + + + Detalhes do Médico + + Informações detalhadas de {viewingDoctor?.nome}. + + +
+
+ + {viewingDoctor?.nome} +
+
+ + + {viewingDoctor?.especialidade} + +
+
+ + {viewingDoctor?.crm} +
+
+ + {viewingDoctor?.email} +
+
+ + {viewingDoctor?.telefone} +
+
+ + + +
+
+ )} +
Mostrando {filtered.length} de {doctors.length}
diff --git a/susconecta/app/(main-routes)/dashboard/pacientes/loading.tsx b/susconecta/app/(main-routes)/pacientes/loading.tsx similarity index 100% rename from susconecta/app/(main-routes)/dashboard/pacientes/loading.tsx rename to susconecta/app/(main-routes)/pacientes/loading.tsx diff --git a/susconecta/app/(main-routes)/dashboard/pacientes/page.tsx b/susconecta/app/(main-routes)/pacientes/page.tsx similarity index 77% rename from susconecta/app/(main-routes)/dashboard/pacientes/page.tsx rename to susconecta/app/(main-routes)/pacientes/page.tsx index 074b512..3f2d75f 100644 --- a/susconecta/app/(main-routes)/dashboard/pacientes/page.tsx +++ b/susconecta/app/(main-routes)/pacientes/page.tsx @@ -6,6 +6,8 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; import { MoreHorizontal, Plus, Search, Eye, Edit, Trash2, ArrowLeft } from "lucide-react"; import { Paciente, Endereco, listarPacientes, buscarPacientePorId, excluirPaciente } from "@/lib/api"; @@ -47,6 +49,7 @@ export default function PacientesPage() { const [search, setSearch] = useState(""); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); + const [viewingPatient, setViewingPatient] = useState(null); async function loadAll() { try { @@ -88,6 +91,10 @@ export default function PacientesPage() { setShowForm(true); } + function handleView(patient: Paciente) { + setViewingPatient(patient); + } + async function handleDelete(id: string) { if (!confirm("Excluir este paciente?")) return; try { @@ -161,7 +168,6 @@ export default function PacientesPage() { return (
- {}

Pacientes

@@ -217,7 +223,7 @@ export default function PacientesPage() { - alert(JSON.stringify(p, null, 2))}> + handleView(p)}> Ver @@ -245,6 +251,46 @@ export default function PacientesPage() {
+ {viewingPatient && ( + setViewingPatient(null)}> + + + Detalhes do Paciente + + Informações detalhadas de {viewingPatient.nome}. + + +
+
+ + {viewingPatient.nome} +
+
+ + {viewingPatient.cpf} +
+
+ + {viewingPatient.telefone} +
+
+ + + {`${viewingPatient.endereco.logradouro}, ${viewingPatient.endereco.numero} - ${viewingPatient.endereco.bairro}, ${viewingPatient.endereco.cidade} - ${viewingPatient.endereco.estado}`} + +
+
+ + {viewingPatient.observacoes || "Nenhuma"} +
+
+ + + +
+
+ )} +
Mostrando {filtered.length} de {patients.length}
); diff --git a/susconecta/app/agenda/page.tsx b/susconecta/app/agenda/page.tsx index 1cf8b4e..7c6fabb 100644 --- a/susconecta/app/agenda/page.tsx +++ b/susconecta/app/agenda/page.tsx @@ -1,373 +1,35 @@ "use client"; -import { useState } from "react"; -import Link from "next/link"; -import { usePathname, useRouter } from "next/navigation"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Switch } from "@/components/ui/switch"; -import { Calendar } from "lucide-react"; - -import { - RotateCcw, - Accessibility, - Volume2, - Flame, - Settings, - Clipboard, - Search, - ChevronDown, - Upload, - FileDown, - Tag, - Save, -} from "lucide-react"; +import { useRouter } from "next/navigation"; +import { AppointmentForm } from "@/components/forms/appointment-form"; import HeaderAgenda from "@/components/agenda/HeaderAgenda"; import FooterAgenda from "@/components/agenda/FooterAgenda"; export default function NovoAgendamentoPage() { - const [bloqueio, setBloqueio] = useState(false); + const router = useRouter(); + + const handleSave = (data: any) => { + console.log("Salvando novo agendamento...", data); + // Aqui viria a chamada da API para criar um novo agendamento + alert("Novo agendamento salvo (simulado)!"); + router.push("/consultas"); // Volta para a lista após salvar + }; + + const handleCancel = () => { + router.back(); // Simplesmente volta para a página anterior + }; return ( - // ====== WRAPPER COM ESPAÇAMENTO GERAL ======
- {/* HEADER fora do
, usando o MESMO container do footer */} - - {/* Conteúdo */} -
- {/* ==== INFORMAÇÕES DO PACIENTE — layout idêntico ao print ==== */} -
-

Informações do paciente

- - {/* grade principal: 12 colunas para controlar as larguras */} -
- {/* ===== Linha 1 ===== */} -
- -
- - -
-
- -
- - -
- -
- - -
- - {/* ===== Linha 2 ===== */} - {/* 1ª coluna (span 6) com sub-grid: Data (5 col) + Telefone (7 col) */} -
-
-
- - -
- -
- -
- - -
-
-
-
- - {/* 2ª coluna da linha 2: E-mail (span 6) */} -
- - -
- - {/* ===== Linha 3 ===== */} -
- -
- - -
-
- -
-
- - -
-
- - -
-
-
- - {/* link Informações adicionais */} - - - {/* barra Documentos e anexos */} -
- Documentos e anexos -
- - - -
-
-
- - {/* ==== INFORMAÇÕES DO ATENDIMENTO ==== */} -
-

Informações do atendimento

- - {/* GRID PRINCIPAL: 12 colunas */} -
- {/* COLUNA ESQUERDA (span 6) */} -
- {/* Nome do profissional */} -
- -
- - - - RA - -
-
- -
- {/* Unidade */} -
- - -
- - {/* Data com ícone */} -
- -
- - -
-
-
- - {/* Início / Término / Profissional solicitante (na mesma linha) */} -
- {/* Início (maior) */} -
- - -
- - {/* Término (maior) */} -
- - -
- - {/* Profissional solicitante */} -
- -
- {/* ícone de busca à esquerda */} - - - - - - - - - - -
-
-
-
- - {/* COLUNA DIREITA — altura/posição como a imagem 1 */} -
- {/* toolbar */} -
- - - - - -
- - {/* Tipo de atendimento + campo de busca */} -
-
- - -
- -
- - - -
-
- - {/* Observações + imprimir */} -
- - -
- - {/* Textarea mais baixo e compacto */} -