fix: mudança de status de consultas painel secretaria

This commit is contained in:
Pedro Araujo da Silveira 2025-11-04 16:37:23 -03:00
parent 60c8b5eaa9
commit 398b731dd9
2 changed files with 152 additions and 27 deletions

View File

@ -123,6 +123,11 @@ export function SecretaryAppointmentList() {
// Mapeia o valor selecionado no select para o valor real usado na API/data // Mapeia o valor selecionado no select para o valor real usado na API/data
const mapStatusFilterToValue = (label: string) => { const mapStatusFilterToValue = (label: string) => {
if (label === "Todos") return null; if (label === "Todos") return null;
// Se já é um valor de API, retorna ele mesmo
if (['requested', 'confirmed', 'checked_in', 'in_progress', 'completed', 'cancelled', 'no_show'].includes(label)) {
return label;
}
// Mantém mapeamento legado por compatibilidade
const map: Record<string, string> = { const map: Record<string, string> = {
Confirmada: "confirmed", Confirmada: "confirmed",
Agendada: "requested", Agendada: "requested",
@ -249,6 +254,50 @@ export function SecretaryAppointmentList() {
loadAppointments(); loadAppointments();
}; };
const handleStatusChange = async (appointmentId: string, newStatus: string) => {
try {
console.log(`[SecretaryAppointmentList] Atualizando status da consulta ${appointmentId} para ${newStatus}`);
await appointmentService.update(appointmentId, {
status: newStatus as any
});
toast.success(`Status atualizado para: ${
newStatus === 'requested' ? 'Solicitada' :
newStatus === 'confirmed' ? 'Confirmada' :
newStatus === 'checked_in' ? 'Check-in' :
newStatus === 'in_progress' ? 'Em Atendimento' :
newStatus === 'completed' ? 'Concluída' :
newStatus === 'cancelled' ? 'Cancelada' :
newStatus === 'no_show' ? 'Não Compareceu' : newStatus
}`);
loadAppointments();
} catch (error) {
console.error("Erro ao atualizar status:", error);
toast.error("Erro ao atualizar status da consulta");
}
};
const handleDeleteAppointment = async (appointmentId: string) => {
if (!confirm("Tem certeza que deseja cancelar esta consulta?")) {
return;
}
try {
await appointmentService.update(appointmentId, {
status: "cancelled",
cancelled_at: new Date().toISOString(),
cancellation_reason: "Cancelado pela secretaria"
});
toast.success("Consulta cancelada com sucesso!");
loadAppointments();
} catch (error) {
console.error("Erro ao cancelar consulta:", error);
toast.error("Erro ao cancelar consulta");
}
};
// Reset página quando filtros mudarem // Reset página quando filtros mudarem
useEffect(() => { useEffect(() => {
setCurrentPage(1); setCurrentPage(1);
@ -355,10 +404,13 @@ export function SecretaryAppointmentList() {
className="px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" className="px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
> >
<option>Todos</option> <option>Todos</option>
<option>Confirmada</option> <option value="requested">Solicitada</option>
<option>Agendada</option> <option value="confirmed">Confirmada</option>
<option>Cancelada</option> <option value="checked_in">Check-in</option>
<option>Concluída</option> <option value="in_progress">Em Atendimento</option>
<option value="completed">Concluída</option>
<option value="cancelled">Cancelada</option>
<option value="no_show">Não Compareceu</option>
</select> </select>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -496,27 +548,58 @@ export function SecretaryAppointmentList() {
</span> </span>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
{getStatusBadge(appointment.status || "agendada")} <select
value={appointment.status || "requested"}
onChange={(e) => handleStatusChange(appointment.id, e.target.value)}
className="px-3 py-1.5 text-xs font-medium rounded-full border-0 focus:ring-2 focus:ring-green-500 cursor-pointer"
style={{
backgroundColor:
appointment.status === "requested" ? "#fef3c7" :
appointment.status === "confirmed" ? "#d1fae5" :
appointment.status === "checked_in" ? "#fed7aa" :
appointment.status === "in_progress" ? "#fed7aa" :
appointment.status === "completed" ? "#dbeafe" :
appointment.status === "cancelled" ? "#f3f4f6" :
appointment.status === "no_show" ? "#fee2e2" : "#f3f4f6",
color:
appointment.status === "requested" ? "#92400e" :
appointment.status === "confirmed" ? "#065f46" :
appointment.status === "checked_in" ? "#9a3412" :
appointment.status === "in_progress" ? "#9a3412" :
appointment.status === "completed" ? "#1e40af" :
appointment.status === "cancelled" ? "#4b5563" :
appointment.status === "no_show" ? "#991b1b" : "#4b5563"
}}
>
<option value="requested">Solicitada</option>
<option value="confirmed">Confirmada</option>
<option value="checked_in">Check-in</option>
<option value="in_progress">Em Atendimento</option>
<option value="completed">Concluída</option>
<option value="cancelled">Cancelada</option>
<option value="no_show">Não Compareceu</option>
</select>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => handleViewAppointment(appointment)} onClick={() => handleViewAppointment(appointment)}
title="Visualizar" title="Visualizar"
className="p-2 text-blue-600 hover:bg-blue-50 rounded-lg transition-colors" className="p-2 text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900 rounded-lg transition-colors"
> >
<Eye className="h-4 w-4" /> <Eye className="h-4 w-4" />
</button> </button>
<button <button
onClick={() => handleEditAppointment(appointment)} onClick={() => handleEditAppointment(appointment)}
title="Editar" title="Editar"
className="p-2 text-orange-600 hover:bg-orange-50 rounded-lg transition-colors" className="p-2 text-orange-600 hover:bg-orange-50 dark:hover:bg-orange-900 rounded-lg transition-colors"
> >
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
</button> </button>
<button <button
onClick={() => handleDeleteAppointment(appointment.id)}
title="Cancelar" title="Cancelar"
className="p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors" className="p-2 text-red-600 hover:bg-red-50 dark:hover:bg-red-900 rounded-lg transition-colors"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</button> </button>

View File

@ -516,24 +516,32 @@ export function SecretaryDoctorSchedule() {
{/* Legenda */} {/* Legenda */}
<div className="mb-4 flex flex-wrap gap-3 text-xs"> <div className="mb-4 flex flex-wrap gap-3 text-xs">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-yellow-100 border border-yellow-300"></div> <div className="w-3 h-3 rounded bg-yellow-100 dark:bg-yellow-900 border border-yellow-300"></div>
<span className="text-gray-600 dark:text-gray-400">Solicitada</span> <span className="text-gray-600 dark:text-gray-400">Solicitada</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-green-100 border border-green-300"></div> <div className="w-3 h-3 rounded bg-green-100 dark:bg-green-900 border border-green-300"></div>
<span className="text-gray-600 dark:text-gray-400">Confirmada</span> <span className="text-gray-600 dark:text-gray-400">Confirmada</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-blue-100 border border-blue-300"></div> <div className="w-3 h-3 rounded bg-orange-100 dark:bg-orange-900 border border-orange-300"></div>
<span className="text-gray-600 dark:text-gray-400">Em Atendimento</span>
</div>
<div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-blue-100 dark:bg-blue-900 border border-blue-300"></div>
<span className="text-gray-600 dark:text-gray-400">Concluída</span> <span className="text-gray-600 dark:text-gray-400">Concluída</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-red-100 border border-red-300"></div> <div className="w-3 h-3 rounded bg-gray-100 dark:bg-gray-700 border border-gray-300"></div>
<span className="text-gray-600">Bloqueio</span> <span className="text-gray-600 dark:text-gray-400">Cancelada</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-purple-100 border border-purple-300"></div> <div className="w-3 h-3 rounded bg-red-100 dark:bg-red-900 border border-red-300"></div>
<span className="text-gray-600">Disponibilidade Extra</span> <span className="text-gray-600 dark:text-gray-400">Bloqueio</span>
</div>
<div className="flex items-center gap-1">
<div className="w-3 h-3 rounded bg-purple-100 dark:bg-purple-900 border border-purple-300"></div>
<span className="text-gray-600 dark:text-gray-400">Disponibilidade Extra</span>
</div> </div>
</div> </div>
@ -593,21 +601,55 @@ export function SecretaryDoctorSchedule() {
minute: '2-digit', minute: '2-digit',
}) })
: ''; : '';
// Determina as cores baseado no status
let bgColor = '';
let textColor = '';
let borderColor = '';
switch (apt.status) {
case 'requested':
bgColor = '!bg-yellow-100';
textColor = '!text-yellow-800';
borderColor = 'border-yellow-300';
break;
case 'confirmed':
bgColor = '!bg-green-100';
textColor = '!text-green-800';
borderColor = 'border-green-300';
break;
case 'completed':
bgColor = '!bg-blue-100';
textColor = '!text-blue-800';
borderColor = 'border-blue-300';
break;
case 'cancelled':
bgColor = '!bg-gray-100';
textColor = '!text-gray-600';
borderColor = 'border-gray-300';
break;
case 'checked_in':
case 'in_progress':
bgColor = '!bg-orange-100';
textColor = '!text-orange-800';
borderColor = 'border-orange-300';
break;
case 'no_show':
bgColor = '!bg-red-100';
textColor = '!text-red-800';
borderColor = 'border-red-300';
break;
default:
bgColor = '!bg-gray-100';
textColor = '!text-gray-600';
borderColor = 'border-gray-300';
}
return ( return (
<div <div
key={`apt-${i}`} key={`apt-${i}`}
className={`text-xs p-1 rounded mb-1 truncate ${ className={`text-xs p-1 rounded mb-1 truncate border ${bgColor} ${textColor} ${borderColor}`}
apt.status === "requested" title={`${time} - Status: ${apt.status} - Paciente: ${apt.patient_id}`}
? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200"
: apt.status === "confirmed"
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
: apt.status === "completed"
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
: apt.status === "cancelled"
? "bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"
: "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200"
}`}
title={`${time} - ${apt.patient_id}`}
> >
📅 {time} 📅 {time}
</div> </div>