# Admin Panel Guide: Notification Audience — `user_types`

This document describes the changes to the admin notification API. When sending a notification, the admin can now choose **who** receives it by user type: **individual users**, **company users**, **technicians**, or any combination of them. The selected audience is stored with the notification and returned in the notifications list.

---

## 1. Summary of Changes

| # | Change | Where | Breaking? |
|---|---|---|---|
| 1 | New optional request field **`user_types`** | `POST /admin/notifications/send` | No (additive, optional) |
| 2 | Targeted `user_ids` must belong to one of the selected `user_types` | `POST /admin/notifications/send` | **Behaviour** — see §6 |
| 3 | Admin / super admin accounts are never recipients | `POST /admin/notifications/send` | **Behaviour** — see §6 |
| 4 | New response field **`user_types`** on each sent notification | `GET /admin/notifications` | No (additive) |

Existing payloads (without `user_types`) keep working exactly as before, except that admins no longer receive broadcasts.

---

## 2. The `user_types` Values

| Value | Meaning | Suggested label (EN / AR) |
|---|---|---|
| `individual` | Individual (B2C) app users | Individual users / المستخدمون الأفراد |
| `company` | Company accounts | Companies / الشركات |
| `technician` | Technicians | Technicians / الفنيون |

`admin` and `super_admin` are **not** accepted.

---

## 3. Send Notification — `POST /api/v1/admin/notifications/send`

### 3.1 Request fields

| Field | Type | Required | Rules | Notes |
|---|---|---|---|---|
| `title` | `string` | Yes | max 200 | Unchanged |
| `body` | `string` | Yes | max 1000 | Unchanged |
| `target` | `string` | Yes | `broadcast` \| `targeted` | Unchanged |
| **`user_types`** | `string[]` | **No** | array, at least 1 item, no duplicates, each one of `individual`, `company`, `technician` | **New.** When omitted → all three types |
| `user_ids` | `int[]` | When `target = targeted` | array of **user** ids, no duplicates | Every id must be a user whose type is in `user_types` |

### 3.2 How recipients are resolved

| `target` | Recipients |
|---|---|
| `broadcast` | Every **active** user whose type is in `user_types` |
| `targeted` | The users in `user_ids` (all must be of a type in `user_types`) |

If `user_types` is not sent, it is treated as `["individual", "company", "technician"]`.

### 3.3 Example requests

**Broadcast to technicians only**
```json
{
    "title": "New app version",
    "body": "Please update the technician app to the latest version.",
    "target": "broadcast",
    "user_types": ["technician"]
}
```

**Broadcast to individuals and companies**
```json
{
    "title": "Ramadan offers",
    "body": "Enjoy 20% off all maintenance services this month.",
    "target": "broadcast",
    "user_types": ["individual", "company"]
}
```

**Broadcast to everyone (all app users) — same as before**
```json
{
    "title": "Platform maintenance",
    "body": "The platform will be down for maintenance tonight.",
    "target": "broadcast"
}
```

**Targeted to specific companies and technicians**
```json
{
    "title": "Contract reminder",
    "body": "Please review the updated contract terms.",
    "target": "targeted",
    "user_types": ["company", "technician"],
    "user_ids": [12, 15, 40]
}
```

### 3.4 Success response — `200 OK` (unchanged shape)

```json
{
    "status": "success",
    "message": "Notification sent successfully",
    "data": {
        "recipients_count": 2
    }
}
```

`recipients_count` is the number of users the notification was actually sent to. For a broadcast to a type that has no active users, it can be `0`. Consider showing a warning in that case.

### 3.5 Validation errors — `422 Unprocessable Entity`

All errors use the standard envelope: `{"status": "fail", "message": "...", "data": null}`. The message follows the `Accept-Language` header.

| Case | Example payload part | `message` (en) | `message` (ar) |
|---|---|---|---|
| Unknown or admin type | `"user_types": ["admin"]` | `The selected user_types.0 is invalid.` | `user_types.0 المحدد غير صالح.` |
| Empty array | `"user_types": []` | `The User Types must have at least 1 items.` | `يجب أن يحتوي أنواع المستخدمين على 1 عنصر على الأقل.` |
| Not an array | `"user_types": "technician"` | `The User Types must be an array.` | `يجب أن يكون أنواع المستخدمين مصفوفة.` |
| Duplicate type | `"user_types": ["company", "company"]` | `The user_types.0 field has a duplicate value.` | `حق user_types.0 يحتوي على قيمة مكررة.` |
| A user id is not of a selected type | `"user_types": ["company"], "user_ids": [<technician id>]` | `The selected user_ids.0 is invalid.` | `user_ids.0 المحدد غير صالح.` |
| A user id is an admin account | `"user_ids": [<admin id>]` | `The selected user_ids.0 is invalid.` | `user_ids.0 المحدد غير صالح.` |
| `targeted` without `user_ids` | `"target": "targeted"` | `The User IDs field is required when Target is targeted.` | — (unchanged) |

> The `user_types.*` / `user_ids.*` messages are generic and show the array index, not a friendly name. The UI should prevent these cases (see §7) rather than rely on showing these messages.

---

## 4. Getting the Right `user_ids` for Each Type

`user_ids` always means **`users.id`**. The admin list endpoints don't all return the user id in the same field, so use the field from this table:

| Type | Picker endpoint | Field to send in `user_ids` |
|---|---|---|
| `individual` | `GET /api/v1/admin/users` | `id` |
| `company` | `GET /api/v1/admin/companies` | `id` |
| `technician` | `GET /api/v1/admin/technicians` | **`user_id`** (⚠️ **not** `id` — `id` is the technician profile id) |

For dropdowns, use `?paginate=false` (and `?search=term` for search-as-you-type).

> ⚠️ Sending a technician's `id` instead of `user_id` will either fail with `The selected user_ids.0 is invalid.` or — worse — send the notification to a different user who happens to have that id and a matching type.

---

## 5. Notifications List — `GET /api/v1/admin/notifications`

Each item now includes **`user_types`**:

```json
{
    "id": 14,
    "title": "Contract reminder",
    "body": "Please review the updated contract terms.",
    "target": "targeted",
    "user_types": ["company", "technician"],
    "sender": { "id": 1, "name": "Admin", "email": "admin@example.com" },
    "recipients_count": 3,
    "is_sent": true,
    "created_at": "2026-10-15 15:46:25"
}
```

| Field | Type | Notes |
|---|---|---|
| **`user_types`** | `string[] \| null` | The audience selected when the notification was sent. **`null` for notifications sent before this update.** Show those as "—" or "All users". |
| `target` | `string` | Unchanged (`broadcast` / `targeted`) |
| `recipients_count` | `int` | Unchanged — number of users who actually received it |

> `user_types` is the audience the admin **selected**. For a targeted send without `user_types`, it is saved as all three types, even if every selected user is the same type.

Pagination, search and sorting on this endpoint are unchanged.

---

## 6. Behaviour Changes to Be Aware Of

1. **Admins no longer receive broadcasts.** Before this update, `broadcast` went to every active account, including admin and super admin accounts. It now goes only to individual, company and technician users. Expect `recipients_count` to be slightly lower than before for the same broadcast.
2. **Targeted sends cannot include admin accounts.** An admin user id in `user_ids` now returns `422`.
3. **Targeted `user_ids` are checked against `user_types`.** If the admin picks a technician and then deselects "Technicians" in the audience, the request fails with `422` instead of silently skipping that user.
4. **Broadcast only reaches active users** (unchanged), while **targeted** sends reach the chosen users whatever their status (unchanged).

---

## 7. Recommended UI Changes (Send Notification Form)

1. **Add an "Audience" multi-select** (checkboxes or chips) with the three types from §2.
   - Default: all three selected.
   - Require at least one selected (disable "Send" otherwise) — an empty array returns `422`.
   - If all three are selected you may either send all three or omit `user_types`; both behave the same.
2. **Broadcast mode:** the audience selection is all that's needed. Show a hint such as "Will be sent to all active technicians".
3. **Targeted mode:**
   - Show one user picker per selected type, or one combined picker with a type filter, using the endpoints in §4.
   - Store each selected user as `{ userId, type }` so you can manage them by type.
   - **When a type is deselected, remove the selected users of that type** from `user_ids` (or block deselecting while users of that type are selected). This avoids the `422` in §3.5.
   - For technicians, map **`user_id`**, not `id`.
4. **After sending**, show the returned `recipients_count` (e.g. "Sent to 25 users"). Warn when it is `0`.
5. **Notifications table:** add an "Audience" column that renders `user_types` as tags; render `null` as "—" / "All users".

---

## 8. TypeScript Types

```ts
export type NotificationUserType = 'individual' | 'company' | 'technician';

export type NotificationTarget = 'broadcast' | 'targeted';

export interface SendNotificationPayload {
    title: string;
    body: string;
    target: NotificationTarget;
    user_types?: NotificationUserType[]; // omit = all three
    user_ids?: number[];                 // required when target === 'targeted'; users.id values
}

export interface SendNotificationResponse {
    recipients_count: number;
}

export interface AdminNotificationListItem {
    id: number;
    title: string | null;
    body: string | null;
    target: NotificationTarget;
    user_types: NotificationUserType[] | null; // null = sent before this update
    sender: { id: number; name: string; email: string } | null;
    recipients_count: number;
    is_sent: boolean;
    created_at: string;
}
```

Example payload builder:

```ts
interface SelectedRecipient {
    userId: number; // users.id — for technicians this is `user_id` from /admin/technicians
    type: NotificationUserType;
}

function buildPayload(
    form: { title: string; body: string; target: NotificationTarget },
    userTypes: NotificationUserType[],
    recipients: SelectedRecipient[],
): SendNotificationPayload {
    const payload: SendNotificationPayload = { ...form, user_types: userTypes };

    if (form.target === 'targeted') {
        payload.user_ids = [
            ...new Set(recipients.filter(r => userTypes.includes(r.type)).map(r => r.userId)),
        ];
    }

    return payload;
}
```

---

## 9. Integration Checklist

- [ ] Audience multi-select added with `individual`, `company`, `technician`; at least one required
- [ ] `user_types` sent in the send payload
- [ ] Targeted pickers use the endpoints in §4; technicians map **`user_id`**
- [ ] Selected users of a deselected type are removed from `user_ids`
- [ ] No duplicate ids in `user_ids` and no duplicate values in `user_types`
- [ ] `recipients_count` shown after send; warning when `0`
- [ ] Notifications list shows the new `user_types` column; `null` rendered as "—" / "All users"
- [ ] `422` messages shown from `message` in the response envelope
