diff --git a/src/App.js b/src/App.js
index 98be1df..c74aabd 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,26 +1,34 @@
// src/App.js
//import PerfilSecretaria from "./perfis/perfil_secretaria/PerfilSecretaria";
-import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
+import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import { useState } from "react";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Forgot from "./pages/ForgotPassword";
import PerfilSecretaria from "./perfis/perfil_secretaria/PerfilSecretaria";
+import PerfilFinanceiro from "./perfis/perfil_financeiro/PerfilFinanceiro";
import LandingPage from './pages/LandingPage';
// Mantenha todas as importações de CSS globais aqui se houver!
function App() {
- // O estado controla qual view mostrar: false = Landing Page, true = Dashboard
- const [isInternalView, setIsInternalView] = useState(false);
- // const [isSecretaria, setIsSecretaria] = useState(false);
+ const [userProfile, setUserProfile] = useState(
+ localStorage.getItem('userProfile') || null
+ );
- const handleEnterSystem = () => {
- setIsInternalView(true);
+ const handleEnterSystem = (profile, keepConnected) => {
+ setUserProfile(profile);
+
+ if (keepConnected) {
+ localStorage.setItem('userProfile', profile);
+ } else {
+
+ localStorage.removeItem('userProfile');
+ }
};
-
const handleExitSystem = () => {
- setIsInternalView(false);
+ setUserProfile(null);
+ localStorage.removeItem('userProfile');
};
// if (isSecretaria) {
@@ -28,26 +36,45 @@ function App() {
// }
// Se não estiver na visualização interna, retorna a LandingPage.
- if (!isInternalView) {
- return (
+
+ return (
- } />
- } />
- } />
+ } />
+ } />
+ } />
} />
- } />
- Página não encontrada} />
+
+ ) : (
+
+ )
+ }
+ />
+
+ ) : (
+
+ )
+ }
+ />
+ :
+ // Se não logado, volta para a Landing Page
+ }
+ />
- )
+ );
}
-
- // Se estiver na visualização interna, retorna o PerfilSecretaria
- return (
- // Passamos a função de saída (logout)
-
- );
-}
export default App;
\ No newline at end of file
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx
index 0820c1c..43b5c5b 100644
--- a/src/components/Sidebar.jsx
+++ b/src/components/Sidebar.jsx
@@ -1,123 +1,133 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
-import menuItems from "../data/sidebar-items.json";
-function Sidebar() {
- const [isActive, setIsActive] = useState(true);
- const [openSubmenu, setOpenSubmenu] = useState(null);
+// 1. Recebe 'menuItems' e 'onLogout' como props
+function Sidebar({ menuItems, onLogout }) {
+ const [isActive, setIsActive] = useState(true);
+ const [openSubmenu, setOpenSubmenu] = useState(null);
- const toggleSidebar = () => {
- setIsActive(!isActive);
- };
+ const toggleSidebar = () => {
+ setIsActive(!isActive);
+ };
- const handleSubmenuClick = (submenuName) => {
- setOpenSubmenu(openSubmenu === submenuName ? null : submenuName);
- };
+ const handleSubmenuClick = (submenuName) => {
+ setOpenSubmenu(openSubmenu === submenuName ? null : submenuName);
+ };
- const renderLink = (item) => {
- // Links internos (rotas do React Router)
- if (item.url && item.url.startsWith("/")) {
- return (
-
- {item.icon && }
- {item.name}
-
- );
- }
-
- // Links externos
- return (
-
- {item.icon && }
- {item.name}
-
- );
- };
+ const renderLink = (item) => {
+ // Links internos (rotas do React Router)
+ if (item.url && item.url.startsWith("/")) {
+ return (
+
+ {item.icon && }
+ {item.name}
+
+ );
+ }
+ // Links externos
return (
-
+
+ {item.icon && }
+ {item.name}
+
);
+ };
+
+ return (
+
+ );
}
-export default Sidebar;
+export default Sidebar;
\ No newline at end of file
diff --git a/src/data/sidebar-items-financeiro.json b/src/data/sidebar-items-financeiro.json
new file mode 100644
index 0000000..8183087
--- /dev/null
+++ b/src/data/sidebar-items-financeiro.json
@@ -0,0 +1,31 @@
+[
+ {
+ "name": "Menu-Financeiro",
+ "isTitle": true
+ },
+
+ {
+ "name":"Início",
+ "url": "/financeiro/inicio",
+ "icon": "house"
+ },
+
+ {
+ "name": "Lista de Pacientes",
+ "icon": "clipboard-heart-fill",
+ "url": "/financeiro/pacientes"
+ },
+
+ {
+ "name": "Lista de Médico",
+ "icon": "hospital-fill",
+ "url": "/financeiro/medicos"
+ },
+
+ {
+ "name": "Controle Financeiro",
+ "icon": "cash-coin",
+ "url": "/financeiro/controlefinanceiro"
+ }
+
+]
\ No newline at end of file
diff --git a/src/data/sidebar-items.json b/src/data/sidebar-items.json
index f6e1519..8c87d01 100644
--- a/src/data/sidebar-items.json
+++ b/src/data/sidebar-items.json
@@ -43,6 +43,12 @@
"name": "Laudo do Paciente",
"icon": "table",
"url": "/secretaria/laudo"
+ },
+
+ {
+ "name": "Controle Financeiro",
+ "icon": "cash-coin",
+ "url": "/secretaria/controlefinanceiro"
}
]
\ No newline at end of file
diff --git a/src/pages/FinanceiroDashboard.jsx b/src/pages/FinanceiroDashboard.jsx
new file mode 100644
index 0000000..b3768ce
--- /dev/null
+++ b/src/pages/FinanceiroDashboard.jsx
@@ -0,0 +1,435 @@
+import React, { useState, useEffect, useMemo, useCallback } from "react";
+import './style/FinanceiroDashboard.css';
+
+const CONVENIOS_LIST = [
+ "Particular",
+ "Amil",
+ "Bradesco Saúde",
+ "SulAmérica",
+ "Unimed",
+ "Cassio",
+ "Outro"
+];
+
+function CurrencyInput({ value, onChange, label, id }) {
+ const formattedValue = useMemo(() => {
+ let numericValue = Number(value) || 0;
+
+ let stringValue = String(numericValue);
+ while (stringValue.length < 3) {
+ stringValue = '0' + stringValue;
+ }
+
+ const integerPart = stringValue.slice(0, -2);
+ const decimalPart = stringValue.slice(-2);
+
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+
+ return `R$ ${formattedInteger},${decimalPart}`;
+ }, [value]);
+
+ const handleKeyDown = useCallback((e) => {
+ const key = e.key;
+
+ if (['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key)) {
+ if (key === 'Backspace' || key === 'Delete') {
+ e.preventDefault();
+ const numericValue = value || 0;
+ let newValueString = String(numericValue);
+ if (newValueString.length <= 1) {
+ onChange(0);
+ } else {
+ const newNumericValue = parseInt(newValueString.slice(0, -1)) || 0;
+ onChange(newNumericValue);
+ }
+ }
+ return;
+ }
+
+ if (!/^\d$/.test(key)) {
+ e.preventDefault();
+ return;
+ }
+
+ e.preventDefault();
+
+ const digit = key;
+ const numericValue = value || 0;
+
+ let newValueString = String(numericValue) + digit;
+
+ if (newValueString.length > 10) return;
+
+ const newNumericValue = parseInt(newValueString);
+
+ onChange(newNumericValue);
+ }, [value, onChange]);
+
+ return (
+
+
+ {}}
+ onKeyDown={handleKeyDown}
+ placeholder="R$ 0,00"
+ />
+
+ );
+}
+
+function mockFetchPagamentos() {
+ return [
+ {
+ id: "PAY-001",
+ paciente: { nome: "Sarah Oliveira", convenio: "Unimed" },
+ valor: 20000,
+ forma_pagamento: "Cartão",
+ data_vencimento: "2025-09-30",
+ status: "pendente",
+ desconto: 0,
+ observacoes: "Pagamento parcelado em 2x"
+ },
+ {
+ id: "PAY-002",
+ paciente: { nome: "Laissa Marquetti", convenio: "Bradesco Saúde" },
+ valor: 15000,
+ forma_pagamento: "Dinheiro",
+ data_vencimento: "2025-09-15",
+ status: "pago",
+ desconto: 1000,
+ observacoes: ""
+ },
+ {
+ id: "PAY-003",
+ paciente: { nome: "Vera Santos", convenio: "Particular" },
+ valor: 30000,
+ forma_pagamento: "Pix",
+ data_vencimento: "2025-09-20",
+ status: "vencido",
+ desconto: 0,
+ observacoes: "Não respondeu ao contato"
+ },
+ {
+ id: "PAY-004",
+ paciente: { nome: "Carlos Almeida", convenio: "Particular" },
+ valor: 10000,
+ forma_pagamento: "Transferência",
+ data_vencimento: "2025-09-29",
+ status: "pago",
+ desconto: 500,
+ observacoes: "Desconto por pagamento adiantado"
+ }
+ ];
+}
+
+export default function FinanceiroDashboard() {
+ const [pagamentos, setPagamentos] = useState([]);
+ const [modalPagamento, setModalPagamento] = useState(null);
+ const [query, setQuery] = useState("");
+ const [filtroStatus, setFiltroStatus] = useState("Todos");
+ const [novoPagamento, setNovoPagamento] = useState(false);
+ const [summary, setSummary] = useState({ totalRecebido: 0, totalAReceber: 0, totalDescontos: 0 });
+
+ useEffect(() => {
+ const data = mockFetchPagamentos();
+ setPagamentos(data);
+ }, []);
+
+ function formatCurrency(centavos) {
+ const valorEmReais = centavos / 100;
+ return "R$ " + valorEmReais.toFixed(2).replace(".", ",").replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+ }
+
+ function getValorLiquido(valor, desconto) {
+ return valor - desconto;
+ }
+
+ const filteredPagamentos = useMemo(() => {
+ return pagamentos.filter(p => {
+ const q = query.toLowerCase();
+ const statusOk = filtroStatus === "Todos" || p.status === filtroStatus;
+ const buscaOk = p.paciente.nome.toLowerCase().includes(q) ||
+ p.id.toLowerCase().includes(q);
+ return statusOk && buscaOk;
+ });
+ }, [pagamentos, query, filtroStatus]);
+
+ useEffect(() => {
+ let recebido = 0;
+ let aReceber = 0;
+ let descontos = 0;
+
+ filteredPagamentos.forEach(p => {
+ const valorLiquido = getValorLiquido(p.valor, p.desconto);
+ if (p.status === 'pago') {
+ recebido += valorLiquido;
+ descontos += p.desconto;
+ } else {
+ aReceber += p.valor;
+ }
+ });
+
+ setSummary({
+ totalRecebido: recebido,
+ totalAReceber: aReceber,
+ totalDescontos: descontos
+ });
+ }, [filteredPagamentos]);
+
+ function handleDelete(id) {
+ if (window.confirm("Tem certeza que deseja excluir este pagamento?")) {
+ setPagamentos(prev => prev.filter(p => p.id !== id));
+ setModalPagamento(null);
+ }
+ }
+
+ function handleSave(pagamento) {
+ if (!pagamento.paciente.nome || !pagamento.valor || !pagamento.data_vencimento || !pagamento.paciente.convenio) {
+ alert("Preencha Paciente, Convênio, Valor e Data de Vencimento.");
+ return;
+ }
+
+ if (novoPagamento) {
+ const newId = "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0");
+ pagamento.id = newId;
+ setPagamentos(prev => [...prev, pagamento]);
+ } else {
+ setPagamentos(prev => prev.map(p => p.id === pagamento.id ? pagamento : p));
+ }
+ setModalPagamento(null);
+ setNovoPagamento(false);
+ }
+
+ const closeModal = () => {
+ setModalPagamento(null);
+ setNovoPagamento(false);
+ };
+
+ return (
+
+
Controle Financeiro
+
+
+
+
Total Recebido (Filtrado)
+
{formatCurrency(summary.totalRecebido)}
+
+
+
Total a Receber (Filtrado)
+
{formatCurrency(summary.totalAReceber)}
+
+
+
Descontos Aplicados
+
{formatCurrency(summary.totalDescontos)}
+
+
+
+
+
+ setQuery(e.target.value)}
+ style={{ flexGrow: 1 }}
+ />
+
+
+
+
+ {filteredPagamentos.length === 0 ? (
+
Nenhum pagamento encontrado.
+ ) : (
+
+
+
+
+ | Paciente |
+ Convênio |
+ Valor Total (R$) |
+ Desconto (R$) |
+ Valor Líquido (R$) |
+ Forma |
+ Vencimento |
+ Status |
+ Ações |
+
+
+
+ {filteredPagamentos.map(p => (
+
+ | {p.paciente.nome} |
+ {p.paciente.convenio} |
+ {formatCurrency(p.valor)} |
+ {formatCurrency(p.desconto)} |
+ {formatCurrency(getValorLiquido(p.valor, p.desconto))} |
+ {p.forma_pagamento} |
+ {p.data_vencimento.split('-').reverse().join('/')} |
+ {p.status.toUpperCase()} |
+
+
+
+
+
+ |
+
+ ))}
+
+
+
+ )}
+
+
+ {modalPagamento && (
+
e.target.classList.contains('modal') && closeModal()}>
+
+
+
{novoPagamento ? "Adicionar Pagamento" : `Editar Pagamento - ${modalPagamento.paciente.nome}`}
+
+
+
+
+
+
+ setModalPagamento({...modalPagamento, paciente:{...modalPagamento.paciente, nome:e.target.value}})}
+ />
+
+
+
+
+
+
+
setModalPagamento({...modalPagamento, valor: newValue})}
+ />
+
+ setModalPagamento({...modalPagamento, desconto: newValue})}
+ />
+
+
+
+
+
+
+
+
+ setModalPagamento({...modalPagamento, data_vencimento:e.target.value})}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/pages/Inicio.jsx b/src/pages/Inicio.jsx
index dc47fae..fadc0ac 100644
--- a/src/pages/Inicio.jsx
+++ b/src/pages/Inicio.jsx
@@ -110,28 +110,6 @@ function Inicio({ setCurrentPage }) {
-
-
-
Próximos Agendamentos
- {agendamentosHoje > 0 ? (
-
- {agendamentosDoDia.map(agendamento => (
-
-
{agendamento.nomePaciente}
-
{new Date(agendamento.data).toLocaleTimeString()}
-
- ))}
-
- ) : (
-
-
-
Nenhum agendamento para hoje
-
-
- )}
-
);
}
diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx
index 9d31b70..39fc044 100644
--- a/src/pages/Login.jsx
+++ b/src/pages/Login.jsx
@@ -1,12 +1,26 @@
-import React from 'react';
import { Link, useNavigate } from "react-router-dom";
+import React, { useState } from 'react';
-function Login() {
+function Login({ onEnterSystem }) {
const navigate = useNavigate();
+ const [username, setUsername] = useState('');
+ const [password, setPassword] = useState('');
+ const [keepConnected, setKeepConnected] = useState(false);
const handleLogin = (e) => {
e.preventDefault();
- // ...login logic...
- navigate('/secretaria/inicio');
+
+ let profile = null;
+ if (username.toLowerCase() === 'secretaria') {
+ profile = 'secretaria';
+ } else if (username.toLowerCase() === 'financeiro') {
+ profile = 'financeiro';
+ }
+ if (!profile) {
+ alert("Usuário não reconhecido. Use 'secretaria' ou 'financeiro' para testar.");
+ return;
+ }
+ onEnterSystem(profile, keepConnected);
+ navigate(`/${profile}/`);
};
return (
<>
@@ -24,12 +38,15 @@ function Login() {
Entre com os dados que você inseriu durante o registro.
-