From 684fcf390cb5d65b5abe5e29092274ba96b8774d Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Thu, 26 Mar 2026 06:37:16 +0100 Subject: [PATCH] feat: add type, status, and library filters to jobs list Filter jobs by type, status, or library with dropdowns above the table. Shows filtered count and a clear button when filters are active. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/backoffice/app/components/JobsList.tsx | 68 +++++++++++++++++++-- apps/backoffice/lib/i18n/en.ts | 3 + apps/backoffice/lib/i18n/fr.ts | 3 + 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/apps/backoffice/app/components/JobsList.tsx b/apps/backoffice/app/components/JobsList.tsx index 24e1074..61b1d63 100644 --- a/apps/backoffice/app/components/JobsList.tsx +++ b/apps/backoffice/app/components/JobsList.tsx @@ -46,9 +46,19 @@ function formatDuration(start: string, end: string | null): string { export function JobsList({ initialJobs, libraries, highlightJobId }: JobsListProps) { const { t, locale } = useTranslation(); const [jobs, setJobs] = useState(initialJobs); + const [filterType, setFilterType] = useState(""); + const [filterStatus, setFilterStatus] = useState(""); + const [filterLibrary, setFilterLibrary] = useState(""); + + const filteredJobs = jobs.filter((job) => { + if (filterType && job.type !== filterType) return false; + if (filterStatus && job.status !== filterStatus) return false; + if (filterLibrary && job.library_id !== filterLibrary) return false; + return true; + }); const initialPage = highlightJobId - ? Math.ceil((initialJobs.findIndex(j => j.id === highlightJobId) + 1) / PAGE_SIZE) || 1 + ? Math.ceil((filteredJobs.findIndex(j => j.id === highlightJobId) + 1) / PAGE_SIZE) || 1 : 1; const [currentPage, setCurrentPage] = useState(initialPage); @@ -65,9 +75,14 @@ export function JobsList({ initialJobs, libraries, highlightJobId }: JobsListPro }); }; - const totalPages = Math.ceil(jobs.length / PAGE_SIZE); + // Derive unique types, statuses, libraries for filter options + const jobTypes = [...new Set(jobs.map(j => j.type))].sort(); + const jobStatuses = [...new Set(jobs.map(j => j.status))].sort(); + const jobLibraryIds = [...new Set(jobs.map(j => j.library_id).filter(Boolean))] as string[]; + + const totalPages = Math.ceil(filteredJobs.length / PAGE_SIZE); const pageStart = (currentPage - 1) * PAGE_SIZE; - const visibleJobs = jobs.slice(pageStart, pageStart + PAGE_SIZE); + const visibleJobs = filteredJobs.slice(pageStart, pageStart + PAGE_SIZE); // Refresh jobs list via SSE useEffect(() => { @@ -128,6 +143,49 @@ export function JobsList({ initialJobs, libraries, highlightJobId }: JobsListPro return (
+
+ + + + {(filterType || filterStatus || filterLibrary) && ( + + )} + + {filteredJobs.length} / {jobs.length} + +
@@ -163,8 +221,8 @@ export function JobsList({ initialJobs, libraries, highlightJobId }: JobsListPro {t("pagination.range", { start: pageStart + 1, - end: Math.min(pageStart + PAGE_SIZE, jobs.length), - total: jobs.length, + end: Math.min(pageStart + PAGE_SIZE, filteredJobs.length), + total: filteredJobs.length, })}
diff --git a/apps/backoffice/lib/i18n/en.ts b/apps/backoffice/lib/i18n/en.ts index 8353eb4..17eaef0 100644 --- a/apps/backoffice/lib/i18n/en.ts +++ b/apps/backoffice/lib/i18n/en.ts @@ -282,6 +282,9 @@ const en: Record = { "jobsList.duration": "Duration", "jobsList.created": "Created", "jobsList.actions": "Actions", + "jobsList.allTypes": "All types", + "jobsList.allStatuses": "All statuses", + "jobsList.allLibraries": "All libraries", // Job row "jobRow.showProgress": "Show progress", diff --git a/apps/backoffice/lib/i18n/fr.ts b/apps/backoffice/lib/i18n/fr.ts index 445f89b..e56510a 100644 --- a/apps/backoffice/lib/i18n/fr.ts +++ b/apps/backoffice/lib/i18n/fr.ts @@ -280,6 +280,9 @@ const fr = { "jobsList.duration": "Durée", "jobsList.created": "Créé", "jobsList.actions": "Actions", + "jobsList.allTypes": "Tous les types", + "jobsList.allStatuses": "Tous les statuts", + "jobsList.allLibraries": "Toutes les bibliothèques", // Job row "jobRow.showProgress": "Afficher la progression",