import { Component, lazy, Suspense, useCallback, useEffect, useMemo, useState } from 'react'
import { AppShell } from './components/AppShell.jsx'
import { canAccess } from './config/permissions.js'
import { useAuth } from './hooks/useAuth.js'
import { ForgotPasswordPage, LoginPage, RegisterPage } from './pages/AuthPages.jsx'
import { NotFoundPage } from './pages/NotFoundPage.jsx'
import { patientRepository } from './repositories/patientRepository.js'
const AgendaPage = lazyPage(() => import('./pages/AgendaPage.jsx'), 'AgendaPage')
const AnalyticsPage = lazyPage(() => import('./pages/AnalyticsPage.jsx'), 'AnalyticsPage')
const HomePage = lazyPage(() => import('./pages/HomePage.jsx'), 'HomePage')
const MedicalRecordsPage = lazyPage(() => import('./pages/MedicalRecordsPage.jsx'), 'MedicalRecordsPage')
const MessagesPage = lazyPage(() => import('./pages/MessagesPage.jsx'), 'MessagesPage')
const PatientDetailPage = lazyPage(() => import('./pages/PatientsPage.jsx'), 'PatientDetailPage')
const PatientsPage = lazyPage(() => import('./pages/PatientsPage.jsx'), 'PatientsPage')
const ProfilePage = lazyPage(() => import('./pages/ProfilePage.jsx'), 'ProfilePage')
const ReportsPage = lazyPage(() => import('./pages/ReportsPage.jsx'), 'ReportsPage')
const SettingsPage = lazyPage(() => import('./pages/SettingsPage.jsx'), 'SettingsPage')
const UsersPage = lazyPage(() => import('./pages/UsersPage.jsx'), 'UsersPage')
const VisitsPage = lazyPage(() => import('./pages/VisitsPage.jsx'), 'VisitsPage')
const PANEL_PATHS = ['/inicio', '/home', '/dashboard']
const ROLE_HOME_PATHS = {
medico: '/agenda',
secretaria: '/agenda',
}
function lazyPage(loader, exportName) {
return lazy(() => loader().then((module) => ({ default: module[exportName] })))
}
function App() {
const [location, setLocation] = useState(() => readLocation())
const { isAuthenticated, role, loading: authLoading } = useAuth()
const navigate = useCallback((to, options = {}) => {
if (options.replace) {
window.history.replaceState({}, '', to)
} else {
window.history.pushState({}, '', to)
}
setLocation(readLocation())
const hash = to.split('#')[1]
window.requestAnimationFrame(() => {
if (hash) {
document.getElementById(hash)?.scrollIntoView({ block: 'start' })
} else {
window.scrollTo({ left: 0, top: 0 })
}
})
}, [])
useEffect(() => {
function handlePopState() {
setLocation(readLocation())
}
window.addEventListener('popstate', handlePopState)
return () => window.removeEventListener('popstate', handlePopState)
}, [])
const route = useMemo(
() => resolveRoute(location.pathname, navigate, role),
[location.pathname, navigate, role],
)
// Tela de carregamento enquanto busca o role do usuário
if (authLoading) {
return (
)
}
// Rotas públicas (sem shell)
if (!route.withShell) {
return {route.element}
}
// Usuário não autenticado
if (!isAuthenticated) {
return
}
// Usuário autenticado mas sem permissão para a rota
if (!role || !canAccess(role, location.pathname)) {
const roleHomePath = ROLE_HOME_PATHS[role]
if (roleHomePath && PANEL_PATHS.includes(location.pathname)) {
navigate(roleHomePath, { replace: true })
return null
}
return (
)
}
return (
{route.element}
)
}
class RouteErrorBoundary extends Component {
state = { error: null }
static getDerivedStateFromError(error) {
return { error }
}
componentDidUpdate(previousProps) {
if (previousProps.resetKey !== this.props.resetKey && this.state.error) {
this.setState({ error: null })
}
}
render() {
if (this.state.error) {
return
}
return this.props.children
}
}
function RouteSuspense({ children, resetKey }) {
return (
}>
{children}
)
}
function RouteFallback() {
return (
)
}
function RouteErrorFallback() {
return (
Não foi possÃvel carregar esta tela
Ocorreu um erro ao abrir o modulo. Recarregue a pagina e tente novamente.
)
}
function resolveRoute(pathname, navigate, role) {
if (pathname === '/' || pathname === '/login') {
return {
element: ,
title: 'Login',
withShell: false,
}
}
if (pathname === '/cadastro') {
return {
element: ,
title: 'Cadastro',
withShell: false,
}
}
if (pathname === '/recuperar-senha') {
return {
element: ,
title: 'Recuperar senha',
withShell: false,
}
}
if (pathname === '/inicio' || pathname === '/home' || pathname === '/dashboard') {
return {
element: ,
title: 'Painel',
withShell: true,
}
}
if (pathname === '/agenda') {
return {
element: ,
title: 'Agenda',
withShell: true,
}
}
if (pathname === '/pacientes') {
return {
element: ,
title: 'Pacientes',
withShell: true,
}
}
if (pathname === '/prontuario') {
return {
element: ,
title: 'Prontuário',
withShell: true,
}
}
if (pathname === '/prontuario/novo') {
return {
element: ,
title: 'Novo prontuário',
withShell: true,
}
}
if (pathname.startsWith('/prontuario/')) {
const [, , recordId, action] = pathname.split('/')
return {
element: ,
title: action === 'editar' ? 'Editar prontuário' : 'Prontuário',
withShell: true,
}
}
if (pathname.startsWith('/pacientes/')) {
const patientId = pathname.split('/')[2]
return {
element: ,
title: 'Paciente',
withShell: true,
}
}
if (pathname === '/consultas') {
return {
element: ,
title: 'Consultas',
withShell: true,
}
}
if (pathname === '/laudos') {
return {
element: ,
title: 'Relatórios',
withShell: true,
}
}
if (pathname === '/relatorios') {
return {
element: ,
title: 'Analytics',
withShell: true,
}
}
if (pathname === '/camunicacao') {
navigate('/comunicacao', { replace: true })
return {
element: ,
title: 'Comunicação',
withShell: true,
}
}
if (pathname === '/comunicacao' || pathname === '/mensagens') {
return {
element: ,
title: 'Comunicação',
withShell: true,
}
}
if (pathname === '/usuarios') {
return {
element: ,
title: 'Usuários',
withShell: true,
}
}
if (pathname === '/perfil') {
return {
element: ,
title: 'Perfil',
withShell: true,
}
}
if (pathname === '/configuracoes' || pathname === '/config') {
return {
element: ,
title: 'Configurações',
withShell: true,
}
}
return {
element: ,
title: 'Página não encontrada',
withShell: true,
}
}
function PatientDetailRoute({ navigate, patientId, role }) {
const [patient, setPatient] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let active = true
patientRepository
.getById(patientId)
.then((data) => {
if (active) setPatient(data)
})
.finally(() => {
if (active) setLoading(false)
})
return () => {
active = false
}
}, [patientId])
if (loading) {
return Carregando paciente...
}
return patient ? (
) : (
)
}
function UnauthorizedPage({ navigate }) {
return (
🔒
Acesso não permitido
Você não tem permissão para acessar esta página.
)
}
function readLocation() {
return {
pathname: normalizePath(window.location.pathname),
search: window.location.search,
}
}
function normalizePath(pathname) {
if (!pathname || pathname === '/') {
return '/'
}
return pathname.replace(/\/+$/, '')
}
export default App