diff --git a/src/PagesPaciente/ConsultasPaciente.jsx b/src/PagesPaciente/ConsultasPaciente.jsx index 993b2d3b..867029d2 100644 --- a/src/PagesPaciente/ConsultasPaciente.jsx +++ b/src/PagesPaciente/ConsultasPaciente.jsx @@ -35,6 +35,12 @@ const ConsultasPaciente = ({ setDictInfo }) => { const [showConfirmModal, setShowConfirmModal] = useState(false) + const [waitlistSearch, setWaitlistSearch] = useState(''); + const [waitSortKey, setWaitSortKey] = useState(null); // 'paciente' | 'medico' | 'data' | null + const [waitSortDir, setWaitSortDir] = useState('asc'); // 'asc' | 'desc' + const [waitPage, setWaitPage] = useState(1); + const [waitPerPage, setWaitPerPage] = useState(10); + useEffect(() => { @@ -188,11 +194,60 @@ const confirmConsulta = (selectedPatientId) => { } } + + const filaEsperaFiltrada = useMemo(() => { + if (!waitlistSearch.trim()) return filaDeEspera; + const term = waitlistSearch.toLowerCase(); + return filaDeEspera.filter(item => { + const paciente = item?.paciente_nome?.toLowerCase() || ''; + const medico = item?.medico_nome?.toLowerCase() || ''; + return paciente.includes(term) || medico.includes(term); + }); + }, [waitlistSearch, filaDeEspera]); + + + const applySortingWaitlist = (arr) => { + if (!Array.isArray(arr) || !waitSortKey) return arr; + const copy = [...arr]; + if (waitSortKey === 'paciente') { + copy.sort((a, b) => (a?.paciente_nome || '').localeCompare((b?.paciente_nome || ''), undefined, { sensitivity: 'base' })); + } else if (waitSortKey === 'medico') { + copy.sort((a, b) => (a?.medico_nome || '').localeCompare((b?.medico_nome || ''), undefined, { sensitivity: 'base' })); + } else if (waitSortKey === 'data') { + copy.sort((a, b) => new Date(a?.created_at || 0) - new Date(b?.created_at || 0)); + } + if (waitSortDir === 'desc') copy.reverse(); + return copy; + }; + + const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); + + + const waitTotalPages = Math.ceil(filaEsperaFiltrada.length / waitPerPage) || 1; + const waitIndiceInicial = (waitPage - 1) * waitPerPage; + const waitIndiceFinal = waitIndiceInicial + waitPerPage; + const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); + + const gerarNumerosWaitPages = () => { + const paginas = []; + const paginasParaMostrar = 5; + let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); + let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); + inicio = Math.max(1, fim - paginasParaMostrar + 1); + for (let i = inicio; i <= fim; i++) { + paginas.push(i); + } + return paginas; + }; + + useEffect(() => { + setWaitPage(1); + }, [waitlistSearch, waitSortKey, waitSortDir]); + return (

Gerencie suas consultas

- {/*Adicionei esse className para poder ter o fundo branco presente no style, mesmo não sendo para um form */}
@@ -209,50 +264,173 @@ const confirmConsulta = (selectedPatientId) => { {viewFila ? -
-
- setSearchTerm(e.target.value)} - /> -

Fila de Espera

-
- - - - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - {/* Ajustado o cabeçalho */} - - - - - {filaDeEspera.map((item, index) => ( - - - - - - - - ))} - -
Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções

{item?.paciente_nome}

{item?.paciente_cpf}

{item?.medico_nome}

{dayjs(item?.created_at).format('DD/MM/YYYY HH:mm')}
+
+
+
+
+
+

Fila de Espera

+
+
+ {/* Filtros */} +
+
+ Filtros +
- -
+
+ setWaitlistSearch(e.target.value)} + /> + + Digite o nome do paciente ou nome do médico + +
+ +
+ {/* Ordenação rápida */} +
+ Ordenar por: + {(() => { + const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; + return ( + + ); + })()} +
+
+ +
+
+ {filaEsperaFiltrada.length} DE {filaDeEspera.length} SOLICITAÇÕES ENCONTRADAS +
+
+
+ +
+ + + + + + + + + + + + {filaEsperaPaginada.length > 0 ? ( + filaEsperaPaginada.map((item, index) => ( + + + + + + + + )) + ) : ( + + + + )} + +
Nome do PacienteCPFMédico SolicitadoData da SolicitaçãoAções
{item?.paciente_nome}{item?.paciente_cpf}{item?.medico_nome}{dayjs(item?.created_at).format('DD/MM/YYYY HH:mm')} + +
+
+ +

Nenhuma solicitação encontrada.

+
+
+ + {/* Paginação */} + {filaEsperaFiltrada.length > 0 && ( +
+
+ Itens por página: + +
+ +
+ + Página {waitPage} de {waitTotalPages} • + Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length} + + + +
+
+ )} +
+
+
+
+ :
diff --git a/src/components/AgendarConsulta/FormNovaConsulta.jsx b/src/components/AgendarConsulta/FormNovaConsulta.jsx index 39575c29..2ae4a6d4 100644 --- a/src/components/AgendarConsulta/FormNovaConsulta.jsx +++ b/src/components/AgendarConsulta/FormNovaConsulta.jsx @@ -195,7 +195,6 @@ const FormNovaConsulta = ({ onCancel, onSave, setAgendamento, agendamento }) =>
Sucesso
-

Agendamento salvo com sucesso!

diff --git a/src/components/Estilo/Toggle.css b/src/components/Estilo/Toggle.css index 22dac7b1..731f0cf9 100644 --- a/src/components/Estilo/Toggle.css +++ b/src/components/Estilo/Toggle.css @@ -1,75 +1,101 @@ /* ========== Variáveis CSS ========== */ :root { - --primary-blue: #4a90e2; - --primary-blue-light: #5da3f5; - --primary-blue-lighter: #70b4ff; - --dark-blue: #1a3a5c; - --dark-blue-deep: #0d2640; - --white: #ffffff; - --transition: all 0.3s ease; + --toggle-bg: #ffffff; + --toggle-border: #e5e7eb; + --toggle-hover: #f3f4f6; + --toggle-active: #dbeafe; + --toggle-text: #1f2937; + --toggle-text-secondary: #374151; + --toggle-icon: #6b7280; + --toggle-accent: #2563eb; + --toggle-accent-hover: #1d4ed8; + --toggle-shadow: rgba(0, 0, 0, 0.05); + --toggle-shadow-hover: rgba(0, 0, 0, 0.1); } /* ========== Container Principal ========== */ .toggle-sidebar-wrapper { - background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-blue-light) 100%); + background: var(--toggle-bg); border-radius: 12px; - padding: 8px; - margin-bottom: 20px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); - border: 1px solid var(--primary-blue-lighter); + border: 1px solid var(--toggle-border); + margin-bottom: 16px; + overflow-y: auto; + overflow-x: hidden; + box-shadow: 0 1px 3px var(--toggle-shadow); + transition: box-shadow 0.2s ease; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.toggle-sidebar-wrapper::-webkit-scrollbar { + display: none !important; + width: 0 !important; + height: 0 !important; +} + +.toggle-sidebar-wrapper:hover { + box-shadow: 0 4px 6px var(--toggle-shadow-hover); } .container-title { display: flex; align-items: center; - padding: 14px 20px; + justify-content: space-between; + padding: 14px 18px; cursor: pointer; - background-color: var(--primary-blue); - border-radius: 8px; - margin-bottom: 12px; - transition: var(--transition); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); - border: 1px solid var(--primary-blue-light); + background-color: var(--toggle-bg); + transition: background-color 0.2s ease; + border-bottom: 1px solid var(--toggle-border); } .container-title:hover { - background-color: var(--primary-blue-light); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - transform: translateY(-1px); -} - -.container-title:hover .toggle-arrow { - color: var(--dark-blue-deep) !important; - transform: scale(1.1); + background-color: var(--toggle-hover); } .toggle-title { - color: var(--white) !important; - font-weight: 700; - font-size: 18px; + color: var(--toggle-text); + font-weight: 600; + font-size: 16px; margin: 0; - flex-grow: 1; user-select: none; + letter-spacing: -0.01em; } .toggle-arrow { - transition: var(--transition); - color: var(--dark-blue) !important; + color: var(--toggle-icon); + transition: transform 0.2s ease; + font-size: 14px; +} + +.container-title:hover .toggle-arrow { + color: var(--toggle-accent); } /* ========== Menu Lista ========== */ .sidebar-menu-list { list-style: none; - padding: 0; + padding: 8px; margin: 0; - animation: fadeIn 0.3s ease; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + min-height: 100%; + + /* Scroll invisível mas funcional */ + scrollbar-width: none; + -ms-overflow-style: none; +} + +.sidebar-menu-list::-webkit-scrollbar { + display: none !important; + width: 0 !important; + height: 0 !important; } .sidebar-item { list-style: none; - margin: 6px 0; - border-radius: 6px; - transition: all 0.2s ease; + margin: 2px 0; } /* ========== Links da Sidebar ========== */ @@ -77,233 +103,237 @@ display: flex; align-items: center; text-decoration: none; - color: var(--white) !important; - padding: 12px 20px; - border-radius: 6px; - transition: all 0.2s ease; - font-size: 16px; + color: var(--toggle-text-secondary); + padding: 11px 14px; + border-radius: 8px; + font-size: 15px; width: 100%; text-align: left; border: none; background: none; cursor: pointer; - position: relative; - overflow: hidden; font-weight: 500; -} - -.sidebar-link::before { - content: ''; - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 3px; - background-color: var(--dark-blue); - transform: scaleY(0); - transition: transform 0.2s ease; + transition: none; + gap: 12px; } .sidebar-link i { - margin-right: 12px; font-size: 19px; - transition: transform 0.2s ease; - color: var(--dark-blue) !important; + color: var(--toggle-icon); + transition: none; } .sidebar-link:hover { - color: var(--white) !important; - background-color: var(--primary-blue-light); - padding-left: 25px; -} - -.sidebar-link:hover::before { - transform: scaleY(1); + background-color: var(--toggle-hover); + color: var(--toggle-text); } .sidebar-link:hover i { - transform: scale(1.1); - color: var(--dark-blue-deep) !important; + color: var(--toggle-accent); } .sidebar-link.active { - color: var(--white) !important; - background-color: #1e3a8a; + background-color: var(--toggle-active); + color: var(--toggle-accent); font-weight: 600; - box-shadow: 0 4px 12px rgba(30, 58, 138, 0.4); - padding-left: 25px; -} - -.sidebar-link.active::before { - transform: scaleY(1); - background-color: var(--primary-blue-lighter); } .sidebar-link.active i { - color: var(--white) !important; + color: var(--toggle-accent); } /* ========== Título da Sidebar ========== */ .sidebar-title { - padding: 20px 20px 8px; - font-size: 14px; - color: var(--white) !important; + padding: 18px 14px 10px; + font-size: 13px; + color: var(--toggle-text-secondary); text-transform: uppercase; - font-weight: 700; - letter-spacing: 1px; - position: relative; -} - -.sidebar-title::after { - content: ''; - position: absolute; - bottom: 4px; - left: 20px; - right: 20px; - height: 2px; - background: linear-gradient(90deg, var(--white), transparent); + font-weight: 600; + letter-spacing: 0.05em; } /* ========== Itens com Submenu ========== */ .sidebar-item.has-sub { - border-radius: 6px 6px 0 0; - overflow: hidden; - background-color: rgba(74, 144, 226, 0.6); -} - -.sidebar-item.has-sub.active { - background-color: var(--primary-blue-light); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + margin: 4px 0; } .sidebar-item.has-sub .sidebar-link { - background-color: var(--primary-blue); - color: var(--white) !important; + position: relative; } .sidebar-item.has-sub .sidebar-link::after { - content: "\F282"; + content: "\F285"; font-family: "bootstrap-icons"; - font-weight: 900; margin-left: auto; - transition: var(--transition); - display: flex; - align-items: center; - font-size: 14px; - color: var(--dark-blue) !important; -} - -.sidebar-item.has-sub.active .sidebar-link::after, -.sidebar-item.has-sub .sidebar-link:hover::after { - color: var(--dark-blue-deep) !important; + transition: transform 0.2s ease; + font-size: 12px; + color: var(--toggle-icon); } .sidebar-item.has-sub.active .sidebar-link::after { transform: rotate(180deg); } -.sidebar-item.has-sub .sidebar-link.submenu-active, -.sidebar-item.has-sub.active .sidebar-link.submenu-active { - color: var(--white) !important; +.sidebar-item.has-sub .sidebar-link.submenu-active { + background-color: var(--toggle-hover); + color: var(--toggle-text); } .sidebar-item.has-sub .sidebar-link.submenu-active i { - color: var(--dark-blue) !important; -} - -.sidebar-item.has-sub.active .sidebar-link.submenu-active { - background-color: #1e3a8a; -} - -.sidebar-item.has-sub.active .sidebar-link.submenu-active i { - color: var(--white) !important; + color: var(--toggle-accent); } /* ========== Submenu ========== */ .submenu { list-style: none; - padding-left: 0; + padding: 4px 0 4px 8px; margin: 0; max-height: 0; overflow: hidden; - transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1); - background-color: rgba(74, 144, 226, 0.9); - border-radius: 0 0 6px 6px; + transition: max-height 0.3s ease; + + /* Scroll invisível */ + scrollbar-width: none; + -ms-overflow-style: none; +} + +.submenu::-webkit-scrollbar { + display: none; } .sidebar-item.has-sub.active .submenu { - max-height: 800px; + max-height: 1000px; + overflow: visible; } .submenu-item { - position: relative; + margin: 2px 0; } .submenu-item .sidebar-link { - padding-left: 45px; - font-size: 15px; - color: var(--white) !important; - background-color: transparent; - font-weight: 500; + padding: 9px 14px 9px 40px; + font-size: 14px; + position: relative; } .submenu-item .sidebar-link::before { - left: 30px; - width: 2px; - background-color: var(--dark-blue); -} - -.submenu-item .sidebar-link:hover { - background-color: var(--primary-blue-lighter); - color: var(--white) !important; - padding-left: 48px; + content: ''; + position: absolute; + left: 22px; + top: 50%; + transform: translateY(-50%); + width: 5px; + height: 5px; + border-radius: 50%; + background-color: var(--toggle-icon); } .submenu-item .sidebar-link:hover::before { - background-color: var(--dark-blue-deep); + background-color: var(--toggle-accent); } -.submenu-item:last-child .sidebar-link { - border-radius: 0 0 6px 6px; -} - -.sidebar-item.has-sub.active .submenu-item:first-child .sidebar-link { - border-radius: 0; -} - -.sidebar-link.submenu-link.active::before { - background-color: var(--primary-blue-lighter); +.submenu-item .sidebar-link.active::before { + background-color: var(--toggle-accent); } /* ========== Ícones e Indicadores ========== */ .external-icon { - font-size: 12px; - margin-left: 8px; - opacity: 0.8; - color: var(--dark-blue) !important; + font-size: 11px; + margin-left: auto; + opacity: 0.6; + color: var(--toggle-icon); } .active-indicator { - position: absolute; - right: 15px; - top: 50%; - transform: translateY(-50%); - width: 8px; - height: 8px; - background-color: var(--primary-blue-lighter); - border-radius: 50%; - animation: pulse 2s infinite; - box-shadow: 0 0 10px rgba(112, 180, 255, 0.6); + display: none; } -/* ========== Animações ========== */ -@keyframes fadeIn { - from { opacity: 0; transform: translateY(-10px); } - to { opacity: 1; transform: translateY(0); } +/* ========== Botão de Logout ========== */ +.logout-item { + margin-top: auto; + padding-top: 16px; + border-top: 1px solid var(--toggle-border); + position: sticky; + bottom: 0; + background: var(--toggle-bg); + z-index: 10; } -@keyframes pulse { - 0% { transform: translateY(-50%) scale(1); opacity: 1; } - 50% { transform: translateY(-50%) scale(1.3); opacity: 0.8; } - 100% { transform: translateY(-50%) scale(1); opacity: 1; } -} \ No newline at end of file +.logout-button { + display: flex; + align-items: center; + text-decoration: none; + color: #dc3545; + padding: 11px 14px; + border-radius: 8px; + font-size: 15px; + width: 100%; + text-align: left; + border: none; + background: none; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s ease; + gap: 12px; +} + +.logout-button i { + font-size: 19px; + color: #dc3545; +} + +.logout-button:hover { + background-color: #fee; + color: #c82333; +} + +.logout-button:hover i { + color: #c82333; +} + +/* ========== Scroll Invisível mas Funcional ========== */ +/* Containers do Mazer template que envolvem o ToggleSidebar */ +#sidebar, +#sidebar .sidebar-wrapper, +#sidebar .sidebar-wrapper.active, +.sidebar-wrapper, +.sidebar-wrapper.active, +.sidebar-menu, +.sidebar-menu ul, +.sidebar-menu ul.menu, +ul.menu { + overflow-y: auto !important; + overflow-x: hidden !important; + scrollbar-width: none !important; + -ms-overflow-style: none !important; +} + +#sidebar::-webkit-scrollbar, +#sidebar .sidebar-wrapper::-webkit-scrollbar, +#sidebar .sidebar-wrapper.active::-webkit-scrollbar, +.sidebar-wrapper::-webkit-scrollbar, +.sidebar-wrapper.active::-webkit-scrollbar, +.sidebar-menu::-webkit-scrollbar, +.sidebar-menu ul::-webkit-scrollbar, +.sidebar-menu ul.menu::-webkit-scrollbar, +ul.menu::-webkit-scrollbar { + display: none !important; + width: 0 !important; + height: 0 !important; + background: transparent !important; +} + +/* ========== Dark Mode Support ========== */ +html[data-bs-theme="dark"] { + --toggle-bg: #1f2937; + --toggle-border: #374151; + --toggle-hover: #374151; + --toggle-active: #1e3a8a; + --toggle-text: #f9fafb; + --toggle-text-secondary: #d1d5db; + --toggle-icon: #9ca3af; + --toggle-accent: #60a5fa; + --toggle-accent-hover: #3b82f6; + --toggle-shadow: rgba(0, 0, 0, 0.3); + --toggle-shadow-hover: rgba(0, 0, 0, 0.5); +} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 5922c088..6f21395c 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -273,6 +273,17 @@ function Sidebar({ menuItems }) { : null } + + {/* Botão de Logout */} +
  • + +
  • diff --git a/src/pages/Agendamento.jsx b/src/pages/Agendamento.jsx index cfec1d7b..9ad921f4 100644 --- a/src/pages/Agendamento.jsx +++ b/src/pages/Agendamento.jsx @@ -59,6 +59,13 @@ const Agendamento = ({ setDictInfo }) => { const [coresConsultas,setCoresConsultas] = useState([]) const [showSpinner,setShowSpinner] = useState(true) + + // Estados para Fila de Espera (padrão TablePaciente) + const [waitlistSearch, setWaitlistSearch] = useState(''); + const [waitSortKey, setWaitSortKey] = useState(null); // 'paciente' | 'medico' | 'data' | null + const [waitSortDir, setWaitSortDir] = useState('asc'); // 'asc' | 'desc' + const [waitPage, setWaitPage] = useState(1); + const [waitPerPage, setWaitPerPage] = useState(10); let authHeader = getAuthorizationHeader() @@ -314,6 +321,65 @@ useEffect(() => { setShowSpinner(false) },[filaEsperaData]) + // Filtrar Fila de Espera + const filaEsperaFiltrada = useMemo(() => { + if (!waitlistSearch.trim()) return filaEsperaData; + const term = waitlistSearch.toLowerCase(); + return filaEsperaData.filter(item => { + const paciente = item?.Infos?.paciente_nome?.toLowerCase() || ''; + const cpf = item?.Infos?.paciente_cpf?.toLowerCase() || ''; + const medico = item?.Infos?.nome_medico?.toLowerCase() || ''; + return paciente.includes(term) || cpf.includes(term) || medico.includes(term); + }); + }, [waitlistSearch, filaEsperaData]); + + // Ordenar Fila de Espera + const applySortingWaitlist = (arr) => { + if (!Array.isArray(arr) || !waitSortKey) return arr; + const copy = [...arr]; + if (waitSortKey === 'paciente') { + copy.sort((a, b) => (a?.Infos?.paciente_nome || '').localeCompare((b?.Infos?.paciente_nome || ''), undefined, { sensitivity: 'base' })); + } else if (waitSortKey === 'medico') { + copy.sort((a, b) => (a?.Infos?.nome_medico || '').localeCompare((b?.Infos?.nome_medico || ''), undefined, { sensitivity: 'base' })); + } else if (waitSortKey === 'data') { + copy.sort((a, b) => new Date(a?.agendamento?.scheduled_at || 0) - new Date(b?.agendamento?.scheduled_at || 0)); + } + if (waitSortDir === 'desc') copy.reverse(); + return copy; + }; + + const filaEsperaOrdenada = applySortingWaitlist(filaEsperaFiltrada); + + // Paginação Fila de Espera + const waitTotalPages = Math.ceil(filaEsperaFiltrada.length / waitPerPage) || 1; + const waitIndiceInicial = (waitPage - 1) * waitPerPage; + const waitIndiceFinal = waitIndiceInicial + waitPerPage; + const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal); + + const gerarNumerosWaitPages = () => { + const paginas = []; + const paginasParaMostrar = 5; + let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2)); + let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1); + inicio = Math.max(1, fim - paginasParaMostrar + 1); + for (let i = inicio; i <= fim; i++) { + paginas.push(i); + } + return paginas; + }; + + // Reset página quando filtros/ordenação mudarem + useEffect(() => { + setWaitPage(1); + }, [waitlistSearch, waitSortKey, waitSortDir]); + + const limparFiltrosWaitlist = () => { + setWaitlistSearch(''); + setWaitSortKey(null); + setWaitSortDir('asc'); + setWaitPage(1); + }; + return (
    @@ -445,8 +511,64 @@ useEffect(() => {

    Fila de Espera

    + {/* Filtros */} +
    +
    + Filtros +
    + +
    + setWaitlistSearch(e.target.value)} + /> + + Digite o nome do paciente, CPF ou nome do médico + +
    + +
    + {/* Ordenação rápida */} +
    + Ordenar por: + {(() => { + const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : ''; + return ( + + ); + })()} +
    +
    + +
    +
    + {filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS +
    +
    +
    - +
    @@ -457,34 +579,97 @@ useEffect(() => { - {filaEsperaData.map((item, index) => ( - - - - - - - - ))} - {showSpinner && + {filaEsperaPaginada.length > 0 ? ( + filaEsperaPaginada.map((item, index) => ( + + + + + + + + )) + ) : ( - - } + )}
    Nome do Paciente

    {item?.Infos?.paciente_nome}

    {item?.Infos?.paciente_cpf}{item?.Infos?.nome_medico}{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')} - -
    {item?.Infos?.paciente_nome}{item?.Infos?.paciente_cpf}{item?.Infos?.nome_medico}{dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')} + +
    - + +
    + {showSpinner ? ( + + ) : ( + <> + +

    Nenhuma solicitação encontrada.

    + + )} +
    + + {/* Paginação */} + {filaEsperaFiltrada.length > 0 && ( +
    +
    + Itens por página: + +
    + +
    + + Página {waitPage} de {waitTotalPages} • + Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length} + + + +
    +
    + )}
    diff --git a/src/pages/DoctorTable.jsx b/src/pages/DoctorTable.jsx index cd218139..e5c2d76f 100644 --- a/src/pages/DoctorTable.jsx +++ b/src/pages/DoctorTable.jsx @@ -27,6 +27,10 @@ function TableDoctor() { const [showDeleteModal, setShowDeleteModal] = useState(false); const [selectedDoctorId, setSelectedDoctorId] = useState(null); + // Ordenação rápida + const [sortKey, setSortKey] = useState(null); // 'nome' | 'idade' | null + const [sortDir, setSortDir] = useState('asc'); // 'asc' | 'desc' + const limparFiltros = () => { setSearch(""); setFiltroEspecialidade("Todos"); @@ -143,11 +147,25 @@ function TableDoctor() { return resultado; }) : []; + // Aplica ordenação rápida + const applySorting = (arr) => { + if (!Array.isArray(arr) || !sortKey) return arr; + const copy = [...arr]; + if (sortKey === 'nome') { + copy.sort((a, b) => (a.full_name || '').localeCompare((b.full_name || ''), undefined, { sensitivity: 'base' })); + } else if (sortKey === 'idade') { + copy.sort((a, b) => calcularIdade(a.birth_date) - calcularIdade(b.birth_date)); + } + if (sortDir === 'desc') copy.reverse(); + return copy; + }; + + const medicosOrdenados = applySorting(medicosFiltrados); const totalPaginas = Math.ceil(medicosFiltrados.length / itensPorPagina); const indiceInicial = (paginaAtual - 1) * itensPorPagina; const indiceFinal = indiceInicial + itensPorPagina; - const medicosPaginados = medicosFiltrados.slice(indiceInicial, indiceFinal); + const medicosPaginados = medicosOrdenados.slice(indiceInicial, indiceFinal); const irParaPagina = (pagina) => { @@ -185,7 +203,7 @@ function TableDoctor() { useEffect(() => { setPaginaAtual(1); - }, [search, filtroEspecialidade, filtroAniversariante, filtroCidade, filtroEstado, idadeMinima, idadeMaxima, dataInicial, dataFinal]); + }, [search, filtroEspecialidade, filtroAniversariante, filtroCidade, filtroEstado, idadeMinima, idadeMaxima, dataInicial, dataFinal, sortKey, sortDir]); return ( <> @@ -258,6 +276,34 @@ function TableDoctor() { Aniversariantes
    + + {/* Ordenação rápida */} +
    +
    + Ordenar por: + {(() => { + const sortValue = sortKey ? `${sortKey}-${sortDir}` : ''; + return ( + + ); + })()} +
    @@ -357,24 +403,6 @@ function TableDoctor() {
    - {(search || filtroEspecialidade !== "Todos" || filtroAniversariante || - filtroCidade || filtroEstado || idadeMinima || idadeMaxima || dataInicial || dataFinal) && ( -
    - Filtros ativos: -
    - {search && Busca: "{search}"} - {filtroEspecialidade !== "Todos" && Especialidade: {filtroEspecialidade}} - {filtroAniversariante && Aniversariantes} - {filtroCidade && Cidade: {filtroCidade}} - {filtroEstado && Estado: {filtroEstado}} - {idadeMinima && Idade mín: {idadeMinima}} - {idadeMaxima && Idade máx: {idadeMaxima}} - {dataInicial && Data inicial: {dataInicial}} - {dataFinal && Data final: {dataFinal}} -
    -
    - )} -
    diff --git a/src/pages/TablePaciente.jsx b/src/pages/TablePaciente.jsx index 386e7ad9..c8046189 100644 --- a/src/pages/TablePaciente.jsx +++ b/src/pages/TablePaciente.jsx @@ -23,6 +23,10 @@ function TablePaciente({ setCurrentPage, setPatientID }) { const [dataInicial, setDataInicial] = useState(""); const [dataFinal, setDataFinal] = useState(""); + // Ordenação rápida + const [sortKey, setSortKey] = useState(null); // 'nome' | 'idade' | null + const [sortDir, setSortDir] = useState('asc'); // 'asc' | 'desc' + const [paginaAtual, setPaginaAtual] = useState(1); const [itensPorPagina, setItensPorPagina] = useState(10); @@ -109,11 +113,8 @@ function TablePaciente({ setCurrentPage, setPatientID }) { } }; - // Função para refresh token (adicionada) const RefreshingToken = () => { console.log("Refreshing token..."); - // Aqui você pode adicionar a lógica de refresh do token se necessário - // Por enquanto é apenas um placeholder para evitar o erro }; useEffect(() => { @@ -249,11 +250,25 @@ function TablePaciente({ setCurrentPage, setPatientID }) { return resultado; }) : []; + // Aplica ordenação rápida + const applySorting = (arr) => { + if (!Array.isArray(arr) || !sortKey) return arr; + const copy = [...arr]; + if (sortKey === 'nome') { + copy.sort((a, b) => (a.full_name || '').localeCompare((b.full_name || ''), undefined, { sensitivity: 'base' })); + } else if (sortKey === 'idade') { + copy.sort((a, b) => calcularIdade(a.birth_date) - calcularIdade(b.birth_date)); + } + if (sortDir === 'desc') copy.reverse(); + return copy; + }; + + const pacientesOrdenados = applySorting(pacientesFiltrados); const totalPaginas = Math.ceil(pacientesFiltrados.length / itensPorPagina); const indiceInicial = (paginaAtual - 1) * itensPorPagina; const indiceFinal = indiceInicial + itensPorPagina; - const pacientesPaginados = pacientesFiltrados.slice(indiceInicial, indiceFinal); + const pacientesPaginados = pacientesOrdenados.slice(indiceInicial, indiceFinal); const irParaPagina = (pagina) => { @@ -292,7 +307,7 @@ function TablePaciente({ setCurrentPage, setPatientID }) { useEffect(() => { setPaginaAtual(1); - }, [search, filtroConvenio, filtroVIP, filtroAniversariante, filtroCidade, filtroEstado, idadeMinima, idadeMaxima, dataInicial, dataFinal]); + }, [search, filtroConvenio, filtroVIP, filtroAniversariante, filtroCidade, filtroEstado, idadeMinima, idadeMaxima, dataInicial, dataFinal, sortKey, sortDir]); return ( <> @@ -361,6 +376,34 @@ function TablePaciente({ setCurrentPage, setPatientID }) { > Aniversariantes + + {/* Ordenação rápida (estilo compacto por select) */} +
    +
    + Ordenar por: + {(() => { + const sortValue = sortKey ? `${sortKey}-${sortDir}` : ''; + return ( + + ); + })()} +
    @@ -460,25 +503,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
    - {(search || filtroConvenio !== "Todos" || filtroVIP || filtroAniversariante || - filtroCidade || filtroEstado || idadeMinima || idadeMaxima || dataInicial || dataFinal) && ( -
    - Filtros ativos: -
    - {search && Busca: "{search}"} - {filtroConvenio !== "Todos" && Convênio: {filtroConvenio}} - {filtroVIP && VIP} - {filtroAniversariante && Aniversariantes} - {filtroCidade && Cidade: {filtroCidade}} - {filtroEstado && Estado: {filtroEstado}} - {idadeMinima && Idade mín: {idadeMinima}} - {idadeMaxima && Idade máx: {idadeMaxima}} - {dataInicial && Data inicial: {dataInicial}} - {dataFinal && Data final: {dataFinal}} -
    -
    - )} -
    diff --git a/src/pages/style/Agendamento.css b/src/pages/style/Agendamento.css index 101b58f9..137e79bc 100644 --- a/src/pages/style/Agendamento.css +++ b/src/pages/style/Agendamento.css @@ -360,20 +360,12 @@ html[data-bs-theme="dark"] { .input-e-dropdown-wrapper { position: relative; - - width: 350px; margin-left: auto; } -.busca-atendimento { - -} - .busca-atendimento > div { - /* Garante que a div interna do input ocupe toda a largura do wrapper */ width: 100%; - /* Estilos para o contêiner do ícone e input, se necessário */ } .busca-atendimento input { @@ -449,3 +441,240 @@ button.btn.btn-danger:active { border-color: #9c1c28 !important; color: #ffffff !important; } + +/* ===== ESTILOS REUTILIZADOS DA TABELA DE PACIENTES ===== */ + +/* Containers e Cards */ +.table-paciente-container { + line-height: 2.5; +} + +.table-paciente-card { + border: none; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} + +.table-paciente-card .card-header { + background-color: #f8f9fa; + border-bottom: 1px solid #dee2e6; + padding: 1rem 1.25rem; +} + +/* Filtros */ +.table-paciente-filters { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 0.375rem; +} + +.table-paciente-filters h5 { + color: #495057; + font-weight: 600; +} + +/* Tabela */ +.table-paciente-table { + width: 100%; + border-collapse: collapse; +} + +.table-paciente-table th { + background-color: #f8f9fa; + color: #495057; + font-weight: 600; + padding: 15px 8px; + border-bottom: 2px solid #dee2e6; + vertical-align: middle; +} + +.table-paciente-table td { + padding: 15px 8px; + vertical-align: middle; + border-bottom: 1px solid #dee2e6; +} + +.table-paciente-table tbody tr:hover { + background-color: rgba(0, 0, 0, 0.025); + transition: background-color 0.15s ease-in-out; +} + +/* Badges e Contador */ +.contador-pacientes { + background-color: #1e3a8a; + color: white; + padding: 0.5em 0.75em; + font-size: 0.875em; + font-weight: 500; + border-radius: 0.375rem; + text-align: center; + display: inline-block; +} + +.filters-active .badge { + font-size: 0.75em; + padding: 0.4em 0.65em; + margin-bottom: 0.25rem; +} + +/* Botões */ +.btn-view { + background-color: #E6F2FF !important; + color: #004085 !important; + border: 1px solid #B8D4F0; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + transition: all 0.15s ease-in-out; +} + +.btn-view:hover { + background-color: #D1E7FF !important; + border-color: #9EC5FE; +} + +.btn-edit { + background-color: #FFF3CD !important; + color: #856404 !important; + border: 1px solid #FFEAA7; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + transition: all 0.15s ease-in-out; +} + +.btn-edit:hover { + background-color: #FFEEBA !important; + border-color: #FFE087; +} + +.btn-delete { + background-color: #F8D7DA !important; + color: #721C24 !important; + border: 1px solid #F5C6CB; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + transition: all 0.15s ease-in-out; +} + +.btn-delete:hover { + background-color: #F1B0B7 !important; + border-color: #ED969E; +} + +/* Selects compactos */ +.compact-select { + font-size: 1.0rem; + padding: 0.45rem 0.5rem; +} + +.compact-select option { + font-size: 0.875rem; +} + +/* Paginação */ +.pagination { + margin-bottom: 0; +} + +.page-link { + color: #495057; + border: 1px solid #dee2e6; + padding: 0.375rem 0.75rem; + font-size: 0.875rem; +} + +.page-link:hover { + color: #1e3a8a; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.page-item.active .page-link { + background-color: #1e3a8a; + border-color: #1e3a8a; + color: white; +} + +.page-item.disabled .page-link { + color: #6c757d; + background-color: #f8f9fa; + border-color: #dee2e6; +} + +.d-flex.justify-content-between.align-items-center { + border-top: 1px solid #dee2e6; + padding-top: 1rem; + margin-top: 1rem; +} + +/* Empty State */ +.text-center.py-4 .text-muted { + padding: 2rem; +} + +.text-center.py-4 .bi-inbox { + font-size: 3rem; + opacity: 0.5; +} + +.text-center.py-4 p { + margin-bottom: 0.5rem; + font-size: 1.1rem; +} + +.text-center.py-4 td { + border-bottom: none; + padding: 2rem !important; +} + +/* Responsividade */ +@media (max-width: 768px) { + .table-paciente-table { + font-size: 0.875rem; + } + + .table-paciente-table th, + .table-paciente-table td { + padding: 10px 6px; + } + + .btn-view, + .btn-edit, + .btn-delete { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + } + + .table-paciente-filters .d-flex { + flex-direction: column; + gap: 0.5rem; + } + + .table-paciente-filters .form-select { + min-width: 100% !important; + } + + .d-flex.justify-content-between.align-items-center { + flex-direction: column; + gap: 1rem; + align-items: stretch !important; + } + + .d-flex.justify-content-between.align-items-center > div { + justify-content: center !important; + } + + .pagination { + flex-wrap: wrap; + justify-content: center; + } + + .me-3.text-muted { + text-align: center; + margin-bottom: 0.5rem; + font-size: 0.8rem; + } + + .contador-pacientes { + font-size: 0.8rem; + padding: 0.4em 0.6em; + } +}