# 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 — reuses existing topbar bell**

`admin-layout.component.html:141-157` already has an unused bell icon +
dropdown panel (currently hardcoded to always show "no notifications").
Wire the watcher's data into it instead of adding a new indicator:

- `unreadCount` signal (from `AdminOrderWatcherService`) rendered as a badge
  on the bell icon (`admin-layout__icon-button`).
- Opening the panel (`notificationsOpen()`, already wired to the bell click)
  lists the unread new orders instead of the static "notificationsEmpty"
  text.
- Opening the panel marks all currently-known orders as seen → badge resets
  to 0 (same trigger `AdminLayoutComponent.toggleNotifications()` already
  has).

**Click behavior**

- Toast click → `/admin/orders/:id` (the new order's detail page).
- Clicking an order row inside the bell panel → same, then closes the panel.

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