Skip to content

Commit 998ab3e

Browse files
committed
add changes
1 parent 1c84cf6 commit 998ab3e

16 files changed

Lines changed: 912 additions & 342 deletions

File tree

apps/api/src/db/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ export function initializeDatabase(): void {
142142
"CREATE UNIQUE INDEX IF NOT EXISTS idx_transactions_import_hash_scoped ON transactions(import_hash, profile_id) WHERE import_hash IS NOT NULL AND TRIM(import_hash) != ''"
143143
);
144144
} catch (error) {
145-
console.warn('Failed to update import_hash index (might be expected):', error);
145+
console.warn(
146+
'Failed to update import_hash index (might be expected):',
147+
error
148+
);
146149
}
147150

148151
// Backfill missing import_hash values so older data also dedupes correctly
@@ -357,7 +360,9 @@ function backfillMissingProfileIds(): void {
357360
.prepare(`UPDATE ${table} SET profile_id = ? WHERE profile_id IS NULL`)
358361
.run(defaultProfileId);
359362
if (result.changes > 0) {
360-
console.warn(`Backfilled profile_id for ${result.changes} rows in ${table}`);
363+
console.warn(
364+
`Backfilled profile_id for ${result.changes} rows in ${table}`
365+
);
361366
}
362367
}
363368
} catch (error) {

apps/web/src/components/layout/ProfileSwitcher.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ export function ProfileSwitcher() {
4646
return null;
4747
}
4848

49-
const otherProfiles = profiles.filter((p) => p.id !== activeProfile.id);
49+
// Filter out hidden profiles from the dropdown (but show all in the profile manager)
50+
const otherProfiles = profiles.filter(
51+
(p) => p.id !== activeProfile.id && !p.isHidden
52+
);
5053
const Icon = PROFILE_TYPE_ICONS[activeProfile.type] || User;
5154
const typeColor =
5255
PROFILE_TYPE_COLORS[activeProfile.type] || 'from-gray-500 to-gray-600';

apps/web/src/components/onboarding/OnboardingContext.tsx

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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
4544
const 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

Comments
 (0)