feat: add SLA category breakdown report
This commit is contained in:
parent
6ab8a6ce89
commit
a62f3d5283
8 changed files with 231 additions and 10 deletions
|
|
@ -60,6 +60,20 @@ function buildQueue(overrides: Partial<Doc<"queues">>): Doc<"queues"> {
|
|||
return { ...(base as Doc<"queues">), ...overrides }
|
||||
}
|
||||
|
||||
function buildCategory(overrides: Partial<Doc<"ticketCategories">>): Doc<"ticketCategories"> {
|
||||
const base: Record<string, unknown> = {
|
||||
_id: "category_base" as Id<"ticketCategories">,
|
||||
tenantId: TENANT_ID,
|
||||
name: "Onboarding",
|
||||
slug: "onboarding",
|
||||
description: null,
|
||||
order: 0,
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
return { ...(base as Doc<"ticketCategories">), ...overrides }
|
||||
}
|
||||
|
||||
describe("convex.reports.slaOverview", () => {
|
||||
const requireStaffMock = vi.mocked(requireStaff)
|
||||
const FIXED_NOW = Date.UTC(2024, 4, 8, 12, 0, 0)
|
||||
|
|
@ -83,26 +97,38 @@ describe("convex.reports.slaOverview", () => {
|
|||
_id: "queue_1" as Id<"queues">,
|
||||
name: "Suporte Nível 1",
|
||||
})
|
||||
const category = buildCategory({
|
||||
_id: "category_1" as Id<"ticketCategories">,
|
||||
name: "Admissões",
|
||||
})
|
||||
|
||||
const tickets = [
|
||||
buildTicket({
|
||||
_id: "ticket_open" as Id<"tickets">,
|
||||
status: "PENDING",
|
||||
queueId: queue._id,
|
||||
categoryId: category._id,
|
||||
createdAt: Date.UTC(2024, 4, 7, 9, 0, 0),
|
||||
dueAt: Date.UTC(2024, 4, 7, 11, 0, 0),
|
||||
slaSnapshot: { categoryId: category._id, categoryName: category.name, priority: "MEDIUM" },
|
||||
slaResponseStatus: "pending",
|
||||
slaSolutionStatus: "pending",
|
||||
}),
|
||||
buildTicket({
|
||||
_id: "ticket_resolved" as Id<"tickets">,
|
||||
status: "RESOLVED",
|
||||
queueId: queue._id,
|
||||
categoryId: category._id,
|
||||
createdAt: Date.UTC(2024, 4, 6, 8, 0, 0),
|
||||
firstResponseAt: Date.UTC(2024, 4, 6, 8, 30, 0),
|
||||
resolvedAt: Date.UTC(2024, 4, 6, 10, 0, 0),
|
||||
slaSnapshot: { categoryId: category._id, categoryName: category.name, priority: "MEDIUM" },
|
||||
slaResponseStatus: "met",
|
||||
slaSolutionStatus: "met",
|
||||
}),
|
||||
]
|
||||
|
||||
const ctx = createReportsCtx({ tickets, queues: [queue] }) as Parameters<typeof slaOverviewHandler>[0]
|
||||
const ctx = createReportsCtx({ tickets, queues: [queue], categories: [category] }) as Parameters<typeof slaOverviewHandler>[0]
|
||||
|
||||
const result = await slaOverviewHandler(ctx, {
|
||||
tenantId: TENANT_ID,
|
||||
|
|
@ -115,6 +141,18 @@ describe("convex.reports.slaOverview", () => {
|
|||
expect(result.response).toEqual({ averageFirstResponseMinutes: 30, responsesRegistered: 1 })
|
||||
expect(result.resolution).toEqual({ averageResolutionMinutes: 120, resolvedCount: 1 })
|
||||
expect(result.queueBreakdown).toEqual([{ id: queue._id, name: queue.name, open: 1 }])
|
||||
expect(result.categoryBreakdown).toEqual([
|
||||
{
|
||||
categoryId: String(category._id),
|
||||
categoryName: category.name,
|
||||
priority: "MEDIUM",
|
||||
total: 2,
|
||||
responseMet: 1,
|
||||
solutionMet: 1,
|
||||
responseRate: 0.5,
|
||||
solutionRate: 0.5,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ type ReportsCtxOptions = {
|
|||
tickets?: Doc<"tickets">[]
|
||||
createdRangeTickets?: Doc<"tickets">[]
|
||||
queues?: Doc<"queues">[]
|
||||
categories?: Doc<"ticketCategories">[]
|
||||
companies?: Map<string, Doc<"companies">>
|
||||
users?: Map<string, Doc<"users">>
|
||||
ticketEventsByTicket?: Map<string, Array<{ type: string; payload?: unknown; createdAt: number }>>
|
||||
|
|
@ -38,6 +39,7 @@ export function createReportsCtx({
|
|||
tickets = [],
|
||||
createdRangeTickets = tickets,
|
||||
queues = [],
|
||||
categories = [],
|
||||
companies = new Map<string, Doc<"companies">>(),
|
||||
users = new Map<string, Doc<"users">>(),
|
||||
ticketEventsByTicket = new Map<string, Array<{ type: string; payload?: unknown; createdAt: number }>>(),
|
||||
|
|
@ -78,6 +80,18 @@ export function createReportsCtx({
|
|||
}
|
||||
}
|
||||
|
||||
if (table === "ticketCategories") {
|
||||
return {
|
||||
withIndex: vi.fn((_indexName: string, cb?: (builder: typeof noopIndexBuilder) => unknown) => {
|
||||
cb?.(noopIndexBuilder)
|
||||
return {
|
||||
collect: vi.fn(async () => categories),
|
||||
}
|
||||
}),
|
||||
collect: vi.fn(async () => categories),
|
||||
}
|
||||
}
|
||||
|
||||
if (table === "ticketEvents") {
|
||||
return {
|
||||
withIndex: vi.fn((_indexName: string, cb?: (builder: { eq: (field: unknown, value: unknown) => unknown }) => unknown) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue