Merge remote-tracking branch 'origin/main'

This commit is contained in:
Eduarda-SS 2025-09-21 19:01:40 -03:00
commit bd20c2d811
38 changed files with 3981 additions and 7920 deletions

6334
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,11 @@
"apexcharts": "^5.3.4", "apexcharts": "^5.3.4",
"bootstrap": "^5.3.8", "bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1", "bootstrap-icons": "^1.13.1",
"dayjs": "^1.11.18",
"flatpickr": "^4.6.13", "flatpickr": "^4.6.13",
"lucide-react": "^0.543.0",
"perfect-scrollbar": "^1.5.6", "perfect-scrollbar": "^1.5.6",
"powershell": "^2.3.3",
"quill": "^2.0.3", "quill": "^2.0.3",
"rater-js": "^1.0.1", "rater-js": "^1.0.1",
"react": "^18.2.0", "react": "^18.2.0",
@ -22,6 +25,7 @@
"react-bootstrap": "^2.10.10", "react-bootstrap": "^2.10.10",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-flatpickr": "^4.0.11", "react-flatpickr": "^4.0.11",
"react-icons": "^5.5.0",
"react-input-mask": "^2.0.4", "react-input-mask": "^2.0.4",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@ -55,7 +59,10 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"sass": "^1.91.0", "sass": "^1.91.0",
"sass-loader": "^16.0.5" "sass-loader": "^16.0.5",
"tailwindcss": "^4.1.13"
} }
} }

View File

@ -1,101 +1,62 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import Sidebar from './components/Sidebar'; import Sidebar from './components/Sidebar';
import Header from './components/Header'; //import Header from './components/Header';
import Table from "./pages/Table"; // <-- ADIÇÃO AQUI import Table from "./pages/Table";
import DataTable from "./pages/DataTable";
import Files from "./pages/files"; import Inicio from './pages/Inicio';
import EmailApp from "./pages/EmailApp"; import PatientCadastroManager from './pages/PatientCadastroManager';
//import ChatApp from "./pages/ChatApp";
import GalleryApp from "./pages/GalleryApp";
import FormLayout from './pages/FormLayout';
import EditPage from './pages/EditPage'; import EditPage from './pages/EditPage';
import PatientForm from "./components/patients/PatientForm"; import DoctorEditPage from './pages/DoctorEditPage';
import Details from './pages/Details'; import Details from './pages/Details';
//import DoctorEditPage from './components/doctors/DoctorEditPage'; import DoctorDetails from './pages/DoctorDetails';
import DoctorTable from './pages/DoctorTable'; import DoctorTable from './pages/DoctorTable';
import DoctorFormLayout from './pages/DoctorFormLayout'; import DoctorCadastroManager from './pages/DoctorCadastroManager';
import Agendamento from './pages/Agendamento'
import LaudoManager from "./pages/LaudoManager";
function App() { function App() {
const [isSidebarActive, setIsSidebarActive] = useState(true); const [isSidebarActive] = useState(true);
const [currentPage, setCurrentPage] = useState('table '); const [currentPage, setCurrentPage] = useState('Inicio');
const [patientID, setPatientID] = useState(null);
const [patientID, setPatientID] = useState(null)
const renderPageContent = () => {
switch (currentPage) {
const toggleSidebar = () => { case 'Inicio':
setIsSidebarActive(!isSidebarActive); return <Inicio setCurrentPage={setCurrentPage} />;
}; case 'agendamento':
return <Agendamento/>;
const renderPageContent = () => { case 'form-layout':
if (currentPage === 'form-layout') { return <PatientCadastroManager setCurrentPage={setCurrentPage}/>;
return <FormLayout/>; case 'doctor-form-layout':
} return <DoctorCadastroManager />;
else if(currentPage === 'doctor-form-layout'){ case 'table':
return <DoctorFormLayout/> return <Table setCurrentPage={setCurrentPage} setPatientID={setPatientID} />;
} case 'doctor-table':
else if (currentPage === 'table') { return <DoctorTable setCurrentPage={setCurrentPage} setPatientID={setPatientID} />;
return <Table setCurrentPage={setCurrentPage} setPatientID={setPatientID}/>; case 'details-page-paciente':
} return <Details patientID={patientID} setCurrentPage={setCurrentPage} />;
else if(currentPage === 'doctor-table'){ case 'details-page-doctor':
return <DoctorTable setCurrentPage={setCurrentPage} setPatientID={setPatientID}/> return <DoctorDetails patientID={patientID} setCurrentPage={setCurrentPage} />;
} case 'edit-page-paciente':
else if (currentPage === 'data-table') { return <EditPage id={patientID} />;
return <DataTable />; case 'edit-page-doctor':
} return <DoctorEditPage id={patientID} />;
else if (currentPage === 'files') { case 'laudo-manager':
return <Files />; return <LaudoManager />;
} default:
else if (currentPage === 'email-app') { return <Inicio setCurrentPage={setCurrentPage} />;
return <EmailApp />; }
}
//else if (currentPage === 'chat-app') {
// return <ChatApp />;
//}
else if (currentPage === 'gallery-app') {
return <GalleryApp />;
}
else if(currentPage === 'edit-page-paciente'){
return <EditPage id={patientID} />
}
// else if(currentPage === 'doctor-form-layout'){
// return <DoctorEditPage id={patientID} />
//}
else if(currentPage === 'details-page-paciente'){
return <Details patientID={patientID} setCurrentPage={setCurrentPage} />;
}
// Dashboard por padrão
return (
<>
<div className="page-heading">
<h3>Profile Statistics</h3>
</div>
<div className="page-content">
<section className="row">
<div className="col-12 col-lg-9">
<div className="row">
<div className="col-12 col-md-6 col-lg-8">
</div>
</div>
</div>
<div className="col-12 col-lg-3">
</div>
</section>
</div>
</>
);
}; };
return ( return (
<div id="app" className={isSidebarActive ? 'active' : ''}> <div id="app" className={isSidebarActive ? 'active' : ''}>
<Sidebar isSidebarActive={isSidebarActive} setCurrentPage={setCurrentPage} /> <Sidebar isSidebarActive={isSidebarActive} setCurrentPage={setCurrentPage} />
<div id="main"> <div id="main">
<Header toggleSidebar={toggleSidebar} />
{renderPageContent()} {renderPageContent()}
</div> </div>
</div> </div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,32 @@
import React from 'react'
const CardConsulta = ( {DadosConsulta, TabelaAgendamento} ) => {
// Status (agendado, confirmado, realizado, cancelado)
return (
<div className={`container-cardconsulta-${TabelaAgendamento}`}>
{DadosConsulta.status !== 'vazio'?
<div className='cardconsulta' id={`status-card-consulta-${DadosConsulta.status}`}>
<section className='cardconsulta-infosecundaria'>
<p>{DadosConsulta.horario}|GEAP| {DadosConsulta.medico}</p>
</section>
<section className='cardconsulta-infoprimaria'>
<p>{DadosConsulta.paciente} - {DadosConsulta.motivo} - 23 anos</p>
</section>
</div>
:
null
}
</div>
)
}
export default CardConsulta

View File

@ -0,0 +1,105 @@
// Status (agendado, confirmado, realizado, cancelado)
let AgendamentosMes = {semana1:{
segunda:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
terca:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
quarta:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
quinta:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
sexta:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }]
}, semana2:{segunda:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
terca:[ { horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' },
{ horario: '07:10', status:'vazio' },
{ horario: '07:20', medico: 'Rogerio Cena', paciente: 'Ana Paula', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '07:30', medico: 'Rogerio Cena', paciente: 'Bruno Lima', status: 'realizado', motivo:'Consulta de Rotina' },
{ horario: '07:40', status:'vazio' },
{ horario: '07:50', medico: 'Rogerio Cena', paciente: 'Felipe Duarte', status: 'cancelado', motivo:'Consulta de Rotina' },
{ horario: '08:00', medico: 'Rogerio Cena', paciente: 'Carolina Alves', status: 'confirmado', motivo:'2 Exames' },
{ horario: '08:10', medico: 'Rogerio Cena', paciente: 'Ricardo Gomes', status: 'agendado', motivo:'retorno' },
{ horario: '08:20', medico: 'Rogerio Cena', paciente: 'Tatiane Ramos', status: 'confirmado', motivo: '' },
{ horario: '08:30', medico: 'Rogerio Cena', paciente: 'Daniel Oliveira', status: 'realizado', motivo: '' }],
quarta:[{ horario: '07:10', status:'vazio' }],
quinta:[{ horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' }],
sexta:[{ horario: '07:00', medico: 'Rogerio Cena', paciente: 'Caio Miguel', status: 'confirmado', motivo:'Consulta de Rotina' }]}
}
export default AgendamentosMes

View File

@ -0,0 +1,117 @@
import React from "react";
import InputMask from "react-input-mask";
import "./style/styleagendamentos.css";
const FormNovaConsulta = ({ onCancel }) => {
const handleSubmit = (e) => {
e.preventDefault();
alert("Agendamento salvo!");
};
return (
<div className="form-container">
<form className="form-agendamento" onSubmit={handleSubmit}>
<h2 className="section-title">Informações do paciente</h2>
<div id="informacoes-paciente-primeiralinha">
<label>Nome *</label>
<input type="text" name="nome" placeholder="Insira o nome do paciente" required />
<label>CPF do paciente</label>
<InputMask mask="999.999.999-99" placeholder="000.000.000-00">
{(inputProps) => <input {...inputProps} type="text" name="cpf" />}
</InputMask>
<label>RG</label>
<input type="text" name="rg" placeholder="Insira o nº do RG" maxLength={9} />
</div>
<label>Data de nascimento *</label>
<input type="date" name="data_nascimento" required />
<label>Telefone</label>
<InputMask mask="(99) 99999-9999" placeholder="(99) 99999-9999">
{(inputProps) => <input {...inputProps} type="tel" name="telefone" />}
</InputMask>
<label>E-mail</label>
<input type="email" name="email" placeholder="Email" />
<label>Convênio</label>
<select name="convenio">
<option value="particular">Particular</option>
<option value="publico">Público</option>
</select>
<label>Matrícula</label>
<input type="text" name="matricula" placeholder="000000000" />
<label>Validade</label>
<InputMask mask="99/99/9999" placeholder="00/00/0000">
{(inputProps) => <input {...inputProps} type="text" name="validade" />}
</InputMask>
<h3 className="section-subtitle">Informações adicionais</h3>
<button type="button" className="btn-secondary">Documentos e anexos</button>
<h2 className="section-title">Informações do atendimento</h2>
<label>Nome do profissional *</label>
<input type="text" name="profissional" required />
<label>Tipo de atendimento *</label>
<input type="text" name="tipoAtendimento" required />
<label>Unidade *</label>
<select name="unidade">
<option value="centro">Núcleo de Especialidades Integradas</option>
<option value="leste">Unidade Leste</option>
</select>
<label>Data *</label>
<input type="date" name="dataAtendimento" required />
<label>Início *</label>
<input type="time" name="inicio" required />
<label>Término *</label>
<input type="time" name="termino" required />
<label>Profissional solicitante</label>
<select name="solicitante">
<option value="">Selecione o solicitante</option>
<option value="secretaria">Secretária</option>
<option value="medico">Médico</option>
</select>
<label>Observações</label>
<textarea name="observacoes"></textarea>
<label>
<input type="checkbox" name="reembolso" /> Pagamento via Reembolso
</label>
<label>
<input type="checkbox" name="imprimirEtiqueta" /> Imprimir na Etiqueta / Pulseira
</label>
<h3 className="section-subtitle">Acessibilidade</h3>
<div className="btn-group">
<button type="button"></button>
<button type="button"></button>
<button type="button"></button>
<button type="button"></button>
</div>
<div className="form-actions">
<button type="submit" className="btn-primary">Salvar agendamento</button>
<button type="button" className="btn-cancel" onClick={onCancel}>Cancelar</button>
</div>
</form>
</div>
);
};
export default FormNovaConsulta;

View File

@ -0,0 +1,48 @@
import React from 'react'
import CardConsulta from './CardConsulta'
import AgendamentosMes from './DadosConsultasMock'
import "./style/styleTabelas/tabeladia.css";
const TabelaAgendamentoDia = ({handleClickAgendamento}) => {
let agendamentosDessaSemana = AgendamentosMes.semana1
let agendamentos = agendamentosDessaSemana.segunda
console.log(agendamentos)
return (
<div>
<table className='tabeladiaria'>
<thead>
<tr>
<th></th>
<th>{agendamentos.medico}</th>
</tr>
</thead>
<tbody>
{agendamentos.map((agendamento) => (
<tr key={agendamento.id} border='2' >
<td ><p>{agendamento.horario}</p></td>
<td className='mostrar-horario'>
<div onClick={() => handleClickAgendamento(agendamento)} >
<CardConsulta DadosConsulta={agendamento} TabelaAgendamento={'dia'} />
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default TabelaAgendamentoDia

View File

@ -0,0 +1,133 @@
import React from 'react'
import AgendamentosMes from './DadosConsultasMock'
import dayjs from "dayjs"
import CardConsulta from './CardConsulta'
import "./style/styleTabelas/tabelames.css";
const TabelaAgendamentoMes = ({ListarDiasdoMes}) => {
const dataHoje = dayjs()
const AnoAtual = dataHoje.year()
const mes = dataHoje.month() + 1
let ListaDiasDatas = ListarDiasdoMes(AnoAtual, mes)
let segundas = ListaDiasDatas[0];
let tercas = ListaDiasDatas[1];
let quartas = ListaDiasDatas[2];
let quintas = ListaDiasDatas[3];
let sextas = ListaDiasDatas[4]
console.log(AnoAtual, 'ano', mes, 'mes')
return (
<div>
<table className='tabelamensal'>
<tr>
<th>Seg</th>
<th>Ter</th>
<th>Qua</th>
<th>Qui</th>
<th>Sex</th>
</tr>
{Object.entries(AgendamentosMes).map(([semanas, dias], index) => (
<tr key={index}>
<td>
<div>
<p>{segundas[index]}</p>
<div>
{dias.segunda.slice(0,4).map((consulta, idx) => (
<CardConsulta
key={idx}
DadosConsulta={consulta}
className={`usuario-${consulta.cor || "default"}`}
/>
))}
</div>
{dias.segunda.length > 3 ?
<div><p>+ {dias.segunda.length - 3}</p></div>
: null}
</div>
</td>
<td>
<div>
{tercas[index]}
<div>
{dias.terca.slice(0,4).map((consulta, idx) => (
<CardConsulta
key={idx}
DadosConsulta={consulta}
className={`usuario-${consulta.cor || "default"}`}
/>
))}
</div>
{dias.terca.length > 3 ?
<div><p>+ {dias.terca.length - 3}</p></div>
: null}
</div>
</td>
<td>
<div>
{quartas[index]}
<div>
{dias.quarta.slice(0,4).map((consulta, idx) => (
<CardConsulta
key={idx}
DadosConsulta={consulta}
className={`usuario-${consulta.cor || "default"}`}
/>
))}
</div>
{dias.quarta.length > 3 ?
<div><p>+ {dias.quarta.length - 3}</p></div>
: null}
</div>
</td>
<td>
<div>
{quintas[index]}
<div>
{dias.quinta.slice(0,4).map((consulta, idx) => (
<CardConsulta
key={idx}
DadosConsulta={consulta}
className={`usuario-${consulta.cor || "default"}`}
/>
))}
</div>
{dias.quinta.length > 3 ?
<div><p>+ {dias.quinta.length - 3}</p></div>
: null}
</div>
</td>
<td>
<div>
{sextas[index]}
<div>
{dias.sexta.slice(0,4).map((consulta, idx) => (
<CardConsulta
key={idx}
DadosConsulta={consulta}
className={`usuario-${consulta.cor || "default"}`}
/>
))}
</div>
{dias.sexta.length > 3 ?
<div><p>+ {dias.sexta.length - 3}</p></div>
: null}
</div>
</td>
</tr>
))}
</table>
</div>
)
}
export default TabelaAgendamentoMes

View File

@ -0,0 +1,54 @@
import React from 'react'
import AgendamentosMes from './DadosConsultasMock'
import CardConsulta from './CardConsulta'
import "./style/styleTabelas/tabelasemana.css";
const TabelaAgendamentoSemana = () => {
let AgendamentosDesseMes = AgendamentosMes
let AgendamentoSemana = AgendamentosDesseMes.semana1
let AgendamentosdeSegunda = AgendamentoSemana.segunda
let AgendamentosdeTerca = AgendamentoSemana.terca
let AgendamentosdeQuarta = AgendamentoSemana.quarta
let AgendamentosdeQuinta = AgendamentoSemana.quinta
let AgendamentosdeSexta = AgendamentoSemana.sexta
return (
<div >
<table className='tabelasemanal'>
<tr>
<th></th>
<th>Segunda</th>
<th>Terça</th>
<th>Quarta</th>
<th>Quinta</th>
<th>Sexta</th>
</tr>
{AgendamentosdeSegunda.map((consulta, index) => (
<tr key={index}>
<td>{consulta.horario}</td>
<td className='coluna-tipo1'> <CardConsulta DadosConsulta={AgendamentosdeSegunda[index]} /> </td>
<td> <CardConsulta DadosConsulta={AgendamentosdeTerca[index]} /> </td>
<td> <CardConsulta DadosConsulta={AgendamentosdeQuarta[index]} /> </td>
<td><CardConsulta DadosConsulta={AgendamentosdeQuinta[index]} /></td>
<td><CardConsulta DadosConsulta={AgendamentosdeSexta[index]} /></td>
</tr>
))}
</table>
</div>
)
}
export default TabelaAgendamentoSemana

View File

@ -0,0 +1,80 @@
.tabeladiaria {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
border-radius: 10px;
overflow: hidden; /* mantém o arredondado */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 4px solid #4a90e2; /* borda azul, altere para a cor desejada */
}
/* Células da tabela */
.tabeladiaria th, .tabeladiaria td {
padding: 9px;
text-align: left;
border: 1px solid #e0e0e0;
}
/* Cabeçalho */
.tabeladiaria thead th {
background-color: #0078d7;
color: #ffffff;
font-weight: 600;
border-bottom: 2px solid #005a9e; /* borda inferior mais forte no cabeçalho */
}
/* Remover bordas laterais do cabeçalho (se quiser) */
.tabeladiaria thead th:first-child {
border-left: none;
}
.tabeladiaria thead th:last-child {
border-right: none;
}
/* Linhas pares do corpo */
.tabeladiaria tbody tr:nth-child(even) {
background-color: #e7e7e7a6;
}
/* Hover nas linhas */
.tabeladiaria tbody tr:hover {
background-color: #f1f1f1;
}
/* Card dentro da tabela */
.tabeladiaria .cardconsulta {
border-radius: 10px;
color: black;
height: 80px;
width: 100%;
padding: 8px;
}
/* Ajuste para a classe .diadia, se for usada */
.diadia {
margin-top: 20px;
border-collapse: collapse;
width: 100%;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 12px rgb(255, 255, 255);
border: 10px solid #ffffffc5;
background-color: rgb(253, 253, 253);
}
.mostrar-horario td, .mostrar-horario th {
padding: 4px 6px;
height: 30px;
border: 1px solid #e0e0e0;
text-align: center;
}
.container-cardconsulta-dia {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
height: 70px;
}

View File

@ -0,0 +1,150 @@
.tabelamensal {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
border-radius: 10px;
overflow: hidden; /* mantém o arredondado */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 4px solid #4a90e2; /* borda azul, altere para a cor desejada */
}
/* Células da tabela */
.tabelamensal th, .tabelamensal td {
padding: 9px;
text-align: left;
border: 1px solid #e0e0e0;
}
/* Cabeçalho */
.tabelamensal thead th {
background-color: #0078d7;
color: #0078d7;
font-weight: 600;
border-bottom: 2px solid #0078d7; /* borda inferior mais forte no cabeçalho */
}
/* Remover bordas laterais do cabeçalho (se quiser) */
.tabelamensal thead th:first-child {
border-left: none;
}
.tabelamensal thead th:last-child {
border-right: none;
}
/* Linhas pares do corpo */
.tabelamensal tbody tr:nth-child(even) {
background-color: #e7e7e7a6;
}
/* Hover nas linhas */
.tabelamensal tbody tr:hover {
background-color: #f1f1f1;
}
/* Card dentro da tabela */
.tabelamensal .cardconsulta {
border-radius: 10px;
color: black;
height: 80px;
width: 100%;
padding: 12px;
}
/* Ajuste para a classe .diadia, se for usada */
.diadia {
margin-top: 20px;
border-collapse: collapse;
width: 100%;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 12px rgb(255, 255, 255);
border: 10px solid #ffffffc5;
background-color: rgb(253, 253, 253);
}
.tabelamensal {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
border-radius: 10px;
overflow: hidden; /* mantém o arredondado */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 4px solid #4a90e2; /* borda azul, altere para a cor desejada */
}
.tabelamensal th, .tabelamensal td {
padding: 9px;
text-align: left;
border: 1px solid #e0e0e0;
}
.tabelamensal th {
background-color: #0078d7;
color: #ffffff;
font-weight: 600;
border-bottom: 2px solid #005a9e; /* borda inferior mais forte no cabeçalho */
}
.tabelamensal td {
background-color: #f7f7f7;
}
.tabelamensal td:hover {
background-color: #f1f1f1;
}
/* Estilo geral do card de consulta */
.tabelamensal .cardconsulta {
border-radius: 8px;
background-color: #fff;
padding: 6px;
margin: 4px 0;
font-size: 0.9rem;
font-weight: 500;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border-left: 5px solid transparent; /* espaço da borda colorida */
}
.usuario-azul {
background-color: #E3F2FD;
border-left: 4px solid #2196F3;
padding: 4px 8px;
margin-bottom: 5px;
border-radius: 4px;
}
.usuario-verde {
background-color: #E8F5E9;
border-left: 4px solid #4CAF50;
padding: 4px 8px;
margin-bottom: 5px;
border-radius: 4px;
}
.usuario-roxo {
background-color: #F3E5F5;
border-left: 4px solid #9C27B0;
padding: 4px 8px;
margin-bottom: 5px;
border-radius: 4px;
}
.usuario-laranja {
background-color: #FFF3E0;
border-left: 4px solid #FF9800;
padding: 4px 8px;
margin-bottom: 5px;
border-radius: 4px;
}
.usuario-default {
background-color: #FAFAFA;
border-left: 4px solid #BDBDBD;
padding: 4px 8px;
margin-bottom: 5px;
border-radius: 4px;
}

View File

@ -0,0 +1,82 @@
.tabelasemanal {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
border-radius: 10px;
overflow: hidden; /* mantém o arredondado */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 4px solid #4a90e2; /* borda azul, altere para a cor desejada */
}
/* Células da tabela */
.tabelasemanal th, .tabelasemanal td {
padding: 9px;
text-align: left;
border: 1px solid #e0e0e0;
}
/* Cabeçalho da semanal */
.tabelasemanal thead th,
.tabelasemanal thead td,
.tabelasemanal tr:first-child th,
.tabelasemanal tr:first-child td {
background-color: #0078d7 !important;
color: #ffffff !important;
font-weight: 600;
text-align: center;
border-bottom: 2px solid #005a9e;
}
/* Remover bordas laterais do cabeçalho (se quiser) */
.tabelasemanal thead th:first-child {
border-left: none;
}
.tabelasemanal thead th:last-child {
border-right: none;
}
/* Linhas pares do corpo */
.tabelasemanal tbody tr:nth-child(even) {
background-color: #e7e7e7a6;
}
/* Hover nas linhas */
.tabelasemanal tbody tr:hover {
background-color: #f1f1f1;
}
/* Card dentro da tabela */
.tabelasemanal .cardconsulta {
border-radius: 10px;
color: black;
height: 80px;
width: 100%;
padding: 12px;
}
/* Ajuste para a classe .diadia, se for usada */
.diadia {
margin-top: 20px;
border-collapse: collapse;
width: 100%;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 12px rgb(255, 255, 255);
border: 10px solid #ffffffc5;
background-color: rgb(253, 253, 253);
}
.tabelasemanal tr:nth-child(even) {
background-color: #e7e7e7a6 !important;
margin-left: 10px !important;
}
.tabelasemanal tr:nth-child(odd) {
background-color: #ffffff !important;
}
.tabelasemanal tr:hover {
background-color: #f1f1f1 !important;
}

View File

@ -0,0 +1,108 @@
.form-container {
max-width: 800px;
margin: 20px auto;
padding: 25px;
background: #f5f8ff;
border-radius: 12px;
font-family: Arial, sans-serif;
}
.form-title {
font-size: 26px;
font-weight: bold;
color: #1e3a8a;
margin-bottom: 20px;
}
.form-agendamento label {
display: block;
margin-top: 12px;
font-weight: bold;
color: #333;
}
.form-agendamento input,
.form-agendamento select,
.form-agendamento textarea {
width: 100%;
padding: 8px;
margin-top: 6px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 14px;
}
.section-title {
font-size: 20px;
margin-top: 25px;
color: #1e40af;
}
.section-subtitle {
font-size: 16px;
margin-top: 20px;
color: #374151;
}
.btn-group button {
margin-right: 8px;
padding: 8px 12px;
border: 1px solid #1e3a8a;
border-radius: 6px;
background: #fff;
cursor: pointer;
}
.btn-group button:hover {
background: #e0e7ff;
}
.form-actions {
margin-top: 25px;
display: flex;
gap: 12px;
}
.btn-primary {
background: #1e3a8a;
color: white;
border: none;
padding: 10px 18px;
border-radius: 6px;
cursor: pointer;
}
.btn-primary:hover {
background: #172554;
}
.btn-cancel {
background: #e5e7eb;
border: none;
padding: 10px 18px;
border-radius: 6px;
cursor: pointer;
}
.btn-cancel:hover {
background: #d1d5db;
}
.btn-secondary {
margin: 10px 0;
background: #f3f4f6;
padding: 8px 14px;
border: 1px solid #9ca3af;
border-radius: 6px;
cursor: pointer;
}
.btn-secondary:hover {
background: #e5e7eb;
}
.cardconsulta-infosecundaria{
font-size: small;
}

View File

@ -1,14 +0,0 @@
import React from 'react';
// O componente agora recebe a função `toggleSidebar` como uma prop.
function Header({ toggleSidebar }) {
return (
<header className="mb-3">
<a href="#" className="burger-btn d-block d-xl-none" onClick={toggleSidebar}>
<i className="bi bi-justify fs-3"></i>
</a>
</header>
);
}
export default Header;

View File

@ -31,62 +31,97 @@ function DoctorForm({ onSave, onCancel, PatientDict }) {
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
nome: PatientDict.nome, foto: null,
nomeSocial: PatientDict.nome_social, nome: PatientDict.nome,
dataNascimento: PatientDict.data_nascimento, nomeSocial: PatientDict.nome_social,
genero: PatientDict.sexo, dataNascimento: PatientDict.data_nascimento,
//documento: '', genero: PatientDict.sexo,
//numeroDocumento: '', cpf: PatientDict.cpf,
cpf: PatientDict.cpf, profissao: PatientDict.profissao ,
profissao: PatientDict.profissao , nomeConjuge: '',
//nomeConjuge: '', outroId: '',
//outroId: '', cep: '',
cep: '', cidade: PatientDict.cidade,
cidade: PatientDict.cidade, estado: PatientDict.estado,
estado: PatientDict.estado, bairro: PatientDict.bairro,
bairro: PatientDict.bairro, rua: PatientDict.logradouro,
rua: PatientDict.logradouro, numero: '',
numero: '', complemento: '',
complemento: '', email: PatientDict.email,
email: PatientDict.email, telefone1: PatientDict.celular,
telefone1: PatientDict.celular, telefone2: '',
telefone2: '', telefone3: '',
telefone3: '', observacoes: '',
observacoes: '' rg: '',
}); documentoTipo: '',
numeroDocumento: '',
etniaRaca: '',
naturalidade: '',
nacionalidade: '',
estadoCivil: '',
const handleChange = (e) => { // INFORMAÇÕES MÉDICAS
const { name, value } = e.target; tipoSanguineo: '',
setFormData({ peso: '',
...formData, altura: '',
[name]: value imc: '',
alergias: '',
// ANEXO
anexos: null,
}); });
// Estado para armazenar a URL da foto do avatar
const [avatarUrl, setAvatarUrl] = useState(null);
if(name.includes('cpf')){ // 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
infoMedicas: false,
infoConvenio: false,
endereco: false,
contato: false,
});
let cpfFormatado = FormatCPF(e.target.value) // Função para alternar o estado de colapso de uma seção
const handleToggleCollapse = (section) => {
setCollapsedSections(prevState => ({
...prevState,
[section]: !prevState[section]
}));
};
setFormData({...formData, const handleChange = (e) => {
[name]: cpfFormatado,} const { name, value, type, checked, files } = e.target;
)}
else if(name.includes('telefone')){ if (type === 'checkbox') {
let telefoneFormatado = FormatTelefones(value) setFormData({ ...formData, [name]: checked });
} else if (type === 'file') {
setFormData({ ...formData, [name]: files[0] });
console.log(telefoneFormatado) // Lógica para pré-visualizar a imagem no avatar
if (name === 'foto' && files[0]) {
const reader = new FileReader();
reader.onloadend = () => {
setAvatarUrl(reader.result);
};
reader.readAsDataURL(files[0]);
} else if (name === 'foto' && !files[0]) {
setAvatarUrl(null); // Limpa o avatar se nenhum arquivo for selecionado
}
} else {
setFormData({ ...formData, [name]: value });
setFormData({...formData,
[name]: telefoneFormatado
})
} }
if (name.includes('cpf')) {
let cpfFormatado = FormatCPF(value);
}; setFormData(prev => ({ ...prev, [name]: cpfFormatado }));
} else if (name.includes('telefone')) {
let telefoneFormatado = FormatTelefones(value);
setFormData(prev => ({ ...prev, [name]: telefoneFormatado }));
}
};
// Função para buscar endereço pelo CEP // Função para buscar endereço pelo CEP
const handleCepBlur = async () => { const handleCepBlur = async () => {
@ -113,196 +148,421 @@ function DoctorForm({ onSave, onCancel, PatientDict }) {
}; };
const handleSubmit = () => { const handleSubmit = () => {
if (!formData.nome || !formData.cpf || !formData.genero || !formData.dataNascimento || !formData.email){ if (!formData.nome || !formData.cpf || !formData.genero || !formData.dataNascimento || !formData.email) {
alert('Por favor, preencha: Nome ,CPF, Gênero, Data de nascimento e Email.'); alert('Por favor, preencha: Nome ,CPF, Gênero, Data de Nascimento e Email.');
return; return;
} }
onSave( onSave(
{nome: formData.nome, {
nomeSocial: formData.nomeSocial, ...formData,
dataNascimento: formData.dataNascimento, endereco: {
genero: formData.genero, cep: formData.cep,
//documento: formData.documento, cidade: formData.cidade,
//numeroDocumento: formData.numeroDocumento, estado: formData.estado,
cpf: formData.cpf, bairro: formData.bairro,
profissao: formData.profissao, logradouro: formData.rua,
//nomeConjuge: formData.nomeConjuge, numero: formData.numero,
//outroId: formData.outroId, complemento: formData.complemento,
endereco: { },
cep: formData.cep, contato: {
cidade: formData.cidade, email: formData.email,
estado: formData.estado, telefone1: formData.telefone1,
bairro: formData.bairro, telefone2: formData.telefone2,
logradouro: formData.rua, telefone3: formData.telefone3,
numero: formData.numero, },
complemento: formData.complemento, infoMedicas: {
}, tipoSanguineo: formData.tipoSanguineo,
peso: formData.peso,
contato: { altura: formData.altura,
email: formData.email, imc: formData.imc,
telefone1: formData.telefone1, alergias: formData.alergias,
telefone2: formData.telefone2, }
telefone3: formData.telefone3, }
},
observacoes: formData.observacoes,
}
); );
}; };
return ( return (
<div className="card p-3"> <div className="card p-3 shadow-sm">
<h3 className="mb-3 text-center">MediConnect</h3> <h3 className="mb-4 text-center" style={{ fontSize: '2.5rem' }}>MediConnect</h3>
{/* ------------------ DADOS PESSOAIS ------------------ */} {/* DADOS PESSOAIS */}
<h5 className="mb-3">Dados Pessoais</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="row"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('dadosPessoais')} style={{ fontSize: '1.8rem' }}>
<div className="col-md-6 mb-3"> Dados Pessoais
<label>Nome: *</label> <span className="fs-5">
<input type="text" className="form-control" name="nome" value={formData.nome} onChange={handleChange} /> {collapsedSections.dadosPessoais ? '▲' : '▼'}
</div> </span>
<div className="col-md-6 mb-3"> </h4>
<label>Nome social:</label> <div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}>
<input type="text" className="form-control" name="nomeSocial" value={formData.nomeSocial} onChange={handleChange} /> <div className="row mt-3">
</div> {/* AVATAR E INPUT DE FOTO */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3 d-flex align-items-center">
<label>Data de nascimento:</label> <div className="me-3">
<input type="date" className="form-control" name="dataNascimento" value={formData.dataNascimento} onChange={handleChange} /> {avatarUrl ? (
</div> <img
<div className="col-md-6 mb-3"> src={avatarUrl}
<label>Gênero: *</label> alt="Avatar do Médico"
<select className="form-control" name="genero" value={formData.genero} onChange={handleChange}> style={{ width: '100px', height: '100px', borderRadius: '50%', objectFit: 'cover' }}
<option value="">Selecione</option> />
<option value="Masculino">Masculino</option> ) : (
<option value="Feminino">Feminino</option> <div
<option value="Outro">Outro</option> style={{
</select> width: '100px',
</div> height: '100px',
{/* borderRadius: '50%',
<div className="col-md-6 mb-3"> backgroundColor: '#e0e0e0',
<label>Outro documento:</label> display: 'flex',
<input type="text" className="form-control" name="documento" value={formData.documento} onChange={handleChange} /> alignItems: 'center',
</div> justifyContent: 'center',
<div className="col-md-6 mb-3"> fontSize: '3.5rem',
<label>Número do documento:</label> color: '#9e9e9e'
<input type="text" className="form-control" name="numeroDocumento" value={formData.numeroDocumento} onChange={handleChange} /> }}
</div> >
*/} &#x2624;
<div className="col-md-6 mb-3"> </div>
<label>CPF: *</label> )}
<input type="text" className="form-control" name="cpf" value={formData.cpf} onChange={ handleChange} /> </div>
</div> <div>
<div className="col-md-6 mb-3"> <label htmlFor="foto-input" className="btn btn-primary" style={{ fontSize: '1rem' }}>Carregar Foto</label>
<label>Especialização:</label> <input
<select type="file"
className="form-control" className="form-control d-none"
name="profissao" name="foto"
value={formData.profissao} id="foto-input"
onChange={handleChange} onChange={handleChange}
> accept="image/*"
<option value="">Selecione uma especialização</option> />
<option value="Cardiologia">Clínica médica (clínico geral)</option> {formData.foto && <span className="ms-2" style={{ fontSize: '1rem' }}>{formData.foto.name}</span>}
<option value="Dermatologia">Pediatria</option> </div>
<option value="Ginecologia">Ginecologia e obstetrícia</option> </div>
<option value="Pediatria">Cardiologia</option> {/* CADASTRO */}
<option value="Ortopedia">Ortopedia e traumatologia</option> <div className="col-md-6 mb-3">
<option value="Oftalmologia">Oftalmologia</option> <label style={{ fontSize: '1.1rem' }}>Nome: *</label>
<option value="Neurologia">Otorrinolaringologia</option> <input type="text" className="form-control" name="nome" value={formData.nome} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<option value="Psiquiatria">Dermatologia</option> </div>
<option value="Endocrinologia">Neurologia</option> <div className="col-md-6 mb-3">
<option value="Oncologia">Psiquiatria</option> <label style={{ fontSize: '1.1rem' }}>Nome social:</label>
<option value="Oncologia">Endocrinologia</option> <input type="text" className="form-control" name="nomeSocial" value={formData.nomeSocial} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<option value="Oncologia">Gastroenterologia</option> </div>
<option value="Oncologia">Urologia</option> <div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Data de nascimento: *</label>
<input type="date" className="form-control" name="dataNascimento" value={formData.dataNascimento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Gênero: *</label>
<select className="form-control" name="genero" value={formData.genero} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Masculino">Masculino</option>
<option value="Feminino">Feminino</option>
<option value="Outro">Outro</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Identificador de outro sistema:</label>
<input type="text" className="form-control" name="outroId" value={formData.outroId} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF: *</label>
<input type="text" className="form-control" name="cpf" value={formData.cpf} onChange={ handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>RG:</label>
<input type="text" className="form-control" name="rg" value={formData.rg} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Outros documentos:</label>
<select className="form-control" name="documentoTipo" value={formData.documentoTipo} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="CNH">CNH</option>
<option value="Passaporte">Passaporte</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número do documento:</label>
<input type="text" className="form-control" name="numeroDocumento" value={formData.numeroDocumento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Etnia e Raça:</label>
<select className="form-control" name="etniaRaca" value={formData.etniaRaca} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Branca">Branca</option>
<option value="Preta">Preta</option>
<option value="Parda">Parda</option>
<option value="Amarela">Amarela</option>
<option value="Indígena">Indígena</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Naturalidade:</label>
<input type="text" className="form-control" name="naturalidade" value={formData.naturalidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nacionalidade:</label>
<input type="text" className="form-control" name="nacionalidade" value={formData.nacionalidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Especialização:</label>
<select className="form-control" name="profissao" value={formData.profissao} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Cardiologia">Clínica médica (clínico geral)</option>
<option value="Dermatologia">Pediatria</option>
<option value="Ginecologia">Ginecologia e obstetrícia</option>
<option value="Pediatria">Cardiologia</option>
<option value="Ortopedia">Ortopedia e traumatologia</option>
<option value="Oftalmologia">Oftalmologia</option>
<option value="Neurologia">Otorrinolaringologia</option>
<option value="Psiquiatria">Dermatologia</option>
<option value="Endocrinologia">Neurologia</option>
<option value="Oncologia">Psiquiatria</option>
<option value="Oncologia">Endocrinologia</option>
<option value="Oncologia">Gastroenterologia</option>
<option value="Oncologia">Urologia</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado civil:</label>
<select className="form-control" name="estadoCivil" value={formData.estadoCivil} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Solteiro">Solteiro(a)</option>
<option value="Casado">Casado(a)</option>
<option value="Divorciado">Divorciado(a)</option>
<option value="Viuvo">Viúvo(a)</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do esposo(a):</label>
<input type="text" className="form-control" name="nomeConjuge" value={formData.nomeConjuge} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
</select> {/* CAMPOS MOVIDOS */}
<div className="col-md-12 mb-3 mt-3">
<label style={{ fontSize: '1.1rem' }}>Observações:</label>
<textarea className="form-control" name="observacoes" value={formData.observacoes} onChange={handleChange} style={{ fontSize: '1.1rem' }}></textarea>
</div>
<div className="col-md-12 mb-3">
<label style={{ fontSize: '1.1rem' }}>Anexos do Médico:</label>
<div>
<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>
</div>
</div> </div>
{/*
<div className="col-md-6 mb-3">
<label>Nome do esposo(a):</label>
<input type="text" className="form-control" name="nomeConjuge" value={formData.nomeConjuge} onChange={handleChange} />
</div>
<div className="col-md-6 mb-3">
<label>Identificador de outro sistema:</label>
<input type="text" className="form-control" name="outroId" value={formData.outroId} onChange={handleChange} />
</div>
*/}
</div>
{/* ------------------ ENDEREÇO ------------------ */}
<h5>Endereço</h5>
<div className="row">
<div className="col-md-4 mb-3">
<label>CEP:</label>
<input type="text" className="form-control" name="cep" value={formData.cep} onChange={handleChange} onBlur={handleCepBlur} />
</div>
<div className="col-md-8 mb-3">
<label>Rua:</label>
<input type="text" className="form-control" name="rua" value={formData.rua} onChange={handleChange} />
</div>
<div className="col-md-6 mb-3">
<label>Bairro:</label>
<input type="text" className="form-control" name="bairro" value={formData.bairro} onChange={handleChange} />
</div>
<div className="col-md-4 mb-3">
<label>Cidade:</label>
<input type="text" className="form-control" name="cidade" value={formData.cidade} onChange={handleChange} />
</div>
<div className="col-md-2 mb-3">
<label>Estado:</label>
<input type="text" className="form-control" name="estado" value={formData.estado} onChange={handleChange} />
</div>
<div className="col-md-4 mb-3">
<label>Número:</label>
<input type="text" className="form-control" name="numero" value={formData.numero} onChange={handleChange} />
</div>
<div className="col-md-8 mb-3">
<label>Complemento:</label>
<input type="text" className="form-control" name="complemento" value={formData.complemento} onChange={handleChange} />
</div> </div>
</div> </div>
{/* ------------------ CONTATO ------------------ */} {/* ENDEREÇO */}
<h5>Contato</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="row"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('endereco')} style={{ fontSize: '1.8rem' }}>
<div className="col-md-6 mb-3"> Endereço
<label>E-mail: *</label> <span className="fs-5">
<input type="email" className="form-control" name="email" value={formData.email} onChange={handleChange} /> {collapsedSections.endereco ? '▲' : '▼'}
</div> </span>
<div className="col-md-6 mb-3"> </h4>
<label>Telefone: *</label> <div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}>
<input type="text" className="form-control" name="telefone1" value={formData.telefone1} onChange={handleChange} /> <div className="row mt-3">
</div> <div className="col-md-4 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>CEP:</label>
<label>Telefone 2:</label> <input type="text" className="form-control" name="cep" value={formData.cep} onChange={handleChange} onBlur={handleCepBlur} style={{ fontSize: '1.1rem' }} />
<input type="text" className="form-control" name="telefone2" value={formData.telefone2} onChange={handleChange} /> </div>
</div> <div className="col-md-8 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Rua:</label>
<label>Telefone 3:</label> <input type="text" className="form-control" name="rua" value={formData.rua} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<input type="text" className="form-control" name="telefone3" value={formData.telefone3} onChange={handleChange} /> </div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Bairro:</label>
<input type="text" className="form-control" name="bairro" value={formData.bairro} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Cidade:</label>
<input type="text" className="form-control" name="cidade" value={formData.cidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado:</label>
<input type="text" className="form-control" name="estado" value={formData.estado} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número:</label>
<input type="text" className="form-control" name="numero" value={formData.numero} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-8 mb-3">
<label style={{ fontSize: '1.1rem' }}>Complemento:</label>
<input type="text" className="form-control" name="complemento" value={formData.complemento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
</div>
</div> </div>
</div> </div>
{/* ------------------ INFORMAÇÕES ADICIONAIS ------------------ */} {/* CONTATO */}
<h5>Informações Adicionais</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="mb-3"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('contato')} style={{ fontSize: '1.8rem' }}>
<label>Observações:</label> Contato
<textarea className="form-control" name="observacoes" value={formData.observacoes} onChange={handleChange}></textarea> <span className="fs-5">
{collapsedSections.contato ? '▲' : '▼'}
</span>
</h4>
<div className={`collapse${collapsedSections.contato ? ' show' : ''}`}>
<div className="row mt-3">
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Email: *</label>
<input type="email" className="form-control" name="email" value={formData.email} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone: *</label>
<input type="text" className="form-control" name="telefone1" value={formData.telefone1} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 2:</label>
<input type="text" className="form-control" name="telefone2" value={formData.telefone2} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Celular:</label>
<input type="text" className="form-control" name="telefone3" value={formData.telefone3} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
</div>
</div>
</div> </div>
{/* Botões */} {/* Botões */}
<div className="mt-3"> <div className="mt-3 text-center">
<button className="btn btn-success me-2" onClick={handleSubmit}> <button className="btn btn-success me-3" onClick={handleSubmit} style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}>
Salvar Paciente Salvar Paciente
</button> </button>
<button className="btn btn-light" onClick={onCancel}> <button className="btn btn-light" onClick={onCancel} style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}>
Cancelar Cancelar
</button> </button>
</div> </div>
</div> </div>
// <div className="card p-3">
// <h3 className="mb-4 text-center" style={{ fontSize: '2.5rem' }}>MediConnect</h3>
// {/* ------------------ DADOS PESSOAIS ------------------ */}
// <h5 className="mb-3">Dados Pessoais</h5>
// <div className="row">
// <div className="col-md-6 mb-3">
// <label>Nome: *</label>
// <input type="text" className="form-control" name="nome" value={formData.nome} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Nome social:</label>
// <input type="text" className="form-control" name="nomeSocial" value={formData.nomeSocial} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Data de nascimento:</label>
// <input type="date" className="form-control" name="dataNascimento" value={formData.dataNascimento} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Gênero: *</label>
// <select className="form-control" name="genero" value={formData.genero} onChange={handleChange}>
// <option value="">Selecione</option>
// <option value="Masculino">Masculino</option>
// <option value="Feminino">Feminino</option>
// <option value="Outro">Outro</option>
// </select>
// </div>
// <div className="col-md-6 mb-3">
// <label>CPF: *</label>
// <input type="text" className="form-control" name="cpf" value={formData.cpf} onChange={ handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Especialização:</label>
// <select
// className="form-control"
// name="profissao"
// value={formData.profissao}
// onChange={handleChange}
// >
// <option value="">Selecione uma especialização</option>
// <option value="Cardiologia">Clínica médica (clínico geral)</option>
// <option value="Dermatologia">Pediatria</option>
// <option value="Ginecologia">Ginecologia e obstetrícia</option>
// <option value="Pediatria">Cardiologia</option>
// <option value="Ortopedia">Ortopedia e traumatologia</option>
// <option value="Oftalmologia">Oftalmologia</option>
// <option value="Neurologia">Otorrinolaringologia</option>
// <option value="Psiquiatria">Dermatologia</option>
// <option value="Endocrinologia">Neurologia</option>
// <option value="Oncologia">Psiquiatria</option>
// <option value="Oncologia">Endocrinologia</option>
// <option value="Oncologia">Gastroenterologia</option>
// <option value="Oncologia">Urologia</option>
// </select>
// </div>
// </div>
// {/* ------------------ ENDEREÇO ------------------ */}
// <h5>Endereço</h5>
// <div className="row">
// <div className="col-md-4 mb-3">
// <label>CEP:</label>
// <input type="text" className="form-control" name="cep" value={formData.cep} onChange={handleChange} onBlur={handleCepBlur} />
// </div>
// <div className="col-md-8 mb-3">
// <label>Rua:</label>
// <input type="text" className="form-control" name="rua" value={formData.rua} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Bairro:</label>
// <input type="text" className="form-control" name="bairro" value={formData.bairro} onChange={handleChange} />
// </div>
// <div className="col-md-4 mb-3">
// <label>Cidade:</label>
// <input type="text" className="form-control" name="cidade" value={formData.cidade} onChange={handleChange} />
// </div>
// <div className="col-md-2 mb-3">
// <label>Estado:</label>
// <input type="text" className="form-control" name="estado" value={formData.estado} onChange={handleChange} />
// </div>
// <div className="col-md-4 mb-3">
// <label>Número:</label>
// <input type="text" className="form-control" name="numero" value={formData.numero} onChange={handleChange} />
// </div>
// <div className="col-md-8 mb-3">
// <label>Complemento:</label>
// <input type="text" className="form-control" name="complemento" value={formData.complemento} onChange={handleChange} />
// </div>
// </div>
// {/* ------------------ CONTATO ------------------ */}
// <h5>Contato</h5>
// <div className="row">
// <div className="col-md-6 mb-3">
// <label>E-mail: *</label>
// <input type="email" className="form-control" name="email" value={formData.email} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Telefone: *</label>
// <input type="text" className="form-control" name="telefone1" value={formData.telefone1} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Telefone 2:</label>
// <input type="text" className="form-control" name="telefone2" value={formData.telefone2} onChange={handleChange} />
// </div>
// <div className="col-md-6 mb-3">
// <label>Telefone 3:</label>
// <input type="text" className="form-control" name="telefone3" value={formData.telefone3} onChange={handleChange} />
// </div>
// </div>
// {/* ------------------ INFORMAÇÕES ADICIONAIS ------------------ */}
// <h5>Informações Adicionais</h5>
// <div className="mb-3">
// <label>Observações:</label>
// <textarea className="form-control" name="observacoes" value={formData.observacoes} onChange={handleChange}></textarea>
// </div>
// {/* Botões */}
// <div className="mt-3">
// <button className="btn btn-success me-2" onClick={handleSubmit}>
// Salvar Paciente
// </button>
// <button className="btn btn-light" onClick={onCancel}>
// Cancelar
// </button>
// </div>
// </div>
); );
} }

View File

@ -1,108 +1,149 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import InputMask from "react-input-mask";
function PatientForm({ onSave, onCancel, PatientDict }) {
function PatientForm({ onSave, onCancel,formData, setFormData }) {
const FormatTelefones = (valor) => { const FormatTelefones = (valor) => {
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
const digits = String(valor).replace(/\D/g, '').slice(0, 11); return digits
.replace(/(\d)/, '($1')
.replace(/(\d{2})(\d)/, '$1) $2' )
return digits .replace(/(\d)(\d{4})/, '$1 $2')
.replace(/(\d)/, '($1') // 123 -> 123. .replace(/(\d{4})(\d{4})/, '$1-$2')
.replace(/(\d{2})(\d)/, '$1) $2' )
.replace(/(\d)(\d{4})/, '$1 $2')
.replace(/(\d{4})(\d{4})/, '$1-$2')
} }
const ReceberRespostaAPIdoCPF = async (cpf) =>{
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
myHeaders.append("Content-Type", "application/json");
const FormatCPF = (valor) => { var raw = JSON.stringify({
"cpf": cpf
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
return digits
.replace(/(\d{3})(\d)/, '$1.$2') // 123 -> 123.
.replace(/(\d{3})(\d)/, '$1.$2') // 123.456 -> 123.456.
.replace(/(\d{3})(\d{1,2})$/, '$1-$2'); // 123.456.789 -> 123.456.789-01
}
const [formData, setFormData] = useState({
nome: PatientDict.nome,
nomeSocial: PatientDict.nome_social,
dataNascimento: PatientDict.data_nascimento,
genero: PatientDict.sexo,
documento: '',
numeroDocumento: '',
cpf: PatientDict.cpf,
profissao: PatientDict.profissao ,
nomeMae: PatientDict.nome_mae,
profissaoMae: PatientDict.profissao_mae,
nomePai: PatientDict.nome_pai,
profissaoPai: PatientDict.profissao_pai,
nomeResponsavel: '',
cpfResponsavel: '',
nomeConjuge: '',
outroId: '',
cep: '',
cidade: PatientDict.cidade,
estado: PatientDict.estado,
bairro: PatientDict.bairro,
rua: PatientDict.logradouro,
numero: '',
complemento: '',
email: PatientDict.email,
telefone1: PatientDict.celular,
telefone2: '',
telefone3: '',
observacoes: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
}); });
var requestOptions = {
if(name.includes('cpf')){ method: 'POST',
headers: myHeaders,
let cpfFormatado = FormatCPF(e.target.value) body: raw,
redirect: 'follow'
setFormData({...formData,
[name]: cpfFormatado,}
)}
else if(name.includes('telefone')){
let telefoneFormatado = FormatTelefones(value)
console.log(telefoneFormatado)
setFormData({...formData,
[name]: telefoneFormatado
})
}
else if(name.includes('cep')){
const digitsCep = String(value).replace(/\D/g, '').slice(0, 8);
setFormData({...formData,
[name]: digitsCep
})
}
}; };
const response = await fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes/validar-cpf", requestOptions)
const result = await response.json()
return result.data
}
const ValidarCPF = async (cpf) => {
let aviso
let Erro = false
const resutadoAPI = await ReceberRespostaAPIdoCPF(cpf)
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
}
return [Erro,aviso]
}
const FormatCPF = (valor) => {
const digits = String(valor).replace(/\D/g, '').slice(0, 11);
return digits
.replace(/(\d{3})(\d)/, '$1.$2')
.replace(/(\d{3})(\d)/, '$1.$2')
.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
}
// Estado para armazenar a URL da foto do avatar
const [avatarUrl, setAvatarUrl] = useState(null);
// 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
infoMedicas: false,
infoConvenio: false,
endereco: false,
contato: false,
});
// Função para alternar o estado de colapso de uma seção
const handleToggleCollapse = (section) => {
setCollapsedSections(prevState => ({
...prevState,
[section]: !prevState[section]
}));
};
// Lógica para calcular o IMC
useEffect(() => {
const peso = parseFloat(formData.peso);
const altura = parseFloat(formData.altura);
if (peso > 0 && altura > 0) {
const imcCalculado = peso / (altura * altura);
setFormData(prev => ({ ...prev, imc: imcCalculado.toFixed(2) }));
} else {
setFormData(prev => ({ ...prev, imc: '' }));
}
}, [formData.peso, formData.altura]);
const [enderecoData, setEnderecoData] = useState({})
useEffect(() => {setEnderecoData(formData.endereco || {}); console.log(enderecoData)}, [formData.endereco])
const [contato, setContato] = useState({})
useEffect(() => {setContato(formData.contato || {})}, [formData.contato])
const handleChange = (e) => {
const { name, value, type, checked, files } = e.target;
console.log(formData, name)
if (type === 'checkbox') {
setFormData({ ...formData, [name]: checked });
} else if (type === 'file') {
setFormData({ ...formData, [name]: files[0] });
// Lógica para pré-visualizar a imagem no avatar
if (name === 'foto' && files[0]) {
const reader = new FileReader();
reader.onloadend = () => {
setAvatarUrl(reader.result);
};
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 }));
}else if(name.includes('endereco')) {
setEnderecoData(prev => ({ ...prev, [name.split('.')[1]]: value }));
}else{
setFormData({ ...formData, [name]: value });
}
};
// Função para buscar endereço pelo CEP
const handleCepBlur = async () => { const handleCepBlur = async () => {
const cep = formData.cep.replace(/\D/g, ''); const cep = formData.cep.replace(/\D/g, '');
if (cep.length === 8) { if (cep.length === 8) {
@ -126,198 +167,419 @@ function PatientForm({ onSave, onCancel, PatientDict }) {
} }
}; };
const handleSubmit = () => { const handleSubmit = async () => {
if (!formData.nome || !formData.cpf || !formData.genero || !formData.dataNascimento || !formData.email||!formData.telefone1){ if (!formData.nome || !formData.cpf || !formData.sexo || !formData.data_nascimento){
alert('Por favor, preencha: Nome ,CPF, Gênero, Data de nascimento, telefone e Email.'); alert('Por favor, preencha Nome ,CPF, Gênero e data de nascimento.');
return; return;
} }
onSave( const CPFinvalido = await ValidarCPF(formData.cpf)
{nome: formData.nome, console.log(CPFinvalido)
nomeSocial: formData.nomeSocial, if(CPFinvalido[0] === true){
dataNascimento: formData.dataNascimento, alert(CPFinvalido[1])
genero: formData.genero, return
documento: formData.documento, }
numeroDocumento: formData.numeroDocumento,
cpf: formData.cpf,
profissao: formData.profissao,
nomeMae: formData.nomeMae,
profissaoMae: formData.profissaoMae,
nomePai: formData.nomePai,
profissaoPai: formData.profissaoPai,
nomeResponsavel: formData.nomeResponsavel,
cpfResponsavel: formData.cpfResponsavel,
nomeConjuge: formData.nomeConjuge,
outroId: formData.outroId,
endereco: {
cep: formData.cep,
cidade: formData.cidade,
estado: formData.estado,
bairro: formData.bairro,
logradouro: formData.rua,
numero: formData.numero,
complemento: formData.complemento,
},
contato: {
email: formData.email,
telefone1: formData.telefone1,
telefone2: formData.telefone2,
telefone3: formData.telefone3,
},
observacoes: formData.observacoes,
}
); onSave({
...formData,
endereco: {
cep: enderecoData.cep,
cidade: enderecoData.cidade,
estado: enderecoData.estado,
bairro: enderecoData.bairro,
logradouro: enderecoData.logradouro,
numero: enderecoData.numero,
complemento: enderecoData.complemento,
},
contato: {
email: contato.email,
telefone1: contato.telefone1,
telefone2: contato.telefone2,
telefone3: contato.telefone3,
},
infoMedicas: {
tipoSanguineo: formData.tipoSanguineo,
peso: formData.peso,
altura: formData.altura,
imc: formData.imc,
alergias: formData.alergias,
},
infoConvenio: {
convenio: formData.convenio,
plano: formData.plano,
numeroMatricula: formData.numeroMatricula,
validadeCarteira: formData.validadeCarteira,
validadeIndeterminada: formData.validadeIndeterminada,
pacienteVip: formData.pacienteVip,
},
});
}; };
return ( return (
<div className="card p-3"> <div className="card p-3">
<h3 className="mb-3 text-center">MediConnect</h3> <h3 className="mb-4 text-center" style={{ fontSize: '2.5rem' }}>MediConnect</h3>
{/* ------------------ DADOS PESSOAIS ------------------ */} {/* DADOS PESSOAIS */}
<h5 className="mb-3">Dados Pessoais</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="row"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('dadosPessoais')} style={{ fontSize: '1.8rem' }}>
<div className="col-md-6 mb-3"> Dados Pessoais
<label>Nome: *</label> <span className="fs-5">
<input type="text" className="form-control" name="nome" value={formData.nome} onChange={handleChange} /> {collapsedSections.dadosPessoais ? '▲' : '▼'}
</div> </span>
<div className="col-md-6 mb-3"> </h4>
<label>Nome social:</label> <div className={`collapse${collapsedSections.dadosPessoais ? ' show' : ''}`}>
<input type="text" className="form-control" name="nomeSocial" value={formData.nomeSocial} onChange={handleChange} /> <div className="row mt-3">
</div> {/* AVATAR E INPUT DE FOTO */}
<div className="col-md-6 mb-3"> <div className="col-md-6 mb-3 d-flex align-items-center">
<label>Data de nascimento: *</label> <div className="me-3">
<input type="date" className="form-control" name="dataNascimento" value={formData.dataNascimento} onChange={handleChange} /> {avatarUrl ? (
</div> <img
<div className="col-md-6 mb-3"> src={avatarUrl}
<label>Gênero: *</label> alt="Avatar do Paciente"
<select className="form-control" name="genero" value={formData.genero} onChange={handleChange}> style={{ width: '100px', height: '100px', borderRadius: '50%', objectFit: 'cover' }}
<option value="">Selecione</option> />
<option value="Masculino">Masculino</option> ) : (
<option value="Feminino">Feminino</option> <div
<option value="Outro">Outro</option> style={{
</select> width: '100px',
</div> height: '100px',
<div className="col-md-6 mb-3"> borderRadius: '50%',
<label>Outro documento:</label> backgroundColor: '#e0e0e0',
<input type="text" className="form-control" name="documento" value={formData.documento} onChange={handleChange} /> display: 'flex',
</div> alignItems: 'center',
<div className="col-md-6 mb-3"> justifyContent: 'center',
<label>Número do documento:</label> fontSize: '3.5rem',
<input type="text" className="form-control" name="numeroDocumento" value={formData.numeroDocumento} onChange={handleChange} /> color: '#9e9e9e'
</div> }}
<div className="col-md-6 mb-3"> >
<label>CPF: *</label> &#x2624;
<input type="text" className="form-control" name="cpf" value={formData.cpf} onChange={ handleChange} /> </div>
</div> )}
<div className="col-md-6 mb-3"> </div>
<label>Profissão:</label> <div>
<input type="text" className="form-control" name="profissao" value={formData.profissao} onChange={handleChange} /> <label htmlFor="foto-input" className="btn btn-primary" style={{ fontSize: '1rem' }}>Carregar Foto</label>
</div> <input
<div className="col-md-6 mb-3"> type="file"
<label>Nome da Mãe:</label> className="form-control d-none"
<input type="text" className="form-control" name="nomeMae" value={formData.nomeMae} onChange={handleChange} /> name="foto"
</div> id="foto-input"
<div className="col-md-6 mb-3"> onChange={handleChange}
<label>Profissão da mãe:</label> accept="image/*"
<input type="text" className="form-control" name="profissaoMae" value={formData.profissaoMae} onChange={handleChange} /> />
</div> {formData.foto && <span className="ms-2" style={{ fontSize: '1rem' }}>{formData.foto.name}</span>}
<div className="col-md-6 mb-3"> </div>
<label>Nome do Pai:</label> </div>
<input type="text" className="form-control" name="nomePai" value={formData.nomePai} onChange={handleChange} /> {/* CADASTRO */}
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Nome: *</label>
<label>Profissão do pai:</label> <input type="text" className="form-control" name="nome" value={formData.nome} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<input type="text" className="form-control" name="profissaoPai" value={formData.profissaoPai} onChange={handleChange} /> </div>
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Nome social:</label>
<label>Nome do responsável:</label> <input type="text" className="form-control" name="nome_social" value={formData.nome_social} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<input type="text" className="form-control" name="nomeResponsavel" value={formData.nomeResponsavel} onChange={handleChange} /> </div>
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Data de nascimento: *</label>
<label>CPF do responsável:</label> <input type="date" className="form-control" name="data_nascimento" value={formData.data_nascimento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<input type="text" className="form-control" name="cpfResponsavel" value={formData.cpfResponsavel} onChange={handleChange} /> </div>
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Gênero: *</label>
<label>Nome do esposo(a):</label> <select className="form-control" name="sexo" value={formData.sexo} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<input type="text" className="form-control" name="nomeConjuge" value={formData.nomeConjuge} onChange={handleChange} /> <option value="">Selecione</option>
</div> <option value="Masculino">Masculino</option>
<div className="col-md-6 mb-3"> <option value="Feminino">Feminino</option>
<label>Identificador de outro sistema:</label> <option value="Outro">Outro</option>
<input type="text" className="form-control" name="outroId" value={formData.outroId} onChange={handleChange} /> </select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF: *</label>
<input type="text" className="form-control" name="cpf" value={formData.cpf} onChange={ handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>RG:</label>
<input type="text" className="form-control" name="rg" value={formData.rg} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Outros documentos:</label>
<select className="form-control" name="documentoTipo" value={formData.documentoTipo} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="CNH">CNH</option>
<option value="Passaporte">Passaporte</option>
<option value="carteira de trabalho">Carteira de Trabalho</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número do documento:</label>
<input type="text" className="form-control" name="numero_documento" value={formData.numero_documento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Etnia e Raça:</label>
<select className="form-control" name="etniaRaca" value={formData.etniaRaca} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Branca">Branca</option>
<option value="Preta">Preta</option>
<option value="Parda">Parda</option>
<option value="Amarela">Amarela</option>
<option value="Indígena">Indígena</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Naturalidade:</label>
<input type="text" className="form-control" name="naturalidade" value={formData.naturalidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nacionalidade:</label>
<input type="text" className="form-control" name="nacionalidade" value={formData.nacionalidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão:</label>
<input type="text" className="form-control" name="profissao" value={formData.profissao} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado civil:</label>
<select className="form-control" name="estado_civil" value={formData.estado_civil} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<option value="">Selecione</option>
<option value="Solteiro">Solteiro(a)</option>
<option value="Casado">Casado(a)</option>
<option value="Divorciado">Divorciado(a)</option>
<option value="Viuvo">Viúvo(a)</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome da Mãe:</label>
<input type="text" className="form-control" name="nomeMae" value={formData.nome_mae} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão da mãe:</label>
<input type="text" className="form-control" name="profissaoMae" value={formData.profissao_mae} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do Pai:</label>
<input type="text" className="form-control" name="nomePai" value={formData.nome_pai} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Profissão do pai:</label>
<input type="text" className="form-control" name="profissaoPai" value={formData.profissao_pai} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do responsável:</label>
<input type="text" className="form-control" name="nomeResponsavel" value={formData.nome_responsavel} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>CPF do responsável:</label>
<input type="text" className="form-control" name="cpfResponsavel" value={formData.cpf_responsavel} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Nome do esposo(a):</label>
<input type="text" className="form-control" name="nomeConjuge" value={formData.nome_conjuge} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Identificador de outro sistema:</label>
<input type="text" className="form-control" name="outroId" value={formData.outroId} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-12 mb-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" name="rnConvenio" checked={formData.rnConvenio} onChange={handleChange} id="rnConvenio" style={{ transform: 'scale(1.2)' }} />
<label className="form-check-label ms-2" htmlFor="rnConvenio" style={{ fontSize: '1.1rem' }}>
RN na Guia do convênio
</label>
</div>
</div>
{/* CAMPOS MOVIDOS */}
<div className="col-md-12 mb-3 mt-3">
<label style={{ fontSize: '1.1rem' }}>Observações:</label>
<textarea className="form-control" name="observacoes" value={formData.observacoes} onChange={handleChange} style={{ fontSize: '1.1rem' }}></textarea>
</div>
<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>
<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>
</div>
</div>
</div> </div>
</div> </div>
{/* ------------------ ENDEREÇO ------------------ */} {/* INFORMAÇÕES MÉDICAS */}
<h5>Endereço</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="row"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('infoMedicas')} style={{ fontSize: '1.8rem' }}>
<div className="col-md-4 mb-3"> Informações Médicas
<label>CEP:</label> <span className="fs-5">
<input type="text" className="form-control" name="cep" value={formData.cep} onChange={handleChange} onBlur={handleCepBlur} /> {collapsedSections.infoMedicas ? '▲' : '▼'}
</div> </span>
<div className="col-md-8 mb-3"> </h4>
<label>Rua:</label> <div className={`collapse${collapsedSections.infoMedicas ? ' show' : ''}`}>
<input type="text" className="form-control" name="rua" value={formData.rua} onChange={handleChange} /> <div className="row mt-3">
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Tipo Sanguíneo:</label>
<label>Bairro:</label> <select className="form-control" name="tipoSanguineo" value={formData.tipoSanguineo} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<input type="text" className="form-control" name="bairro" value={formData.bairro} onChange={handleChange} /> <option value="">Selecione</option>
</div> <option value="A+">A+</option>
<div className="col-md-4 mb-3"> <option value="A-">A-</option>
<label>Cidade:</label> <option value="B+">B+</option>
<input type="text" className="form-control" name="cidade" value={formData.cidade} onChange={handleChange} /> <option value="B-">B-</option>
</div> <option value="AB+">AB+</option>
<div className="col-md-2 mb-3"> <option value="AB-">AB-</option>
<label>Estado:</label> <option value="O+">O+</option>
<input type="text" className="form-control" name="estado" value={formData.estado} onChange={handleChange} /> <option value="O-">O-</option>
</div> </select>
<div className="col-md-4 mb-3"> </div>
<label>Número:</label> <div className="col-md-2 mb-3">
<input type="text" className="form-control" name="numero" value={formData.numero} onChange={handleChange} /> <label style={{ fontSize: '1.1rem' }}>Peso (kg):</label>
</div> <input type="number" step="0.1" className="form-control" name="peso" value={formData.peso} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
<div className="col-md-8 mb-3"> </div>
<label>Complemento:</label> <div className="col-md-2 mb-3">
<input type="text" className="form-control" name="complemento" value={formData.complemento} onChange={handleChange} /> <label style={{ fontSize: '1.1rem' }}>Altura (m):</label>
<input type="number" step="0.01" className="form-control" name="altura" value={formData.altura} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>IMC (kg/):</label>
<input type="text" className="form-control" name="imc" value={formData.imc} readOnly disabled style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-12 mb-3">
<label style={{ fontSize: '1.1rem' }}>Alergias:</label>
<textarea className="form-control" name="alergias" value={formData.alergias} onChange={handleChange} placeholder="Ex: AAS, Dipirona, etc" style={{ fontSize: '1.1rem' }}></textarea>
</div>
</div>
</div> </div>
</div> </div>
{/* ------------------ CONTATO ------------------ */} {/* INFORMAÇÕES DE CONVÊNIO */}
<h5>Contato</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="row"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('infoConvenio')} style={{ fontSize: '1.8rem' }}>
<div className="col-md-6 mb-3"> Informações de convênio
<label>Email: *</label> <span className="fs-5">
<input type="email" className="form-control" name="email" value={formData.email} onChange={handleChange} /> {collapsedSections.infoConvenio ? '▲' : '▼'}
</div> </span>
<div className="col-md-6 mb-3"> </h4>
<label>Telefone: *</label> <div className={`collapse${collapsedSections.infoConvenio ? ' show' : ''}`}>
<input type="text" className="form-control" name="telefone1" value={formData.telefone1} onChange={handleChange} /> <div className="row mt-3">
</div> <div className="col-md-6 mb-3">
<div className="col-md-6 mb-3"> <label style={{ fontSize: '1.1rem' }}>Convênio:</label>
<label>Telefone 2:</label> <select className="form-control" name="convenio" value={formData.convenio} onChange={handleChange} style={{ fontSize: '1.1rem' }}>
<input type="text" className="form-control" name="telefone2" value={formData.telefone2} onChange={handleChange} /> <option value="">Selecione</option>
</div> <option value="Amil">Amil</option>
<div className="col-md-6 mb-3"> <option value="Bradesco Saúde">Bradesco Saúde</option>
<label>Telefone 3:</label> <option value="SulAmérica">SulAmérica</option>
<input type="text" className="form-control" name="telefone3" value={formData.telefone3} onChange={handleChange} /> <option value="Unimed">Unimed</option>
</select>
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Plano:</label>
<input type="text" className="form-control" name="plano" value={formData.plano} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}> de matrícula:</label>
<input type="text" className="form-control" name="numeroMatricula" value={formData.numeroMatricula} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Validade da Carteira:</label>
<input type="date" className="form-control" name="validadeCarteira" value={formData.validadeCarteira} onChange={handleChange} disabled={formData.validadeIndeterminada} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-2 d-flex align-items-end mb-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" name="validadeIndeterminada" checked={formData.validadeIndeterminada} onChange={handleChange} id="validadeIndeterminada" style={{ transform: 'scale(1.2)' }} />
<label className="form-check-label ms-2" htmlFor="validadeIndeterminada" style={{ fontSize: '1.1rem' }}>
Validade indeterminada
</label>
</div>
</div>
{/* PACIENTE VIP */}
<div className="col-md-12 mb-3 mt-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" name="pacienteVip" checked={formData.pacienteVip} onChange={handleChange} id="pacienteVip" style={{ transform: 'scale(1.2)' }} />
<label className="form-check-label ms-2" htmlFor="pacienteVip" style={{ fontSize: '1.1rem' }}>
Paciente VIP
</label>
</div>
</div>
</div>
</div> </div>
</div> </div>
{/* ------------------ INFORMAÇÕES ADICIONAIS ------------------ */} {/* ENDEREÇO */}
<h5>Informações Adicionais</h5> <div className="mb-5 p-4 border rounded shadow-sm">
<div className="mb-3"> <h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('endereco')} style={{ fontSize: '1.8rem' }}>
<label>Observações:</label> Endereço
<textarea className="form-control" name="observacoes" value={formData.observacoes} onChange={handleChange}></textarea> <span className="fs-5">
{collapsedSections.endereco ? '▲' : '▼'}
</span>
</h4>
<div className={`collapse${collapsedSections.endereco ? ' show' : ''}`}>
<div className="row mt-3">
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>CEP:</label>
<input type="text" className="form-control" name="endereco.cep" value={enderecoData.cep} onChange={handleChange} onBlur={handleCepBlur} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-8 mb-3">
<label style={{ fontSize: '1.1rem' }}>Rua:</label>
<input type="text" className="form-control" name="endereco.logradouro" value={enderecoData.logradouro} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Bairro:</label>
<input type="text" className="form-control" name="endereco.bairro" value={enderecoData.bairro} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Cidade:</label>
<input type="text" className="form-control" name="endereco.cidade" value={enderecoData.cidade} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-2 mb-3">
<label style={{ fontSize: '1.1rem' }}>Estado:</label>
<input type="text" className="form-control" name="endereco.estado" value={enderecoData.estado} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-4 mb-3">
<label style={{ fontSize: '1.1rem' }}>Número:</label>
<input type="text" className="form-control" name="endereco.numero" value={enderecoData.numero} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-8 mb-3">
<label style={{ fontSize: '1.1rem' }}>Complemento:</label>
<input type="text" className="form-control" name="endereco.complemento" value={enderecoData.complemento} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
</div>
</div>
</div>
{/* CONTATO */}
<div className="mb-5 p-4 border rounded shadow-sm">
<h4 className="mb-4 cursor-pointer d-flex justify-content-between align-items-center" onClick={() => handleToggleCollapse('contato')} style={{ fontSize: '1.8rem' }}>
Contato
<span className="fs-5">
{collapsedSections.contato ? '▲' : '▼'}
</span>
</h4>
<div className={`collapse${collapsedSections.contato ? ' show' : ''}`}>
<div className="row mt-3">
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Email:</label>
<input type="email" className="form-control" name="email" value={contato.email || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone:</label>
<input type="text" className="form-control" name="telefone1" value={contato.telefone1 || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 2:</label>
<input type="text" className="form-control" name="telefone2" value={contato.telefone2 || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
<div className="col-md-6 mb-3">
<label style={{ fontSize: '1.1rem' }}>Telefone 3:</label>
<input type="text" className="form-control" name="telefone3" value={contato.telefone3 || ''} onChange={handleChange} style={{ fontSize: '1.1rem' }} />
</div>
</div>
</div>
</div> </div>
{/* Botões */} {/* Botões */}
<div className="mt-3"> <div className="mt-3 text-center">
<button className="btn btn-success me-2" onClick={handleSubmit}> <button className="btn btn-success me-3" onClick={handleSubmit} style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}>
Salvar Paciente Salvar Paciente
</button> </button>
<button className="btn btn-light" onClick={onCancel}> <button className="btn btn-light" onClick={onCancel} style={{ fontSize: '1.2rem', padding: '0.75rem 1.5rem' }}>
Cancelar Cancelar
</button> </button>
</div> </div>

View File

@ -4,28 +4,45 @@
"isTitle": true "isTitle": true
}, },
{
"name":"Início",
"url": "Inicio",
"icon": "house"
},
{ {
"name": "Cadastro de Pacientes", "name": "Cadastro de Pacientes",
"url": "form-layout", "url": "form-layout",
"icon": "file-earmark-medical-fill" "icon": "heart-pulse-fill"
}, },
{ {
"name": "Cadastro do Médico", "name": "Cadastro do Médico",
"url": "doctor-form-layout", "url": "doctor-form-layout",
"icon": "file-earmark-medical-fill" "icon": "capsule"
}, },
{ {
"name": "Lista de Pacientes", "name": "Lista de Pacientes",
"icon": "table", "icon": "clipboard-heart-fill",
"url": "table" "url": "table"
}, },
{ {
"name": "Lista de Médico", "name": "Lista de Médico",
"icon": "table", "icon": "hospital-fill",
"url": "doctor-table" "url": "doctor-table"
},
{
"name": "Agendar consulta",
"icon": "calendar-plus-fill",
"url": "agendamento"
},
{
"name": "Laudo do Paciente",
"icon": "table",
"url": "laudo-manager"
} }
] ]

View File

@ -1,409 +0,0 @@
[
{
"name": "Dashboard",
"url": "dashboard",
"icon": "grid-fill"
},
{
"name": "Components",
"key": "component",
"icon": "stack",
"submenu": [
{
"name": "Alert",
"url": "component-alert.html"
},
{
"name": "Badge",
"url": "component-badge.html"
},
{
"name": "Breadcrumb",
"url": "component-breadcrumb.html"
},
{
"name": "Button",
"url": "component-button.html"
},
{
"name": "Card",
"url": "component-card.html"
},
{
"name": "Carousel",
"url": "component-carousel.html"
},
{
"name": "Collapse",
"url": "component-collapse.html"
},
{
"name": "Dropdown",
"url": "component-dropdown.html"
},
{
"name": "List Group",
"url": "component-list-group.html"
},
{
"name": "Modal",
"url": "component-modal.html"
},
{
"name": "Navs",
"url": "component-navs.html"
},
{
"name": "Pagination",
"url": "component-pagination.html"
},
{
"name": "Progress",
"url": "component-progress.html"
},
{
"name": "Spinner",
"url": "component-spinner.html"
},
{
"name": "Tooltip",
"url": "component-tooltip.html"
},
{
"name": "Extra Components",
"key": "extra-component",
"icon": "collection-fill",
"submenu": [
{
"name": "Avatar",
"url": "extra-component-avatar.html"
},
{
"name": "Comment",
"url": "extra-component-comment.html"
},
{
"name": "Sweet Alert",
"url": "extra-component-sweetalert.html"
},
{
"name": "Toastify",
"url": "extra-component-toastify.html"
},
{
"name": "Rating",
"url": "extra-component-rating.html"
},
{
"name": "Divider",
"url": "extra-component-divider.html"
},
{
"name": "GLightbox",
"url": "extra-component-glightbox.html"
}
]
}
]
},
{
"name": "Layouts",
"key": "layout",
"icon": "grid-1x2-fill",
"submenu": [
{
"name": "Default Layout",
"url": "layout-default.html"
},
{
"name": "1 Column",
"url": "layout-vertical-1-column.html"
},
{
"name": "Vertical Navbar",
"url": "layout-vertical-navbar.html"
},
{
"name": "RTL Layout",
"url": "layout-rtl.html"
},
{
"name": "Horizontal Menu",
"url": "layout-horizontal.html"
}
]
},
{
"name": "Forms",
"key": "form",
"icon": "file-earmark-medical-fill",
"submenu": [
{
"name": "Form Elements",
"key": "form-element",
"icon": "hexagon-fill",
"submenu": [
{
"name": "Input",
"url": "form-element-input.html"
},
{
"name": "Input Group",
"url": "form-element-input-group.html"
},
{
"name": "Select",
"url": "form-element-select.html"
},
{
"name": "Radio",
"url": "form-element-radio.html"
},
{
"name": "Checkbox",
"url": "form-element-checkbox.html"
},
{
"name": "Textarea",
"url": "form-element-textarea.html"
}
]
},
{
"name": "Form Layout",
"url": "form-layout",
"icon": "file-earmark-medical-fill"
},
{
"name": "Form Validation",
"icon": "journal-check",
"key": "form-validation",
"submenu": [
{
"name": "Parsley",
"url": "form-validation-parsley.html"
}
]
},
{
"name": "Form Editor",
"icon": "pen-fill",
"key": "form-editor",
"submenu": [
{
"name": "Quill",
"url": "form-editor-quill.html"
},
{
"name": "CKEditor",
"url": "form-editor-ckeditor.html"
},
{
"name": "Summernote",
"url": "form-editor-summernote.html"
},
{
"name": "TinyMCE",
"url": "form-editor-tinymce.html"
}
]
}
]
},
{
"name": "Table",
"icon": "table",
"submenu": [
{
"name": "Table",
"url": "table",
"icon": "file-earmark-spreadsheet-fill"
},
{
"name": "Datatable",
"url": "table-datatable.html",
"icon": "file-earmark-spreadsheet-fill"
},
{
"name": "Datatable (jQuery)",
"url": "table-datatable-jquery.html",
"icon": "file-earmark-spreadsheet-fill"
}
]
},
{
"name": "Extras",
"key": "extras",
"icon": "plus-square-fill",
"submenu": [
{
"name": "Widgets",
"key": "ui-widgets",
"icon": "pentagon-fill",
"submenu": [
{
"name": "Chatbox",
"url": "ui-widgets-chatbox.html"
},
{
"name": "Pricing",
"url": "ui-widgets-pricing.html"
},
{
"name": "To-do List",
"url": "ui-widgets-todolist.html"
}
]
},
{
"name": "Icons",
"key": "ui-icons",
"icon": "egg-fill",
"submenu": [
{
"name": "Bootstrap Icons ",
"url": "ui-icons-bootstrap-icons.html"
},
{
"name": "Fontawesome",
"url": "ui-icons-fontawesome.html"
},
{
"name": "Dripicons",
"url": "ui-icons-dripicons.html"
}
]
},
{
"name": "Charts",
"key": "ui-chart",
"icon": "bar-chart-fill",
"submenu": [
{
"name": "ChartJS",
"url": "ui-chart-chartjs.html"
},
{
"name": "Apexcharts",
"url": "ui-chart-apexcharts.html"
}
]
}
]
},
{
"name": "Pages",
"key": "pages",
"icon": "file-earmark-fill",
"submenu": [
{
"name": "Authentication",
"key": "auth",
"icon": "person-badge-fill",
"submenu": [
{
"name": "Login",
"url": "auth-login.html"
},
{
"name": "Register",
"url": "auth-register.html"
},
{
"name": "Forgot Password",
"url": "auth-forgot-password.html"
}
]
},
{
"name": "Errors",
"key": "error",
"icon": "x-octagon-fill",
"submenu": [
{
"name": "403",
"url": "error-403.html"
},
{
"name": "404",
"url": "error-404.html"
},
{
"name": "500",
"url": "error-500.html"
}
]
},
{
"name": "File Uploader",
"key": "ui-file",
"icon": "cloud-arrow-up-fill",
"url": "ui-file-uploader.html"
},
{
"name": "Maps",
"key": "ui-map",
"icon": "map-fill",
"submenu": [
{
"name": "Google Map",
"url": "ui-map-google-map.html"
},
{
"name": "JS Vector Map",
"url": "ui-map-jsvectormap.html"
}
]
},
{
"name": "Email Application",
"key": "application-email",
"icon": "envelope-fill",
"url": "application-email.html"
},
{
"name": "Chat Application",
"key": "application-chat",
"icon": "chat-dots-fill",
"url": "application-chat.html"
},
{
"name": "Photo Gallery",
"key": "application-gallery",
"icon": "image-fill",
"url": "application-gallery.html"
},
{
"name": "Checkout Page",
"key": "application-checkout",
"icon": "basket-fill",
"url": "application-checkout.html"
}
]
},
{
"name": "Support",
"key": "error",
"icon": "life-preserver",
"submenu": [
{
"name": "Documentation",
"key": "error",
"icon": "life-preserver",
"url": "https://zuramai.github.io/mazer/docs"
},
{
"name": "Contribute",
"key": "error",
"url": "https://github.com/zuramai/mazer/blob/main/CONTRIBUTING.md",
"icon": "puzzle"
},
{
"name": "Donate",
"key": "error",
"url": "https://github.com/zuramai/mazer#donation",
"icon": "cash"
}
]
}
]

183
src/pages/Agendamento.jsx Normal file
View File

@ -0,0 +1,183 @@
import React from 'react'
import TabelaAgendamentoDia from '../components/AgendarConsulta/TabelaAgendamentoDia';
import TabelaAgendamentoSemana from '../components/AgendarConsulta/TabelaAgendamentoSemana';
import TabelaAgendamentoMes from '../components/AgendarConsulta/TabelaAgendamentoMes';
import FormNovaConsulta from '../components/AgendarConsulta/FormNovaConsulta';
import { useState} from 'react';
import dayjs from 'dayjs'
import "./style/Agendamento.css";
const Agendamento = () => {
const ListarDiasdoMes = (ano, mes) => {
let segundas = []; let tercas = []; let quartas = []; let quintas = []; let sextas = []
const base = dayjs(`${ano}-${mes}-01`)
const DiasnoMes= base.daysInMonth()
for(let d = 1; d <= DiasnoMes; d++){
const data = dayjs(`${ano}-${mes}-${d}`)
const dia = data.format('dddd')
switch(dia){
case'Monday':
segundas.push(d)
break
case'Tuesday':
tercas.push(d)
break
case'Wednesday':
quartas.push(d)
break
case'Thursday':
quintas.push(d)
break
case'Friday':
sextas.push(d)
break
default:
break
}}
let ListaDiasDatas = [segundas,tercas, quartas, quintas,sextas]
console.log('dentro da função', ListaDiasDatas)
return ListaDiasDatas
}
const [tabela, setTabela] = useState('diario')
const [PageNovaConsulta, setPageConsulta] = useState(false)
const handleClickAgendamento = (agendamento) => {
if(agendamento.status !== 'vazio')return
else{
setPageConsulta(true)
}
}
const handleClickCancel = () => {
setPageConsulta(false)
}
return(
<div>
<h1>Agendar nova consulta</h1>
{!PageNovaConsulta? (
<div className='atendimento-eprocura'>
<div className='busca-atendimento'>
<div className="">
<i className="fa-solid fa-calendar-day"></i>
<input type="text" placeholder="Buscar atendimento" />
</div>
<div>
<select>
<option value="" disabled selected>Agendar</option>
<option value="">Atendimento</option>
<option value="">Sessões</option>
<option value="">Urgência</option>
</select>
</div>
</div>
<div className='unidade-selecionarprofissional'>
<select>
<option value="" disabled selected >Unidade</option>
<option value="">Unidade Central</option>
<option value="">Unidade Zona Norte</option>
<option value="">Unidade Zona Oeste</option>
</select>
<input type="text" placeholder='Selecionar profissional' />
</div>
<div className='diadia'>
<div>
<button className={`btn-selecionar-tabeladia ${tabela === "diario" ? "ativo" : ""}`}
onClick={() => setTabela("diario")}>
<i className="fa-solid fa-calendar-day"></i>
Dia
</button>
<button className={`btn-selecionar-tabelasemana ${tabela === 'semanal' ? 'ativo': ""}`} onClick={() => setTabela("semanal")}>
<i className="fa-solid fa-calendar-day"></i>
Semana
</button>
<button className={`btn-selecionar-tabelames ${tabela === 'mensal' ? 'ativo':''}`} onClick={() => setTabela("mensal")}>
<i className="fa-solid fa-calendar-day"></i>
Mês
</button>
<div className='legenda-tabela'>
<div className='legenda-item-realizado'>
<span>Realizado</span>
</div>
<div className='legenda-item-confirmado'>
<span>Confirmado</span>
</div>
<div className='legenda-item-agendado'>
<span>Agendado</span>
</div>
<div className='legenda-item-cancelado'>
<span>Cancelado</span>
</div>
</div>
{tabela === "diario" && (
<TabelaAgendamentoDia
handleClickAgendamento={handleClickAgendamento}
/>
)}
{tabela === 'semanal' &&
<TabelaAgendamentoSemana/>
}
{tabela === 'mensal' && (
<TabelaAgendamentoMes
ListarDiasdoMes={ListarDiasdoMes}
aplicarCores={true} // 🔹 Ativa a lógica de cores só no mensal
/>
)}
</div>
</div>
</div>
) : (
<FormNovaConsulta onCancel={handleClickCancel} />
)}
</div>
)
}
export default Agendamento

View File

@ -1,110 +0,0 @@
import React, { useState } from "react";
function ChatApp() {
const [selectedUser, setSelectedUser] = useState(1);
const users = [
{ id: 1, name: "Alice", avatar: "https://i.pravatar.cc/40?img=1" },
{ id: 2, name: "Bob", avatar: "https://i.pravatar.cc/40?img=2" },
{ id: 3, name: "Charlie", avatar: "https://i.pravatar.cc/40?img=3" }
];
const messages = {
1: [
{ from: "Alice", text: "Oi Rafael 👋", time: "10:15" },
{ from: "Você", text: "E aí Alice, tudo bem?", time: "10:16" },
{ from: "Alice", text: "Tudo ótimo! 😄", time: "10:17" }
],
2: [
{ from: "Bob", text: "Fala mano!", time: "09:30" },
{ from: "Você", text: "Tranquilo Bob?", time: "09:32" },
{ from: "Bob", text: "Sempre ✌️", time: "09:33" }
],
3: [
{ from: "Charlie", text: "Já viu a novidade?", time: "Ontem" },
{ from: "Você", text: "Qual novidade?", time: "Ontem" },
{ from: "Charlie", text: "Te conto depois kkk 🤐", time: "Ontem" }
]
};
const currentMessages = messages[selectedUser] || [];
return (
<>
<div className="page-heading">
<h3>Chat Application</h3>
<p className="text-muted">Converse em tempo real com seus contatos</p>
</div>
<div className="page-content">
<div className="row">
{/* Sidebar - Contatos */}
<div className="col-3 border-end">
<h6 className="mb-3">Contatos</h6>
<ul className="list-group">
{users.map((user) => (
<li
key={user.id}
className={`list-group-item d-flex align-items-center ${
selectedUser === user.id ? "active" : ""
}`}
style={{ cursor: "pointer" }}
onClick={() => setSelectedUser(user.id)}
>
<img
src={user.avatar}
alt={user.name}
className="rounded-circle me-2"
width={40}
height={40}
/>
<span>{user.name}</span>
</li>
))}
</ul>
</div>
{/* Chat */}
<div className="col-9 d-flex flex-column" style={{ height: "70vh" }}>
<div
className="flex-grow-1 border rounded p-3 mb-2 overflow-auto"
style={{ background: "#f9f9f9" }}
>
{currentMessages.map((msg, index) => (
<div
key={index}
className={`mb-2 ${
msg.from === "Você" ? "text-end" : "text-start"
}`}
>
<div
className={`d-inline-block p-2 rounded ${
msg.from === "Você"
? "bg-primary text-white"
: "bg-light"
}`}
>
<strong>{msg.from}:</strong> {msg.text}
</div>
<div className="small text-muted">{msg.time}</div>
</div>
))}
</div>
{/* Caixa de mensagem */}
<div className="d-flex">
<input
type="text"
className="form-control me-2"
placeholder="Digite sua mensagem..."
/>
<button className="btn btn-primary">Enviar</button>
</div>
</div>
</div>
</div>
</>
);
}
export default ChatApp;

View File

@ -1,27 +0,0 @@
import React from "react";
function DataTable() {
return (
<>
<div className="page-heading">
<h3>Data Table</h3>
</div>
<div className="page-content">
<section className="row">
<div className="col-12">
<div className="card">
<div className="card-header">
<h4 className="card-title">Minha Data Table</h4>
</div>
<div className="card-body">
<p>Aqui você vai construir sua DataTable futuramente 🚀</p>
</div>
</div>
</div>
</section>
</div>
</>
);
}
export default DataTable;

View File

@ -1,148 +1,272 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import avatarPlaceholder from '../assets/images/avatar_placeholder.png';
const Details = ({ patientID, setCurrentPage }) => { const Details = ({ patientID, setCurrentPage }) => {
const [paciente, setPaciente] = useState(null); const [paciente, setPaciente] = useState({});
useEffect(() => { useEffect(() => {
if (!patientID) return; if (!patientID) return;
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}`) fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}`)
.then(res => res.json()) .then(res => res.json())
.then(data => setPaciente(data))
.then(result => {setPaciente(result.data)})
.catch(err => console.error("Erro ao buscar paciente:", err)); .catch(err => console.error("Erro ao buscar paciente:", err));
}, [patientID]); }, [patientID]);
if (!paciente) return <p style={{ textAlign: "center" }}>Carregando...</p>;
return ( return (
<div style={styles.container}> <>
{/* Botão voltar */} <div className="card p-3 shadow-sm">
<button style={styles.backBtn} onClick={() => setCurrentPage("tabela")}> <h3 className="mb-3 text-center">MediConnect</h3>
Voltar <hr />
</button> <div className="d-flex justify-content-between align-items-center mb-3">
{/* Cabeçalho */} <div className="d-flex mb-3">
<div style={styles.header}> <div className="avatar avatar-xl">
<img <img src={avatarPlaceholder} alt="" />
src="https://via.placeholder.com/80" </div>
alt="Foto do paciente" <div className="media-body ms-3 font-extrabold">
style={styles.avatar} <span>{paciente.nome || "Nome Completo"}</span>
/> <p>{paciente.cpf || "CPF"}</p>
<h2 style={styles.nome}> </div>
{paciente.nomeCompleto}
</h2>
<p style={styles.cpf}>CPF: {paciente.cpf}</p>
<button style={styles.editBtn}>Editar</button>
</div>
{/* Dados pessoais */}
<div style={styles.section}>
<h3 style={styles.sectionTitle}>Dados Pessoais</h3>
<div style={styles.grid}>
<div style={styles.item}><strong>Nome Completo:</strong> {paciente.nomeCompleto}</div>
<div style={styles.item}><strong>CPF:</strong> {paciente.cpf}</div>
<div style={styles.item}><strong>Idade:</strong> {paciente.idade}</div>
<div style={styles.item}><strong>Data de Nascimento:</strong> {paciente.dataNascimento}</div>
<div style={styles.item}><strong>Sexo:</strong> {paciente.sexo}</div>
<div style={styles.item}><strong>Estado Civil:</strong> {paciente.estadoCivil}</div>
<div style={styles.item}><strong>Profissão:</strong> {paciente.profissao}</div>
<div style={styles.item}><strong>Nacionalidade:</strong> {paciente.nacionalidade}</div>
<div style={styles.item}><strong>Naturalidade:</strong> {paciente.naturalidade}</div>
<div style={styles.item}><strong>Etnia:</strong> {paciente.etnia}</div>
</div> </div>
</div>
{/* Contato */}
<div style={styles.section}>
<h3 style={styles.sectionTitle}>Informações de Contato</h3>
<div style={styles.grid}>
<div style={styles.item}><strong>E-mail:</strong> {paciente.email}</div>
<div style={styles.item}><strong>Celular:</strong> {paciente.celular}</div>
<div style={styles.item}><strong>Telefone 1:</strong> {paciente.telefone1 || "-"}</div>
<div style={styles.item}><strong>Telefone 2:</strong> {paciente.telefone2 || "-"}</div>
</div>
</div> </div>
</div> </div>
{/* ------------------ DADOS PESSOAIS ------------------ */}
<div className="card p-3 shadow-sm">
<h5 className="mb-3">Dados Pessoais</h5>
<hr />
<div className="row">
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome:</label>
<p>{paciente.nome || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome social:</label>
<p>{paciente.nome_social || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Data de nascimento:</label>
<p>{paciente.data_nascimento || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Gênero:</label>
<p>{paciente.sexo || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">CPF:</label>
<p>{paciente.cpf || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">RG:</label>
<p>{paciente.rg || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Outro documento:</label>
<p>{paciente.outros_documentos?.tipo || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Número do documento:</label>
<p>{paciente.outros_documentos?.numero || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Etnia:</label>
<p>{paciente.etnia || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Raça:</label>
<p>{paciente.raca || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Naturalidade:</label>
<p>{paciente.etniaRaca || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Profissão:</label>
<p>{paciente.profissao || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome da Mãe:</label>
<p>{paciente.nome_mae || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Profissão da mãe:</label>
<p>{paciente.profissao_mae || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome do Pai:</label>
<p>{paciente.nome_pai || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Profissão do pai:</label>
<p>{paciente.profissao_pai || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome do responsável:</label>
<p>{paciente.nome_responsavel || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">CPF do responsável:</label>
<p>{paciente.cpf_responsavel || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Estado civil:</label>
<p>{paciente.estado_civil || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome do esposo(a):</label>
<p>{paciente.nome_conjuge || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Identificador de outro sistema:</label>
<p>{paciente.outro_id || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={paciente.rnConvenio} disabled/>
<label className="font-extrabold">RN na Guia do convênio:</label>
</div>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Observações:</label>
<p>{paciente.observacoes || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Anexos do Paciente:</label>
<p>{ "-"}</p>
</div>
</div>
</div>
{/* ------------------ INFORMAÇÕES MÉDICAS ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Informações Médicas</h5>
<hr />
<div className="row">
<div className="col-md-3 mb-3">
<label className="font-extrabold">Tipo Sanguíneo:</label>
<p>{paciente.tipoSanguineo || "-"}</p>
</div>
<div className="col-md-3 mb-3">
<label className="font-extrabold">Peso (kg):</label>
<p>{paciente.peso || "-"}</p>
</div>
<div className="col-md-3 mb-3">
<label className="font-extrabold">Altura (m):</label>
<p>{paciente.altura || "-"}</p>
</div>
<div className="col-md-3 mb-3">
<label className="font-extrabold">IMC (kg/):</label>
<p>{paciente.imc || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Alergias:</label>
<p>{paciente.alergias || "-"}</p>
</div>
</div>
</div>
{/* ------------------ INFORMAÇÕES DE CONVÊNIO ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Informações de Convênio</h5>
<hr />
<div className="row">
<div className="col-md-6 mb-3">
<label className="font-extrabold">Convênio:</label>
<p>{paciente.convenio || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Plano:</label>
<p>{paciente.plano || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold"> de matrícula:</label>
<p>{paciente.numeroMatricula || "-"}</p>
</div>
<div className="col-md-3 mb-3">
<label className="font-extrabold">Validade da Carteira:</label>
<p>{paciente.validadeCarteira || "-"}</p>
</div>
<div className="col-md-2 d-flex align-items-end mb-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={paciente.validadeIndeterminada} disabled/>
<label className="font-extrabold">Validade indeterminada:</label>
</div>
</div>
<div className="col-md-2 d-flex align-items-end mb-3">
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={paciente.pacienteVip} disabled/>
<label className="font-extrabold">Paciente VIP: </label>
</div>
</div>
</div>
</div>
{/* ------------------ ENDEREÇO ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Endereço</h5>
<hr />
<div className="row">
<div className="col-md-4 mb-3">
<label className="font-extrabold">CEP:</label>
<p>{paciente.endereco?.cep || "-"}</p>
</div>
<div className="col-md-8 mb-3">
<label className="font-extrabold">Rua:</label>
<p>{paciente.endereco?.logradouro || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Bairro:</label>
<p>{paciente.endereco?.bairro || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Cidade:</label>
<p>{paciente.endereco?.cidade || "-"}</p>
</div>
<div className="col-md-2 mb-3">
<label className="font-extrabold">Estado:</label>
<p>{paciente.endereco?.estado || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Número:</label>
<p>{paciente.endereco?.numero || "-"}</p>
</div>
<div className="col-md-8 mb-3">
<label className="font-extrabold">Complemento:</label>
<p>{paciente.endereco?.complemento || "-"}</p>
</div>
</div>
</div>
{/* ------------------ CONTATO ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Contato</h5>
<hr />
<div className="row">
<div className="col-md-6 mb-3">
<label className="font-extrabold">Email:</label>
<p>{paciente.contato?.email || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Telefone:</label>
<p>{paciente.contato?.celular || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Telefone 2:</label>
<p>{paciente.contato?.telefone2 || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Telefone 3:</label>
<p>{paciente.contato?.telefone3 || "-"}</p>
</div>
</div>
</div>
</>
); );
}; };
const styles = {
container: {
width: "80%",
maxWidth: "800px",
margin: "30px auto",
background: "#fff",
borderRadius: "10px",
padding: "20px",
boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
fontFamily: "Arial, sans-serif",
position: "relative"
},
backBtn: {
background: "#ddd",
border: "none",
borderRadius: "6px",
padding: "8px 14px",
cursor: "pointer",
marginBottom: "15px"
},
header: {
textAlign: "center",
marginBottom: "20px",
position: "relative"
},
avatar: {
width: "80px",
height: "80px",
borderRadius: "50%",
background: "#ddd",
display: "block",
margin: "0 auto"
},
nome: {
margin: "10px 0 5px"
},
vip: {
color: "#f7b500",
fontWeight: "bold",
marginLeft: "5px"
},
cpf: {
margin: "5px 0",
color: "#555"
},
editBtn: {
background: "#0d6efd",
color: "white",
padding: "8px 14px",
border: "none",
borderRadius: "6px",
cursor: "pointer",
position: "absolute",
right: "20px",
top: "20px"
},
section: {
marginBottom: "20px",
border: "1px solid #e0e0e0",
borderRadius: "8px",
padding: "15px"
},
sectionTitle: {
marginTop: 0,
borderBottom: "1px solid #ddd",
paddingBottom: "8px",
fontSize: "18px"
},
grid: {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: "10px 20px",
marginTop: "10px"
},
item: {
fontSize: "14px"
}
};
export default Details; export default Details;

View File

@ -4,7 +4,7 @@ import React, { useState } from 'react';
import DoctorList from '../components/doctors/DoctorList'; import DoctorList from '../components/doctors/DoctorList';
import DoctorForm from '../components/doctors/DoctorForm'; import DoctorForm from '../components/doctors/DoctorForm';
function FormLayout( ) { function DoctorCadastroManager( ) {
// Este estado vai controlar qual "tela" mostrar: 'list' (lista) ou 'form' (formulário) // Este estado vai controlar qual "tela" mostrar: 'list' (lista) ou 'form' (formulário)
const [view, setView] = useState('form'); const [view, setView] = useState('form');
@ -66,4 +66,4 @@ function FormLayout( ) {
); );
} }
export default FormLayout; export default DoctorCadastroManager;

View File

@ -0,0 +1,182 @@
import React, { useEffect, useState } from "react";
import avatarPlaceholder from '../assets/images/avatar_placeholder.png';
const Details = ({ patientID, setCurrentPage }) => {
const [paciente, setPaciente] = useState({});
useEffect(() => {
if (!patientID) return;
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientID}`)
.then(res => res.json())
.then(data => setPaciente(data))
.catch(err => console.error("Erro ao buscar médico:", err));
}, [patientID]);
//if (!paciente) return <p style={{ textAlign: "center" }}>Carregando...</p>;
return (
<>
<div className="card p-3 shadow-sm">
<h3 className="mb-3 text-center">MediConnect</h3>
<hr />
<div className="d-flex justify-content-between align-items-center mb-3">
<button className="btn btn-success me-2" onClick={() => setCurrentPage("table")}>
<i className="bi bi-chevron-left"></i> Voltar
</button>
<div className="d-flex mb-3">
<div className="avatar avatar-xl">
<img src={avatarPlaceholder} alt="" />
</div>
<div className="media-body ms-3 font-extrabold">
<span>{paciente.nome || "Nome Completo"}</span>
<p>{paciente.cpf || "CPF"}</p>
</div>
</div>
<button className="btn btn-light" onClick={() => setCurrentPage("edit-page-doctor")}>
<i className="bi bi-pencil-square"></i> Editar
</button>
</div>
</div>
{/* ------------------ DADOS PESSOAIS ------------------ */}
<div className="card p-3 shadow-sm">
<h5 className="mb-3">Dados Pessoais</h5>
<hr />
<div className="row">
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome:</label>
<p>{paciente.nome || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome social:</label>
<p>{paciente.nomeSocial || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Data de nascimento:</label>
<p>{paciente.dataNascimento || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Gênero:</label>
<p>{paciente.sexo || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">CPF:</label>
<p>{paciente.cpf || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">CRM:</label>
<p>{paciente.rg || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Outro documento:</label>
<p>{paciente.documento || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Número do documento:</label>
<p>{paciente.numeroDocumento || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Etnia e Raça:</label>
<p>{paciente.etniaRaca || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nacionalidade:</label>
<p>{paciente.etniaRaca || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Naturalidade:</label>
<p>{paciente.etniaRaca || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Especialização:</label>
<p>{paciente.profissao || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Estado civil:</label>
<p>{paciente.estadoCivil || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Nome do esposo(a):</label>
<p>{paciente.nomeConjuge || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Identificador de outro sistema:</label>
<p>{paciente.outroId || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Observações:</label>
<p>{paciente.observacoes || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Anexos do Médico:</label>
<p>{paciente.anexos || "-"}</p>
</div>
</div>
</div>
{/* ------------------ ENDEREÇO ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Endereço</h5>
<hr />
<div className="row">
<div className="col-md-4 mb-3">
<label className="font-extrabold">CEP:</label>
<p>{paciente.cep || "-"}</p>
</div>
<div className="col-md-8 mb-3">
<label className="font-extrabold">Rua:</label>
<p>{paciente.rua || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Bairro:</label>
<p>{paciente.bairro || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Cidade:</label>
<p>{paciente.cidade || "-"}</p>
</div>
<div className="col-md-2 mb-3">
<label className="font-extrabold">Estado:</label>
<p>{paciente.estado || "-"}</p>
</div>
<div className="col-md-4 mb-3">
<label className="font-extrabold">Número:</label>
<p>{paciente.numero || "-"}</p>
</div>
<div className="col-md-8 mb-3">
<label className="font-extrabold">Complemento:</label>
<p>{paciente.complemento || "-"}</p>
</div>
</div>
</div>
{/* ------------------ CONTATO ------------------ */}
<div className="card p-3 shadow-sm">
<h5>Contato</h5>
<hr />
<div className="row">
<div className="col-md-6 mb-3">
<label className="font-extrabold">Email:</label>
<p>{paciente.email || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Telefone:</label>
<p>{paciente.telefone1 || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Telefone 2:</label>
<p>{paciente.telefone2 || "-"}</p>
</div>
<div className="col-md-6 mb-3">
<label className="font-extrabold">Celular:</label>
<p>{paciente.telefone3 || "-"}</p>
</div>
</div>
</div>
</>
);
};
export default Details;

View File

@ -1,52 +1,51 @@
// import React from 'react' import React from 'react'
// import DoctorForm from '../components/doctors/DoctorForm' import DoctorForm from '../components/doctors/DoctorForm'
// import {useEffect, useState} from 'react' import {useEffect, useState} from 'react'
// const EditPage = ( {id}) => { const DoctorEditPage = ( {id}) => {
// const [PatientToPUT, setPatientPUT] = useState({}) const [PatientToPUT, setPatientPUT] = useState({})
// var requestOptions = { var requestOptions = {
// method: 'GET', method: 'GET',
// redirect: 'follow' redirect: 'follow'
// }; };
// useEffect(() => { useEffect(() => {
// fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOptions) fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOptions)
// .then(response => response.json()) .then(response => response.json())
// .then(result => result.data) .then(result => result.data)
// .then(data => console.log(data)) .then(data => console.log(data))
// .catch(error => console.log('error', error)); .catch(error => console.log('error', error));
// }, []) }, [])
// const HandlePutPatient = () => { const HandlePutPatient = () => {
// console.log('médico atualizado') console.log('médico atualizado')
// } }
// return ( return (
// <div> <div>
// <button>Voltar para lista sem salvar</button>
// <PatientForm
// onSave={HandlePutPatient}
// onCancel={console.log('Não atualizar')}
// PatientDict={PatientToPUT}
// /> <DoctorForm
onSave={HandlePutPatient}
onCancel={console.log('Não atualizar')}
PatientDict={PatientToPUT}
// </div> />
// )
// }
// export default EditPage </div>
)
}
export default DoctorEditPage

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-paciente"); hoje.getDate() === nascimento.getDate() &&
} hoje.getMonth() === nascimento.getMonth()
);
if (value === "editar") {
setCurrentPage("edit-page-paciente");
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

@ -19,13 +19,32 @@ useEffect(() => {
fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOptions) fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOptions)
.then(response => response.json()) .then(response => response.json())
.then(result => result.data) .then(result => result.data)
.then(data => console.log(data)) .then(data => setPatientPUT(data))
.catch(error => console.log('error', error)); .catch(error => console.log('error', error));
}, []) }, [])
const HandlePutPatient = () => {
console.log('paciente atualizado') const HandlePutPatient = () => {
alert(`Atualizando paciente "${PatientToPUT.nome}" com sucesso`)
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer <token>");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify(PatientToPUT)
console.log(PatientToPUT)
var requestOptions = {
method: 'PUT',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes/", requestOptions)
.then(response => response.text())
.then(result => console.log('ATUALIZADO COM SUCESSO',result))
.catch(error => console.log('error', error));
} }
@ -34,15 +53,14 @@ fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`, requestOpt
<div> <div>
<button>Voltar para lista sem salvar</button>
<PatientForm <PatientForm
onSave={HandlePutPatient} onSave={HandlePutPatient}
onCancel={console.log('Não atualizar')} onCancel={console.log('Não atualizar')}
PatientDict={PatientToPUT}
formData={PatientToPUT}
setFormData={setPatientPUT}
/> />
</div> </div>

View File

@ -1,113 +0,0 @@
import React from "react";
function EmailApp() {
// Lista fake de emails para exibir
const emails = [
{
id: 1,
from: "Open source project public release",
preview: "Hey John, bah kivu decrete epanorthotic unnotched...",
time: "4:14 AM",
starred: true,
avatar: "https://i.pravatar.cc/40?img=1"
},
{
id: 2,
from: "Ecommerce website Paypal integration",
preview: "We will start the new application development soon...",
time: "2:15 AM",
starred: false,
avatar: "https://i.pravatar.cc/40?img=2"
},
{
id: 3,
from: "How To Set Intentions That Energize You",
preview: "I will provide you more details after this Saturday...",
time: "Yesterday",
starred: false,
avatar: "https://i.pravatar.cc/40?img=3"
}
];
return (
<>
<div className="page-heading">
<h3>Email Application</h3>
<p className="text-muted">An application for user to check email inbox</p>
</div>
<div className="page-content">
<div className="row">
{/* Sidebar */}
<div className="col-3">
<button className="btn btn-primary w-100 mb-3">Compose</button>
<ul className="list-group">
<li className="list-group-item">📥 Inbox</li>
<li className="list-group-item">📤 Sent</li>
<li className="list-group-item">📝 Draft</li>
<li className="list-group-item"> Starred</li>
<li className="list-group-item">🚫 Spam</li>
<li className="list-group-item">🗑 Trash</li>
</ul>
<h6 className="mt-4">Labels</h6>
<ul className="list-group">
<li className="list-group-item">🏷 Product</li>
<li className="list-group-item">💼 Work</li>
<li className="list-group-item">📌 Misc</li>
<li className="list-group-item">👨👩👧 Family</li>
<li className="list-group-item">🎨 Design</li>
</ul>
</div>
{/* Emails */}
<div className="col-9">
<div className="d-flex justify-content-between mb-2">
<div>
<button className="btn btn-light me-2">🗑</button>
<button className="btn btn-light me-2"></button>
<button className="btn btn-light me-2">📌</button>
<button className="btn btn-light me-2">🏷</button>
</div>
<input
type="text"
className="form-control w-50"
placeholder="Search email.."
/>
</div>
<div className="list-group">
{emails.map((mail) => (
<div
key={mail.id}
className="list-group-item d-flex align-items-center justify-content-between"
>
<div className="d-flex align-items-center">
<input type="checkbox" className="me-2" />
<img
src={mail.avatar}
alt="avatar"
className="rounded-circle me-2"
width={40}
height={40}
/>
<div>
<strong>{mail.from}</strong>
<p className="mb-0 text-muted small">{mail.preview}</p>
</div>
</div>
<div>
{mail.starred ? "⭐" : "☆"} <br />
<small className="text-muted">{mail.time}</small>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</>
);
}
export default EmailApp;

View File

@ -1,82 +0,0 @@
import React, { useState } from "react";
function GalleryApp() {
const [photos, setPhotos] = useState([
{ id: 1, url: "https://picsum.photos/400/300?random=7", title: "Paisagem 1" },
/*{url da fotos },*/
/*{url da fotos },*/
/*{url da fotos },*/
/*{url da fotos },*/
/*{url da fotos },*/
]);
const [selectedPhoto, setSelectedPhoto] = useState(null);
return (
<div className="page-content">
<div className="page-heading">
<h3>Galeria de Fotos</h3>
</div>
{/* Grade de fotos */}
<div className="row">
{photos.map((photo) => (
<div key={photo.id} className="col-12 col-md-4 mb-4">
<div className="card shadow-sm" onClick={() => setSelectedPhoto(photo)}>
<img
src={photo.url}
alt={photo.title}
className="card-img-top"
style={{ borderRadius: "10px", cursor: "pointer" }}
/>
<div className="card-body text-center">
<h6>{photo.title}</h6>
</div>
</div>
</div>
))}
</div>
{/* Modal de visualização */}
{selectedPhoto && (
<div
className="modal d-block"
style={{
background: "rgba(0,0,0,0.7)",
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
onClick={() => setSelectedPhoto(null)}
>
<div
className="modal-content p-3 bg-white"
style={{ maxWidth: "800px", borderRadius: "12px" }}
onClick={(e) => e.stopPropagation()}
>
<img
src={selectedPhoto.url}
alt={selectedPhoto.title}
className="img-fluid"
style={{ borderRadius: "12px" }}
/>
<h5 className="mt-2">{selectedPhoto.title}</h5>
<button
className="btn btn-danger mt-2"
onClick={() => setSelectedPhoto(null)}
>
Fechar
</button>
</div>
</div>
)}
</div>
);
}
export default GalleryApp;

137
src/pages/Inicio.jsx Normal file
View File

@ -0,0 +1,137 @@
import React, { useState, useEffect } from 'react';
import { FaUser, FaUserPlus, FaCalendarAlt, FaCalendarCheck } from 'react-icons/fa';
import './style/Inicio.css';
function Inicio({ setCurrentPage }) {
const [pacientes, setPacientes] = useState([]);
const [agendamentos, setAgendamentos] = useState([]);
useEffect(() => {
const fetchPacientes = async () => {
try {
const res = await fetch("https://mock.apidog.com/m1/1053378-0-default/pacientes");
const data = await res.json();
setPacientes(data.data);
} catch (error) {
console.error("Erro ao buscar pacientes:", error);
}
};
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);
}
};
fetchPacientes();
fetchAgendamentos();
}, []);
const totalPacientes = pacientes.length;
const novosEsseMes = pacientes.filter(p => p.createdAt && new Date(p.createdAt).getMonth() === new Date().getMonth()).length;
const hoje = new Date();
const agendamentosDoDia = agendamentos.filter(
a => a.data && new Date(a.data).getDate() === hoje.getDate()
);
const agendamentosHoje = agendamentosDoDia.length;
return (
<div className="dashboard-container">
<div className="dashboard-header">
<h1>Bem-vindo ao MediConnect</h1><br></br>
</div>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-info">
<span className="stat-label">TOTAL DE PACIENTES</span>
<span className="stat-value">{totalPacientes}</span>
</div>
<div className="stat-icon-wrapper blue"><FaUser className="stat-icon" /></div>
</div>
<div className="stat-card">
<div className="stat-info">
<span className="stat-label">NOVOS ESTE MÊS</span>
<span className="stat-value">{novosEsseMes}</span>
</div>
<div className="stat-icon-wrapper green"><FaUserPlus className="stat-icon" /></div>
</div>
<div className="stat-card">
<div className="stat-info">
<span className="stat-label">AGENDAMENTOS HOJE</span>
<span className="stat-value">{agendamentosHoje}</span>
</div>
<div className="stat-icon-wrapper purple"><FaCalendarCheck className="stat-icon" /></div>
</div>
<div className="stat-card">
<div className="stat-info">
<span className="stat-label">PENDÊNCIAS</span>
<span className="stat-value">0</span>
</div>
<div className="stat-icon-wrapper orange"><FaCalendarAlt className="stat-icon" /></div>
</div>
</div>
<div className="quick-actions">
<h2>Ações Rápidas</h2>
<div className="actions-grid">
<div className="action-button" onClick={() => setCurrentPage('form-layout')}>
<FaUserPlus className="action-icon" />
<div className="action-info">
<span className="action-title">Novo Paciente</span>
<span className="action-desc">Cadastrar um novo paciente</span>
</div>
</div>
<div className="action-button" onClick={() => setCurrentPage('table')}>
<FaUser className="action-icon" />
<div className="action-info">
<span className="action-title">Lista de Pacientes</span>
<span className="action-desc">Ver todos os pacientes</span>
</div>
</div>
<div className="action-button" onClick={() => setCurrentPage('agendamento')}>
<FaCalendarCheck className="action-icon" />
<div className="action-info">
<span className="action-title">Agendamentos</span>
<span className="action-desc">Gerenciar consultas</span>
</div>
</div>
</div>
</div>
<div className="appointments-section">
<h2>Próximos Agendamentos</h2>
{agendamentosHoje > 0 ? (
<div>
{agendamentosDoDia.map(agendamento => (
<div key={agendamento.id} className="agendamento-item">
<p>{agendamento.nomePaciente}</p>
<p>{new Date(agendamento.data).toLocaleTimeString()}</p>
</div>
))}
</div>
) : (
<div className="no-appointments-content">
<FaCalendarCheck className="no-appointments-icon" />
<p>Nenhum agendamento para hoje</p>
<button className="manage-button" onClick={() => setCurrentPage('agendamento')}>
Gerenciar Agendamentos
</button>
</div>
)}
</div>
</div>
);
}
export default Inicio;

374
src/pages/LaudoManager.jsx Normal file
View File

@ -0,0 +1,374 @@
// src/pages/LaudoManager.jsx
import React, { useState, useEffect } from "react";
/* ===== Estilos embutidos ===== */
/* Eu coloquei os estilos aqui para simplificar a edição. */
const styles = `
.laudo-wrap { display:flex; gap:24px; padding:18px; font-family: Inter, Roboto, Arial, sans-serif; }
.left-col { width: 100%; max-width: 1160px; background:#ffffff; border-radius:8px; padding:18px; box-shadow: 0 1px 0 rgba(0,0,0,0.03); } /* <-- fundo branco */
.title-row { display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; }
.page-title { font-size:20px; color:#2b4a78; font-weight:700; }
.laudo-row { display:flex; padding:14px 12px; align-items:center; border-bottom:1px solid #eef3f8; position:relative; overflow:visible; background: transparent; }
.col { flex:1; padding:0 8px; font-size:14px; color:#2e3a4b; }
.col.small { flex:0 0 90px; text-align:right; }
.row-actions { position:relative; flex: 0 0 88px; display:flex; justify-content:flex-end; }
.action-btn { background:transparent; border:1px solid #d7e6fb; border-radius:8px; height:40px; width:40px; display:flex; align-items:center; justify-content:center; cursor:pointer; }
.dropdown { position:absolute; right:0; top:48px; background:white; border-radius:8px; box-shadow: 0 10px 30px rgba(20,30,50,0.12); min-width:220px; padding:8px 0; z-index:9999; }
.dropdown .item { padding:12px 18px; cursor:pointer; font-size:15px; color:#244056; }
.dropdown .item:hover { background:#f6fbff; }
.viewer-modal, .preview-modal, .confirm-modal { position:fixed; inset:0; display:flex; align-items:center; justify-content:center; z-index:12000; pointer-events:none; } /* deixar pointer-events none para que não bloqueie por padrão */
.modal-backdrop { position:absolute; inset:0; background: rgba(9,20,40,0.45); pointer-events:auto; } /* usado apenas quando necessário */
.modal-card { position:relative; width:92%; max-width:1100px; background:white; border-radius:10px; padding:18px; box-shadow: 0 10px 60px rgba(10,20,40,0.25); max-height:88vh; overflow:auto; pointer-events:auto; }
.viewer-header { display:flex; justify-content:space-between; align-items:flex-start; gap:10px; margin-bottom:12px; }
.patient-info { font-size:13px; color:#3a556b; }
.toolbar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:12px; }
.tool-btn { padding:8px 10px; border-radius:6px; border:1px solid #e6eef8; cursor:pointer; background:#fff; font-size:13px; }
.editor-area { border:1px solid #e6eef8; border-radius:8px; padding:14px; min-height:360px; background: #fff; color:#1f2d3d; font-size:15px; line-height:1.5; }
.footer-controls { display:flex; justify-content:space-between; align-items:center; margin-top:12px; }
.toggle { display:flex; align-items:center; gap:8px; }
.btn { padding:8px 12px; border-radius:8px; border:none; cursor:pointer; font-weight:600; }
.btn.secondary { background:#eef6ff; color:#2f63a6; border:1px solid #d6e9ff; }
.btn.primary { background:#2f63a6; color:white; }
.small-muted { color:#7f95a8; font-size:13px; }
.empty { padding:40px; text-align:center; color:#7d97b4; }
/* notificação simples (centralizada) */
.notice-card { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:#fff; border-radius:8px; padding:14px 18px; box-shadow:0 8px 30px rgba(10,20,40,0.12); z-index:13000; pointer-events:auto; max-width:720px; }
`;
/* ===== Mock data (simula APIDOG) ===== */
function mockFetchLaudos() {
return [
{
id: "LAU-300551296",
pedido: 300551296,
data: "29/07/2025",
paciente: { nome: "Sarah Mariana Oliveira", cpf: "616.869.070-**", nascimento: "1990-03-25", convenio: "Unimed" },
solicitante: "Sandro Rangel Santos",
exame: "US - Abdome Total",
conteudo: "RELATÓRIO MÉDICO\n\nAchados: Imagens compatíveis com ...\nConclusão: Órgãos sem alterações significativas.",
status: "rascunho"
},
{
id: "LAU-300659170",
pedido: 300659170,
data: "29/07/2025",
paciente: { nome: "Laissa Helena Marquetti", cpf: "950.684.57-**", nascimento: "1986-09-12", convenio: "Bradesco" },
solicitante: "Sandro Rangel Santos",
exame: "US - Mamária Bilateral",
conteudo: "RELATÓRIO MÉDICO\n\nAchados: text...",
status: "liberado"
},
{
id: "LAU-300658301",
pedido: 300658301,
data: "28/07/2025",
paciente: { nome: "Vera Lúcia Oliveira Santos", cpf: "928.005.**", nascimento: "1979-02-02", convenio: "Particular" },
solicitante: "Dr. Fulano",
exame: "US - Transvaginal",
conteudo: "RELATÓRIO MÉDICO\n\nAchados: ...",
status: "entregue"
}
];
}
function mockDeleteLaudo(id) {
return new Promise((res) => setTimeout(() => res({ ok: true }), 500));
}
/* ===== Componente ===== */
export default function LaudoManager() {
const [laudos, setLaudos] = useState([]);
const [openDropdownId, setOpenDropdownId] = useState(null);
/* viewerLaudo é usado para mostrar o editor/leitura;
previewLaudo é usado para a pré-visualização (sem bloquear) */
const [viewerLaudo, setViewerLaudo] = useState(null);
const [previewLaudo, setPreviewLaudo] = useState(null);
const [showPreview, setShowPreview] = useState(false);
const [showConfirmDelete, setShowConfirmDelete] = useState(false);
const [toDelete, setToDelete] = useState(null);
const [loadingDelete, setLoadingDelete] = useState(false);
/* notificação simples (sem backdrop) para 'sem permissão' */
const [showNoPermission, setShowNoPermission] = useState(false);
/* pesquisa */
const [query, setQuery] = useState("");
/* Para simplificar: eu assumo aqui que estamos na visão da secretaria */
const isSecretary = true; // permanece true (somente leitura)
useEffect(() => {
const el = document.createElement("style");
el.innerHTML = styles;
document.head.appendChild(el);
const data = mockFetchLaudos();
setLaudos(data);
return () => document.head.removeChild(el);
}, []);
// Fecha dropdown ao clicar fora
useEffect(() => {
function onDocClick(e) {
if (e.target.closest && e.target.closest('.action-btn')) return;
if (e.target.closest && e.target.closest('.dropdown')) return;
setOpenDropdownId(null);
}
document.addEventListener('click', onDocClick);
return () => document.removeEventListener('click', onDocClick);
}, []);
function toggleDropdown(id, e) {
e.stopPropagation();
setOpenDropdownId(prev => (prev === id ? null : id));
}
/* (botao editar) */
function handleOpenViewer(laudo) {
setOpenDropdownId(null);
if (isSecretary) {
// (notificação sem bloquear)
setShowNoPermission(true);
return;
}
setViewerLaudo(laudo);
}
/* (botao imprimir) */
function handlePrint(laudo) {
// evitar bug: fechar viewer antes de abrir preview
setViewerLaudo(null);
setPreviewLaudo(laudo);
setShowPreview(true);
setOpenDropdownId(null);
}
/* (botao excluir) */
function handleRequestDelete(laudo) {
setToDelete(laudo);
setOpenDropdownId(null);
setShowConfirmDelete(true);
}
/* (funcionalidade do botao de excluir) */
async function doConfirmDelete(confirm) {
if (!toDelete) return;
if (!confirm) {
setShowConfirmDelete(false);
setToDelete(null);
return;
}
setLoadingDelete(true);
try {
const resp = await mockDeleteLaudo(toDelete.id);
if (resp.ok || resp === true) {
// removo o laudo da lista local
setLaudos(curr => curr.filter(l => l.id !== toDelete.id));
setShowConfirmDelete(false);
setToDelete(null);
alert("Laudo excluído com sucesso.");
} else {
alert("Erro ao excluir. Tente novamente.");
}
} catch (err) {
alert("Erro de rede ao excluir.");
} finally {
setLoadingDelete(false);
}
}
/* filtro de pesquisa (por pedido ou nome do paciente) */
const normalized = (s = "") => String(s).toLowerCase();
const filteredLaudos = laudos.filter(l => {
const q = normalized(query).trim();
if (!q) return true;
if (normalized(l.pedido).includes(q)) return true;
if (normalized(l.paciente?.nome).includes(q)) return true;
return false;
});
return (
<div className="laudo-wrap">
<div className="left-col">
<div className="title-row">
<div>
<div className="page-title">Gerenciamento de Laudo</div>
{/* removi a linha "Visualização: Secretaria" conforme pedido */}
</div>
</div>
<div style={{ marginBottom:12 }}>
<input
placeholder="Pesquisar paciente ou pedido..."
value={query}
onChange={e => setQuery(e.target.value)}
style={{ width:"100%", padding:12, borderRadius:8, border:"1px solid #e6eef8" }}
/>
</div>
{filteredLaudos.length === 0 ? (
<div className="empty">Nenhum laudo encontrado.</div>
) : (
<div style={{ borderRadius:8, overflow:"visible", boxShadow:"0 0 0 1px #eef6ff" }}>
{filteredLaudos.map((l) => (
<div className="laudo-row" key={l.id}>
<div className="col" style={{ flex: "0 0 160px" }}>
<div style={{ fontWeight:700 }}>{l.pedido}</div>
<div className="small-muted">{l.data}</div>
</div>
<div className="col" style={{ flex:2 }}>
<div style={{ fontWeight:600 }}>{l.paciente.nome}</div>
<div className="small-muted">{l.paciente.cpf} {l.paciente.convenio}</div>
</div>
<div className="col" style={{ flex:1 }}>{l.exame}</div>
<div className="col small">{l.solicitante}</div>
<div className="col small" style={{ flex: "0 0 80px", textAlign:"left" }}>{l.status}</div>
<div className="row-actions">
<div className="action-btn" onClick={(e)=> toggleDropdown(l.id, e)} title="Ações">
<svg width="16" height="16" viewBox="0 0 24 24"><circle cx="12" cy="5" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="12" cy="19" r="1.6"/></svg>
</div>
{openDropdownId === l.id && (
<div className="dropdown" data-laudo-dropdown={l.id}>
<div className="item" onClick={() => handleOpenViewer(l)}>Editar</div>
<div className="item" onClick={() => handlePrint(l)}>Imprimir</div>
<div className="item" onClick={() => { alert("Protocolo de entrega: formulário (não implementado)."); setOpenDropdownId(null); }}>Protocolo de entrega</div>
<div className="item" onClick={() => { alert("Liberar laudo: requer permissão de médico. (não implementado)"); setOpenDropdownId(null); }}>Liberar laudo</div>
<div className="item" onClick={() => handleRequestDelete(l)} style={{ color:"#c23b3b" }}>Excluir laudo</div>
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* Viewer modal (modo leitura) — só abre para quem tem permissão */}
{viewerLaudo && !showPreview && !isSecretary && (
<div className="viewer-modal" style={{ pointerEvents:"auto" }}>
<div className="modal-backdrop" onClick={() => setViewerLaudo(null)} />
<div className="modal-card" role="dialog" aria-modal="true">
<div className="viewer-header">
<div>
<div style={{ fontSize:18, fontWeight:700 }}>{viewerLaudo.paciente.nome}</div>
<div className="patient-info">
Nasc.: {viewerLaudo.paciente.nascimento} {computeAge(viewerLaudo.paciente.nascimento)} anos {viewerLaudo.paciente.cpf} {viewerLaudo.paciente.convenio}
</div>
</div>
<div style={{ display:"flex", gap:8 }}>
<button className="tool-btn" onClick={() => { setPreviewLaudo(viewerLaudo); setShowPreview(true); setViewerLaudo(null); }}>Pré-visualizar / Imprimir</button>
<button className="tool-btn" onClick={() => setViewerLaudo(null)}>Fechar</button>
</div>
</div>
<div className="toolbar">
<div className="tool-btn">B</div>
<div className="tool-btn"><i>I</i></div>
<div className="tool-btn"><u>U</u></div>
<div className="tool-btn">Fonte</div>
<div className="tool-btn">Tamanho</div>
<div className="tool-btn">Lista</div>
<div className="tool-btn">Campos</div>
<div className="tool-btn">Modelos</div>
<div className="tool-btn">Imagens</div>
</div>
<div className="editor-area" aria-readonly>
{viewerLaudo.conteudo.split("\n").map((line, i) => (
<p key={i} style={{ margin: line.trim()==="" ? "8px 0" : "6px 0" }}>{line}</p>
))}
</div>
<div className="footer-controls">
<div className="toggle small-muted">
<label><input type="checkbox" disabled /> Pré-visualização</label>
<label style={{ marginLeft:12 }}><input type="checkbox" disabled /> Ocultar data</label>
<label style={{ marginLeft:12 }}><input type="checkbox" disabled /> Ocultar assinatura</label>
</div>
<div style={{ display:"flex", gap:8 }}>
<button className="btn secondary" onClick={() => { if(window.confirm("Cancelar e voltar à lista? Todas alterações não salvas serão perdidas.")) setViewerLaudo(null); }}>Cancelar</button>
<button className="btn primary" onClick={() => alert("Salvar (não implementado para secretaria).")}>Salvar laudo</button>
</div>
</div>
</div>
</div>
)}
{/* Preview modal — agora não bloqueia a tela (sem backdrop escuro), botão imprimir é interativo */}
{showPreview && previewLaudo && (
<div className="preview-modal" style={{ pointerEvents:"none" /* container não bloqueia */ }}>
<div /* sem backdrop, assim não deixa a tela escura/blocked */ />
<div className="modal-card" style={{ maxWidth:900, pointerEvents:"auto" }}>
<div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12 }}>
<div style={{ fontWeight:700 }}>Pré-visualização - {previewLaudo.paciente.nome}</div>
<div style={{ display:"flex", gap:8 }}>
<button className="tool-btn" onClick={() => alert("Imprimir (simulado).")}>Imprimir / Download</button>
<button className="tool-btn" onClick={() => { setShowPreview(false); setPreviewLaudo(null); }}>Fechar</button>
</div>
</div>
<div style={{ border: "1px solid #e6eef8", borderRadius:6, padding:18, background:"#fff" }}>
<div style={{ marginBottom:8, fontSize:14, color:"#33475b" }}>
<strong>RELATÓRIO MÉDICO</strong>
</div>
<div style={{ marginBottom:14, fontSize:13, color:"#546b7f" }}>
{previewLaudo.paciente.nome} Nasc.: {previewLaudo.paciente.nascimento} CPF: {previewLaudo.paciente.cpf}
</div>
<div style={{ whiteSpace:"pre-wrap", fontSize:15, color:"#1f2d3d", lineHeight:1.5 }}>
{previewLaudo.conteudo}
</div>
</div>
</div>
</div>
)}
{/* Notificação simples: Sem permissão (exibe sem backdrop escuro) - centralizada */}
{showNoPermission && (
<div className="notice-card" role="alert" aria-live="polite">
<div style={{ fontWeight:700, marginBottom:6 }}>Sem permissão para editar</div>
<div style={{ marginBottom:10, color:"#5a6f80" }}>Você está na visualização da secretaria. Edição disponível somente para médicos autorizados.</div>
<div style={{ textAlign:"right" }}>
<button className="tool-btn" onClick={() => setShowNoPermission(false)}>Fechar</button>
</div>
</div>
)}
{/* Confirm delete modal (simples: Sim / Não) */}
{showConfirmDelete && toDelete && (
<div className="confirm-modal" style={{ pointerEvents:"auto" }}>
<div className="modal-card" style={{ maxWidth:480 }}>
<div style={{ fontWeight:700, marginBottom:8 }}>Confirmar exclusão</div>
<div style={{ marginBottom:12 }}>Você tem certeza que quer excluir o laudo <strong>{toDelete.pedido} - {toDelete.paciente.nome}</strong> ? Esta ação é irreversível.</div>
<div style={{ display:"flex", justifyContent:"flex-end", gap:8 }}>
<button className="tool-btn" onClick={() => doConfirmDelete(false)} disabled={loadingDelete}>Não</button>
<button className="tool-btn" onClick={() => doConfirmDelete(true)} disabled={loadingDelete} style={{ background: loadingDelete ? "#d7e8ff" : "#ffecec", border: "1px solid #ffd7d7" }}>
{loadingDelete ? "Excluindo..." : "Sim, excluir"}
</button>
</div>
</div>
</div>
)}
</div>
);
}
/* ===== Helpers ===== */
function computeAge(birth) {
if (!birth) return "-";
const [y,m,d] = birth.split("-").map(x => parseInt(x,10));
if (!y) return "-";
const today = new Date();
let age = today.getFullYear() - y;
const mm = today.getMonth() + 1;
const dd = today.getDate();
if (mm < m || (mm === m && dd < d)) age--;
return age;
}

View File

@ -1,14 +1,14 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
// Importamos os dois novos componentes que criamos // Importamos os dois novos componentes que criamos
import PatientList from '../components/patients/PatientList';
import PatientForm from '../components/patients/PatientForm'; import PatientForm from '../components/patients/PatientForm';
function FormLayout( ) { function PatientCadastroManager( {setCurrentPage} ) {
// Este estado vai controlar qual "tela" mostrar: 'list' (lista) ou 'form' (formulário) // Este estado vai controlar qual "tela" mostrar: 'list' (lista) ou 'form' (formulário)
const [view, setView] = useState('form');
const [formData, setFormData] = useState({})
var myHeaders = new Headers(); var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json"); myHeaders.append("Content-Type", "application/json");
@ -23,7 +23,6 @@ function FormLayout( ) {
header: myHeaders, header: myHeaders,
body:raw, body:raw,
redirect:'follow' redirect:'follow'
} }
@ -34,7 +33,7 @@ function FormLayout( ) {
alert(`Paciente "${patientData.nome}" salvo com sucesso!`); //altere isso para integração com backend alert(`Paciente "${patientData.nome}" salvo com sucesso!`); //altere isso para integração com backend
// Após salvar, voltamos para a tela de lista // Após salvar, voltamos para a tela de lista
setView('list');
}; };
return ( return (
@ -49,16 +48,13 @@ function FormLayout( ) {
{/* Se a view for 'list', mostramos a lista com o botão. */} {/* Se a view for 'list', mostramos a lista com o botão. */}
{/* Se for 'form', mostramos o formulário de cadastro. */} {/* Se for 'form', mostramos o formulário de cadastro. */}
{view === 'list' ? (
<PatientList onAddPatient={() => setView('form')} />
) : (
<PatientForm <PatientForm
onSave={handleSavePatient} onSave={handleSavePatient}
onCancel={() => setView('list')} onCancel={() => {setCurrentPage('table')}}
PatientDict={{}} formData={formData}
setFormData={setFormData}
/> />
)}
</div> </div>
</section> </section>
</div> </div>
@ -66,4 +62,4 @@ function FormLayout( ) {
); );
} }
export default FormLayout; export default PatientCadastroManager;

View File

@ -1,13 +1,77 @@
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) => {
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',
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));
}
}
// Função para excluir paciente // Função para excluir paciente
const deletePatient = async (id) => { const deletePatient = async (id) => {
DeleteAnexo(id)
const requestOptionsDelete = { method: "DELETE", redirect: "follow" }; const requestOptionsDelete = { method: "DELETE", redirect: "follow" };
if (!window.confirm("Tem certeza que deseja excluir este paciente?")) return; if (!window.confirm("Tem certeza que deseja excluir este paciente?")) return;
@ -21,39 +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'){
setCurrentPage('details-page-paciente')
setPatientID(id);
}
if(value === 'editar'){
setCurrentPage('edit-page-paciente')
setPatientID(id);
await fetch(
`https://mock.apidog.com/m1/1053378-0-default/pacientes/${id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vip: novoStatus }),
} }
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) =>
@ -61,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 (
<> <>
@ -77,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>
@ -108,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>
@ -121,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>
@ -159,4 +338,5 @@ function TablePaciente({ setCurrentPage, setPatientID }) {
</> </>
); );
} }
export default TablePaciente; export default TablePaciente;

View File

@ -1,84 +0,0 @@
import React, { useState } from "react";
function Files() {
// 1. Criamos um estado para armazenar o arquivo selecionado pelo usuário
const [selectedFile, setSelectedFile] = useState(null);
// 2. Esta função é chamada quando o usuário seleciona um arquivo no input
const handleFileChange = (event) => {
// Pegamos o primeiro arquivo da lista (mesmo que só possa selecionar um)
const file = event.target.files[0];
if (file) {
setSelectedFile(file);
}
};
// 3. Esta função é chamada quando o botão "Enviar Arquivo" é clicado
const handleUpload = () => {
if (!selectedFile) {
alert("Por favor, selecione um arquivo antes de enviar.");
return;
}
// --- Lógica de Envio (Simulação) ---
// Em um projeto real, aqui você enviaria o 'selectedFile' para um servidor.
// Por enquanto, vamos apenas mostrar um alerta de sucesso.
console.log("Enviando o arquivo:", selectedFile.name);
alert(`Arquivo "${selectedFile.name}" enviado com sucesso! (Isso é uma simulação)`);
// Limpa o estado após o envio
setSelectedFile(null);
};
return (
<>
<div className="page-heading">
<h3>Files</h3>
</div>
<div className="page-content">
<section className="row">
<div className="col-12">
<div className="card">
<div className="card-header">
<h4 className="card-title">Gerenciamento de Arquivos</h4>
</div>
<div className="card-body">
{/* 4. Adicionamos o input de arquivo e o botão */}
<div className="mb-3">
<label htmlFor="formFile" className="form-label">
Selecione um arquivo para enviar
</label>
{/* O 'key' ajuda o React a resetar o input quando o arquivo é "enviado" */}
<input
className="form-control"
type="file"
id="formFile"
onChange={handleFileChange}
key={selectedFile ? selectedFile.name : 'no-file'}
/>
</div>
{/* Mostra o nome do arquivo selecionado */}
{selectedFile && (
<p className="text-muted">
Arquivo selecionado: <strong>{selectedFile.name}</strong>
</p>
)}
<button
className="btn btn-primary"
onClick={handleUpload}
disabled={!selectedFile} // O botão fica desabilitado se nenhum arquivo for selecionado
>
<i className="bi bi-upload"></i> Enviar Arquivo
</button>
</div>
</div>
</div>
</section>
</div>
</>
);
}
export default Files;

View File

@ -0,0 +1,144 @@
.filtros-container select,
.filtros-container input {
padding: 0.5rem;
border-radius: 5px;
border: 1px solid #ccc;
}
.btn-buscar {
padding: 0.5rem 1rem;
margin-right: 0.5rem;
border: none;
border-radius: 5px;
background-color: #f0f0f0;
cursor: pointer;
}
.unidade-selecionarprofissional{
background-color: #fdfdfdde;
padding: 20px 10px;
display: flex;
border-radius:10px ;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.unidade-selecionarprofissional input, .unidade-selecionarprofissional select {
margin-left: 8px;
border-radius: 8px;
padding: 5px;
width: 20%;
}
.unidade-selecionarprofissional select{
width: 7%;
}
.busca-atendimento{
display: flex;
flex-direction: row;
margin:10px;
justify-content: flex-end;
}
.busca-atendimento select{
padding:5px;
border-radius:8px ;
margin-left: 15px;
background-color: #0078d7;
color: white;
font-weight: bold;
}
.busca-atendimento input{
margin-left: 8px;
border-radius: 8px;
padding: 5px;
width: 100%;
}
.btn-selecionar-tabeladia, .btn-selecionar-tabelasemana, .btn-selecionar-tabelames {
background-color: rgba(231, 231, 231, 0.808);
padding:8px 10px;
font-size: larger;
font-weight: bold;
border-style: hidden;
}
.btn-selecionar-tabeladia{
border-radius: 10px 0px 0px 10px;
}
.btn-selecionar-tabelames{
border-radius: 0px 10px 10px 0px;
}
.btn-selecionar-tabeladia.ativo, .btn-selecionar-tabelasemana.ativo, .btn-selecionar-tabelames.ativo{
background-color: lightcyan;
border-color: darkcyan;
font-weight: bolder;
}
.legenda-tabela{
display: flex;
justify-content: flex-end;
margin-top: 10px;
margin-bottom: 10px;
gap: 15px;
}
.legenda-item-realizado{
background-color: #2c5e37;
}
.legenda-item-confirmado{
background-color: #1e90ff;
}
.legenda-item-cancelado{
background-color: #d9534f;
}
.legenda-item-agendado{
background-color: #f0ad4e;
}
#status-card-consulta-realizado, .legenda-item-realizado {
background-color: #b7ffbd;
border:3px solid #91d392;
padding: 5px;
font-weight: bold;
border-radius: 10px;
}
#status-card-consulta-cancelado, .legenda-item-cancelado {
background-color: #ffb7cc;
border:3px solid #ff6c84;
padding: 5px;
font-weight: bold;
border-radius: 10px;
}
#status-card-consulta-confirmado, .legenda-item-confirmado {
background-color: #eef8fb;
border:3px solid #d8dfe7;
padding: 5px;
font-weight: bold;
border-radius: 10px;
}
#status-card-consulta-agendado, .legenda-item-agendado {
background-color: #f7f7c4;
border:3px solid #f3ce67;
padding: 5px;
font-weight: bold;
border-radius: 10px;
}

182
src/pages/style/Inicio.css Normal file
View File

@ -0,0 +1,182 @@
/* Container Principal */
.dashboard-container {
padding: 2rem;
background-color: #f7f9fc;
flex-grow: 1; /* Permite que o container ocupe o espaço restante */
}
/* Header */
.dashboard-header h1 {
font-size: 1.8rem;
font-weight: 600;
color: #333;
margin-bottom: 0.25rem;
}
.dashboard-header p {
font-size: 0.9rem;
color: #666;
margin-bottom: 2rem;
}
/* Seção de Estatísticas (Cartões) */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
.stat-card {
background-color: #fff;
border-radius: 12px;
padding: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}
.stat-info {
display: flex;
flex-direction: column;
}
.stat-label {
font-size: 0.8rem;
font-weight: 500;
color: #888;
margin-bottom: 0.25rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #444;
}
.stat-icon-wrapper {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.stat-icon {
font-size: 1.25rem;
color: #fff;
}
/* Cores dos ícones */
.stat-icon-wrapper.blue { background-color: #5d5dff; }
.stat-icon-wrapper.green { background-color: #30d158; }
.stat-icon-wrapper.purple { background-color: #a272ff; }
.stat-icon-wrapper.orange { background-color: #f1952e; }
/* Seção de Ações Rápidas */
.quick-actions h2 {
font-size: 1.2rem;
font-weight: 600;
color: #333;
margin-bottom: 1.5rem;
}
.actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
.action-button {
background-color: #fff;
border-radius: 12px;
padding: 1.5rem;
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.2s ease-in-out;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}
.action-button:hover {
transform: translateY(-5px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.action-icon {
font-size: 2.25rem;
margin-right: 1rem;
color: #a272ff; /* Cor padrão para os ícones das ações */
}
.action-info {
display: flex;
flex-direction: column;
}
.action-title {
font-size: 1rem;
font-weight: 600;
color: #444;
}
.action-desc {
font-size: 0.8rem;
color: #888;
}
/* Seção de Próximos Agendamentos */
.appointments-section {
background-color: #fff;
border-radius: 12px;
padding: 2rem;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}
.appointments-section h2 {
font-size: 1.2rem;
font-weight: 600;
color: #333;
margin-bottom: 1.5rem;
}
.no-appointments-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
height: 100%;
padding: 2rem 0;
}
.no-appointments-icon {
font-size: 3rem;
color: #bbb;
margin-bottom: 1rem;
}
.no-appointments-content p {
font-size: 1rem;
color: #666;
margin-bottom: 1.5rem;
}
.manage-button {
background-color: #5d5dff;
color: #fff;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease;
}
.manage-button:hover {
background-color: #4444ff;
}