From 2f3c0420e31e6ddd1e0faee7b6cfe9f0f35071c4 Mon Sep 17 00:00:00 2001 From: VPS 4 Builder Date: Thu, 14 May 2026 04:44:53 +0200 Subject: [PATCH] feat(routing): switch from hash routing to HTML5 History API Remove all window.location.hash assignments and hashchange events. Navigate with history.pushState; listen on popstate. Fix base href to '/' so assets resolve correctly on deep paths. Update magic link URL. Co-Authored-By: Claude Sonnet 4.6 --- backend/server.js | 2 +- frontend/App.tsx | 27 +++++----------------- frontend/components/NotificationCenter.tsx | 2 +- frontend/index.html | 2 +- frontend/services/backend.ts | 2 +- frontend/views/Dashboard.tsx | 16 ++++++------- frontend/views/DentistRegisterView.tsx | 4 ++-- 7 files changed, 20 insertions(+), 35 deletions(-) diff --git a/backend/server.js b/backend/server.js index b11a768..fb13b23 100644 --- a/backend/server.js +++ b/backend/server.js @@ -264,7 +264,7 @@ app.post('/api/dentistas/magic-link', async (req, res) => { try { const token = Math.random().toString(36).substring(2, 15); const appUrl = process.env.APP_URL || `http://localhost:${process.env.FRONTEND_PORT || 3000}`; - const magicLink = `${appUrl}/#/cadastro-dentista?token=${token}&clinica=${clinicaId}&nome=${encodeURIComponent(dentistName)}`; + const magicLink = `${appUrl}/cadastro-dentista?token=${token}&clinica=${clinicaId}&nome=${encodeURIComponent(dentistName)}`; res.json({ success: true, link: magicLink }); } catch (err) { res.status(500).json({ error: err.message }); } }); diff --git a/frontend/App.tsx b/frontend/App.tsx index de345d1..5b15e03 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -97,29 +97,16 @@ const VIEW_TO_ROUTE: Record = { 'cadastro-dentista': '/cadastro-dentista' }; -/** Get the hash path, e.g. "/#/pacientes" → "/pacientes" */ -function getHashPath(): string { - const hash = window.location.hash; - if (hash.startsWith('#/')) return hash.slice(1); // "#/pacientes" → "/pacientes" - if (hash === '#' || hash === '') return '/'; - return '/'; -} - /** Resolve current URL to a ViewKey */ function resolveViewFromUrl(): ViewKey { - // Legacy support: ?p=contato - if (window.location.search.includes('p=contato')) return 'public'; - // Legacy support: #update_db (old non-slash hash) - if (window.location.hash === '#update_db') return 'update'; - - const path = getHashPath(); + const path = window.location.pathname || '/'; return ROUTE_MAP[path] ?? 'dashboard'; } -/** Push a new hash-based URL without reloading the page */ +/** Push a new URL without reloading the page */ function navigate(view: ViewKey) { const route = VIEW_TO_ROUTE[view]; - window.location.hash = route; + history.pushState({}, '', route); } // ------- App ------- @@ -200,8 +187,8 @@ const App: React.FC = () => { setCurrentView(fromUrl); }; - window.addEventListener('hashchange', onHashChange); - return () => window.removeEventListener('hashchange', onHashChange); + window.addEventListener('popstate', onHashChange); + return () => window.removeEventListener('popstate', onHashChange); }, []); // Inject dynamic brand color @@ -210,9 +197,7 @@ const App: React.FC = () => { document.documentElement.style.setProperty('--brand-color-soft', `${clinColor}11`); }, [clinColor]); - // 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). + // On mount: always sync URL to match the actual current view (handles auth redirects). useEffect(() => { navigate(currentView); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/components/NotificationCenter.tsx b/frontend/components/NotificationCenter.tsx index b2a93f9..759f0c4 100644 --- a/frontend/components/NotificationCenter.tsx +++ b/frontend/components/NotificationCenter.tsx @@ -148,7 +148,7 @@ export const NotificationCenter: React.FC = () => {
@@ -253,7 +253,7 @@ export const Dashboard: React.FC = () => { {recent.length > 0 ? recent.map((app: any) => (
window.location.hash = '#/agenda'} + onClick={() => history.pushState({}, '', '/agenda')} className="flex items-center gap-4 group cursor-pointer" >
@@ -276,7 +276,7 @@ export const Dashboard: React.FC = () => {