# Workflow — Notification Fan-out

How VisoAdmin produces notifications and how those propagate across in-app inboxes, realtime sockets, and (potentially) external mail/SMS dispatchers.

---

## Sequence

```
                  ┌───────────────────────────────────┐
                  │ Trigger source in VisoAdmin       │
                  │  - RM creates event (Feature 09)  │
                  │  - Other admin mutations (future) │
                  └────────────────┬──────────────────┘
                                   │
            ┌──────────────────────┼──────────────────────┐
            │                      │                      │
            ▼                      ▼                      ▼
   ┌──────────────────┐   ┌────────────────────┐  ┌──────────────────────┐
   │ Write a row to   │   │ POST to Viso-Chat  │  │ Route to external    │
   │ notifications    │   │ /notification      │  │ email/SMS dispatcher │
   │  (data_props or  │   │  realtime push     │  │ via notification_    │
   │   payload)       │   │                    │  │ setting rows         │
   └────────┬─────────┘   └─────────┬──────────┘  └──────────┬───────────┘
            │                       │                        │
            ▼                       ▼                        ▼
   ┌──────────────────┐   ┌────────────────────┐  ┌──────────────────────┐
   │ Public app polls │   │ WebSocket frame    │  │ Email/SMS service    │
   │ in-app inbox →   │   │ to connected       │  │ (external; not in    │
   │ rows appear      │   │ device (mobile)    │  │ this repo)           │
   └──────────────────┘   └────────────────────┘  └──────────────────────┘

                            VisoAdmin own inbox (badge poll)
                                       │
                                       ▼
                  ┌────────────────────────────────────────────┐
                  │ Every page load:                           │
                  │   GET /notificationCount                   │
                  │     → COUNT type='admin' AND read_user_ids=''│
                  │     → top 5 dropdown items                 │
                  │     → "See all" links to /notifications    │
                  └────────────────────────────────────────────┘
```

---

## Step-by-Step

| # | Step | Feature(s) | Endpoint / Action |
|---|------|------------|------------------|
| 1 | A trigger in VisoAdmin (e.g. RM creates event) decides a notification is needed | [09 RM Create Event](../09-rm-create-event/spec.md) | `POST /rm/store-event` |
| 2 | A `notifications` row is inserted with `created_for`, `created_for_type`, `module`, `payload` | [09 RM Create Event](../09-rm-create-event/spec.md) | DB INSERT (try/catch wrapped, non-fatal) |
| 3 | A POST is sent to Viso-Chat's `/notification` endpoint with `receiverId`, `title`, `body`, `props` | [09 RM Create Event](../09-rm-create-event/spec.md) + Viso-Chat | HTTP POST |
| 4 | Viso-Chat broadcasts a WebSocket frame to the connected client device | Viso-Chat | WS push |
| 5 | The client app updates its in-app notification badge | (OotboApps client) | — |
| 6 | The admin team sees an inbox row via the badge poll on next page load | [16 Notifications — Admin Inbox](../16-notifications-admin/spec.md) | `GET /notificationCount` |
| 7 | Admin clicks "See All" → reads the full inbox | [16 Notifications — Admin Inbox](../16-notifications-admin/spec.md) | `GET /notifications` |

External email/SMS dispatching (gray box) uses `notification_setting` rows ([17 Notification Settings](../17-notification-settings/spec.md)) — but the dispatcher itself is **not in this repo**. VisoAdmin only writes/deletes those routing rows.

---

## Touched Tables

`notifications`, `events` (for event_name resolution in admin inbox), `notification_setting` (routing table).

---

## Failure Modes

- **`payload` vs `data_props` column mismatch** — the most critical bug area. Writer writes one, reader reads the other → notification visible but body empty. See Feature 16 Known Issues.
- **Socket POST fails** — try/catch swallows; admin gets logged-only error. Client app never gets the realtime push but the DB row exists so next list-refresh shows it.
- **No retry queue** — failed socket POSTs are not retried.
- **`type='admin'`** is hard-coded for the badge query — notifications written without this `type` are invisible to admins.
- **`read_user_ids = ''`** is the "unread" sentinel — there's no "mark all read" endpoint in this codebase. Badge count never naturally decrements.
- **External email/SMS dispatcher is unimplemented** in this repo — `notification_setting` is purely a routing table for code that lives elsewhere (or has yet to be written).

---

## Improvement Opportunities (Not Implemented)

- Single source of truth: pick `data_props` OR `payload`, deprecate the other.
- Add a "mark read" endpoint that updates `read_user_ids`.
- Queue notification dispatch via Laravel queues (`QUEUE_CONNECTION=sync` today).
- Add a retry policy for the Viso-Chat HTTP POST.
- Document/implement the external email/SMS dispatcher that reads `notification_setting`.
