From ac3329cafe2ffd0d5ad5d5f3d886044682fb4597 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Wed, 22 Jul 2026 09:11:14 +0200 Subject: [PATCH] fix(campaign): use stable aggregate status filters --- .../features/reports/AggregateReportsPage.tsx | 22 ++++++++++++++++++- .../aggregate-report-ui-structure.test.mjs | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/webui/src/features/reports/AggregateReportsPage.tsx b/webui/src/features/reports/AggregateReportsPage.tsx index 2736996..b328806 100644 --- a/webui/src/features/reports/AggregateReportsPage.tsx +++ b/webui/src/features/reports/AggregateReportsPage.tsx @@ -14,7 +14,8 @@ import { formatDateTime, i18nMessage, type ApiSettings, - type DataGridColumn + type DataGridColumn, + type DataGridListOption } from "@govoplan/core-webui"; import { getAggregateCampaignReport, @@ -24,6 +25,21 @@ import { type AggregateReportCount } from "../../api/campaigns"; +const CAMPAIGN_STATUS_OPTIONS: DataGridListOption[] = [ + { value: "draft", label: "i18n:govoplan-campaign.draft.23d33e22" }, + { value: "validated", label: "i18n:govoplan-campaign.valid.a4aefa35" }, + { value: "needs_review", label: "i18n:govoplan-campaign.needs_review.33a506cf" }, + { value: "ready_to_queue", label: "i18n:govoplan-campaign.ready.20c7c552" }, + { value: "queued", label: "i18n:govoplan-campaign.queued.6a599877" }, + { value: "sending", label: "i18n:govoplan-campaign.sending.cf765512" }, + { value: "sent", label: "i18n:govoplan-campaign.sent.35f49dcf" }, + { value: "partially_completed", label: "i18n:govoplan-campaign.partially_completed.760bc5e6" }, + { value: "outcome_unknown", label: "i18n:govoplan-campaign.outcome_unknown.6e929fca" }, + { value: "failed", label: "i18n:govoplan-campaign.failed.09fef5d8" }, + { value: "cancelled", label: "i18n:govoplan-campaign.cancelled.a1bf92ef" }, + { value: "archived", label: "i18n:govoplan-campaign.archived.eddc813f" } +]; + export default function AggregateReportsPage({ settings }: {settings: ApiSettings;}) { const [searchParams, setSearchParams] = useSearchParams(); const selectedFromUrl = searchParams.get("campaign") ?? ""; @@ -110,6 +126,8 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting width: 170, sortable: true, filterable: true, + columnType: "from-list", + list: { options: CAMPAIGN_STATUS_OPTIONS, display: "pill" }, render: (campaign) => , value: (campaign) => campaign.status }, @@ -190,6 +208,8 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting + {/* Aggregate outcome cards are deliberately not filter shortcuts: each subgroup + would require a separately suppressed aggregate response from the server. */}
diff --git a/webui/tests/aggregate-report-ui-structure.test.mjs b/webui/tests/aggregate-report-ui-structure.test.mjs index ce2194a..68c627e 100644 --- a/webui/tests/aggregate-report-ui-structure.test.mjs +++ b/webui/tests/aggregate-report-ui-structure.test.mjs @@ -23,6 +23,28 @@ assert(!page.includes("localStorage"), "the aggregate page does not persist repo assert(!page.includes("sessionStorage"), "the aggregate page does not persist report data in session storage"); assert(page.includes("TableActionGroup"), "campaign selection uses the central icon-only table action group"); assert(page.includes("disabled: campaign.id === selectedFromUrl"), "the selected row action stays visible and disabled"); +assert(page.includes('columnType: "from-list"'), "campaign status uses the stable shared list-filter model"); +const expectedCampaignStatuses = [ + "draft", + "validated", + "needs_review", + "ready_to_queue", + "queued", + "sending", + "sent", + "partially_completed", + "outcome_unknown", + "failed", + "cancelled", + "archived" +]; +for (const status of expectedCampaignStatuses) { + assert(page.includes(`{ value: "${status}", label:`), `the aggregate list declares the ${status} status explicitly`); +} +const outcomeCardsStart = page.indexOf("Aggregate outcome cards are deliberately not filter shortcuts"); +const outcomeCardsEnd = page.indexOf('