Compare commits

...

7 Commits

13 changed files with 6634 additions and 141 deletions

56
et --hard 63659b6 Normal file
View File

@ -0,0 +1,56 @@
3993097 (HEAD -> main) Merge branch 'main' of https://git.popcode.com.br/RiseUP/riseup-squad23
63659b6 Verificação do cpf e colocar o erro 404
ecae83c (riseup/main, riseup/HEAD, origin/main, origin/HEAD) Merge pull request 'Conectando-o-resto-das-API' (#2) from Conectando-o-resto-das-API into main
908d545 (riseup/Conectando-o-resto-das-API, origin/Conectando-o-resto-das-API) feat: adicionar upload e delete de anexos do paciente
4b404c0 Merge branch 'main' of https://git.popcode.com.br/RiseUP/riseup-squad23
bd20c2d Merge remote-tracking branch 'origin/main'
8aeabd1 (riseup/Fix-dos-erros-do-projeto, origin/Fix-dos-erros-do-projeto) FIx: todos os erros que aparecia no console foram resolvidos
0e29e7d melhorias na organização de pastas
98f076a Mergin com TableMelhorias
589d590 Mergin com novas alterações de laudo
7b28e2a Details melhorias
9480edc (riseup/PaginaDetalhes, origin/PaginaDetalhes) Pàgina detalhes
e4515cf Adição das cores nos cards de consulta
d3dd2fd (riseup/TableMelhorias, origin/TableMelhorias) Detalhe nas tabelas
a54b119 Delete Anexos apos pacientes forem excluidos
6e93cb5 atualizar paciente
b9a35be começo do concerto do editar
82469bc Details funcional
cdfe4ea Validação de CPF
57c8f67 (riseup/DetalhesMedico, origin/DetalhesMedico) Detalhes do medico
b021444 Mudanças formularios e detalhes
d5d03b0 (riseup/mudanças-de-laudo, origin/mudanças-de-laudo) atualização do laudo
a502bbd agendamentos no incio
8e1fcd9 Merge branch 'feature/novo-cadastro-paciente'
bea9076 Merge remote-tracking branch 'origin/PaginaDetalhes'
e35f217 mergin branch inicio com main
1af8268 Atualizacão do laudo
725d60d feat: ajeitei o nome
bab85ff (riseup/AgendamentoSidebar, origin/AgendamentoSidebar) Concertar Agendamento
b2707e3 Refatora o estilo do formulário do paciente para uma aparência de cartão com tipografia maior
37e8959 Refatora o estilo do formulário do paciente para uma aparência de cartão com tipografia maior
0930385 feat: uma piquena mudança
f6a19c4 feat: Adiciona formulário de cadastro de paciente
d91b5cf form de agendar consulta melhorado
0a60dd7 Tabela semana e mes
7f07950 (riseup/feature-Melhoria-no-Dashboard, origin/feature-Melhoria-no-Dashboard) feat: Criação da página início e melhoria na navegação
39e25ad Pagina de detalhes atualizada
4f84791 pequenas mudanaças na tabela de semana e mes
6737955 form para nova consulta e tabelas de horario
26ded17 Nova pagina de detalhes
874de84 Inicio do agendamento
f3e7470 (riseup/gerenciamento-de-laudo, origin/gerenciamento-de-laudo) Laudo do Paciente
709cd4e Merge finalizado
d6b3e86 Merge detalhes-do-pacientes para main
08ffa55 Merge remote-tracking branch 'origin/CrudMedico'
70c4d5f Termino da organização
edd567d Inicio da organização
9c09113 Mudanças pos feedback de davi
aa3a5fa Criação da página dos detalhes dos pacientes
5534568 Inicio de detalhes e atualização do paciente
06ff7d5 Funcionalidade de delete e botão de opções
5b63fa2 Mascara telefones
fb9d783 adição da mascara do CPF
a489d84 metodo GET e POST
4eaabbd first commit
a244691 Initial commit

6205
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
"react-icons": "^5.5.0",
"react-input-mask": "^2.0.4",
"react-quill": "^2.0.0",
"react-router-dom": "^7.9.2",
"react-scripts": "5.0.1",
"recharts": "^3.1.2",
"sweetalert2": "^11.22.4",

View File

@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- Mazer CSS -->
@ -21,8 +20,8 @@
<div id="root"></div>
<!-- Mazer JS -->
<script src="%PUBLIC_URL%/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script>
<!-- <script src="%PUBLIC_URL%/vendors/perfect-scrollbar/perfect-scrollbar.min.js"></script>
<script src="%PUBLIC_URL%/js/bootstrap.bundle.min.js"></script>
<script src="%PUBLIC_URL%/js/main.js"></script>
<script src="%PUBLIC_URL%/js/main.js"></script> -->
</body>
</html>

View File

@ -6,16 +6,6 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",

View File

@ -1,8 +1,53 @@
import React from "react";
import React, { useState, useEffect } from "react";
import InputMask from "react-input-mask";
import "./style/styleagendamentos.css";
const FormNovaConsulta = ({ onCancel }) => {
const FormNovaConsulta = ({ onCancel, patientID }) => {
const [selectedFile, setSelectedFile] = useState(null);
const [anexos, setAnexos] = useState([]);
const [loadingAnexos, setLoadingAnexos] = useState(false);
useEffect(() => {
if (!patientID) return;
const fetchAnexos = async () => {
setLoadingAnexos(true);
try {
const res = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos`);
const data = await res.json();
setAnexos(data.data || []);
} catch (err) {
console.error("Erro ao buscar anexos:", err);
} finally {
setLoadingAnexos(false);
}
};
fetchAnexos();
}, [patientID]);
const handleUpload = async () => {
if (!selectedFile) return;
const formData = new FormData();
formData.append("file", selectedFile);
try {
const res = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos`, {
method: "POST",
body: formData
});
if (res.ok) {
const novoAnexo = await res.json();
setAnexos(prev => [...prev, novoAnexo]);
setSelectedFile(null);
} else {
console.error("Erro ao enviar anexo");
}
} catch (err) {
console.error("Erro ao enviar anexo:", err);
}
};
const handleSubmit = (e) => {
e.preventDefault();
alert("Agendamento salvo!");
@ -55,7 +100,29 @@ const FormNovaConsulta = ({ onCancel }) => {
<h3 className="section-subtitle">Informações adicionais</h3>
<button type="button" className="btn-secondary">Documentos e anexos</button>
<label htmlFor="anexo-input" className="btn btn-secondary">Adicionar Anexo</label>
<input
type="file"
id="anexo-input"
className="d-none"
onChange={(e) => setSelectedFile(e.target.files[0])}
/>
{selectedFile && (
<button type="button" className="btn btn-primary ms-2" onClick={handleUpload}>
Enviar
</button>
)}
<div className="anexos-list">
{loadingAnexos ? (
<p>Carregando anexos...</p>
) : (
anexos.map((anexo, index) => (
<div key={index} className="anexo-item">
<span>{anexo.nome || anexo.fileName}</span>
</div>
))
)}
</div>
<h2 className="section-title">Informações do atendimento</h2>
<label>Nome do profissional *</label>

View File

@ -56,7 +56,7 @@ function Sidebar(props) {
props.setCurrentPage('dashboard');
}}
>
<hi>MediConnect</hi>
<h1>MediConnect</h1>
</a>
</div>
<div className="toggler">

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import InputMask from "react-input-mask";
function DoctorForm({ onSave, onCancel, PatientDict }) {

View File

@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react';
function PatientForm({ onSave, onCancel,formData, setFormData }) {
function PatientForm({ onSave, onCancel, formData, setFormData }) {
const [showModal, setShowModal] = useState(false);
const [showModal404, setShowModal404] = useState(false);
const [pacienteExistente, setPacienteExistente] = useState(null);
const FormatTelefones = (valor) => {
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
@ -29,29 +31,63 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
};
const response = await fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes/validar-cpf", requestOptions)
if (!response.ok) {
throw new Error('Erro na API de validação de CPF. Status: ' + response.status);
}
const result = await response.json()
return result.data
}
const BuscarPacientePorId = async (id) => {
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
try {
const response = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOptions);
const result = await response.json();
return result.data;
} catch (error) {
console.error("Erro ao buscar paciente por ID:", error);
return null;
}
};
const ValidarCPF = async (cpf) => {
let aviso
let Erro = false
const resutadoAPI = await ReceberRespostaAPIdoCPF(cpf)
try {
const resultadoAPI = await ReceberRespostaAPIdoCPF(cpf)
const valido = resultadoAPI.valido
const ExisteNoBancoDeDados = resultadoAPI.existe
const idPaciente = resultadoAPI.id_paciente
const valido = resutadoAPI.valido
const ExisteNoBancoDeDados = resutadoAPI.existe
if(valido === false){
aviso = 'CPF inválido'
Erro = true
}
else if(ExisteNoBancoDeDados === true){
aviso = 'O CPF informado já está presente no sistema'
Erro = true
if(valido === false){
aviso = 'CPF inválido'
Erro = true
}
else if(ExisteNoBancoDeDados === true){
const paciente = await BuscarPacientePorId(idPaciente);
if (paciente) {
setPacienteExistente(paciente);
setShowModal(true);
}
Erro = true
}
} catch (error) {
console.error("Erro na validação do CPF:", error);
setShowModal404(true);
Erro = true;
}
return [Erro,aviso]
}
@ -70,7 +106,7 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
// Estado para controlar quais seções estão colapsadas
const [collapsedSections, setCollapsedSections] = useState({
dadosPessoais: true, // Alterado para true para a seção ficar aberta por padrão
dadosPessoais: true,
infoMedicas: false,
infoConvenio: false,
endereco: false,
@ -125,23 +161,21 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
reader.readAsDataURL(files[0]);
} else if (name === 'foto' && !files[0]) {
setAvatarUrl(null); // Limpa o avatar se nenhum arquivo for selecionado
}}
}}
if (name.includes('cpf')) {
setFormData({...formData, cpf:FormatCPF(value) });
} else if (name.includes('telefone')) {
let telefoneFormatado = FormatTelefones(value);
setContato(prev => ({ ...prev, [name]: telefoneFormatado }));
}else if (name === 'email') {
setContato(prev => ({ ...prev, email: value }));
setContato(prev => ({ ...prev, email: value }));
}else if(name.includes('endereco')) {
setEnderecoData(prev => ({ ...prev, [name.split('.')[1]]: value }));
}else{
setFormData({ ...formData, [name]: value });
}
}
};
const handleCepBlur = async () => {
@ -173,14 +207,11 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
return;
}
const CPFinvalido = await ValidarCPF(formData.cpf)
console.log(CPFinvalido)
if(CPFinvalido[0] === true){
alert(CPFinvalido[1])
return
const [CPFinvalido] = await ValidarCPF(formData.cpf);
if(CPFinvalido === true){
return;
}
onSave({
...formData,
endereco: {
@ -395,7 +426,7 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
<div className="col-md-12 mb-3">
<label style={{ fontSize: '1.1rem' }}>Anexos do Paciente:</label>
<div>
<label htmlFor="anexos-input" className="btn btn-secondary" style={{ fontSize: '1.1rem' }}>Escolher arquivo</label>
<label htmlFor="anexos-input" className="btn btn-secondary" style={{ fontSize: '1.1rem', background: '#9ca3af' }}>Escolher arquivo</label>
<input type="file" className="form-control d-none" name="anexos" id="anexos-input" onChange={handleChange} />
<span className="ms-2" style={{ fontSize: '1.1rem' }}>{formData.anexos ? formData.anexos.name : 'Nenhum arquivo escolhido'}</span>
</div>
@ -583,6 +614,77 @@ function PatientForm({ onSave, onCancel,formData, setFormData }) {
Cancelar
</button>
</div>
{/* Modal para paciente existente */}
{showModal && pacienteExistente && (
<div className="modal" style={{ display: 'block', backgroundColor: 'rgba(0,0,0,0.5)' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Ops! Este CPF está cadastrado</h5>
<button type="button" className="btn-close" onClick={() => setShowModal(false)}></button>
</div>
<div className="modal-body">
<div className="text-center mb-3">
<img
src={pacienteExistente.foto || 'https://via.placeholder.com/100'}
alt="Foto do Paciente"
className="rounded-circle"
style={{ width: '100px', height: '100px', objectFit: 'cover' }}
/>
</div>
<p><strong>ID do Paciente:</strong> {pacienteExistente.id}</p>
<p><strong>Nome Completo:</strong> {pacienteExistente.nome}</p>
<p><strong>CPF:</strong> {pacienteExistente.cpf}</p>
<p><strong>Data de Nascimento:</strong> {pacienteExistente.data_nascimento}</p>
<p><strong>Telefone:</strong> {pacienteExistente.contato.telefone1}</p>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-primary"
onClick={() => setShowModal(false)}
>
Fechar e Continuar no Cadastro
</button>
<button
type="button"
className="btn btn-primary"
onClick={() => {
alert(`Navegando para os detalhes do paciente ID: ${pacienteExistente.id}`);
setShowModal(false);
}}
>
Visualizar Paciente Existente
</button>
</div>
</div>
</div>
</div>
)}
{/* Erro 404 */}
{showModal404 && (
<div className="modal" style={{ display: 'block', backgroundColor: 'rgba(0,0,0,0.5)' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header bg-danger text-white">
<h5 className="modal-title">Erro de Validação</h5>
<button type="button" className="btn-close btn-close-white" onClick={() => setShowModal404(false)}></button>
</div>
<div className="modal-body">
<p style={{ fontSize: '1.4rem' }}>(Erro 404).Por favor,tente novamente mais tarde.</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-primary" onClick={() => setShowModal404(false)}>
Fechar
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
}

View File

@ -1,12 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOM from 'react-dom/client';
import './assets/scss/bootstrap.scss';
import './assets/scss/app.scss';
import App from './App';
ReactDOM.render(
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

View File

@ -3,17 +3,69 @@ import avatarPlaceholder from '../assets/images/avatar_placeholder.png';
const Details = ({ patientID, setCurrentPage }) => {
const [paciente, setPaciente] = useState({});
const [anexos, setAnexos] = useState([]);
const [selectedFile, setSelectedFile] = useState(null);
useEffect(() => {
if (!patientID) return;
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}`)
.then(res => res.json())
.then(result => {setPaciente(result.data)})
.catch(err => console.error("Erro ao buscar paciente:", err));
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos`)
.then(res => res.json())
.then(data => setAnexos(data.data || []))
.catch(err => console.error("Erro ao buscar anexos:", err));
}, [patientID]);
const handleUpload = async () => {
if (!selectedFile) return;
const formData = new FormData();
formData.append('file', selectedFile);
try {
const response = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos`, {
method: 'POST',
body: formData,
});
if (response.ok) {
const newAnexo = await response.json();
setAnexos(prev => [...prev, newAnexo]);
setSelectedFile(null);
} else {
console.error('Erro ao enviar anexo');
}
} catch (err) {
console.error('Erro ao enviar anexo:', err);
}
};
const handleDelete = async (anexoId) => {
try {
const response = await fetch(
`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos/${anexoId}`,
{
method: "DELETE",
}
);
if (response.ok) {
setAnexos((prev) => prev.filter((a) => a.id !== anexoId));
} else {
console.error("Erro ao deletar anexo");
}
} catch (err) {
console.error("Erro ao deletar anexo:", err);
}
};
return (
@ -137,7 +189,29 @@ const Details = ({ patientID, setCurrentPage }) => {
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Anexos do Paciente:</label>
<p>{ "-"}</p>
{anexos.length > 0 ?(
<ul>
{anexos.map((anexo) => (
<li key={anexo.id} className="d-flex aling-items-center">
<a href={anexo.url} target="-blank" rel="noopener noreferrer">
{anexo.nome}
</a>
<button className="btn btn-danger btn-sm" onclick={() => handleDelete(anexo.id)} >Remover</button>
</li>
))}
</ul>
) : (
<p>-</p>
)}
</div>
<div className="col-md-6 mb-3">
<label htmlFor="foto-input" className="btn btn-primary" style={{ fontSize: '1rem' }}>
Carregar Um Novo Anexo
</label>
<input
type="file" className="form-control d-none" name="foto" id="foto-input" onChange={(e) => setSelectedFile(e.target.files[0])} accept="image/*"/>
{selectedFile && <span className="ms-2" style={{ fontSize: '1rem' }}>{selectedFile.name}</span>}
<button onClick={handleUpload} className="btn btn-success ms-2">Enviar</button>
</div>
</div>
</div>

View File

@ -18,13 +18,14 @@ function Inicio({ setCurrentPage }) {
};
const fetchAgendamentos = async () => {
try {
const res = await fetch();
const data = await res.json();
setAgendamentos(data.data);
} catch (error) {
console.error("Erro ao buscar agendamentos:", error);
}
return; // <===serve para que nao cause erro
// try {
// const res = await fetch();
// const data = await res.json();
// setAgendamentos(data.data);
// } catch (error) {
// console.error("Erro ao buscar agendamentos:", error);
// }
};
fetchPacientes();

View File

@ -7,88 +7,83 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
const [filtroVIP, setFiltroVIP] = useState(false);
const [filtroAniversariante, setFiltroAniversariante] = useState(false);
// Estado para controlar a exibição do erro 404
const [showError404, setShowError404] = useState(false);
const GetAnexos = async (id) => {
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
try {
const response = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}/anexos`, requestOptions);
const result = await response.json();
return result.data; // agora retorna corretamente
} catch (error) {
console.log('error', error);
return [];
}
}
const DeleteAnexo = async (patientID) => {
const RespostaGetAnexos = await GetAnexos(patientID)
for(let i = 0; i < RespostaGetAnexos.length; i++){
const idAnexo = RespostaGetAnexos[i].id;
console.log('anexos',RespostaGetAnexos)
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
var requestOptions = {
method: 'DELETE',
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos/${idAnexo}`, requestOptions)
.then(response => response.text())
.then(result => console.log('anexo excluido com sucesso',result))
.catch(error => console.log('error', error));
try {
const response = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}/anexos`, requestOptions);
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000); // Esconde a mensagem após 5 segundos
throw new Error('Erro 404');
}
const result = await response.json();
return result.data;
} catch (error) {
console.log('error', error);
return [];
}
}
const DeleteAnexo = async (patientID) => {
const RespostaGetAnexos = await GetAnexos(patientID);
for (let i = 0; i < RespostaGetAnexos.length; i++) {
const idAnexo = RespostaGetAnexos[i].id;
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
var requestOptions = {
method: 'DELETE',
headers: myHeaders,
redirect: 'follow'
};
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}/anexos/${idAnexo}`, requestOptions)
.then(response => {
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000); // Esconde a mensagem após 5 segundos
throw new Error('Erro 404');
}
return response.text();
})
.then(result => console.log('anexo excluido com sucesso', result))
.catch(error => console.log('error', error));
}
}
// Função para excluir paciente
const deletePatient = async (id) => {
DeleteAnexo(id)
DeleteAnexo(id);
const requestOptionsDelete = { method: "DELETE", redirect: "follow" };
if (!window.confirm("Tem certeza que deseja excluir este paciente?")) return;
await fetch(
`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`,
requestOptionsDelete
)
.then((response) => response.text())
.then((response) => {
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000); // Esconde a mensagem após 5 segundos
throw new Error('Erro 404');
}
return response.text();
})
.then((mensage) => console.log(mensage))
.catch((error) => console.log("Deu problema", error));
};
// Função para marcar/desmarcar VIP
const toggleVIP = async (id, atual) => {
const novoStatus = atual === true ? false : true;
await fetch(
`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`,
{
@ -97,7 +92,14 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
body: JSON.stringify({ vip: novoStatus }),
}
)
.then((response) => response.json())
.then((response) => {
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000);
throw new Error('Erro 404');
}
return response.json();
})
.then(() => {
setPacientes((prev) =>
prev.map((p) => (p.id === id ? { ...p, vip: novoStatus } : p))
@ -106,7 +108,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
.catch((error) => console.log("Erro ao atualizar VIP:", error));
};
// Função para atualizar convênio/particular
const updateConvenio = async (id, convenio) => {
await fetch(
`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`,
@ -116,7 +117,14 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
body: JSON.stringify({ convenio }),
}
)
.then((response) => response.json())
.then((response) => {
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000);
throw new Error('Erro 404');
}
return response.json();
})
.then(() => {
setPacientes((prev) =>
prev.map((p) => (p.id === id ? { ...p, convenio } : p))
@ -125,32 +133,34 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
.catch((error) => console.log("Erro ao atualizar convênio:", error));
};
// Requisição inicial para buscar pacientes
useEffect(() => {
fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes")
.then((response) => response.json())
.then((response) => {
if (!response.ok) {
setShowError404(true);
setTimeout(() => setShowError404(false), 5000);
throw new Error('Erro 404');
}
return response.json();
})
.then((result) => setPacientes(result["data"]))
.catch((error) =>
console.log("Erro para encontrar pacientes no banco de dados", error)
);
}, []);
// Função para verificar se hoje é aniversário do paciente
const ehAniversariante = (dataNascimento) => {
if (!dataNascimento) return false;
const hoje = new Date();
const nascimento = new Date(dataNascimento);
return (
hoje.getDate() === nascimento.getDate() &&
hoje.getMonth() === nascimento.getMonth()
);
};
const pacientesFiltrados = pacientes.filter((paciente) => {
const texto = `${paciente.nome}`.toLowerCase();
const passaBusca = texto.includes(search.toLowerCase());
const passaVIP = filtroVIP ? paciente.vip === true : true;
const passaConvenio =
@ -158,7 +168,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
const passaAniversario = filtroAniversariante
? ehAniversariante(paciente.data_nascimento)
: true;
return passaBusca && passaVIP && passaConvenio && passaAniversario;
});
@ -182,11 +191,15 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
</div>
<div className="card-body">
{showError404 && (
<div className="alert alert-danger" role="alert">
(Erro 404). Por favor, tente novamente mais tarde.
</div>
)}
<div className="card p-3 mb-3">
<h5 className="mb-3">
<i className="bi bi-funnel-fill me-2 text-primary"></i> Filtros
</h5>
<div
className="d-flex flex-nowrap align-items-center gap-2"
style={{ overflowX: "auto", paddingBottom: "6px" }}
@ -204,7 +217,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
flex: "0 0 auto",
}}
/>
<select
className="form-select"
value={filtroConvenio}
@ -220,16 +232,13 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
<option>Hapvida</option>
<option>Unimed</option>
</select>
<button
className={`btn ${filtroVIP ? "btn-primary" : "btn-outline-primary"
}`}
className={`btn ${filtroVIP ? "btn-primary" : "btn-outline-primary"}`}
onClick={() => setFiltroVIP(!filtroVIP)}
style={{ flex: "0 0 auto", whiteSpace: "nowrap" }}
>
<i className="bi bi-award me-1"></i> VIP
</button>
<button
className={`btn ${filtroAniversariante
? "btn-primary"
@ -244,8 +253,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
</button>
</div>
</div>
<div className="table-responsive">
<table className="table table-striped table-hover">
<thead>
@ -254,6 +261,8 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
<th>CPF</th>
<th>Email</th>
<th>Telefone</th>
<th>Status</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
@ -276,7 +285,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
</td>
<td>
<div className="d-flex gap-2">
<button
className="btn btn-sm"
style={{
@ -290,8 +298,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
>
<i className="bi bi-eye me-1"></i> Ver Detalhes
</button>
<button
className="btn btn-sm"
style={{
@ -305,7 +311,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
>
<i className="bi bi-pencil me-1"></i> Editar
</button>
<button
className="btn btn-sm"
style={{
@ -322,7 +327,7 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
))
) : (
<tr>
<td colSpan="8" className="text-center">
<td colSpan="6" className="text-center">
Nenhum paciente encontrado.
</td>
</tr>
@ -339,4 +344,4 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
);
}
export default TablePaciente;
export default TablePaciente;