# SLA Configuration UI

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 145  
**Tier:** Business+ (configure SLA policies); All tiers (read SLA status)  
**Depends on:** `crm-support-center`, `ticket-sla-escalation`, `foundation-auth-rbac`, `foundation-design-system`  
**Referenced by:** `crm-support-center`, `ticket-sla-escalation`

---

## Overview

Spec 91 (`ticket-sla-escalation`) defines the SLA data model (`sla_policies` table), cron-based breach detection, and escalation logic. It contains no UI for viewing or managing SLA policies. Spec 14 (`crm-support-center`) is unchanged — it shows ticket priority but has no SLA status indicators or config link. This spec defines the SLA settings UI at `/settings/sla` and the SLA status indicators in the ticket list and detail.

---

## Route

`/settings/sla` — requires `users:manage` (Business+ tier only).

---

## SLA Settings Page

```
┌──────────────────────────────────────────────────────────────┐
│  Settings > SLA Policies                                     │
│                                                              │
│  Business+: SLA policies set response and resolution targets │
│  per ticket priority. Breaches trigger escalation.           │
│                                                              │
│  Priority   First Response   Resolution   Escalation email   │
│  ─────────────────────────────────────────────────────────  │
│  urgent     1 hour           4 hours      admin@acme.com     │
│  high       4 hours          8 hours      admin@acme.com     │
│  medium     8 hours          24 hours     (none)             │
│  low        24 hours         72 hours     (none)             │
│                                                              │
│  Active: ✓ SLA tracking enabled for all tickets              │
│  [Edit] per row                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Edit SLA Policy Sheet

**[Edit]** on a policy → slide-in sheet:

```
┌──────────────────────────────────────────────────────────────┐
│  Edit SLA — high priority                                    │
│                                                              │
│  First response target                                       │
│  [4____] hours  (0 = no target)                              │
│                                                              │
│  Resolution target                                           │
│  [8____] hours  (0 = no target)                              │
│                                                              │
│  Escalation email (on breach)                                │
│  [admin@acme.com__________________]                          │
│  (leave blank for no escalation email)                       │
│                                                              │
│  Notify on breach                                            │
│  ☑ In-app notification to assignee      NEW                  │
│  ☑ Email to escalation address          NEW                  │
│                                                              │
│  [Cancel]    [Save]                                          │
└──────────────────────────────────────────────────────────────┘
```

---

## SLA Status in Ticket List

In `/tickets` list, each ticket row shows an SLA indicator:

```
│  Ticket                    Customer  Priority  Status  SLA         │
│  ─────────────────────────────────────────────────────────────── │
│  #001 Login broken         Acme      urgent    Open    🔴 Breached │
│  #002 Export not working   Beta      high      Open    🟡 4h left  │
│  #003 UI question          Gama      low       Open    🟢 2d left  │
│  #004 Cannot export PDF    Acme      medium    Open    — No SLA    │
```

SLA indicator:
| State | Icon | Color | Label |
|-------|------|-------|-------|
| No SLA policy defined | — | grey | "No SLA" |
| Time remaining > 25% | 🟢 | `--success` | "{N}h left" |
| Time remaining ≤ 25% | 🟡 | `--warning` | "{N}h left" |
| Breached (first response) | 🔴 | `--danger` | "Response breached" |
| Breached (resolution) | 🔴 | `--danger` | "Breached {N}h ago" |

Shows the more critical of the two targets (first response vs resolution).

---

## SLA Status in Ticket Detail

In the ticket detail sidebar:

```
SLA
─────────────────────────────────
First response:  ✓ Responded 14:32
Resolution:      ⏳ 3h 45m remaining
                 (due by 18:00 today)
```

If breached:
```
SLA
─────────────────────────────────
First response:  ✓ Responded
Resolution:      ✗ Breached 2h ago
                 [Mark resolved]
```

---

## Non-Business+ Upsell

For non-Business+ tenants visiting `/settings/sla`:

```
SLA Policies — Business+ feature

Set response time targets per ticket priority and auto-escalate
breaches to your team.

[Upgrade to Business+]
```

---

## Schema Delta

No new tables and **no schema delta**. The full `sla_policies` table — `id`, `tenant_id`, `priority` (`low/medium/high/urgent`), `first_response_hours`, `resolution_hours`, `escalation_email`, and the per-policy notification columns `notify_email` / `notify_in_app` (BOOLEAN NOT NULL DEFAULT true) — is created by spec 91 (`ticket-sla-escalation`). This spec consumes those columns; it adds no DDL.

---

## API

The SLA **settings** routes are owned by spec 91 (`ticket-sla-escalation`) and consumed by this spec's page/hooks — this spec does NOT redeclare them (declaring a second `GET /api/settings/sla` would double-mount the route):

```
# Owned by spec 91 — consumed here, not declared:
GET   /api/settings/sla              → { policies: [{ id, priority, first_response_hours,
                                          resolution_hours, escalation_email,
                                          notify_email, notify_in_app }], sla_enabled }
PATCH /api/settings/sla/:policyId    → update one policy's targets + notify prefs

# Declared by THIS spec (spec 91 has no per-ticket status endpoint):
GET /api/tickets/:id/sla
    → SLA status for a specific ticket
      Returns: { first_response_status, resolution_status,
                 first_response_due_at, resolution_due_at, is_breached, indicator_state }
      Requires: authenticated
```

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| SLA indicator in ticket list | Not a separate report | Staff need to see SLA urgency inline when triaging; a separate report would be too late for operational response |
| Time remaining in real-time | Polled (30s) | SLA deadlines change by the minute; 30s polling matches acceptable operational latency without WebSocket overhead |
| Edit existing policies only | Not add/remove priorities | SLA policies map 1:1 to ticket priorities (low/medium/high/urgent per spec 14); adding custom priority names is out of scope |
| `escalation_email` per policy | Not role-based escalation | Spec 91 already stores `escalation_email TEXT` per policy; role-based escalation would require a runtime role→email lookup with no added reliability benefit |
