# 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.