reports: calls per day, missed, busiest hours, queue table
ci / check (push) Successful in 56s
ci / images (push) Successful in 35s

GET /api/reports/summary and /api/reports/queues (company-time-zone days, last 30 by
default, a year at most) and the Reports page: totals, stacked bars per day, missed per
day, a weekday x hour heatmap and the queue table. Plain divs, no chart library.

Task: M11-T6
This commit is contained in:
iamdoubz committed 2026-10-19 22:09:43 -05:00
1 parent 60846afddd
commit 29ee4ba105
13 files changed
+714 -3

No files matched your search

+16
View File
@@ -78,3 +78,19 @@ export type RecordingDto = {
toNumber: string | null;
} | null;
};
/** GET /api/reports/summary and /api/reports/queues (04 §7.3). Both default to the last 30 days. */
export const ReportRangeSchema = z
.object({ from: z.iso.date().optional(), to: z.iso.date().optional() })
.refine((r) => !r.from || !r.to || r.from <= r.to, { message: 'From is after To' });
export type ReportRange = z.input<typeof ReportRangeSchema>;
export type ReportSummary = {
from: string;
to: string;
/** Every day in the range, empty ones too. Missed = missed, voicemail or abandoned. */
days: { date: string; inbound: number; outbound: number; internal: number; missed: number }[];
totals: { calls: number; missed: number; avgTalkSeconds: number | null };
/** Calls by weekday (0 = Sunday) and hour, in the company time zone: hours[day][hour]. */
hours: number[][];
};
+8
View File
@@ -81,3 +81,11 @@ export type QueueStats = {
*/
withinSlaPercent: number | null;
};
/** GET /api/reports/queues: one row per queue. */
export type QueueReportRow = QueueStats & {
id: string;
number: string;
name: string;
slaSeconds: number;
};