medicos ordem alfabetica

This commit is contained in:
m1guelmcf 2025-11-26 11:14:25 -03:00
parent 83bdaed7aa
commit c41d561dd6

View File

@ -9,15 +9,20 @@ import { AvailabilityService } from "@/services/availabilityApi.mjs";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Calendar as CalendarShadcn } from "@/components/ui/calendar"; import { Calendar as CalendarShadcn } from "@/components/ui/calendar";
import { format, addDays } from "date-fns"; import { format, addDays } from "date-fns";
import { User, StickyNote, Calendar } from "lucide-react"; import { User, StickyNote, Calendar } from "lucide-react";
import {smsService } from "@/services/Sms.mjs" import { smsService } from "@/services/Sms.mjs";
import { toast } from "@/hooks/use-toast"; import { toast } from "@/hooks/use-toast";
export default function ScheduleForm() { export default function ScheduleForm() {
// Estado do usuário e role // Estado do usuário e role
const [role, setRole] = useState<string>("paciente"); const [role, setRole] = useState<string>("paciente");
@ -39,17 +44,32 @@ export default function ScheduleForm() {
const [tipoConsulta] = useState("presencial"); const [tipoConsulta] = useState("presencial");
const [duracao] = useState("30"); const [duracao] = useState("30");
const [disponibilidades, setDisponibilidades] = useState<any[]>([]); const [disponibilidades, setDisponibilidades] = useState<any[]>([]);
const [availabilityCounts, setAvailabilityCounts] = useState<Record<string, number>>({}); const [availabilityCounts, setAvailabilityCounts] = useState<
const [tooltip, setTooltip] = useState<{ x: number; y: number; text: string } | null>(null); Record<string, number>
>({});
const [tooltip, setTooltip] = useState<{
x: number;
y: number;
text: string;
} | null>(null);
const calendarRef = useRef<HTMLDivElement | null>(null); const calendarRef = useRef<HTMLDivElement | null>(null);
// Funções auxiliares // Funções auxiliares
const getWeekdayNumber = (weekday: string) => const getWeekdayNumber = (weekday: string) =>
["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] [
.indexOf(weekday.toLowerCase()) + 1; "monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
].indexOf(weekday.toLowerCase()) + 1;
const getBrazilDate = (date: Date) => const getBrazilDate = (date: Date) =>
new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0)); new Date(
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0)
);
// 🔹 Buscar dados do usuário e role // 🔹 Buscar dados do usuário e role
useEffect(() => { useEffect(() => {
@ -78,7 +98,10 @@ export default function ScheduleForm() {
setDoctors(data || []); setDoctors(data || []);
} catch (err) { } catch (err) {
console.error("Erro ao buscar médicos:", err); console.error("Erro ao buscar médicos:", err);
toast({ title: "Erro", description: "Não foi possível carregar médicos." }); toast({
title: "Erro",
description: "Não foi possível carregar médicos.",
});
} finally { } finally {
setLoadingDoctors(false); setLoadingDoctors(false);
} }
@ -101,7 +124,10 @@ export default function ScheduleForm() {
} }
}, []); }, []);
const computeAvailabilityCountsPreview = async (doctorId: string, dispList: any[]) => { const computeAvailabilityCountsPreview = async (
doctorId: string,
dispList: any[]
) => {
try { try {
const today = new Date(); const today = new Date();
const start = format(today, "yyyy-MM-dd"); const start = format(today, "yyyy-MM-dd");
@ -123,7 +149,9 @@ export default function ScheduleForm() {
const d = addDays(today, i); const d = addDays(today, i);
const key = format(d, "yyyy-MM-dd"); const key = format(d, "yyyy-MM-dd");
const dayOfWeek = d.getDay() === 0 ? 7 : d.getDay(); const dayOfWeek = d.getDay() === 0 ? 7 : d.getDay();
const dailyDisp = dispList.filter((p) => getWeekdayNumber(p.weekday) === dayOfWeek); const dailyDisp = dispList.filter(
(p) => getWeekdayNumber(p.weekday) === dayOfWeek
);
if (dailyDisp.length === 0) { if (dailyDisp.length === 0) {
counts[key] = 0; counts[key] = 0;
continue; continue;
@ -135,7 +163,8 @@ export default function ScheduleForm() {
const startMin = sh * 60 + sm; const startMin = sh * 60 + sm;
const endMin = eh * 60 + em; const endMin = eh * 60 + em;
const slot = p.slot_minutes || 30; const slot = p.slot_minutes || 30;
if (endMin >= startMin) possible += Math.floor((endMin - startMin) / slot) + 1; if (endMin >= startMin)
possible += Math.floor((endMin - startMin) / slot) + 1;
}); });
const occupied = apptsByDate[key] || 0; const occupied = apptsByDate[key] || 0;
counts[key] = Math.max(0, possible - occupied); counts[key] = Math.max(0, possible - occupied);
@ -161,7 +190,8 @@ export default function ScheduleForm() {
}, [selectedDoctor, loadDoctorDisponibilidades]); }, [selectedDoctor, loadDoctorDisponibilidades]);
// 🔹 Buscar horários disponíveis // 🔹 Buscar horários disponíveis
const fetchAvailableSlots = useCallback(async (doctorId: string, date: string) => { const fetchAvailableSlots = useCallback(
async (doctorId: string, date: string) => {
if (!doctorId || !date) return; if (!doctorId || !date) return;
setLoadingSlots(true); setLoadingSlots(true);
setAvailableTimes([]); setAvailableTimes([]);
@ -176,11 +206,18 @@ export default function ScheduleForm() {
(d: any) => getWeekdayNumber(d.weekday) === diaAPI (d: any) => getWeekdayNumber(d.weekday) === diaAPI
); );
if (!disponibilidadeDia) { if (!disponibilidadeDia) {
toast({ title: "Nenhuma disponibilidade", description: "Nenhum horário para este dia." }); toast({
title: "Nenhuma disponibilidade",
description: "Nenhum horário para este dia.",
});
return setAvailableTimes([]); return setAvailableTimes([]);
} }
const [startHour, startMin] = disponibilidadeDia.start_time.split(":").map(Number); const [startHour, startMin] = disponibilidadeDia.start_time
const [endHour, endMin] = disponibilidadeDia.end_time.split(":").map(Number); .split(":")
.map(Number);
const [endHour, endMin] = disponibilidadeDia.end_time
.split(":")
.map(Number);
const slot = disponibilidadeDia.slot_minutes || 30; const slot = disponibilidadeDia.slot_minutes || 30;
const horariosGerados: string[] = []; const horariosGerados: string[] = [];
let atual = new Date(date); let atual = new Date(date);
@ -202,10 +239,13 @@ export default function ScheduleForm() {
} finally { } finally {
setLoadingSlots(false); setLoadingSlots(false);
} }
}, []); },
[]
);
useEffect(() => { useEffect(() => {
if (selectedDoctor && selectedDate) fetchAvailableSlots(selectedDoctor, selectedDate); if (selectedDoctor && selectedDate)
fetchAvailableSlots(selectedDoctor, selectedDate);
}, [selectedDoctor, selectedDate, fetchAvailableSlots]); }, [selectedDoctor, selectedDate, fetchAvailableSlots]);
// 🔹 Submeter agendamento // 🔹 Submeter agendamento
@ -220,7 +260,10 @@ const handleSubmit = async (e: React.FormEvent) => {
const patientId = isSecretaryLike ? selectedPatient : userId; const patientId = isSecretaryLike ? selectedPatient : userId;
if (!patientId || !selectedDoctor || !selectedDate || !selectedTime) { if (!patientId || !selectedDoctor || !selectedDate || !selectedTime) {
toast({ title: "Campos obrigatórios", description: "Preencha todos os campos." }); toast({
title: "Campos obrigatórios",
description: "Preencha todos os campos.",
});
return; return;
} }
@ -261,11 +304,14 @@ try {
// Paciente → telefone vem do perfil do próprio usuário logado // Paciente → telefone vem do perfil do próprio usuário logado
const me = await usersService.getMe(); const me = await usersService.getMe();
const rawPhone = const rawPhone =
me?.profile?.phone || me?.profile?.phone ||
(typeof me?.profile === "object" && "phone_mobile" in me.profile ? (me.profile as any).phone_mobile : null) || (typeof me?.profile === "object" && "phone_mobile" in me.profile
(typeof me === "object" && "user_metadata" in me ? (me as any).user_metadata?.phone : null) || ? (me.profile as any).phone_mobile
: null) ||
(typeof me === "object" && "user_metadata" in me
? (me as any).user_metadata?.phone
: null) ||
null; null;
if (rawPhone) phoneNumber = rawPhone; if (rawPhone) phoneNumber = rawPhone;
@ -283,7 +329,6 @@ const rawPhone =
console.warn("⚠️ Não foi possível obter telefone do paciente:", err); console.warn("⚠️ Não foi possível obter telefone do paciente:", err);
} }
// 💬 envia o SMS de confirmação // 💬 envia o SMS de confirmação
// 💬 Envia o SMS de lembrete (sem mostrar nada ao paciente) // 💬 Envia o SMS de lembrete (sem mostrar nada ao paciente)
// 💬 Envia o SMS de lembrete (somente loga no console, não mostra no sistema) // 💬 Envia o SMS de lembrete (somente loga no console, não mostra no sistema)
@ -303,9 +348,6 @@ try {
console.error("❌ Erro ao enviar SMS:", smsErr); console.error("❌ Erro ao enviar SMS:", smsErr);
} }
// 🧹 limpa os campos // 🧹 limpa os campos
setSelectedDoctor(""); setSelectedDoctor("");
setSelectedDate(""); setSelectedDate("");
@ -318,10 +360,6 @@ try {
} }
}; };
// 🔹 Tooltip no calendário // 🔹 Tooltip no calendário
useEffect(() => { useEffect(() => {
const cont = calendarRef.current; const cont = calendarRef.current;
@ -359,19 +397,27 @@ try {
<CardTitle>Dados da Consulta</CardTitle> <CardTitle>Dados da Consulta</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleSubmit} className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <form
onSubmit={handleSubmit}
className="grid grid-cols-1 lg:grid-cols-2 gap-6"
>
<div className="space-y-3"> <div className="space-y-3">
{/* Se secretária/gestor/admin → mostrar campo Paciente */} {/* Se secretária/gestor/admin → mostrar campo Paciente */}
{["secretaria", "gestor", "admin"].includes(role) && ( {["secretaria", "gestor", "admin"].includes(role) && (
<div> <div>
<Label>Paciente</Label> <Label>Paciente</Label>
<Select value={selectedPatient} onValueChange={setSelectedPatient}> <Select
value={selectedPatient}
onValueChange={setSelectedPatient}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Selecione o paciente" /> <SelectValue placeholder="Selecione o paciente" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{patients.map((p) => ( {patients.map((p) => (
<SelectItem key={p.id} value={p.id}>{p.full_name}</SelectItem> <SelectItem key={p.id} value={p.id}>
{p.full_name}
</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@ -380,15 +426,24 @@ try {
<div> <div>
<Label>Médico</Label> <Label>Médico</Label>
<Select value={selectedDoctor} onValueChange={setSelectedDoctor}> <Select
value={selectedDoctor}
onValueChange={setSelectedDoctor}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Selecione o médico" /> <SelectValue placeholder="Selecione o médico" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{loadingDoctors ? ( {loadingDoctors ? (
<SelectItem value="loading" disabled>Carregando...</SelectItem> <SelectItem value="loading" disabled>
Carregando...
</SelectItem>
) : ( ) : (
doctors.map((d) => ( [...doctors]
.sort((a, b) =>
String(a.full_name).localeCompare(String(b.full_name))
)
.map((d) => (
<SelectItem key={d.id} value={d.id}> <SelectItem key={d.id} value={d.id}>
{d.full_name} {d.specialty} {d.full_name} {d.specialty}
</SelectItem> </SelectItem>
@ -404,10 +459,17 @@ try {
<CalendarShadcn <CalendarShadcn
mode="single" mode="single"
disabled={!selectedDoctor} disabled={!selectedDoctor}
selected={selectedDate ? new Date(selectedDate + "T12:00:00") : undefined} selected={
selectedDate
? new Date(selectedDate + "T12:00:00")
: undefined
}
onSelect={(date) => { onSelect={(date) => {
if (!date) return; if (!date) return;
const formatted = format(new Date(date.getTime() + 12 * 60 * 60 * 1000), "yyyy-MM-dd"); const formatted = format(
new Date(date.getTime() + 12 * 60 * 60 * 1000),
"yyyy-MM-dd"
);
setSelectedDate(formatted); setSelectedDate(formatted);
}} }}
/> />
@ -436,7 +498,8 @@ try {
<User className="h-4 w-4 text-blue-600" /> <User className="h-4 w-4 text-blue-600" />
<div className="text-xs"> <div className="text-xs">
{selectedDoctor {selectedDoctor
? doctors.find((d) => d.id === selectedDoctor)?.full_name ? doctors.find((d) => d.id === selectedDoctor)
?.full_name
: "Médico"} : "Médico"}
</div> </div>
</div> </div>
@ -447,7 +510,10 @@ try {
<div className="space-y-2"> <div className="space-y-2">
<Label>Horário</Label> <Label>Horário</Label>
<Select onValueChange={setSelectedTime} disabled={loadingSlots || availableTimes.length === 0}> <Select
onValueChange={setSelectedTime}
disabled={loadingSlots || availableTimes.length === 0}
>
<SelectTrigger> <SelectTrigger>
<SelectValue <SelectValue
placeholder={ placeholder={
@ -461,7 +527,9 @@ try {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{availableTimes.map((h) => ( {availableTimes.map((h) => (
<SelectItem key={h} value={h}>{h}</SelectItem> <SelectItem key={h} value={h}>
{h}
</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>