Problem
frontend/providers/ProtectedRoute.tsx:19 uses a hardcoded 100ms setTimeout to wait for Zustand's persist middleware to rehydrate from localStorage. On slower devices the auth check at line 24 executes before hydration completes. The result is that unauthenticated users briefly see protected admin content before being redirected.
Proposed Solution
- In the auth Zustand store, add a
_hasHydrated: boolean field (default: false)
- Use Zustand's
onRehydrateStorage callback to set _hasHydrated = true once persistence finishes
- Create a
useAuthRehydrated() hook that subscribes to _hasHydrated
- Update
ProtectedRoute to render a full-screen skeleton loader until _hasHydrated is true, then perform the auth check
- Remove the
setTimeout entirely
Acceptance Criteria
Problem
frontend/providers/ProtectedRoute.tsx:19uses a hardcoded 100mssetTimeoutto wait for Zustand'spersistmiddleware to rehydrate from localStorage. On slower devices the auth check at line 24 executes before hydration completes. The result is that unauthenticated users briefly see protected admin content before being redirected.Proposed Solution
_hasHydrated: booleanfield (default:false)onRehydrateStoragecallback to set_hasHydrated = trueonce persistence finishesuseAuthRehydrated()hook that subscribes to_hasHydratedProtectedRouteto render a full-screen skeleton loader until_hasHydratedistrue, then perform the auth checksetTimeoutentirelyAcceptance Criteria
null) renders during hydration_hasHydratedresolves correctly on cold start, page refresh, and back-navigationsetTimeoutor arbitrary delay remains in auth initialization code