Merge pull request #32 from m1guelmcf/Stage

Stage
This commit is contained in:
DaniloSts 2025-11-27 16:23:47 -03:00 committed by GitHub
commit 732c3a4b02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 154 additions and 171 deletions

View File

@ -24,11 +24,8 @@ import Link from "next/link";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "@/hooks/use-toast"; import { toast } from "@/hooks/use-toast";
// --- IMPORTS ADICIONADOS PARA A CORREÇÃO ---
import { useAuthLayout } from "@/hooks/useAuthLayout"; import { useAuthLayout } from "@/hooks/useAuthLayout";
import { patientsService } from "@/services/patientsApi.mjs"; import { patientsService } from "@/services/patientsApi.mjs";
// --- FIM DOS IMPORTS ADICIONADOS ---
import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { appointmentsService } from "@/services/appointmentsApi.mjs";
import { format, parseISO, isAfter, isSameMonth, startOfToday } from "date-fns"; import { format, parseISO, isAfter, isSameMonth, startOfToday } from "date-fns";
import { ptBR } from "date-fns/locale"; import { ptBR } from "date-fns/locale";
@ -40,6 +37,21 @@ import { usersService } from "@/services/usersApi.mjs";
import Sidebar from "@/components/Sidebar"; import Sidebar from "@/components/Sidebar";
import WeeklyScheduleCard from "@/components/ui/WeeklyScheduleCard"; import WeeklyScheduleCard from "@/components/ui/WeeklyScheduleCard";
// --- TIPOS ADICIONADOS PARA CORREÇÃO ---
type Appointment = {
id: string;
doctor_id: string;
patient_id: string;
scheduled_at: string;
status: string;
};
type EnrichedAppointment = Appointment & {
patientName: string;
};
// --- FIM DOS TIPOS ADICIONADOS ---
type Availability = { type Availability = {
id: string; id: string;
doctor_id: string; doctor_id: string;
@ -119,19 +131,24 @@ interface UserData {
} }
interface Exception { interface Exception {
id: string; // id da exceção id: string;
doctor_id: string; doctor_id: string;
date: string; // formato YYYY-MM-DD date: string;
start_time: string | null; // null = dia inteiro start_time: string | null;
end_time: string | null; // null = dia inteiro end_time: string | null;
kind: "bloqueio" | "disponibilidade"; // tipos conhecidos kind: "bloqueio" | "disponibilidade";
reason: string | null; // pode ser null reason: string | null;
created_at: string; // timestamp ISO created_at: string;
created_by: string; created_by: string;
} }
// Minimal type for Patient, adjust if more fields are needed
type Patient = {
id: string;
full_name: string;
};
export default function PatientDashboard() { export default function PatientDashboard() {
// --- USA O HOOK DE AUTENTICAÇÃO PARA PEGAR O USUÁRIO LOGADO ---
const { user } = useAuthLayout({ requiredRole: ['medico'] }); const { user } = useAuthLayout({ requiredRole: ['medico'] });
const [loggedDoctor, setLoggedDoctor] = useState<Doctor | null>(null); const [loggedDoctor, setLoggedDoctor] = useState<Doctor | null>(null);
@ -144,19 +161,16 @@ export default function PatientDashboard() {
const [exceptionToDelete, setExceptionToDelete] = useState<string | null>(null); const [exceptionToDelete, setExceptionToDelete] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// --- ESTADOS PARA OS CARDS ATUALIZADOS ---
const [nextAppointment, setNextAppointment] = useState<EnrichedAppointment | null>(null); const [nextAppointment, setNextAppointment] = useState<EnrichedAppointment | null>(null);
const [monthlyCount, setMonthlyCount] = useState<number>(0); const [monthlyCount, setMonthlyCount] = useState<number>(0);
const weekdaysPT: Record<string, string> = { sunday: "Domingo", monday: "Segunda", tuesday: "Terça", wednesday: "Quarta", thursday: "Quinta", friday: "Sexta", saturday: "Sábado" }; const weekdaysPT: Record<string, string> = { sunday: "Domingo", monday: "Segunda", tuesday: "Terça", wednesday: "Quarta", thursday: "Quinta", friday: "Sexta", saturday: "Sábado" };
// ▼▼▼ LÓGICA DE BUSCA CORRIGIDA E ATUALIZADA ▼▼▼
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
if (!user?.id) return; // Aguarda o usuário ser carregado if (!user?.id) return;
try { try {
// Encontra o perfil de médico correspondente ao usuário logado
const doctorsList: Doctor[] = await doctorsService.list(); const doctorsList: Doctor[] = await doctorsService.list();
const currentDoctor = doctorsList.find(doc => doc.user_id === user.id); const currentDoctor = doctorsList.find(doc => doc.user_id === user.id);
@ -166,7 +180,6 @@ export default function PatientDashboard() {
} }
setLoggedDoctor(currentDoctor); setLoggedDoctor(currentDoctor);
// Busca todos os dados necessários em paralelo
const [appointmentsList, patientsList, availabilityList, exceptionsList] = await Promise.all([ const [appointmentsList, patientsList, availabilityList, exceptionsList] = await Promise.all([
appointmentsService.list(), appointmentsService.list(),
patientsService.list(), patientsService.list(),
@ -174,32 +187,27 @@ export default function PatientDashboard() {
exceptionsService.list() exceptionsService.list()
]); ]);
// Mapeia pacientes por ID para consulta rápida const patientsMap = new Map(patientsList.map((p: Patient) => [p.id, p.full_name]));
const patientsMap = new Map(patientsList.map((p: any) => [p.id, p.full_name]));
// Filtra e enriquece as consultas APENAS do médico logado
const doctorAppointments = appointmentsList const doctorAppointments = appointmentsList
.filter((apt: any) => apt.doctor_id === currentDoctor.id) .filter((apt: Appointment) => apt.doctor_id === currentDoctor.id)
.map((apt: any): EnrichedAppointment => ({ .map((apt: Appointment): EnrichedAppointment => ({
...apt, ...apt,
patientName: patientsMap.get(apt.patient_id) || "Paciente Desconhecido", patientName: String(patientsMap.get(apt.patient_id) || "Paciente Desconhecido"),
})); }));
// 1. Lógica para "Próxima Consulta"
const today = startOfToday(); const today = startOfToday();
const upcomingAppointments = doctorAppointments const upcomingAppointments = doctorAppointments
.filter(apt => isAfter(parseISO(apt.scheduled_at), today)) .filter(apt => isAfter(parseISO(apt.scheduled_at), today))
.sort((a, b) => new Date(a.scheduled_at).getTime() - new Date(b.scheduled_at).getTime()); .sort((a, b) => new Date(a.scheduled_at).getTime() - new Date(b.scheduled_at).getTime());
setNextAppointment(upcomingAppointments[0] || null); setNextAppointment(upcomingAppointments[0] || null);
// 2. Lógica para "Consultas Este Mês" (apenas ativas)
const activeStatuses = ['confirmed', 'requested', 'checked_in']; const activeStatuses = ['confirmed', 'requested', 'checked_in'];
const currentMonthAppointments = doctorAppointments.filter(apt => const currentMonthAppointments = doctorAppointments.filter(apt =>
isSameMonth(parseISO(apt.scheduled_at), new Date()) && activeStatuses.includes(apt.status) isSameMonth(parseISO(apt.scheduled_at), new Date()) && activeStatuses.includes(apt.status)
); );
setMonthlyCount(currentMonthAppointments.length); setMonthlyCount(currentMonthAppointments.length);
// Busca e filtra o restante dos dados
setAvailability(availabilityList.filter((d: any) => d.doctor_id === currentDoctor.id)); setAvailability(availabilityList.filter((d: any) => d.doctor_id === currentDoctor.id));
setExceptions(exceptionsList.filter((e: any) => e.doctor_id === currentDoctor.id)); setExceptions(exceptionsList.filter((e: any) => e.doctor_id === currentDoctor.id));
@ -210,8 +218,7 @@ export default function PatientDashboard() {
}; };
fetchData(); fetchData();
}, [user]); // A busca de dados agora depende do usuário logado }, [user]);
// ▲▲▲ FIM DA LÓGICA DE BUSCA ATUALIZADA ▲▲▲
function findDoctorById(id: string, doctors: Doctor[]) { function findDoctorById(id: string, doctors: Doctor[]) {
return doctors.find((doctor) => doctor.user_id === id); return doctors.find((doctor) => doctor.user_id === id);
@ -264,7 +271,6 @@ export default function PatientDashboard() {
</div> </div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* ▼▼▼ CARD "PRÓXIMA CONSULTA" CORRIGIDO PARA MOSTRAR NOME DO PACIENTE ▼▼▼ */}
<Card> <Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Próxima Consulta</CardTitle> <CardTitle className="text-sm font-medium">Próxima Consulta</CardTitle>
@ -273,12 +279,12 @@ export default function PatientDashboard() {
<CardContent> <CardContent>
{nextAppointment ? ( {nextAppointment ? (
<> <>
<div className="text-2xl font-bold capitalize"> <p className="text-2xl font-bold capitalize">
{format(parseISO(nextAppointment.scheduled_at), "dd MMM", { locale: ptBR })}
</div>
<p className="text-xs text-muted-foreground">
{nextAppointment.patientName} - {format(parseISO(nextAppointment.scheduled_at), "HH:mm")} {nextAppointment.patientName} - {format(parseISO(nextAppointment.scheduled_at), "HH:mm")}
</p> </p>
<div className="text-x text-muted-foreground">
{format(parseISO(nextAppointment.scheduled_at), "dd MMM", { locale: ptBR })}
</div>
</> </>
) : ( ) : (
<> <>
@ -288,9 +294,7 @@ export default function PatientDashboard() {
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* ▲▲▲ FIM DO CARD ATUALIZADO ▲▲▲ */}
{/* ▼▼▼ CARD "CONSULTAS ESTE MÊS" CORRIGIDO PARA CONTAGEM CORRETA ▼▼▼ */}
<Card> <Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Consultas Este Mês</CardTitle> <CardTitle className="text-sm font-medium">Consultas Este Mês</CardTitle>
@ -301,7 +305,6 @@ export default function PatientDashboard() {
<p className="text-xs text-muted-foreground">{monthlyCount === 1 ? '1 agendada' : `${monthlyCount} agendadas`}</p> <p className="text-xs text-muted-foreground">{monthlyCount === 1 ? '1 agendada' : `${monthlyCount} agendadas`}</p>
</CardContent> </CardContent>
</Card> </Card>
{/* ▲▲▲ FIM DO CARD ATUALIZADO ▲▲▲ */}
<Card> <Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@ -315,7 +318,6 @@ export default function PatientDashboard() {
</Card> </Card>
</div> </div>
{/* O restante do código permanece o mesmo */}
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
<Card> <Card>
<CardHeader> <CardHeader>
@ -332,26 +334,7 @@ export default function PatientDashboard() {
</CardContent> </CardContent>
</Card> </Card>
<Card>
<CardHeader>
<CardTitle>Próximas Consultas</CardTitle>
<CardDescription>Suas consultas agendadas</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div>
<p className="font-medium">Dr. João Santos</p>
<p className="text-sm text-gray-600">Cardiologia</p>
</div>
<div className="text-right">
<p className="font-medium">02 out</p>
<p className="text-sm text-gray-600">14:30</p>
</div>
</div>
</div>
</CardContent>
</Card>
</div> </div>
<div className="grid md:grid-cols-1 gap-6"> <div className="grid md:grid-cols-1 gap-6">
<Card> <Card>

View File

@ -251,7 +251,7 @@ export default function EditarMedicoPage() {
</h2> </h2>
<div className="grid md:grid-cols-4 gap-4"> <div className="grid md:grid-cols-4 gap-4">
<div className="space-y-2 col-span-2"> <div className="space-y-2 col-span-2">
<Label htmlFor="nomeCompleto">Nome Completo (full_name)</Label> <Label htmlFor="nomeCompleto">Nome Completo</Label>
<Input <Input
id="nomeCompleto" id="nomeCompleto"
value={formData.nomeCompleto} value={formData.nomeCompleto}
@ -269,7 +269,7 @@ export default function EditarMedicoPage() {
/> />
</div> </div>
<div className="space-y-2 col-span-1"> <div className="space-y-2 col-span-1">
<Label htmlFor="crmEstado">UF do CRM (crm_uf)</Label> <Label htmlFor="crmEstado">UF do CRM</Label>
<Select value={formData.crmEstado} onValueChange={(v) => handleInputChange("crmEstado", v)}> <Select value={formData.crmEstado} onValueChange={(v) => handleInputChange("crmEstado", v)}>
<SelectTrigger id="crmEstado"> <SelectTrigger id="crmEstado">
<SelectValue placeholder="UF" /> <SelectValue placeholder="UF" />
@ -286,7 +286,7 @@ export default function EditarMedicoPage() {
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="especialidade">Especialidade (specialty)</Label> <Label htmlFor="especialidade">Especialidade</Label>
<Input <Input
id="especialidade" id="especialidade"
value={formData.especialidade} value={formData.especialidade}
@ -327,7 +327,7 @@ export default function EditarMedicoPage() {
/> />
</div> </div>
<div className="space-y-2 col-span-1"> <div className="space-y-2 col-span-1">
<Label htmlFor="dataNascimento">Data de Nascimento (birth_date)</Label> <Label htmlFor="dataNascimento">Data de Nascimento</Label>
<Input <Input
id="dataNascimento" id="dataNascimento"
type="date" type="date"
@ -342,7 +342,7 @@ export default function EditarMedicoPage() {
checked={formData.ativo} checked={formData.ativo}
onCheckedChange={(checked) => handleInputChange("ativo", checked === true)} onCheckedChange={(checked) => handleInputChange("ativo", checked === true)}
/> />
<Label htmlFor="ativo">Médico Ativo (active)</Label> <Label htmlFor="ativo">Médico Ativo</Label>
</div> </div>
</div> </div>
</div> </div>
@ -355,7 +355,7 @@ export default function EditarMedicoPage() {
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="telefoneCelular">Telefone Celular (phone_mobile)</Label> <Label htmlFor="telefoneCelular">Telefone Celular</Label>
<Input <Input
id="telefoneCelular" id="telefoneCelular"
value={formData.telefoneCelular} value={formData.telefoneCelular}
@ -365,7 +365,7 @@ export default function EditarMedicoPage() {
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="telefone2">Telefone Adicional (phone2)</Label> <Label htmlFor="telefone2">Telefone Adicional</Label>
<Input <Input
id="telefone2" id="telefone2"
value={formData.telefone2} value={formData.telefone2}
@ -389,7 +389,7 @@ export default function EditarMedicoPage() {
/> />
</div> </div>
<div className="space-y-2 col-span-3"> <div className="space-y-2 col-span-3">
<Label htmlFor="endereco">Logradouro (street)</Label> <Label htmlFor="endereco">Logradouro</Label>
<Input <Input
id="endereco" id="endereco"
value={formData.endereco} value={formData.endereco}
@ -440,7 +440,7 @@ export default function EditarMedicoPage() {
/> />
</div> </div>
<div className="space-y-2 col-span-1"> <div className="space-y-2 col-span-1">
<Label htmlFor="estado">Estado (state)</Label> <Label htmlFor="estado">Estado</Label>
<Input <Input
id="estado" id="estado"
value={formData.estado} value={formData.estado}