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

15 — Flutter Implementation Guide (Scheduler Module)

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.fromJson mirrors scheduler.service.ts:152-186 only — no status fields exist yet (09 B2); enrich map filled from E4 when it ships.

3. Blocs

  • JobsBloc: load(), refresh({force}), Timer.periodic(60 s) started on Loaded, cancelled in close(); single-flight + coalescing; background pause via WidgetsBindingObserver (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+$ mirroring dto:20-24; duplicate warn by comparing against current JobsBloc rows (08 §F1).

4. Key widgets

WidgetImpl notes
JobStatusBadgeicon+text always; unknown grey until E4 (07 C1)
CronChipmonospace pattern + humanized via the C2 map; invalid state for F1 input
QueueChipfamily colour coding from queue.constants.ts:1-17 (07 C3)
DLQCardfields 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 lint before any backend pairing change