create mvp path

This commit is contained in:
hamid
2026-08-02 20:01:31 +03:30
parent 72ab290da1
commit fb58ca54e1
203 changed files with 863 additions and 156 deletions
@@ -0,0 +1,48 @@
# notifications — the in-app feed and unread badge
> Client seam `client/src/services/notifications/` · `USE_NOTIFICATIONS_MOCK = false` (**real**) · 4 server ops
> Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29).
Server-raised, user-scoped notifications. In-app only — there is no push channel and no email channel.
## Endpoints
| Method | Path | Verdict |
| --- | --- | --- |
| GET | `/api/v1/notifications/get_notifications` | wired · paginated |
| GET | `/api/v1/notifications/get_unread_count` | wired — polled for the bell badge |
| POST | `/api/v1/notifications/mark_notification_read` | wired |
| POST | `/api/v1/notifications/mark_all_read` | wired |
All `[Authorize]`. No phantoms. The domain maps 1:1.
## Shape rules the JSON does not express
- **`dataJson` is a string containing JSON**, not an object. It is the deep-link payload: the client parses
it (`parse.ts`) and resolves a route from it (`deepLink.ts`), both with unit tests, and **falls back to
an inert notification rather than throwing** on anything unrecognised. A notification whose payload
cannot be parsed still renders — it just is not tappable.
- **Unread count is a separate read, not derived from the list.** The badge must be correct without
fetching a page, so `get_unread_count` is its own cheap query.
- **`mark_all_read` is a bulk write**; the client invalidates both the list and the count keys, and does not
patch items locally.
- The feed is **day-grouped in the UI** with Shamsi headers — a client-side transform over UTC
`createdAt`. The server sends no grouping.
- Notifications are raised through a self-committing facade (`DispatchAsync`) that runs **after** the
originating handler's `CommitAsync` — so a notification never exists for a transaction that rolled back.
## Enums
`type` is a **bare string** on the wire and the vocabulary is open-ended by design — new server-side
notification types must not break an older client. The client models the *payload* as a discriminated
union (`NotificationData`, keyed on a `kind` inside `dataJson`) and treats an unknown `type` as
non-actionable rather than an error.
Consequence for the server: **adding a notification type is safe; changing an existing type's `dataJson`
shape is not.** The deep-link parser keys off the payload, not the type string.
## Open REQs
| REQ | Status | Effect |
| --- | --- | --- |
| REQ-054 | deferred, non-blocking | No web push for new booking requests. The nurse dashboard's 15 s poll remains the only signal. Nothing was built for it |