Skip to content

fix(onboarding): source topic list from DB to stop selections being silently dropped#599

Merged
nicholasjjlim merged 2 commits into
mainfrom
fix/onboarding-topics-from-db-598
Jun 25, 2026
Merged

fix(onboarding): source topic list from DB to stop selections being silently dropped#599
nicholasjjlim merged 2 commits into
mainfrom
fix/onboarding-topics-from-db-598

Conversation

@nicholasjjlim

Copy link
Copy Markdown
Collaborator

🚀 Summary

Closes #598.

The onboarding topic picker rendered a hardcoded list and submitted display titles, which the POST handler matched case-sensitively against Collection.title. The UI list and DB drifted (Artificial Intelligence vs AI, Educator Voices vs Educators Voices, etc.), so selections silently failed to match — some learners couldn't complete onboarding (400, no profile created), others had picks dropped with no error (200, partial save).

This sources the selectable topics from the database and submits collection ids, so the picker and the validator can never drift again.

✏️ Changes

  • (protected)/+layout.server.ts — query topic collections (isTopic = true, selecting id, title, description, tag.code) and expose them as onboardingTopics. Keyed distinctly from the home page's own topics (different shape) — page data overrides layout data on key collision, which would otherwise leave the picker empty.
  • (protected)/+layout.svelte — pass onboardingTopics down to OnboardingView.
  • OnboardingView.svelte — remove the hardcoded collectionsList; accept a typed topics prop; render from it; submit collection ids. Icons are keyed off the stable tag code (e.g. AI, BOB) — the same identifier Collection.svelte uses — not titles/slugs, so they can't drift. Topics without a known icon are filtered out and not shown.
  • api/onboarding/+server.ts — request body changes from topics (titles) to collectionIds (UUIDs). Ids are resolved strictly: if the resolved isTopic = true count ≠ submitted (deduped) count, log a warning and return 422 instead of saving a partial profile. Duplicate ids are deduped so they can't create duplicate UserInterest rows. csrfToken handling and LearningFrequency mapping unchanged.
  • api/onboarding/server.test.ts — new tests covering the full success/error contract (401, 415, 400, 422 for invalid body / fewer than 3 / unresolved id, dedup, 500 on DB failure, 200 with one interest per id).

No Prisma schema change or migration.

✅ Verification

  • pnpm test run — 350/350 pass (11 new onboarding handler tests).
  • pnpm check — 0 errors, 0 warnings.
  • Manually verified the picker renders the seeded topics with icons on the home route.

🤖 Generated with Claude Code

…ilently dropped

The onboarding picker rendered a hardcoded topic list and submitted display
titles, which the POST handler matched case-sensitively against
`Collection.title`. UI and DB strings drifted, so selections silently failed
to match: some learners couldn't complete onboarding, others had picks
dropped with no error.

Source the selectable topics from the database (`Collection` where
`isTopic = true`) and submit collection ids instead of titles:

- `+layout.server.ts`: query topic collections (id, title, description, tag
  code) and expose them as `onboardingTopics` — keyed distinctly from the home
  page's own `topics` so page data can't shadow it.
- `OnboardingView.svelte`: drop the hardcoded `collectionsList`; accept a typed
  `topics` prop; render from it; submit collection ids. Icons are keyed off the
  stable tag code (not titles/slugs); topics without a known icon are hidden.
- `api/onboarding/+server.ts`: accept `collectionIds`, resolve strictly, and
  reject (422) when any id does not resolve to an `isTopic = true` collection
  instead of saving a partial profile. Dedupe ids so duplicates can't create
  duplicate interests.
- Add handler tests covering the full success/error contract.

Closes #598

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/lib/components/OnboardingView/OnboardingView.svelte
Comment thread src/lib/components/OnboardingView/OnboardingView.svelte
Comment thread src/routes/(main)/(protected)/+layout.server.ts
Comment thread src/routes/(main)/api/onboarding/+server.ts
@nicholasjjlim

Copy link
Copy Markdown
Collaborator Author

Code Review Summary

Severity Count
Critical 0
High 1
Medium 1
Low 2

What Looks Good

  • The core fix is correct and complete: topics now flow from the DB and selections are submitted as collection IDs, eliminating the case-sensitive title-matching that silently dropped selections (fix(onboarding): source topic list from DB to stop selections being silently dropped #598).
  • Strong server-side validation: every(id => typeof id === 'string'), dedup via Set, and strict resolution (collections.length !== uniqueCollectionIds.length → 422) that fails loudly instead of partially writing. The unresolved-ids warning log with userId is good observability.
  • The onboardingTopics key choice is a deliberate, well-commented fix for the layout/page-data shadowing collision with the home page's own topics key — confirmed real (home +page.server.ts exposes a differently-shaped topics).
  • Prisma conventions followed cleanly: selectwhere clause-key order, satisfies CollectionFindManyArgs, CollectionGetPayload<typeof args> row typing, no as const.
  • Thorough new test file covering 401/415/400/422/500 paths, dedup, and the all-resolve happy path.

🤖 aif-code-review · claude-opus-4-8[1m]

Add `orderBy: { title: 'asc' }` to the onboarding topic query so the picker
renders topics in a stable order across requests. Addresses code review
finding #3 on #599.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nicholasjjlim nicholasjjlim self-assigned this Jun 25, 2026
@nicholasjjlim
nicholasjjlim requested a review from santosral June 25, 2026 05:07
@nicholasjjlim

Copy link
Copy Markdown
Collaborator Author

LGTM 👍


🤖 aif-code-review · claude-opus-4-8[1m]

@nicholasjjlim
nicholasjjlim merged commit 0eb6d4e into main Jun 25, 2026
8 checks passed
@nicholasjjlim
nicholasjjlim deleted the fix/onboarding-topics-from-db-598 branch June 25, 2026 06:48
@nicholasjjlim nicholasjjlim mentioned this pull request Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skill:aif-code-review Reviewed with the aif-code-review skill

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(onboarding): source topic list from DB to stop selections being silently dropped

2 participants