fix: relatorios

This commit is contained in:
Pedro Araujo da Silveira 2025-11-05 21:23:46 -03:00
parent 5a60e9a233
commit 9b6fa7ff36
8 changed files with 157 additions and 126 deletions

View File

@ -65,7 +65,6 @@ export default function AgendamentoConsulta({
>("presencial"); >("presencial");
const [motivo, setMotivo] = useState(""); const [motivo, setMotivo] = useState("");
const [showConfirmDialog, setShowConfirmDialog] = useState(false); const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [bookingSuccess, setBookingSuccess] = useState(false);
const [bookingError, setBookingError] = useState(""); const [bookingError, setBookingError] = useState("");
const [showResultModal, setShowResultModal] = useState(false); const [showResultModal, setShowResultModal] = useState(false);
const [resultType, setResultType] = useState<"success" | "error">("success"); const [resultType, setResultType] = useState<"success" | "error">("success");
@ -133,40 +132,9 @@ export default function AgendamentoConsulta({
return; return;
} }
// Mapeamento de string para número (formato da API) // Mapeia os dias da semana que o médico atende (weekday é sempre número 0-6)
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)
const availableWeekdays = new Set<number>( const availableWeekdays = new Set<number>(
availabilities availabilities.map((avail) => avail.weekday)
.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
); );
console.log( console.log(
@ -249,18 +217,8 @@ export default function AgendamentoConsulta({
return; return;
} }
// Pega o dia da semana da data selecionada // Pega o dia da semana da data selecionada (0-6)
const weekdayMap: Record<number, string> = { const dayOfWeek = selectedDate.getDay() as 0 | 1 | 2 | 3 | 4 | 5 | 6;
0: "sunday",
1: "monday",
2: "tuesday",
3: "wednesday",
4: "thursday",
5: "friday",
6: "saturday",
};
const dayOfWeek = weekdayMap[selectedDate.getDay()];
console.log( console.log(
"[AgendamentoConsulta] Dia da semana selecionado:", "[AgendamentoConsulta] Dia da semana selecionado:",
dayOfWeek 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 // Busca agendamentos existentes para esta data
const appointments = await appointmentService.list({ const appointments = await appointmentService.list({
doctor_id: selectedMedico.id, doctor_id: selectedMedico.id,
@ -402,7 +407,6 @@ export default function AgendamentoConsulta({
setSelectedDate(undefined); setSelectedDate(undefined);
setSelectedTime(""); setSelectedTime("");
setMotivo(""); setMotivo("");
setBookingSuccess(false);
setBookingError(""); setBookingError("");
// Scroll suave para a seção de detalhes // Scroll suave para a seção de detalhes
@ -455,7 +459,6 @@ export default function AgendamentoConsulta({
setResultType("success"); setResultType("success");
setShowResultModal(true); setShowResultModal(true);
setShowConfirmDialog(false); setShowConfirmDialog(false);
setBookingSuccess(true);
} catch (error: unknown) { } catch (error: unknown) {
console.error("[AgendamentoConsulta] Erro ao agendar:", error); console.error("[AgendamentoConsulta] Erro ao agendar:", error);
@ -478,7 +481,7 @@ export default function AgendamentoConsulta({
{/* Modal de Resultado (Sucesso ou Erro) com Animação */} {/* Modal de Resultado (Sucesso ou Erro) com Animação */}
{showResultModal && ( {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="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"> <div className="flex flex-col items-center text-center space-y-4">
{/* Ícone com Animação Giratória (1 volta) */} {/* Ícone com Animação Giratória (1 volta) */}
<div className="relative"> <div className="relative">
@ -520,7 +523,6 @@ export default function AgendamentoConsulta({
<button <button
onClick={() => { onClick={() => {
setShowResultModal(false); setShowResultModal(false);
setBookingSuccess(false);
setBookingError(""); setBookingError("");
navigate("/acompanhamento", { navigate("/acompanhamento", {
state: { activeTab: "consultas" }, state: { activeTab: "consultas" },
@ -543,7 +545,6 @@ export default function AgendamentoConsulta({
<button <button
onClick={() => { onClick={() => {
setShowResultModal(false); setShowResultModal(false);
setBookingSuccess(false);
setBookingError(""); setBookingError("");
// Limpa o formulário se for sucesso // Limpa o formulário se for sucesso
if (resultType === "success") { if (resultType === "success") {
@ -572,10 +573,10 @@ export default function AgendamentoConsulta({
Escolha um médico e horário disponível Escolha um médico e horário disponível
</p> </p>
</div> </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="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2"> <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 Buscar por nome ou especialidade
</label> </label>
<div className="relative"> <div className="relative">
@ -596,7 +597,7 @@ export default function AgendamentoConsulta({
<select <select
value={selectedSpecialty} value={selectedSpecialty}
onChange={(e) => setSelectedSpecialty(e.target.value)} 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> <option value="all">Todas as especialidades</option>
{specialties.map((esp) => ( {specialties.map((esp) => (
@ -612,9 +613,9 @@ export default function AgendamentoConsulta({
{filteredMedicos.map((medico) => ( {filteredMedicos.map((medico) => (
<div <div
key={medico.id} 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 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 && ( {selectedMedico && (
<div <div
ref={detailsRef} 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> <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 Detalhes do Agendamento
</h2> </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} -{" "} Consulta com {selectedMedico.nome} -{" "}
{selectedMedico.especialidade} {selectedMedico.especialidade}
</p> </p>
@ -947,11 +948,11 @@ export default function AgendamentoConsulta({
)} )}
{showConfirmDialog && ( {showConfirmDialog && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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"> <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"> <h3 className="text-lg sm:text-xl font-semibold dark:text-white">
Confirmar Agendamento Confirmar Agendamento
</h3> </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 Revise os detalhes da sua consulta antes de confirmar
</p> </p>
<div className="space-y-3"> <div className="space-y-3">

View File

@ -681,9 +681,9 @@ export function SecretaryAppointmentList() {
{/* Modal de Criar Consulta */} {/* Modal de Criar Consulta */}
{showCreateModal && ( {showCreateModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <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="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"> <div className="p-6 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white">
{modalMode === "edit" ? "Editar Consulta" : "Nova Consulta"} {modalMode === "edit" ? "Editar Consulta" : "Nova Consulta"}
</h2> </h2>
</div> </div>
@ -841,9 +841,9 @@ export function SecretaryAppointmentList() {
{/* Modal de Visualizar Consulta */} {/* Modal de Visualizar Consulta */}
{selectedAppointment && ( {selectedAppointment && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <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="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 flex items-center justify-between"> <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"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white">
Visualizar Consulta Visualizar Consulta
</h2> </h2>
<button <button

View File

@ -539,10 +539,10 @@ export function SecretaryDoctorList({
{/* Modal de Formulário */} {/* Modal de Formulário */}
{showModal && ( {showModal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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 */} {/* Header */}
<div className="flex items-center justify-between p-6 border-b border-gray-200"> <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"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white">
{modalMode === "create" ? "Novo Médico" : "Editar Médico"} {modalMode === "create" ? "Novo Médico" : "Editar Médico"}
</h2> </h2>
<button <button
@ -731,9 +731,9 @@ export function SecretaryDoctorList({
{/* Modal de Visualizar Médico */} {/* Modal de Visualizar Médico */}
{showViewModal && selectedDoctor && ( {showViewModal && selectedDoctor && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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">
<div className="flex items-center justify-between p-6 border-b border-gray-200"> <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"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white">
Visualizar Médico Visualizar Médico
</h2> </h2>
<button <button

View File

@ -679,14 +679,14 @@ export function SecretaryDoctorSchedule() {
</div> </div>
{/* Current Availability */} {/* Current Availability */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6"> <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 mb-4"> <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Disponibilidade Atual Disponibilidade Atual
</h3> </h3>
{loading ? ( {loading ? (
<p className="text-gray-500">Carregando...</p> <p className="text-gray-500 dark:text-gray-400">Carregando...</p>
) : availabilities.length === 0 ? ( ) : 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"> <div className="space-y-3">
{availabilities.map((avail) => ( {availabilities.map((avail) => (
@ -729,8 +729,8 @@ export function SecretaryDoctorSchedule() {
{/* Exceções (Bloqueios e Disponibilidades Extras) */} {/* Exceções (Bloqueios e Disponibilidades Extras) */}
{exceptions.length > 0 && ( {exceptions.length > 0 && (
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6"> <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 mb-4"> <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Exceções Cadastradas Exceções Cadastradas
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
@ -805,14 +805,14 @@ export function SecretaryDoctorSchedule() {
{/* Availability Dialog */} {/* Availability Dialog */}
{showAvailabilityDialog && ( {showAvailabilityDialog && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> <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"> <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 mb-4"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
Adicionar Disponibilidade Adicionar Disponibilidade
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<div> <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 Dias da Semana
</label> </label>
<div className="space-y-2"> <div className="space-y-2">
@ -903,14 +903,14 @@ export function SecretaryDoctorSchedule() {
{/* Exception Dialog */} {/* Exception Dialog */}
{showExceptionDialog && ( {showExceptionDialog && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> <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"> <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 mb-4"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
Adicionar Exceção Adicionar Exceção
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<div> <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 Tipo de Exceção
</label> </label>
<select <select
@ -1030,13 +1030,13 @@ export function SecretaryDoctorSchedule() {
{/* Edit Dialog */} {/* Edit Dialog */}
{showEditDialog && editingAvailability && ( {showEditDialog && editingAvailability && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> <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"> <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 mb-4"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
Editar Disponibilidade Editar Disponibilidade
</h3> </h3>
<div className="mb-4 p-3 bg-blue-50 rounded-lg"> <div className="mb-4 p-3 bg-blue-50 dark:bg-blue-900/30 rounded-lg">
<p className="text-sm text-blue-900 font-medium"> <p className="text-sm text-blue-900 dark:text-blue-200 font-medium">
{weekdayToText(editingAvailability.weekday)} {weekdayToText(editingAvailability.weekday)}
</p> </p>
</div> </div>

View File

@ -660,10 +660,10 @@ export function SecretaryPatientList({
{/* Modal de Formulário */} {/* Modal de Formulário */}
{showModal && ( {showModal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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 */} {/* Header */}
<div className="flex items-center justify-between p-6 border-b border-gray-200"> <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"> <h2 className="text-xl font-semibold text-gray-900 dark:text-white">
{modalMode === "create" ? "Novo Paciente" : "Editar Paciente"} {modalMode === "create" ? "Novo Paciente" : "Editar Paciente"}
</h2> </h2>
<button <button
@ -699,9 +699,9 @@ export function SecretaryPatientList({
{/* Modal de Visualizar Paciente */} {/* Modal de Visualizar Paciente */}
{showViewModal && selectedPatient && ( {showViewModal && selectedPatient && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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="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 flex items-center justify-between"> <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">Visualizar Paciente</h2> <h2 className="text-xl font-semibold text-gray-900 dark:text-white">Visualizar Paciente</h2>
<button <button
onClick={() => setShowViewModal(false)} onClick={() => setShowViewModal(false)}
className="p-2 text-gray-400 hover:text-gray-600 rounded-lg transition-colors" className="p-2 text-gray-400 hover:text-gray-600 rounded-lg transition-colors"
@ -743,13 +743,13 @@ export function SecretaryPatientList({
{/* Delete Confirmation Dialog */} {/* Delete Confirmation Dialog */}
{showDeleteDialog && patientToDelete && ( {showDeleteDialog && patientToDelete && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <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 items-start gap-4">
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-red-100 flex items-center justify-center"> <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" /> <Trash2 className="h-6 w-6 text-red-600 dark:text-red-400" />
</div> </div>
<div className="flex-1"> <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 Confirmar Exclusão
</h3> </h3>
<p className="text-sm text-gray-600 mb-4"> <p className="text-sm text-gray-600 mb-4">

View File

@ -27,6 +27,7 @@ export function SecretaryReportList() {
>({}); >({});
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
patient_id: "", patient_id: "",
doctor_id: "",
exam: "", exam: "",
diagnosis: "", diagnosis: "",
conclusion: "", conclusion: "",
@ -39,6 +40,7 @@ export function SecretaryReportList() {
loadReports(); loadReports();
loadPatients(); loadPatients();
loadDoctors(); loadDoctors();
loadDoctors();
}, []); }, []);
// Recarrega automaticamente quando o filtro de status muda // Recarrega automaticamente quando o filtro de status muda
@ -69,6 +71,7 @@ export function SecretaryReportList() {
const handleOpenCreateModal = () => { const handleOpenCreateModal = () => {
setFormData({ setFormData({
patient_id: "", patient_id: "",
doctor_id: "",
exam: "", exam: "",
diagnosis: "", diagnosis: "",
conclusion: "", conclusion: "",
@ -88,6 +91,7 @@ export function SecretaryReportList() {
setSelectedReport(report); setSelectedReport(report);
setFormData({ setFormData({
patient_id: report.patient_id, patient_id: report.patient_id,
doctor_id: "",
exam: report.exam || "", exam: report.exam || "",
diagnosis: report.diagnosis || "", diagnosis: report.diagnosis || "",
conclusion: report.conclusion || "", conclusion: report.conclusion || "",
@ -106,12 +110,30 @@ export function SecretaryReportList() {
return; return;
} }
if (!formData.doctor_id && !formData.requested_by) {
toast.error("Selecione um médico solicitante");
return;
}
try { try {
await reportService.create({ console.log("[SecretaryReportList] Criando relatório com dados:", {
patient_id: formData.patient_id, patient_id: formData.patient_id,
exam: formData.exam, exam: formData.exam,
diagnosis: formData.diagnosis, diagnosis: formData.diagnosis,
conclusion: formData.conclusion, 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!"); toast.success("Relatório criado com sucesso!");
@ -441,7 +463,7 @@ export function SecretaryReportList() {
</div> </div>
{/* Search and Filters */} {/* 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 gap-3">
<div className="flex-1 relative"> <div className="flex-1 relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" /> <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> </div>
{/* Table */} {/* 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"> <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> <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 Relatório
</th> </th>
<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 uppercase tracking-wider">
@ -620,9 +642,9 @@ export function SecretaryReportList() {
{/* Modal de Criar Relatório */} {/* Modal de Criar Relatório */}
{showCreateModal && ( {showCreateModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <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="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"> <div className="p-6 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-2xl font-bold text-gray-900"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white">
Novo Relatório Novo Relatório
</h2> </h2>
</div> </div>
@ -667,19 +689,25 @@ export function SecretaryReportList() {
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Solicitado por Médico Solicitante *
</label> </label>
<select <select
value={formData.requested_by} value={formData.doctor_id}
onChange={(e) => onChange={(e) => {
setFormData({ ...formData, requested_by: e.target.value }) 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" className="form-input"
required
> >
<option value="">Selecione um médico</option> <option value="">Selecione um médico</option>
{doctors.map((doctor) => ( {doctors.map((doctor) => (
<option key={doctor.id} value={doctor.id}> <option key={doctor.id} value={doctor.id}>
{doctor.full_name} Dr. {doctor.full_name} - {doctor.specialty}
</option> </option>
))} ))}
</select> </select>
@ -737,9 +765,9 @@ export function SecretaryReportList() {
{/* Modal de Visualizar Relatório */} {/* Modal de Visualizar Relatório */}
{showViewModal && selectedReport && ( {showViewModal && selectedReport && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <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="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 flex items-center justify-between"> <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"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white">
Visualizar Relatório Visualizar Relatório
</h2> </h2>
<button <button
@ -876,9 +904,9 @@ export function SecretaryReportList() {
{/* Modal de Editar Relatório */} {/* Modal de Editar Relatório */}
{showEditModal && selectedReport && ( {showEditModal && selectedReport && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <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="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 flex items-center justify-between"> <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"> <h2 className="text-2xl font-bold text-gray-900 dark:text-white">
Editar Relatório Editar Relatório
</h2> </h2>
<button <button

View File

@ -1023,7 +1023,7 @@ const AcompanhamentoPaciente: React.FC = () => {
Exame Exame
</th> </th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"> <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>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status Status
@ -1049,7 +1049,9 @@ const AcompanhamentoPaciente: React.FC = () => {
{laudo.exam || "-"} {laudo.exam || "-"}
</td> </td>
<td className="px-6 py-4 text-sm text-gray-600 dark:text-gray-300"> <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>
<td className="px-6 py-4 whitespace-nowrap"> <td className="px-6 py-4 whitespace-nowrap">
<span <span

View File

@ -36,13 +36,13 @@ export default function PainelSecretaria() {
]; ];
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50 dark:bg-gray-900">
{/* Header */} {/* 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="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="flex items-center justify-between gap-4">
<div className="min-w-0 flex-1"> <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 Painel da Secretaria
</h1> </h1>
{user && ( {user && (
@ -63,7 +63,7 @@ export default function PainelSecretaria() {
</header> </header>
{/* Tabs Navigation */} {/* 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"> <div className="max-w-[1400px] mx-auto px-4 sm:px-6">
<nav className="flex gap-1 sm:gap-2 min-w-max"> <nav className="flex gap-1 sm:gap-2 min-w-max">
{tabs.map((tab) => { {tabs.map((tab) => {