Frontend: A2 — Household setup + invite #19

Closed
opened 2026-04-02 11:26:33 +02:00 by marcel · 8 comments
Owner

Summary

Planner names the household and optionally generates an invite link for household members. Step 1 of 3 in onboarding.

Journey: J6 — Household setup
Role: Planner only

Layout

Mobile (< 768px)

  • Step indicator text "Step 1 of 3" + form below, full-width
  • Padding: 24px 20px

Desktop (> 1024px)

  • Left: progress sidebar 300px, --color-surface bg, 3 numbered steps
  • Right: form area flex:1, --color-page bg, centered, max-width 420px

Progress Steps

  • Step 1: "Name your household" (current = green circle)
  • Step 2: "Pantry staples" (future = subtle circle)
  • Step 3: "You're ready" (future = subtle circle)

Form Fields

  • Household name input (e.g., "Smith family")
  • "Generate invite link" button → generates and displays a shareable link/code
  • Continue button → saves household + creates household_member (role=planner) → A3

Behavior

  • Invite link uses the existing invite code system from the backend
  • Planner shares the link via any messaging app (no in-app email invite)
  • Continue → navigates to A3

Acceptance Criteria

  • Mobile: step text + form
  • Desktop: 300px progress sidebar + centered form
  • Creates household with provided name
  • Generates invite link/code on demand
  • Navigates to A3 on continue
## Summary Planner names the household and optionally generates an invite link for household members. Step 1 of 3 in onboarding. **Journey:** J6 — Household setup **Role:** Planner only ## Layout ### Mobile (< 768px) - Step indicator text "Step 1 of 3" + form below, full-width - Padding: 24px 20px ### Desktop (> 1024px) - Left: progress sidebar 300px, `--color-surface` bg, 3 numbered steps - Right: form area flex:1, `--color-page` bg, centered, max-width 420px ## Progress Steps - Step 1: "Name your household" (current = green circle) - Step 2: "Pantry staples" (future = subtle circle) - Step 3: "You're ready" (future = subtle circle) ## Form Fields - Household name input (e.g., "Smith family") - "Generate invite link" button → generates and displays a shareable link/code - Continue button → saves household + creates `household_member` (role=planner) → A3 ## Behavior - Invite link uses the existing invite code system from the backend - Planner shares the link via any messaging app (no in-app email invite) - Continue → navigates to A3 ## Acceptance Criteria - [ ] Mobile: step text + form - [ ] Desktop: 300px progress sidebar + centered form - [ ] Creates household with provided name - [ ] Generates invite link/code on demand - [ ] Navigates to A3 on continue
marcel added the kind/featurepriority/high labels 2026-04-02 11:29:53 +02:00
Author
Owner

Spec file: specs/frontend/j6-household-setup.html — screen A2 with mobile + desktop previews, agent table, and LLM implementation guide.

**Spec file:** [`specs/frontend/j6-household-setup.html`](../specs/frontend/j6-household-setup.html) — screen A2 with mobile + desktop previews, agent table, and LLM implementation guide.
Author
Owner

🧑‍💻 Kai — Frontend Engineer

Questions & Observations

  • Layout group: A1 is (auth) (no nav, no session). A2 is post-signup — the user is authenticated. But A2 also has no standard nav chrome; it shows a progress sidebar instead. Should A2 live in a new (onboarding) route group with its own layout, or does it re-use the (auth) bare layout? If we add more onboarding screens later (A3, etc.), a dedicated (onboarding)/+layout.svelte with the progress sidebar baked in is cleaner than repeating the sidebar in every page.

  • A3 route path: The Continue button redirects to A3 — same problem we had with A2 in issue #18. What's the exact path? /household/staples? Needs to be confirmed before the form action can be written.

  • Invite link generation flow: "Generate invite link" is a button that triggers an API call and then displays the link. Is this a second named form action (actions.generateInvite) in +page.server.ts, or a +server.ts endpoint hit via fetch? A form action with use:enhance is simpler and avoids client-side state management for the generated link. But it requires the household to already exist (or be created atomically). Which comes first — household creation or invite generation?

  • Household created on Continue or earlier?: The spec says "Continue → saves household + creates household_member". Does "Generate invite link" work without having saved the household yet? If the invite code must be attached to a household ID on the backend, the household needs to exist before the invite is generated. This ordering matters for the UX and the form action structure.

  • Component split: ProgressSidebar.svelte + HouseholdSetupForm.svelte feels right. ProgressSidebar is likely reused across A2, A3 — confirm whether it should live in a shared components folder or just be co-located with the onboarding layout.

Suggestions

  • If household creation and invite generation are two distinct backend operations, consider a two-phase approach: create household on Continue (required), and offer invite generation as an optional step before or after. Trying to do both in one action risks confusing error states.
  • The invite link display after generation needs a copy-to-clipboard button — don't rely on the user manually selecting the URL text. This is a UX must for a share flow.
  • The Continue button should be disabled until the household name field is non-empty. A $derived() on the name $state() handles this cleanly.
## 🧑‍💻 Kai — Frontend Engineer ### Questions & Observations - **Layout group**: A1 is `(auth)` (no nav, no session). A2 is post-signup — the user is authenticated. But A2 also has no standard nav chrome; it shows a progress sidebar instead. Should A2 live in a new `(onboarding)` route group with its own layout, or does it re-use the `(auth)` bare layout? If we add more onboarding screens later (A3, etc.), a dedicated `(onboarding)/+layout.svelte` with the progress sidebar baked in is cleaner than repeating the sidebar in every page. - **A3 route path**: The Continue button redirects to A3 — same problem we had with A2 in issue #18. What's the exact path? `/household/staples`? Needs to be confirmed before the form action can be written. - **Invite link generation flow**: "Generate invite link" is a button that triggers an API call and then displays the link. Is this a second named form action (`actions.generateInvite`) in `+page.server.ts`, or a `+server.ts` endpoint hit via `fetch`? A form action with `use:enhance` is simpler and avoids client-side state management for the generated link. But it requires the household to already exist (or be created atomically). Which comes first — household creation or invite generation? - **Household created on Continue or earlier?**: The spec says "Continue → saves household + creates `household_member`". Does "Generate invite link" work without having saved the household yet? If the invite code must be attached to a household ID on the backend, the household needs to exist before the invite is generated. This ordering matters for the UX and the form action structure. - **Component split**: `ProgressSidebar.svelte` + `HouseholdSetupForm.svelte` feels right. ProgressSidebar is likely reused across A2, A3 — confirm whether it should live in a shared components folder or just be co-located with the onboarding layout. ### Suggestions - If household creation and invite generation are two distinct backend operations, consider a two-phase approach: create household on Continue (required), and offer invite generation as an optional step before or after. Trying to do both in one action risks confusing error states. - The invite link display after generation needs a copy-to-clipboard button — don't rely on the user manually selecting the URL text. This is a UX must for a share flow. - The Continue button should be disabled until the household name field is non-empty. A `$derived()` on the name `$state()` handles this cleanly.
Author
Owner

🗄️ Backend Engineer — Spring Boot / PostgreSQL

Questions & Observations

  • Atomicity of Continue action: The spec says Continue "saves household + creates household_member (role=planner)". Are these two separate inserts wrapped in a single transaction, or two separate API calls? They should be a single atomic operation — if household creation succeeds but household_member insert fails, you'd have an orphaned household with no planner. One endpoint, one transaction.

  • "Existing invite code system": The spec references "the existing invite code system from the backend". Is this already implemented? If so, what does the endpoint look like — POST /api/households/{id}/invite-codes? If it doesn't exist yet, this issue implicitly requires backend work. That dependency needs to be surfaced explicitly.

  • Ordering problem: Invite codes must be associated with a household ID. But if the household is only created on Continue, how does "Generate invite link" work before the user clicks Continue? Either: (a) the household is created as a draft when the user lands on A2, (b) invite generation is only possible after Continue, or (c) the invite is generated client-side (weak — avoid). This is a significant design question.

  • Household name validation: What are the constraints? Min length (1? 2?)? Max length? Allowed characters? The spec shows "Smith family" as an example but gives no bounds. The DB column needs a CHECK constraint and the API needs @Size validation.

  • Planner role assignment: The household_member is created with role=planner. Is planner a value in a DB enum? And is it possible for the authenticated user to already have a household_member record (e.g., if they somehow reached A2 twice)? The endpoint should handle the duplicate case gracefully — 409 or idempotent.

Suggestions

  • One endpoint: POST /api/households — creates household + household_member in a single transaction and returns the new household ID. Invite generation is a separate subsequent call: POST /api/households/{id}/invite-codes.
  • Add a UNIQUE constraint on household_member(user_account_id) if a user can only belong to one household. This enforces the business rule at the database level.
  • The household creation endpoint must verify the caller is authenticated (session check) — this is not a public endpoint like signup.
## 🗄️ Backend Engineer — Spring Boot / PostgreSQL ### Questions & Observations - **Atomicity of Continue action**: The spec says Continue "saves household + creates `household_member` (role=planner)". Are these two separate inserts wrapped in a single transaction, or two separate API calls? They should be a single atomic operation — if household creation succeeds but `household_member` insert fails, you'd have an orphaned household with no planner. One endpoint, one transaction. - **"Existing invite code system"**: The spec references "the existing invite code system from the backend". Is this already implemented? If so, what does the endpoint look like — `POST /api/households/{id}/invite-codes`? If it doesn't exist yet, this issue implicitly requires backend work. That dependency needs to be surfaced explicitly. - **Ordering problem**: Invite codes must be associated with a household ID. But if the household is only created on Continue, how does "Generate invite link" work before the user clicks Continue? Either: (a) the household is created as a draft when the user lands on A2, (b) invite generation is only possible after Continue, or (c) the invite is generated client-side (weak — avoid). This is a significant design question. - **Household name validation**: What are the constraints? Min length (1? 2?)? Max length? Allowed characters? The spec shows "Smith family" as an example but gives no bounds. The DB column needs a CHECK constraint and the API needs `@Size` validation. - **Planner role assignment**: The `household_member` is created with `role=planner`. Is `planner` a value in a DB enum? And is it possible for the authenticated user to already have a household_member record (e.g., if they somehow reached A2 twice)? The endpoint should handle the duplicate case gracefully — 409 or idempotent. ### Suggestions - One endpoint: `POST /api/households` — creates household + household_member in a single transaction and returns the new household ID. Invite generation is a separate subsequent call: `POST /api/households/{id}/invite-codes`. - Add a UNIQUE constraint on `household_member(user_account_id)` if a user can only belong to one household. This enforces the business rule at the database level. - The household creation endpoint must verify the caller is authenticated (session check) — this is not a public endpoint like signup.
Author
Owner

🧪 QA Engineer — Test Coverage

Questions & Observations

The acceptance criteria only cover the happy path and layout. Here's what's missing:

Missing AC — bad paths & edge cases:

  • Continue with empty household name → validation error, no request fired
  • Household name at minimum length (1 char?) → accepted
  • Household name at maximum length → accepted; beyond max → error
  • Generate invite link → link/code is displayed and copyable
  • Generate invite link twice → second call replaces or appends (which is the intended behavior?)
  • Continue without generating invite link → succeeds (invite is optional, per spec)
  • Continue fails (server error) → user sees error, household is not partially created
  • Back navigation from A2 to A1 → what happens? (session already exists, user already created)

Missing AC — structural:

  • Progress sidebar: step 1 visually active, steps 2 and 3 visually inactive
  • Progress steps 2 and 3 are not clickable/navigable (they're not yet reachable)
  • A2 is only accessible to authenticated planners — unauthenticated users are redirected

Suggestions

  • Component test for HouseholdSetupForm: Cover initial state (Continue disabled), name input enables Continue, generate invite link renders link, form submission calls the action.
  • Component test for ProgressSidebar: Correct step highlighted given currentStep prop. Steps rendered with correct labels.
  • E2E — full onboarding flow: Signup (A1) → household setup (A2) → A3 as a single journey test. This is a critical path that should have one focused E2E test.
  • E2E — invite link: Verify the generated link is displayed and the copy button is present. Don't test clipboard API directly — just assert the button exists and the link text is non-empty.
## 🧪 QA Engineer — Test Coverage ### Questions & Observations The acceptance criteria only cover the happy path and layout. Here's what's missing: **Missing AC — bad paths & edge cases:** - [ ] Continue with empty household name → validation error, no request fired - [ ] Household name at minimum length (1 char?) → accepted - [ ] Household name at maximum length → accepted; beyond max → error - [ ] Generate invite link → link/code is displayed and copyable - [ ] Generate invite link twice → second call replaces or appends (which is the intended behavior?) - [ ] Continue without generating invite link → succeeds (invite is optional, per spec) - [ ] Continue fails (server error) → user sees error, household is not partially created - [ ] Back navigation from A2 to A1 → what happens? (session already exists, user already created) **Missing AC — structural:** - [ ] Progress sidebar: step 1 visually active, steps 2 and 3 visually inactive - [ ] Progress steps 2 and 3 are not clickable/navigable (they're not yet reachable) - [ ] A2 is only accessible to authenticated planners — unauthenticated users are redirected ### Suggestions - **Component test for `HouseholdSetupForm`**: Cover initial state (Continue disabled), name input enables Continue, generate invite link renders link, form submission calls the action. - **Component test for `ProgressSidebar`**: Correct step highlighted given `currentStep` prop. Steps rendered with correct labels. - **E2E — full onboarding flow**: Signup (A1) → household setup (A2) → A3 as a single journey test. This is a critical path that should have one focused E2E test. - **E2E — invite link**: Verify the generated link is displayed and the copy button is present. Don't test clipboard API directly — just assert the button exists and the link text is non-empty.
Author
Owner

🔒 Sable — Security Engineer

Questions & Observations

  • Authentication gate on A2: A2 is post-signup, so the user is authenticated. But is this enforced? The hooks.server.ts should redirect unauthenticated requests to /login before they ever reach /household/setup. This must be confirmed — not assumed. A newly signed-up user who loses their session mid-onboarding and refreshes should land on login, not on a broken A2 page.

  • Authorization — planner only: The issue says "Role: Planner only". At this point in the flow, the user has just signed up and has no household yet — so they have no household_member record and no role. How is "planner only" enforced? Is A2 accessible to any authenticated user who has no household, or is there a specific flag? The authorization logic needs to be explicit, not implicit.

  • Invite code security: The invite code system is referenced but not specified here. Per the project threat model: codes must be UUIDv4 minimum (not guessable), single-use, and time-limited (expiry). If the existing backend system doesn't enforce all three, it's a gap. Specifically: what's the expiry window? 24 hours? 7 days? And is the code invalidated immediately upon use (or only after the invited user completes setup)?

  • Invite link exposure: The generated link is displayed in the UI and shared via messaging apps. This means the link will appear in message history, possibly on third-party servers. The time-limited + single-use constraints are the primary defense. Confirm these are non-negotiable.

  • CSRF on Continue action: Using SvelteKit form actions with use:enhance covers this via the built-in Origin check. If the invite generation uses a fetch POST instead, it needs an explicit CSRF token. Flag which pattern is chosen.

Suggestions

  • The household creation endpoint should verify: (1) user is authenticated, (2) user does not already have a household_member record. The second check prevents a user from creating multiple households by hitting the endpoint repeatedly or replaying the request.
  • Log the household creation event (user ID, household ID, timestamp) — this is an onboarding action worth having in the audit trail.
  • Do not display the full invite URL in a form input that gets submitted back to the server. Display it as read-only text or a <pre> block to prevent accidental re-submission.
## 🔒 Sable — Security Engineer ### Questions & Observations - **Authentication gate on A2**: A2 is post-signup, so the user is authenticated. But is this enforced? The `hooks.server.ts` should redirect unauthenticated requests to `/login` before they ever reach `/household/setup`. This must be confirmed — not assumed. A newly signed-up user who loses their session mid-onboarding and refreshes should land on login, not on a broken A2 page. - **Authorization — planner only**: The issue says "Role: Planner only". At this point in the flow, the user has just signed up and has no household yet — so they have no `household_member` record and no role. How is "planner only" enforced? Is A2 accessible to any authenticated user who has no household, or is there a specific flag? The authorization logic needs to be explicit, not implicit. - **Invite code security**: The invite code system is referenced but not specified here. Per the project threat model: codes must be UUIDv4 minimum (not guessable), single-use, and time-limited (expiry). If the existing backend system doesn't enforce all three, it's a gap. Specifically: what's the expiry window? 24 hours? 7 days? And is the code invalidated immediately upon use (or only after the invited user completes setup)? - **Invite link exposure**: The generated link is displayed in the UI and shared via messaging apps. This means the link will appear in message history, possibly on third-party servers. The time-limited + single-use constraints are the primary defense. Confirm these are non-negotiable. - **CSRF on Continue action**: Using SvelteKit form actions with `use:enhance` covers this via the built-in Origin check. If the invite generation uses a `fetch` POST instead, it needs an explicit CSRF token. Flag which pattern is chosen. ### Suggestions - The household creation endpoint should verify: (1) user is authenticated, (2) user does not already have a `household_member` record. The second check prevents a user from creating multiple households by hitting the endpoint repeatedly or replaying the request. - Log the household creation event (user ID, household ID, timestamp) — this is an onboarding action worth having in the audit trail. - Do not display the full invite URL in a form input that gets submitted back to the server. Display it as read-only text or a `<pre>` block to prevent accidental re-submission.
Author
Owner

🎨 Atlas — UI/UX Designer

Questions & Observations

  • Progress sidebar — completed step treatment: The spec defines current step (green circle) and future steps (subtle circle), but doesn't define the visual for a completed step. When the user advances to A3, step 1 should look "done" — typically a checkmark icon or filled green circle with reduced prominence. Should completed steps use the same green circle as the current step, or a distinct "done" state? This matters for A3 and especially for the "You're ready" confirmation step.

  • Invite link display: After clicking "Generate invite link", where does the link appear? Inline below the button? In a separate card/box? The spec doesn't define this. Suggested treatment: a --color-surface bg container with --radius-md, the URL in font-mono (DM Mono), and a copy icon button. Does this match your intent?

  • "Generate invite link" button style: Is this a secondary/outline button, or the same primary green as Continue? Since Continue is the primary action and invite generation is optional, the generate button should be visually subordinate — likely a ghost/outline variant.

  • Household name input placeholder: The spec shows "Smith family" as an example — is that the placeholder text, or just an illustration in the spec? Placeholder copy should be finalized (German or English?).

  • Continue button — disabled state: Should the Continue button be visually disabled (grayed out) when the name field is empty, or just show a validation error on attempt? Disabled-until-valid is a UX pattern that prevents confusion, but it can also frustrate users who don't know why the button isn't working. A subtle disabled state with an accessible aria-disabled and a tooltip on hover would be ideal.

  • Mobile: "Step 1 of 3" text: What's the typography? font-sans, 14px, --color-text-subtle? This should match the spec token system.

Suggestions

  • The progress sidebar is likely reused in A3. Design it as a standalone component (ProgressSidebar.svelte) that accepts a currentStep: number prop — not hardcoded per screen.
  • The copy-to-clipboard interaction for the invite link: use a button with a clipboard icon that briefly changes to a checkmark on success ($state toggle, reset after 2 seconds). No toast needed — inline feedback is sufficient.
  • Ensure the progress sidebar steps have accessible labels — the numbered circles alone are not sufficient for screen readers. Each step should have visible text ("Step 1: Name your household") or an aria-label on the indicator.
## 🎨 Atlas — UI/UX Designer ### Questions & Observations - **Progress sidebar — completed step treatment**: The spec defines current step (green circle) and future steps (subtle circle), but doesn't define the visual for a *completed* step. When the user advances to A3, step 1 should look "done" — typically a checkmark icon or filled green circle with reduced prominence. Should completed steps use the same green circle as the current step, or a distinct "done" state? This matters for A3 and especially for the "You're ready" confirmation step. - **Invite link display**: After clicking "Generate invite link", where does the link appear? Inline below the button? In a separate card/box? The spec doesn't define this. Suggested treatment: a `--color-surface` bg container with `--radius-md`, the URL in `font-mono` (DM Mono), and a copy icon button. Does this match your intent? - **"Generate invite link" button style**: Is this a secondary/outline button, or the same primary green as Continue? Since Continue is the primary action and invite generation is optional, the generate button should be visually subordinate — likely a ghost/outline variant. - **Household name input placeholder**: The spec shows "Smith family" as an example — is that the placeholder text, or just an illustration in the spec? Placeholder copy should be finalized (German or English?). - **Continue button — disabled state**: Should the Continue button be visually disabled (grayed out) when the name field is empty, or just show a validation error on attempt? Disabled-until-valid is a UX pattern that prevents confusion, but it can also frustrate users who don't know why the button isn't working. A subtle disabled state with an accessible `aria-disabled` and a tooltip on hover would be ideal. - **Mobile: "Step 1 of 3" text**: What's the typography? `font-sans`, 14px, `--color-text-subtle`? This should match the spec token system. ### Suggestions - The progress sidebar is likely reused in A3. Design it as a standalone component (`ProgressSidebar.svelte`) that accepts a `currentStep: number` prop — not hardcoded per screen. - The copy-to-clipboard interaction for the invite link: use a button with a clipboard icon that briefly changes to a checkmark on success (`$state` toggle, reset after 2 seconds). No toast needed — inline feedback is sufficient. - Ensure the progress sidebar steps have accessible labels — the numbered circles alone are not sufficient for screen readers. Each step should have visible text ("Step 1: Name your household") or an `aria-label` on the indicator.
Author
Owner

🧑‍💻 Kai — Frontend Engineer — Implementation Discussion

Worked through all open frontend items. Everything resolved.

Resolved

  • Route group — No route group. A2 lives at src/routes/household/setup/+page.svelte — no (onboarding) or (auth) wrapper.

  • A3 route path/household/staples. Continue action does throw redirect(303, '/household/staples').

  • Household creation / invite ordering — Household is created when the user clicks Continue (the button that redirects to the invite page). Invite generation is not A2's concern — it lives on the subsequent page. No chicken-and-egg problem on A2.

  • Invite generation pattern — Standard actions.default in +page.server.ts. Creates household + household_member in one action, then redirects. Invite logic is handled separately on the next page.

  • ProgressSidebar location — Shared component at $lib/components/ProgressSidebar.svelte. Reused across A2, A3, and any future onboarding steps.

Unresolved

None.


Ready to implement. Form action creates household + household_member atomically, redirects to /household/staples. ProgressSidebar is a shared component accepting a currentStep prop.

## 🧑‍💻 Kai — Frontend Engineer — Implementation Discussion Worked through all open frontend items. Everything resolved. ### Resolved - **Route group** — No route group. A2 lives at `src/routes/household/setup/+page.svelte` — no `(onboarding)` or `(auth)` wrapper. - **A3 route path** — `/household/staples`. Continue action does `throw redirect(303, '/household/staples')`. - **Household creation / invite ordering** — Household is created when the user clicks Continue (the button that redirects to the invite page). Invite generation is not A2's concern — it lives on the subsequent page. No chicken-and-egg problem on A2. - **Invite generation pattern** — Standard `actions.default` in `+page.server.ts`. Creates household + household_member in one action, then redirects. Invite logic is handled separately on the next page. - **ProgressSidebar location** — Shared component at `$lib/components/ProgressSidebar.svelte`. Reused across A2, A3, and any future onboarding steps. ### Unresolved None. --- Ready to implement. Form action creates household + household_member atomically, redirects to `/household/staples`. ProgressSidebar is a shared component accepting a `currentStep` prop.
Author
Owner

Implementation complete — branch feat/issue-19-household-setup

What was built

All 4 plan items delivered with red/green/refactor TDD. 183 tests pass, 0 type errors.


Commits

Commit What
b9ef06f ProgressSidebar component — app logo + 3 steps, active/completed/future states, aria-labels
175bfbe HouseholdSetupForm component — name input, disabled-until-valid Continue button, server error display
e85a7ca +page.server.ts — load guard (redirects to /planner if user already has a household), POST action creates household via POST /v1/households, redirects to /household/staples
6de7f5a +page.svelte — desktop: 300px progress sidebar (step 1 active) + flex form area; mobile: "Schritt 1 von 3" eyebrow + form. Stub /household/staples page.

Files created

  • frontend/src/lib/components/ProgressSidebar.svelte + .test.ts (8 tests)
  • frontend/src/lib/onboarding/HouseholdSetupForm.svelte + .test.ts (9 tests)
  • frontend/src/routes/household/setup/+page.server.ts + page.server.test.ts (8 tests)
  • frontend/src/routes/household/setup/+page.svelte + page.test.ts (8 tests)
  • frontend/src/routes/household/staples/+page.svelte (stub)

Deferred from scope

  • Invite link generation — per Kai's resolution in the last comment, this is not A2's concern and will be addressed on a subsequent page. The AC "Generates invite link/code on demand" is deferred.

Suggested next action

Open a PR: /review-pr on this branch, or implement A3 (pantry staples) next.

## ✅ Implementation complete — branch `feat/issue-19-household-setup` ### What was built All 4 plan items delivered with red/green/refactor TDD. 183 tests pass, 0 type errors. --- ### Commits | Commit | What | |--------|------| | `b9ef06f` | `ProgressSidebar` component — app logo + 3 steps, active/completed/future states, aria-labels | | `175bfbe` | `HouseholdSetupForm` component — name input, disabled-until-valid Continue button, server error display | | `e85a7ca` | `+page.server.ts` — load guard (redirects to `/planner` if user already has a household), POST action creates household via `POST /v1/households`, redirects to `/household/staples` | | `6de7f5a` | `+page.svelte` — desktop: 300px progress sidebar (step 1 active) + flex form area; mobile: "Schritt 1 von 3" eyebrow + form. Stub `/household/staples` page. | --- ### Files created - `frontend/src/lib/components/ProgressSidebar.svelte` + `.test.ts` (8 tests) - `frontend/src/lib/onboarding/HouseholdSetupForm.svelte` + `.test.ts` (9 tests) - `frontend/src/routes/household/setup/+page.server.ts` + `page.server.test.ts` (8 tests) - `frontend/src/routes/household/setup/+page.svelte` + `page.test.ts` (8 tests) - `frontend/src/routes/household/staples/+page.svelte` (stub) --- ### Deferred from scope - **Invite link generation** — per Kai's resolution in the last comment, this is not A2's concern and will be addressed on a subsequent page. The AC "Generates invite link/code on demand" is deferred. --- ### Suggested next action Open a PR: `/review-pr` on this branch, or implement A3 (pantry staples) next.
Sign in to join this conversation.