feat(d28): #3 ticket desk — SLA aging + overdue filter + bill-from-ticket
Age column on the workbench (days since opened); a still-open ticket aged past ticket.sla_days (dated setting, default 7) shows red with a warning. New 'Overdue' filter chip with an honest server-side count (still-open AND opened on/before the SLA cutoff). Each row gains a 'Bill' action that opens New Document with the client pre-selected (/documents/new?client=<id>) — the light ticket->invoice path, staff add the lines. Kept deliberately un-bulky: no kanban, reuses the existing table. Test locks the overdue filter + count. typecheck + full suite (403) green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/client-detail-redesign
parent
e83d70a4ff
commit
650cde43a2
@ -0,0 +1,26 @@
|
||||
// apps/hq/test/ticket-sla.test.ts — D28: ticket SLA (overdue = still-open, aged past cutoff).
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { openDb } from '../src/db'
|
||||
import { seedIfEmpty } from '../src/seed'
|
||||
import { createClient } from '../src/repos-clients'
|
||||
import { createTicket, updateTicket, listTickets, overdueCount } from '../src/repos-tickets'
|
||||
|
||||
const iso = (daysAgo: number) => new Date(Date.now() - daysAgo * 86_400_000).toISOString().slice(0, 10)
|
||||
|
||||
describe('ticket SLA overdue filter (D28)', () => {
|
||||
it('counts/lists only still-open tickets opened on or before the cutoff', async () => {
|
||||
const db = openDb(':memory:'); await seedIfEmpty(db)
|
||||
const c = await createClient(db, 'u1', { name: 'A', stateCode: '32' })
|
||||
const old = await createTicket(db, 'u1', { clientId: c.id, description: 'old open', openedOn: iso(20) })
|
||||
await createTicket(db, 'u1', { clientId: c.id, description: 'fresh open', openedOn: iso(2) })
|
||||
const oldClosed = await createTicket(db, 'u1', { clientId: c.id, description: 'old but closed', openedOn: iso(30) })
|
||||
await updateTicket(db, 'u1', oldClosed.id, { status: 'closed' })
|
||||
|
||||
const cutoff = iso(7) // SLA window = 7 days
|
||||
expect(await overdueCount(db, cutoff)).toBe(1) // only the 20-day-old open one
|
||||
|
||||
const page = await listTickets(db, { overdueBefore: cutoff })
|
||||
expect(page.total).toBe(1)
|
||||
expect(page.tickets[0]!.id).toBe(old.id)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue