From 3bd2fb7c1f30c36ab8918b69a4e4dfd1ab19b996 Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Wed, 11 Mar 2026 17:50:48 +0100 Subject: [PATCH] feat(jobs): introduce extracting_pages status and update job progress handling - Added a new job status 'extracting_pages' to represent the first sub-phase of thumbnail generation. - Updated the database schema to include a timestamp for when thumbnail generation starts. - Enhanced job progress components to handle the new status, including UI updates for displaying progress and status labels. - Refactored job-related logic to accommodate the two-phase process: extracting pages and generating thumbnails. - Adjusted SQL queries and job detail responses to include the new fields and statuses. This change improves the clarity of job processing states and enhances user feedback during the thumbnail generation process. --- apps/api/src/index_jobs.rs | 7 +- .../backoffice/app/components/JobProgress.tsx | 6 +- apps/backoffice/app/components/JobRow.tsx | 17 +- .../app/components/JobsIndicator.tsx | 6 +- apps/backoffice/app/components/ui/Badge.tsx | 2 + apps/backoffice/app/jobs/[id]/page.tsx | 62 ++- apps/backoffice/tsconfig.tsbuildinfo | 2 +- apps/indexer/src/analyzer.rs | 355 +++++++++++------- .../0018_add_extracting_pages_status.sql | 7 + ...9_add_generating_thumbnails_started_at.sql | 5 + 10 files changed, 300 insertions(+), 169 deletions(-) create mode 100644 infra/migrations/0018_add_extracting_pages_status.sql create mode 100644 infra/migrations/0019_add_generating_thumbnails_started_at.sql diff --git a/apps/api/src/index_jobs.rs b/apps/api/src/index_jobs.rs index c761114..eddb1b1 100644 --- a/apps/api/src/index_jobs.rs +++ b/apps/api/src/index_jobs.rs @@ -65,6 +65,8 @@ pub struct IndexJobDetailResponse { pub finished_at: Option>, #[schema(value_type = Option)] pub phase2_started_at: Option>, + #[schema(value_type = Option)] + pub generating_thumbnails_started_at: Option>, pub stats_json: Option, pub error_opt: Option, #[schema(value_type = String)] @@ -324,6 +326,7 @@ fn map_row_detail(row: sqlx::postgres::PgRow) -> IndexJobDetailResponse { started_at: row.get("started_at"), finished_at: row.get("finished_at"), phase2_started_at: row.try_get("phase2_started_at").ok().flatten(), + generating_thumbnails_started_at: row.try_get("generating_thumbnails_started_at").ok().flatten(), stats_json: row.get("stats_json"), error_opt: row.get("error_opt"), created_at: row.get("created_at"), @@ -350,7 +353,7 @@ pub async fn get_active_jobs(State(state): State) -> Result, ) -> Result, ApiError> { let row = sqlx::query( - "SELECT id, library_id, book_id, type, status, started_at, finished_at, phase2_started_at, + "SELECT id, library_id, book_id, type, status, started_at, finished_at, phase2_started_at, generating_thumbnails_started_at, stats_json, error_opt, created_at, current_file, progress_percent, total_files, processed_files FROM index_jobs WHERE id = $1" ) diff --git a/apps/backoffice/app/components/JobProgress.tsx b/apps/backoffice/app/components/JobProgress.tsx index c74fc97..d53e6f5 100644 --- a/apps/backoffice/app/components/JobProgress.tsx +++ b/apps/backoffice/app/components/JobProgress.tsx @@ -87,8 +87,8 @@ export function JobProgress({ jobId, onComplete }: JobProgressProps) { const percent = progress.progress_percent ?? 0; const processed = progress.processed_files ?? 0; const total = progress.total_files ?? 0; - const isThumbnailsPhase = progress.status === "generating_thumbnails"; - const unitLabel = isThumbnailsPhase ? "thumbnails" : "files"; + const isPhase2 = progress.status === "extracting_pages" || progress.status === "generating_thumbnails"; + const unitLabel = progress.status === "extracting_pages" ? "pages" : progress.status === "generating_thumbnails" ? "thumbnails" : "files"; return (
@@ -112,7 +112,7 @@ export function JobProgress({ jobId, onComplete }: JobProgressProps) { )}
- {progress.stats_json && !isThumbnailsPhase && ( + {progress.stats_json && !isPhase2 && (
Scanned: {progress.stats_json.scanned_files} Indexed: {progress.stats_json.indexed_files} diff --git a/apps/backoffice/app/components/JobRow.tsx b/apps/backoffice/app/components/JobRow.tsx index c38c8fc..7a44be4 100644 --- a/apps/backoffice/app/components/JobRow.tsx +++ b/apps/backoffice/app/components/JobRow.tsx @@ -33,7 +33,7 @@ interface JobRowProps { } export function JobRow({ job, libraryName, highlighted, onCancel, formatDate, formatDuration }: JobRowProps) { - const isActive = job.status === "running" || job.status === "pending" || job.status === "generating_thumbnails"; + const isActive = job.status === "running" || job.status === "pending" || job.status === "extracting_pages" || job.status === "generating_thumbnails"; const [showProgress, setShowProgress] = useState(highlighted || isActive); const handleComplete = () => { @@ -52,13 +52,14 @@ export function JobRow({ job, libraryName, highlighted, onCancel, formatDate, fo const removed = job.stats_json?.removed_files ?? 0; const errors = job.stats_json?.errors ?? 0; + const isPhase2 = job.status === "extracting_pages" || job.status === "generating_thumbnails"; const isThumbnailPhase = job.status === "generating_thumbnails"; const isThumbnailJob = job.type === "thumbnail_rebuild" || job.type === "thumbnail_regenerate"; - const hasThumbnailPhase = isThumbnailPhase || isThumbnailJob; + const hasThumbnailPhase = isPhase2 || isThumbnailJob; - // Files column: index-phase stats only + // Files column: index-phase stats only (Phase 1 discovery) const filesDisplay = - job.status === "running" && !isThumbnailPhase + job.status === "running" && !isPhase2 ? job.total_files != null ? `${job.processed_files ?? 0}/${job.total_files}` : scanned > 0 @@ -70,8 +71,8 @@ export function JobRow({ job, libraryName, highlighted, onCancel, formatDate, fo ? `${scanned} scanned` : "—"; - // Thumbnails column - const thumbInProgress = hasThumbnailPhase && (job.status === "running" || isThumbnailPhase); + // Thumbnails column (Phase 2: extracting_pages + generating_thumbnails) + const thumbInProgress = hasThumbnailPhase && (job.status === "running" || isPhase2); const thumbDisplay = thumbInProgress && job.total_files != null ? `${job.processed_files ?? 0}/${job.total_files}` @@ -128,7 +129,7 @@ export function JobRow({ job, libraryName, highlighted, onCancel, formatDate, fo {errors > 0 && ⚠ {errors}}
)} - {job.status === "running" && !isThumbnailPhase && job.total_files != null && ( + {job.status === "running" && !isPhase2 && job.total_files != null && ( )} @@ -155,7 +156,7 @@ export function JobRow({ job, libraryName, highlighted, onCancel, formatDate, fo > View - {(job.status === "pending" || job.status === "running" || job.status === "generating_thumbnails") && ( + {(job.status === "pending" || job.status === "running" || job.status === "extracting_pages" || job.status === "generating_thumbnails") && (