feat(campaign-ui): configure attachment delivery behavior

This commit is contained in:
2026-07-20 20:08:53 +02:00
parent 0a6064ec62
commit c71fa3dc64
3 changed files with 45 additions and 22 deletions
@@ -6,6 +6,7 @@ import { Button } from "@govoplan/core-webui";
import { Card } from "@govoplan/core-webui";
import { PageTitle } from "@govoplan/core-webui";
import { LoadingFrame } from "@govoplan/core-webui";
import { MetricCard } from "@govoplan/core-webui";
import LockedVersionNotice from "./components/LockedVersionNotice";
import VersionLine from "./components/VersionLine";
import { ToggleSwitch } from "@govoplan/core-webui";
@@ -40,7 +41,7 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
const [zipNameEditorIndex, setZipNameEditorIndex] = useState<number | null>(null);
const version = data.currentVersion;
const locked = isAuditLockedVersion(version, data.campaign?.current_version_id);
const { draft, setDraft, displayDraft, dirty, saveState, localError, patch, markDirty, saveDraft } = useCampaignDraftEditor({
const { draft, setDraft, displayDraft, dirty, saveState, localError, patch, markDirty, discardDraft, saveDraft } = useCampaignDraftEditor({
settings,
campaignId,
version,
@@ -238,7 +239,7 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
</div>
<div className="button-row compact-actions">
{filesModuleInstalled && <Button onClick={() => navigate("/files")}>i18n:govoplan-campaign.manage_files.90a419f7</Button>}
<Button onClick={reload} disabled={loading}>i18n:govoplan-campaign.reload.cce71553</Button>
<Button onClick={() => void discardDraft()} disabled={loading}>Discard</Button>
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!canSave}>i18n:govoplan-campaign.save.efc007a3</Button>
</div>
</div>
@@ -249,6 +250,13 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
<>
<div className="metric-grid attachment-statistics-grid">
<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.upload_support.1c54931c" value={managedFilesAvailable ? "Files" : "Manual"} tone={managedFilesAvailable ? "good" : filesModuleInstalled ? "warning" : "neutral"} />
</div>
<Card title="i18n:govoplan-campaign.attachment_sources.8ef0a6ce">
<div className="admin-table-surface attachment-sources-table-surface">
<DataGrid
@@ -300,7 +308,6 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
{zipArchiveNameValidation.message &&
<DismissibleAlert tone="danger" compact resetKey={zipArchiveNameValidation.message} className="attachment-zip-name-error">{zipArchiveNameValidation.message}</DismissibleAlert>
}
<p className="muted small-note">i18n:govoplan-campaign.archive_names_support_recipient_and_campaign_fie.d5b1b2d1</p>
</Card>
<Card title="i18n:govoplan-campaign.global_attachments.492bd841" collapsible>
@@ -319,20 +326,6 @@ export default function AttachmentsDataPage({ settings, campaignId }: {settings:
</Card>
<Card title="i18n:govoplan-campaign.statistics.2086b21f" collapsible>
<dl className="detail-list">
<div><dt>i18n:govoplan-campaign.base_paths.edf35a97</dt><dd>{basePaths.length}</dd></div>
<div><dt>i18n:govoplan-campaign.global_attachments.438263a1</dt><dd>direct: {globalSummary.direct} i18n:govoplan-campaign.rules.e5d36074 {globalSummary.rules}</dd></div>
<div><dt>i18n:govoplan-campaign.per_recipient_patterns.53da30da</dt><dd>{individualRulesCount}</dd></div>
<div><dt>i18n:govoplan-campaign.upload_support.1c54931c</dt><dd>{managedFilesAvailable ? "i18n:govoplan-campaign.connected_through_files.95007112" : "i18n:govoplan-campaign.manual_paths.8e20627a"}</dd></div>
</dl>
<p className="muted small-note">{managedFilesAvailable ?
"i18n:govoplan-campaign.files_are_managed_in_the_top_level_files_module_.4b370222" :
filesModuleInstalled ?
"i18n:govoplan-campaign.managed_file_browsing_is_unavailable_use_manual_.782867fe" :
"i18n:govoplan-campaign.the_files_module_is_not_installed_use_manual_pat.49abc725"}</p>
</Card>
</>
</LoadingFrame>
@@ -450,6 +443,19 @@ function zipArchiveColumns({ disabled, archives, invalidNameIndexes, onEditName,
render: (archive, index) => <ToggleSwitch label="i18n:govoplan-campaign.protected.28531336" checked={archive.password_enabled} disabled={disabled} onChange={(checked) => patchArchive(index, { password_enabled: checked })} />,
value: (archive) => archive.password_enabled ? "protected" : "none"
},
{
id: "method", header: "ZIP mode", width: 280, sortable: true, filterable: true,
columnType: "from-list", list: { options: [{ value: "aes", label: "AES" }, { value: "zip_standard", label: "Win-compatible" }] },
render: (archive, index) =>
<ToggleSwitch
label="Win-compatible"
checked={archive.method === "zip_standard"}
disabled={disabled}
help="Win-compatible ZIP uses the legacy ZipCrypto format so password-protected archives can be opened with Windows Explorer. Use AES when recipients can use 7-Zip, NanaZip, WinRAR, or another AES-capable ZIP tool."
onChange={(checked) => patchArchive(index, { method: checked ? "zip_standard" : "aes" })} />,
value: (archive) => archive.method
},
{
id: "password_field", header: "i18n:govoplan-campaign.password_field.a1fc8a1c", width: 230, sortable: true, filterable: true,
columnType: "from-list", list: { options: [{ value: "", label: "i18n:govoplan-campaign.no_field.1fe00ed4" }, ...passwordFields.map((field) => ({ value: field.name, label: field.label || field.name }))] },