Skip to content

Feat/dynamic shopping#102

Merged
Bogdan-ca merged 4 commits into
mainfrom
feat/dynamic-shopping
May 26, 2026
Merged

Feat/dynamic shopping#102
Bogdan-ca merged 4 commits into
mainfrom
feat/dynamic-shopping

Conversation

@Bogdan-ca

Copy link
Copy Markdown
Collaborator

Builds on top of PR #100/#102 with full lifecycle for recommendations:

What's new

Tab Card actions Behavior
Spotlight 🔖 Save · 🚫 Hide · 🗑️ Delete Auto-generated picks
Saved 🚫 Hide · 🗑️ Delete Kept for later
Hidden ↩️ Restore · 🗑️ Delete forever Soft-dismissed, restorable
  • Refresh button wipes all cached rows server-side, then regenerates via ?force=1
  • Hide ≠ Delete — Hide goes to Hidden tab (restorable), Delete is permanent
  • Auto-refresh fires when Spotlight drops below 6 items so the user stays in a steady stream

Server-side

  • New SavedRecommendation + DismissedRecommendation (with permanent flag) collections,
    indexed per user
  • New endpoints: POST /save, GET /saved, DELETE /item, GET /hidden, POST /hidden/:key/restore, POST /hidden/:key/delete-forever, DELETE /cache
  • AI response is post-filtered against the dismissed list so nothing re-surfaces

Bogdan-ca added 4 commits May 26, 2026 13:28
…rows; refresh button now clears all stale rows before regenerating
Replaces the single "X" dismiss button with two distinct actions on
each recommendation card:

- 🔖 Save — moves the item from Spotlight to a server-persisted history.
  Auto-fires a force-refresh when Spotlight thins below 6/8 so the user
  stays in a steady stream of fresh picks. Saved is what surfaces under
  the new Saved tab.
- 🗑️ Delete — permanently dismisses the item. Removed from Spotlight,
  Saved, and added to a per-user dismissed list that subsequent AI
  generations filter out. Same Delete works from either tab.

Server side:
- New SavedRecommendation model (one row per user × stableKey, indexed
  by savedAt for the listing).
- New DismissedRecommendation model used to filter AI results before
  they hit the cache.
- New endpoints: POST /api/recommendations/save, GET /api/recommendations/saved,
  DELETE /api/recommendations/item, DELETE /api/recommendations/cache.
- The existing GET handler now post-filters AI results against the
  dismissed list so dismissed items never re-surface, even on refresh.

Frontend:
- ShoppingPage gains a Spotlight | Saved tab strip with a count badge.
- New saveRecommendation / deleteRecommendation actions in the store,
  optimistic with server roundtrip; failures revert Save but not Delete
  (delete-on-failure is a worse UX than leaving the item dismissed).
- Refresh still wipes cache server-side before regenerating (already
  shipped) and now also implicitly survives the dismissed-filter step.

Tests: 7 new backend integration cases cover save idempotency, history
listing order, dismissed filtering, force-bypass, and cache wipe. Frontend
Playwright spec passes against the new tab layout (heading + budget
empty + reachability).
Splits dismissal into two distinct user actions across the four states:

- Spotlight (auto) — 3 actions: Save, Hide, Delete
- Saved — 2 actions: Hide, Delete
- Hidden — 2 actions: Restore, Delete forever

Hide is reversible (item goes to the Hidden tab and can be Restored).
Delete is final (item is gone, AI filter still excludes it forever).
Both flavors block the AI from re-surfacing the item; the difference
is whether the user has a UI affordance to undo.

Server side:
- DismissedRecommendation grows a `permanent` boolean.
- DELETE /api/recommendations/item accepts `permanent` in the body so
  Hide (false) and Delete (true) share the same endpoint.
- GET /api/recommendations/hidden filters to permanent: false only.
- POST /api/recommendations/hidden/:key/restore wipes the dismissed
  record so the AI may surface the item again.
- POST /api/recommendations/hidden/:key/delete-forever upgrades a
  hidden item to permanent (drops from the Hidden tab, stays filtered).

Frontend:
- New hideRecommendation + deleteHiddenForever actions in the store,
  alongside the existing save/delete/restore. All optimistic.
- RecommendationCard supports the new `hidden` variant and shows
  Restore + Delete-forever icons. Spotlight card renders 3 icons.
- ShoppingPage tabs grew a Hidden entry with its own section and copy.
- api.delete now accepts a body, used to send the item snapshot when
  dismissing so the Hidden tab can render the card later.

3 new backend integration cases cover Hide-vs-Delete visibility,
restore semantics, and the delete-forever promotion path. Frontend
Playwright shopping suite still 3/3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant