Mergin com TableMelhorias

This commit is contained in:
jp-lima 2025-09-18 12:32:27 -03:00
commit 98f076a030
3 changed files with 283 additions and 6264 deletions

6151
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from "react";
import DoctorList from '../components/doctors/DoctorList';
import DoctorForm from '../components/doctors/DoctorForm';
function TableDoctor({ setCurrentPage, setPatientID }) { function TableDoctor({ setCurrentPage, setPatientID }) {
const [pacientes, setPacientes] = useState([]); const [medicos, setMedicos] = useState([]);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [filtroAniversariante, setFiltroAniversariante] = useState(false);
// Função para excluir médicos // Função para excluir médicos
const deletePatient = async (id) => { const deleteDoctor = async (id) => {
const requestOptionsDelete = { method: "DELETE", redirect: "follow" }; const requestOptionsDelete = { method: "DELETE", redirect: "follow" };
if (!window.confirm("Tem certeza que deseja excluir este médico?")) return; if (!window.confirm("Tem certeza que deseja excluir este médico?")) return;
@ -21,46 +20,35 @@ function TableDoctor({ setCurrentPage, setPatientID }) {
.catch((error) => console.log("Deu problema", error)); .catch((error) => console.log("Deu problema", error));
}; };
const onChange = (e, id) => { // Função para verificar se hoje é aniversário
let value = e.target.value; const ehAniversariante = (dataNascimento) => {
if (!dataNascimento) return false;
const hoje = new Date();
const nascimento = new Date(dataNascimento);
if (value === "verdetalhes") { return (
setCurrentPage("details-page-doctor"); hoje.getDate() === nascimento.getDate() &&
} hoje.getMonth() === nascimento.getMonth()
);
if (value === "editar") {
setCurrentPage("edit-page-doctor");
setPatientID(id);
}
if (value === "excluir") {
console.log(`Excluir ${id}`);
deletePatient(id);
}
};
var requestOptions = {
method: "GET",
redirect: "follow",
}; };
// Buscar médicos da API
useEffect(() => { useEffect(() => {
fetch( fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes")
"https://mock.apidog.com/m1/1053378-0-default/pacientes",
requestOptions
)
.then((response) => response.json()) .then((response) => response.json())
.then((result) => setPacientes(result["data"])) .then((result) => setMedicos(result["data"]))
.catch((error) => .catch((error) =>
console.log("Erro para encontrar médicos no banco de dados", error) console.log("Erro para encontrar médicos no banco de dados", error)
); );
}, []); }, []);
// Filtrar médicos pelo campo de pesquisa (nome, cpf, email, telefone) // Filtrar médicos pelo campo de pesquisa e aniversariantes
const pacientesFiltrados = pacientes.filter((paciente) => const medicosFiltrados = medicos.filter(
`${paciente.nome} ${paciente.cpf} ${paciente.email} ${paciente.telefone}` (medico) =>
.toLowerCase() `${medico.nome} ${medico.cpf} ${medico.email} ${medico.telefone}`
.includes(search.toLowerCase()) .toLowerCase()
.includes(search.toLowerCase()) &&
(filtroAniversariante ? ehAniversariante(medico.data_nascimento) : true)
); );
return ( return (
@ -72,29 +60,42 @@ function TableDoctor({ setCurrentPage, setPatientID }) {
<section className="row"> <section className="row">
<div className="col-12"> <div className="col-12">
<div className="card"> <div className="card">
{/* Header com título e botão alinhados */}
<div className="card-header d-flex justify-content-between align-items-center"> <div className="card-header d-flex justify-content-between align-items-center">
<h4 className="card-title mb-0">Médicos Cadastrados</h4> <h4 className="card-title mb-0">Médicos Cadastrados</h4>
<button <button
className="btn btn-primary" className="btn btn-primary"
onClick={() => setCurrentPage("form-layout")} onClick={() => setCurrentPage("form-layout")}
> >
<i className="bi bi-plus-circle"></i> Adicionar Médico <i className="bi bi-plus-circle"></i> Adicionar Médico
</button> </button>
</div> </div>
<div className="card-body"> <div className="card-body">
{/* Barra de pesquisa abaixo do título */}
<div className="mb-3"> <div className="d-flex gap-2 mb-3">
<input <input
type="text" type="text"
placeholder="Pesquisar médico..." placeholder="Pesquisar médico..."
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
className="form-control" className="form-control"
style={{ maxWidth: "300px" }}
/> />
<button
className={`btn ${
filtroAniversariante
? "btn-primary"
: "btn-outline-primary"
}`}
onClick={() => setFiltroAniversariante(!filtroAniversariante)}
>
<i className="bi bi-calendar me-1"></i> Aniversariantes
</button>
</div> </div>
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-striped table-hover"> <table className="table table-striped table-hover">
<thead> <thead>
@ -103,43 +104,80 @@ function TableDoctor({ setCurrentPage, setPatientID }) {
<th>CPF</th> <th>CPF</th>
<th>Email</th> <th>Email</th>
<th>Telefone</th> <th>Telefone</th>
<th>Opções</th> <th></th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{pacientesFiltrados.length > 0 ? ( {medicosFiltrados.length > 0 ? (
pacientesFiltrados.map((paciente) => ( medicosFiltrados.map((medico) => (
<tr key={paciente.id}> <tr key={medico.id}>
<td>{paciente.nome}</td> <td>{medico.nome}</td>
<td>{paciente.cpf}</td> <td>{medico.cpf}</td>
<td>{paciente.email}</td> <td>{medico.email}</td>
<td>{paciente.telefone}</td> <td>{medico.telefone}</td>
<td> <td>
<span <span
className={`badge ${ className={`badge ${
paciente.ativo === "ativo" medico.ativo === "ativo"
? "bg-success" ? "bg-success"
: "bg-danger" : "bg-danger"
}`} }`}
> >
{paciente.ativo} {medico.ativo}
</span> </span>
</td> </td>
<td> <td>
<select onChange={(e) => onChange(e, paciente.id)}> <div className="d-flex gap-2">
<option value="">:</option>
<option value="verdetalhes">Ver detalhes</option> <button
<option value="editar">Editar</option> className="btn btn-sm"
<option value="excluir">Excluir</option> style={{
</select> backgroundColor: "#E6F2FF",
color: "#004085",
}}
onClick={() => {
setCurrentPage("details-page-paciente");
setPatientID(medico.id);
}}
>
<i className="bi bi-eye me-1"></i> Ver
Detalhes
</button>
<button
className="btn btn-sm"
style={{
backgroundColor: "#FFF3CD",
color: "#856404",
}}
onClick={() => {
setCurrentPage("edit-page-paciente");
setPatientID(medico.id);
}}
>
<i className="bi bi-pencil me-1"></i> Editar
</button>
<button
className="btn btn-sm"
style={{
backgroundColor: "#F8D7DA",
color: "#721C24",
}}
onClick={() => deleteDoctor(medico.id)}
>
<i className="bi bi-trash me-1"></i> Excluir
</button>
</div>
</td> </td>
</tr> </tr>
)) ))
) : ( ) : (
<tr> <tr>
<td colSpan="6" className="text-center"> <td colSpan="6" className="text-center">
Nenhum paciente encontrado. Nenhum médico encontrado.
</td> </td>
</tr> </tr>
)} )}
@ -154,4 +192,5 @@ function TableDoctor({ setCurrentPage, setPatientID }) {
</> </>
); );
} }
export default TableDoctor;
export default TableDoctor;

View File

@ -1,10 +1,11 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from "react";
import PatientList from '../components/patients/PatientList';
import PatientForm from '../components/patients/PatientForm';
function TablePaciente({ setCurrentPage, setPatientID }) { function TablePaciente({ setCurrentPage, setPatientID }) {
const [pacientes, setPacientes] = useState([]); const [pacientes, setPacientes] = useState([]);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [filtroConvenio, setFiltroConvenio] = useState("Todos");
const [filtroVIP, setFiltroVIP] = useState(false);
const [filtroAniversariante, setFiltroAniversariante] = useState(false);
const GetAnexos = async (id) => { const GetAnexos = async (id) => {
@ -84,33 +85,49 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
.catch((error) => console.log("Deu problema", error)); .catch((error) => console.log("Deu problema", error));
}; };
const onChange = (e, id) => { // Função para marcar/desmarcar VIP
let value = e.target.value; const toggleVIP = async (id, atual) => {
const novoStatus = atual === true ? false : true;
if(value === 'verdetalhes'){ await fetch(
setCurrentPage('details-page-paciente') `https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`,
setPatientID(id); {
} method: "PUT",
if(value === 'editar'){ headers: { "Content-Type": "application/json" },
setCurrentPage('edit-page-paciente') body: JSON.stringify({ vip: novoStatus }),
setPatientID(id); }
}
if (value === "excluir") {
deletePatient(id);
console.log(`Excluir ${id}`);
}
};
var requestOptions = {
method: "GET",
redirect: "follow",
};
useEffect(() => {
fetch(
"https://mock.apidog.com/m1/1053378-0-default/pacientes",
requestOptions
) )
.then((response) => response.json())
.then(() => {
setPacientes((prev) =>
prev.map((p) => (p.id === id ? { ...p, vip: novoStatus } : p))
);
})
.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}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ convenio }),
}
)
.then((response) => response.json())
.then(() => {
setPacientes((prev) =>
prev.map((p) => (p.id === id ? { ...p, convenio } : p))
);
})
.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) => response.json())
.then((result) => setPacientes(result["data"])) .then((result) => setPacientes(result["data"]))
.catch((error) => .catch((error) =>
@ -118,12 +135,32 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
); );
}, []); }, []);
// Filtrar pacientes pelo campo de pesquisa (nome, cpf, email, telefone) // Função para verificar se hoje é aniversário do paciente
const pacientesFiltrados = pacientes.filter((paciente) => const ehAniversariante = (dataNascimento) => {
`${paciente.nome} ${paciente.cpf} ${paciente.email} ${paciente.telefone}` if (!dataNascimento) return false;
.toLowerCase() const hoje = new Date();
.includes(search.toLowerCase()) 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 =
filtroConvenio === "Todos" || paciente.convenio === filtroConvenio;
const passaAniversario = filtroAniversariante
? ehAniversariante(paciente.data_nascimento)
: true;
return passaBusca && passaVIP && passaConvenio && passaAniversario;
});
return ( return (
<> <>
@ -134,29 +171,81 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
<section className="row"> <section className="row">
<div className="col-12"> <div className="col-12">
<div className="card"> <div className="card">
{/* Header com título e botão alinhados */}
<div className="card-header d-flex justify-content-between align-items-center"> <div className="card-header d-flex justify-content-between align-items-center">
<h4 className="card-title mb-0">Pacientes Cadastrados</h4> <h4 className="card-title mb-0">Pacientes Cadastrados</h4>
<button <button
className="btn btn-primary" className="btn btn-primary"
onClick={() => setCurrentPage("form-layout")} onClick={() => setCurrentPage("form-layout")}
> >
<i className="bi bi-plus-circle"></i> Adicionar Paciente <i className="bi bi-plus-circle"></i> Adicionar Paciente
</button> </button>
</div> </div>
<div className="card-body"> <div className="card-body">
{/* Barra de pesquisa abaixo do título */} <div className="card p-3 mb-3">
<div className="mb-3"> <h5 className="mb-3">
<input <i className="bi bi-funnel-fill me-2 text-primary"></i> Filtros
type="text" </h5>
placeholder="Pesquisar paciente..."
value={search} <div
onChange={(e) => setSearch(e.target.value)} className="d-flex flex-nowrap align-items-center gap-2"
className="form-control" style={{ overflowX: "auto", paddingBottom: "6px" }}
/> >
<input
type="text"
className="form-control"
placeholder="Buscar por nome..."
value={search}
onChange={(e) => setSearch(e.target.value)}
style={{
minWidth: 250,
maxWidth: 300,
width: 260,
flex: "0 0 auto",
}}
/>
<select
className="form-select"
value={filtroConvenio}
onChange={(e) => setFiltroConvenio(e.target.value)}
style={{
minWidth: 200,
width: 180,
flex: "0 0 auto",
}}
>
<option>Todos os Convênios</option>
<option>Bradesco Saúde</option>
<option>Hapvida</option>
<option>Unimed</option>
</select>
<button
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"
: "btn-outline-primary"
}`}
onClick={() =>
setFiltroAniversariante(!filtroAniversariante)
}
style={{ flex: "0 0 auto", whiteSpace: "nowrap" }}
>
<i className="bi bi-calendar me-1"></i> Aniversariantes
</button>
</div>
</div> </div>
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-striped table-hover"> <table className="table table-striped table-hover">
<thead> <thead>
@ -165,7 +254,6 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
<th>CPF</th> <th>CPF</th>
<th>Email</th> <th>Email</th>
<th>Telefone</th> <th>Telefone</th>
<th>Opções</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -178,29 +266,63 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
<td>{paciente.telefone}</td> <td>{paciente.telefone}</td>
<td> <td>
<span <span
className={`badge ${ className={`badge ${paciente.ativo === "ativo"
paciente.ativo === "ativo" ? "bg-success"
? "bg-success" : "bg-danger"
: "bg-danger" }`}
}`}
> >
{paciente.ativo} {paciente.ativo}
</span> </span>
</td> </td>
<td> <td>
<select onChange={(e) => onChange(e, paciente.id)}> <div className="d-flex gap-2">
<option value=""> </option>
<option value="verdetalhes">Ver detalhes</option> <button
<option value="editar">Editar</option> className="btn btn-sm"
<option value="excluir">Excluir</option> style={{
</select> backgroundColor: "#E6F2FF",
color: "#004085",
}}
onClick={() => {
setCurrentPage("details-page-paciente");
setPatientID(paciente.id);
}}
>
<i className="bi bi-eye me-1"></i> Ver Detalhes
</button>
<button
className="btn btn-sm"
style={{
backgroundColor: "#FFF3CD",
color: "#856404",
}}
onClick={() => {
setCurrentPage("edit-page-paciente");
setPatientID(paciente.id);
}}
>
<i className="bi bi-pencil me-1"></i> Editar
</button>
<button
className="btn btn-sm"
style={{
backgroundColor: "#F8D7DA",
color: "#721C24",
}}
onClick={() => deletePatient(paciente.id)}
>
<i className="bi bi-trash me-1"></i> Excluir
</button>
</div>
</td> </td>
</tr> </tr>
)) ))
) : ( ) : (
<tr> <tr>
<td colSpan="6" className="text-center"> <td colSpan="8" className="text-center">
Nenhum paciente encontrado. Nenhum paciente encontrado.
</td> </td>
</tr> </tr>
@ -216,4 +338,5 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
</> </>
); );
} }
export default TablePaciente;
export default TablePaciente;