Feat/call statistics - #59
Conversation
…ed through sample data
There was a problem hiding this comment.
Pull request overview
Adds end-to-end “call statistics” functionality (admin-only) including new backend analytics endpoints and a new admin dashboard page to visualize call volume and resolution metrics.
Changes:
- Added backend analytics model + admin-protected routes under
/api/calls/analytics/*. - Added a new admin UI page at
/admin/calls-statisticswith charts and filters, plus a dashboard link. - Minor admin dashboard UI tweaks (fraud report tabs styling/labeling; show assigned fraud reports only for non-admin users).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| 8cbc_frontend/lib/api/call-statistics.ts | Adds frontend API client + types for call statistics endpoints. |
| 8cbc_frontend/components/dashboard/admin/AssignedFraudReports.tsx | Updates tabs layout/styling and label for “Under Review”. |
| 8cbc_frontend/app/(admin)/admin/page.tsx | Adds navigation action to call statistics; gates AssignedFraudReports to non-admins. |
| 8cbc_frontend/app/(admin)/admin/calls-statistics/page.tsx | New admin call statistics dashboard page with charts and month filtering. |
| 8cbc_backend/model/calls/call-statistics.model.ts | Implements analytics aggregation queries and summary composition. |
| 8cbc_backend/controllers/calls/calls.controller.ts | Mounts the call statistics controller under the calls router. |
| 8cbc_backend/controllers/calls/call-statistics.controller.ts | New admin-only analytics endpoints with zod query validation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
XuanHanTan-School
left a comment
There was a problem hiding this comment.
Very nice feature but some usability improvements in addition to algorithmic improvements:
- Completion vs no-show vs cancellation graph could be displayed in a way that does not induce cognitive overload - Try a 7 day rolling average as the main highlighted line, and the daily values as dotted low-opacity lines.
- Can put some key call stats on the home page
| if (completedCallsData.length > 0) { | ||
| const durations = completedCallsData | ||
| .map(call => { | ||
| // Use history timestamp if available, otherwise use updatedAt | ||
| const completionTimestamp = call.completionAt || call.updatedAt; | ||
| if (!completionTimestamp) return NaN; | ||
| const created = new Date(call.createdAt).getTime(); | ||
| const completed = new Date(completionTimestamp).getTime(); | ||
| return completed - created; | ||
| }) | ||
| .filter((ms) => Number.isFinite(ms) && ms >= 0) | ||
| .map((ms) => ms / 1000); |
There was a problem hiding this comment.
Shouldn't we compare the average scheduled time to call start time? Since booking is subject to customer preferences. Also please use SQL aggregation queries.
| const formatDuration = (iso: string) => { | ||
| const match = iso.match(/P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?/); | ||
| if (!match) return iso; | ||
|
|
||
| const [, d, h, m, s] = match; | ||
|
|
||
| const days = parseInt(d || "0", 10); | ||
| const hours = parseInt(h || "0", 10); | ||
| const minutes = parseInt(m || "0", 10); | ||
| const seconds = parseInt(s || "0", 10); | ||
|
|
||
| const totalSeconds = | ||
| days * 86_400 + hours * 3_600 + minutes * 60 + seconds; | ||
|
|
||
| if (totalSeconds >= 86_400) { | ||
| const totalDays = totalSeconds / 86_400; | ||
| return totalDays >= 10 ? `${Math.round(totalDays)}d` : `${totalDays.toFixed(1)}d`; | ||
| } | ||
|
|
||
| if (totalSeconds >= 3_600) { | ||
| const totalHours = totalSeconds / 3_600; | ||
| return totalHours >= 10 ? `${Math.round(totalHours)}h` : `${totalHours.toFixed(1)}h`; | ||
| } | ||
|
|
||
| if (totalSeconds >= 60) { | ||
| const totalMinutes = totalSeconds / 60; | ||
| return `${Math.round(totalMinutes)}m`; | ||
| } | ||
|
|
||
| return `${seconds}s`; | ||
| }; |
There was a problem hiding this comment.
Why don't your API just return duration in seconds? Like 80000s for example. This would make it so much easier to parse.
…type, multiple fraud reports, calls between nov 2025 and feb 2026 for statistics
…DP2025_8CBC into feat/call-statistics
Complete call statistics frontend and backend.