@@ -40,8 +40,7 @@ const checkRestartFlag = (): boolean => {
4040} ;
4141
4242// Load state from localStorage
43- // On page refresh, set isActive to false - user must manually restart
44- // Exception: if restart flag is set, start onboarding
43+ // On page refresh, resume if it was active
4544const loadState = ( ) : OnboardingState => {
4645 const shouldRestart = checkRestartFlag ( ) ;
4746
@@ -54,8 +53,8 @@ const loadState = (): OnboardingState => {
5453 const stored = localStorage . getItem ( STORAGE_KEY ) ;
5554 if ( stored ) {
5655 const parsed = JSON . parse ( stored ) ;
57- // Always set isActive to false on load - closing/refreshing the page closes onboarding
58- return { ...defaultState , ...parsed , isActive : false } ;
56+ // Resume isActive state from storage instead of forcing false
57+ return { ...defaultState , ...parsed } ;
5958 }
6059 } catch {
6160 // Ignore parse errors
@@ -77,7 +76,8 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
7776 const navigate = useNavigate ( ) ;
7877 const queryClient = useQueryClient ( ) ;
7978 const { isReady : isDatabaseReady } = useDatabase ( ) ;
80- const { switchProfile, profiles, activeProfileId } = useProfile ( ) ;
79+ const { switchProfile, profiles, activeProfileId, setProfileHidden } =
80+ useProfile ( ) ;
8181 const { isEncryptionEnabled } = useEncryption ( ) ;
8282 const [ state , setState ] = useState < OnboardingState > ( loadState ) ;
8383 // isCreatingDemo is kept for the context value but we don't set it anymore
@@ -184,13 +184,22 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
184184 } ;
185185 const requestedPath = normalizePath ( window . location . pathname ) ;
186186
187- // If demo profile exists but not active, switch to it
187+ // If demo profile exists, unhide it and switch to it
188188 try {
189- const demoProfile = profiles . find ( ( p ) => p . id === DEMO_PROFILE_ID ) ;
190- if ( demoProfile && activeProfileId !== DEMO_PROFILE_ID ) {
191- switchProfile ( DEMO_PROFILE_ID ) ;
192- await queryClient . invalidateQueries ( ) ;
193- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
189+ const demoProfile = profiles . find (
190+ ( p ) => p . id === DEMO_PROFILE_ID || p . name === 'Demo'
191+ ) ;
192+ if ( demoProfile ) {
193+ // Unhide demo profile if it was hidden
194+ if ( demoProfile . isHidden ) {
195+ await setProfileHidden ( demoProfile . id , false ) ;
196+ }
197+ // Switch to demo profile if not already active
198+ if ( activeProfileId !== demoProfile . id ) {
199+ switchProfile ( demoProfile . id ) ;
200+ await queryClient . invalidateQueries ( ) ;
201+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
202+ }
194203 }
195204 } catch ( error ) {
196205 console . error ( 'Error switching to demo profile:' , error ) ;
@@ -208,19 +217,26 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
208217 const currentPath = requestedPath ;
209218 // Find all chapters matching this route (excluding welcome at index 0 and completion at last index)
210219 const lastIndex = onboardingChapters . length - 1 ;
220+
221+ // Special case for dashboard: it's at '/' but we want to skip 'welcome' and 'navigation'
222+ // if we are specifically starting from the dashboard page.
211223 const matchingChapters = onboardingChapters
212224 . map ( ( chapter , index ) => ( { chapter, index } ) )
213- . filter (
214- ( { chapter, index } ) =>
215- normalizePath ( chapter . route ) === currentPath &&
216- index > 0 &&
217- index < lastIndex
218- ) ;
225+ . filter ( ( { chapter, index } ) => {
226+ const chapterRoute = normalizePath ( chapter . route ) ;
227+ // Exact match for route
228+ if ( chapterRoute !== currentPath ) return false ;
229+
230+ // Skip welcome (0) and completion (last)
231+ if ( index === 0 || index === lastIndex ) return false ;
232+
233+ return true ;
234+ } ) ;
219235
220236 // If we found matching chapters, use the first content chapter (after navigation chapters)
221- // For dashboard route, this will find the 'dashboard' chapter, not 'welcome' or 'navigation'
222237 if ( matchingChapters . length > 0 ) {
223238 // Prefer chapter with id matching the menuItem (content chapters)
239+ // For dashboard, this will be the 'dashboard' chapter (index 2)
224240 const contentChapter = matchingChapters . find (
225241 ( { chapter } ) =>
226242 chapter . id === chapter . menuItem &&
@@ -299,6 +315,7 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
299315 hasCompletedInitialSetup ,
300316 profiles ,
301317 switchProfile ,
318+ setProfileHidden ,
302319 activeProfileId ,
303320 queryClient ,
304321 ]
@@ -309,9 +326,11 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
309326 // This function just marks onboarding as complete
310327 const completeOnboarding = useCallback ( async ( ) => {
311328 // If demo profile exists but not active, switch to it
312- const demoProfile = profiles . find ( ( p ) => p . id === DEMO_PROFILE_ID ) ;
313- if ( demoProfile && activeProfileId !== DEMO_PROFILE_ID ) {
314- switchProfile ( DEMO_PROFILE_ID ) ;
329+ const demoProfile = profiles . find (
330+ ( p ) => p . id === DEMO_PROFILE_ID || p . name === 'Demo'
331+ ) ;
332+ if ( demoProfile && activeProfileId !== demoProfile . id ) {
333+ switchProfile ( demoProfile . id ) ;
315334 await queryClient . invalidateQueries ( ) ;
316335 }
317336
@@ -491,7 +510,9 @@ export function OnboardingProvider({ children }: { children: ReactNode }) {
491510 // Separate concerns:
492511 // 1. needsSecuritySetup: No encryption set up yet (show SecuritySetup component)
493512 // 2. needsOnboarding: User/demo profile not ready (show onboarding tour on top of dashboard)
494- const hasDemoProfile = profiles . some ( ( p ) => p . id === DEMO_PROFILE_ID ) ;
513+ const hasDemoProfile = profiles . some (
514+ ( p ) => p . id === DEMO_PROFILE_ID || p . name === 'Demo'
515+ ) ;
495516
496517 // Security setup needed when encryption is not enabled
497518 const needsSecuritySetup =
0 commit comments