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 (Scheduler Module)

Detailed specs for the Scheduled Jobs console. (proposed) markers mean the screen/endpoint does not exist in source; everything else cites file:line. Data shapes are quoted from scheduler.service.ts and dlq.setup.ts.


S1 — Scheduled Jobs List

Layout (desktop, ≥ 1024 dp)

┌──────────────────────────────────────────────────────────────┐
│ Admin Settings · Scheduled Jobs                    [+ New]   │
│ 10 default · 2 custom · 0 dead letters  (summary strip)      │
├──────────────────────────────────────────────────────────────┤
│ [status] job name          queue         cron        tz  ⋮   │
│ ● running  audit-flush      audit-write  */1 * * * *  UTC ⋮  │
│ ● ok       daily-digest     emails       0 9 * * *    UTC ⋮  │
│ ▲ failed   attendance-report-daily report-generate 0 7 * * * │
└──────────────────────────────────────────────────────────────┘
SpecDetail
DataGET /scheduler{queue, name, pattern, tz} (scheduler.service.ts:152-186); enrich (proposed) with lastRunAt, lastStatus, nextRunAt
Sortdefault: name asc; toggle group by queue (drop-down)
Status derivation(proposed) — from BullMQ job states of last run; today the API has no status field
Row tap→ S2 (/admin/scheduler/jobs/:queue/:name)
MenuRun now (S5) · View logs (S4) · Remove — remove calls DELETE /scheduler?queue=&name=&pattern= (scheduler.controller.ts:37-46, scheduler.service.ts:188-196)
Empty"No scheduled jobs — the 10 defaults register on boot (scheduler.service.ts:43-45)"
Refreshpull + auto-poll 60 s; offline → AppOfflineBanner
Permissionscheduler.read (403 → AppEmptyState permission variant)

S2 — Job Detail / Run History

SpecDetail
Headername, CronChip(pattern), tz chip, queue chip, status badge
Definition blockretention: removeOnComplete {age:3600, count:100}, removeOnFail {age: 7d} (scheduler.service.ts:145-146); global retry: attempts: 3, exponential backoff 5 s (bullmq.module.ts:60-65); trigger payload envelope {eventType, tenantId:'system', correlationId, actorId:'scheduler'} (scheduler.service.ts:136-142)
Run history(proposed) table: startedAt · status (ok/failed/in-progress) · duration · error excerpt. Source (proposed) GET /scheduler/runs?queue=&name=
Retention notehistory window is 1 h / 100 completed jobs for the trigger, and 14 d for failed jobs globally (bullmq.module.ts:63-64) — display accordingly
CTAsRun now · View logs · Remove schedule (confirm)

S3 — Create Custom Schedule

SpecDetail
TriggerFAB "+ New" (S1) → AppBottomSheet
Fieldsqueue (AppDropdown, 11 whitelisted: create-schedule.dto.ts:4-16), jobName (required, dto:30-32), pattern (5-field cron, dto:20-24; monospace; hint row of preset chips e.g. 0 6 * * *), tz (default UTC dto:38-41), payload (JSON textarea, optional)
Validationinline per field; server re-validates (dto:19-41); pattern regex error message "Invalid cron pattern (5 fields required)" (dto:22)
SubmitPOST /scheduler (scheduler.controller.ts:30-35) → queue.add with repeat {pattern, tz} (scheduler.service.ts:198-208)
Successsheet closes, snackbar, new row at top (optimistic, rollback on fail)
DuplicateregisterRepeatable skips existing name+pattern (scheduler.service.ts:128-133) — UI mirrors: warn if identical row exists

S4 — Job Logs

SpecDetail
Route/admin/scheduler/jobs/:queue/:name/logs
Source(proposed) GET /scheduler/jobs/:queue/:name/logs?level=&tail=
Contenttimestamp · level · message · jobId; monospace; auto-scroll; "tail live" AppSwitch
Filterlevel chips (debug/info/warn/error); default info
Empty"No logs kept — completed trigger jobs live 1 h (scheduler.service.ts:145)"
Noteworker-side logs exist via nestjs-pino (AGENTS.md stack); surfacing them is (proposed)

S5 — Manual Trigger Dialog

SpecDetail
Triggerrow menu "Run now" or S2 CTA
Contentconfirm: "Trigger daily-digestsend-daily-digest on queue emails now? (Deliveries may duplicate — worker idempotency must hold.)"
Action(proposed) POST /scheduler/:queue/:name/trigger → re-enqueues via job class (daily-digest.job.ts:12-25 pattern)
Statesin-flight (button spinner, dialog not dismissible twice), success snackbar "Triggered — watch run history", failure inline
Idempotency guardIdempotencyService (bullmq.module.ts:76) must de-dupe on replay — QA item 14 §6

S6 — Dead Letter Queue

SpecDetail
Route/admin/scheduler/dlq
Source(proposed) GET /scheduler/dlq — records written by setupDlqListener (dlq.setup.ts:5-27)
Card fieldsoriginalQueue, originalJobName, originalJobId, failedReason, attemptsMade, failedAt, data (JSON expandable)
ActionsReplay(proposed) POST /scheduler/dlq/:id/retry (re-add to original queue with same data); Delete → confirm dialog
Empty"No dead letters"
NoteDLQ condition: attemptsMade >= (job.opts.attempts ?? 3) (dlq.setup.ts:8-9); replay must preserve correlationId for idempotency