fix(webui): localize Campaign delivery reporting

This commit is contained in:
2026-07-22 09:04:42 +02:00
parent b0282ebff2
commit 79b576b4bc
11 changed files with 380 additions and 94 deletions
@@ -12,6 +12,7 @@ import {
StatusBadge,
TableActionGroup,
formatDateTime,
i18nMessage,
type ApiSettings,
type DataGridColumn
} from "@govoplan/core-webui";
@@ -131,11 +132,11 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
render: (campaign) => (
<TableActionGroup actions={[{
id: "view-aggregate-report",
label: "View aggregate report",
label: "i18n:govoplan-campaign.view_aggregate_report.94b42124",
icon: <Eye aria-hidden="true" />,
variant: campaign.id === selectedFromUrl ? "primary" : "secondary",
disabled: campaign.id === selectedFromUrl,
disabledReason: campaign.id === selectedFromUrl ? "This report is already selected." : undefined,
disabledReason: campaign.id === selectedFromUrl ? "i18n:govoplan-campaign.this_report_is_already_selected_.983c5be4" : undefined,
onClick: () => selectCampaign(campaign.id)
}]} />
)
@@ -150,75 +151,75 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
<div className="page-heading split workspace-heading">
<div>
<PageTitle loading={listLoading || reportLoading}>i18n:govoplan-campaign.reports.88bc3fe3</PageTitle>
<p className="muted">Privacy-protected Campaign outcomes without recipient or delivery diagnostics.</p>
<p className="muted">i18n:govoplan-campaign.privacy_protected_campaign_outcomes_without_reci.5b8a100d</p>
</div>
<div className="button-row compact-actions">
<Button
onClick={() => void Promise.all([loadCampaigns(), selectedFromUrl ? loadReport(selectedFromUrl) : Promise.resolve()])}
disabled={listLoading || reportLoading}
>
<RefreshCw size={16} aria-hidden="true" /> Refresh
<RefreshCw size={16} aria-hidden="true" /> i18n:govoplan-campaign.refresh.56e3badc
</Button>
</div>
</div>
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
<Card title="Campaign reports available to you">
<LoadingFrame loading={listLoading} label="Loading Campaign reports">
<Card title="i18n:govoplan-campaign.campaign_reports_available_to_you.f14fa403">
<LoadingFrame loading={listLoading} label="i18n:govoplan-campaign.loading_campaign_reports_.61ec1ee8">
<DataGrid
id="campaign-aggregate-report-list"
rows={campaigns}
columns={columns}
getRowKey={(campaign) => campaign.id}
initialSort={{ columnId: "updated", direction: "desc" }}
emptyText="No aggregate Campaign reports are available to you."
emptyText="i18n:govoplan-campaign.no_aggregate_campaign_reports_are_available_to_y.dafb2781"
/>
</LoadingFrame>
</Card>
<LoadingFrame loading={reportLoading} label="Loading aggregate Campaign report">
<LoadingFrame loading={reportLoading} label="i18n:govoplan-campaign.loading_aggregate_campaign_report_.72f2f091">
{report && outcomes && (
<>
<Card title={report.campaign.name}>
<dl className="detail-list">
<div><dt>Status</dt><dd><StatusBadge status={report.campaign.status} /></dd></div>
<div><dt>Completion</dt><dd><StatusBadge status={report.completion_state} /></dd></div>
<div><dt>Version</dt><dd>{report.version_number ?? "—"}</dd></div>
<div><dt>Generated</dt><dd>{formatDateTime(report.generated_at)}</dd></div>
<div><dt>i18n:govoplan-campaign.status.bae7d5be</dt><dd><StatusBadge status={report.campaign.status} /></dd></div>
<div><dt>i18n:govoplan-campaign.completion.2ff25568</dt><dd><StatusBadge status={report.completion_state} label={aggregateCompletionLabel(report.completion_state)} /></dd></div>
<div><dt>i18n:govoplan-campaign.version.2da600bf</dt><dd>{report.version_number ?? "—"}</dd></div>
<div><dt>i18n:govoplan-campaign.generated.8eefdd52</dt><dd>{formatDateTime(report.generated_at)}</dd></div>
</dl>
</Card>
<div className="dashboard-grid">
<MetricCard label="SMTP accepted" value={countValue(outcomes.smtp_accepted)} tone="good" />
<MetricCard label="Failed" value={countValue(outcomes.failed)} tone="danger" />
<MetricCard label="Outcome unknown" value={countValue(outcomes.outcome_unknown)} tone="warning" />
<MetricCard label="Queued or active" value={countValue(outcomes.queued_or_active)} tone="info" />
<MetricCard label="Not attempted" value={countValue(outcomes.not_attempted)} />
<MetricCard label="Cancelled" value={countValue(outcomes.cancelled)} />
<MetricCard label="Excluded" value={countValue(outcomes.excluded)} />
<MetricCard label="i18n:govoplan-campaign.smtp_accepted.e3aa7603" value={countValue(outcomes.smtp_accepted)} tone="good" />
<MetricCard label="i18n:govoplan-campaign.failed.09fef5d8" value={countValue(outcomes.failed)} tone="danger" />
<MetricCard label="i18n:govoplan-campaign.outcome_unknown.6e929fca" value={countValue(outcomes.outcome_unknown)} tone="warning" />
<MetricCard label="i18n:govoplan-campaign.queued_or_active.afdcd7da" value={countValue(outcomes.queued_or_active)} tone="info" />
<MetricCard label="i18n:govoplan-campaign.not_attempted.e1be3c69" value={countValue(outcomes.not_attempted)} />
<MetricCard label="i18n:govoplan-campaign.cancelled.a1bf92ef" value={countValue(outcomes.cancelled)} />
<MetricCard label="i18n:govoplan-campaign.excluded.9804952b" value={countValue(outcomes.excluded)} />
</div>
<Card title="Population and exclusions">
<Card title="i18n:govoplan-campaign.population_and_exclusions.cdff62ad">
<dl className="detail-list">
<div><dt>Report denominator</dt><dd>{countValue(report.population.denominator)}</dd></div>
<div><dt>Inactive source entries</dt><dd>{countValue(report.population.inactive_source_entries)}</dd></div>
<div><dt>Excluded or blocked jobs</dt><dd>{countValue(report.population.excluded_or_blocked_jobs)}</dd></div>
<div><dt>i18n:govoplan-campaign.report_denominator.d089074a</dt><dd>{countValue(report.population.denominator)}</dd></div>
<div><dt>i18n:govoplan-campaign.inactive_source_entries.88fa0cf8</dt><dd>{countValue(report.population.inactive_source_entries)}</dd></div>
<div><dt>i18n:govoplan-campaign.excluded_or_blocked_jobs.e2190d47</dt><dd>{countValue(report.population.excluded_or_blocked_jobs)}</dd></div>
</dl>
<p className="muted">{report.population.denominator_definition}</p>
<p className="muted">i18n:govoplan-campaign.all_persisted_recipient_delivery_jobs_for_the_se.208e90cc</p>
</Card>
<Card title="Delivery activity range">
<Card title="i18n:govoplan-campaign.delivery_activity_range.0d1b62ec">
<dl className="detail-list">
<div><dt>First activity</dt><dd>{report.time_range.suppressed ? "Suppressed" : formatDateTime(report.time_range.first_activity_at ?? undefined)}</dd></div>
<div><dt>Last activity</dt><dd>{report.time_range.suppressed ? "Suppressed" : formatDateTime(report.time_range.last_activity_at ?? undefined)}</dd></div>
<div><dt>i18n:govoplan-campaign.first_activity.3fe36ef4</dt><dd>{report.time_range.suppressed ? "i18n:govoplan-campaign.suppressed.fce88db5" : formatDateTime(report.time_range.first_activity_at ?? undefined)}</dd></div>
<div><dt>i18n:govoplan-campaign.last_activity.1c73b806</dt><dd>{report.time_range.suppressed ? "i18n:govoplan-campaign.suppressed.fce88db5" : formatDateTime(report.time_range.last_activity_at ?? undefined)}</dd></div>
</dl>
</Card>
<Card title="Privacy boundary">
<p>{report.privacy.rule}</p>
<Card title="i18n:govoplan-campaign.privacy_boundary.d37e12bc">
<p>i18n:govoplan-campaign.positive_counts_below_the_threshold_are_hidden_a.1dc97093</p>
<p className="muted">
Small-cell threshold: {threshold}. Suppressed values cannot be opened, filtered, exported, or inspected from this view.
{i18nMessage("i18n:govoplan-campaign.small_cell_threshold_value0_suppressed_values_ca.d73f87a6", { value0: threshold })}
</p>
</Card>
</>
@@ -229,5 +230,18 @@ export default function AggregateReportsPage({ settings }: {settings: ApiSetting
}
function countValue(count: AggregateReportCount): string | number {
return count.suppressed ? "Suppressed" : count.value ?? "—";
return count.suppressed ? "i18n:govoplan-campaign.suppressed.fce88db5" : count.value ?? "—";
}
function aggregateCompletionLabel(state: AggregateCampaignReport["completion_state"]): string {
const labels: Record<AggregateCampaignReport["completion_state"], string> = {
not_started: "i18n:govoplan-campaign.not_started.db2c45d5",
in_progress: "i18n:govoplan-campaign.in_progress.b6bd42e4",
completed: "i18n:govoplan-campaign.completed.1798b3ba",
partially_completed: "i18n:govoplan-campaign.partially_completed.760bc5e6",
incomplete: "i18n:govoplan-campaign.incomplete.387fd1bb",
outcome_unknown: "i18n:govoplan-campaign.outcome_unknown.6e929fca",
suppressed: "i18n:govoplan-campaign.suppressed.fce88db5"
};
return labels[state];
}