diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f66ea77..0aa66e0 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -81,7 +81,7 @@ import { useOptimisticVehicles } from './features/vehicles/hooks/useOptimisticVe import { CreateVehicleRequest } from './features/vehicles/types/vehicles.types'; import { MobileSettingsScreen } from './features/settings/mobile/MobileSettingsScreen'; import { SecurityMobileScreen } from './features/settings/mobile/SecurityMobileScreen'; -import { useNavigationStore, useUserStore } from './core/store'; +import { useNavigationStore, useUserStore, routeToScreen, screenToRoute } from './core/store'; import { useNeedsVehicleSelection, useDowngrade } from './features/subscription/hooks/useSubscription'; import { useVehicles } from './features/vehicles/hooks/useVehicles'; import { VehicleSelectionDialog } from './features/subscription/components/VehicleSelectionDialog'; @@ -364,6 +364,22 @@ function App() { const [selectedVehicle, setSelectedVehicle] = useState(null); const [showAddVehicle, setShowAddVehicle] = useState(false); + // Sync browser URL to Zustand screen state on mount (enables direct URL navigation on mobile) + useEffect(() => { + const screen = routeToScreen[window.location.pathname]; + if (screen && screen !== activeScreen) { + navigateToScreen(screen, { source: 'url-sync' }); + } + }, []); // eslint-disable-line react-hooks/exhaustive-deps -- intentionally runs once on mount + + // Sync Zustand screen changes back to browser URL (enables bookmarks and URL sharing) + useEffect(() => { + const targetPath = screenToRoute[activeScreen]; + if (targetPath && window.location.pathname !== targetPath) { + window.history.replaceState(null, '', targetPath); + } + }, [activeScreen]); + // Update mobile mode on window resize useEffect(() => { const checkMobileMode = () => { diff --git a/frontend/src/core/store/index.ts b/frontend/src/core/store/index.ts index 20f3c4b..d8ec504 100644 --- a/frontend/src/core/store/index.ts +++ b/frontend/src/core/store/index.ts @@ -1,5 +1,5 @@ // Export navigation store -export { useNavigationStore } from './navigation'; +export { useNavigationStore, routeToScreen, screenToRoute } from './navigation'; export type { MobileScreen, VehicleSubScreen } from './navigation'; // Export user store diff --git a/frontend/src/core/store/navigation.ts b/frontend/src/core/store/navigation.ts index fb507a0..e2a8d34 100644 --- a/frontend/src/core/store/navigation.ts +++ b/frontend/src/core/store/navigation.ts @@ -5,6 +5,45 @@ import { safeStorage } from '../utils/safe-storage'; export type MobileScreen = 'Dashboard' | 'Vehicles' | 'Log Fuel' | 'Maintenance' | 'Stations' | 'Documents' | 'Settings' | 'Security' | 'Subscription' | 'AdminUsers' | 'AdminCatalog' | 'AdminCommunityStations' | 'AdminEmailTemplates' | 'AdminBackup' | 'AdminLogs'; export type VehicleSubScreen = 'list' | 'detail' | 'add' | 'edit'; +/** Maps browser URL paths to mobile screen names for direct URL navigation */ +export const routeToScreen: Record = { + '/garage': 'Dashboard', + '/garage/dashboard': 'Dashboard', + '/garage/vehicles': 'Vehicles', + '/garage/fuel-logs': 'Log Fuel', + '/garage/maintenance': 'Maintenance', + '/garage/stations': 'Stations', + '/garage/documents': 'Documents', + '/garage/settings': 'Settings', + '/garage/settings/security': 'Security', + '/garage/settings/subscription': 'Subscription', + '/garage/settings/admin/users': 'AdminUsers', + '/garage/settings/admin/catalog': 'AdminCatalog', + '/garage/settings/admin/community-stations': 'AdminCommunityStations', + '/garage/settings/admin/email-templates': 'AdminEmailTemplates', + '/garage/settings/admin/backup': 'AdminBackup', + '/garage/settings/admin/logs': 'AdminLogs', +}; + +/** Reverse mapping: mobile screen name to canonical URL path */ +export const screenToRoute: Record = { + 'Dashboard': '/garage/dashboard', + 'Vehicles': '/garage/vehicles', + 'Log Fuel': '/garage/fuel-logs', + 'Maintenance': '/garage/maintenance', + 'Stations': '/garage/stations', + 'Documents': '/garage/documents', + 'Settings': '/garage/settings', + 'Security': '/garage/settings/security', + 'Subscription': '/garage/settings/subscription', + 'AdminUsers': '/garage/settings/admin/users', + 'AdminCatalog': '/garage/settings/admin/catalog', + 'AdminCommunityStations': '/garage/settings/admin/community-stations', + 'AdminEmailTemplates': '/garage/settings/admin/email-templates', + 'AdminBackup': '/garage/settings/admin/backup', + 'AdminLogs': '/garage/settings/admin/logs', +}; + interface NavigationHistory { screen: MobileScreen; vehicleSubScreen?: VehicleSubScreen; @@ -196,7 +235,6 @@ export const useNavigationStore = create()( name: 'motovaultpro-mobile-navigation', storage: createJSONStorage(() => safeStorage), partialize: (state) => ({ - activeScreen: state.activeScreen, vehicleSubScreen: state.vehicleSubScreen, selectedVehicleId: state.selectedVehicleId, formStates: state.formStates,