forked from RiseUP/riseup-squad23
598 lines
26 KiB
JavaScript
598 lines
26 KiB
JavaScript
import React from 'react'
|
|
import "./style.css"
|
|
import "../pages/style/TablePaciente.css"
|
|
import CardConsultaPaciente from './CardConsultaPaciente'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { useEffect, useState, useMemo } from 'react'
|
|
import API_KEY from '../components/utils/apiKeys'
|
|
import { useAuth } from '../components/utils/AuthProvider'
|
|
import { GetPatientByID } from '../components/utils/Functions-Endpoints/Patient'
|
|
import { GetDoctorByID } from '../components/utils/Functions-Endpoints/Doctor'
|
|
|
|
import { UserInfos } from '../components/utils/Functions-Endpoints/General'
|
|
import dayjs from 'dayjs'
|
|
import TabelaAgendamentoDia from "../components/AgendarConsulta/TabelaAgendamentoDia"
|
|
|
|
const ConsultasPaciente = ({ setDictInfo }) => {
|
|
const { getAuthorizationHeader } = useAuth()
|
|
const [agendamentosOrganizados, setAgendamentosOrganizados] = useState({})
|
|
const [listaTodasConsultas, setListaTodasConsultas] = useState([])
|
|
const [patientID, setPatientID] = useState("")
|
|
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
|
const [selectedID, setSelectedId] = useState("")
|
|
let authHeader = getAuthorizationHeader()
|
|
|
|
const [motivoCancelamento, setMotivoCancelamento] = useState("")
|
|
|
|
const [consultas, setConsultas] = useState([])
|
|
|
|
const [consultasOrganizadas, setConsultasOrganizadas] = useState({})
|
|
const [filaDeEspera, setFilaDeEspera] = useState([])
|
|
const [viewFila, setViewFila] = useState(false)
|
|
|
|
const [listaConsultasID, setListaConsultaID] = useState([])
|
|
const [coresConsultas,setCoresConsultas] = useState([])
|
|
|
|
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(() => {
|
|
|
|
console.log(listaConsultasID, coresConsultas, "ojwhdofigewfey7few0fr74r")
|
|
|
|
}, [coresConsultas, listaConsultasID])
|
|
|
|
useMemo(() => {
|
|
let conjuntoConsultas = {}
|
|
let filaEspera = []
|
|
|
|
const fetchInfosConsultas = async (consulta) => {
|
|
//console.log(doctor, "PACIENTE TRAZIDO PELO ")
|
|
|
|
//let consultaMelhorada = {...consulta, paciente_nome:paciente[0].full_name, medico_nome:doctor[0].full_name }
|
|
|
|
//console.log(consultaMelhorada,"ID DO MEDICO")
|
|
|
|
for(let i = 0; listaTodasConsultas.length > i; i++){
|
|
|
|
let consulta = listaTodasConsultas[i]
|
|
|
|
let doctor = await GetDoctorByID(consulta.doctor_id, authHeader)
|
|
let paciente = await GetPatientByID(consulta.patient_id, authHeader)
|
|
|
|
consulta = {...consulta, medico_nome:doctor[0]?.full_name, paciente_nome:paciente[0]?.full_name}
|
|
|
|
|
|
|
|
|
|
if(consulta.status === "requested"){
|
|
|
|
filaEspera.push(consulta)
|
|
|
|
}else{
|
|
|
|
let data = consulta.scheduled_at.split("T")[0]
|
|
let chavesConsultas = Object.keys(conjuntoConsultas)
|
|
|
|
if(chavesConsultas.includes(data)){
|
|
let lista = conjuntoConsultas[data]
|
|
|
|
lista.push(consulta)
|
|
|
|
conjuntoConsultas = {...conjuntoConsultas, [data]:lista}
|
|
}else{
|
|
conjuntoConsultas = {...conjuntoConsultas, [data]:[consulta] }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
setConsultasOrganizadas(conjuntoConsultas)
|
|
setFilaDeEspera(filaEspera)
|
|
|
|
}
|
|
|
|
console.log("so muda")
|
|
if(!listaTodasConsultas.length) return
|
|
|
|
console.log(filaEspera, "fila de espera")
|
|
fetchInfosConsultas();
|
|
|
|
}, [listaTodasConsultas])
|
|
|
|
|
|
useEffect(() => {
|
|
let userInfos = UserInfos(authHeader)
|
|
|
|
const fetchConsultas = async () => {
|
|
try {
|
|
const myHeaders = new Headers();
|
|
myHeaders.append("Authorization", authHeader);
|
|
myHeaders.append("apikey", API_KEY)
|
|
|
|
const requestOptions = {
|
|
method: 'GET',
|
|
headers: myHeaders,
|
|
redirect: 'follow'
|
|
};
|
|
|
|
const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?patient_id=eq.${"6e7f8829-0574-42df-9290-8dbb70f75ada"}`, requestOptions);
|
|
const result = await response.json();
|
|
setListaTodasConsultas(result);
|
|
} catch (error) {
|
|
console.log('error', error);
|
|
}
|
|
};
|
|
|
|
fetchConsultas();
|
|
}, []);
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
const confirmConsulta = (selectedPatientId) => {
|
|
var myHeaders = new Headers();
|
|
myHeaders.append("Content-Type", "application/json");
|
|
myHeaders.append('apikey', API_KEY)
|
|
myHeaders.append("authorization", authHeader)
|
|
|
|
|
|
var raw = JSON.stringify({ "status":"confirmed"
|
|
});
|
|
|
|
|
|
var requestOptions = {
|
|
method: 'PATCH',
|
|
headers: myHeaders,
|
|
body: raw,
|
|
redirect: 'follow'
|
|
};
|
|
|
|
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${selectedPatientId}`, requestOptions)
|
|
.then(response => {if(response.status !== 200)(console.log(response))})
|
|
.then(result => console.log(result))
|
|
.catch(error => console.log('error', error));
|
|
|
|
}
|
|
|
|
const deleteConsulta = async (ID) => {
|
|
try {
|
|
const myHeaders = new Headers();
|
|
myHeaders.append("Content-Type", "application/json");
|
|
myHeaders.append('apikey', API_KEY);
|
|
myHeaders.append("authorization", authHeader);
|
|
|
|
const raw = JSON.stringify({ "status": "cancelled", "cancellation_reason":motivoCancelamento });
|
|
|
|
const requestOptions = {
|
|
method: 'PATCH',
|
|
headers: myHeaders,
|
|
body: raw,
|
|
redirect: 'follow'
|
|
};
|
|
|
|
const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${ID}`, requestOptions);
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`Falha ao cancelar consulta: ${response.status} - ${errorText}`);
|
|
}
|
|
setConsultas(prevConsultas => prevConsultas.filter(consulta => consulta.id !== ID));
|
|
|
|
console.log("Consulta cancelada com sucesso!");
|
|
alert("Consulta cancelada com sucesso!");
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao cancelar a consulta:', error);
|
|
alert('Erro ao cancelar a consulta. Veja o console.');
|
|
}
|
|
}
|
|
|
|
|
|
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 (
|
|
<div>
|
|
<h1> Gerencie suas consultas</h1>
|
|
|
|
<div className='form-container'>
|
|
|
|
<div className='btns-container'>
|
|
<button className="btn btn-primary" onClick={() => { navigate("criar") }}>
|
|
<i className="bi bi-plus-circle"></i> Adicionar Consulta
|
|
</button>
|
|
{!viewFila ?
|
|
<button onClick={() => setViewFila(true)} className="btn btn-primary">Ver fila de espera</button>
|
|
:
|
|
<button onClick={() => setViewFila(false)} className="btn btn-primary">Ver consultas </button>
|
|
}
|
|
</div>
|
|
|
|
|
|
|
|
{viewFila ?
|
|
<div className="page-content table-paciente-container">
|
|
<section className="row">
|
|
<div className="col-12">
|
|
<div className="card table-paciente-card">
|
|
<div className="card-header">
|
|
<h4 className="card-title mb-0">Fila de Espera</h4>
|
|
</div>
|
|
<div className="card-body">
|
|
{/* Filtros */}
|
|
<div className="card p-3 mb-3 table-paciente-filters">
|
|
<h5 className="mb-3">
|
|
<i className="bi bi-funnel-fill me-2 text-primary"></i> Filtros
|
|
</h5>
|
|
|
|
<div className="mb-3">
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Buscar por paciente ou médico..."
|
|
value={waitlistSearch}
|
|
onChange={(e) => setWaitlistSearch(e.target.value)}
|
|
/>
|
|
<small className="text-muted">
|
|
Digite o nome do paciente ou nome do médico
|
|
</small>
|
|
</div>
|
|
|
|
<div className="d-flex flex-wrap align-items-center gap-2 mb-3">
|
|
{/* Ordenação rápida */}
|
|
<div className="d-flex align-items-center gap-2">
|
|
<span className="text-muted small">Ordenar por:</span>
|
|
{(() => {
|
|
const sortValue = waitSortKey ? `${waitSortKey}-${waitSortDir}` : '';
|
|
return (
|
|
<select
|
|
className="form-select compact-select sort-select w-auto"
|
|
value={sortValue}
|
|
onChange={(e) => {
|
|
const v = e.target.value;
|
|
if (!v) { setWaitSortKey(null); setWaitSortDir('asc'); return; }
|
|
const [k, d] = v.split('-');
|
|
setWaitSortKey(k);
|
|
setWaitSortDir(d);
|
|
}}
|
|
>
|
|
<option value="">Sem ordenação</option>
|
|
<option value="paciente-asc">Paciente (A-Z)</option>
|
|
<option value="paciente-desc">Paciente (Z-A)</option>
|
|
<option value="medico-asc">Médico (A-Z)</option>
|
|
<option value="medico-desc">Médico (Z-A)</option>
|
|
<option value="data-asc">Data (mais antiga)</option>
|
|
<option value="data-desc">Data (mais recente)</option>
|
|
</select>
|
|
);
|
|
})()}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-3">
|
|
<div className="contador-pacientes">
|
|
{filaEsperaFiltrada.length} DE {filaDeEspera.length} SOLICITAÇÕES ENCONTRADAS
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="table-responsive">
|
|
<table className="table table-striped table-hover table-paciente-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Nome do Paciente</th>
|
|
<th>CPF</th>
|
|
<th>Médico Solicitado</th>
|
|
<th>Data da Solicitação</th>
|
|
<th>Ações</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{filaEsperaPaginada.length > 0 ? (
|
|
filaEsperaPaginada.map((item, index) => (
|
|
<tr key={index}>
|
|
<td>{item?.paciente_nome}</td>
|
|
<td>{item?.paciente_cpf}</td>
|
|
<td>{item?.medico_nome}</td>
|
|
<td>{dayjs(item?.created_at).format('DD/MM/YYYY HH:mm')}</td>
|
|
<td>
|
|
<button
|
|
className="btn btn-sm btn-delete"
|
|
onClick={() => {
|
|
setSelectedId(item.id)
|
|
setShowDeleteModal(true);
|
|
}}
|
|
>
|
|
<i className="bi bi-trash me-1"></i> Excluir
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td colSpan="5" className="text-center py-4">
|
|
<div className="text-muted">
|
|
<i className="bi bi-inbox display-4"></i>
|
|
<p className="mt-2">Nenhuma solicitação encontrada.</p>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
|
|
{/* Paginação */}
|
|
{filaEsperaFiltrada.length > 0 && (
|
|
<div className="d-flex justify-content-between align-items-center mt-3">
|
|
<div className="d-flex align-items-center">
|
|
<span className="me-2 text-muted">Itens por página:</span>
|
|
<select
|
|
className="form-select form-select-sm w-auto"
|
|
value={waitPerPage}
|
|
onChange={(e) => {
|
|
setWaitPerPage(Number(e.target.value));
|
|
setWaitPage(1);
|
|
}}
|
|
>
|
|
<option value={5}>5</option>
|
|
<option value={10}>10</option>
|
|
<option value={25}>25</option>
|
|
<option value={50}>50</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="d-flex align-items-center">
|
|
<span className="me-3 text-muted">
|
|
Página {waitPage} de {waitTotalPages} •
|
|
Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
|
|
</span>
|
|
|
|
<nav>
|
|
<ul className="pagination pagination-sm mb-0">
|
|
<li className={`page-item ${waitPage === 1 ? 'disabled' : ''}`}>
|
|
<button className="page-link" onClick={() => setWaitPage(p => Math.max(1, p - 1))}>
|
|
<i className="bi bi-chevron-left"></i>
|
|
</button>
|
|
</li>
|
|
|
|
{gerarNumerosWaitPages().map(pagina => (
|
|
<li key={pagina} className={`page-item ${pagina === waitPage ? 'active' : ''}`}>
|
|
<button className="page-link" onClick={() => setWaitPage(pagina)}>
|
|
{pagina}
|
|
</button>
|
|
</li>
|
|
))}
|
|
|
|
<li className={`page-item ${waitPage === waitTotalPages ? 'disabled' : ''}`}>
|
|
<button className="page-link" onClick={() => setWaitPage(p => Math.min(waitTotalPages, p + 1))}>
|
|
<i className="bi bi-chevron-right"></i>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
:
|
|
<div>
|
|
<h2 className='fila-titulo'>Suas proximas consultas</h2>
|
|
|
|
<TabelaAgendamentoDia agendamentos={consultasOrganizadas} setDictInfo={setDictInfo}
|
|
selectedID={selectedID} setSelectedId={setSelectedId} setShowDeleteModal={setShowDeleteModal}
|
|
coresConsultas={coresConsultas} setListaConsultaID={setListaConsultaID}
|
|
listaConsultasID={listaConsultasID} setShowConfirmModal={setShowConfirmModal}
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
{showDeleteModal && (
|
|
<div
|
|
className="modal fade show"
|
|
style={{
|
|
display: "block",
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
}}
|
|
tabIndex="-1"
|
|
onClick={(e) =>
|
|
e.target.classList.contains("modal") && setShowDeleteModal(false)
|
|
}
|
|
>
|
|
<div className="modal-dialog modal-dialog-centered">
|
|
<div className="modal-content">
|
|
|
|
<div className="modal-header bg-danger bg-opacity-25">
|
|
<h5 className="modal-title text-danger">
|
|
Confirmação de Cancelamento
|
|
</h5>
|
|
<button
|
|
type="button"
|
|
className="btn-close"
|
|
onClick={() => setShowDeleteModal(false)}
|
|
></button>
|
|
</div>
|
|
|
|
<div className="modal-body">
|
|
<p className="mb-0 fs-5">
|
|
Qual o motivo do cancelamento?
|
|
</p>
|
|
<div className='campo-de-input'>
|
|
|
|
<textarea className='input-modal' value={motivoCancelamento} onChange={(e) => setMotivoCancelamento(e.target.value)} />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="modal-footer">
|
|
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary"
|
|
onClick={() => {setShowDeleteModal(false);
|
|
|
|
}}
|
|
>
|
|
Cancelar
|
|
</button>
|
|
|
|
|
|
<button
|
|
type="button"
|
|
className="btn btn-danger"
|
|
onClick={() => {
|
|
|
|
deleteConsulta(selectedID)
|
|
setShowDeleteModal(false)
|
|
let lista_cores = coresConsultas
|
|
|
|
let lista = listaConsultasID
|
|
|
|
lista.push(selectedID)
|
|
lista_cores.push("cancelled")
|
|
|
|
setCoresConsultas(lista_cores)
|
|
|
|
setListaConsultaID(lista)
|
|
|
|
console.log("lista", lista)
|
|
|
|
}}
|
|
|
|
>
|
|
<i className="bi bi-trash me-1"></i> Excluir
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>)}
|
|
|
|
{showConfirmModal &&(
|
|
<div
|
|
className="modal fade show"
|
|
style={{
|
|
display: "block",
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
}}
|
|
tabIndex="-1"
|
|
onClick={(e) =>
|
|
e.target.classList.contains("modal") && setShowDeleteModal(false)
|
|
}
|
|
>
|
|
<div className="modal-dialog modal-dialog-centered">
|
|
<div className="modal-content">
|
|
|
|
<div className="modal-header bg-success">
|
|
<h5 className="modal-title">
|
|
Confirmação de edição
|
|
</h5>
|
|
|
|
</div>
|
|
|
|
<div className="modal-body">
|
|
<p className="mb-0 fs-5">
|
|
Tem certeza que deseja retirar o cancelamento ?
|
|
</p>
|
|
</div>
|
|
|
|
<div className="modal-footer">
|
|
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary"
|
|
onClick={() => {setShowConfirmModal(false); setSelectedId("")}}
|
|
>
|
|
Cancelar
|
|
</button>
|
|
|
|
|
|
<button
|
|
type="button"
|
|
className="btn btn-success"
|
|
onClick={() => {confirmConsulta(selectedID);setShowConfirmModal(false)
|
|
let lista_cores = coresConsultas
|
|
|
|
let lista = listaConsultasID
|
|
|
|
lista.push(selectedID)
|
|
lista_cores.push("confirmed")
|
|
|
|
setCoresConsultas(lista_cores)
|
|
|
|
setListaConsultaID(lista)
|
|
}}
|
|
|
|
>
|
|
<i className="bi bi-trash me-1"></i> Confirmar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>)
|
|
|
|
|
|
|
|
}
|
|
</div>
|
|
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ConsultasPaciente; |