2026-09-09 11:15:22 +05:30
|
|
|
---
|
2026-09-09 11:59:38 +05:30
|
|
|
description: 'Task list for 015-reporting-dashboards'
|
2026-09-09 11:15:22 +05:30
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Tasks: Reporting and Analytics Dashboards
|
|
|
|
|
|
|
|
|
|
**Input**: Design documents from `specs/015-reporting-dashboards/`
|
|
|
|
|
|
|
|
|
|
**Prerequisites**: [plan.md](./plan.md), [spec.md](./spec.md), [research.md](./research.md),
|
|
|
|
|
[data-model.md](./data-model.md),
|
|
|
|
|
[contracts/reports-api-contract.md](./contracts/reports-api-contract.md),
|
|
|
|
|
[quickstart.md](./quickstart.md)
|
|
|
|
|
|
|
|
|
|
**Organization**: Tasks are grouped by user story (US1 = P1 Management, US2 = P1 Product,
|
|
|
|
|
US3 = P2 Support, US4 = P2 AI). All four share the Foundational phase (schema, config, shared
|
|
|
|
|
helpers, module scaffolding) but are otherwise independent of each other.
|
|
|
|
|
|
|
|
|
|
## Format: `[ID] [P?] [Story] Description`
|
|
|
|
|
|
|
|
|
|
All file paths are relative to `supporthub-api/` (repo root).
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 1: Foundational (Blocking Prerequisites)
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T001 Add `REPORTING_DEFAULT_WINDOW_DAYS` (default `30`),
|
2026-09-09 11:15:22 +05:30
|
|
|
`REPORTING_SLA_RISK_THRESHOLD_MINUTES` (default `60`), and `REPORTING_TOP_N_LIMIT`
|
|
|
|
|
(default `10`) to `src/config/env.ts`, exposed via a new `reportingConfig` in
|
|
|
|
|
`src/config/reporting.ts` (or added to an existing config file, matching this codebase's
|
|
|
|
|
own per-feature config-file convention)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T002 Add the `ErrorCodeLookup` model to `prisma/schema.prisma` per data-model.md, generate
|
2026-09-09 11:15:22 +05:30
|
|
|
the migration via `prisma migrate diff --from-url <db-url> --to-schema-datamodel
|
2026-09-09 11:59:38 +05:30
|
|
|
./prisma/schema.prisma --script`, hand-write it into
|
2026-09-09 11:15:22 +05:30
|
|
|
`prisma/migrations/<timestamp>_add_error_code_lookup/migration.sql`, apply via `prisma
|
2026-09-09 11:59:38 +05:30
|
|
|
migrate deploy` against the throwaway test database (depends on T001 only in that both
|
2026-09-09 11:15:22 +05:30
|
|
|
are Foundational — no code dependency)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T003 Add `ai-support/knowledge/repository/error-code-lookup.repository.ts` —
|
2026-09-09 11:15:22 +05:30
|
|
|
`create(errorCodeId, productId)`, exported from the knowledge module's repository index
|
|
|
|
|
(depends on T002)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T004 [P] Call the new repository's `create(...)` from
|
2026-09-09 11:15:22 +05:30
|
|
|
`ai-support/knowledge/service/error-codes.service.ts`'s existing
|
|
|
|
|
`findKnownIssuesByErrorCode`, alongside (not replacing) 014's own
|
|
|
|
|
`knownErrorLookupsCounter.inc(...)` call at that same call site (depends on T003)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T005 [P] Add `platform/reports/mapper/date-range.ts` — parses/validates `from`/`to` query
|
2026-09-09 11:15:22 +05:30
|
|
|
params, defaulting via T001's `reportingConfig.defaultWindowDays`, throwing
|
|
|
|
|
`ValidationError` when `from > to` (depends on T001)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T006 [P] Add `platform/reports/mapper/rate.ts` — a shared `computeRate(numerator,
|
|
|
|
|
denominator): number | null` and `computeAverageSeconds(durations: number[]): number |
|
|
|
|
|
null` pair, both returning `null` (never `NaN`/`0`) when there's no qualifying data
|
2026-09-09 11:15:22 +05:30
|
|
|
(research.md §3) — no dependency, pure functions
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T007 Scaffold `platform/reports/schema/` (query-param zod schema using T005's date-range
|
2026-09-09 11:15:22 +05:30
|
|
|
parsing), `platform/reports/controller/reports.controller.ts` (empty methods to be filled
|
|
|
|
|
in per user story below), `platform/reports/routes/reports.routes.ts` registering all four
|
|
|
|
|
routes behind `requireRole('ADMIN')`, and update `platform/reports/index.ts` to export the
|
|
|
|
|
new public surface, replacing `generateSummaryReport`'s stub entirely (depends on T005,
|
|
|
|
|
T006)
|
|
|
|
|
|
|
|
|
|
**Checkpoint**: Config, schema, shared helpers, and module scaffolding in place. Each dashboard
|
|
|
|
|
can now be built independently.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 2: User Story 1 - Management sees organization-wide support health (Priority: P1)
|
|
|
|
|
|
|
|
|
|
**Goal**: `GET /admin/reports/management` returns real figures per data-model.md's
|
|
|
|
|
`ManagementDashboard` shape.
|
|
|
|
|
|
|
|
|
|
**Independent Test**: Quickstart Scenario 1.
|
|
|
|
|
|
|
|
|
|
### Tests for User Story 1
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T008 [P] [US1] Unit tests for T006's `computeRate`/`computeAverageSeconds` (empty input ->
|
2026-09-09 11:15:22 +05:30
|
|
|
`null`; a real mix -> the correct value) in
|
|
|
|
|
`tests/unit/platform/reports/rate-helpers.test.ts`
|
|
|
|
|
|
|
|
|
|
### Implementation for User Story 1
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T009 [US1] Add `platform/reports/repository/management.repository.ts` — one method per
|
2026-09-09 11:15:22 +05:30
|
|
|
research.md §2's Management table row (ticket counts by status, SLA-run outcome counts,
|
|
|
|
|
response/resolution duration row-fetches for T006 to average) (depends on T007)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T010 [US1] Add `ReportsService.getManagementDashboard(range)` composing T009's repository
|
2026-09-09 11:15:22 +05:30
|
|
|
calls into the `ManagementDashboard` shape, reusing the `Resolution.resolvedBy` convention
|
|
|
|
|
(research.md §4) for the AI-vs-human split (depends on T009)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T011 [US1] Wire `GET /admin/reports/management` to the controller/service (depends on T010)
|
|
|
|
|
- [x] T012 [US1] Integration test covering Quickstart Scenario 1 (real tickets in various
|
2026-09-09 11:15:22 +05:30
|
|
|
terminal states, a met and a breached SLA run, verified figure-by-figure; a no-activity
|
|
|
|
|
range returns all-zero counts and all-null rates) in
|
|
|
|
|
`tests/integration/platform-reports/management-dashboard.test.ts` (depends on T011)
|
|
|
|
|
|
|
|
|
|
**Checkpoint**: Quickstart Scenario 1 passes.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 3: User Story 2 - See support broken down by product (Priority: P1)
|
|
|
|
|
|
|
|
|
|
**Goal**: `GET /admin/reports/product/:externalProductId` returns real figures per
|
|
|
|
|
`ProductDashboard`.
|
|
|
|
|
|
|
|
|
|
**Independent Test**: Quickstart Scenario 2.
|
|
|
|
|
|
|
|
|
|
### Implementation for User Story 2
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T013 [P] [US2] Add `platform/reports/repository/product.repository.ts` — ticket/problem
|
2026-09-09 11:15:22 +05:30
|
|
|
queries scoped by `productId`, plus a query against T003's `ErrorCodeLookup` table for
|
|
|
|
|
the top-N ranking (`reportingConfig.topNLimit`) (depends on T007)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T014 [US2] Add `ReportsService.getProductDashboard(externalProductId, range)`, 404-ing via
|
2026-09-09 11:15:22 +05:30
|
|
|
`NotFoundError` when the product doesn't resolve (FR-006) before running any aggregation
|
|
|
|
|
query (depends on T013)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T015 [US2] Wire `GET /admin/reports/product/:externalProductId` (depends on T014)
|
|
|
|
|
- [x] T016 [US2] Integration test covering Quickstart Scenario 2 (two products' data never
|
2026-09-09 11:15:22 +05:30
|
|
|
cross-contaminating each other's figures; an unknown product 404s) in
|
|
|
|
|
`tests/integration/platform-reports/product-dashboard.test.ts` (depends on T015)
|
|
|
|
|
|
|
|
|
|
**Checkpoint**: Quickstart Scenario 2 passes.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 4: User Story 3 - Support sees team workload and performance (Priority: P2)
|
|
|
|
|
|
|
|
|
|
**Goal**: `GET /admin/reports/support` returns real figures per `SupportDashboard`.
|
|
|
|
|
|
|
|
|
|
**Independent Test**: Quickstart Scenario 3.
|
|
|
|
|
|
|
|
|
|
### Implementation for User Story 3
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T017 [P] [US3] Add `platform/reports/repository/support.repository.ts` — current
|
2026-09-09 11:15:22 +05:30
|
|
|
`Assignment` workload-by-agent query, `SLARun` at-risk/breached queries (`resolutionDueAt`
|
|
|
|
|
within `reportingConfig.slaRiskThresholdMinutes` of now, per research.md §2) (depends on
|
|
|
|
|
T007)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T018 [US3] Add `ReportsService.getSupportDashboard(range)` (depends on T017)
|
|
|
|
|
- [x] T019 [US3] Wire `GET /admin/reports/support` (depends on T018)
|
|
|
|
|
- [x] T020 [US3] Integration test covering Quickstart Scenario 3 (real per-agent assignment
|
2026-09-09 11:15:22 +05:30
|
|
|
counts; a near-due-but-not-breached run counted as at-risk, distinct from breached) in
|
|
|
|
|
`tests/integration/platform-reports/support-dashboard.test.ts` (depends on T019)
|
|
|
|
|
|
|
|
|
|
**Checkpoint**: Quickstart Scenario 3 passes.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 5: User Story 4 - See how well the AI is performing (Priority: P2)
|
|
|
|
|
|
|
|
|
|
**Goal**: `GET /admin/reports/ai` returns real figures per `AiDashboard`.
|
|
|
|
|
|
|
|
|
|
**Independent Test**: Quickstart Scenario 4.
|
|
|
|
|
|
|
|
|
|
### Tests for User Story 4
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T021 [P] [US4] Unit test: the confidence-distribution bucketing reuses
|
2026-09-09 11:15:22 +05:30
|
|
|
`decideConfidenceBand` (005-ai-support) against `aiConfig` defaults, not a reimplemented
|
|
|
|
|
threshold check, in `tests/unit/platform/reports/confidence-distribution.test.ts`
|
|
|
|
|
|
|
|
|
|
### Implementation for User Story 4
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T022 [US4] Add `platform/reports/repository/ai.repository.ts` — `AISupportSession` outcome
|
2026-09-09 11:15:22 +05:30
|
|
|
counts, `AIDiagnosis` confidence fetch, `AIKnowledgeReference` presence-per-session query,
|
|
|
|
|
`AIAction`/`AIActionResult` outcome counts (depends on T007)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T023 [US4] Add `ReportsService.getAiDashboard(range)`, bucketing confidence via
|
2026-09-09 11:15:22 +05:30
|
|
|
`decideConfidenceBand` + `aiConfig.defaultHighConfidence`/`defaultLowConfidence`
|
|
|
|
|
(research.md §7) (depends on T022, T021)
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T024 [US4] Wire `GET /admin/reports/ai` (depends on T023)
|
|
|
|
|
- [x] T025 [US4] Integration test covering Quickstart Scenario 4 (real AI sessions to mixed
|
2026-09-09 11:15:22 +05:30
|
|
|
outcomes, mixed tool results, a spread of diagnosis confidence values) in
|
|
|
|
|
`tests/integration/platform-reports/ai-dashboard.test.ts` (depends on T024)
|
|
|
|
|
|
|
|
|
|
**Checkpoint**: Quickstart Scenario 4 passes. All four dashboards work independently and
|
|
|
|
|
together — this feature's full scope.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 6: Polish & Cross-Cutting Concerns
|
|
|
|
|
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T026 [P] Update `specs/015-reporting-dashboards/checklists/requirements.md` Notes with any
|
2026-09-09 11:15:22 +05:30
|
|
|
implementation-time findings
|
2026-09-09 11:59:38 +05:30
|
|
|
- [x] T027 Run `npx tsx scripts/check-architecture.ts` and `npm run lint`/`npm run typecheck`
|
|
|
|
|
- [x] T028 Full regression: `npm run test:unit` then the full integration suite against real
|
2026-09-09 11:15:22 +05:30
|
|
|
Docker-provisioned Postgres/Redis, confirming nothing outside this feature regressed
|
|
|
|
|
(particularly `error-codes.service.ts`'s own existing tests, now touched by T004)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Dependencies & Execution Order
|
|
|
|
|
|
|
|
|
|
- **Foundational (Phase 1)**: No dependencies — BLOCKS all four user stories
|
|
|
|
|
- **User Story 1 (Phase 2)**: Depends on Foundational — independent of US2/US3/US4
|
|
|
|
|
- **User Story 2 (Phase 3)**: Depends on Foundational — independent of US1/US3/US4
|
|
|
|
|
- **User Story 3 (Phase 4)**: Depends on Foundational — independent of US1/US2/US4
|
|
|
|
|
- **User Story 4 (Phase 5)**: Depends on Foundational — independent of US1/US2/US3
|
|
|
|
|
- **Polish (Phase 6)**: Depends on all four user stories
|