Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

06 — Screen Specifications (Reports Module)

Detailed, per-state specifications for the five screens. Wire contracts in 12_API_Mapping.md; shared components in 00-shared/03; tokens in 00-shared/02 (M3, proposed). All server quotes from src/modules/reports/** unless noted.


S1 — Report Catalog (/reports)

Purpose

Pick a report template. Static content derived from the server enum (report-job.schema.ts:7-11).

Layout (mobile → tablet)

  • AppBar: "Reports" (title), optional filter menu (forward-looking).
  • List (AppListTile × 3):
    1. Report card — "Per-student marksheet with grades (A+…D)". Badge: needs student + exam.
    2. Attendance summary — "Counts per status for a class/period". Badge: can be scheduled daily/weekly.
    3. Fee summary — "Collections: paid / pending / overdue". Badge: no params.
  • Below list: muted row "Result delivery: PDF/CSV coming soon" (planned).

Data

Static table (no API). Sources: report-job.schema.ts:7-11, generate-report.dto.ts:6-33, reports.service.ts:85-160.

States

StateRender
idle3 tiles
permission-gapmodule hidden (no report.read) (forward-looking)
offlineAppOfflineBanner; tiles still render (static)

Permissions / events

  • View: report.read (permissions.constants.ts:40).
  • reports.catalog.select(type) (proposed).

Accessibility / motion

  • Tiles: min 48 dp, semantics label per tile; m-fast press ripple; page enter m-base fade-slide (00-shared/08).

S2 — Generate Report Form (/reports/new)

Purpose

Collect params per 08_Form_Specifications.md; POST and hand off to job detail.

Layout

  • AppBar: "New report" + back.
  • Fixed select (non-editable): report type from catalog selection.
  • Param fields per type (08):
    • report_card: student picker + exam picker (both required by service — reports.service.ts:91).
    • attendance_summary: class picker (optional), start date, end date (optional; generate-report.dto.ts:17-33).
    • fee_summary: no fields; info card "No parameters needed".
  • Summary row: params → chips (reports.service.ts:31-33 echoes params).
  • Primary CTA: "Generate report" full-width AppButton.

Submission contract

POST /reports/generate body = GenerateReportDto (generate-report.dto.ts:5-34):

{ "type": "attendance_summary", "classId": "…", "startDate": "2026-08-01", "endDate": "2026-08-31" }

Response: {jobId, status: "queued"} (reports.service.ts:43).

States

StateRender
idleform
validatinginline errors (presence for report_card — DTO does NOT enforce presence, only Mongo-Id format: generate-report.dto.ts:11-13,22-23)
submittingbutton spinner, fields disabled (prevents duplicate job — server has no idempotency key)
successnavigate S4 with jobId
error 400inline per field (invalid type enum, malformed MongoId)
error 429countdown, no auto-retry (00-shared/10)
error 5xxAppSnackbar + form preserved (re-POST is safe — new job per POST)
offlineAppOfflineBanner; submit blocked

Behavior rules

  1. Presence validation for studentId/examId on report_card BEFORE POST — the server fails the job (async), not the request: "studentId and examId required" (reports.service.ts:91). Client must not ship that UX.
  2. No double-submit while in flight.
  3. Date fields sent as strings (generate-report.dto.ts:27-33), no format validation server-side — client sends ISO YYYY-MM-DD; server builds Mongo $gte/$lte (reports.service.ts:120-122).

Permissions / analytics

  • Submit: report.generate (permissions.constants.ts:39) — server does not enforce today (reports.controller.ts:9 only JWT) (forward-looking).
  • reports.generate.submit|success|failure (proposed).

S3 — Job List (/reports/jobs)

Purpose

Recent jobs, newest first.

Data — known client gap

No GET /reports endpoint exists. Server only has GET /reports/:jobId (reports.controller.ts:20-24). The {tenantId, createdAt:-1} index (report-job.schema.ts:48) implies list queries are intended. Client strategy:

  • Local persisted history of submitted jobIds (max 50, oldest evicted);
  • Poll only rows currently visible;
  • (forward-looking): add GET /reports?status=queued|processing|completed|failed for full history + scheduled jobs visibility.

Layout

  • AppBar "Report jobs".
  • Filter chips (forward-looking): All / Running / Completed / Failed.
  • List rows (AppListTile):
    • Leading: status icon (queue/gear/check/cross).
    • Title: type label.
    • Subtitle: createdAt (relative), jobId short.
    • Trailing: status AppBadge (queued gray, processing amber, completed green, failed red — enum report-job.schema.ts:13-18); source badge "Scheduled" when from scheduler (local heuristic (planned) — no actorId stored on job doc).
  • Pull-to-refresh: re-poll visible rows.

States

StateRender
loadingAppSkeleton rows
emptyAppEmptyState "No reports yet" + CTA → catalog
errorbanner + retry (poll failures do not clear list)

Behavior

  • Tap row → S4.
  • Rows in terminal state are never re-polled in-session.
  • Retry action on failed rows: re-POST original params from job doc (report-job.schema.ts:33) → new jobId, row prepended.

Permissions / analytics

  • report.read (forward-looking); reports.job.list (proposed).

S4 — Job Detail / Progress (/reports/jobs/:jobId)

Purpose

Live lifecycle view + result entry. The polling hub (10_Interaction_Specification).

Data

GET /reports/:jobId (reports.controller.ts:20-24) → full job doc (report-job.schema.ts:21-43):

{
  "_id": "…", "tenantId": "…", "type": "fee_summary", "status": "completed",
  "params": {"type":"fee_summary"}, "result": {"totalInvoices": 3, "totalCollected": 1500,
  "totalPending": 400, "totalOverdue": 100}, "completedAt": "2026-08-03T…",
  "createdAt": "…", "updatedAt": "…", "version": 0
}

Layout

  1. Header card: type label + status badge + createdAt + short jobId.
  2. Params card: params as chips (empty → "No parameters").
  3. Status card (status-dependent, see table).
  4. Result card (completed only).
  5. Actions: Retry (failed), "New report from these params" (completed), Download/Share (planned).

Status-dependent UI

StatusUIPolling
queued"Waiting in queue" + indeterminate progress rowevery 2 s
processing"Generating…" + indeterminate progress row + elapsed timerevery 2 s
completedsuccess icon; result card; CTAsstop
failederror icon; error text from error field (report-job.schema.ts:39); Retrystop

Notes:

  • No progress % — server has none; indeterminate only.
  • Elapsed timer computed client-side from createdAt.
  • Terminal state is sticky: no re-poll, cached doc until screen exit.

States

StateRender
loadingfull skeleton
404"Report not found" empty state + back (reports.service.ts:48)
network failbanner; polling paused; manual "Retry check"
offlinebanner + last-good doc
cross-tenant 404same "not found" (server scopes by tenant via BaseRepository — no leak, base.repository.ts:21-23)

Permissions / analytics

  • report.read (forward-looking).
  • reports.job.status(jobId,status), reports.job.retry(jobId) (proposed).

S5 — Result View (/reports/jobs/:jobId/result)

Purpose

Render completed result per type; export (planned).

Result renderers (from reports.service.ts:96-160)

Report card (reports.service.ts:96-107):

  • Header: studentName (⚠ currently admission number, :98), examId, generatedAt.
  • Subject table: per row subjects[] (name, marks, max, grade — shape from result.service.ts:68-91).
  • Footer metrics: totalMarksObtained / totalMaximumMarks, percentage, grade badge (bands A+…D, result.service.ts:130-136).

Attendance summary (reports.service.ts:131-136):

  • Metric cards: total + per-status counts (summary keyed by status — present/late/absent/etc. as stored by attendance module).
  • Period line: classId, period.startDate/endDate (nulls → "All time").

Fee summary (reports.service.ts:154-158):

  • 4 metric cards: totalInvoices, totalCollected, totalPending, totalOverdue (numbers from invoiceRepo.find({}) + paid/due math :142-152).
  • Derived: collection rate = collected ÷ totalInvoices (client-derived).

States

StateRender
readyrenderer above
completed but result missingempty state (e.g. zero attendance) — result may be {} (zero-record summaries still complete, :131-136)
not completedguard: redirect to S4 (result only reachable when status == completed)
offlinelast-good cached result + banner

Export (planned)

  • "Download CSV"/"Share PDF" — NO endpoint today. Blueprint: GET /reports/:jobId/download (04-Modules/Reports.md:26), streamed (:49), files under sl/{tenantId}/reports/{uuid} TTL (:50).
  • Until then: hide export CTAs or show "coming soon" tooltip.

Permissions / analytics

  • report.read; reports.result.open|download (proposed).

S6 — Scheduled Reports Admin (planned)

  • Scheduler enqueues attendance reports daily/weekly today (attendance-report.job.ts:13-24), but there is no UI to configure.
  • Future screen: schedule list + CRUD (perm scheduler.read/create/delete, permissions.constants.ts:94-96); result surfaced through job list.