6.5 KiB
6.5 KiB
Sistema de Agendamento com API de Slots
Implementação Concluída ✅
Fluxo de Agendamento
- Usuário seleciona médico → Mostra calendário
- Usuário seleciona data → Chama API de slots disponíveis
- API calcula horários → Considera:
- Disponibilidade do médico (agenda configurada)
- Exceções (bloqueios e horários extras)
- Antecedência mínima para agendamento
- Consultas já agendadas
- Usuário seleciona horário e preenche motivo
- Sistema cria agendamento → Salva no banco
APIs Implementadas
1. Calcular Slots Disponíveis
Endpoint: POST /functions/v1/get-available-slots
Request:
{
"doctor_id": "uuid-do-medico",
"date": "2025-10-30"
}
Response:
{
"slots": [
{
"time": "09:00",
"available": true
},
{
"time": "09:30",
"available": false
},
{
"time": "10:00",
"available": true
}
]
}
Código Implementado:
// src/services/appointments/appointmentService.ts
async getAvailableSlots(data: GetAvailableSlotsInput): Promise<GetAvailableSlotsResponse> {
const response = await apiClient.post<GetAvailableSlotsResponse>(
"/functions/v1/get-available-slots",
data
);
return response.data;
}
2. Criar Agendamento
Endpoint: POST /rest/v1/appointments
Request:
{
"doctor_id": "uuid-do-medico",
"patient_id": "uuid-do-paciente",
"scheduled_at": "2025-10-30T09:00:00Z",
"duration_minutes": 30,
"appointment_type": "presencial",
"chief_complaint": "Consulta de rotina",
"created_by": "uuid-do-usuario"
}
Response:
{
"id": "uuid-do-agendamento",
"order_number": "APT-2025-0001",
"status": "requested",
...
}
Código Implementado:
// src/services/appointments/appointmentService.ts
async create(data: CreateAppointmentInput): Promise<Appointment> {
const payload = {
...data,
duration_minutes: data.duration_minutes || 30,
appointment_type: data.appointment_type || "presencial",
status: "requested",
};
const response = await apiClient.post<Appointment[]>(
"/rest/v1/appointments",
payload,
{
headers: {
Prefer: "return=representation",
},
}
);
return response.data[0];
}
Componente AgendamentoConsulta
Principais Melhorias
Antes ❌
- Calculava slots manualmente no frontend
- Precisava carregar disponibilidade + exceções separadamente
- Lógica complexa de validação no cliente
- Não considerava antecedência mínima
- Não verificava consultas já agendadas
Depois ✅
- Usa Edge Function para calcular slots
- API retorna apenas horários realmente disponíveis
- Validações centralizadas no backend
- Considera todas as regras de negócio
- Performance melhorada (menos requisições)
Código Simplificado
// src/components/AgendamentoConsulta.tsx
const calculateAvailableSlots = useCallback(async () => {
if (!selectedDate || !selectedMedico) {
setAvailableSlots([]);
return;
}
try {
const dateStr = format(selectedDate, "yyyy-MM-dd");
// Chama a Edge Function
const response = await appointmentService.getAvailableSlots({
doctor_id: selectedMedico.id,
date: dateStr,
});
if (response && response.slots) {
// Filtra apenas slots disponíveis
const available = response.slots
.filter((slot) => slot.available)
.map((slot) => slot.time);
setAvailableSlots(available);
} else {
setAvailableSlots([]);
}
} catch (error) {
console.error("Erro ao buscar slots:", error);
setAvailableSlots([]);
}
}, [selectedDate, selectedMedico]);
const confirmAppointment = async () => {
if (!selectedMedico || !selectedDate || !selectedTime || !user) return;
try {
const scheduledAt =
format(selectedDate, "yyyy-MM-dd") + "T" + selectedTime + ":00Z";
// Cria o agendamento
const appointment = await appointmentService.create({
patient_id: user.id,
doctor_id: selectedMedico.id,
scheduled_at: scheduledAt,
duration_minutes: 30,
appointment_type:
appointmentType === "online" ? "telemedicina" : "presencial",
chief_complaint: motivo,
});
console.log("Consulta criada:", appointment);
setBookingSuccess(true);
} catch (error) {
setBookingError(error.message);
}
};
Tipos TypeScript
// src/services/appointments/types.ts
export interface GetAvailableSlotsInput {
doctor_id: string;
date: string; // YYYY-MM-DD
}
export interface TimeSlot {
time: string; // HH:MM (ex: "09:00")
available: boolean;
}
export interface GetAvailableSlotsResponse {
slots: TimeSlot[];
}
export interface CreateAppointmentInput {
patient_id: string;
doctor_id: string;
scheduled_at: string; // ISO 8601
duration_minutes?: number;
appointment_type?: "presencial" | "telemedicina";
chief_complaint?: string;
patient_notes?: string;
insurance_provider?: string;
}
Benefícios da Implementação
✅ Performance: Menos requisições ao backend
✅ Confiabilidade: Validações centralizadas
✅ Manutenibilidade: Lógica de negócio no servidor
✅ Escalabilidade: Edge Functions são otimizadas
✅ UX: Interface mais responsiva e clara
✅ Segurança: Validações no backend não podem ser burladas
Próximos Passos (Opcional)
- Adicionar loading states mais detalhados
- Implementar cache de slots (evitar chamadas repetidas)
- Adicionar retry automático em caso de falha
- Mostrar motivo quando slot não está disponível
- Implementar notificações (SMS/Email) após agendamento
Testando
1. Selecione um médico
2. Selecione uma data futura
3. Verifique os slots disponíveis
4. Selecione um horário
5. Preencha o motivo
6. Confirme o agendamento
Logs no Console:
[AppointmentService] Buscando slots para: {doctor_id, date}
[AppointmentService] Slots recebidos: 12 slots
[AppointmentService] Criando agendamento...
[AppointmentService] Consulta criada: {id, order_number, ...}
Data de Implementação
30 de Outubro de 2025
Implementado por: GitHub Copilot
Revisado por: Equipe RiseUp Squad 18