feat(categories): dashboard v1 discovery banner (#118) #130

Merged
maximus merged 1 commit from issue-118-dashboard-discovery-banner into main 2026-04-21 01:16:21 +00:00
Owner

Fixes #118

Summary

Adds a non-destructive, dismissable Dashboard banner that invites profiles still on the legacy v2 category seed to discover the new v1 IPC taxonomy guide shipped in #117.

  • New component: src/components/dashboard/CategoriesV1DiscoveryBanner.tsx
  • Integrated at the top of src/pages/DashboardPage.tsx
  • Uses the existing user_preferences key/value table via getPreference/setPreference (no schema change, no new migration)
  • i18n keys under dashboard.categoriesBanner.* (FR + EN)
  • CHANGELOG entries under [Unreleased] in both CHANGELOG.md and CHANGELOG.fr.md

Behaviour

  • src/components/dashboard/CategoriesV1DiscoveryBanner.tsx created
  • Visibility condition: categories_schema_version='v2' AND categories_v1_banner_dismissed != '1'
  • Persistent dismiss flag in user_preferences (categories_v1_banner_dismissed)
  • CTA links to /settings/categories/standard (guide page from #117)
  • Dismiss button hides the banner and persists the flag
  • i18n FR / EN
  • No destructive action — read-only CTA, no category mutation
  • New v1-seeded profiles never see the banner
  • Persistence survives restart (DB-backed, not sessionStorage)

Test plan

  • npx tsc --noEmit clean
  • npx vitest run — 168/168 tests pass
  • npm run build succeeds
  • Manual: open a v2 profile → banner visible → click Discover → navigates to guide. Reload → banner still visible. Dismiss → banner gone. Reload → still gone.
  • Manual: open a v1 profile (new profile) → banner never appears.
Fixes #118 ## Summary Adds a non-destructive, dismissable Dashboard banner that invites profiles still on the legacy v2 category seed to discover the new v1 IPC taxonomy guide shipped in #117. - New component: `src/components/dashboard/CategoriesV1DiscoveryBanner.tsx` - Integrated at the top of `src/pages/DashboardPage.tsx` - Uses the existing `user_preferences` key/value table via `getPreference`/`setPreference` (no schema change, no new migration) - i18n keys under `dashboard.categoriesBanner.*` (FR + EN) - CHANGELOG entries under `[Unreleased]` in both `CHANGELOG.md` and `CHANGELOG.fr.md` ## Behaviour - [x] `src/components/dashboard/CategoriesV1DiscoveryBanner.tsx` created - [x] Visibility condition: `categories_schema_version='v2'` **AND** `categories_v1_banner_dismissed != '1'` - [x] Persistent dismiss flag in `user_preferences` (`categories_v1_banner_dismissed`) - [x] CTA links to `/settings/categories/standard` (guide page from #117) - [x] Dismiss button hides the banner and persists the flag - [x] i18n FR / EN - [x] No destructive action — read-only CTA, no category mutation - [x] New v1-seeded profiles never see the banner - [x] Persistence survives restart (DB-backed, not sessionStorage) ## Test plan - [x] `npx tsc --noEmit` clean - [x] `npx vitest run` — 168/168 tests pass - [x] `npm run build` succeeds - [ ] Manual: open a v2 profile → banner visible → click *Discover* → navigates to guide. Reload → banner still visible. Dismiss → banner gone. Reload → still gone. - [ ] Manual: open a v1 profile (new profile) → banner never appears.
maximus added 1 commit 2026-04-21 01:12:13 +00:00
feat(categories): add dashboard v1 discovery banner (#118)
All checks were successful
PR Check / rust (push) Successful in 21m42s
PR Check / frontend (push) Successful in 2m14s
PR Check / rust (pull_request) Successful in 21m50s
PR Check / frontend (pull_request) Successful in 2m17s
0ded5a1ac6
Adds a non-destructive, dismissable banner on the Dashboard that invites
profiles still on the legacy v2 category seed to discover the new v1 IPC
taxonomy. The banner links to the standard categories guide page
(/settings/categories/standard, shipped in #117).

Visibility rules:
- Only rendered when `categories_schema_version='v2'` in `user_preferences`.
- Hidden once the user dismisses it — dismissal is persisted in the same
  `user_preferences` table under the key `categories_v1_banner_dismissed`
  (value '1'), so the banner never reappears across app restarts.
- New v1-seeded profiles never see it.

No DB schema change: reuses the existing key/value `user_preferences`
table via the existing `getPreference`/`setPreference` helpers. No
migration added.

i18n keys under `dashboard.categoriesBanner.*` (FR + EN).
Changelog entry added under [Unreleased] in both CHANGELOG.md and
CHANGELOG.fr.md.
Author
Owner

Self-review — APPROVE

Security

  • No user input rendered as HTML — only string comparisons (=== "v2", !== "1") and i18n strings.
  • No telemetry, no network call. Privacy-first respected.
  • Preference writes go through existing parameterized helpers (userPreferenceService.setPreference) — no SQL injection surface introduced.
  • CTA navigates to a hardcoded internal route /settings/categories/standard.

Correctness

  • Initial state "loading" avoids a flash of the banner on first mount before prefs are read.
  • cancelled flag in useEffect correctly prevents setState after unmount.
  • Visibility rule matches spec: schemaVersion === "v2" && dismissed !== "1" — new v1-seeded profiles never see it.
  • Dismiss is optimistic: UI hides immediately, then persists. If persistence fails, banner may reappear on next launch — acceptable non-destructive degradation.
  • No race between read and dismiss — the dismiss write is strict ON CONFLICT UPDATE (existing helper contract).

Quality

  • Follows the existing TokenStoreFallbackBanner pattern (icon + title + description + dismiss).
  • Tailwind uses the theme CSS variables (var(--primary), var(--foreground), var(--muted-foreground)) — matches the rest of the app.
  • ARIA: role="status" on the container, aria-label on the dismiss button.
  • Component is under 105 lines, no dead code.

Data / migrations

  • No DB schema change — reuses the existing user_preferences key/value table. Migration v8 already exists (INSERT OR IGNORE … categories_schema_version) and is the authoritative source the banner reads from.
  • New flag key categories_v1_banner_dismissed matches the spec exactly.

i18n

  • 4 new keys symmetric in FR + EN under dashboard.categoriesBanner.* (title / description / cta / dismiss).
  • No hardcoded strings in the component.

Changelog

  • Entries added under [Unreleased] / ### Added in both CHANGELOG.md and CHANGELOG.fr.md, referencing (#118).

Build / tests

  • npx tsc --noEmit — clean.
  • npx vitest run — 168/168 pass.
  • npm run build — succeeds.

Verdict: APPROVE.

## Self-review — APPROVE ### Security - No user input rendered as HTML — only string comparisons (`=== "v2"`, `!== "1"`) and i18n strings. - No telemetry, no network call. Privacy-first respected. - Preference writes go through existing parameterized helpers (`userPreferenceService.setPreference`) — no SQL injection surface introduced. - CTA navigates to a hardcoded internal route `/settings/categories/standard`. ### Correctness - Initial state `"loading"` avoids a flash of the banner on first mount before prefs are read. - `cancelled` flag in `useEffect` correctly prevents `setState` after unmount. - Visibility rule matches spec: `schemaVersion === "v2" && dismissed !== "1"` — new v1-seeded profiles never see it. - Dismiss is optimistic: UI hides immediately, then persists. If persistence fails, banner may reappear on next launch — acceptable non-destructive degradation. - No race between read and dismiss — the dismiss write is strict ON CONFLICT UPDATE (existing helper contract). ### Quality - Follows the existing `TokenStoreFallbackBanner` pattern (icon + title + description + dismiss). - Tailwind uses the theme CSS variables (`var(--primary)`, `var(--foreground)`, `var(--muted-foreground)`) — matches the rest of the app. - ARIA: `role="status"` on the container, `aria-label` on the dismiss button. - Component is under 105 lines, no dead code. ### Data / migrations - **No DB schema change** — reuses the existing `user_preferences` key/value table. Migration v8 already exists (`INSERT OR IGNORE … categories_schema_version`) and is the authoritative source the banner reads from. - New flag key `categories_v1_banner_dismissed` matches the spec exactly. ### i18n - 4 new keys symmetric in FR + EN under `dashboard.categoriesBanner.*` (title / description / cta / dismiss). - No hardcoded strings in the component. ### Changelog - Entries added under `[Unreleased] / ### Added` in both `CHANGELOG.md` and `CHANGELOG.fr.md`, referencing `(#118)`. ### Build / tests - `npx tsc --noEmit` — clean. - `npx vitest run` — 168/168 pass. - `npm run build` — succeeds. **Verdict: APPROVE.**
maximus merged commit 084b621506 into main 2026-04-21 01:16:21 +00:00
maximus deleted branch issue-118-dashboard-discovery-banner 2026-04-21 01:16:21 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#130
No description provided.