All core features are migrated to multi-tenancy with path-scoped endpoints and token-based auth. The codebase no longer uses the `X-Household-Slug` header. Tests and type checks are fully green.
Status of tests and typing
- All tests pass: 27 files, 47 tests (router tests fixed to use memory history in Vitest environment).
Transition the frontend from a single-tenant application to a multi-tenant one centered around the concept of "Households". This involves refactoring the existing authentication system, user onboarding, and data presentation to ensure all information (recipes, meals, shopping lists) is scoped to the active household.
This plan is adapted to the existing codebase, focusing on refactoring rather than starting from scratch.
## 2. Core Concepts
- **Household**: A private group of users. All data is isolated and only visible to members of that household.
- **URL-based Tenancy**: The active household is determined by the URL, e.g., `/the-smiths/recipes`. This makes household context explicit and shareable.
- **Household Switching**: Users belonging to multiple households can switch between them.
- **Authentication**: Evolve the existing system from a simple username login to a robust one supporting email/password and Google OAuth, using JWTs.
## 3. User Flows & UI Requirements
### 3.1. Authentication & Onboarding
- **Login Page (`/login`)**:
- **Refactor `src/components/LoginPage.vue`**.
- Replace the current username input with:
- Email & Password login fields.
- A "Sign in with Google" button.
- Add a link to the "Create Account" page (`/create-account`).
- **Create Account Page (`/create-account`)**:
- **Create a new view `src/views/CreateAccount.vue`**.
- After a new user logs in for the first time, they are directed here.
- The page should present two choices:
1.**Create a new household**: A simple form with "Household Name".
2.**Join an existing household**: This section should clearly state: "To join a household, ask an existing member to send an invitation to your email address."
- **Invitation Flow**:
- A user receives an email with a link like `https://<app-domain>/invitations/accept?token=...`.
- Visiting this link while logged in should add them to the household and redirect them to that household's dashboard.
### 3.2. In-App Experience
- **Household-Scoped URLs**:
- All application routes must be nested under a household slug: `/:householdSlug/recipes`, `/:householdSlug/shopping`, etc.
- The router must be updated to handle this dynamic parameter. The `householdSlug` will be used in all API calls to fetch household-specific data.
- **Household Switcher**:
- **Create a new component `src/components/HouseholdSwitcher.vue`**.
- Place it in a prominent location (e.g., inside `App.vue`'s navigation bar).
- It should list all households the current user is a member of.
- Clicking a household name should navigate the user to the dashboard of that household, e.g., `/<new-household-slug>/dashboard`.
- **Invite Members UI**:
- **Create a new view `src/views/HouseholdSettings.vue`**.
- It should be accessible at `/:householdSlug/settings/members`.
- It should contain a simple form to enter an email address and a button to "Send Invitation".
- Status: Welcome page implements create-household flow using `POST /api/v1/households` and redirects to `/:slug/mealplan`. Create Account UI implemented. Invitation Accept implemented: reads `token` from query, calls `POST /api/v1/invitations/accept`, and redirects to the accepted household. Uses a temporary raw fetch helper until OpenAPI adds this endpoint.
- **Update API Services**: Implemented header-injection in `src/api/client.ts` via `X-Household-Slug` and `Authorization` using configurable providers; no path changes.
- Verified by `tests/household.header.test.ts` and auth tests.
All core features are migrated to multi-tenancy with path-scoped endpoints and token-based auth. The codebase no longer uses the `X-Household-Slug` header. Tests and type checks are fully green.
-`src/router/index.ts`: feature-flagged nesting under `/:householdSlug/...`; public routes include `/create-account`, `/welcome`, and `/invitations/accept`.
- Guard fetches households, redirects root `/` to the first household's `mealplan`, and uses memory history in tests (hash in browser).
-`src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Persons and parse use temporary raw fetch endpoints where OpenAPI lacks coverage.
## Amendment: API Services and Typing Strategy (Updated Nov 1, 2025)
The backend OpenAPI has been updated and now exposes `householdSlug` as a path parameter for scoped endpoints (recipes, meals, shopping, invitations, whoami). Auth endpoints are fully typed (login/register/refresh/logout). Invitation acceptance remains a global endpoint (`/api/v1/invitations/accept`) with a typed operation.
- Implemented `useHousehold.ts`, Authorization provider in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Replaced header injection test with path-scoped assertion.
- Implemented JWT login/register in `auth.ts` and wired token to client provider. `useAuth` updated with households fetching. `currentUser` refactored to token-only refresh plus households load.
- Implemented Household Settings invite form and members list UI. `src/views/HouseholdSettings.vue` now loads members via a temporary `listMembers()` in `src/api/households.ts` using the raw fetch helper. When the backend exposes a typed endpoint, we will swap to the generated client.
- Completed migration to typed path parameters; header injection removed; only small raw fetch helpers remain for endpoints not yet in OpenAPI (persons, parse, members list).
- Login Page: Refactored to show email/password form when multitenant flag is enabled; legacy person list retained otherwise. Link to Create Account added.
-`src/views/HouseholdSettings.vue`: Invite members form wired to `sendInvitation(email)`. Members list rendered from `listMembers()` using the typed endpoint. (Completed)
- Household scoping: use typed path params (`{ params: { path: { householdSlug } } }`); do not mutate path strings.
- Auth refresh: returns `{ accessToken, tokenType }`. After refresh, call user/household endpoints to populate app state. Use `whoami` to validate the active route’s slug when needed.
2) Replace temporary raw fetch calls with generated typed endpoints where available: invitations and members listing are now typed; migrate usages. (Members listing migrated) Persons and parse remain raw for now.
3) Integrate the new `POST /shopping/current/ingredients` endpoint into the SDK (`requestIngredient(ingredientId: number)`) and expose via `useShopping`; refactor `MyShoppingPage.vue` accordingly and remove legacy stubs. (SDK + composable done; UI refactor next)
4) Legacy identity: continue using `User` as the primary identity. Keep `Person` in meal-related UIs where required by backend, but remove Person as the login/identity concept.
-`VUE_APP_MULTITENANT_ENABLED` flag controls nested household routes. With the migration complete, keep this flag for rollout control; default can be enabled once backend is stable across environments.
- The v2 OpenAPI spec is being updated to include endpoints for creating, listing, updating, and deleting ad-hoc requested items on the current shopping list (independent of meals), scoped under `/api/v1/households/{householdSlug}/shopping/current/items`.
-`listRequestedItems()` (if provided separately; otherwise rely on `getCurrentShoppingList()`)
- Refactor `src/components/shopping/MyShoppingPage.vue` to use the above methods and remove legacy stubs `getMyShoppingList/saveMyShoppingList` from the SDK and `useShopping` composable.
- Update tests to drive TDD:
- MSW handlers for the new endpoints with path-scoped URLs and Authorization.
- Component tests to add, edit, and delete an ad-hoc requested item and verify it appears under outstanding items in `getCurrentShoppingList()`.
- Acceptance criteria:
- Users can add an item without associating it to a meal.
- Users can edit and delete such items.
- All calls use typed path-scoped endpoints; no header injection; Authorization still via provider.
- Note: No temporary measures required; proceed directly once backend ships endpoints.
- Remove the legacy username login shim (`login(username: string)`) and any fallback UI; standardize on email/password (and Google) only.
- Rollout flag: default `VUE_APP_MULTITENANT_ENABLED` to true across environments and plan removal of legacy flat routes and related tests once stable.