feat: improve reports filters and ticket flows
This commit is contained in:
parent
9c74e10675
commit
15d11b6b12
29 changed files with 437 additions and 140 deletions
|
|
@ -51,6 +51,8 @@ export type ReportArtifact = {
|
|||
type BaseOptions = {
|
||||
range?: string
|
||||
companyId?: string | null
|
||||
dateFrom?: string | null
|
||||
dateTo?: string | null
|
||||
}
|
||||
|
||||
export async function buildHoursWorkbook(
|
||||
|
|
@ -61,6 +63,8 @@ export async function buildHoursWorkbook(
|
|||
tenantId: ctx.tenantId,
|
||||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
type Item = {
|
||||
|
|
@ -120,6 +124,174 @@ export async function buildHoursWorkbook(
|
|||
options.companyId ? `-${options.companyId}` : ""
|
||||
}${options.search ? `-${encodeURIComponent(options.search)}` : ""}.xlsx`
|
||||
|
||||
const arrayBuffer = workbook.buffer.slice(workbook.byteOffset, workbook.byteOffset + workbook.byteLength) as ArrayBuffer
|
||||
return {
|
||||
fileName,
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
buffer: arrayBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildCategoryInsightsWorkbook(
|
||||
ctx: ConvexReportContext,
|
||||
options: BaseOptions
|
||||
): Promise<ReportArtifact> {
|
||||
const report = await ctx.client.query(api.reports.categoryInsights, {
|
||||
tenantId: ctx.tenantId,
|
||||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
const categories = (report.categories ?? []) as Array<{
|
||||
name: string
|
||||
total: number
|
||||
resolved: number
|
||||
topAgent: { name: string | null; total: number } | null
|
||||
}>
|
||||
|
||||
const totalTickets = typeof report.totalTickets === "number"
|
||||
? report.totalTickets
|
||||
: categories.reduce((acc, item) => acc + (item.total ?? 0), 0)
|
||||
|
||||
const summaryRows: Array<Array<unknown>> = [
|
||||
["Relatório", "Categorias"],
|
||||
["Período", report.rangeDays ? `Últimos ${report.rangeDays} dias` : options.range ?? "90d"],
|
||||
["Total de tickets", totalTickets],
|
||||
]
|
||||
if (options.companyId) summaryRows.push(["EmpresaId", options.companyId])
|
||||
if (report.spotlight) {
|
||||
summaryRows.push(["Destaque", report.spotlight.categoryName])
|
||||
summaryRows.push(["Tickets no destaque", report.spotlight.tickets ?? 0])
|
||||
if (report.spotlight.agentName) {
|
||||
summaryRows.push(["Agente destaque", report.spotlight.agentName])
|
||||
}
|
||||
}
|
||||
|
||||
const categoryRows = categories.map((category) => {
|
||||
const resolvedRate = category.total > 0 ? (category.resolved / category.total) * 100 : null
|
||||
return [
|
||||
category.name,
|
||||
category.total,
|
||||
category.resolved,
|
||||
resolvedRate === null ? null : Number(resolvedRate.toFixed(1)),
|
||||
category.topAgent?.name ?? "Sem responsável",
|
||||
category.topAgent?.total ?? 0,
|
||||
]
|
||||
})
|
||||
|
||||
const workbook = buildXlsxWorkbook([
|
||||
{ name: "Resumo", headers: ["Item", "Valor"], rows: summaryRows },
|
||||
{
|
||||
name: "Categorias",
|
||||
headers: ["Categoria", "Tickets", "Resolvidos", "% resolvidos", "Agente destaque", "Tickets agente"],
|
||||
rows: categoryRows.length > 0 ? categoryRows : [["—", 0, 0, null, "—", 0]],
|
||||
},
|
||||
])
|
||||
|
||||
const fileName = `category-insights-${ctx.tenantId}-${report.rangeDays ?? "90"}d${
|
||||
options.companyId ? `-${options.companyId}` : ""
|
||||
}.xlsx`
|
||||
|
||||
const arrayBuffer = workbook.buffer.slice(workbook.byteOffset, workbook.byteOffset + workbook.byteLength) as ArrayBuffer
|
||||
return {
|
||||
fileName,
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
buffer: arrayBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildMachineCategoryWorkbook(
|
||||
ctx: ConvexReportContext,
|
||||
options: BaseOptions & { machineId?: string | null; userId?: string | null }
|
||||
): Promise<ReportArtifact> {
|
||||
const response = await ctx.client.query(api.reports.ticketsByMachineAndCategory, {
|
||||
tenantId: ctx.tenantId,
|
||||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
machineId: options.machineId ? (options.machineId as Id<"machines">) : undefined,
|
||||
userId: options.userId ? (options.userId as Id<"users">) : undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
}) as {
|
||||
rangeDays: number
|
||||
items: Array<{
|
||||
date: string
|
||||
machineHostname: string | null
|
||||
machineId: string | null
|
||||
companyName: string | null
|
||||
categoryName: string
|
||||
total: number
|
||||
}>
|
||||
}
|
||||
|
||||
const items = response.items ?? []
|
||||
const summaryRows: Array<Array<unknown>> = [
|
||||
["Relatório", "Máquinas x categorias"],
|
||||
[
|
||||
"Período",
|
||||
response.rangeDays && response.rangeDays > 0
|
||||
? `Últimos ${response.rangeDays} dias`
|
||||
: options.range ?? "30d",
|
||||
],
|
||||
["Total de registros", items.length],
|
||||
]
|
||||
if (options.companyId) summaryRows.push(["EmpresaId", options.companyId])
|
||||
if (options.machineId) summaryRows.push(["MáquinaId", options.machineId])
|
||||
if (options.userId) summaryRows.push(["SolicitanteId", options.userId])
|
||||
|
||||
const machineAggregation = new Map<
|
||||
string,
|
||||
{ machine: string; company: string; total: number; categories: Set<string> }
|
||||
>()
|
||||
for (const item of items) {
|
||||
const key = item.machineId ?? item.machineHostname ?? "sem-maquina"
|
||||
if (!machineAggregation.has(key)) {
|
||||
machineAggregation.set(key, {
|
||||
machine: item.machineHostname ?? key,
|
||||
company: item.companyName ?? "—",
|
||||
total: 0,
|
||||
categories: new Set<string>(),
|
||||
})
|
||||
}
|
||||
const entry = machineAggregation.get(key)!
|
||||
entry.total += item.total
|
||||
entry.categories.add(item.categoryName)
|
||||
}
|
||||
|
||||
const perMachineRows = Array.from(machineAggregation.values())
|
||||
.sort((a, b) => b.total - a.total)
|
||||
.map((entry) => [entry.machine, entry.company, entry.total, Array.from(entry.categories).join(", ")])
|
||||
|
||||
const occurrencesRows = items.map((item) => [
|
||||
item.date,
|
||||
item.machineHostname ?? "Sem identificação",
|
||||
item.companyName ?? "—",
|
||||
item.categoryName,
|
||||
item.total,
|
||||
])
|
||||
|
||||
const workbook = buildXlsxWorkbook([
|
||||
{ name: "Resumo", headers: ["Item", "Valor"], rows: summaryRows },
|
||||
{
|
||||
name: "Máquinas",
|
||||
headers: ["Máquina", "Empresa", "Tickets", "Categorias"],
|
||||
rows: perMachineRows.length > 0 ? perMachineRows : [["—", "—", 0, "—"]],
|
||||
},
|
||||
{
|
||||
name: "Ocorrências",
|
||||
headers: ["Data", "Máquina", "Empresa", "Categoria", "Total"],
|
||||
rows: occurrencesRows.length > 0 ? occurrencesRows : [["—", "—", "—", "—", 0]],
|
||||
},
|
||||
])
|
||||
|
||||
const fileName = `machine-category-${ctx.tenantId}-${options.range ?? response.rangeDays ?? "30"}d${
|
||||
options.companyId ? `-${options.companyId}` : ""
|
||||
}.xlsx`
|
||||
|
||||
const arrayBuffer = workbook.buffer.slice(workbook.byteOffset, workbook.byteOffset + workbook.byteLength) as ArrayBuffer
|
||||
return {
|
||||
fileName,
|
||||
|
|
@ -137,6 +309,8 @@ export async function buildBacklogWorkbook(
|
|||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
const summaryRows: Array<Array<unknown>> = [
|
||||
|
|
@ -200,6 +374,8 @@ export async function buildSlaWorkbook(
|
|||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
const summaryRows: Array<Array<unknown>> = [
|
||||
|
|
@ -248,6 +424,8 @@ export async function buildCsatWorkbook(
|
|||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
const summaryRows: Array<Array<unknown>> = [
|
||||
|
|
@ -300,6 +478,8 @@ export async function buildTicketsByChannelWorkbook(
|
|||
viewerId: ctx.viewerId,
|
||||
range: options.range,
|
||||
companyId: (options.companyId ?? undefined) as Id<"companies"> | undefined,
|
||||
dateFrom: options.dateFrom ?? undefined,
|
||||
dateTo: options.dateTo ?? undefined,
|
||||
})
|
||||
|
||||
const CHANNEL_PT: Record<string, string> = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue