15 — Flutter Implementation Guide (Scheduler Module)
- 1. Module skeleton
- 2. Data layer
- 3. Blocs
- 4. Key widgets
- 5. Polling & background
- 6. Console behaviour gates
- 7. Tests
- 8. Delivery checklist
Build guide for the Scheduled Jobs console (
(proposed)screens S1-S6). Native mobile is excluded from PRD Phase 1 (web-first,PRODUCT_REQUIREMENTS_DOCUMENT.md:144) — this guide targets the Flutter web-first responsive client per 00-shared/11; the scheduler console is a superadmin platform surface (scheduler.controller.ts:24,31,38). Only E1-E3 exist server-side; E4-E8(proposed)— stub behind a repository interface and ship read-only until they land.
1. Module skeleton
features/scheduler/
├── data/
│ ├── scheduler_api.dart // E1-E3 + stubbed E4-E8 (proposed)
│ └── scheduler_repository.dart // console talks to this only
├── domain/
│ └── job_row.dart // {queue,name,pattern,tz} (scheduler.service.ts:152-186)
├── blocs/
│ ├── jobs_bloc.dart // list + poll + enrich (13 §1-3)
│ ├── job_detail_bloc.dart // S2
│ ├── create_schedule_bloc.dart // S3/F1
│ ├── dlq_bloc.dart // S6
│ └── logs_bloc.dart // S4
├── screens/ s1_jobs_list.dart · s2_job_detail.dart · s3_create_sheet.dart
│ s4_job_logs.dart · s6_dlq.dart
└── widgets/ job_status_badge.dart · cron_chip.dart · queue_chip.dart
schedule_summary_strip.dart · run_history_row.dart
job_definition_card.dart · dlq_card.dart · preset_cron_chips.dart
Routes: /admin/scheduler, /admin/scheduler/jobs/:queue/:name,
/admin/scheduler/jobs/:queue/:name/logs, /admin/scheduler/dlq (04 §5).
2. Data layer
class SchedulerApi {
Future<List<JobRow>> listJobs() // E1: GET /scheduler
Future<void> createJob(CreateScheduleDto dto) // E2: POST /scheduler
Future<void> removeJob(q, name, pattern) // E3: DELETE /scheduler
// (proposed — throw UnimplementedError until shipped):
// runs(q,name) E4 · logs(q,name) E5 · trigger(q,name) E6 · dlq() E7 · dlqRetry(id) E8
}
- Auth: Bearer JWT via shared client; 401 → refresh → replay (
00-shared/06 §3.6). - Error mapping per
13 §7(401 reauth, 403 hide, 429 backoff, 404 drop row, 5xx keep-last-good). JobRow.fromJsonmirrorsscheduler.service.ts:152-186only — no status fields exist yet (09 B2);enrichmap filled from E4 when it ships.
3. Blocs
JobsBloc:load(),refresh({force}),Timer.periodic(60 s)started on Loaded, cancelled inclose(); single-flight + coalescing; background pause viaWidgetsBindingObserver(13 §3).JobDetailBloc: header from E1 row + run history E4 (stub → empty state with retention note "triggers keep 1 h/100 completed, failed 7 d",scheduler.service.ts:145-146).CreateScheduleBloc: F1 fields; cron regex client-side^(\S+\s+){4}\S+$mirroringdto:20-24; duplicate warn by comparing against currentJobsBlocrows (08 §F1).
4. Key widgets
| Widget | Impl notes |
|---|---|
JobStatusBadge | icon+text always; unknown grey until E4 (07 C1) |
CronChip | monospace pattern + humanized via the C2 map; invalid state for F1 input |
QueueChip | family colour coding from queue.constants.ts:1-17 (07 C3) |
DLQCard | fields from dlq.setup.ts:12-21; expandable JSON data |
All stateless + controlled (07 reuse rules); tokens from 00-shared/02.
5. Polling & background
- 60 s poll on S1 only (09 B3); S2/S6 refresh on entry + pull.
AppLifecycleListener: pause timer on background, immediate staleness check on resume.- Rate budget: 1 req/min ≈ 60/h per tab — inside api tier (
00-shared/07 §4).
6. Console behaviour gates
- Without E4: S1 badges show
unknown, S2 shows retention note, strip shows "0 known" (09 B2) — never fake health. - Without E6: "Run now" disabled with tooltip "manual trigger pending" (13 §5).
- Without E8: DLQ replay button disabled (14 §9).
- Create/remove work today (E2/E3) — those are live, test them fully.
7. Tests
- Widget: badge states (ok/running/failed/missed/unknown), cron chip
humanizer for all 10 defaults (
scheduler.service.ts:48-119), F1 regex (5-field cron in/out). - Bloc: poll timer lifecycle, keep-last-good on 5xx, single-flight coalescing (13 §3).
- Integration (console,
(proposed)endpoints): repository stub contract — swap fake for real when E4-E8 ship.
8. Delivery checklist
- Reads E1; writes E2/E3 wired through repository
-
(proposed)endpoints stubbed, UI degrades honestly (07 C1 unknown) - a11y: badge icon+text, live regions, focus traps (11 §6)
-
Analytics
scheduler.{screen}.{action}(proposed)(05 §Analytics) -
npm run typecheck && npm run lintbefore any backend pairing change