views: criar página /views com gráficos (Canais movido do dashboard, CSAT distribuição, Filas abertas); dashboard: trocar por gráfico Abertos x Resolvidos (últimos 7/30/90 dias); reports: nova query openedResolvedByDay

This commit is contained in:
Esdras Renan 2025-10-07 17:28:01 -03:00
parent 5b14ecbe0f
commit 88b65c3e15
5 changed files with 317 additions and 2 deletions

View file

@ -226,6 +226,52 @@ export const csatOverview = query({
},
});
export const openedResolvedByDay = query({
args: { tenantId: v.string(), viewerId: v.id("users"), range: v.optional(v.string()), companyId: v.optional(v.id("companies")) },
handler: async (ctx, { tenantId, viewerId, range, companyId }) => {
const viewer = await requireStaff(ctx, viewerId, tenantId);
let tickets = await fetchScopedTickets(ctx, tenantId, viewer);
if (companyId) tickets = tickets.filter((t) => t.companyId === companyId)
const days = range === "7d" ? 7 : range === "30d" ? 30 : 90;
const end = new Date();
end.setUTCHours(0, 0, 0, 0);
const endMs = end.getTime() + ONE_DAY_MS;
const startMs = endMs - days * ONE_DAY_MS;
const opened: Record<string, number> = {}
const resolved: Record<string, number> = {}
// pre-fill buckets
for (let i = days - 1; i >= 0; i--) {
const d = new Date(endMs - (i + 1) * ONE_DAY_MS)
const key = formatDateKey(d.getTime())
opened[key] = 0
resolved[key] = 0
}
for (const t of tickets) {
if (t.createdAt >= startMs && t.createdAt < endMs) {
const key = formatDateKey(t.createdAt)
opened[key] = (opened[key] ?? 0) + 1
}
if (t.resolvedAt && t.resolvedAt >= startMs && t.resolvedAt < endMs) {
const key = formatDateKey(t.resolvedAt)
resolved[key] = (resolved[key] ?? 0) + 1
}
}
const series: Array<{ date: string; opened: number; resolved: number }> = []
for (let i = days - 1; i >= 0; i--) {
const d = new Date(endMs - (i + 1) * ONE_DAY_MS)
const key = formatDateKey(d.getTime())
series.push({ date: key, opened: opened[key] ?? 0, resolved: resolved[key] ?? 0 })
}
return { rangeDays: days, series }
},
})
export const backlogOverview = query({
args: { tenantId: v.string(), viewerId: v.id("users"), range: v.optional(v.string()), companyId: v.optional(v.id("companies")) },
handler: async (ctx, { tenantId, viewerId, range, companyId }) => {