{t('admin.jobs.title', 'Jobs')}

{t( 'admin.jobs.hint', 'Fires periodic + on-demand jobs. Consistency checks are safe to run at any time — they are read-only.' )}

{#if hasBatch}
{/if}
{#if loadError}

{loadError}

{:else if !jobs}

{t('common.loading', 'Loading…')}

{:else if jobs.length === 0}

{t('admin.jobs.none_registered', 'No jobs registered.')}

{:else} {#each jobs as job (job.name)} {@const runs = runsByJob[job.name]} {@const runsErr = runsErrorByJob[job.name]} {@const runsLoading = runsLoadingByJob[job.name]} {@const expandedRun = expandedRunByJob[job.name] ?? null} {@const canExpand = isRecoverable(job)} {#if expandedJob === job.name} {/if} {/each}
{t('admin.jobs.col_name', 'Name')} {t('admin.jobs.col_cadence', 'Cadence')} {t('admin.jobs.col_last_run', 'Last run')} {t('admin.jobs.col_outcome', 'Outcome')} {t('admin.jobs.col_state', 'State')} {t('admin.jobs.col_actions', 'Actions')}
{#if canExpand} {:else} {job.name} {/if} {cadenceLabel(job)} {timeAgo(job.last_run_at)} {outcomeLabel(job)} {#if isRunning(job)} {t('admin.jobs.state_running', 'running')} {:else} — {/if} {#if supportsDeep(job.name)} {/if} {#if isRunning(job) && canExpand} {/if}

{t('admin.jobs.runs_title', 'Recent runs')}

{#if runsErr}

{runsErr}

{:else if !runs}

{t('common.loading', 'Loading…')}

{:else if runs.length === 0}

{t('admin.jobs.no_runs', 'No runs yet.')}

{:else} {#each runs as run (run.id)} {@const scanned = statNumber(run, 'scanned_count')} {@const findingCount = statNumber(run, 'finding_count')} {@const isRunExpanded = expandedRun === run.id} {#if isRunExpanded} {@const findings = findingsByRun[run.id]} {@const findingsErr = findingsErrorByRun[run.id]} {@const fLoading = findingsLoadingByRun[run.id]} {/if} {/each}
{t('admin.jobs.col_started_at', 'Started')} {t('admin.jobs.col_status', 'Status')} {t('admin.jobs.col_duration', 'Duration')} {t('admin.jobs.col_scanned', 'Scanned')} {t('admin.jobs.col_findings', 'Findings')} {t('admin.jobs.col_error', 'Error')}
{timeAgo(run.started_at)} {run.status} {runDurationLabel(run)} {scanned ?? '—'} {findingCount ?? 0} {#if run.error_message} {run.error_message} {:else} — {/if}
{t('admin.jobs.run_json', 'Run summary (JSON)')}
{JSON.stringify(
																				{
																					id: run.id,
																					status: run.status,
																					started_at: run.started_at,
																					last_progress_at: run.last_progress_at,
																					completed_at: run.completed_at,
																					stats: run.stats,
																					params: run.params,
																					cursor_hex: run.cursor_hex,
																					error_message: run.error_message
																				},
																				null,
																				2
																			)}

{t('admin.jobs.findings_title', 'Findings')}

{#if findingsErr}

{findingsErr}

{:else if !findings}

{t('common.loading', 'Loading…')}

{:else if findings.length === 0}

{t('admin.jobs.no_findings', 'No findings — clean run.')}

{:else} {#each findings as f (f.id)} {/each}
{t('admin.jobs.col_kind', 'Kind')} {t('admin.jobs.col_severity', 'Severity')} {t('admin.jobs.col_resource', 'Resource')} {t('admin.jobs.col_detail', 'Detail')}
{f.kind} {f.severity} {f.resource_id ?? '—'} {JSON.stringify(f.detail)}
{/if}
{/if}
{/if}