# Ticket Collaborators Architecture

## Summary

MVNexus tickets have **one primary assignee** (`current_assignee_id`) and **many collaborators** (`ticket_collaborators`). Collaborators are supporting participants — not owners.

## Why single assignee was preserved

- SLA ownership stays on `current_assignee_id`
- Dashboard assignee metrics and workload reporting remain accurate
- Assignment history (`ticket_assignments`) continues to represent ownership changes
- No `assigned_users[]`, JSON assignees, or multi-assignee fields were introduced

## Data model

Table: `ticket_collaborators`

| Column | Purpose |
|--------|---------|
| `id` | UUID primary key |
| `ticket_id` | FK → tickets (cascade delete) |
| `user_id` | FK → users |
| `added_by_user_id` | FK → users (actor) |
| `created_at` / `updated_at` | timestamps |

Constraints:

- `unique(ticket_id, user_id)`
- indexes on `ticket_id`, `user_id`

Model: `App\Modules\Ticketing\Models\TicketCollaborator`

Relationships:

- `Ticket::collaborators()` → hasMany
- `Ticket::collaboratorUsers()` → belongsToMany
- `User::collaboratedTickets()` → belongsToMany

## Lifecycle

1. Authorized actor adds eligible department user via `POST /api/v1/tickets/{ticket}/collaborators`
2. Service validates company + department scope, active user, no duplicate, not assignee/requester
3. Row inserted → `TicketCollaboratorAdded` event
4. Notifications + realtime broadcast fire
5. Audit log created on insert (`created`) and manual audit on remove (`deleted` + `removed_by_*` metadata)
6. Remove via `DELETE /api/v1/tickets/{ticket}/collaborators/{userId}`

Optional later: `PUT` sync endpoint using `TicketCollaboratorService::syncCollaborators()`.

## Permissions

Backend enum (`department.tickets.collaborators.*`):

| Permission | Purpose |
|------------|---------|
| `department.tickets.collaborators.view` | See collaborator list |
| `department.tickets.collaborators.manage` | Add/remove collaborators |
| `department.tickets.collaborators.add_self` | Add yourself only |

Policy highlights:

- Super admin: global
- Company admin (`company.users.manage`): company scope
- Department head: own department
- Assignee/collaborator/requester: ticket view rules extended for collaborators

## Notification behavior

Collaborators receive notifications for:

- Replies
- Internal notes (agent recipient set)
- Status / assignment observer notifications
- Mention notifications (existing mention service)

They do **not** receive SLA warning/breach notifications (assignee remains SLA owner).

Primary assignee still receives dedicated assignment notification.

## Realtime

Broadcast actions on private channels:

- `ticket.collaborator_added`
- `ticket.collaborator_removed`

Channels: `ticket.{id}`, `department.{departmentId}.tickets`

Frontend invalidates ticket detail, collaborators query, and activity timeline.

## API

| Method | Path |
|--------|------|
| GET | `/api/v1/tickets/{ticket}/collaborators` |
| POST | `/api/v1/tickets/{ticket}/collaborators` `{ "user_id": "uuid" }` |
| DELETE | `/api/v1/tickets/{ticket}/collaborators/{userId}` |
| GET | `/api/v1/tickets/my-collaborations` |
| GET | `/api/v1/departments/{id}/collaborator-users?search=` |

Ticket detail (`GET /api/v1/tickets/{id}`) includes:

- `collaborators[]`
- `collaborator_activity[]`

## Search / dashboard

- `GET /tickets/my-collaborations` supports “tickets I collaborate on”
- Dashboard ownership metrics continue to use `current_assignee_id` only
- Future: collaborator filters on list/search endpoints

## Migration notes

Run:

```bash
php artisan migrate
php artisan db:seed --class=RolePermissionSeeder
```

Existing tickets start with zero collaborators. No backfill required.

Existing assignee data is untouched.

## Future extensibility

- Sync endpoint for bulk collaborator updates
- Collaborator filter on ticket list/search
- Configurable SLA notifications for collaborators
- Online presence indicator per collaborator
- Self-join CTA for agents with `add_self` permission
