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 { Button } from "@/components/ui/button";
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 { Calendar as CalendarShadcn } from "@/components/ui/calendar";
import { format, addDays } from "date-fns";
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";
export default function ScheduleForm() {
// Estado do usuário e role
const [role, setRole] = useState<string>("paciente");
@ -39,17 +44,32 @@ export default function ScheduleForm() {
const [tipoConsulta] = useState("presencial");
const [duracao] = useState("30");
const [disponibilidades, setDisponibilidades] = useState<any[]>([]);
const [availabilityCounts, setAvailabilityCounts] = useState<Record<string, number>>({});
const [tooltip, setTooltip] = useState<{ x: number; y: number; text: string } | null>(null);
const [availabilityCounts, setAvailabilityCounts] = useState<
Record<string, number>
>({});
const [tooltip, setTooltip] = useState<{
x: number;
y: number;
text: string;
} | null>(null);
const calendarRef = useRef<HTMLDivElement | null>(null);
// Funções auxiliares
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) =>
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
useEffect(() => {
@ -78,7 +98,10 @@ export default function ScheduleForm() {
setDoctors(data || []);
} catch (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 {
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 {
const today = new Date();
const start = format(today, "yyyy-MM-dd");
@ -123,7 +149,9 @@ export default function ScheduleForm() {
const d = addDays(today, i);
const key = format(d, "yyyy-MM-dd");
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) {
counts[key] = 0;
continue;
@ -135,7 +163,8 @@ export default function ScheduleForm() {
const startMin = sh * 60 + sm;
const endMin = eh * 60 + em;
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;
counts[key] = Math.max(0, possible - occupied);
@ -161,7 +190,8 @@ export default function ScheduleForm() {
}, [selectedDoctor, loadDoctorDisponibilidades]);
// 🔹 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;
setLoadingSlots(true);
setAvailableTimes([]);
@ -176,11 +206,18 @@ export default function ScheduleForm() {
(d: any) => getWeekdayNumber(d.weekday) === diaAPI
);
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([]);
}
const [startHour, startMin] = disponibilidadeDia.start_time.split(":").map(Number);
const [endHour, endMin] = disponibilidadeDia.end_time.split(":").map(Number);
const [startHour, startMin] = disponibilidadeDia.start_time
.split(":")
.map(Number);
const [endHour, endMin] = disponibilidadeDia.end_time
.split(":")
.map(Number);
const slot = disponibilidadeDia.slot_minutes || 30;
const horariosGerados: string[] = [];
let atual = new Date(date);
@ -202,10 +239,13 @@ export default function ScheduleForm() {
} finally {
setLoadingSlots(false);
}
}, []);
},
[]
);
useEffect(() => {
if (selectedDoctor && selectedDate) fetchAvailableSlots(selectedDoctor, selectedDate);
if (selectedDoctor && selectedDate)
fetchAvailableSlots(selectedDoctor, selectedDate);
}, [selectedDoctor, selectedDate, fetchAvailableSlots]);
// 🔹 Submeter agendamento
@ -220,7 +260,10 @@ const handleSubmit = async (e: React.FormEvent) => {
const patientId = isSecretaryLike ? selectedPatient : userId;
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;
}
@ -261,11 +304,14 @@ try {
// Paciente → telefone vem do perfil do próprio usuário logado
const me = await usersService.getMe();
const rawPhone =
me?.profile?.phone ||
(typeof me?.profile === "object" && "phone_mobile" in me.profile ? (me.profile as any).phone_mobile : null) ||
(typeof me === "object" && "user_metadata" in me ? (me as any).user_metadata?.phone : null) ||
(typeof me?.profile === "object" && "phone_mobile" in me.profile
? (me.profile as any).phone_mobile
: null) ||
(typeof me === "object" && "user_metadata" in me
? (me as any).user_metadata?.phone
: null) ||
null;
if (rawPhone) phoneNumber = rawPhone;
@ -283,7 +329,6 @@ const rawPhone =
console.warn("⚠️ Não foi possível obter telefone do paciente:", err);
}
// 💬 envia o SMS de confirmação
// 💬 Envia o SMS de lembrete (sem mostrar nada ao paciente)
// 💬 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);
}
// 🧹 limpa os campos
setSelectedDoctor("");
setSelectedDate("");
@ -318,10 +360,6 @@ try {
}
};
// 🔹 Tooltip no calendário
useEffect(() => {
const cont = calendarRef.current;
@ -359,19 +397,27 @@ try {
<CardTitle>Dados da Consulta</CardTitle>
</CardHeader>
<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">
{/* Se secretária/gestor/admin → mostrar campo Paciente */}
{["secretaria", "gestor", "admin"].includes(role) && (
<div>
<Label>Paciente</Label>
<Select value={selectedPatient} onValueChange={setSelectedPatient}>
<Select
value={selectedPatient}
onValueChange={setSelectedPatient}
>
<SelectTrigger>
<SelectValue placeholder="Selecione o paciente" />
</SelectTrigger>
<SelectContent>
{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>
</Select>
@ -380,15 +426,24 @@ try {
<div>
<Label>Médico</Label>
<Select value={selectedDoctor} onValueChange={setSelectedDoctor}>
<Select
value={selectedDoctor}
onValueChange={setSelectedDoctor}
>
<SelectTrigger>
<SelectValue placeholder="Selecione o médico" />
</SelectTrigger>
<SelectContent>
{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}>
{d.full_name} {d.specialty}
</SelectItem>
@ -404,10 +459,17 @@ try {
<CalendarShadcn
mode="single"
disabled={!selectedDoctor}
selected={selectedDate ? new Date(selectedDate + "T12:00:00") : undefined}
selected={
selectedDate
? new Date(selectedDate + "T12:00:00")
: undefined
}
onSelect={(date) => {
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);
}}
/>
@ -436,7 +498,8 @@ try {
<User className="h-4 w-4 text-blue-600" />
<div className="text-xs">
{selectedDoctor
? doctors.find((d) => d.id === selectedDoctor)?.full_name
? doctors.find((d) => d.id === selectedDoctor)
?.full_name
: "Médico"}
</div>
</div>
@ -447,7 +510,10 @@ try {
<div className="space-y-2">
<Label>Horário</Label>
<Select onValueChange={setSelectedTime} disabled={loadingSlots || availableTimes.length === 0}>
<Select
onValueChange={setSelectedTime}
disabled={loadingSlots || availableTimes.length === 0}
>
<SelectTrigger>
<SelectValue
placeholder={
@ -461,7 +527,9 @@ try {
</SelectTrigger>
<SelectContent>
{availableTimes.map((h) => (
<SelectItem key={h} value={h}>{h}</SelectItem>
<SelectItem key={h} value={h}>
{h}
</SelectItem>
))}
</SelectContent>
</Select>