feat(webui): add campaign metric drill-downs

This commit is contained in:
2026-08-19 18:47:45 +02:00
parent 039ce35e78
commit 8e2f9d743d
17 changed files with 124 additions and 73 deletions
@@ -246,6 +246,21 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
patchZipCollection({ ...zipConfig, archives: moveArrayItem(zipConfig.archives, index, targetIndex) });
}
function revealAttachmentSection(id: string) {
const section = document.getElementById(id);
if (!section) return;
section.querySelector<HTMLButtonElement>('.card-collapse-toggle[aria-expanded="false"]')?.click();
window.requestAnimationFrame(() => {
section.scrollIntoView({ behavior: "smooth", block: "start" });
section.focus({ preventScroll: true });
});
}
function openRecipients() {
const query = version?.id ? `?version=${encodeURIComponent(version.id)}` : "";
navigate(`/campaigns/${campaignId}/recipients${query}`);
}
return (
<PageLayout
@@ -257,8 +272,7 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
error={error}
actions={<PageActionBar
variant="editor"
dirty={dirty}
saving={loading}
state={loading ? "saving" : dirty ? "dirty" : "clean"}
contextActions={filesModuleInstalled ? <Button onClick={() => navigate("/files")}>i18n:govoplan-campaign.manage_files.90a419f7</Button> : undefined}
discardAction={{ label: "i18n:govoplan-campaign.discard.36fff63c", onClick: () => void discardDraft() }}
saveAction={{ label: "i18n:govoplan-campaign.save.efc007a3", onClick: () => saveDraft("manual"), disabled: !canSave && dirty, disabledReason: !canSave && dirty ? "Resolve the current editor blocker before saving." : undefined }}
@@ -272,13 +286,13 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
<>
<MetricGrid>
<MetricCard label="i18n:govoplan-campaign.base_paths.edf35a97" value={basePaths.length} tone="neutral" />
<MetricCard label="i18n:govoplan-campaign.global_attachments.438263a1" value={`${globalSummary.direct} / ${globalSummary.rules}`} tone="info" detail="direct / rules" />
<MetricCard label="i18n:govoplan-campaign.per_recipient_patterns.53da30da" value={individualRulesCount} tone="neutral" />
<MetricCard label="i18n:govoplan-campaign.base_paths.edf35a97" value={basePaths.length} tone="neutral" drilldown={{ label: i18nMessage("i18n:govoplan-campaign.open_value.a34416a9", { value0: "Attachment sources" }), onActivate: () => revealAttachmentSection("campaign-attachment-sources") }} />
<MetricCard label="i18n:govoplan-campaign.global_attachments.438263a1" value={`${globalSummary.direct} / ${globalSummary.rules}`} tone="info" detail="direct / rules" drilldown={{ label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealAttachmentSection("campaign-global-attachments") }} />
<MetricCard label="i18n:govoplan-campaign.per_recipient_patterns.53da30da" value={individualRulesCount} tone="neutral" drilldown={{ label: i18nMessage("i18n:govoplan-campaign.open_value.a34416a9", { value0: "Recipients" }), onActivate: openRecipients }} />
<MetricCard label="i18n:govoplan-campaign.upload_support.1c54931c" value={managedFilesAvailable ? "Files" : "Manual"} tone={managedFilesAvailable ? "good" : filesModuleInstalled ? "warning" : "neutral"} />
</MetricGrid>
<Card title="i18n:govoplan-campaign.attachment_sources.8ef0a6ce">
<Card id="campaign-attachment-sources" tabIndex={-1} title="i18n:govoplan-campaign.attachment_sources.8ef0a6ce">
<div className="admin-table-surface attachment-sources-table-surface">
<DataGrid
id={`campaign-${campaignId}-attachment-sources`}
@@ -373,7 +387,7 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
}
</Card>
<Card title="i18n:govoplan-campaign.global_attachments.492bd841" collapsible>
<Card id="campaign-global-attachments" tabIndex={-1} title="i18n:govoplan-campaign.global_attachments.492bd841" collapsible>
<AttachmentRulesDataGrid
id={`campaign-${campaignId}-global-attachments`}
rules={globalRules}