Inicio Crud usuario

This commit is contained in:
RafaelMTA13 2025-10-22 23:15:18 -03:00
parent ba98884667
commit 6f5031e24b
8 changed files with 879 additions and 334 deletions

510
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,7 @@ import LandingPage from './pages/LandingPage';
import PerfilFinanceiro from "./perfis/perfil_financeiro/PerfilFinanceiro"; import PerfilFinanceiro from "./perfis/perfil_financeiro/PerfilFinanceiro";
import Perfiladm from "./perfis/Perfil_adm/Perfiladm"; import Perfiladm from "./perfis/Perfil_adm/Perfiladm";
import PerfilMedico from "./perfis/Perfil_medico/PerfilMedico"; import PerfilMedico from "./perfis/Perfil_medico/PerfilMedico";
import PerfilUsuario from "./perfis/perfil_usuario/PerfilUsuario";
// COMBINADO: Importações de ambas as versões
import PerfilPaciente from "./perfis/Perfil_paciente/Perfilpaciente" import PerfilPaciente from "./perfis/Perfil_paciente/Perfilpaciente"
import ProfilePage from "./pages/ProfilePage"; import ProfilePage from "./pages/ProfilePage";
import Header from "./components/Header/Header"; import Header from "./components/Header/Header";
@ -36,6 +35,7 @@ function App() {
<Route path="/financeiro/*" element={<PerfilFinanceiro />} /> <Route path="/financeiro/*" element={<PerfilFinanceiro />} />
<Route path="/medico/*" element={<PerfilMedico />} /> <Route path="/medico/*" element={<PerfilMedico />} />
<Route path="/admin/*" element={<Perfiladm />} /> <Route path="/admin/*" element={<Perfiladm />} />
<Route path="/usuario/*" element={<PerfilUsuario />} />
{/* COMBINADO: Rotas de ambas as versões */} {/* COMBINADO: Rotas de ambas as versões */}
<Route path="/paciente/*" element={<PerfilPaciente />} /> <Route path="/paciente/*" element={<PerfilPaciente />} />

View File

@ -34,9 +34,17 @@ const TrocardePerfis = () => {
{ key: "financeiro", label: "Financeiro", route: "/financeiro" }, { key: "financeiro", label: "Financeiro", route: "/financeiro" },
{ key: "admin", label: "Administração", route: "/admin" }, { key: "admin", label: "Administração", route: "/admin" },
{ key: "paciente", label: "Paciente", route: "/paciente" }, { key: "paciente", label: "Paciente", route: "/paciente" },
].filter( { key: "usuario", label: "Usuário", route: "/usuario" },
(opt) => ];
showProfiles?.includes(opt.key) || showProfiles?.includes("admin")
// Normalize roles to lowercase so we can accept variations like 'user' or 'usuario'
const rolesLower = (showProfiles || []).map((r) => String(r).toLowerCase());
const filteredOptions = options.filter((opt) =>
rolesLower.includes(opt.key) ||
rolesLower.includes("admin") ||
// accept backend returning 'user' for the usuario profile
(opt.key === "usuario" && rolesLower.includes("user"))
); );
return ( return (
@ -48,7 +56,7 @@ const TrocardePerfis = () => {
onChange={handleSelectChange} onChange={handleSelectChange}
> >
<option value="">Selecionar perfil</option> <option value="">Selecionar perfil</option>
{options.map((opt) => ( {filteredOptions.map((opt) => (
<option key={opt.key} value={opt.route}> <option key={opt.key} value={opt.route}>
{opt.label} {opt.label}
</option> </option>

View File

@ -1,272 +1,203 @@
/* --- ESTILO PARA ESCONDER O BOTÃO ORIGINAL DO VLIBRAS --- */ @import url('https://fonts.cdnfonts.com/css/open-dyslexic');
[vw-access-button] { [vw-access-button] {
display: none !important; display: none !important;
} }
/* --- ESTILOS GERAIS DO COMPONENTE --- */
.container-acessibilidade { .container-acessibilidade {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
right: 20px; right: 20px;
z-index: 99998; z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
pointer-events: none; /* Impede cliques no contêiner */
} }
.botao-flutuante-acessibilidade { .botao-flutuante-acessibilidade {
position: relative;
z-index: 2; /* Acima do menu */
background: linear-gradient(45deg, #007bff, #0056b3);
color: white;
border: none;
border-radius: 50%;
width: 60px; width: 60px;
height: 60px; height: 60px;
border-radius: 50%;
background-color: #007bff;
color: white;
border: none;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
cursor: pointer; cursor: pointer;
box-shadow: 0 5px 15px rgba(0, 91, 179, 0.4);
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease; justify-content: center;
margin-top: 15px; /* Distância do menu */
pointer-events: auto; /* Permite que o botão seja clicável */
} }
.botao-flutuante-acessibilidade:hover {
transform: scale(1.1);
box-shadow: 0 8px 20px rgba(0, 91, 179, 0.5);
}
/* --- ESTILOS DO MENU "BALÃO" --- */
.menu-opcoes { .menu-opcoes {
background-color: #ffffff; position: absolute;
border-radius: 12px; bottom: 70px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); right: 0;
padding: 8px;
width: 280px; width: 280px;
z-index: 1; /* Abaixo do botão principal */ max-height: calc(100vh - 100px);
border: 1px solid #e9ecef; overflow-y: auto;
background-color: white;
/* Animação */ border-radius: 8px;
transform-origin: bottom center; box-shadow: 0 6px 12px rgba(0,0,0,0.3);
transform: translateY(10px) scale(0.95); display: flex;
flex-direction: column;
gap: 8px;
padding: 15px;
transform: scale(0.95);
transform-origin: bottom right;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: all 0.2s ease; transition: transform 0.2s ease, opacity 0.2s ease, visibility 0.2s;
pointer-events: auto; /* Permite que o menu seja clicável */
} }
.menu-opcoes.aberto { .menu-opcoes.aberto {
transform: translateY(0) scale(1); transform: scale(1);
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} }
.menu-titulo { .menu-titulo {
font-size: 14px; font-size: 18px;
font-weight: 600; font-weight: bold;
color: #6c757d; margin-bottom: 10px;
padding: 8px 12px; text-align: center;
border-bottom: 1px solid #f1f3f5; color: #333;
margin-bottom: 5px;
transition: color 0.2s ease, border-bottom-color 0.2s ease;
} }
/* --- ESTILOS DOS BOTÕES E DA CHECKBOX NO MENU --- */
.menu-opcoes button, .menu-opcoes button,
.checkbox-label-button { .checkbox-label-button {
width: 100%;
padding: 12px;
border-radius: 6px;
border: 1px solid #ddd;
background-color: #f9f9f9;
cursor: pointer;
text-align: left;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 10px;
background-color: transparent; font-size: 15px;
border: none; color: #333;
padding: 12px; transition: background-color 0.2s;
text-align: left;
cursor: pointer;
font-size: 16px;
color: #212529;
width: 100%;
border-radius: 8px;
transition: background-color 0.2s ease, color 0.2s ease;
} }
.menu-opcoes button:hover, .menu-opcoes button:hover,
.checkbox-label-button:hover { .checkbox-label-button:hover {
background-color: #f8f9fa; background-color: #f0f0f0;
} }
/* --- ESTILO DO INTERRUPTOR (CHECKBOX) --- */
.checkbox-label-button {
justify-content: space-between;
}
.checkbox-label-button input[type="checkbox"] { .checkbox-label-button input[type="checkbox"] {
appearance: none; margin-left: auto;
-webkit-appearance: none;
position: relative;
width: 44px;
height: 24px;
background-color: #ced4da;
border-radius: 12px;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
}
.checkbox-label-button input[type="checkbox"]::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px; width: 20px;
height: 20px; height: 20px;
background-color: white; cursor: pointer;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
transition: transform 0.3s ease-in-out;
} }
.checkbox-label-button input[type="checkbox"]:checked { /* Tamanho da Fonte */
background-color: #0d6efd;
}
.checkbox-label-button input[type="checkbox"]:checked::before {
transform: translateX(20px);
}
/* --- ✨ NOVOS ESTILOS PARA O CONTROLE DE FONTE ✨ --- */
.font-size-control { .font-size-control {
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: space-between; padding: 10px;
padding: 8px 12px; background-color: rgba(0, 0, 0, 0.05);
color: #212529; border-radius: 8px;
transition: color 0.2s ease; margin-bottom: 5px;
border-top: 1px solid #f1f3f5; gap: 10px;
margin-top: 5px;
} }
.font-size-label { .font-size-label {
font-size: 16px;
display: flex;
align-items: center;
gap: 12px;
}
.font-size-buttons {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
}
.font-size-buttons button {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-weight: bold;
font-size: 16px;
background-color: #e9ecef;
color: #495057;
border: 1px solid #dee2e6;
border-radius: 6px;
width: 36px;
height: 32px;
padding: 0;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.2s ease, color 0.2s ease;
}
.font-size-buttons button:hover {
background-color: #dee2e6;
}
.font-size-buttons button:disabled {
background-color: #f8f9fa;
color: #adb5bd;
cursor: not-allowed;
border-color: #f1f3f5;
}
.font-size-display {
font-size: 14px;
font-weight: 600; font-weight: 600;
color: #495057; font-size: 15px;
min-width: 45px;
text-align: center;
transition: color 0.2s ease;
} }
.font-size-buttons {
/* Dark mode styles */ display: flex;
html[data-bs-theme="dark"] { justify-content: space-between;
align-items: center;
/* Floating button */
.botao-flutuante-acessibilidade {
background: linear-gradient(45deg, #212529, #343a40);
color: #f8f9fa;
box-shadow: 0 5px 15px rgba(33, 37, 41, 0.4);
}
.botao-flutuante-acessibilidade:hover {
box-shadow: 0 8px 20px rgba(33, 37, 41, 0.5);
}
/* Menu balloon */
.menu-opcoes {
background-color: #23272b;
border: 1px solid #343a40;
box-shadow: 0 8px 25px rgba(0,0,0,0.4);
}
.menu-titulo {
color: #adb5bd;
border-bottom: 1px solid #343a40;
}
/* Menu buttons and checkbox */
.menu-opcoes button,
.checkbox-label-button {
color: #f8f9fa;
}
.menu-opcoes button:hover,
.checkbox-label-button:hover {
background-color: #343a40;
}
/* Checkbox switch */
.checkbox-label-button input[type="checkbox"] {
background-color: #495057;
}
.checkbox-label-button input[type="checkbox"]:checked {
background-color: #0d6efd;
}
.checkbox-label-button input[type="checkbox"]::before {
background-color: #f8f9fa;
}
/* Font size control */
.font-size-control {
color: #f8f9fa;
border-top: 1px solid #343a40;
}
.font-size-label {
color: #f8f9fa;
} }
.font-size-buttons button { .font-size-buttons button {
background-color: #343a40; border-radius: 50%;
color: #f8f9fa; width: 38px;
border: 1px solid #495057; height: 38px;
border: 1px solid #ddd;
background-color: #fff;
font-size: 16px;
font-weight: bold;
cursor: pointer;
color: #333;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
} }
.font-size-buttons button:hover { .font-size-buttons span {
background-color: #495057; font-size: 16px;
font-weight: 600;
min-width: 50px;
text-align: center;
} }
.font-size-buttons button:disabled {
background-color: #23272b; /* Fonte para Dislexia */
color: #6c757d; .dyslexia-font-active * {
border-color: #343a40; font-family: 'Open-Dyslexic', sans-serif !important;
} }
.font-size-display {
color: #f8f9fa; /* Espaçamento e Altura */
.letter-spacing-active p, .letter-spacing-active li, .letter-spacing-active span, .letter-spacing-active a, .letter-spacing-active div, .letter-spacing-active td, .letter-spacing-active h1, .letter-spacing-active h2, .letter-spacing-active h3, .letter-spacing-active h4, .letter-spacing-active h5, .letter-spacing-active h6 {
letter-spacing: 1.5px !important;
} }
.line-height-active p, .line-height-active li, .line-height-active span, .line-height-active a, .line-height-active div, .line-height-active td, .line-height-active h1, .line-height-active h2, .line-height-active h3, .line-height-active h4, .line-height-active h5, .line-height-active h6 {
line-height: 3 !important;
} }
/* Guia de Leitura */
.reading-guide-mask {
position: fixed;
left: 0;
width: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 99999;
pointer-events: none;
transition: all 0.05s ease-out;
}
.reading-guide-top {
top: 0;
height: 0;
}
.reading-guide-bottom {
bottom: 0;
top: 100vh;
}
/* Cursor Grande */
.big-cursor-active, .big-cursor-active * {
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="black" stroke="white" stroke-width="2" d="M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V4A1,1 0 0,1 7,3C7.24,3 7.45,3.08 7.62,3.22L16.39,10.22C16.78,10.5 16.88,11.05 16.6,11.45L13.64,21.97Z" /></svg>') 1 1, auto !important;
}
/* Filtros de Daltonismo */
.colorblind-protanopia { filter: url(#protanopia); }
.colorblind-deuteranopia { filter: url(#deuteranopia); }
.colorblind-tritanopia { filter: url(#tritanopia); }
.colorblind-achromatopsia { filter: url(#achromatopsia); }
.acessibilidade-select-control {
display: flex;
flex-direction: column;
gap: 8px;
background-color: rgba(0, 0, 0, 0.05);
padding: 10px;
border-radius: 8px;
margin-bottom: 5px;
}
.acessibilidade-select-control label {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 15px;
}
.acessibilidade-select-control select {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
font-size: 14px;
}
/* Dark Mode */
.dark-mode .menu-opcoes { background-color: #2d2d2d; color: #f1f1f1; }
.dark-mode .menu-titulo { color: #f1f1f1; }
.dark-mode .menu-opcoes button, .dark-mode .checkbox-label-button { background-color: #424242; border-color: #555; color: #f1f1f1; }
.dark-mode .menu-opcoes button:hover, .dark-mode .checkbox-label-button:hover { background-color: #535353; }
.dark-mode .font-size-control, .dark-mode .acessibilidade-select-control { background-color: rgba(255, 255, 255, 0.1); }
.dark-mode .font-size-buttons button { background-color: #535353; color: #f1f1f1; border-color: #666; }
.dark-mode .acessibilidade-select-control select { background-color: #535353; color: #f1f1f1; border-color: #666; }

View File

@ -2,34 +2,121 @@ import React, { useState, useEffect, useRef } from 'react';
import './botaoacessibilidade.css'; // Importando o CSS import './botaoacessibilidade.css'; // Importando o CSS
import { setTheme } from '../assets/static/js/components/dark'; import { setTheme } from '../assets/static/js/components/dark';
// Componente para o Guia de Leitura
function GuiaDeLeitura() {
const topMaskRef = useRef(null);
const bottomMaskRef = useRef(null);
useEffect(() => {
const handleMouseMove = (e) => {
if (topMaskRef.current && bottomMaskRef.current) {
const windowHeight = 40;
const offset = windowHeight / 2;
topMaskRef.current.style.height = `${e.clientY - offset}px`;
bottomMaskRef.current.style.top = `${e.clientY + offset}px`;
}
};
window.addEventListener('mousemove', handleMouseMove);
return () => { window.removeEventListener('mousemove', handleMouseMove); };
}, []);
return (
<>
<div ref={topMaskRef} className="reading-guide-mask reading-guide-top"></div>
<div ref={bottomMaskRef} className="reading-guide-mask reading-guide-bottom"></div>
</>
);
}
// --- COMPONENTE PRINCIPAL ---
function BotaoAcessibilidade() { function BotaoAcessibilidade() {
// Estados de todas as funcionalidades
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isReadOnHoverActive, setIsReadOnHoverActive] = useState(false);
const [isDarkMode, setIsDarkMode] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false);
const [isReadOnHoverActive, setIsReadOnHoverActive] = useState(false);
const [fontSize, setFontSize] = useState(1);
const [isBigCursor, setIsBigCursor] = useState(false);
const [colorblindMode, setColorblindMode] = useState('none');
const [isReadingGuide, setIsReadingGuide] = useState(false);
const [isDyslexiaFont, setIsDyslexiaFont] = useState(false);
const [isLetterSpacing, setIsLetterSpacing] = useState(false);
const [isLineHeight, setIsLineHeight] = useState(false);
const lastSpokenTargetRef = useRef(null); const lastSpokenTargetRef = useRef(null);
// Efeitos para aplicar as funcionalidades na página
useEffect(() => { setTheme(isDarkMode ? "dark" : "light", true); }, [isDarkMode]);
useEffect(() => { useEffect(() => {
setTheme(isDarkMode ? "dark" : "light", true); const originalFontSize = document.documentElement.style.fontSize;
}, [isDarkMode]); document.documentElement.style.fontSize = `${fontSize * 100}%`;
return () => { document.documentElement.style.fontSize = originalFontSize; };
}, [fontSize]);
useEffect(() => {
document.body.classList.toggle('big-cursor-active', isBigCursor);
return () => { document.body.classList.remove('big-cursor-active'); };
}, [isBigCursor]);
useEffect(() => {
document.body.classList.toggle('dyslexia-font-active', isDyslexiaFont);
return () => { document.body.classList.remove('dyslexia-font-active'); };
}, [isDyslexiaFont]);
useEffect(() => {
document.body.classList.toggle('letter-spacing-active', isLetterSpacing);
return () => { document.body.classList.remove('letter-spacing-active'); };
}, [isLetterSpacing]);
useEffect(() => {
document.body.classList.toggle('line-height-active', isLineHeight);
return () => { document.body.classList.remove('line-height-active'); };
}, [isLineHeight]);
useEffect(() => {
const classesToRemove = ['colorblind-protanopia', 'colorblind-deuteranopia', 'colorblind-tritanopia', 'colorblind-achromatopsia'];
document.documentElement.classList.remove(...classesToRemove);
if (colorblindMode !== 'none') {
document.documentElement.classList.add(`colorblind-${colorblindMode}`);
}
}, [colorblindMode]);
// VERSÃO DEFINITIVA - Muito mais abrangente
useEffect(() => { useEffect(() => {
if (!isReadOnHoverActive) { if (!isReadOnHoverActive) {
window.speechSynthesis.cancel(); window.speechSynthesis.cancel();
return; return;
} }
const handleMouseOver = (event) => { const handleMouseOver = (event) => {
const target = event.target; // Seletor muito mais completo, incluindo DIVs, SPANs, células de tabela, imagens e papéis de acessibilidade
if (target && target !== lastSpokenTargetRef.current && target.innerText) { const selector = 'P, H1, H2, H3, H4, H5, H6, BUTTON, A, LI, LABEL, SPAN, TD, TH, DT, DD, FIGCAPTION, [role="button"], [role="link"], [role="menuitem"], IMG';
const text = target.innerText.trim(); const relevantElement = event.target.closest(selector);
if (text.length > 0 && ['P', 'H1', 'H2', 'H3', 'BUTTON', 'A', 'LI', 'LABEL'].includes(target.tagName)) {
lastSpokenTargetRef.current = target; if (relevantElement && relevantElement !== lastSpokenTargetRef.current) {
let textToSpeak = '';
// Lógica especial para IMAGENS: lê o atributo "alt"
if (relevantElement.tagName === 'IMG') {
textToSpeak = relevantElement.getAttribute('alt');
}
// Lógica para outros elementos: prioriza aria-label, depois title, e por último o texto interno
else {
textToSpeak =
relevantElement.getAttribute('aria-label') ||
relevantElement.getAttribute('title') ||
relevantElement.innerText;
}
if (textToSpeak) {
textToSpeak = textToSpeak.trim();
// Evita ler elementos que só têm elementos filhos, mas nenhum texto próprio direto
const hasTextAndNoChildren = textToSpeak.length > 0 && relevantElement.children.length === 0;
const hasTextAndIsBlock = textToSpeak.length > 0 && !['SPAN', 'A', 'STRONG', 'EM'].includes(relevantElement.tagName);
if (hasTextAndNoChildren || hasTextAndIsBlock) {
lastSpokenTargetRef.current = relevantElement;
window.speechSynthesis.cancel(); window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text); const utterance = new SpeechSynthesisUtterance(textToSpeak);
utterance.lang = 'pt-BR'; utterance.lang = 'pt-BR';
window.speechSynthesis.speak(utterance); window.speechSynthesis.speak(utterance);
} }
} }
}
}; };
document.body.addEventListener('mouseover', handleMouseOver); document.body.addEventListener('mouseover', handleMouseOver);
return () => { return () => {
document.body.removeEventListener('mouseover', handleMouseOver); document.body.removeEventListener('mouseover', handleMouseOver);
@ -37,6 +124,9 @@ function BotaoAcessibilidade() {
}; };
}, [isReadOnHoverActive]); }, [isReadOnHoverActive]);
// Funções de controle
const handleIncreaseFontSize = () => setFontSize(prevSize => Math.min(prevSize + 0.1, 1.6));
const handleDecreaseFontSize = () => setFontSize(prevSize => Math.max(prevSize - 0.1, 0.8));
const handleVlibrasClick = () => { const handleVlibrasClick = () => {
const originalVlibrasButton = document.querySelector('[vw-access-button]'); const originalVlibrasButton = document.querySelector('[vw-access-button]');
if (originalVlibrasButton) { if (originalVlibrasButton) {
@ -47,53 +137,96 @@ function BotaoAcessibilidade() {
setIsMenuOpen(false); setIsMenuOpen(false);
}; };
const handleReadAloud = () => {
const selectedText = window.getSelection().toString().trim();
if (selectedText) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(selectedText);
utterance.lang = 'pt-BR';
window.speechSynthesis.speak(utterance);
} else {
alert("Por favor, selecione um texto para ler em voz alta.");
}
setIsMenuOpen(false);
};
return ( return (
<div className={`container-acessibilidade ${isDarkMode ? 'dark-mode' : ''}`}> <div className={`container-acessibilidade ${isDarkMode ? 'dark-mode' : ''}`}>
{isReadingGuide && <GuiaDeLeitura />}
<svg style={{ position: 'absolute', height: 0, width: 0 }}>
<defs>
<filter id="protanopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.567, 0.433, 0, 0, 0, 0.558, 0.442, 0, 0, 0, 0, 0.242, 0.758, 0, 0, 0, 0, 0, 1, 0"/></filter>
<filter id="deuteranopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.625, 0.375, 0, 0, 0, 0.7, 0.3, 0, 0, 0, 0, 0.3, 0.7, 0, 0, 0, 0, 0, 1, 0"/></filter>
<filter id="tritanopia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.95, 0.05, 0, 0, 0, 0, 0.433, 0.567, 0, 0, 0, 0.475, 0.525, 0, 0, 0, 0, 0, 1, 0"/></filter>
<filter id="achromatopsia"><feColorMatrix in="SourceGraphic" type="matrix" values="0.299, 0.587, 0.114, 0, 0, 0.299, 0.587, 0.114, 0, 0, 0.299, 0.587, 0.114, 0, 0, 0, 0, 0, 1, 0"/></filter>
</defs>
</svg>
<div className={`menu-opcoes ${isMenuOpen ? 'aberto' : ''}`}> <div className={`menu-opcoes ${isMenuOpen ? 'aberto' : ''}`}>
<div className="menu-titulo">Acessibilidade</div> <div className="menu-titulo">Acessibilidade</div>
<div className="font-size-control">
<div className="font-size-label">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="4 7 4 4 20 4 20 7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line></svg>
Tamanho da Fonte
</div>
<div className="font-size-buttons">
<button onClick={handleDecreaseFontSize} title="Diminuir Fonte">A-</button>
<span>{`${Math.round(fontSize * 100)}%`}</span>
<button onClick={handleIncreaseFontSize} title="Aumentar Fonte">A+</button>
</div>
</div>
<div className="acessibilidade-select-control">
<label htmlFor="colorblind-select">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
Modo Daltonismo
</label>
<select id="colorblind-select" value={colorblindMode} onChange={(e) => setColorblindMode(e.target.value)}>
<option value="none">Desativado</option>
<option value="protanopia">Protanopia</option>
<option value="deuteranopia">Deuteranopia</option>
<option value="tritanopia">Tritanopia</option>
<option value="achromatopsia">Monocromático</option>
</select>
</div>
<label htmlFor="darkModeCheckbox" className="checkbox-label-button"> <label htmlFor="darkModeCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
Modo Escuro Modo Escuro
<input <input type="checkbox" id="darkModeCheckbox" checked={isDarkMode} onChange={() => setIsDarkMode(!isDarkMode)} />
type="checkbox"
id="darkModeCheckbox"
checked={isDarkMode}
onChange={() => setIsDarkMode(!isDarkMode)}
/>
</label> </label>
<label htmlFor="dyslexiaFontCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
Fonte para Dislexia
<input type="checkbox" id="dyslexiaFontCheckbox" checked={isDyslexiaFont} onChange={() => setIsDyslexiaFont(!isDyslexiaFont)} />
</label>
<label htmlFor="letterSpacingCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
Espaçamento entre Letras
<input type="checkbox" id="letterSpacingCheckbox" checked={isLetterSpacing} onChange={() => setIsLetterSpacing(!isLetterSpacing)} />
</label>
<label htmlFor="lineHeightCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
Altura da Linha
<input type="checkbox" id="lineHeightCheckbox" checked={isLineHeight} onChange={() => setIsLineHeight(!isLineHeight)} />
</label>
<label htmlFor="readingGuideCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
Guia de Leitura
<input type="checkbox" id="readingGuideCheckbox" checked={isReadingGuide} onChange={() => setIsReadingGuide(!isReadingGuide)} />
</label>
<label htmlFor="bigCursorCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg>
Cursor Grande
<input type="checkbox" id="bigCursorCheckbox" checked={isBigCursor} onChange={() => setIsBigCursor(!isBigCursor)} />
</label>
<label htmlFor="readOnHoverCheckbox" className="checkbox-label-button"> <label htmlFor="readOnHoverCheckbox" className="checkbox-label-button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path></svg>
Leitura instantânea Leitura instantânea
<input <input type="checkbox" id="readOnHoverCheckbox" checked={isReadOnHoverActive} onChange={() => setIsReadOnHoverActive(!isReadOnHoverActive)} />
type="checkbox"
id="readOnHoverCheckbox"
checked={isReadOnHoverActive}
onChange={() => setIsReadOnHoverActive(!isReadOnHoverActive)}
/>
</label> </label>
{/* ADICIONADO DE VOLTA: Botão para LIBRAS */}
<button onClick={handleVlibrasClick}> <button onClick={handleVlibrasClick}>
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0" /><path d="M14 10V4a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v2" /><path d="M10 10.5V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v8" /><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v0" /><path d="M14 10V4a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v2" /><path d="M10 10.5V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v8" /><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h2.3" /></svg>
Traduzir para LIBRAS Traduzir para LIBRAS
</button> </button>
</div> </div>
<button
className="botao-flutuante-acessibilidade" <button className="botao-flutuante-acessibilidade" onClick={() => setIsMenuOpen(!isMenuOpen)} title="Menu de Acessibilidade">
onClick={() => setIsMenuOpen(!isMenuOpen)}
title="Menu de Acessibilidade"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="30" height="30" fill="white"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="30" height="30" fill="white">
<path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z" /> <path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z" />
</svg> </svg>
@ -101,6 +234,4 @@ function BotaoAcessibilidade() {
</div> </div>
); );
} }
export default BotaoAcessibilidade; export default BotaoAcessibilidade;

View File

@ -0,0 +1,17 @@
[
{
"name": "Menu",
"isTitle": true
},
{
"name": "Cadastro de Pacientes",
"icon": "clipboard-heart-fill",
"url": "/usuario/pacientes"
},
{
"name": "Laudo do Paciente",
"icon": "table",
"url": "/usuario/laudo"
}
]

View File

@ -158,7 +158,8 @@ function PatientCadastroManager({ setCurrentPage }) {
setTimeout(() => { setTimeout(() => {
setShowModal(false); setShowModal(false);
navigate('/secretaria/pacientes'); // use relative navigation so this component works under different parent routes
navigate('..');
}, 2000); }, 2000);
} catch (error) { } catch (error) {
@ -203,7 +204,8 @@ function PatientCadastroManager({ setCurrentPage }) {
onClick={() => { onClick={() => {
setShowModal(false); setShowModal(false);
if (infosModal.title === 'Sucesso') { if (infosModal.title === 'Sucesso') {
navigate('/secretaria/pacientes'); // navigate to parent 'pacientes' route
navigate('..');
} }
}} }}
className={`modal-confirm-button ${infosModal.title === 'Sucesso' ? 'success' : 'error'}`} className={`modal-confirm-button ${infosModal.title === 'Sucesso' ? 'success' : 'error'}`}
@ -228,7 +230,7 @@ function PatientCadastroManager({ setCurrentPage }) {
<div className="col-12"> <div className="col-12">
<PatientForm <PatientForm
onSave={handleSavePatient} onSave={handleSavePatient}
onCancel={() => navigate('/secretaria/pacientes')} onCancel={() => navigate('..')}
formData={formData} formData={formData}
setFormData={setFormData} setFormData={setFormData}
isLoading={isLoading} isLoading={isLoading}

View File

@ -0,0 +1,42 @@
import { Routes, Route } from "react-router-dom";
import Sidebar from "../../components/Sidebar";
import UsuarioItems from "../../data/sidebar-items-usuario.json";
import TablePaciente from "../../pages/TablePaciente";
import LaudoManager from "../../pages/LaudoManager";
import PatientCadastroManager from "../../pages/PatientCadastroManager";
import { Navigate } from "react-router-dom";
const UsuarioMensagens = () => (
<div>
<h2>Minhas Mensagens</h2>
<p>Aqui ficam as mensagens do usuário.</p>
</div>
);
const UsuarioConfig = () => (
<div>
<h2>Configurações</h2>
<p>Opções de configuração do usuário.</p>
</div>
);
function PerfilUsuario({ onLogout }) {
return (
<div id="app" className="active">
<Sidebar onLogout={onLogout} menuItems={UsuarioItems} />
<div id="main">
<Routes>
<Route path="/" element={<Navigate to="pacientes/cadastro" replace />} />
<Route path="perfil" element={<Navigate to="pacientes/cadastro" replace />} />
<Route path="pacientes/cadastro" element={<PatientCadastroManager />} />
<Route path="pacientes" element={<TablePaciente />} />
<Route path="laudo" element={<LaudoManager />} />
<Route path="*" element={<h2>Página não encontrada</h2>} />
</Routes>
</div>
</div>
);
}
export default PerfilUsuario;