-
Notifications
You must be signed in to change notification settings - Fork 44
Description
Summary
Add a customer-facing Wishlist plus a “Notify me” alert system for restock and price-drop events. Shoppers can save products to a wishlist, subscribe to alerts per product/variant, and receive notifications when (a) an out-of-stock item is back or (b) the price falls below a user-selected threshold or percentage.
Motivation
- Increases engagement and return visits without requiring paid vector DB credits.
- Complements current recommendations (Weaviate/FAISS/LangChain) by keeping users in the loop for items they actually want.
- Common e-commerce expectations (wishlists + restock emails) make the demo feel production-grade.
Scope (v1)
-
Wishlist
- Add/remove product (and optional variant) to user wishlist.
- Wishlist page & mini-widget (header icon + count).
-
Alerts
- “Notify me” for restock (when
inventory > 0
) and price-drop (below target/percent). - Single alert per user/product/variant per alert-type (idempotent).
- Delivery: email (dev: console logger; prod: pluggable provider).
- “Notify me” for restock (when
-
Backoffice hooks
- Trigger checks when products are updated (price/inventory changes).
- Daily sweep job to re-evaluate alerts (safety net).
Non-Goals (v1): SMS/mobile push, complex targeting, multi-currency handling, bulk imports.
UX Notes
-
Product detail page:
- If OOS → show “Notify me when back in stock”.
- If in stock → show “Add to wishlist” and a “Price-drop alert” dialog (target price or % drop).
-
Wishlist page:
- Grid of saved items (name, price, stock badge, remove, go to product).
- If OOS item has an active subscription, show “Subscribed • Manage”.
-
Toasts for add/remove/success; form validation for target price.
Data Model (MongoDB)
New collections:
// wishlists
{
_id, userId, items: [
{ productId, variantId: String|null, addedAt }
],
updatedAt, createdAt
}
// alert_subscriptions
{
_id,
userId,
productId,
variantId: String|null,
type: "RESTOCK" | "PRICE_DROP",
// Exactly one of the below for price alerts
targetPrice: Number|null,
dropPercent: Number|null, // e.g., 15 => notify when price <= price_at_subscribe * 0.85
status: "ACTIVE" | "CANCELLED" | "TRIGGERED",
lastNotifiedAt: Date|null,
createdAt
}
Indexes:
wishlists
:{ userId: 1 }
alert_subscriptions
:{ userId: 1, productId: 1, variantId: 1, type: 1, status: 1 }
(unique on{userId, productId, variantId, type, status: ACTIVE}
)
API (Express)
Wishlist
POST /api/wishlist body: { productId, variantId? }
DELETE /api/wishlist body: { productId, variantId? }
GET /api/wishlist -> { items: [...] }
Alerts
POST /api/alerts/subscribe body: { productId, variantId?, type: "RESTOCK" | "PRICE_DROP", targetPrice?, dropPercent? }
POST /api/alerts/cancel body: { subscriptionId }
GET /api/alerts/mine -> { subscriptions: [...] }
Admin/Hook (internal)
POST /api/admin/products/:id/reindex // optional: trigger recompute after price/stock update
Validation rules:
PRICE_DROP
: exactly one oftargetPrice
ordropPercent
must be provided.- Prevent duplicate ACTIVE subscription by (userId, productId, variantId, type).
Update openapi.yaml + Swagger docs with new schemas and endpoints.
Triggering Logic
-
On product update (price or inventory), run
evaluateSubscriptions(productId)
:-
RESTOCK: notify if
inventory > 0
. -
PRICE_DROP:
- If
targetPrice
: notify ifcurrentPrice <= targetPrice
. - If
dropPercent
: compute threshold from current price at subscribe time or storebasePrice
in doc (recommended field:basePriceAtSubscribe
).
- If
-
-
Mark subscription as
TRIGGERED
and setlastNotifiedAt
. -
Idempotency: keep a small Redis/Memory TTL key (fallback to Mongo query)
{subId}:{productPrice}:{inventory}
to avoid double-send in bursty updates.
Email Delivery (pluggable)
-
Dev: console transport (logs JSON of “email”).
-
Prod option: environment-driven provider (
SENDGRID_API_KEY
orRESEND_API_KEY
orMAILGUN_*
). -
Templated subject lines:
- Restock:
“Back in stock: {{productName}}”
- Price drop:
“Price dropped to {{price}}: {{productName}}”
- Restock:
Background Job
-
Add a small scheduler:
- Option A (simple):
node-cron
daily at 02:00 server time → re-evaluate all ACTIVE subs for safety. - Option B (if Redis available): BullMQ queue
alerts:eval
to process in batches.
- Option A (simple):
Security
- All endpoints require JWT (already used in app).
- Verify user ownership on wishlist and subscriptions.
- Rate limit write endpoints (e.g., 10/min/user) to prevent abuse.
Observability
-
Logs:
alerts.triggered
,alerts.skipped_reason
,wishlist.add/remove
with userId, productId. -
Counters:
alerts_triggered_total{type}
,subscriptions_active_total
. -
Add basic unit tests for:
- Subscription validation.
- Threshold evaluation.
- Idempotent trigger on repeated product updates.
Acceptance Criteria
-
A logged-in user can:
- Add/remove products from wishlist; wishlist persists and renders on its page.
- Subscribe to RESTOCK for an OOS product; when stock becomes available, an email (or console log in dev) is produced and subscription becomes
TRIGGERED
. - Subscribe to PRICE_DROP (target price or percent); on qualifying price change, receive notification and subscription becomes
TRIGGERED
.
-
API and Swagger/OpenAPI are updated and pass validation.
-
Unit tests cover rule evaluation and API validators.
-
No duplicate notifications are sent for the same event/version.
-
CI passes and feature is behind env flag
FEATURE_ALERTS=true
.
Tasks
Backend
- Mongo models for
wishlists
andalert_subscriptions
(+ indexes). - POST/DELETE/GET wishlist endpoints with auth + validation.
- Subscribe/cancel/list alert endpoints with validation.
- Evaluation service
evaluateSubscriptions(product)
with idempotency. - Hook in product update path (price/stock) to call evaluator.
- Email provider abstraction + console transport (dev) + env-based provider (prod).
-
node-cron
(or BullMQ) daily sweep. - Unit tests (Jest) for validators & evaluator.
Frontend (React)
- Wishlist icon in header + page
/wishlist
. - Product detail: “Add to wishlist” / “Remove from wishlist”.
- OOS state: “Notify me when back in stock” button (requires auth).
- Price-drop dialog (target or %), client-side validation.
- Toasts and loading states.
Docs & CI
- Update
README.md
(feature overview + envs). - Update
openapi.yaml
+ Swagger UI. - Add
.env.example
keys for email provider +FEATURE_ALERTS
. - Extend GitHub Actions to run new tests.
Open Questions
- Should
PRICE_DROP
alerts auto-rearm (stay ACTIVE) until user cancels, or single-shot (v1 uses single-shotTRIGGERED
)? - Variant-level stock/price: do we already store variant prices/inventory separately? If yes, require
variantId
to avoid false alerts. - Retention: auto-clean
TRIGGERED
subscriptions older than 90 days?
Metadata
Metadata
Assignees
Labels
Projects
Status