fix: relatorios
This commit is contained in:
parent
5a60e9a233
commit
9b6fa7ff36
@ -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">
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user