diff --git a/app/doctor/consultas/page.tsx b/app/doctor/consultas/page.tsx index 4cd5ebf..9332a8b 100644 --- a/app/doctor/consultas/page.tsx +++ b/app/doctor/consultas/page.tsx @@ -4,7 +4,6 @@ import type React from "react"; import { useState, useEffect, useMemo } from "react"; -import DoctorLayout from "@/components/doctor-layout"; import { useAuthLayout } from "@/hooks/useAuthLayout"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { patientsService } from "@/services/patientsApi.mjs"; @@ -19,6 +18,7 @@ import { Clock, Calendar as CalendarIcon, User, X, RefreshCw, Loader2, MapPin, P import { format, isFuture, parseISO, isValid, isToday, isTomorrow } from "date-fns"; import { ptBR } from "date-fns/locale"; import { toast } from "sonner"; +import Sidebar from "@/components/Sidebar"; // Interfaces (sem alteração) interface EnrichedAppointment { @@ -129,11 +129,11 @@ export default function DoctorAppointmentsPage() { }; if (isAuthLoading) { - return
Carregando...
; + return
Carregando...
; } return ( - +

Agenda Médica

@@ -224,6 +224,6 @@ export default function DoctorAppointmentsPage() {
-
+ ); } \ No newline at end of file diff --git a/app/doctor/dashboard/page.tsx b/app/doctor/dashboard/page.tsx index cf9bad5..060d8e4 100644 --- a/app/doctor/dashboard/page.tsx +++ b/app/doctor/dashboard/page.tsx @@ -1,6 +1,5 @@ "use client"; -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, Trash2 } from "lucide-react"; @@ -14,6 +13,7 @@ import { AvailabilityService } from "@/services/availabilityApi.mjs"; import { exceptionsService } from "@/services/exceptionApi.mjs"; import { doctorsService } from "@/services/doctorsApi.mjs"; import { usersService } from "@/services/usersApi.mjs"; +import Sidebar from "@/components/Sidebar"; type Availability = { id: string; @@ -231,7 +231,7 @@ export default function PatientDashboard() { }, [availability]); return ( - +

Dashboard

@@ -409,6 +409,6 @@ export default function PatientDashboard() {
- + ); } diff --git a/app/doctor/disponibilidade/excecoes/page.tsx b/app/doctor/disponibilidade/excecoes/page.tsx index 3e7b316..932cb3f 100644 --- a/app/doctor/disponibilidade/excecoes/page.tsx +++ b/app/doctor/disponibilidade/excecoes/page.tsx @@ -3,14 +3,12 @@ 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 { Calendar as CalendarIcon, RefreshCw } from "lucide-react"; import { useRouter } from "next/navigation"; import { toast } from "@/hooks/use-toast"; import { exceptionsService } from "@/services/exceptionApi.mjs"; @@ -19,6 +17,7 @@ import { exceptionsService } from "@/services/exceptionApi.mjs"; import { Calendar } from "@/components/ui/calendar"; import { format } from "date-fns"; // Usaremos o date-fns para formatação e comparação de datas import { doctorsService } from "@/services/doctorsApi.mjs"; +import Sidebar from "@/components/Sidebar"; type Doctor = { id: string; @@ -147,7 +146,7 @@ export default function ExceptionPage() { const displayDate = selectedCalendarDate ? new Date(selectedCalendarDate).toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long" }) : "Selecione uma data"; return ( - +

Adicione exceções

@@ -254,6 +253,6 @@ export default function ExceptionPage() {
-
+ ); } diff --git a/app/doctor/disponibilidade/page.tsx b/app/doctor/disponibilidade/page.tsx index 36f0e98..ff0ce77 100644 --- a/app/doctor/disponibilidade/page.tsx +++ b/app/doctor/disponibilidade/page.tsx @@ -7,7 +7,6 @@ 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 DoctorLayout from "@/components/doctor-layout"; import { AvailabilityService } from "@/services/availabilityApi.mjs"; import { usersService } from "@/services/usersApi.mjs"; @@ -17,9 +16,10 @@ import { toast } from "@/hooks/use-toast"; import { useRouter } from "next/navigation"; import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; -import { Eye, Edit, Calendar, Trash2 } from "lucide-react"; +import { Edit, Trash2 } from "lucide-react"; import { AvailabilityEditModal } from "@/components/ui/availability-edit-modal"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; +import Sidebar from "@/components/Sidebar"; // ... (Interfaces de tipo omitidas para brevidade, pois não foram alteradas) @@ -323,7 +323,7 @@ export default function AvailabilityPage() { }; return ( - +
@@ -506,6 +506,6 @@ export default function AvailabilityPage() { onSubmit={handleEdit} /> - + ); } \ No newline at end of file diff --git a/app/doctor/medicos/[id]/editar/page.tsx b/app/doctor/medicos/[id]/editar/page.tsx index 0049db2..7bc5595 100644 --- a/app/doctor/medicos/[id]/editar/page.tsx +++ b/app/doctor/medicos/[id]/editar/page.tsx @@ -12,7 +12,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { ArrowLeft, Save } from "lucide-react"; import Link from "next/link"; -import DoctorLayout from "@/components/doctor-layout"; +import Sidebar from "@/components/Sidebar"; // Mock data - in a real app, this would come from an API const mockDoctors = [ @@ -124,7 +124,7 @@ export default function EditarMedicoPage() { }; return ( - +
@@ -512,6 +512,6 @@ export default function EditarMedicoPage() {
-
+ ); } diff --git a/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx b/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx index 02f1e6c..52525fd 100644 --- a/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx +++ b/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx @@ -2,7 +2,6 @@ import { useParams, useRouter } from "next/navigation"; import { useState, useEffect } from "react"; -import DoctorLayout from "@/components/doctor-layout"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; @@ -17,6 +16,7 @@ import { format } from "date-fns"; import TiptapEditor from "@/components/ui/tiptap-editor"; import { Skeleton } from "@/components/ui/skeleton"; import { reportsApi } from "@/services/reportsApi.mjs"; +import Sidebar from "@/components/Sidebar"; export default function EditarLaudoPage() { const router = useRouter(); @@ -108,7 +108,7 @@ export default function EditarLaudoPage() { if (loading) { return ( - +
@@ -130,12 +130,12 @@ export default function EditarLaudoPage() {
-
+ ) } return ( - +
@@ -232,6 +232,6 @@ export default function EditarLaudoPage() {
-
+ ); } \ No newline at end of file diff --git a/app/doctor/medicos/[id]/laudos/novo/page.tsx b/app/doctor/medicos/[id]/laudos/novo/page.tsx index 215d3a6..9c9b8a9 100644 --- a/app/doctor/medicos/[id]/laudos/novo/page.tsx +++ b/app/doctor/medicos/[id]/laudos/novo/page.tsx @@ -17,7 +17,7 @@ import { format } from "date-fns"; import TiptapEditor from "@/components/ui/tiptap-editor"; import { reportsApi } from "@/services/reportsApi.mjs"; -import DoctorLayout from "@/components/doctor-layout"; +import Sidebar from "@/components/Sidebar"; @@ -97,7 +97,7 @@ export default function NovoLaudoPage() { }; return ( - +
@@ -189,6 +189,6 @@ export default function NovoLaudoPage() {
-
+ ); } \ No newline at end of file diff --git a/app/doctor/medicos/[id]/laudos/page.tsx b/app/doctor/medicos/[id]/laudos/page.tsx index 981b250..848bb40 100644 --- a/app/doctor/medicos/[id]/laudos/page.tsx +++ b/app/doctor/medicos/[id]/laudos/page.tsx @@ -8,7 +8,7 @@ import Link from 'next/link'; import { useParams } from 'next/navigation'; import { api } from '@/services/api.mjs'; import { reportsApi } from '@/services/reportsApi.mjs'; -import DoctorLayout from '@/components/doctor-layout'; +import Sidebar from '@/components/Sidebar'; export default function LaudosPage() { const [patient, setPatient] = useState(null); @@ -49,7 +49,7 @@ export default function LaudosPage() { const paginate = (pageNumber) => setCurrentPage(pageNumber); return ( - +
{loading ? (

Carregando...

@@ -123,6 +123,6 @@ export default function LaudosPage() { )}
-
+ ); } \ No newline at end of file diff --git a/app/doctor/medicos/novo/page.tsx b/app/doctor/medicos/novo/page.tsx index dee0314..8aa1b75 100644 --- a/app/doctor/medicos/novo/page.tsx +++ b/app/doctor/medicos/novo/page.tsx @@ -9,7 +9,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Upload, Plus, X, ChevronDown } from "lucide-react"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import DoctorLayout from "@/components/doctor-layout"; +import Sidebar from "@/components/Sidebar"; export default function NovoMedicoPage() { const [anexosOpen, setAnexosOpen] = useState(false); @@ -24,7 +24,7 @@ export default function NovoMedicoPage() { }; return ( - +
@@ -466,6 +466,6 @@ export default function NovoMedicoPage() {
- + ); } diff --git a/app/doctor/medicos/page.tsx b/app/doctor/medicos/page.tsx index bc18221..cd357a1 100644 --- a/app/doctor/medicos/page.tsx +++ b/app/doctor/medicos/page.tsx @@ -2,25 +2,14 @@ "use client"; import { useEffect, useState, useCallback } from "react"; -import DoctorLayout from "@/components/doctor-layout"; import Link from "next/link"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Eye, Edit, Calendar, Trash2, Loader2 } from "lucide-react"; import { api } from "@/services/api.mjs"; import { PatientDetailsModal } from "@/components/ui/patient-details-modal"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; +import Sidebar from "@/components/Sidebar"; interface Paciente { id: string; @@ -171,7 +160,7 @@ export default function PacientesPage() { }, [fetchPacientes]); return ( - +
{/* Cabeçalho */}
@@ -363,6 +352,6 @@ export default function PacientesPage() { isOpen={isModalOpen} onClose={handleCloseModal} /> - + ); } \ No newline at end of file diff --git a/app/finance/home/page.tsx b/app/finance/home/page.tsx index c9e0567..1c07932 100644 --- a/app/finance/home/page.tsx +++ b/app/finance/home/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; -import FinancierLayout from "@/components/finance-layout"; +import Sidebar from "@/components/Sidebar"; interface Paciente { id: string; @@ -14,43 +14,10 @@ interface Paciente { } export default function PacientesPage() { - const [pacientes, setPacientes] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - async function fetchPacientes() { - try { - setLoading(true); - setError(null); - const res = await fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes"); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const json = await res.json(); - const items = Array.isArray(json?.data) ? json.data : []; - - const mapped = items.map((p: any) => ({ - id: String(p.id ?? ""), - nome: p.nome ?? "", - telefone: p?.contato?.celular ?? p?.contato?.telefone1 ?? p?.telefone ?? "", - cidade: p?.endereco?.cidade ?? p?.cidade ?? "", - estado: p?.endereco?.estado ?? p?.estado ?? "", - ultimoAtendimento: p.ultimo_atendimento ?? p.ultimoAtendimento ?? "", - proximoAtendimento: p.proximo_atendimento ?? p.proximoAtendimento ?? "", - })); - - setPacientes(mapped); - } catch (e: any) { - setError(e?.message || "Erro ao carregar pacientes"); - } finally { - setLoading(false); - } - } - fetchPacientes(); - }, []); return ( - +
-
+ ); } diff --git a/app/login/page.tsx b/app/login/page.tsx index b193142..6232d58 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,82 +1,247 @@ // Caminho: app/login/page.tsx + +"use client"; + + +import {usersService} from "@/services/usersApi.mjs"; import { LoginForm } from "@/components/LoginForm"; import Link from "next/link"; import Image from "next/image"; import { Button } from "@/components/ui/button"; -import { ArrowLeft } from "lucide-react"; // Importa o ícone de seta +import { Input } from "@/components/ui/input"; +import { ArrowLeft, X } from "lucide-react"; +import { useState } from "react"; +import RenderFromTemplateContext from "next/dist/client/components/render-from-template-context"; + export default function LoginPage() { - return ( -
- - {/* PAINEL ESQUERDO: O Formulário */} -
- - {/* Link para Voltar */} -
- - - Voltar à página inicial - -
+ const [isModalOpen, setIsModalOpen] = useState(false); + const [email, setEmail] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null); - {/* O contêiner principal que agora terá a sombra e o estilo de card */} -
-
-

Acesse sua conta

-

Bem-vindo(a) de volta ao MedConnect!

+ + const handleOpenModal = () => { + // Tenta pegar o email do input do formulário de login + const emailInput = document.querySelector('input[type="email"]') as HTMLInputElement; + if (emailInput?.value) { + setEmail(emailInput.value); + } + setIsModalOpen(true); + }; + + + const handleResetPassword = async () => { + if (!email.trim()) { + setMessage({ type: "error", text: "Por favor, insira um e-mail válido." }); + return; + } + + + setIsLoading(true); + setMessage(null); + + + try { + // Chama o método que já faz o fetch corretamente + const data = await usersService.resetPassword(email); + + + console.log("Resposta resetPassword:", data); + + + setMessage({ + type: "success", + text: "E-mail de recuperação enviado! Verifique sua caixa de entrada.", + }); + + + setTimeout(() => { + setIsModalOpen(false); + setMessage(null); + setEmail(""); + }, 2000); + } catch (error) { + console.error("Erro no reset de senha:", error); + setMessage({ + type: "error", + text: + error instanceof Error + ? error.message + : "Erro ao enviar e-mail. Tente novamente.", + }); + } finally { + setIsLoading(false); + } +}; + + + + + const closeModal = () => { + setIsModalOpen(false); + setMessage(null); + setEmail(""); + }; + + + return ( + <> +
+ + {/* PAINEL ESQUERDO: O Formulário */} +
+ + {/* Link para Voltar */} +
+ + + Voltar à página inicial +
- - {/* Children para o LoginForm */} -
- - + + {/* O contêiner principal que agora terá a sombra e o estilo de card */} +
+
+

Acesse sua conta

+

Bem-vindo(a) de volta ao MedConnect!

+
+ + + + {/* Children para o LoginForm */} +
+ +
+
+ + +
+ Não tem uma conta de paciente? + + + Crie uma agora
- - -
- Não tem uma conta de paciente? - - - Crie uma agora - -
-
- {/* PAINEL DIREITO: A Imagem e Branding */} -
- {/* Usamos o componente para otimização e performance */} - Médica utilizando um tablet na clínica MedConnect - {/* Camada de sobreposição para escurecer a imagem e destacar o texto */} -
+ + {/* PAINEL DIREITO: A Imagem e Branding */} +
+ {/* Usamos o componente para otimização e performance */} + Médica utilizando um tablet na clínica MedConnect + {/* Camada de sobreposição para escurecer a imagem e destacar o texto */} +
{/* BLOCO DE NOME ADICIONADO */}
-

+

MedConnect -

+

- Tecnologia e Cuidado a Serviço da Sua Saúde. + Tecnologia e Cuidado a Serviço da Sua Saúde.

- Acesse seu portal para uma experiência de saúde integrada, segura e eficiente. + Acesse seu portal para uma experiência de saúde integrada, segura e eficiente.

+
+ +
-
+ + {/* Modal de Recuperação de Senha */} + {isModalOpen && ( +
+
+ {/* Botão de fechar */} + + + + {/* Cabeçalho */} +
+

Recuperar Senha

+

+ Insira seu e-mail e enviaremos um link para redefinir sua senha. +

+
+ + + {/* Input de e-mail */} +
+
+ + setEmail(e.target.value)} + placeholder="seu@email.com" + disabled={isLoading} + className="w-full" + /> +
+ + + {/* Mensagem de feedback */} + {message && ( +
+ {message.text} +
+ )} + + + {/* Botões */} +
+ + +
+
+
+
+ )} + ); -} \ No newline at end of file +} diff --git a/app/manager/dashboard/page.tsx b/app/manager/dashboard/page.tsx index df56541..6558bd0 100644 --- a/app/manager/dashboard/page.tsx +++ b/app/manager/dashboard/page.tsx @@ -1,6 +1,5 @@ "use client"; -import ManagerLayout from "@/components/manager-layout"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Calendar, Clock, Plus, User } from "lucide-react"; @@ -8,6 +7,7 @@ import Link from "next/link"; import React, { useState, useEffect } from "react"; import { usersService } from "services/usersApi.mjs"; import { doctorsService } from "services/doctorsApi.mjs"; +import Sidebar from "@/components/Sidebar"; export default function ManagerDashboard() { // 🔹 Estados para usuários @@ -55,7 +55,7 @@ export default function ManagerDashboard() { }, []); return ( - +
{/* Cabeçalho */}
@@ -185,6 +185,6 @@ export default function ManagerDashboard() {
-
+ ); } diff --git a/app/manager/home/[id]/editar/page.tsx b/app/manager/home/[id]/editar/page.tsx index 6619f67..89a5afb 100644 --- a/app/manager/home/[id]/editar/page.tsx +++ b/app/manager/home/[id]/editar/page.tsx @@ -10,7 +10,7 @@ import { Textarea } from "@/components/ui/textarea" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Checkbox } from "@/components/ui/checkbox" import { Save, Loader2, ArrowLeft } from "lucide-react" -import ManagerLayout from "@/components/manager-layout" +import Sidebar from "@/components/Sidebar" import { doctorsService } from "services/doctorsApi.mjs"; const UF_LIST = ["AC", "AL", "AP", "AM", "BA", "CE", "DF", "ES", "GO", "MA", "MT", "MS", "MG", "PA", "PB", "PR", "PE", "PI", "RJ", "RN", "RS", "RO", "RR", "SC", "SP", "SE", "TO"]; @@ -207,17 +207,17 @@ export default function EditarMedicoPage() { }; if (loading) { return ( - +

Carregando dados do médico...

-
+ ); } return ( - +
@@ -487,6 +487,6 @@ export default function EditarMedicoPage() {
- + ); } \ No newline at end of file diff --git a/app/manager/home/page.tsx b/app/manager/home/page.tsx index 03b64a5..0d35193 100644 --- a/app/manager/home/page.tsx +++ b/app/manager/home/page.tsx @@ -1,25 +1,16 @@ "use client"; import React, { useEffect, useState, useCallback, useMemo } from "react" -import ManagerLayout from "@/components/manager-layout"; import Link from "next/link" import { useRouter } from "next/navigation"; import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" -import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog" +import { Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react" +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog" import { doctorsService } from "services/doctorsApi.mjs"; +import Sidebar from "@/components/Sidebar"; interface Doctor { @@ -193,7 +184,7 @@ export default function DoctorsPage() { return ( - +
{/* Cabeçalho */} @@ -430,6 +421,6 @@ export default function DoctorsPage() {
-
+ ); } \ No newline at end of file diff --git a/app/manager/pacientes/[id]/editar/page.tsx b/app/manager/pacientes/[id]/editar/page.tsx index 254be97..51858d7 100644 --- a/app/manager/pacientes/[id]/editar/page.tsx +++ b/app/manager/pacientes/[id]/editar/page.tsx @@ -13,9 +13,8 @@ import { Checkbox } from "@/components/ui/checkbox"; import { ArrowLeft, Save, Trash2, Paperclip, Upload } from "lucide-react"; import Link from "next/link"; import { useToast } from "@/hooks/use-toast"; -import SecretaryLayout from "@/components/secretary-layout"; import { patientsService } from "@/services/patientsApi.mjs"; -import { json } from "stream/consumers"; +import Sidebar from "@/components/Sidebar"; export default function EditarPacientePage() { const router = useRouter(); @@ -247,7 +246,7 @@ export default function EditarPacientePage() { }; return ( - +
@@ -677,6 +676,6 @@ export default function EditarPacientePage() {
-
+ ); } diff --git a/app/manager/pacientes/page.tsx b/app/manager/pacientes/page.tsx index 5fb159b..e10990d 100644 --- a/app/manager/pacientes/page.tsx +++ b/app/manager/pacientes/page.tsx @@ -6,10 +6,10 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"; +import { Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { patientsService } from "@/services/patientsApi.mjs"; -import ManagerLayout from "@/components/manager-layout"; +import Sidebar from "@/components/Sidebar"; // Defina o tamanho da página. const PAGE_SIZE = 5; @@ -145,7 +145,7 @@ export default function PacientesPage() { }; return ( - +
{/* Header (Responsividade OK) */}
@@ -449,6 +449,6 @@ export default function PacientesPage() {
- + ); } \ No newline at end of file diff --git a/app/manager/usuario/[id]/editar/page.tsx b/app/manager/usuario/[id]/editar/page.tsx index 50cb953..030891d 100644 --- a/app/manager/usuario/[id]/editar/page.tsx +++ b/app/manager/usuario/[id]/editar/page.tsx @@ -8,7 +8,7 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Save, Loader2, ArrowLeft } from "lucide-react" -import ManagerLayout from "@/components/manager-layout" +import Sidebar from "@/components/Sidebar" // Mock user service for demonstration. Replace with your actual API service. const usersService = { @@ -155,17 +155,17 @@ export default function EditarUsuarioPage() { if (loading) { return ( - +

Carregando dados do usuário...

-
+ ); } return ( - +
@@ -274,6 +274,6 @@ export default function EditarUsuarioPage() {
- + ); } \ No newline at end of file diff --git a/app/manager/usuario/novo/page.tsx b/app/manager/usuario/novo/page.tsx index 4a128bd..e4176c0 100644 --- a/app/manager/usuario/novo/page.tsx +++ b/app/manager/usuario/novo/page.tsx @@ -10,11 +10,11 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Save, Loader2 } from "lucide-react"; -import ManagerLayout from "@/components/manager-layout"; import { usersService } from "@/services/usersApi.mjs"; import { doctorsService } from "@/services/doctorsApi.mjs"; import { login } from "services/api.mjs"; import { isValidCPF } from "@/lib/utils"; // 1. IMPORTAÇÃO DA FUNÇÃO DE VALIDAÇÃO +import Sidebar from "@/components/Sidebar"; interface UserFormData { email: string; @@ -135,7 +135,7 @@ export default function NovoUsuarioPage() { const isMedico = formData.papel === "medico"; return ( - +
@@ -236,6 +236,6 @@ export default function NovoUsuarioPage() {
- + ); } \ No newline at end of file diff --git a/app/manager/usuario/page.tsx b/app/manager/usuario/page.tsx index 805fb0c..634bf3d 100644 --- a/app/manager/usuario/page.tsx +++ b/app/manager/usuario/page.tsx @@ -2,28 +2,14 @@ "use client"; import React, { useEffect, useState, useCallback } from "react"; -import ManagerLayout from "@/components/manager-layout"; import Link from "next/link"; import { Button } from "@/components/ui/button"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Plus, Eye, Filter, Loader2 } from "lucide-react"; -import { - AlertDialog, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; +import { AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { api, login } from "services/api.mjs"; import { usersService } from "services/usersApi.mjs"; +import Sidebar from "@/components/Sidebar"; interface FlatUser { id: string; @@ -192,7 +178,7 @@ export default function UsersPage() { return ( - +
{/* Header */} @@ -424,6 +410,6 @@ export default function UsersPage() {
-
+ ); } \ No newline at end of file diff --git a/app/patient/appointments/page.tsx b/app/patient/appointments/page.tsx index 3e271b6..23f8ee6 100644 --- a/app/patient/appointments/page.tsx +++ b/app/patient/appointments/page.tsx @@ -1,7 +1,6 @@ "use client"; import { useState, useEffect } from "react"; -import PatientLayout from "@/components/patient-layout"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -10,6 +9,7 @@ import { toast } from "sonner"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { usersService } from "@/services/usersApi.mjs"; +import Sidebar from "@/components/Sidebar"; // Tipagem correta para o usuário interface UserProfile { @@ -129,7 +129,7 @@ export default function PatientAppointmentsPage() { }; return ( - +
@@ -185,6 +185,6 @@ export default function PatientAppointmentsPage() { )}
- + ); } diff --git a/app/patient/dashboard/page.tsx b/app/patient/dashboard/page.tsx index c829041..c0f9266 100644 --- a/app/patient/dashboard/page.tsx +++ b/app/patient/dashboard/page.tsx @@ -1,12 +1,12 @@ -import PatientLayout from "@/components/patient-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 Link from "next/link" +import Sidebar from "@/components/Sidebar" export default function PatientDashboard() { return ( - +

Dashboard

@@ -108,6 +108,6 @@ export default function PatientDashboard() {
-
+ ) } diff --git a/app/patient/profile/page.tsx b/app/patient/profile/page.tsx index 2dfa00b..a81db2a 100644 --- a/app/patient/profile/page.tsx +++ b/app/patient/profile/page.tsx @@ -3,7 +3,7 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import PatientLayout from "@/components/patient-layout"; +import Sidebar from "@/components/Sidebar" import { useAuthLayout } from "@/hooks/useAuthLayout"; import { patientsService } from "@/services/patientsApi.mjs"; import { api } from "@/services/api.mjs"; @@ -121,11 +121,11 @@ export default function PatientProfile() { }; if (isAuthLoading || !patientData) { - return
Carregando seus dados...
; + return
Carregando seus dados...
; } return ( - +
@@ -198,6 +198,6 @@ export default function PatientProfile() {
-
- ); + + ) } \ No newline at end of file diff --git a/app/patient/reports/page.tsx b/app/patient/reports/page.tsx index dc12193..d1fa91c 100644 --- a/app/patient/reports/page.tsx +++ b/app/patient/reports/page.tsx @@ -1,13 +1,13 @@ "use client" import { useState, useEffect } from "react" -import PatientLayout from "@/components/patient-layout" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { toast } from "@/hooks/use-toast" import { FileText, Download, Eye, Calendar, User, X } from "lucide-react" +import Sidebar from "@/components/Sidebar" interface Report { id: string @@ -287,7 +287,7 @@ export default function ReportsPage() { const pendingReports = reports.filter((report) => report.status === "pendente") return ( - +

Meus Laudos

@@ -536,6 +536,6 @@ export default function ReportsPage() {
- + ) } diff --git a/app/patient/schedule/page.tsx b/app/patient/schedule/page.tsx index 369b787..55fb7f8 100644 --- a/app/patient/schedule/page.tsx +++ b/app/patient/schedule/page.tsx @@ -1,11 +1,12 @@ // app/patient/appointments/page.tsx -import PatientLayout from "@/components/patient-layout"; +import Sidebar from "@/components/Sidebar"; import ScheduleForm from "@/components/schedule/schedule-form"; + export default function PatientAppointments() { return ( - + - + ); } diff --git a/app/secretary/appointments/page.tsx b/app/secretary/appointments/page.tsx index 679ba8e..9dbb676 100644 --- a/app/secretary/appointments/page.tsx +++ b/app/secretary/appointments/page.tsx @@ -1,20 +1,17 @@ "use client"; import { useState, useEffect } from "react"; -import SecretaryLayout from "@/components/secretary-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 { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Dialog } from "@/components/ui/dialog"; import { Calendar, Clock, MapPin, Phone, User, Trash2, Pencil } from "lucide-react"; import { toast } from "sonner"; import Link from "next/link"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { patientsService } from "@/services/patientsApi.mjs"; import { doctorsService } from "@/services/doctorsApi.mjs"; +import Sidebar from "@/components/Sidebar"; export default function SecretaryAppointments() { const [appointments, setAppointments] = useState([]); @@ -144,7 +141,7 @@ export default function SecretaryAppointments() { const appointmentStatuses = ["requested", "confirmed", "checked_in", "completed", "cancelled", "no_show"]; return ( - +
@@ -225,6 +222,6 @@ export default function SecretaryAppointments() { {/* ... (código do modal de deleção) ... */} - + ); } \ No newline at end of file diff --git a/app/secretary/dashboard/page.tsx b/app/secretary/dashboard/page.tsx index e37141c..ff47223 100644 --- a/app/secretary/dashboard/page.tsx +++ b/app/secretary/dashboard/page.tsx @@ -1,10 +1,6 @@ "use client"; -import SecretaryLayout from "@/components/secretary-layout"; -import { - Card, - CardContent, - CardDescription, +import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; @@ -14,6 +10,7 @@ import Link from "next/link"; import React, { useState, useEffect } from "react"; import { patientsService } from "@/services/patientsApi.mjs"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; +import Sidebar from "@/components/Sidebar"; export default function SecretaryDashboard() { // Estados @@ -100,7 +97,7 @@ export default function SecretaryDashboard() { }, []); return ( - +
{/* Cabeçalho */}
@@ -299,6 +296,6 @@ export default function SecretaryDashboard() {
-
+ ); } diff --git a/app/secretary/pacientes/[id]/editar/page.tsx b/app/secretary/pacientes/[id]/editar/page.tsx index 00f11fe..fa7e5d9 100644 --- a/app/secretary/pacientes/[id]/editar/page.tsx +++ b/app/secretary/pacientes/[id]/editar/page.tsx @@ -13,9 +13,8 @@ import { Checkbox } from "@/components/ui/checkbox"; import { ArrowLeft, Save, Trash2, Paperclip, Upload } from "lucide-react"; import Link from "next/link"; import { useToast } from "@/hooks/use-toast"; -import SecretaryLayout from "@/components/secretary-layout"; import { patientsService } from "@/services/patientsApi.mjs"; -import { json } from "stream/consumers"; +import Sidebar from "@/components/Sidebar"; export default function EditarPacientePage() { const router = useRouter(); @@ -247,7 +246,7 @@ export default function EditarPacientePage() { }; return ( - +
@@ -677,6 +676,6 @@ export default function EditarPacientePage() {
-
+ ); } diff --git a/app/secretary/pacientes/novo/page.tsx b/app/secretary/pacientes/novo/page.tsx index a028ea6..e7e94cd 100644 --- a/app/secretary/pacientes/novo/page.tsx +++ b/app/secretary/pacientes/novo/page.tsx @@ -1,4 +1,3 @@ -// Caminho: app/(manager)/usuario/novo/page.tsx "use client"; import { useState } from "react"; @@ -7,13 +6,9 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -// O Select foi removido pois não é mais necessário import { Save, Loader2 } from "lucide-react"; -import ManagerLayout from "@/components/manager-layout"; -// Os imports originais foram mantidos, como solicitado import { usersService } from "services/usersApi.mjs"; -import { doctorsService } from "services/doctorsApi.mjs"; -import { login } from "services/api.mjs"; +import Sidebar from "@/components/Sidebar"; // Interface simplificada para refletir apenas os campos necessários interface UserFormData { @@ -97,7 +92,7 @@ export default function NovoUsuarioPage() { }; return ( - +
@@ -167,6 +162,6 @@ export default function NovoUsuarioPage() {
- + ); } \ No newline at end of file diff --git a/app/secretary/pacientes/page.tsx b/app/secretary/pacientes/page.tsx index 623a966..bec32d8 100644 --- a/app/secretary/pacientes/page.tsx +++ b/app/secretary/pacientes/page.tsx @@ -8,8 +8,8 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; -import SecretaryLayout from "@/components/secretary-layout"; import { patientsService } from "@/services/patientsApi.mjs"; +import Sidebar from "@/components/Sidebar"; // Defina o tamanho da página. const PAGE_SIZE = 5; @@ -145,7 +145,7 @@ export default function PacientesPage() { }; return ( - +
{/* Header (Responsividade OK) */}
@@ -457,6 +457,6 @@ export default function PacientesPage() {
- + ); } \ No newline at end of file diff --git a/app/secretary/schedule/page.tsx b/app/secretary/schedule/page.tsx index c79739e..b35a886 100644 --- a/app/secretary/schedule/page.tsx +++ b/app/secretary/schedule/page.tsx @@ -1,11 +1,11 @@ -import SecretaryLayout from "@/components/secretary-layout"; +import Sidebar from "@/components/Sidebar"; import ScheduleForm from "@/components/schedule/schedule-form"; export default function SecretaryAppointments() { return ( - + - + ); } diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx index b76ed32..420ab39 100644 --- a/components/LoginForm.tsx +++ b/components/LoginForm.tsx @@ -13,6 +13,7 @@ import { Label } from "@/components/ui/label"; import { Card, CardContent } from "@/components/ui/card"; import { useToast } from "@/hooks/use-toast"; import { Eye, EyeOff, Mail, Lock, Loader2 } from "lucide-react"; +import { usersService } from "@/services/usersApi.mjs"; interface LoginFormProps { children?: React.ReactNode; @@ -31,8 +32,12 @@ export function LoginForm({ children }: LoginFormProps) { const { toast } = useToast(); const [userRoles, setUserRoles] = useState([]); - - // *** MUDANÇA 1: A função agora recebe o objeto 'user' como parâmetro *** + const [authenticatedUser, setAuthenticatedUser] = useState(null); + + /** + * --- NOVA FUNÇÃO --- + * Finaliza o login com o perfil de dashboard escolhido e redireciona. + */ const handleRoleSelection = (selectedDashboardRole: string, user: any) => { if (!user) { toast({ title: "Erro de Sessão", description: "Não foi possível encontrar os dados do usuário. Tente novamente.", variant: "destructive" }); @@ -47,12 +52,12 @@ export function LoginForm({ children }: LoginFormProps) { localStorage.setItem("user_info", JSON.stringify(completeUserInfo)); let redirectPath = ""; - switch (roleInLowerCase) { - case "manager": redirectPath = "/manager/home"; break; - case "doctor": redirectPath = "/doctor/medicos"; break; - case "secretary": redirectPath = "/secretary/pacientes"; break; - case "patient": redirectPath = "/patient/dashboard"; break; - case "finance": redirectPath = "/finance/home"; break; + switch (selectedDashboardRole) { + case "gestor": redirectPath = "/manager/dashboard"; break; + case "admin": redirectPath = "/manager/dashboard"; break; + case "medico": redirectPath = "/doctor/dashboard"; break; + case "secretaria": redirectPath = "/secretary/dashboard"; break; + case "paciente": redirectPath = "/patient/dashboard"; break; } if (redirectPath) { @@ -77,55 +82,16 @@ export function LoginForm({ children }: LoginFormProps) { } const rolesData = await api.get(`/rest/v1/user_roles?user_id=eq.${user.id}&select=role`); - if (!rolesData || rolesData.length === 0) { + + const me = await usersService.getMeSimple() + console.log(me.roles) + + if (!me.roles || me.roles.length === 0) { throw new Error("Nenhum perfil de acesso foi encontrado para este usuário."); } - const rolesFromApi: string[] = rolesData.map((r: any) => r.role); - - // *** MUDANÇA 2: Passamos o objeto 'user' diretamente para a função de seleção *** - const handleSelectionWithUser = (role: string) => handleRoleSelection(role, user); + handleRoleSelection(me.roles[0], user); - if (rolesFromApi.includes("admin")) { - const allRoles = ["manager", "doctor", "secretary", "patient", "finance"]; - setUserRoles(allRoles); - // Atualizamos o onClick para usar a nova função que já tem o 'user' - const roleButtons = allRoles.map((role) => ( - - )); - // Precisamos de um estado para renderizar os botões - setRoleSelectionUI(roleButtons); - setIsLoading(false); - return; - } - - const displayRoles = new Set(); - rolesFromApi.forEach((role) => { - switch (role) { - case "gestor": displayRoles.add("manager"); displayRoles.add("finance"); break; - case "medico": displayRoles.add("doctor"); break; - case "secretaria": displayRoles.add("secretary"); break; - case "paciente": displayRoles.add("patient"); break; - } - }); - - const finalRoles = Array.from(displayRoles); - - if (finalRoles.length === 1) { - handleSelectionWithUser(finalRoles[0]); - } else { - setUserRoles(finalRoles); - // Atualizamos o onClick aqui também - const roleButtons = finalRoles.map((role) => ( - - )); - setRoleSelectionUI(roleButtons); - setIsLoading(false); - } } catch (error) { localStorage.removeItem("token"); localStorage.removeItem("user_info"); @@ -172,7 +138,11 @@ export function LoginForm({ children }: LoginFormProps) {

Você tem múltiplos perfis

Selecione com qual perfil deseja entrar:

- {roleSelectionUI} + {userRoles.map((role) => ( + + ))}
)} diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx new file mode 100644 index 0000000..fa8ae1e --- /dev/null +++ b/components/Sidebar.tsx @@ -0,0 +1,291 @@ +// Caminho: [seu-caminho]/ManagerLayout.tsx +"use client"; + +import type React from "react"; +import { useState, useEffect } from "react"; +import { useRouter, usePathname } from "next/navigation"; +import Link from "next/link"; +import Cookies from "js-cookie"; // Mantido apenas para a limpeza de segurança no logout +import { api } from "@/services/api.mjs"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Search, Bell, Calendar, User, LogOut, ChevronLeft, ChevronRight, Home, CalendarCheck2, ClipboardPlus, SquareUserRound, CalendarClock, Users, SquareUser, ClipboardList, Stethoscope, ClipboardMinus } from "lucide-react"; +import SidebarUserSection from "@/components/ui/userToolTip"; + +interface UserData { + id: string; + email: string; + app_metadata: { + user_role: string; + }; + user_metadata: { + cpf: string; + email_verified: boolean; + full_name: string; + phone_mobile: string; + role: string; + }; + identities: { + identity_id: string; + id: string; + user_id: string; + provider: string; + }[]; + is_anonymous: boolean; +} + + +interface MenuItem { + href: string; + icon: React.ElementType; + label: string; +} + +interface SidebarProps { + children: React.ReactNode; +} + +export default function Sidebar({ children }: SidebarProps) { + const [userData, setUserData] = useState(); + const [role, setRole] = useState(); + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + const [showLogoutDialog, setShowLogoutDialog] = useState(false); + const router = useRouter(); + const pathname = usePathname(); + + useEffect(() => { + const userInfoString = localStorage.getItem("user_info"); + // --- ALTERAÇÃO 1: Buscando o token no localStorage --- + const token = localStorage.getItem("token"); + + if (userInfoString && token) { + const userInfo = JSON.parse(userInfoString); + + setUserData({ + id: userInfo.id ?? "", + email: userInfo.email ?? "", + app_metadata: { + user_role: userInfo.app_metadata?.user_role ?? "patient", + }, + user_metadata: { + cpf: userInfo.user_metadata?.cpf ?? "", + email_verified: userInfo.user_metadata?.email_verified ?? false, + full_name: userInfo.user_metadata?.full_name ?? "", + phone_mobile: userInfo.user_metadata?.phone_mobile ?? "", + role: userInfo.user_metadata?.role ?? "", + }, + identities: + userInfo.identities?.map((identity: any) => ({ + identity_id: identity.identity_id ?? "", + id: identity.id ?? "", + user_id: identity.user_id ?? "", + provider: identity.provider ?? "", + })) ?? [], + is_anonymous: userInfo.is_anonymous ?? false, + }); + setRole(userInfo.user_metadata?.role) + } else { + // O redirecionamento para /login já estava correto. Ótimo! + router.push("/login"); + } + }, [router]); + + useEffect(() => { + + + const handleResize = () => { + if (window.innerWidth < 1024) { + setSidebarCollapsed(true); + } else { + setSidebarCollapsed(false); + } + }; + handleResize(); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + const handleLogout = () => setShowLogoutDialog(true); + + // --- ALTERAÇÃO 2: A função de logout agora é MUITO mais simples --- + const confirmLogout = async () => { + try { + // Chama a função centralizada para fazer o logout no servidor + await api.logout(); + } catch (error) { + // O erro já é logado dentro da função api.logout, não precisamos fazer nada aqui + } finally { + // A responsabilidade do componente é apenas limpar o estado local e redirecionar + localStorage.removeItem("user_info"); + localStorage.removeItem("token"); + Cookies.remove("access_token"); // Limpeza de segurança + + setShowLogoutDialog(false); + router.push("/"); // Redireciona para a home + } + }; + + const cancelLogout = () => setShowLogoutDialog(false); + + const SetMenuItems = (role: any) => { + const patientItems: MenuItem[] = [ + { href: "/patient/dashboard", icon: Home, label: "Dashboard" }, + { href: "/patient/schedule", icon: CalendarClock, label: "Agendar Consulta" }, + { href: "/patient/appointments", icon: CalendarCheck2, label: "Minhas Consultas" }, + { href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" }, + { href: "/patient/profile", icon: SquareUser, label: "Meus Dados" }, + ] + + const doctorItems: MenuItem[] = [ + { href: "/doctor/dashboard", icon: Home, label: "Dashboard" }, + { href: "/doctor/medicos", icon: Users, label: "Gestão de Pacientes" }, + { href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, + { href: "/doctor/disponibilidade", icon: ClipboardList, label: "Disponibilidade" }, + ] + + const secretaryItems: MenuItem[] = [ + { href: "/secretary/dashboard", icon: Home, label: "Dashboard" }, + { href: "/secretary/appointments", icon: CalendarCheck2, label: "Consultas" }, + { href: "/secretary/schedule", icon: CalendarClock, label: "Agendar Consulta" }, + { href: "/secretary/pacientes", icon: Users, label: "Gestão de Pacientes" }, + ] + + const managerItems: MenuItem[] = [ + { href: "/manager/dashboard", icon: Home, label: "Dashboard" }, + { href: "#", icon: ClipboardMinus, label: "Relatórios gerenciais" }, + { href: "/manager/usuario", icon: Users, label: "Gestão de Usuários" }, + { href: "/manager/home", icon: Stethoscope, label: "Gestão de Médicos" }, + { href: "/manager/pacientes", icon: Users, label: "Gestão de Pacientes" }, + { href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, //adicionar botão de voltar pra pagina anterior + ] + + let menuItems: MenuItem[]; + switch (role) { + case "gestor": + menuItems = managerItems; + break; + case "admin": + menuItems = managerItems; + break; + case "medico": + menuItems = doctorItems; + break; + case "secretaria": + menuItems = secretaryItems; + break; + case "paciente": + menuItems = patientItems; + break; + default: + menuItems = patientItems; + break; + } + return menuItems; + } + + const menuItems = SetMenuItems(role) + + if (!userData) { + return ( +
+ Carregando... +
+ ); + } + + return ( +
+
+
+ {!sidebarCollapsed && ( +
+
+
+
+ MedConnect +
+ )} + +
+ + + + +
+ +
+
+
{children}
+
+ + + + + Confirmar Saída + + Deseja realmente sair do sistema? Você precisará fazer login + novamente para acessar sua conta. + + + + + + + + +
+ ); +} diff --git a/components/doctor-layout.tsx b/components/doctor-layout.tsx deleted file mode 100644 index 9b69c0c..0000000 --- a/components/doctor-layout.tsx +++ /dev/null @@ -1,128 +0,0 @@ -// CÓDIGO REATORADO PARA: components/doctor-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import { useRouter, usePathname } from "next/navigation"; -import Link from "next/link"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; // 1. Importamos nosso novo hook -import { api } from "@/services/api.mjs"; - -// Componentes da UI -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, Clock, User, LogOut, ChevronLeft, ChevronRight, Bell, FileText } from "lucide-react"; -import { Badge } from "./ui/badge"; - -export default function DoctorLayout({ children }: { children: React.ReactNode }) { - // 2. Usamos o hook para buscar o usuário e controlar o acesso para 'medico' - const { user, isLoading } = useAuthLayout({ requiredRole: 'medico' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - // ESTA PARTE É ÚNICA DE CADA LAYOUT E DEVE SER MANTIDA - const menuItems = [ - { href: "/doctor/dashboard", icon: Home, label: "Dashboard" }, - { href: "/doctor/consultas", icon: Calendar, label: "Consultas" }, - { href: "/doctor/medicos/editorlaudo", icon: Clock, label: "Editor de Laudo" }, - { href: "/doctor/medicos", icon: User, label: "patientes" }, - { href: "/doctor/disponibilidade", icon: Calendar, label: "Disponibilidade" }, - ]; - - // 3. Adicionamos o estado de carregamento - if (isLoading || !user) { - return
Carregando...
; - } - - return ( -
-
- {/* Header da Sidebar */} -
- {!sidebarCollapsed && ( -
-
- MediConnect -
- )} - -
- - {/* Menu (específico deste layout) */} - - - {/* Rodapé com Avatar e Logout */} -
-
- {/* 4. A LÓGICA DO AVATAR AGORA É APLICADA AQUI */} - - - {user.name.split(" ").map((n) => n[0]).join("")} - - {!sidebarCollapsed && ( -
-

{user.name}

-

{user.roles.join(', ')}

-
- )} -
- -
-
- - {/* Main Content */} -
-
-
-
- -
-
-
{children}
-
- - {/* Dialog de Logout */} - - - Confirmar SaídaDeseja realmente sair do sistema? - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/finance-layout.tsx b/components/finance-layout.tsx deleted file mode 100644 index 9f6e3bf..0000000 --- a/components/finance-layout.tsx +++ /dev/null @@ -1,117 +0,0 @@ -// CÓDIGO COMPLETO PARA: components/finance-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import { useRouter, usePathname } from "next/navigation"; -import Link from "next/link"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; -import { api } from "@/services/api.mjs"; - -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, User, LogOut, ChevronLeft, ChevronRight, Bell } from "lucide-react"; -import { Badge } from "./ui/badge"; - -export default function FinancierLayout({ children }: { children: React.ReactNode }) { - const { user, isLoading } = useAuthLayout({ requiredRole: 'finance' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - const menuItems = [ - { href: "#", icon: Home, label: "Dashboard" }, - { href: "#", icon: Calendar, label: "Relatórios financeiros" }, - { href: "#", icon: User, label: "Finanças Gerais" }, - { href: "#", icon: Calendar, label: "Configurações" }, - ]; - - if (isLoading || !user) { - return
Carregando...
; - } - - return ( -
-
-
-
- {!sidebarCollapsed && ( -
-
- MediConnect -
- )} - -
-
- -
-
- - - {user.name.split(" ").map((n) => n[0]).join("")} - - {!sidebarCollapsed && ( -
-

{user.name}

-

{user.roles.join(', ')}

-
- )} -
- -
-
-
-
-
-
-
- -
-
-
-
{children}
-
- - - Confirmar SaídaDeseja realmente sair do sistema? - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/hospital-layout.tsx b/components/hospital-layout.tsx deleted file mode 100644 index 04e1e3b..0000000 --- a/components/hospital-layout.tsx +++ /dev/null @@ -1,121 +0,0 @@ -// CÓDIGO COMPLETO PARA: components/hospital-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import Link from "next/link"; -import { useRouter, usePathname } from "next/navigation"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; -import { api } from "@/services/api.mjs"; - -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, Clock, FileText, User, LogOut, ChevronLeft, ChevronRight, Bell, Search } from "lucide-react"; -import { Badge } from "./ui/badge"; -import { Input } from "./ui/input"; - -export default function HospitalLayout({ children }: { children: React.ReactNode }) { - const { user, isLoading } = useAuthLayout({ requiredRole: 'patiente' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - const menuItems = [ - { href: "/patient/dashboard", icon: Home, label: "Dashboard" }, - { href: "/patient/appointments", icon: Calendar, label: "Minhas Consultas" }, - { href: "/patient/schedule", icon: Clock, label: "Agendar Consulta" }, - { href: "/patient/reports", icon: FileText, label: "Meus Laudos" }, - { href: "/patient/profile", icon: User, label: "Meus Dados" }, - ]; - - if (isLoading || !user) { - return
Carregando...
; - } - - return ( -
-
-
-
- {!sidebarCollapsed && ( -
-
- MediConnect -
- )} - -
-
- -
-
- - - {user.name.split(" ").map((n) => n[0]).join("")} - -
-

{user.name}

-

{user.email}

-
-
- -
-
-
-
-
-
-
- - -
-
-
- -
-
-
-
{children}
-
- - - Confirmar SaídaDeseja realmente sair do sistema? - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/manager-layout.tsx b/components/manager-layout.tsx deleted file mode 100644 index 7afb79f..0000000 --- a/components/manager-layout.tsx +++ /dev/null @@ -1,134 +0,0 @@ -// CÓDIGO REATORADO PARA: components/manager-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import { useRouter, usePathname } from "next/navigation"; -import Link from "next/link"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; // 1. Importamos nosso novo hook -import { api } from "@/services/api.mjs"; - -// Componentes da UI (Button, Avatar, etc.) -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, User, LogOut, ChevronLeft, ChevronRight, Bell } from "lucide-react"; -import { Badge } from "./ui/badge"; - -export default function ManagerLayout({ children }: { children: React.ReactNode }) { - // 2. Usamos o hook para buscar o usuário e controlar o acesso - const { user, isLoading } = useAuthLayout({ requiredRole: 'gestor' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - const menuItems = [ - { href: "/manager/dashboard", icon: Home, label: "Dashboard" }, - { href: "#", icon: Calendar, label: "Relatórios gerenciais" }, - { href: "/manager/usuario", icon: User, label: "Gestão de Usuários" }, - { href: "/manager/home", icon: User, label: "Gestão de Médicos" }, - { href: "/manager/patientes", icon: User, label: "Gestão de patientes" }, - { href: "#", icon: Calendar, label: "Configurações" }, - ]; - - // 3. Enquanto o hook está carregando, mostramos uma tela de loading - if (isLoading || !user) { - return
Carregando...
; - } - - // O resto do seu JSX continua igual, mas agora usando a variável 'user' do hook - return ( -
-
- {/* Header da Sidebar */} -
- {!sidebarCollapsed && ( -
-
-
-
- MediConnect -
- )} - -
- - {/* Menu */} - - - {/* Rodapé com Avatar e Logout */} -
-
- {/* 4. A LÓGICA DO AVATAR AGORA É APLICADA AQUI */} - - - {user.name.split(" ").map((n) => n[0]).join("")} - - {!sidebarCollapsed && ( -
-

{user.name}

-

{user.roles.join(', ')}

-
- )} -
- -
-
- - {/* Main Content */} -
-
-
-
- -
-
-
{children}
-
- - {/* Dialog de Logout */} - - - - Confirmar Saída - Deseja realmente sair do sistema? - - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/patient-layout.tsx b/components/patient-layout.tsx deleted file mode 100644 index 8d594da..0000000 --- a/components/patient-layout.tsx +++ /dev/null @@ -1,118 +0,0 @@ -// CÓDIGO COMPLETO PARA: components/patient-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import Link from "next/link"; -import { useRouter, usePathname } from "next/navigation"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; -import { api } from "@/services/api.mjs"; - -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, Clock, FileText, User, LogOut, ChevronLeft, ChevronRight, Bell } from "lucide-react"; -import { Badge } from "./ui/badge"; - -export default function patientLayout({ children }: { children: React.ReactNode }) { - const { user, isLoading } = useAuthLayout({ requiredRole: 'patiente' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - const menuItems = [ - { href: "/patient/dashboard", icon: Home, label: "Dashboard" }, - { href: "/patient/appointments", icon: Calendar, label: "Minhas Consultas" }, - { href: "/patient/schedule", icon: Clock, label: "Agendar Consulta" }, - { href: "/patient/reports", icon: FileText, label: "Meus Laudos" }, - { href: "/patient/profile", icon: User, label: "Meus Dados" }, - ]; - - if (isLoading || !user) { - return
Carregando...
; - } - - return ( -
-
-
-
- {!sidebarCollapsed && ( -
-
- MediConnect -
- )} - -
-
- -
-
- - - {user.name.split(" ").map((n) => n[0]).join("")} - - {!sidebarCollapsed && ( -
-

{user.name}

-

{user.email}

-
- )} -
- -
-
-
-
-
-
-
- -
-
-
-
{children}
-
- - - Confirmar SaídaDeseja realmente sair do sistema? - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/secretary-layout.tsx b/components/secretary-layout.tsx deleted file mode 100644 index 85e5b3d..0000000 --- a/components/secretary-layout.tsx +++ /dev/null @@ -1,117 +0,0 @@ -// CÓDIGO COMPLETO PARA: components/secretary-layout.tsx - -"use client"; - -import type React from "react"; -import { useState } from "react"; -import { useRouter, usePathname } from "next/navigation"; -import Link from "next/link"; -import { useAuthLayout } from "@/hooks/useAuthLayout"; -import { api } from "@/services/api.mjs"; - -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Home, Calendar, Clock, User, LogOut, ChevronLeft, ChevronRight, Bell } from "lucide-react"; -import { Badge } from "./ui/badge"; - -export default function SecretaryLayout({ children }: { children: React.ReactNode }) { - const { user, isLoading } = useAuthLayout({ requiredRole: 'secretaria' }); - - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - const [showLogoutDialog, setShowLogoutDialog] = useState(false); - const router = useRouter(); - const pathname = usePathname(); - - const confirmLogout = async () => { - await api.logout(); - setShowLogoutDialog(false); - router.push("/"); - }; - - const menuItems = [ - { href: "/secretary/dashboard", icon: Home, label: "Dashboard" }, - { href: "/secretary/appointments", icon: Calendar, label: "Consultas" }, - { href: "/secretary/schedule", icon: Clock, label: "Agendar Consulta" }, - { href: "/secretary/pacientes", icon: User, label: "pacientes" }, - ]; - - if (isLoading || !user) { - return
Carregando...
; - } - - return ( -
-
-
-
- {!sidebarCollapsed && ( -
-
- MediConnect -
- )} - -
-
- -
-
- - - {user.name.split(" ").map((n) => n[0]).join("")} - - {!sidebarCollapsed && ( -
-

{user.name}

-

{user.email}

-
- )} -
- -
-
-
-
-
-
-
- -
-
-
-
{children}
-
- - - Confirmar SaídaDeseja realmente sair do sistema? - - - - - - -
- ); -} \ No newline at end of file diff --git a/components/ui/userToolTip.tsx b/components/ui/userToolTip.tsx new file mode 100644 index 0000000..3c8de8d --- /dev/null +++ b/components/ui/userToolTip.tsx @@ -0,0 +1,125 @@ +"use client"; + +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { CalendarCheck2, CalendarClock, ClipboardPlus, Home, LogOut, SquareUser } from "lucide-react"; +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "@/components/ui/popover"; +import { usePathname } from "next/navigation"; +import Link from "next/link"; + +interface UserData { + user_metadata: { + full_name: string; + }; + app_metadata: { + user_role: string; + }; + email: string; +} + +interface Props { + userData: UserData; + sidebarCollapsed: boolean; + handleLogout: () => void; + isActive: boolean; +} + +export default function SidebarUserSection({ + userData, + sidebarCollapsed, + handleLogout, + isActive, +}: Props) { + const pathname = usePathname(); + const menuItems: any[] = [ + { href: "/patient/schedule", icon: CalendarClock, label: "Agendar Consulta" }, + { href: "/patient/appointments", icon: CalendarCheck2, label: "Minhas Consultas" }, + { href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" }, + { href: "/patient/profile", icon: SquareUser, label: "Meus Dados" }, + ] + return ( +
+ {/* POPUP DE INFORMAÇÕES DO USUÁRIO */} + + +
+ + + + {userData.user_metadata.full_name + .split(" ") + .map((n) => n[0]) + .join("")} + + + + {!sidebarCollapsed && ( +
+

+ {userData.user_metadata.full_name} +

+

+ {userData.app_metadata.user_role} +

+
+ )} +
+
+ + {/* Card flutuante */} + + + +
+ + {/* Botão de sair */} + +
+ ); +} diff --git a/services/usersApi.mjs b/services/usersApi.mjs index 396ea16..fc9d0b9 100644 --- a/services/usersApi.mjs +++ b/services/usersApi.mjs @@ -21,6 +21,10 @@ export const usersService = { return await api.post(`/functions/v1/create-user-with-password`, data); }, + async getMeSimple() { + return await api.post(`/functions/v1/user-info`); + }, + async full_data(user_id) { if (!user_id) throw new Error("user_id é obrigatório"); @@ -57,4 +61,40 @@ export const usersService = { permissions, }; }, -}; \ No newline at end of file + async resetPassword(email) { + if (!email) throw new Error("Email é obrigatório para resetar a senha."); + + + try { + const res = await fetch( + `${process.env.NEXT_PUBLIC_SUPABASE_URL}/auth/v1/recover`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + apikey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, + }, + body: JSON.stringify({ email }), + } + ); + + + const data = await res.json().catch(() => ({})); + + + if (!res.ok) { + console.error("Erro no resetPassword:", res.status, data); + throw new Error(`Erro ${res.status}: ${data.message || "Falha ao resetar senha."}`); + } + + + console.log("✅ Reset de senha:", data); + return data; + } catch (err) { + console.error("❌ Erro na chamada resetPassword:", err); + throw new Error(err.message || "Erro inesperado na recuperação de senha."); + } +}, + + +};