2026-08-15 02:08:36 +04:00
|
|
|
# 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.
|
|
|
|
|
|
2026-08-15 03:43:07 +04:00
|
|
|
**Badge — reuses existing topbar bell**
|
2026-08-15 02:08:36 +04:00
|
|
|
|
2026-08-15 03:43:07 +04:00
|
|
|
`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).
|
2026-08-15 02:08:36 +04:00
|
|
|
|
|
|
|
|
**Click behavior**
|
|
|
|
|
|
|
|
|
|
- Toast click → `/admin/orders/:id` (the new order's detail page).
|
2026-08-15 03:43:07 +04:00
|
|
|
- Clicking an order row inside the bell panel → same, then closes the panel.
|
2026-08-15 02:08:36 +04:00
|
|
|
|
|
|
|
|
## 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.
|