fix(nav): corrige sistema de navegação pós-login e sincronização de URL

Causa raiz do login não navegar: getCurrentRole() retornava null quando
vinculos.role era NULL no banco, fazendo isViewAllowed() retornar false
e handleNavigate() falhar silenciosamente sem nenhum feedback.

Fixes:
- backend.ts: getCurrentRole usa workspace?.role || 'paciente' para
  garantir que nunca retorne null/undefined
- App.tsx: onLoginSuccess usa setCurrentView+navigate diretamente,
  sem passar pela guarda de permissão (desnecessária pós-autenticação)
- App.tsx: mount effect sempre chama navigate(currentView) para sincronizar
  a URL com a view real — antes só executava com hash vazio, deixando
  /#/login na URL ao recarregar com usuário autenticado
- LoginView.tsx: remove spinner isInitializing de 3s que bloqueava o
  formulário em toda montagem do componente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-05-13 22:56:30 +02:00
parent 1330147eaf
commit 26a10ac4f7
3 changed files with 10 additions and 24 deletions
+7 -5
View File
@@ -210,11 +210,11 @@ const App: React.FC = () => {
document.documentElement.style.setProperty('--brand-color-soft', `${clinColor}11`); document.documentElement.style.setProperty('--brand-color-soft', `${clinColor}11`);
}, [clinColor]); }, [clinColor]);
// On mount: if there's no hash yet, write the current view into the URL // On mount: always sync URL to match the actual current view.
// This handles auth redirects (e.g. reload at /#/login while authenticated shows dashboard
// but URL would stay /#/login without this call).
useEffect(() => { useEffect(() => {
if (!window.location.hash || window.location.hash === '#update_db') { navigate(currentView);
navigate(currentView);
}
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@@ -244,7 +244,9 @@ const App: React.FC = () => {
default: default:
return <LoginView onLoginSuccess={() => { return <LoginView onLoginSuccess={() => {
const role = HybridBackend.getCurrentRole(); const role = HybridBackend.getCurrentRole();
handleNavigate(getDefaultViewForRole(role)); const view = getDefaultViewForRole(role);
setCurrentView(view);
navigate(view);
}} />; }} />;
} }
}; };
+1 -1
View File
@@ -141,7 +141,7 @@ export const HybridBackend = {
getCurrentRole: (): string => { getCurrentRole: (): string => {
const workspace = HybridBackend.getActiveWorkspace(); const workspace = HybridBackend.getActiveWorkspace();
return workspace ? workspace.role : 'paciente'; return workspace?.role || 'paciente';
}, },
getDashboardStats: async () => { getDashboardStats: async () => {
+2 -18
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import { Database, Lock, User, ArrowRight, ShieldCheck, Loader2 } from 'lucide-react'; import { Database, Lock, User, ArrowRight, ShieldCheck } from 'lucide-react';
import { HybridBackend } from '../services/backend.ts'; import { HybridBackend } from '../services/backend.ts';
interface LoginViewProps { interface LoginViewProps {
@@ -11,12 +11,6 @@ export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [isInitializing, setIsInitializing] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setIsInitializing(false), 3000);
return () => clearTimeout(timer);
}, []);
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -37,16 +31,6 @@ export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
} }
}; };
if (isInitializing) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-4">
<Loader2 className="animate-spin text-blue-600 mb-4" size={48} />
<h2 className="text-gray-500 font-bold uppercase tracking-widest text-sm">Inicializando Sistema...</h2>
<span className="text-gray-400 text-xs mt-2 font-mono font-bold bg-gray-200 px-2 py-1 rounded">V1.0.2</span>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4"> <div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-2xl shadow-xl overflow-hidden border border-gray-100"> <div className="max-w-md w-full bg-white rounded-2xl shadow-xl overflow-hidden border border-gray-100">