diff --git a/app/doctor/consultas/page.tsx b/app/doctor/consultas/page.tsx index 9332a8b..6307570 100644 --- a/app/doctor/consultas/page.tsx +++ b/app/doctor/consultas/page.tsx @@ -31,8 +31,8 @@ interface EnrichedAppointment { } export default function DoctorAppointmentsPage() { - const { user, isLoading: isAuthLoading } = useAuthLayout({ requiredRole: 'medico' }); - + const { user, isLoading: isAuthLoading } = useAuthLayout({ requiredRole: ['medico'] }); + const [allAppointments, setAllAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedDate, setSelectedDate] = useState(new Date()); @@ -56,7 +56,7 @@ export default function DoctorAppointmentsPage() { const patientsMap = new Map( patientsList.map((p: any) => [p.id, { name: p.full_name, phone: p.phone_mobile }]) ); - + const enrichedAppointments = appointmentsList.map((apt: any) => ({ id: apt.id, patientName: patientsMap.get(apt.patient_id)?.name || "Paciente Desconhecido", @@ -85,10 +85,10 @@ export default function DoctorAppointmentsPage() { const appointmentsToDisplay = selectedDate ? allAppointments.filter(app => app.scheduled_at && app.scheduled_at.startsWith(format(selectedDate, "yyyy-MM-dd"))) : allAppointments.filter(app => { - if (!app.scheduled_at) return false; - const dateObj = parseISO(app.scheduled_at); - return isValid(dateObj) && isFuture(dateObj); - }); + if (!app.scheduled_at) return false; + const dateObj = parseISO(app.scheduled_at); + return isValid(dateObj) && isFuture(dateObj); + }); return appointmentsToDisplay.reduce((acc, appointment) => { const dateKey = format(parseISO(appointment.scheduled_at), "yyyy-MM-dd"); @@ -153,7 +153,7 @@ export default function DoctorAppointmentsPage() { Filtrar por DataSelecione um dia para ver os detalhes. - + @@ -188,11 +188,11 @@ export default function DoctorAppointmentsPage() { {format(scheduledAtDate, "HH:mm")} - + {/* Coluna 2: Status e Telefone */}
- {appointment.status.replace('_', ' ')} -
+ {appointment.status.replace('_', ' ')} +
{appointment.patientPhone}
diff --git a/app/doctor/dashboard/page.tsx b/app/doctor/dashboard/page.tsx index 060d8e4..8796952 100644 --- a/app/doctor/dashboard/page.tsx +++ b/app/doctor/dashboard/page.tsx @@ -9,104 +9,42 @@ import Link from "next/link"; import { useEffect, useState } from "react"; import { toast } from "@/hooks/use-toast"; +// --- IMPORTS ADICIONADOS PARA A CORREÇÃO --- +import { useAuthLayout } from "@/hooks/useAuthLayout"; +import { patientsService } from "@/services/patientsApi.mjs"; +// --- FIM DOS IMPORTS ADICIONADOS --- + +import { appointmentsService } from "@/services/appointmentsApi.mjs"; +import { format, parseISO, isAfter, isSameMonth, startOfToday } from "date-fns"; +import { ptBR } from "date-fns/locale"; + 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 = { +// (As interfaces permanecem as mesmas) +type Availability = { id: string; doctor_id: string; weekday: string; start_time: string; end_time: string; slot_minutes: number; appointment_type: string; active: boolean; created_at: string; updated_at: string; created_by: string; updated_by: string | null; }; +type Schedule = { weekday: object; }; +type Doctor = { id: string; user_id: string | null; crm: string; crm_uf: string; specialty: string; full_name: string; cpf: string; email: string; phone_mobile: string | null; phone2: string | null; cep: string | null; street: string | null; number: string | null; complement: string | null; neighborhood: string | null; city: string | null; state: string | null; birth_date: string | null; rg: string | null; active: boolean; created_at: string; updated_at: string; created_by: string; updated_by: string | null; max_days_in_advance: number; rating: number | null; } +interface UserPermissions { isAdmin: boolean; isManager: boolean; isDoctor: boolean; isSecretary: boolean; isAdminOrManager: boolean; } +interface UserData { user: { id: string; email: string; email_confirmed_at: string | null; created_at: string | null; last_sign_in_at: string | null; }; profile: { id: string; full_name: string; email: string; phone: string; avatar_url: string | null; disabled: boolean; created_at: string | null; updated_at: string | null; }; roles: string[]; permissions: UserPermissions; } +interface Exception { id: string; doctor_id: string; date: string; start_time: string | null; end_time: string | null; kind: "bloqueio" | "disponibilidade"; reason: string | null; created_at: string; created_by: string; } + +// --- NOVA INTERFACE PARA A CONSULTA COM NOME DO PACIENTE --- +interface EnrichedAppointment { id: string; - doctor_id: string; - weekday: string; - start_time: string; - end_time: string; - slot_minutes: number; - appointment_type: string; - active: boolean; - created_at: string; - updated_at: string; - created_by: string; - updated_by: string | null; -}; - -type Schedule = { - weekday: object; -}; - -type Doctor = { - id: string; - user_id: string | null; - crm: string; - crm_uf: string; - specialty: string; - full_name: string; - cpf: string; - email: string; - phone_mobile: string | null; - phone2: string | null; - cep: string | null; - street: string | null; - number: string | null; - complement: string | null; - neighborhood: string | null; - city: string | null; - state: string | null; - birth_date: string | null; - rg: string | null; - active: boolean; - created_at: string; - updated_at: string; - created_by: string; - updated_by: string | null; - max_days_in_advance: number; - rating: number | null; -} - -interface UserPermissions { - isAdmin: boolean; - isManager: boolean; - isDoctor: boolean; - isSecretary: boolean; - isAdminOrManager: boolean; -} - -interface UserData { - user: { - id: string; - email: string; - email_confirmed_at: string | null; - created_at: string | null; - last_sign_in_at: string | null; - }; - profile: { - id: string; - full_name: string; - email: string; - phone: string; - avatar_url: string | null; - disabled: boolean; - created_at: string | null; - updated_at: string | null; - }; - roles: string[]; - permissions: UserPermissions; -} - -interface Exception { - id: string; // id da exceção - doctor_id: string; - date: string; // formato YYYY-MM-DD - start_time: string | null; // null = dia inteiro - end_time: string | null; // null = dia inteiro - kind: "bloqueio" | "disponibilidade"; // tipos conhecidos - reason: string | null; // pode ser null - created_at: string; // timestamp ISO - created_by: string; + patientName: string; + scheduled_at: string; + [key: string]: any; } export default function PatientDashboard() { - const [loggedDoctor, setLoggedDoctor] = useState(); + // --- USA O HOOK DE AUTENTICAÇÃO PARA PEGAR O USUÁRIO LOGADO --- + const { user } = useAuthLayout({ requiredRole: ['medico'] }); + + const [loggedDoctor, setLoggedDoctor] = useState(null); const [userData, setUserData] = useState(); const [availability, setAvailability] = useState(null); const [exceptions, setExceptions] = useState([]); @@ -116,56 +54,79 @@ export default function PatientDashboard() { const [exceptionToDelete, setExceptionToDelete] = useState(null); const [error, setError] = useState(null); - // Mapa de tradução - const weekdaysPT: Record = { - sunday: "Domingo", - monday: "Segunda", - tuesday: "Terça", - wednesday: "Quarta", - thursday: "Quinta", - friday: "Sexta", - saturday: "Sábado", - }; + // --- ESTADOS PARA OS CARDS ATUALIZADOS --- + const [nextAppointment, setNextAppointment] = useState(null); + const [monthlyCount, setMonthlyCount] = useState(0); + const weekdaysPT: Record = { sunday: "Domingo", monday: "Segunda", tuesday: "Terça", wednesday: "Quarta", thursday: "Quinta", friday: "Sexta", saturday: "Sábado" }; + + // ▼▼▼ LÓGICA DE BUSCA CORRIGIDA E ATUALIZADA ▼▼▼ useEffect(() => { - const fetchData = async () => { - try { - const doctorsList: Doctor[] = await doctorsService.list(); - const doctor = doctorsList[0]; + const fetchData = async () => { + if (!user?.id) return; // Aguarda o usuário ser carregado - // Salva no estado - setLoggedDoctor(doctor); + try { + // Encontra o perfil de médico correspondente ao usuário logado + const doctorsList: Doctor[] = await doctorsService.list(); + const currentDoctor = doctorsList.find(doc => doc.user_id === user.id); - // Busca disponibilidade - const availabilityList = await AvailabilityService.list(); - - // Filtra já com a variável local - const filteredAvail = availabilityList.filter( - (disp: { doctor_id: string }) => disp.doctor_id === doctor?.id - ); - setAvailability(filteredAvail); + if (!currentDoctor) { + setError("Perfil de médico não encontrado para este usuário."); + return; + } + setLoggedDoctor(currentDoctor); - // Busca exceções - const exceptionsList = await exceptionsService.list(); - const filteredExc = exceptionsList.filter( - (exc: { doctor_id: string }) => exc.doctor_id === doctor?.id - ); - console.log(exceptionsList) - setExceptions(filteredExc); + // Busca todos os dados necessários em paralelo + const [appointmentsList, patientsList, availabilityList, exceptionsList] = await Promise.all([ + appointmentsService.list(), + patientsService.list(), + AvailabilityService.list(), + exceptionsService.list() + ]); - } catch (e: any) { - alert(`${e?.error} ${e?.message}`); - } - }; + // Mapeia pacientes por ID para consulta rápida + const patientsMap = new Map(patientsList.map((p: any) => [p.id, p.full_name])); - fetchData(); -}, []); + // Filtra e enriquece as consultas APENAS do médico logado + const doctorAppointments = appointmentsList + .filter((apt: any) => apt.doctor_id === currentDoctor.id) + .map((apt: any): EnrichedAppointment => ({ + ...apt, + patientName: patientsMap.get(apt.patient_id) || "Paciente Desconhecido", + })); + + // 1. Lógica para "Próxima Consulta" + const today = startOfToday(); + const upcomingAppointments = doctorAppointments + .filter(apt => isAfter(parseISO(apt.scheduled_at), today)) + .sort((a, b) => new Date(a.scheduled_at).getTime() - new Date(b.scheduled_at).getTime()); + setNextAppointment(upcomingAppointments[0] || null); + + // 2. Lógica para "Consultas Este Mês" (apenas ativas) + const activeStatuses = ['confirmed', 'requested', 'checked_in']; + const currentMonthAppointments = doctorAppointments.filter(apt => + isSameMonth(parseISO(apt.scheduled_at), new Date()) && activeStatuses.includes(apt.status) + ); + setMonthlyCount(currentMonthAppointments.length); + + // Busca e filtra o restante dos dados + setAvailability(availabilityList.filter((d: any) => d.doctor_id === currentDoctor.id)); + setExceptions(exceptionsList.filter((e: any) => e.doctor_id === currentDoctor.id)); + + } catch (e: any) { + setError(e?.message || "Erro ao buscar dados do dashboard"); + console.error("Erro no dashboard:", e); + } + }; + + fetchData(); + }, [user]); // A busca de dados agora depende do usuário logado + // ▲▲▲ FIM DA LÓGICA DE BUSCA ATUALIZADA ▲▲▲ - // Função auxiliar para filtrar o id do doctor correspondente ao user logado function findDoctorById(id: string, doctors: Doctor[]) { return doctors.find((doctor) => doctor.user_id === id); } - + const openDeleteDialog = (exceptionId: string) => { setExceptionToDelete(exceptionId); setDeleteDialogOpen(true); @@ -173,53 +134,25 @@ export default function PatientDashboard() { const handleDeleteException = async (ExceptionId: string) => { try { - alert(ExceptionId) const res = await exceptionsService.delete(ExceptionId); - - 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, - }); - + if (res && res.error) { throw new Error(res.message || "A API retornou um erro"); } + toast({ title: "Sucesso", description: "Exceção deletada com sucesso" }); setExceptions((prev: Exception[]) => prev.filter((p) => String(p.id) !== String(ExceptionId))); } catch (e: any) { - toast({ - title: "Erro", - description: e?.message || "Não foi possível deletar a exceção", - }); + toast({ title: "Erro", description: e?.message || "Não foi possível deletar a exceção" }); } setDeleteDialogOpen(false); setExceptionToDelete(null); }; function formatAvailability(data: Availability[]) { - // Agrupar os horários por dia da semana + if (!data) return {}; const schedule = data.reduce((acc: any, item) => { const { weekday, start_time, end_time } = item; - - // Se o dia ainda não existe, cria o array - if (!acc[weekday]) { - acc[weekday] = []; - } - - // Adiciona o horário do dia - acc[weekday].push({ - start: start_time, - end: end_time, - }); - + if (!acc[weekday]) acc[weekday] = []; + acc[weekday].push({ start: start_time, end: end_time }); return acc; }, {} as Record); - return schedule; } @@ -239,27 +172,44 @@ export default function PatientDashboard() {
+ {/* ▼▼▼ CARD "PRÓXIMA CONSULTA" CORRIGIDO PARA MOSTRAR NOME DO PACIENTE ▼▼▼ */} Próxima Consulta -
02 out
-

Dr. Silva - 14:30

+ {nextAppointment ? ( + <> +
+ {format(parseISO(nextAppointment.scheduled_at), "dd MMM", { locale: ptBR })} +
+

+ {nextAppointment.patientName} - {format(parseISO(nextAppointment.scheduled_at), "HH:mm")} +

+ + ) : ( + <> +
Nenhuma
+

Sem próximas consultas

+ + )}
+ {/* ▲▲▲ FIM DO CARD ATUALIZADO ▲▲▲ */} + {/* ▼▼▼ CARD "CONSULTAS ESTE MÊS" CORRIGIDO PARA CONTAGEM CORRETA ▼▼▼ */} Consultas Este Mês -
4
-

4 agendadas

+
{monthlyCount}
+

{monthlyCount === 1 ? '1 agendada' : `${monthlyCount} agendadas`}

+ {/* ▲▲▲ FIM DO CARD ATUALIZADO ▲▲▲ */} @@ -273,6 +223,7 @@ export default function PatientDashboard() {
+ {/* O restante do código permanece o mesmo */}
@@ -353,7 +304,6 @@ export default function PatientDashboard() { {exceptions && exceptions.length > 0 ? ( exceptions.map((ex: Exception) => { - // Formata data e hora const date = new Date(ex.date).toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", @@ -369,10 +319,10 @@ export default function PatientDashboard() {

{date}

-

+

{startTime && endTime - ? `${startTime} - ${endTime}` - : "Dia todo"} + ? `${startTime} - ${endTime}` + : "Dia todo"}

@@ -411,4 +361,4 @@ export default function PatientDashboard() {
); -} +} \ No newline at end of file diff --git a/app/manager/dashboard/page.tsx b/app/manager/dashboard/page.tsx index 6558bd0..b8131b1 100644 --- a/app/manager/dashboard/page.tsx +++ b/app/manager/dashboard/page.tsx @@ -2,12 +2,13 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Calendar, Clock, Plus, User } from "lucide-react"; +import { Clock, Plus, User } from "lucide-react"; // Removi 'Calendar' que não estava sendo usado 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"; +import { api } from "services/api.mjs"; // <-- ADICIONEI ESTE IMPORT export default function ManagerDashboard() { // 🔹 Estados para usuários @@ -18,16 +19,44 @@ export default function ManagerDashboard() { const [doctors, setDoctors] = useState([]); const [loadingDoctors, setLoadingDoctors] = useState(true); - // 🔹 Buscar primeiro usuário + // 🔹 Buscar primeiro usuário (LÓGICA ATUALIZADA) useEffect(() => { async function fetchFirstUser() { + setLoadingUser(true); // Garante que o estado de loading inicie como true try { - const data = await usersService.list_roles(); - if (Array.isArray(data) && data.length > 0) { - setFirstUser(data[0]); + // 1. Busca a lista de usuários com seus cargos (roles) + const rolesData = await usersService.list_roles(); + + // 2. Verifica se a lista não está vazia + if (Array.isArray(rolesData) && rolesData.length > 0) { + const firstUserRole = rolesData[0]; + const firstUserId = firstUserRole.user_id; + + if (!firstUserId) { + throw new Error("O primeiro usuário da lista não possui um ID válido."); + } + + // 3. Usa o ID para buscar o perfil (com nome e email) do usuário + const profileData = await api.get( + `/rest/v1/profiles?select=full_name,email&id=eq.${firstUserId}` + ); + + // 4. Verifica se o perfil foi encontrado + if (Array.isArray(profileData) && profileData.length > 0) { + const userProfile = profileData[0]; + // 5. Combina os dados do cargo e do perfil e atualiza o estado + setFirstUser({ + ...firstUserRole, + ...userProfile + }); + } else { + // Se não encontrar o perfil, exibe os dados que temos + setFirstUser(firstUserRole); + } } } catch (error) { console.error("Erro ao carregar usuário:", error); + setFirstUser(null); // Limpa o usuário em caso de erro } finally { setLoadingUser(false); } @@ -65,17 +94,7 @@ export default function ManagerDashboard() { {/* Cards principais */}
- {/* Card 1 */} - - - Relatórios gerenciais - - - -
0
-

Relatórios disponíveis

-
-
+ {/* Card 2 — Gestão de usuários */} @@ -187,4 +206,4 @@ export default function ManagerDashboard() {
); -} +} \ No newline at end of file diff --git a/app/manager/home/[id]/editar/page.tsx b/app/manager/home/[id]/editar/page.tsx index 89a5afb..b3bfeea 100644 --- a/app/manager/home/[id]/editar/page.tsx +++ b/app/manager/home/[id]/editar/page.tsx @@ -9,47 +9,47 @@ import { Label } from "@/components/ui/label" 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 { Save, Loader2, ArrowLeft } from "lucide-react" import Sidebar from "@/components/Sidebar" -import { doctorsService } from "services/doctorsApi.mjs"; +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"]; interface DoctorFormData { - nomeCompleto: string; - crm: string; - crmEstado: string; - especialidade: string; - cpf: string; - email: string; - dataNascimento: string; - rg: string; - telefoneCelular: string; - telefone2: string; - cep: string; - endereco: string; - numero: string; - complemento: string; - bairro: string; - cidade: string; - estado: string; - ativo: boolean; - observacoes: string; + nomeCompleto: string; + crm: string; + crmEstado: string; + especialidade: string; + cpf: string; + email: string; + dataNascimento: string; + rg: string; + telefoneCelular: string; + telefone2: string; + cep: string; + endereco: string; + numero: string; + complemento: string; + bairro: string; + cidade: string; + estado: string; + ativo: boolean; + observacoes: string; } const apiMap: { [K in keyof DoctorFormData]: string | null } = { - nomeCompleto: 'full_name', crm: 'crm', crmEstado: 'crm_uf', especialidade: 'specialty', - cpf: 'cpf', email: 'email', dataNascimento: 'birth_date', rg: 'rg', - telefoneCelular: 'phone_mobile', telefone2: 'phone2', cep: 'cep', - endereco: 'street', numero: 'number', complemento: 'complement', - bairro: 'neighborhood', cidade: 'city', estado: 'state', ativo: 'active', - observacoes: null, + nomeCompleto: 'full_name', crm: 'crm', crmEstado: 'crm_uf', especialidade: 'specialty', + cpf: 'cpf', email: 'email', dataNascimento: 'birth_date', rg: 'rg', + telefoneCelular: 'phone_mobile', telefone2: 'phone2', cep: 'cep', + endereco: 'street', numero: 'number', complemento: 'complement', + bairro: 'neighborhood', cidade: 'city', estado: 'state', ativo: 'active', + observacoes: null, }; const defaultFormData: DoctorFormData = { - nomeCompleto: '', crm: '', crmEstado: '', especialidade: '', cpf: '', email: '', - dataNascimento: '', rg: '', telefoneCelular: '', telefone2: '', cep: '', - endereco: '', numero: '', complemento: '', bairro: '', cidade: '', estado: '', - ativo: true, observacoes: '', + nomeCompleto: '', crm: '', crmEstado: '', especialidade: '', cpf: '', email: '', + dataNascimento: '', rg: '', telefoneCelular: '', telefone2: '', cep: '', + endereco: '', numero: '', complemento: '', bairro: '', cidade: '', estado: '', + ativo: true, observacoes: '', }; const cleanNumber = (value: string): string => value.replace(/\D/g, ''); @@ -73,420 +73,420 @@ const formatPhoneMobile = (value: string): string => { }; export default function EditarMedicoPage() { - const router = useRouter(); - const params = useParams(); - const id = Array.isArray(params.id) ? params.id[0] : params.id; - const [formData, setFormData] = useState(defaultFormData); - const [loading, setLoading] = useState(true); - const [isSaving, setIsSaving] = useState(false); - const [error, setError] = useState(null); - const apiToFormMap: { [key: string]: keyof DoctorFormData } = { - 'full_name': 'nomeCompleto', 'crm': 'crm', 'crm_uf': 'crmEstado', 'specialty': 'especialidade', - 'cpf': 'cpf', 'email': 'email', 'birth_date': 'dataNascimento', 'rg': 'rg', - 'phone_mobile': 'telefoneCelular', 'phone2': 'telefone2', 'cep': 'cep', - 'street': 'endereco', 'number': 'numero', 'complement': 'complemento', - 'neighborhood': 'bairro', 'city': 'cidade', 'state': 'estado', 'active': 'ativo' - }; - - - useEffect(() => { - if (!id) return; - - const fetchDoctor = async () => { - try { - const data = await doctorsService.getById(id); - - if (!data) { - setError("Médico não encontrado."); - setLoading(false); - return; - } - - const initialData: Partial = {}; - - Object.keys(data).forEach(key => { - const formKey = apiToFormMap[key]; - if (formKey) { - let value = data[key] === null ? '' : data[key]; - if (formKey === 'ativo') { - value = !!value; - } else if (typeof value !== 'boolean') { - value = String(value); - } - initialData[formKey] = value as any; - } - }); - initialData.observacoes = "Observação carregada do sistema (exemplo de campo interno)"; - - setFormData(prev => ({ ...prev, ...initialData })); - } catch (e) { - console.error("Erro ao carregar dados:", e); - setError("Não foi possível carregar os dados do médico."); - } finally { - setLoading(false); - } + const router = useRouter(); + const params = useParams(); + const id = Array.isArray(params.id) ? params.id[0] : params.id; + const [formData, setFormData] = useState(defaultFormData); + const [loading, setLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); + const apiToFormMap: { [key: string]: keyof DoctorFormData } = { + 'full_name': 'nomeCompleto', 'crm': 'crm', 'crm_uf': 'crmEstado', 'specialty': 'especialidade', + 'cpf': 'cpf', 'email': 'email', 'birth_date': 'dataNascimento', 'rg': 'rg', + 'phone_mobile': 'telefoneCelular', 'phone2': 'telefone2', 'cep': 'cep', + 'street': 'endereco', 'number': 'numero', 'complement': 'complemento', + 'neighborhood': 'bairro', 'city': 'cidade', 'state': 'estado', 'active': 'ativo' }; - fetchDoctor(); - }, [id]); - - const handleInputChange = (key: keyof DoctorFormData, value: string | boolean) => { - - - if (typeof value === 'string') { - let maskedValue = value; - if (key === 'cpf') maskedValue = formatCPF(value); - if (key === 'cep') maskedValue = formatCEP(value); - if (key === 'telefoneCelular' || key === 'telefone2') maskedValue = formatPhoneMobile(value); - - setFormData((prev) => ({ ...prev, [key]: maskedValue })); - } else { - setFormData((prev) => ({ ...prev, [key]: value })); - } - }; + useEffect(() => { + if (!id) return; - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - setIsSaving(true); - - if (!id) { - setError("ID do médico ausente."); - setIsSaving(false); - return; - } + const fetchDoctor = async () => { + try { + const data = await doctorsService.getById(id); - const finalPayload: { [key: string]: any } = {}; - const formKeys = Object.keys(formData) as Array; + if (!data) { + setError("Médico não encontrado."); + setLoading(false); + return; + } - - formKeys.forEach((key) => { - const apiFieldName = apiMap[key]; - - if (!apiFieldName) return; + const initialData: Partial = {}; + + Object.keys(data).forEach(key => { + const formKey = apiToFormMap[key]; + if (formKey) { + let value = data[key] === null ? '' : data[key]; + if (formKey === 'ativo') { + value = !!value; + } else if (typeof value !== 'boolean') { + value = String(value); + } + initialData[formKey] = value as any; + } + }); + initialData.observacoes = "Observação carregada do sistema (exemplo de campo interno)"; + + setFormData(prev => ({ ...prev, ...initialData })); + } catch (e) { + console.error("Erro ao carregar dados:", e); + setError("Não foi possível carregar os dados do médico."); + } finally { + setLoading(false); + } + }; + fetchDoctor(); + }, [id]); + + const handleInputChange = (key: keyof DoctorFormData, value: string | boolean) => { - let value = formData[key]; if (typeof value === 'string') { - let trimmedValue = value.trim(); - if (trimmedValue === '') { - finalPayload[apiFieldName] = null; - return; - } - if (key === 'crmEstado' || key === 'estado') { - trimmedValue = trimmedValue.toUpperCase(); - } - - value = trimmedValue; - } - - finalPayload[apiFieldName] = value; - }); + let maskedValue = value; + if (key === 'cpf') maskedValue = formatCPF(value); + if (key === 'cep') maskedValue = formatCEP(value); + if (key === 'telefoneCelular' || key === 'telefone2') maskedValue = formatPhoneMobile(value); - delete finalPayload.user_id; - try { - await doctorsService.update(id, finalPayload); - router.push("/manager/home"); - } catch (e: any) { - console.error("Erro ao salvar o médico:", e); - let detailedError = "Erro ao atualizar. Verifique os dados e tente novamente."; - - if (e.message && e.message.includes("duplicate key value violates unique constraint")) { - detailedError = "O CPF ou CRM informado já está cadastrado em outro registro."; - } else if (e.message && e.message.includes("Detalhes:")) { - detailedError = e.message.split("Detalhes:")[1].trim(); - } else if (e.message) { - detailedError = e.message; - } - - setError(`Erro ao atualizar. Detalhes: ${detailedError}`); - } finally { - setIsSaving(false); + setFormData((prev) => ({ ...prev, [key]: maskedValue })); + } else { + setFormData((prev) => ({ ...prev, [key]: value })); + } + }; + + + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + setIsSaving(true); + + if (!id) { + setError("ID do médico ausente."); + setIsSaving(false); + return; + } + + const finalPayload: { [key: string]: any } = {}; + const formKeys = Object.keys(formData) as Array; + + + formKeys.forEach((key) => { + const apiFieldName = apiMap[key]; + + if (!apiFieldName) return; + + let value = formData[key]; + + if (typeof value === 'string') { + let trimmedValue = value.trim(); + if (trimmedValue === '') { + finalPayload[apiFieldName] = null; + return; + } + if (key === 'crmEstado' || key === 'estado') { + trimmedValue = trimmedValue.toUpperCase(); + } + + value = trimmedValue; + } + + finalPayload[apiFieldName] = value; + }); + + delete finalPayload.user_id; + try { + await doctorsService.update(id, finalPayload); + router.push("/manager/home"); + } catch (e: any) { + console.error("Erro ao salvar o médico:", e); + let detailedError = "Erro ao atualizar. Verifique os dados e tente novamente."; + + if (e.message && e.message.includes("duplicate key value violates unique constraint")) { + detailedError = "O CPF ou CRM informado já está cadastrado em outro registro."; + } else if (e.message && e.message.includes("Detalhes:")) { + detailedError = e.message.split("Detalhes:")[1].trim(); + } else if (e.message) { + detailedError = e.message; + } + + setError(`Erro ao atualizar. Detalhes: ${detailedError}`); + } finally { + setIsSaving(false); + } + }; + if (loading) { + return ( + +
+ +

Carregando dados do médico...

+
+
+ ); } - }; - if (loading) { + return ( -
- -

Carregando dados do médico...

+
+
+
+

+ Editar Médico: {formData.nomeCompleto} +

+

+ Atualize as informações do médico +

+
+ + + +
+ +
+ + {error && ( +
+

Erro na Atualização:

+

{error}

+
+ )} + +
+

+ Dados Principais e Pessoais +

+
+
+ + handleInputChange("nomeCompleto", e.target.value)} + placeholder="Nome do Médico" + /> +
+
+ + handleInputChange("crm", e.target.value)} + placeholder="Ex: 123456" + /> +
+
+ + +
+
+ + +
+
+ + handleInputChange("especialidade", e.target.value)} + placeholder="Ex: Cardiologia" + /> +
+
+ + handleInputChange("cpf", e.target.value)} + placeholder="000.000.000-00" + maxLength={14} + /> +
+
+ + handleInputChange("rg", e.target.value)} + placeholder="00.000.000-0" + /> +
+
+ +
+
+ + handleInputChange("email", e.target.value)} + placeholder="exemplo@dominio.com" + /> +
+
+ + handleInputChange("dataNascimento", e.target.value)} + /> +
+
+
+ handleInputChange("ativo", checked === true)} + /> + +
+
+
+
+ +
+

+ Contato e Endereço +

+ +
+
+ + handleInputChange("telefoneCelular", e.target.value)} + placeholder="(00) 00000-0000" + maxLength={15} + /> +
+
+ + handleInputChange("telefone2", e.target.value)} + placeholder="(00) 00000-0000" + maxLength={15} + /> +
+
+ + +
+
+ + handleInputChange("cep", e.target.value)} + placeholder="00000-000" + maxLength={9} + /> +
+
+ + handleInputChange("endereco", e.target.value)} + placeholder="Rua, Avenida, etc." + /> +
+
+ +
+
+ + handleInputChange("numero", e.target.value)} + placeholder="123" + /> +
+
+ + handleInputChange("complemento", e.target.value)} + placeholder="Apto, Bloco, etc." + /> +
+
+ +
+
+ + handleInputChange("bairro", e.target.value)} + placeholder="Bairro" + /> +
+
+ + handleInputChange("cidade", e.target.value)} + placeholder="São Paulo" + /> +
+
+ + handleInputChange("estado", e.target.value)} + placeholder="SP" + /> +
+
+
+ + +
+

+ Observações (Apenas internas) +

+