fix(onboarding): source topic list from DB to stop selections being silently dropped#599
Merged
Merged
Conversation
…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>
nicholasjjlim
commented
Jun 25, 2026
nicholasjjlim
commented
Jun 25, 2026
nicholasjjlim
commented
Jun 25, 2026
nicholasjjlim
commented
Jun 25, 2026
Collaborator
Author
Code Review Summary
What Looks Good
🤖 aif-code-review · claude-opus-4-8[1m] |
nicholasjjlim
marked this pull request as ready for review
June 25, 2026 05:05
Collaborator
Author
|
LGTM 👍 🤖 aif-code-review · claude-opus-4-8[1m] |
santosral
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🚀 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 IntelligencevsAI,Educator VoicesvsEducators 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, selectingid,title,description,tag.code) and expose them asonboardingTopics. Keyed distinctly from the home page's owntopics(different shape) — page data overrides layout data on key collision, which would otherwise leave the picker empty.(protected)/+layout.svelte— passonboardingTopicsdown toOnboardingView.OnboardingView.svelte— remove the hardcodedcollectionsList; accept a typedtopicsprop; render from it; submit collection ids. Icons are keyed off the stable tag code (e.g.AI,BOB) — the same identifierCollection.svelteuses — 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 fromtopics(titles) tocollectionIds(UUIDs). Ids are resolved strictly: if the resolvedisTopic = truecount ≠ submitted (deduped) count, log a warning and return 422 instead of saving a partial profile. Duplicate ids are deduped so they can't create duplicateUserInterestrows.csrfTokenhandling andLearningFrequencymapping 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.🤖 Generated with Claude Code