fix: relatorios
This commit is contained in:
parent
5a60e9a233
commit
9b6fa7ff36
@ -65,7 +65,6 @@ export default function AgendamentoConsulta({
|
||||
>("presencial");
|
||||
const [motivo, setMotivo] = useState("");
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
const [bookingSuccess, setBookingSuccess] = useState(false);
|
||||
const [bookingError, setBookingError] = useState("");
|
||||
const [showResultModal, setShowResultModal] = useState(false);
|
||||
const [resultType, setResultType] = useState<"success" | "error">("success");
|
||||
@ -133,40 +132,9 @@ export default function AgendamentoConsulta({
|
||||
return;
|
||||
}
|
||||
|
||||
// Mapeamento de string para número (formato da API)
|
||||
const weekdayMap: Record<string, number> = {
|
||||
sunday: 0,
|
||||
monday: 1,
|
||||
tuesday: 2,
|
||||
wednesday: 3,
|
||||
thursday: 4,
|
||||
friday: 5,
|
||||
saturday: 6,
|
||||
};
|
||||
|
||||
// Mapeia os dias da semana que o médico atende (converte para número)
|
||||
// Mapeia os dias da semana que o médico atende (weekday é sempre número 0-6)
|
||||
const availableWeekdays = new Set<number>(
|
||||
availabilities
|
||||
.map((avail) => {
|
||||
// weekday pode vir como número ou string da API
|
||||
let weekdayNum: number;
|
||||
|
||||
if (typeof avail.weekday === "number") {
|
||||
weekdayNum = avail.weekday;
|
||||
} else if (typeof avail.weekday === "string") {
|
||||
weekdayNum = weekdayMap[avail.weekday.toLowerCase()] ?? -1;
|
||||
} else {
|
||||
weekdayNum = -1;
|
||||
}
|
||||
|
||||
console.log("[AgendamentoConsulta] Convertendo weekday:", {
|
||||
original: avail.weekday,
|
||||
type: typeof avail.weekday,
|
||||
converted: weekdayNum,
|
||||
});
|
||||
return weekdayNum;
|
||||
})
|
||||
.filter((day) => day >= 0 && day <= 6) // Remove valores inválidos
|
||||
availabilities.map((avail) => avail.weekday)
|
||||
);
|
||||
|
||||
console.log(
|
||||
@ -249,18 +217,8 @@ export default function AgendamentoConsulta({
|
||||
return;
|
||||
}
|
||||
|
||||
// Pega o dia da semana da data selecionada
|
||||
const weekdayMap: Record<number, string> = {
|
||||
0: "sunday",
|
||||
1: "monday",
|
||||
2: "tuesday",
|
||||
3: "wednesday",
|
||||
4: "thursday",
|
||||
5: "friday",
|
||||
6: "saturday",
|
||||
};
|
||||
|
||||
const dayOfWeek = weekdayMap[selectedDate.getDay()];
|
||||
// Pega o dia da semana da data selecionada (0-6)
|
||||
const dayOfWeek = selectedDate.getDay() as 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
console.log(
|
||||
"[AgendamentoConsulta] Dia da semana selecionado:",
|
||||
dayOfWeek
|
||||
@ -309,6 +267,53 @@ export default function AgendamentoConsulta({
|
||||
}
|
||||
}
|
||||
|
||||
// Busca exceções (bloqueios) para este médico
|
||||
const exceptions = await availabilityService.listExceptions({
|
||||
doctor_id: selectedMedico.id,
|
||||
});
|
||||
|
||||
console.log("[AgendamentoConsulta] Exceções encontradas:", exceptions);
|
||||
|
||||
// Verifica se a data está bloqueada (exceção de bloqueio)
|
||||
const dayException = exceptions.find(
|
||||
(exc) => exc.date === dateStr && exc.kind === "bloqueio"
|
||||
);
|
||||
|
||||
if (dayException) {
|
||||
console.log(
|
||||
"[AgendamentoConsulta] Data bloqueada por exceção:",
|
||||
dayException
|
||||
);
|
||||
|
||||
// Se for bloqueio de dia inteiro (start_time e end_time são null), não há horários disponíveis
|
||||
if (!dayException.start_time || !dayException.end_time) {
|
||||
console.log("[AgendamentoConsulta] Dia completamente bloqueado");
|
||||
setAvailableSlots([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Se for bloqueio parcial, remove os horários bloqueados
|
||||
if (dayException.start_time && dayException.end_time) {
|
||||
const [blockStartHour, blockStartMin] = dayException.start_time.split(":").map(Number);
|
||||
const [blockEndHour, blockEndMin] = dayException.end_time.split(":").map(Number);
|
||||
const blockStartMinutes = blockStartHour * 60 + blockStartMin;
|
||||
const blockEndMinutes = blockEndHour * 60 + blockEndMin;
|
||||
|
||||
// Filtra slots que não estão no período bloqueado
|
||||
const slotsAfterBlock = allSlots.filter(slot => {
|
||||
const [slotHour, slotMin] = slot.split(":").map(Number);
|
||||
const slotMinutes = slotHour * 60 + slotMin;
|
||||
return slotMinutes < blockStartMinutes || slotMinutes >= blockEndMinutes;
|
||||
});
|
||||
|
||||
console.log("[AgendamentoConsulta] Slots após remover bloqueio parcial:", slotsAfterBlock);
|
||||
|
||||
// Usa os slots filtrados em vez de todos
|
||||
allSlots.length = 0;
|
||||
allSlots.push(...slotsAfterBlock);
|
||||
}
|
||||
}
|
||||
|
||||
// Busca agendamentos existentes para esta data
|
||||
const appointments = await appointmentService.list({
|
||||
doctor_id: selectedMedico.id,
|
||||
@ -402,7 +407,6 @@ export default function AgendamentoConsulta({
|
||||
setSelectedDate(undefined);
|
||||
setSelectedTime("");
|
||||
setMotivo("");
|
||||
setBookingSuccess(false);
|
||||
setBookingError("");
|
||||
|
||||
// Scroll suave para a seção de detalhes
|
||||
@ -455,7 +459,6 @@ export default function AgendamentoConsulta({
|
||||
setResultType("success");
|
||||
setShowResultModal(true);
|
||||
setShowConfirmDialog(false);
|
||||
setBookingSuccess(true);
|
||||
} catch (error: unknown) {
|
||||
console.error("[AgendamentoConsulta] Erro ao agendar:", error);
|
||||
|
||||
@ -478,7 +481,7 @@ export default function AgendamentoConsulta({
|
||||
{/* Modal de Resultado (Sucesso ou Erro) com Animação */}
|
||||
{showResultModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black bg-opacity-50 animate-fade-in">
|
||||
<div className="bg-white rounded-2xl shadow-2xl p-6 sm:p-8 max-w-md w-full animate-scale-in">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl p-6 sm:p-8 max-w-md w-full animate-scale-in">
|
||||
<div className="flex flex-col items-center text-center space-y-4">
|
||||
{/* Ícone com Animação Giratória (1 volta) */}
|
||||
<div className="relative">
|
||||
@ -520,7 +523,6 @@ export default function AgendamentoConsulta({
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowResultModal(false);
|
||||
setBookingSuccess(false);
|
||||
setBookingError("");
|
||||
navigate("/acompanhamento", {
|
||||
state: { activeTab: "consultas" },
|
||||
@ -543,7 +545,6 @@ export default function AgendamentoConsulta({
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowResultModal(false);
|
||||
setBookingSuccess(false);
|
||||
setBookingError("");
|
||||
// Limpa o formulário se for sucesso
|
||||
if (resultType === "success") {
|
||||
@ -572,10 +573,10 @@ export default function AgendamentoConsulta({
|
||||
Escolha um médico e horário disponível
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg sm:rounded-xl border p-4 sm:p-6 space-y-3 sm:space-y-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg sm:rounded-xl border dark:border-gray-700 p-4 sm:p-6 space-y-3 sm:space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs sm:text-sm font-medium">
|
||||
<label className="text-xs sm:text-sm font-medium dark:text-gray-200">
|
||||
Buscar por nome ou especialidade
|
||||
</label>
|
||||
<div className="relative">
|
||||
@ -596,7 +597,7 @@ export default function AgendamentoConsulta({
|
||||
<select
|
||||
value={selectedSpecialty}
|
||||
onChange={(e) => setSelectedSpecialty(e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg py-2.5 px-3 text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"
|
||||
className="w-full border border-gray-300 dark:border-gray-600 rounded-lg py-2.5 px-3 text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 dark:text-white"
|
||||
>
|
||||
<option value="all">Todas as especialidades</option>
|
||||
{specialties.map((esp) => (
|
||||
@ -612,9 +613,9 @@ export default function AgendamentoConsulta({
|
||||
{filteredMedicos.map((medico) => (
|
||||
<div
|
||||
key={medico.id}
|
||||
className={`bg-white rounded-lg sm:rounded-xl border p-4 sm:p-6 flex flex-col sm:flex-row gap-3 sm:gap-4 items-start sm:items-center ${
|
||||
className={`bg-white dark:bg-gray-800 rounded-lg sm:rounded-xl border dark:border-gray-700 p-4 sm:p-6 flex flex-col sm:flex-row gap-3 sm:gap-4 items-start sm:items-center ${
|
||||
selectedMedico?.id === medico.id
|
||||
? "border-blue-500 bg-blue-50"
|
||||
? "border-blue-500 bg-blue-50 dark:bg-blue-900/30"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
@ -665,13 +666,13 @@ export default function AgendamentoConsulta({
|
||||
{selectedMedico && (
|
||||
<div
|
||||
ref={detailsRef}
|
||||
className="bg-white rounded-lg shadow p-4 sm:p-6 space-y-4 sm:space-y-6"
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow dark:shadow-gray-900/50 p-4 sm:p-6 space-y-4 sm:space-y-6"
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-lg sm:text-xl font-semibold truncate">
|
||||
<h2 className="text-lg sm:text-xl font-semibold truncate dark:text-white">
|
||||
Detalhes do Agendamento
|
||||
</h2>
|
||||
<p className="text-sm sm:text-base text-gray-600 truncate">
|
||||
<p className="text-sm sm:text-base text-gray-600 dark:text-gray-400 truncate">
|
||||
Consulta com {selectedMedico.nome} -{" "}
|
||||
{selectedMedico.especialidade}
|
||||
</p>
|
||||
@ -947,11 +948,11 @@ export default function AgendamentoConsulta({
|
||||
)}
|
||||
{showConfirmDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-lg shadow-xl max-w-md w-full p-4 sm:p-6 space-y-3 sm:space-y-4 max-h-[90vh] overflow-y-auto">
|
||||
<h3 className="text-lg sm:text-xl font-semibold">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full p-4 sm:p-6 space-y-3 sm:space-y-4 max-h-[90vh] overflow-y-auto">
|
||||
<h3 className="text-lg sm:text-xl font-semibold dark:text-white">
|
||||
Confirmar Agendamento
|
||||
</h3>
|
||||
<p className="text-sm sm:text-base text-gray-600">
|
||||
<p className="text-sm sm:text-base text-gray-600 dark:text-gray-400">
|
||||
Revise os detalhes da sua consulta antes de confirmar
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
|
||||
@ -681,9 +681,9 @@ export function SecretaryAppointmentList() {
|
||||
{/* Modal de Criar Consulta */}
|
||||
{showCreateModal && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
{modalMode === "edit" ? "Editar Consulta" : "Nova Consulta"}
|
||||
</h2>
|
||||
</div>
|
||||
@ -841,9 +841,9 @@ export function SecretaryAppointmentList() {
|
||||
{/* Modal de Visualizar Consulta */}
|
||||
{selectedAppointment && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Visualizar Consulta
|
||||
</h2>
|
||||
<button
|
||||
|
||||
@ -539,10 +539,10 @@ export function SecretaryDoctorList({
|
||||
{/* Modal de Formulário */}
|
||||
{showModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{modalMode === "create" ? "Novo Médico" : "Editar Médico"}
|
||||
</h2>
|
||||
<button
|
||||
@ -731,9 +731,9 @@ export function SecretaryDoctorList({
|
||||
{/* Modal de Visualizar Médico */}
|
||||
{showViewModal && selectedDoctor && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Visualizar Médico
|
||||
</h2>
|
||||
<button
|
||||
|
||||
@ -679,14 +679,14 @@ export function SecretaryDoctorSchedule() {
|
||||
</div>
|
||||
|
||||
{/* Current Availability */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Disponibilidade Atual
|
||||
</h3>
|
||||
{loading ? (
|
||||
<p className="text-gray-500">Carregando...</p>
|
||||
<p className="text-gray-500 dark:text-gray-400">Carregando...</p>
|
||||
) : availabilities.length === 0 ? (
|
||||
<p className="text-gray-500">Nenhuma disponibilidade configurada</p>
|
||||
<p className="text-gray-500 dark:text-gray-400">Nenhuma disponibilidade configurada</p>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{availabilities.map((avail) => (
|
||||
@ -729,8 +729,8 @@ export function SecretaryDoctorSchedule() {
|
||||
|
||||
{/* Exceções (Bloqueios e Disponibilidades Extras) */}
|
||||
{exceptions.length > 0 && (
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Exceções Cadastradas
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
@ -805,14 +805,14 @@ export function SecretaryDoctorSchedule() {
|
||||
{/* Availability Dialog */}
|
||||
{showAvailabilityDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Adicionar Disponibilidade
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
|
||||
Dias da Semana
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
@ -903,14 +903,14 @@ export function SecretaryDoctorSchedule() {
|
||||
{/* Exception Dialog */}
|
||||
{showExceptionDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Adicionar Exceção
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
|
||||
Tipo de Exceção
|
||||
</label>
|
||||
<select
|
||||
@ -1030,13 +1030,13 @@ export function SecretaryDoctorSchedule() {
|
||||
{/* Edit Dialog */}
|
||||
{showEditDialog && editingAvailability && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-md w-full mx-4 p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Editar Disponibilidade
|
||||
</h3>
|
||||
|
||||
<div className="mb-4 p-3 bg-blue-50 rounded-lg">
|
||||
<p className="text-sm text-blue-900 font-medium">
|
||||
<div className="mb-4 p-3 bg-blue-50 dark:bg-blue-900/30 rounded-lg">
|
||||
<p className="text-sm text-blue-900 dark:text-blue-200 font-medium">
|
||||
{weekdayToText(editingAvailability.weekday)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -660,10 +660,10 @@ export function SecretaryPatientList({
|
||||
{/* Modal de Formulário */}
|
||||
{showModal && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{modalMode === "create" ? "Novo Paciente" : "Editar Paciente"}
|
||||
</h2>
|
||||
<button
|
||||
@ -699,9 +699,9 @@ export function SecretaryPatientList({
|
||||
{/* Modal de Visualizar Paciente */}
|
||||
{showViewModal && selectedPatient && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold text-gray-900">Visualizar Paciente</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">Visualizar Paciente</h2>
|
||||
<button
|
||||
onClick={() => setShowViewModal(false)}
|
||||
className="p-2 text-gray-400 hover:text-gray-600 rounded-lg transition-colors"
|
||||
@ -743,13 +743,13 @@ export function SecretaryPatientList({
|
||||
{/* Delete Confirmation Dialog */}
|
||||
{showDeleteDialog && patientToDelete && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full p-6">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-md w-full p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-red-100 flex items-center justify-center">
|
||||
<Trash2 className="h-6 w-6 text-red-600" />
|
||||
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<Trash2 className="h-6 w-6 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
|
||||
Confirmar Exclusão
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
|
||||
@ -27,6 +27,7 @@ export function SecretaryReportList() {
|
||||
>({});
|
||||
const [formData, setFormData] = useState({
|
||||
patient_id: "",
|
||||
doctor_id: "",
|
||||
exam: "",
|
||||
diagnosis: "",
|
||||
conclusion: "",
|
||||
@ -39,6 +40,7 @@ export function SecretaryReportList() {
|
||||
loadReports();
|
||||
loadPatients();
|
||||
loadDoctors();
|
||||
loadDoctors();
|
||||
}, []);
|
||||
|
||||
// Recarrega automaticamente quando o filtro de status muda
|
||||
@ -69,6 +71,7 @@ export function SecretaryReportList() {
|
||||
const handleOpenCreateModal = () => {
|
||||
setFormData({
|
||||
patient_id: "",
|
||||
doctor_id: "",
|
||||
exam: "",
|
||||
diagnosis: "",
|
||||
conclusion: "",
|
||||
@ -88,6 +91,7 @@ export function SecretaryReportList() {
|
||||
setSelectedReport(report);
|
||||
setFormData({
|
||||
patient_id: report.patient_id,
|
||||
doctor_id: "",
|
||||
exam: report.exam || "",
|
||||
diagnosis: report.diagnosis || "",
|
||||
conclusion: report.conclusion || "",
|
||||
@ -106,12 +110,30 @@ export function SecretaryReportList() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.doctor_id && !formData.requested_by) {
|
||||
toast.error("Selecione um médico solicitante");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await reportService.create({
|
||||
console.log("[SecretaryReportList] Criando relatório com dados:", {
|
||||
patient_id: formData.patient_id,
|
||||
exam: formData.exam,
|
||||
diagnosis: formData.diagnosis,
|
||||
conclusion: formData.conclusion,
|
||||
cid_code: formData.cid_code,
|
||||
requested_by: formData.requested_by,
|
||||
status: formData.status,
|
||||
});
|
||||
|
||||
await reportService.create({
|
||||
patient_id: formData.patient_id,
|
||||
exam: formData.exam || undefined,
|
||||
diagnosis: formData.diagnosis || undefined,
|
||||
conclusion: formData.conclusion || undefined,
|
||||
cid_code: formData.cid_code || undefined,
|
||||
requested_by: formData.requested_by || undefined,
|
||||
status: formData.status,
|
||||
});
|
||||
|
||||
toast.success("Relatório criado com sucesso!");
|
||||
@ -441,7 +463,7 @@ export function SecretaryReportList() {
|
||||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6 space-y-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex-1 relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
|
||||
@ -486,11 +508,11 @@ export function SecretaryReportList() {
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 border-b border-gray-200">
|
||||
<thead className="bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600">
|
||||
<tr>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 dark:text-gray-200 uppercase tracking-wider">
|
||||
Relatório
|
||||
</th>
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-gray-700 uppercase tracking-wider">
|
||||
@ -620,9 +642,9 @@ export function SecretaryReportList() {
|
||||
{/* Modal de Criar Relatório */}
|
||||
{showCreateModal && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Novo Relatório
|
||||
</h2>
|
||||
</div>
|
||||
@ -667,19 +689,25 @@ export function SecretaryReportList() {
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Solicitado por
|
||||
Médico Solicitante *
|
||||
</label>
|
||||
<select
|
||||
value={formData.requested_by}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, requested_by: e.target.value })
|
||||
}
|
||||
value={formData.doctor_id}
|
||||
onChange={(e) => {
|
||||
const selectedDoctor = doctors.find(d => d.id === e.target.value);
|
||||
setFormData({
|
||||
...formData,
|
||||
doctor_id: e.target.value,
|
||||
requested_by: selectedDoctor ? `Dr. ${selectedDoctor.full_name}` : ""
|
||||
});
|
||||
}}
|
||||
className="form-input"
|
||||
required
|
||||
>
|
||||
<option value="">Selecione um médico</option>
|
||||
{doctors.map((doctor) => (
|
||||
<option key={doctor.id} value={doctor.id}>
|
||||
{doctor.full_name}
|
||||
Dr. {doctor.full_name} - {doctor.specialty}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@ -737,9 +765,9 @@ export function SecretaryReportList() {
|
||||
{/* Modal de Visualizar Relatório */}
|
||||
{showViewModal && selectedReport && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Visualizar Relatório
|
||||
</h2>
|
||||
<button
|
||||
@ -876,9 +904,9 @@ export function SecretaryReportList() {
|
||||
{/* Modal de Editar Relatório */}
|
||||
{showEditModal && selectedReport && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Editar Relatório
|
||||
</h2>
|
||||
<button
|
||||
|
||||
@ -1023,7 +1023,7 @@ const AcompanhamentoPaciente: React.FC = () => {
|
||||
Exame
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Diagnóstico
|
||||
Médico Solicitante
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Status
|
||||
@ -1049,7 +1049,9 @@ const AcompanhamentoPaciente: React.FC = () => {
|
||||
{laudo.exam || "-"}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600 dark:text-gray-300">
|
||||
{laudo.diagnosis || "-"}
|
||||
{laudo.requested_by
|
||||
? (requestedByNames[laudo.requested_by] || laudo.requested_by)
|
||||
: "-"}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
|
||||
@ -36,13 +36,13 @@ export default function PainelSecretaria() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
{/* Header */}
|
||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-10">
|
||||
<header className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 sticky top-0 z-10">
|
||||
<div className="max-w-[1400px] mx-auto px-4 sm:px-6 py-3 sm:py-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="min-w-0 flex-1">
|
||||
<h1 className="text-lg sm:text-xl lg:text-2xl font-bold text-gray-900 truncate">
|
||||
<h1 className="text-lg sm:text-xl lg:text-2xl font-bold text-gray-900 dark:text-white truncate">
|
||||
Painel da Secretaria
|
||||
</h1>
|
||||
{user && (
|
||||
@ -63,7 +63,7 @@ export default function PainelSecretaria() {
|
||||
</header>
|
||||
|
||||
{/* Tabs Navigation */}
|
||||
<div className="bg-white border-b border-gray-200 overflow-x-auto">
|
||||
<div className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 overflow-x-auto">
|
||||
<div className="max-w-[1400px] mx-auto px-4 sm:px-6">
|
||||
<nav className="flex gap-1 sm:gap-2 min-w-max">
|
||||
{tabs.map((tab) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user