Atualizar app/secretary/schedule/page.tsx
This commit is contained in:
parent
009df09665
commit
8ec438169e
@ -12,72 +12,104 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Calendar, Clock, User } from "lucide-react";
|
import { Calendar, Clock, User } from "lucide-react";
|
||||||
import { patientsService } from "@/services/patientsApi.mjs";
|
import { patientsService } from "@/services/patientsApi.mjs";
|
||||||
import { doctorsService } from "@/services/doctorsApi.mjs"; // Importar o serviço de médicos
|
import { doctorsService } from "@/services/doctorsApi.mjs";
|
||||||
|
import { appointmentsService } from "@/services/appointmentsApi.mjs";
|
||||||
|
import { usersService } from "@/services/usersApi.mjs"; // 1. IMPORTAR O SERVIÇO DE USUÁRIOS
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const APPOINTMENTS_STORAGE_KEY = "clinic-appointments";
|
|
||||||
|
|
||||||
export default function ScheduleAppointment() {
|
export default function ScheduleAppointment() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [patients, setPatients] = useState<any[]>([]);
|
const [patients, setPatients] = useState<any[]>([]);
|
||||||
const [doctors, setDoctors] = useState<any[]>([]); // Estado para armazenar os médicos da API
|
const [doctors, setDoctors] = useState<any[]>([]);
|
||||||
|
const [currentUserId, setCurrentUserId] = useState<string | null>(null); // 2. NOVO ESTADO PARA O ID DO USUÁRIO
|
||||||
|
|
||||||
|
// Estados do formulário
|
||||||
const [selectedPatient, setSelectedPatient] = useState("");
|
const [selectedPatient, setSelectedPatient] = useState("");
|
||||||
const [selectedDoctor, setSelectedDoctor] = useState("");
|
const [selectedDoctor, setSelectedDoctor] = useState("");
|
||||||
const [selectedDate, setSelectedDate] = useState("");
|
const [selectedDate, setSelectedDate] = useState("");
|
||||||
const [selectedTime, setSelectedTime] = useState("");
|
const [selectedTime, setSelectedTime] = useState("");
|
||||||
const [notes, setNotes] = useState("");
|
const [appointmentType, setAppointmentType] = useState("presencial");
|
||||||
|
const [durationMinutes, setDurationMinutes] = useState("30");
|
||||||
|
const [chiefComplaint, setChiefComplaint] = useState("");
|
||||||
|
const [patientNotes, setPatientNotes] = useState("");
|
||||||
|
const [internalNotes, setInternalNotes] = useState("");
|
||||||
|
const [insuranceProvider, setInsuranceProvider] = useState("");
|
||||||
|
|
||||||
|
const availableTimes = [
|
||||||
|
"08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30",
|
||||||
|
"14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Efeito para carregar todos os dados iniciais (pacientes, médicos e usuário atual)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchInitialData = async () => {
|
||||||
try {
|
try {
|
||||||
// Carrega pacientes e médicos em paralelo para melhor performance
|
// Busca tudo em paralelo para melhor performance
|
||||||
const [patientList, doctorList] = await Promise.all([
|
const [patientList, doctorList, currentUser] = await Promise.all([
|
||||||
patientsService.list(),
|
patientsService.list(),
|
||||||
doctorsService.list()
|
doctorsService.list(),
|
||||||
|
usersService.summary_data() // 3. CHAMADA PARA BUSCAR O USUÁRIO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setPatients(patientList);
|
setPatients(patientList);
|
||||||
setDoctors(doctorList);
|
setDoctors(doctorList);
|
||||||
|
|
||||||
|
if (currentUser && currentUser.id) {
|
||||||
|
setCurrentUserId(currentUser.id); // Armazena o ID do usuário no estado
|
||||||
|
console.log("Usuário logado identificado:", currentUser.id);
|
||||||
|
} else {
|
||||||
|
toast.error("Não foi possível identificar o usuário logado. O agendamento pode falhar.");
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Falha ao buscar dados iniciais:", error);
|
console.error("Falha ao buscar dados iniciais:", error);
|
||||||
toast.error("Não foi possível carregar os dados de pacientes e médicos.");
|
toast.error("Não foi possível carregar os dados necessários para a página.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchData();
|
fetchInitialData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const availableTimes = ["08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30"];
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const patientDetails = patients.find((p) => String(p.id) === selectedPatient);
|
// 4. ADICIONAR VALIDAÇÃO PARA O ID DO USUÁRIO
|
||||||
const doctorDetails = doctors.find((d) => String(d.id) === selectedDoctor);
|
if (!currentUserId) {
|
||||||
|
toast.error("Sessão de usuário inválida. Por favor, faça login novamente.");
|
||||||
if (!patientDetails || !doctorDetails) {
|
|
||||||
toast.error("Erro ao encontrar detalhes do paciente ou médico.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newAppointment = {
|
if (!selectedPatient || !selectedDoctor || !selectedDate || !selectedTime) {
|
||||||
id: new Date().getTime(), // ID único simples
|
toast.error("Paciente, médico, data e horário são obrigatórios.");
|
||||||
patientName: patientDetails.full_name,
|
return;
|
||||||
doctor: doctorDetails.full_name, // Usar full_name para consistência
|
}
|
||||||
specialty: doctorDetails.specialty,
|
|
||||||
date: selectedDate,
|
|
||||||
time: selectedTime,
|
|
||||||
status: "agendada",
|
|
||||||
location: doctorDetails.location || "Consultório a definir", // Fallback
|
|
||||||
phone: doctorDetails.phone || "N/A", // Fallback
|
|
||||||
};
|
|
||||||
|
|
||||||
const storedAppointmentsRaw = localStorage.getItem(APPOINTMENTS_STORAGE_KEY);
|
try {
|
||||||
const currentAppointments = storedAppointmentsRaw ? JSON.parse(storedAppointmentsRaw) : [];
|
const scheduledAt = new Date(`${selectedDate}T${selectedTime}:00Z`).toISOString();
|
||||||
const updatedAppointments = [...currentAppointments, newAppointment];
|
|
||||||
|
|
||||||
localStorage.setItem(APPOINTMENTS_STORAGE_KEY, JSON.stringify(updatedAppointments));
|
const newAppointmentData = {
|
||||||
|
patient_id: selectedPatient,
|
||||||
|
doctor_id: selectedDoctor,
|
||||||
|
scheduled_at: scheduledAt,
|
||||||
|
duration_minutes: parseInt(durationMinutes, 10),
|
||||||
|
appointment_type: appointmentType,
|
||||||
|
status: "requested",
|
||||||
|
chief_complaint: chiefComplaint || null,
|
||||||
|
patient_notes: patientNotes || null,
|
||||||
|
notes: internalNotes || null,
|
||||||
|
insurance_provider: insuranceProvider || null,
|
||||||
|
created_by: currentUserId, // 5. INCLUIR O ID DO USUÁRIO NO OBJETO
|
||||||
|
};
|
||||||
|
|
||||||
toast.success("Consulta agendada com sucesso!");
|
console.log("Enviando dados do agendamento:", newAppointmentData); // Log para depuração
|
||||||
router.push("/secretary/appointments");
|
|
||||||
|
await appointmentsService.create(newAppointmentData);
|
||||||
|
|
||||||
|
toast.success("Consulta agendada com sucesso!");
|
||||||
|
router.push("/secretary/appointments");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao criar agendamento:", error);
|
||||||
|
toast.error("Ocorreu um erro ao agendar a consulta. Tente novamente.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -85,7 +117,7 @@ export default function ScheduleAppointment() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900">Agendar Consulta</h1>
|
<h1 className="text-3xl font-bold text-gray-900">Agendar Consulta</h1>
|
||||||
<p className="text-gray-600">Escolha o paciente, médico, data e horário para a consulta</p>
|
<p className="text-gray-600">Preencha os detalhes para criar um novo agendamento</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid lg:grid-cols-3 gap-6">
|
<div className="grid lg:grid-cols-3 gap-6">
|
||||||
@ -97,48 +129,14 @@ export default function ScheduleAppointment() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{/* O restante do formulário permanece exatamente o mesmo */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="patient">Paciente</Label>
|
<Label htmlFor="patient">Paciente</Label>
|
||||||
<Select value={selectedPatient} onValueChange={setSelectedPatient}>
|
<Select value={selectedPatient} onValueChange={setSelectedPatient}><SelectTrigger><SelectValue placeholder="Selecione um paciente" /></SelectTrigger><SelectContent>{patients.map((p) => (<SelectItem key={p.id} value={p.id}>{p.full_name}</SelectItem>))}</SelectContent></Select>
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Selecione um paciente" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{patients.length > 0 ? (
|
|
||||||
patients.map((patient) => (
|
|
||||||
<SelectItem key={patient.id} value={String(patient.id)}>
|
|
||||||
{patient.full_name}
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SelectItem value="loading" disabled>
|
|
||||||
Carregando pacientes...
|
|
||||||
</SelectItem>
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="doctor">Médico</Label>
|
<Label htmlFor="doctor">Médico</Label>
|
||||||
<Select value={selectedDoctor} onValueChange={setSelectedDoctor}>
|
<Select value={selectedDoctor} onValueChange={setSelectedDoctor}><SelectTrigger><SelectValue placeholder="Selecione um médico" /></SelectTrigger><SelectContent>{doctors.map((d) => (<SelectItem key={d.id} value={d.id}>{d.full_name} - {d.specialty}</SelectItem>))}</SelectContent></Select>
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Selecione um médico" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{doctors.length > 0 ? (
|
|
||||||
doctors.map((doctor) => (
|
|
||||||
<SelectItem key={doctor.id} value={String(doctor.id)}>
|
|
||||||
{doctor.full_name} - {doctor.specialty}
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SelectItem value="loading" disabled>
|
|
||||||
Carregando médicos...
|
|
||||||
</SelectItem>
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
@ -146,7 +144,6 @@ export default function ScheduleAppointment() {
|
|||||||
<Label htmlFor="date">Data</Label>
|
<Label htmlFor="date">Data</Label>
|
||||||
<Input id="date" type="date" value={selectedDate} onChange={(e) => setSelectedDate(e.target.value)} min={new Date().toISOString().split("T")[0]} />
|
<Input id="date" type="date" value={selectedDate} onChange={(e) => setSelectedDate(e.target.value)} min={new Date().toISOString().split("T")[0]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="time">Horário</Label>
|
<Label htmlFor="time">Horário</Label>
|
||||||
<Select value={selectedTime} onValueChange={setSelectedTime}>
|
<Select value={selectedTime} onValueChange={setSelectedTime}>
|
||||||
@ -164,12 +161,34 @@ export default function ScheduleAppointment() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="appointmentType">Tipo de Consulta</Label>
|
||||||
|
<Select value={appointmentType} onValueChange={setAppointmentType}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent><SelectItem value="presencial">Presencial</SelectItem><SelectItem value="telemedicina">Telemedicina</SelectItem></SelectContent></Select>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="duration">Duração (minutos)</Label>
|
||||||
|
<Input id="duration" type="number" value={durationMinutes} onChange={(e) => setDurationMinutes(e.target.value)} placeholder="Ex: 30" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="notes">Observações (opcional)</Label>
|
<Label htmlFor="insurance">Convênio (opcional)</Label>
|
||||||
<Textarea id="notes" placeholder="Descreva brevemente o motivo da consulta ou observações importantes" value={notes} onChange={(e) => setNotes(e.target.value)} rows={3} />
|
<Input id="insurance" placeholder="Nome do convênio do paciente" value={insuranceProvider} onChange={(e) => setInsuranceProvider(e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="chiefComplaint">Queixa Principal (opcional)</Label>
|
||||||
|
<Textarea id="chiefComplaint" placeholder="Descreva brevemente o motivo da consulta..." value={chiefComplaint} onChange={(e) => setChiefComplaint(e.target.value)} rows={2} />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="patientNotes">Observações do Paciente (opcional)</Label>
|
||||||
|
<Textarea id="patientNotes" placeholder="Anotações relevantes informadas pelo paciente..." value={patientNotes} onChange={(e) => setPatientNotes(e.target.value)} rows={2} />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="internalNotes">Observações Internas (opcional)</Label>
|
||||||
|
<Textarea id="internalNotes" placeholder="Anotações para a equipe da clínica..." value={internalNotes} onChange={(e) => setInternalNotes(e.target.value)} rows={2} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button type="submit" className="w-full" disabled={!selectedPatient || !selectedDoctor || !selectedDate || !selectedTime}>
|
<Button type="submit" className="w-full" disabled={!selectedPatient || !selectedDoctor || !selectedDate || !selectedTime || !currentUserId}>
|
||||||
Agendar Consulta
|
Agendar Consulta
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
@ -178,61 +197,7 @@ export default function ScheduleAppointment() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card>
|
{/* Card de Resumo e Informações Importantes */}
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center">
|
|
||||||
<Calendar className="mr-2 h-5 w-5" />
|
|
||||||
Resumo
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
{selectedPatient && (
|
|
||||||
<div className="flex items-start space-x-2">
|
|
||||||
<User className="h-4 w-4 text-gray-500 mt-1 flex-shrink-0" />
|
|
||||||
<div className="text-sm">
|
|
||||||
<span className="font-semibold text-gray-800">Paciente:</span>
|
|
||||||
<p className="text-gray-600">{patients.find((p) => String(p.id) === selectedPatient)?.full_name}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedDoctor && (
|
|
||||||
<div className="flex items-start space-x-2">
|
|
||||||
<User className="h-4 w-4 text-gray-500 mt-1 flex-shrink-0" />
|
|
||||||
<div className="text-sm">
|
|
||||||
<span className="font-semibold text-gray-800">Médico:</span>
|
|
||||||
<p className="text-gray-600">{doctors.find((d) => String(d.id) === selectedDoctor)?.full_name}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedDate && (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Calendar className="h-4 w-4 text-gray-500" />
|
|
||||||
<span className="text-sm">{new Date(selectedDate).toLocaleDateString("pt-BR", { timeZone: "UTC" })}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedTime && (
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Clock className="h-4 w-4 text-gray-500" />
|
|
||||||
<span className="text-sm">{selectedTime}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Informações Importantes</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="text-sm text-gray-600 space-y-2">
|
|
||||||
<p>• Chegue com 15 minutos de antecedência</p>
|
|
||||||
<p>• Traga documento com foto</p>
|
|
||||||
<p>• Traga carteirinha do convênio</p>
|
|
||||||
<p>• Traga exames anteriores, se houver</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user