Ajuste das exceções

This commit is contained in:
Eduarda-SS 2025-11-26 17:55:47 -03:00
parent f7cb04a68f
commit 461bf3b413
5 changed files with 171 additions and 101 deletions

View File

@ -4,11 +4,6 @@
"icon": "calendar",
"url": "/medico/agendamento"
},
{
"name": "Exceções de Disponibilidade",
"icon": "calendar-x-fill",
"url": "/medico/excecoes-disponibilidade"
},
{
"name": "Relatórios",

View File

@ -377,8 +377,16 @@ const DeleteModal = () => (
>
<i className="bi bi-plus-circle"></i> Adicionar Consulta
</button>
<button className="manage-button btn" onClick={() => navigate("/secretaria/excecoes-disponibilidade")}><i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções</button>
<button className='manage-button btn' onClick={() => navigate('/secretaria/disponibilidade')}><i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade</button>
<button
className="btn btn-primary"
onClick={() => navigate("/medico/excecoes-disponibilidade")}>
<i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções de Disponibilidade
</button>
<button
className='btn btn-primary'
onClick={() => navigate('/secretaria/disponibilidade')}>
<i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade
</button>
</div>
{!PageNovaConsulta ? (
<div className='atendimento-eprocura'>

View File

@ -2,6 +2,10 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
import { useAuth } from '../../_assets/utils/AuthProvider'
import FormExcecaoDisponibilidade from '../../components/medico/FormExcecaoDisponibilidade';
import '../../_assets/css/components/agendamento/FormAgendamento.css';
import '../../_assets/css/pages/agendamento/Agendamento.css';
import '../../_assets/css/pages/agendamento/FilaEspera.css';
import dayjs from 'dayjs';
import weekday from 'dayjs/plugin/weekday';
import 'dayjs/locale/pt-br';
@ -19,9 +23,12 @@ const getDateRange = (date, view) => {
toDate = base.endOf('day').format('YYYY-MM-DD');
titleRange = base.format('DD/MM/YYYY');
} else if (view === 'semanal') {
fromDate = base.startOf('week').format('YYYY-MM-DD');
toDate = base.endOf('week').format('YYYY-MM-DD');
titleRange = `${base.startOf('week').format('DD/MM')} - ${base.endOf('week').format('DD/MM')}`;
let weekStart = base.startOf('week');
if (weekStart.day() !== 1) weekStart = base.weekday(1);
const weekEnd = weekStart.add(6, 'day');
fromDate = weekStart.format('YYYY-MM-DD');
toDate = weekEnd.format('YYYY-MM-DD');
titleRange = `Semana de ${weekStart.format('DD/MM')} a ${weekEnd.format('DD/MM')}`;
} else { // mensal
fromDate = base.startOf('month').format('YYYY-MM-DD');
toDate = base.endOf('month').format('YYYY-MM-DD');
@ -40,7 +47,7 @@ const ExcecoesDisponibilidadeDoctor = () => {
const [erro, setErro] = useState('');
const [buscaTexto, setBuscaTexto] = useState('');
const doctorID = user?.doctor_id || user?.id; // ajuste conforme estrutura real
const doctorID = user?.doctor_id || user?.id || '';
const { fromDate, toDate, titleRange } = useMemo(
() => getDateRange(dataFiltro, visualizacao),
@ -52,7 +59,6 @@ const ExcecoesDisponibilidadeDoctor = () => {
setLoading(true);
setErro('');
try {
// trata getAuthorizationHeader() que pode retornar objeto ou string
const maybeAuth = getAuthorizationHeader();
const headers = {};
if (typeof maybeAuth === 'string') {
@ -130,118 +136,161 @@ const ExcecoesDisponibilidadeDoctor = () => {
setDataFiltro(nova.format('YYYY-MM-DD'));
};
const handleCancelForm = (recarregar = false) => {
setMostrarForm(false);
if (recarregar) fetchExcecoes();
};
if (mostrarForm) {
return (
<div className="container mt-3">
<h4>Nova Exceção</h4>
<FormExcecaoDisponibilidade
doctorID={doctorID}
onCancel={(reload) => {
setMostrarForm(false);
if (reload) fetchExcecoes();
}}
/>
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h1>Gerenciar Exceções de Disponibilidade</h1>
</div>
<div className="container mt-3">
<FormExcecaoDisponibilidade
doctorID={doctorID}
onCancel={(reload) => handleCancelForm(reload)}
/>
</div>
</div>
);
}
return (
<div className="container mt-3">
<div className="d-flex justify-content-between align-items-center mb-3">
<h4>Exceções de Disponibilidade</h4>
<button className="btn btn-primary" onClick={() => setMostrarForm(true)}>
Criar Exceção
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h1>Gerenciar Exceções de Disponibilidade</h1>
<button
className="btn-primary"
onClick={() => setMostrarForm(true)}
style={{ padding: '10px 20px', fontSize: '14px', whiteSpace: 'nowrap' }}
>
+ Criar Nova Exceção
</button>
</div>
<div className="d-flex gap-2 flex-wrap mb-3">
<div className="btn-group">
<div className='atendimento-eprocura'>
<div className='busca-atendimento'>
<div>
<i className="fa-solid fa-user-doctor"></i>
<input
type="text"
placeholder="ID do Médico"
value={doctorID}
readOnly
/>
</div>
<div>
<i className="fa-solid fa-calendar"></i>
<input
type="date"
value={dataFiltro}
onChange={(e) => setDataFiltro(e.target.value)}
/>
</div>
</div>
<div className='container-btns-agenda-fila_esepera' style={{ marginTop: 8 }}>
<button
className={`btn btn-sm ${visualizacao === 'diario' ? 'btn-secondary' : 'btn-outline-secondary'}`}
className={`btn-agenda ${visualizacao === "diario" ? "opc-agenda-ativo" : ""}`}
onClick={() => setVisualizacao('diario')}
>
Diário
Dia
</button>
<button
className={`btn btn-sm ${visualizacao === 'semanal' ? 'btn-secondary' : 'btn-outline-secondary'}`}
className={`btn-fila-espera ${visualizacao === "semanal" ? "opc-filaespera-ativo" : ""}`}
onClick={() => setVisualizacao('semanal')}
>
Semanal
Semana
</button>
<button
className={`btn btn-sm ${visualizacao === 'mensal' ? 'btn-secondary' : 'btn-outline-secondary'}`}
className={`btn-fila-espera ${visualizacao === "mensal" ? "opc-filaespera-ativo" : ""}`}
onClick={() => setVisualizacao('mensal')}
>
Mensal
Mês
</button>
</div>
<div className="d-flex align-items-center gap-2">
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(-1)}>&lt;</button>
<strong>{titleRange}</strong>
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(1)}>&gt;</button>
</div>
<section className='calendario-ou-filaespera' style={{ marginTop: 12 }}>
<div className="fila-container">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2 className="fila-titulo">Exceções em {titleRange} ({excecoes.length})</h2>
<input
type="date"
className="form-control form-control-sm"
value={dataFiltro}
onChange={e => setDataFiltro(e.target.value)}
style={{ maxWidth: 160 }}
/>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(-1)}>&lt;</button>
<strong>{titleRange}</strong>
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(1)}>&gt;</button>
</div>
</div>
<input
type="text"
placeholder="Buscar (motivo / tipo)"
className="form-control form-control-sm"
value={buscaTexto}
onChange={e => setBuscaTexto(e.target.value)}
style={{ flex: 1, minWidth: 180 }}
/>
<div style={{ marginTop: 8, marginBottom: 8, display: 'flex', gap: 8, alignItems: 'center' }}>
<input
type="text"
placeholder="Buscar (motivo / tipo)"
className="form-control form-control-sm"
value={buscaTexto}
onChange={e => setBuscaTexto(e.target.value)}
style={{ flex: 1, minWidth: 180 }}
/>
</div>
{erro && <div className="alert alert-danger py-1">{erro}</div>}
{loading ? (
<p>Carregando exceções...</p>
) : excecoesFiltradas.length === 0 ? (
<p>Nenhuma exceção encontrada para os filtros aplicados.</p>
) : (
<table className="fila-tabela">
<thead>
<tr>
<th>Médico (Nome)</th>
<th>Data</th>
<th>Início</th>
<th>Término</th>
<th>Tipo</th>
<th>Motivo</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{excecoesFiltradas.map((ex) => (
<tr key={ex.id}>
<td><p>{ex.doctor_name || ex.doctor_id || doctorID}</p></td>
<td>{ex.date ? dayjs(ex.date).format('DD/MM/YYYY') : '-'}</td>
<td>{ex.start_time ? (dayjs(ex.start_time, 'HH:mm:ss').isValid() ? dayjs(ex.start_time, 'HH:mm:ss').format('HH:mm') : ex.start_time) : (ex.inicio || '-')}</td>
<td>{ex.end_time ? (dayjs(ex.end_time, 'HH:mm:ss').isValid() ? dayjs(ex.end_time, 'HH:mm:ss').format('HH:mm') : ex.end_time) : (ex.termino || '-')}</td>
<td>{ex.kind || ex.tipoAtendimento || '-'}</td>
<td><p>{ex.reason || ex.motivo || '-'}</p></td>
<td>
<div className="d-flex gap-2">
<button
className="btn btn-sm btn-edit"
onClick={() => {
// ao editar, mostra o formulário. FormExcecaoDisponibilidade precisa lidar com edição via props
setMostrarForm(true);
// Form pode receber os dados via estado global/URL ou ser ajustado para receber o registro atual
} }
>
<i className="bi bi-pencil me-1"></i> Editar
</button>
<button
className="btn btn-sm btn-delete"
onClick={() => handleDelete(ex.id)}
>
<i className="bi bi-trash me-1"></i> Excluir
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
</div>
{erro && <div className="alert alert-danger py-1">{erro}</div>}
{loading && <div>Carregando...</div>}
{!loading && excecoesFiltradas.length === 0 && (
<div className="alert alert-info py-1">Nenhuma exceção neste intervalo.</div>
)}
{!loading && excecoesFiltradas.length > 0 && (
<div className="table-responsive">
<table className="table table-sm table-striped">
<thead>
<tr>
<th>Data</th>
<th>Início</th>
<th>Término</th>
<th>Tipo</th>
<th>Motivo</th>
<th></th>
</tr>
</thead>
<tbody>
{excecoesFiltradas.map(ex => (
<tr key={ex.id}>
<td>{ex.date ? dayjs(ex.date).format('DD/MM/YYYY') : '-'}</td>
<td>{ex.start_time || ex.inicio || '-'}</td>
<td>{ex.end_time || ex.termino || '-'}</td>
<td>{ex.kind || ex.tipoAtendimento || '-'}</td>
<td>{ex.reason || ex.motivo || '-'}</td>
<td>
<button
className="btn btn-outline-danger btn-sm"
onClick={() => handleDelete(ex.id)}
>
Remover
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
};

View File

@ -199,7 +199,17 @@ const ExcecoesDisponibilidade = () => {
alert('Erro ao excluir exceção.');
}
}
// navegação de datas (adicionada, mesma lógica da versão do médico)
const mudarData = (delta) => {
const base = dayjs(filtroData);
let nova;
if (visualizacao === 'diario') nova = base.add(delta, 'day');
else if (visualizacao === 'semanal') nova = base.add(delta, 'week');
else nova = base.add(delta, 'month');
setFiltroData(nova.format('YYYY-MM-DD'));
}
const handleCancelForm = (recarregar = false) => {
setPageNovaExcecao(false);
if (recarregar) {
@ -273,7 +283,15 @@ const ExcecoesDisponibilidade = () => {
{/* Tabela de Exceções (Título usa o titleRange calculado) */}
<section className='calendario-ou-filaespera'>
<div className="fila-container">
<h2 className="fila-titulo">Exceções em {titleRange} ({excecoes.length})</h2>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2 className="fila-titulo">Exceções em {titleRange} ({excecoes.length})</h2>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(-1)}>&lt;</button>
<strong>{titleRange}</strong>
<button className="btn btn-outline-dark btn-sm" onClick={() => mudarData(1)}>&gt;</button>
</div>
</div>
{loading ? (
<p>Carregando exceções...</p>
) : excecoes.length === 0 ? (

View File

@ -408,13 +408,13 @@ const Agendamento = ({ setDictInfo }) => {
<i className="bi bi-plus-circle"></i> Adicionar Consulta
</button>
<button
className="manage-button btn"
className="btn btn-primary"
onClick={() => navigate("/secretaria/excecoes-disponibilidade")}
>
<i className="bi bi-gear-fill me-1"></i> Gerenciar Exceções
</button>
<button
className='manage-button btn'
className='btn btn-primary'
onClick={() => navigate('/secretaria/disponibilidade')}
>
<i className="bi bi-gear-fill me-1"></i> Mudar Disponibilidade