diff --git a/docs/superpowers/specs/2026-08-15-admin-purchase-notifications-design.md b/docs/superpowers/specs/2026-08-15-admin-purchase-notifications-design.md new file mode 100644 index 0000000..c07b35a --- /dev/null +++ b/docs/superpowers/specs/2026-08-15-admin-purchase-notifications-design.md @@ -0,0 +1,77 @@ +# Admin purchase notifications — design + +**Status:** Approved +**Date:** 2026-08-15 +**Related backlog item:** #7 (marked ВАЖНО — important) + +## Problem + +Admin has no signal when a purchase happens on the marketplace. Orders only surface if +someone manually opens the Orders list and refreshes. Backend exposes no WebSocket/SSE +(confirmed in `BACKEND-API-REFERENCE.md:20` — every "live" feature today, e.g. payment +status, is plain polling), so this has to be poll-based like the rest of the app. + +## Architecture + +**`AdminOrderWatcherService`** (new, `providedIn: root`, admin-scoped) + +- Polls `AdminOrdersLocalGateway.loadOrders()` (sorted `createdAt` desc, already the + default sort) on an interval. +- Diffs the newest order's `id`/`createdAt` against the last-seen value, kept in memory + and persisted via `LocalStorageService` (survives page reload, same pattern as + `AdminPreferencesService`). +- On finding order(s) newer than last-seen: fires one toast per new order and + increments an `unreadCount` signal. +- Started once at the admin shell root, so it keeps polling regardless of which admin + page is open. + +**Poll interval** + +- Editable by admin, default 15s. +- Setting lives in the same admin-settings page as currency rates + (`admin-settings-page.component.ts`), persisted via `LocalStorageService`. + +**Toast delivery** + +- Reuses the existing `UserNotificationService` / `FloatingNotificationsComponent` + (already global — `providedIn: root`, mounted once in `app.html`). No new toast UI. +- `UserNotification` gains an optional `route: string[]` field. +- `FloatingNotificationsComponent` gets a click handler: navigate to `route` (if set) + then dismiss. + +**Badge** + +- `unreadCount` signal (from `AdminOrderWatcherService`) rendered on the admin + sidebar's "Orders" nav item. +- Visiting the orders list marks all currently-known orders as seen → badge resets to 0. + +**Click behavior** + +- Toast click → `/admin/orders/:id` (the new order's detail page). +- Badge click → orders list. + +## Data flow + +``` +AdminOrderWatcherService (interval timer) + -> AdminOrdersLocalGateway.loadOrders() + -> diff against last-seen order id/createdAt (LocalStorageService) + -> new order(s) found? + -> UserNotificationService.show(message, 'info', { route: ['/admin/orders', id] }) + -> unreadCount.update(n => n + 1) + -> admin clicks toast/badge -> router navigate -> orders-list visit resets unreadCount +``` + +## Error handling + +Poll failures are silent/logged only (`console.error`), consistent with existing +polling code (payment status polling in `cart.component.ts`). No toast spam on +transient network errors — watcher just retries on the next interval. + +## Out of scope + +- Native OS push notifications (tab not focused) — user explicitly chose in-app + toast/badge only, not browser Notification API. +- Sound alerts — not selected. +- Telegram/email alerts to staff — not selected, would need backend bot/mail + integration.