Sidebar Preferences
Sidebar Preferences
Per-user setting for which dashboard sidebar navigation items are hidden. Stored in per-user SQLite encrypted settings, surfaced in Settings → Display.
Architecture
- Uses
getSetting/setSettingfrom@/lib/db/settings-helpers.server(per-user encrypted settings) — see Per-user encrypted settings for the underlying pattern. - Stores a JSON array of URL path patterns (e.g.
["/dashboard/ai-chat"]) under a single settings key, serialized withJSON.stringify/JSON.parse— not a dedicated table, since this is one small blob per user rather than rows that need querying individually. - Default (no row): all sidebar items visible (
[]) - Dedicated query key in
query-keys.tsfor cache management - Server fn:
getSidebarPreferencesFn(GET),updateSidebarPreferencesFn(POST)
Gate ordering
The write is a single-user settings blob replace — no read-then-write check on existing state, so it's a straightforward mutation inside withWriteLock, no pre-lock gate needed:
export const updateSidebarPreferencesFn = createServerFn({ method: 'POST' })
.inputValidator(z.object({ hiddenUrls: z.array(z.string()) }))
.handler(async ({ data }) => {
const user = await requireUser();
if (!user) return err(ERROR_CODES.UNAUTHORIZED, 'Not authenticated');
return withWriteLock(user.id, () => {
const db = getUserDb(user.id);
setSetting(db, HIDDEN_SIDEBAR_ITEMS_KEY, JSON.stringify(data.hiddenUrls));
return ok(undefined);
});
});
Reading (getSidebarPreferencesFn) parses the stored JSON, falling back to [] when no row exists.
Tests
Covered in src/features/sidebar-preferences/server/sidebar-preferences-crud.test.ts.