forked from RiseUP/riseup-squad21
commit
732c3a4b02
@ -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>
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user