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.
## 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.
Implications and actions:
- Replace the temporary `X-Household-Slug` header injection with typed path parameters.
- Migrate all API calls to pass `{ params: { path: { householdSlug } } }` where required.
- Remove `setHouseholdSlugProvider` usage once the migration is complete.
- Auth refresh now returns only `{ accessToken, tokenType }` (no `user` object). The `currentUser()` flow must:
1) Call refresh to obtain a token and set the Authorization provider.
2) Load user/household context via typed endpoints (e.g., `GET /api/v1/users/me/households`).
3) Optional: Use `GET /api/v1/households/{householdSlug}/whoami` to validate membership for the active route.
- Invitations:
- Create invitation is now typed at `POST /api/v1/households/{householdSlug}/invitations`.
- Accept invitation is typed at `POST /api/v1/invitations/accept`.
- Remove temporary raw fetch usage for invitations and switch to the generated client.
- Household members listing endpoint is still not present in OpenAPI; continue using the temporary fetch wrapper until the backend exposes it.
- Implemented `useHousehold.ts`, header + auth providers in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Added a header injection test.
- Implemented JWT login/register in `auth.ts` and wired token to client provider. `useAuth` updated with households fetching.
- Router guard updated to handle public/multitenant routing and redirects.
- 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.
- Fixed router tests by using memory history in non-browser environments to avoid relying on `window.location` during unit tests.
- 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()`; guarded for backends that don’t yet support the endpoint.
- 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.
- Cleanup/migration tasks:
1) Remove X-Household-Slug header injection in `api/client.ts` and refactor services to accept `householdSlug` via typed params.
2) Replace temporary raw fetch for invitations (both accept and create) and members listing with generated typed endpoints. Invitations are now typed; members listing remains pending.
3) Legacy identity: continue using `User` as the primary identity. Keep `Person` in meal-related UIs where the backend requires it, but remove Person as the login/identity concept.