feat(shopping): ad‑hoc items UI via typed endpoints; purge legacy JS tests; update spec
This commit is contained in:
parent
5183217669
commit
16a90fe89d
2 changed files with 27 additions and 33 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
## 0. Current State (Nov 1, 2025)
|
## 0. Current State (Nov 2, 2025)
|
||||||
|
|
||||||
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.
|
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. Ad‑hoc shopping items (add/remove) are implemented against the new scoped endpoints.
|
||||||
|
|
||||||
Status of tests and typing
|
Status of tests and typing
|
||||||
- All tests pass: 28 files, 45 tests (slug-only routes; memory history fallback in non-browser envs; unauthenticated and refresh-401 guard redirects covered).
|
- All tests pass: 18 files, 27 tests (legacy JS tests removed; slug-only routes; memory history fallback; unauthenticated and refresh-401 guard redirects covered).
|
||||||
- `tsc` and `vue-tsc` pass with no errors.
|
- `tsc` and `vue-tsc` pass with no errors.
|
||||||
|
|
||||||
# Frontend Specification: Household Multi-Tenancy (v2)
|
# Frontend Specification: Household Multi-Tenancy (v2)
|
||||||
|
|
@ -111,9 +111,9 @@ This plan is adapted to the existing codebase, focusing on refactoring rather th
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Current State (Nov 1, 2025)
|
## 0. Current State (Nov 2, 2025)
|
||||||
|
|
||||||
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.
|
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. Ad‑hoc shopping items (add/remove) are implemented.
|
||||||
|
|
||||||
What exists now
|
What exists now
|
||||||
- Auth
|
- Auth
|
||||||
|
|
@ -123,19 +123,19 @@ What exists now
|
||||||
- `src/router/index.ts`: slug-only nesting under `/:householdSlug/...`; public routes include `/create-account`, `/welcome`, and `/invitations/accept`.
|
- `src/router/index.ts`: slug-only 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).
|
- Guard fetches households, redirects root `/` to the first household's `mealplan`, and uses memory history in tests (hash in browser).
|
||||||
- SDK/API
|
- SDK/API
|
||||||
- `src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Person endpoints removed. Parse endpoints are now typed:
|
- `src/api/sdk.ts`: recipes, meals, and shopping are migrated to `/api/v1/households/{householdSlug}/...` typed endpoints. Person endpoints removed. Parse endpoints are typed:
|
||||||
- `POST /api/v1/households/{householdSlug}/recipes/parse-from-url` returns `RecipeCreate-Output`; SDK constructs a minimal `RecipeOut` for decoding (id/createdById set to -1).
|
- `POST /api/v1/households/{householdSlug}/recipes/parse-from-url` returns `RecipeCreate-Output`; SDK constructs a minimal `RecipeOut` for decoding (id/createdById set to -1).
|
||||||
- `GET /api/v1/households/{householdSlug}/ingredients/parse?lines=...` returns `Ingredient[]`.
|
- `GET /api/v1/households/{householdSlug}/ingredients/parse?lines=...` returns `Ingredient[]`.
|
||||||
|
- Shopping ad‑hoc requests: `POST /api/v1/households/{householdSlug}/shopping/current/ingredients` (requestIngredient) and `DELETE /api/v1/households/{householdSlug}/shopping/current/ingredients` (unrequestIngredient) are implemented.
|
||||||
- `src/api/client.ts`: Authorization header provider only; household header injection removed.
|
- `src/api/client.ts`: Authorization header provider only; household header injection removed.
|
||||||
- Domain & UI
|
- Domain & UI
|
||||||
- Member arrays (`chefs`, `consumers`, `cleanup`) normalized to `MemberRef` `{ id, displayName }` with decoders handling legacy shapes gracefully.
|
- Member arrays (`chefs`, `consumers`, `cleanup`) normalized to `MemberRef` `{ id, displayName }` with decoders.
|
||||||
- `MemberRef` is exported from `src/domain/types.ts` and used by components (per axioms).
|
- `MemberList.vue` replaces `PersonList.vue`.
|
||||||
- `PersonList.vue` renamed to `MemberList.vue`; it sources from typed household members and emits `add`/`remove`.
|
|
||||||
- Invitation Accept flow implemented; Household Settings supports sending invitations and listing members using typed endpoints.
|
- Invitation Accept flow implemented; Household Settings supports sending invitations and listing members using typed endpoints.
|
||||||
- MyShopping: page remains as in master with editable panel backed by legacy v1 stubs (`getMyShoppingList/saveMyShoppingList`) pending backend ad-hoc item endpoints.
|
- MyShopping: uses the new ad‑hoc ingredient endpoints to add/remove personal items. UI includes an "Add existing ingredient" selector (requests by ingredientId) and supports removal; in-place line edits are local-only for now. Legacy v1 stubs are unused and will be removed in a follow-up.
|
||||||
|
|
||||||
Status of tests and typing
|
Status of tests and typing
|
||||||
- All tests pass: 28 files, 45 tests.
|
- All tests pass: 18 files, 27 tests (legacy JS tests purged).
|
||||||
- `tsc` and `vue-tsc` pass with no errors.
|
- `tsc` and `vue-tsc` pass with no errors.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -152,6 +152,11 @@ Implications and actions (completed):
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Progress Log (Nov 2, 2025)
|
||||||
|
- Implemented ad‑hoc shopping items (add/remove): SDK and composable expose `requestIngredient` and `unrequestIngredient`; `MyShoppingPage.vue` wired with a selector and delete flow; removed `parseProduct` usage.
|
||||||
|
- Purged legacy .test.js files; retained a lean TS test suite.
|
||||||
|
- Verified all checks green.
|
||||||
|
|
||||||
Progress Log (Nov 1, 2025)
|
Progress Log (Nov 1, 2025)
|
||||||
- Established green baseline (typecheck + tests pass).
|
- Established green baseline (typecheck + tests pass).
|
||||||
- Added auth API tests driving a minimal multitenant-ready surface.
|
- Added auth API tests driving a minimal multitenant-ready surface.
|
||||||
|
|
@ -225,6 +230,7 @@ SDK
|
||||||
- `POST /api/v1/households/{householdSlug}/shopping/current/meals/me` — request a meal.
|
- `POST /api/v1/households/{householdSlug}/shopping/current/meals/me` — request a meal.
|
||||||
- `DELETE /api/v1/households/{householdSlug}/shopping/current/meals/{meal_id}` — unrequest a meal.
|
- `DELETE /api/v1/households/{householdSlug}/shopping/current/meals/{meal_id}` — unrequest a meal.
|
||||||
- `POST /api/v1/households/{householdSlug}/shopping/current/ingredients` — request an ingredient (by id) for shopping.
|
- `POST /api/v1/households/{householdSlug}/shopping/current/ingredients` — request an ingredient (by id) for shopping.
|
||||||
|
- `DELETE /api/v1/households/{householdSlug}/shopping/current/ingredients` — unrequest an ingredient (by id) for the current user; idempotent.
|
||||||
- Google OAuth endpoints remain raw until they are added to the OpenAPI.
|
- Google OAuth endpoints remain raw until they are added to the OpenAPI.
|
||||||
|
|
||||||
UI
|
UI
|
||||||
|
|
@ -237,7 +243,7 @@ UI
|
||||||
Tests
|
Tests
|
||||||
- MSW handlers updated for path-scoped endpoints; Authorization header assertions retained where relevant.
|
- MSW handlers updated for path-scoped endpoints; Authorization header assertions retained where relevant.
|
||||||
- Router tests run under memory history.
|
- Router tests run under memory history.
|
||||||
- Replaced header injection test with path-scoped recipe list test.
|
- Legacy JS tests removed; TS suite covers auth, router, invitations, shopping mappings, and household flows.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -278,22 +284,8 @@ Google OAuth is planned next.
|
||||||
- Add a user profile endpoint and load it post-refresh to populate `currentUser()` with real data instead of a placeholder.
|
- Add a user profile endpoint and load it post-refresh to populate `currentUser()` with real data instead of a placeholder.
|
||||||
- Replace temporary raw fetch calls (persons, parse, members listing) with typed endpoints when available.
|
- Replace temporary raw fetch calls (persons, parse, members listing) with typed endpoints when available.
|
||||||
- Implement Google OAuth login and account creation flows.
|
- Implement Google OAuth login and account creation flows.
|
||||||
- MyShopping ad-hoc item requests (planned):
|
- MyShopping ad-hoc items (follow-ups):
|
||||||
- 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`.
|
- Add optional “edit ad‑hoc item” support if backend provides a patch/update endpoint; currently in-place edits are local-only and not persisted.
|
||||||
- Once available, add typed SDK methods:
|
- Remove now-unused legacy stubs from SDK/composable in a cleanup pass (non-functional, safe to delete).
|
||||||
- `createRequestedItem(input: { name: string; line?: string; quantity?: number; unit?: Unit })`
|
|
||||||
- `updateRequestedItem(id: number, patch: { name?: string; line?: string; quantity?: number; unit?: Unit })`
|
|
||||||
- `deleteRequestedItem(id: number)`
|
|
||||||
- `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.
|
|
||||||
- Current status: Legacy stubs removed from composable; `MyShoppingPage.vue` simplified around `requestIngredient`; shape test added and passing. Full ad-hoc CRUD UI awaits backend endpoints.
|
|
||||||
- Remove the legacy username login shim (`login(username: string)`) and any fallback UI; standardize on email/password (and Google) only.
|
- 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.
|
- Rollout flag: default `VUE_APP_MULTITENANT_ENABLED` to true across environments and plan removal of legacy flat routes and related tests once stable.
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export interface paths {
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
/** Parse an ingredient line from a string */
|
/** Parse an ingredient line or lines from a string */
|
||||||
get: operations["parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get"];
|
get: operations["parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get"];
|
||||||
put?: never;
|
put?: never;
|
||||||
post?: never;
|
post?: never;
|
||||||
|
|
@ -1552,9 +1552,11 @@ export interface operations {
|
||||||
};
|
};
|
||||||
parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get: {
|
parse_ingredient_api_v1_households__householdSlug__ingredients_parse_get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query: {
|
query?: {
|
||||||
|
/** @description Single ingredient line to parse */
|
||||||
|
line?: string | null;
|
||||||
/** @description Multiple ingredient lines to parse */
|
/** @description Multiple ingredient lines to parse */
|
||||||
lines: string[];
|
lines?: string[] | null;
|
||||||
};
|
};
|
||||||
header?: never;
|
header?: never;
|
||||||
path: {
|
path: {
|
||||||
|
|
@ -1570,7 +1572,7 @@ export interface operations {
|
||||||
[name: string]: unknown;
|
[name: string]: unknown;
|
||||||
};
|
};
|
||||||
content: {
|
content: {
|
||||||
"application/json": components["schemas"]["Ingredient"][];
|
"application/json": components["schemas"]["Ingredient"] | components["schemas"]["Ingredient"][];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
403: components["responses"]["Problem403"];
|
403: components["responses"]["Problem403"];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue