Context
SLA (Service Level Agreement) tracking ensures tickets get timely responses and resolutions. Business-hours-aware SLA calculation accounts for evenings, weekends, and holidays when agents aren't working. The reporting dashboard gives managers visibility into team performance.
Depends on: #599 (MVP — tickets with timestamps), #603 (Agent Availability — work schedule context)
Scope
In Scope
- SLA policies: configurable per priority (e.g., high = 4h response, 24h resolution)
- Business-hours calculator: counts only working hours (Europe/Amsterdam, Mon–Fri 8:00–17:30 default)
- SLA pause/resume: clock pauses on
waiting_on_customer, resumes on customer reply
- Breach detection: track when SLA is breached, send internal notification
- Warning at 80%: alert agent when SLA is 80% consumed
helpdesk_settings table: store business hours config as JSON
sla_paused_at column on tickets: track when clock was paused
- Agent metrics view: avg response time, avg resolution time, tickets per period
- Dashboard UI (
/admin/support/dashboard): metrics overview, SLA breach list, ticket volume chart
Out of Scope
- Customer-facing SLA commitments / external SLA display
- Per-customer SLA tiers
- Holiday calendar integration (workdays covers it)
- Real-time WebSocket updates on dashboard
Acceptance Criteria
Technical Implementation
1. Schema Additions
Settings table (shared config):
export const helpdeskSettings = fdmHelpdeskSchema.table("helpdesk_settings", {
key: text().primaryKey(),
value: jsonb().notNull(),
updated_by: text(),
updated: timestamp({ withTimezone: true }).notNull().defaultNow(),
})
SLA columns on tickets:
// Added to tickets table:
sla_first_response_due: timestamp({ withTimezone: true }),
sla_resolution_due: timestamp({ withTimezone: true }),
sla_first_response_met: boolean(),
sla_resolution_met: boolean(),
sla_paused_at: timestamp({ withTimezone: true }),
sla_paused_duration: integer().notNull().default(0), // accumulated pause time in minutes
2. SLA Policies Configuration
Stored in helpdesk_settings with key sla_policies:
{
"business_hours": {
"timezone": "Europe/Amsterdam",
"workdays": [1, 2, 3, 4, 5],
"start_hour": 8,
"end_hour": 17.5
},
"policies": {
"urgent": { "first_response_hours": 1, "resolution_hours": 8 },
"high": { "first_response_hours": 4, "resolution_hours": 24 },
"normal": { "first_response_hours": 8, "resolution_hours": 48 },
"low": { "first_response_hours": 24, "resolution_hours": 120 }
}
}
3. Business-Hours Calculator
export function calculateSlaDueDate(
createdAt: Date,
targetHours: number,
businessHours: BusinessHoursConfig,
): Date {
// Walk forward from createdAt, counting only business hours
// Skip weekends and outside start_hour/end_hour
// Returns the due date in UTC
}
export function calculateRemainingMinutes(
dueDate: Date,
now: Date,
pausedDuration: number,
businessHours: BusinessHoursConfig,
): number {
// Returns remaining business minutes (can be negative if breached)
}
4. SLA Lifecycle
- Ticket created → calculate
sla_first_response_due and sla_resolution_due based on priority
- Agent sends first reply → set
sla_first_response_met = true/false based on whether due was exceeded
- Status →
waiting_on_customer → set sla_paused_at = now()
- Customer replies (status leaves WOC) → add elapsed pause to
sla_paused_duration, clear sla_paused_at, recalculate sla_resolution_due
- Ticket resolved/closed → set
sla_resolution_met = true/false
5. SLA Visual Indicators
On ticket cards and ticket detail:
// In ticket list:
TK-A3B2C4 · Kan niet inloggen · 🟢 2u 15m
// Color coding:
🟢 Green: > 50% remaining
🟡 Yellow: 20–50% remaining (80% warning)
🔴 Red: < 20% remaining or breached
⚫ Black: Breached (past due)
6. UI — Admin Dashboard (/admin/support/dashboard)
┌──────────────────────────────────────────────────────────────────────────────┐
│ 📊 Helpdesk Dashboard [Laatste 30 dagen ▾] │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌────────────┐ │
│ │ 42 │ │ 3u 12m │ │ 18u 45m │ │ 3 │ │
│ │ Tickets │ │ Gem. 1e reactie │ │ Gem. oplostijd │ │ SLA-breach │ │
│ └──────────┘ └──────────────────┘ └──────────────────┘ └────────────┘ │
│ │
│ ── Ticket volume (afgelopen 30d) ── │
│ ▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊ [sparkline chart] │
│ │
│ ── Agent prestaties ── │
│ Agent 1e reactie Oplostijd Tickets SLA % │
│ ─────────────────────────────────────────────────── │
│ Jan 2u 30m 14u 20m 18 94% │
│ Sophie 3u 50m 22u 10m 14 86% │
│ Kees 4u 15m 19u 55m 10 90% │
│ │
│ ── SLA breaches (openstaand) ── │
│ │ 🔴 TK-X9Y2Z1 · Exportprobleem · 2u over SLA │ │
│ │ 🔴 TK-M4N5P6 · Perceel verdwenen · 45m over SLA │ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
7. App Routes
| Route |
Purpose |
admin.support.dashboard.tsx |
Metrics overview |
admin.support.settings.sla.tsx |
SLA policy configuration |
8. Metrics Queries (SQL views or functions)
-- Average first response time (business hours) over period
-- Average resolution time (business hours) over period
-- SLA compliance rate (% tickets meeting first response + resolution)
-- Ticket volume per day/week
-- Per-agent breakdown
These can be implemented as Drizzle queries with aggregations or as PostgreSQL views for performance.
Testing Requirements
- Unit tests: Business-hours calculator (crossing weekends, overnight, exact boundary), SLA pause/resume duration math
- Integration tests: Create ticket → verify SLA due dates set → simulate agent reply → verify
sla_first_response_met; pause on WOC → resume → verify duration accumulated
- Edge cases: Ticket created Friday 17:00 (due date should be Monday), pause spanning a weekend, midnight boundary
Definition of Done
Agents and managers have visibility into SLA compliance. Tickets show time remaining. The dashboard provides actionable metrics for team performance improvement.
Context
SLA (Service Level Agreement) tracking ensures tickets get timely responses and resolutions. Business-hours-aware SLA calculation accounts for evenings, weekends, and holidays when agents aren't working. The reporting dashboard gives managers visibility into team performance.
Depends on: #599 (MVP — tickets with timestamps), #603 (Agent Availability — work schedule context)
Scope
In Scope
waiting_on_customer, resumes on customer replyhelpdesk_settingstable: store business hours config as JSONsla_paused_atcolumn on tickets: track when clock was paused/admin/support/dashboard): metrics overview, SLA breach list, ticket volume chartOut of Scope
Acceptance Criteria
waiting_on_customerTechnical Implementation
1. Schema Additions
Settings table (shared config):
SLA columns on tickets:
2. SLA Policies Configuration
Stored in
helpdesk_settingswith keysla_policies:{ "business_hours": { "timezone": "Europe/Amsterdam", "workdays": [1, 2, 3, 4, 5], "start_hour": 8, "end_hour": 17.5 }, "policies": { "urgent": { "first_response_hours": 1, "resolution_hours": 8 }, "high": { "first_response_hours": 4, "resolution_hours": 24 }, "normal": { "first_response_hours": 8, "resolution_hours": 48 }, "low": { "first_response_hours": 24, "resolution_hours": 120 } } }3. Business-Hours Calculator
4. SLA Lifecycle
sla_first_response_dueandsla_resolution_duebased on prioritysla_first_response_met = true/falsebased on whether due was exceededwaiting_on_customer→ setsla_paused_at = now()sla_paused_duration, clearsla_paused_at, recalculatesla_resolution_duesla_resolution_met = true/false5. SLA Visual Indicators
On ticket cards and ticket detail:
6. UI — Admin Dashboard (
/admin/support/dashboard)7. App Routes
admin.support.dashboard.tsxadmin.support.settings.sla.tsx8. Metrics Queries (SQL views or functions)
These can be implemented as Drizzle queries with aggregations or as PostgreSQL views for performance.
Testing Requirements
sla_first_response_met; pause on WOC → resume → verify duration accumulatedDefinition of Done
Agents and managers have visibility into SLA compliance. Tickets show time remaining. The dashboard provides actionable metrics for team performance improvement.