feat: Criação da página início e melhoria na navegação

This commit is contained in:
pedrofedericoo 2025-09-10 14:26:48 -03:00
parent 709cd4e13d
commit 7f07950ada
9 changed files with 463 additions and 135 deletions

144
package-lock.json generated
View File

@ -19,6 +19,7 @@
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1",
"flatpickr": "^4.6.13",
"lucide-react": "^0.543.0",
"perfect-scrollbar": "^1.5.6",
"quill": "^2.0.3",
"rater-js": "^1.0.1",
@ -27,6 +28,7 @@
"react-bootstrap": "^2.10.10",
"react-dom": "^18.2.0",
"react-flatpickr": "^4.0.11",
"react-icons": "^5.5.0",
"react-input-mask": "^2.0.4",
"react-quill": "^2.0.0",
"react-scripts": "5.0.1",
@ -36,8 +38,11 @@
"web-vitals": "^2.1.4"
},
"devDependencies": {
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"sass": "^1.91.0",
"sass-loader": "^16.0.5"
"sass-loader": "^16.0.5",
"tailwindcss": "^4.1.13"
}
},
"node_modules/@adobe/css-tools": {
@ -16228,9 +16233,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"license": "MIT",
"engines": {
"node": ">=12"
@ -16240,9 +16245,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"license": "MIT",
"engines": {
"node": ">=12"
@ -16269,9 +16274,9 @@
}
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@ -26833,6 +26838,15 @@
"yallist": "^3.0.2"
}
},
"node_modules/lucide-react": {
"version": "0.543.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.543.0.tgz",
"integrity": "sha512-fpVfuOQO0V3HBaOA1stIiP/A2fPCXHIleRZL16Mx3HmjTYwNSbimhnFBygs2CAfU1geexMX5ItUcWBGUaqw5CA==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
@ -30537,6 +30551,15 @@
"react": ">= 16 <= 19"
}
},
"node_modules/react-icons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-input-mask": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-input-mask/-/react-input-mask-2.0.4.tgz",
@ -30730,6 +30753,18 @@
}
}
},
"node_modules/react-scripts/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/react-scripts/node_modules/sass-loader": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
@ -30768,6 +30803,43 @@
}
}
},
"node_modules/react-scripts/node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -32944,53 +33016,11 @@
"license": "MIT"
},
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tailwindcss/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.2.3",

View File

@ -14,6 +14,7 @@
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1",
"flatpickr": "^4.6.13",
"lucide-react": "^0.543.0",
"perfect-scrollbar": "^1.5.6",
"quill": "^2.0.3",
"rater-js": "^1.0.1",
@ -22,6 +23,7 @@
"react-bootstrap": "^2.10.10",
"react-dom": "^18.2.0",
"react-flatpickr": "^4.0.11",
"react-icons": "^5.5.0",
"react-input-mask": "^2.0.4",
"react-quill": "^2.0.0",
"react-scripts": "5.0.1",
@ -55,7 +57,10 @@
]
},
"devDependencies": {
"autoprefixer": "^10.4.21",
"postcss": "^8.5.6",
"sass": "^1.91.0",
"sass-loader": "^16.0.5"
"sass-loader": "^16.0.5",
"tailwindcss": "^4.1.13"
}
}

View File

@ -1,94 +1,50 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import Sidebar from './components/Sidebar';
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 EmailApp from "./pages/EmailApp";
//import ChatApp from "./pages/ChatApp";
import GalleryApp from "./pages/GalleryApp";
import FormLayout from './pages/FormLayout';
import EditPage from './pages/EditPage';
import PatientForm from "./components/patients/PatientForm";
import Details from './pages/Details';
//import DoctorEditPage from './components/doctors/DoctorEditPage';
import DoctorTable from './pages/DoctorTable';
import DoctorFormLayout from './pages/DoctorFormLayout';
import Inicio from './pages/Inicio';
import Agendamentos from './pages/Agendamentos';
function App() {
const [isSidebarActive, setIsSidebarActive] = useState(true);
const [currentPage, setCurrentPage] = useState('table ');
const [patientID, setPatientID] = useState(null)
const [currentPage, setCurrentPage] = useState('Inicio');
const [patientID, setPatientID] = useState(null);
const toggleSidebar = () => {
setIsSidebarActive(!isSidebarActive);
};
const renderPageContent = () => {
if (currentPage === 'form-layout') {
return <FormLayout/>;
}
else if(currentPage === 'doctor-form-layout'){
return <DoctorFormLayout/>
}
else if (currentPage === 'table') {
return <Table setCurrentPage={setCurrentPage} setPatientID={setPatientID}/>;
}
else if(currentPage === 'doctor-table'){
return <DoctorTable setCurrentPage={setCurrentPage} setPatientID={setPatientID}/>
}
else if (currentPage === 'data-table') {
return <DataTable />;
}
else if (currentPage === 'files') {
return <Files />;
}
else if (currentPage === 'email-app') {
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>
</>
);
const renderPageContent = () => {
switch (currentPage) {
case 'Inicio':
return <Inicio setCurrentPage={setCurrentPage} />;
case 'Agendamentos':
return <Agendamentos/>;
case 'form-layout':
return <FormLayout />;
case 'doctor-form-layout':
return <DoctorFormLayout />;
case 'table':
return <Table setCurrentPage={setCurrentPage} setPatientID={setPatientID} />;
case 'doctor-table':
return <DoctorTable setCurrentPage={setCurrentPage} setPatientID={setPatientID} />;
case 'details-page-paciente':
return <Details patientID={patientID} setCurrentPage={setCurrentPage} />;
case 'edit-page-paciente':
return <EditPage id={patientID} />;
default:
return <Inicio setCurrentPage={setCurrentPage} />;
}
};
return (

View File

@ -48,12 +48,12 @@ function Sidebar(props) {
<div className="sidebar-header">
<div className="d-flex justify-content-between">
<div className="logo">
{/* Logo volta pro Dashboard */}
{/* Logo volta pro Início */}
<a
href="#"
onClick={(e) => {
e.preventDefault();
props.setCurrentPage('dashboard');
props.setCurrentPage('Inicio');
}}
>
<hi>MediConnect</hi>

View File

@ -4,6 +4,12 @@
"isTitle": true
},
{
"name":"Início",
"url": "Inicio",
"icon": "house"
},
{
"name": "Cadastro de Pacientes",
"url": "form-layout",

View File

@ -1,7 +1,7 @@
[
{
"name": "Dashboard",
"url": "dashboard",
"name": "Início",
"url": "Inicio",
"icon": "grid-fill"
},
{

View File

@ -0,0 +1,12 @@
import React from 'react';
function Agendamentos() {
return (
<div style={{ padding: '2rem', textAlign: 'center' }}>
<h1>Página de Agendamentos</h1>
</div>
);
}
export default Agendamentos;

182
src/pages/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;
}

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 './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("SUA_URL_DA_API_DE_AGENDAMENTOS");
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('Agendamentos')}>
<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('Agendamentos')}>
Gerenciar Agendamentos
</button>
</div>
)}
</div>
</div>
);
}
export default Inicio;