Add bulk recipient activation controls

This commit is contained in:
2026-08-04 10:51:44 +02:00
parent f98fe06143
commit 9137300780
5 changed files with 89 additions and 3 deletions
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from "react";
import { CircleCheck, CircleX } from "lucide-react";
import type { ApiSettings } from "../../types";
import {
getCampaignPostboxCatalog,
@@ -13,6 +14,7 @@ import {
"../../api/campaigns";
import { Button } from "@govoplan/core-webui";
import { Card } from "@govoplan/core-webui";
import { ConfirmDialog } from "@govoplan/core-webui";
import { FormField } from "@govoplan/core-webui";
import { usePlatformModuleInstalled } from "@govoplan/core-webui";
import CampaignDraftPageScaffold from "./components/CampaignDraftPageScaffold";
@@ -39,7 +41,7 @@ import {
createAddressSourceImportProvenance
} from "./utils/addressSourceImport";
import { addressesFromValue, type MailboxAddress } from "@govoplan/core-webui";
import { insertAfter, moveArrayItem, useGuardedNavigate, usePlatformLanguage } from "@govoplan/core-webui";
import { i18nMessage, insertAfter, moveArrayItem, useGuardedNavigate, usePlatformLanguage } from "@govoplan/core-webui";
import AddressSourceImportDialog from "./recipients/AddressSourceImportDialog";
import DistributionListImportDialog from "./recipients/DistributionListImportDialog";
import {
@@ -69,6 +71,12 @@ import {
} from "./recipients/RecipientAddressEditor";
import { RecipientImportDialog } from "./recipients/RecipientImportDialog";
import { recipientProfileColumns } from "./recipients/recipientProfileColumns";
type RecipientBulkActivation = {
active: boolean;
count: number;
};
export default function RecipientDataPage({ settings, campaignId }: {settings: ApiSettings;campaignId: string;}) {
const { translateText } = usePlatformLanguage();
const filesModuleInstalled = usePlatformModuleInstalled("files");
@@ -91,6 +99,7 @@ export default function RecipientDataPage({ settings, campaignId }: {settings: A
const [recipientProfilesQuery, setRecipientProfilesQuery] = useState<DataGridQueryState>({ sort: null, filters: {} });
const [recipientAddressEditorIndex, setRecipientAddressEditorIndex] = useState<number | null>(null);
const [postboxTargetEditorIndex, setPostboxTargetEditorIndex] = useState<number | null>(null);
const [bulkActivation, setBulkActivation] = useState<RecipientBulkActivation | null>(null);
const [postboxCatalog, setPostboxCatalog] = useState<CampaignPostboxCatalog>({
available: false,
postboxes: [],
@@ -346,6 +355,19 @@ export default function RecipientDataPage({ settings, campaignId }: {settings: A
});
}
function requestBulkActivation(active: boolean) {
if (locked) return;
const count = inlineEntries.filter((entry) => (entry.active !== false) !== active).length;
if (count === 0) return;
setBulkActivation({ active, count });
}
function confirmBulkActivation() {
if (!bulkActivation || locked) return;
replaceInlineEntries(inlineEntries.map((entry) => ({ ...entry, active: bulkActivation.active })));
setBulkActivation(null);
}
function applyRecipientImport(preview: RecipientImportPreview, mode: RecipientImportMode, provenance?: RecipientImportProvenance | null) {
if (locked || !draft) return;
setDraft(materializeRecipientImportWithAttachmentDefaults(draft, preview, { mode, provenance }));
@@ -492,6 +514,22 @@ export default function RecipientDataPage({ settings, campaignId }: {settings: A
</Button>
}
<Button disabled={locked} onClick={() => setImportOpen(true)}>i18n:govoplan-campaign.import.d6fbc9d2</Button>
<Button
disabled={locked || inlineEntries.every((entry) => entry.active !== false)}
onClick={() => requestBulkActivation(true)}>
<CircleCheck size={16} aria-hidden="true" />
{i18nMessage("i18n:govoplan-campaign.activate_all_value0.7e911e3e", {
value0: inlineEntries.filter((entry) => entry.active === false).length
})}
</Button>
<Button
disabled={locked || inlineEntries.every((entry) => entry.active === false)}
onClick={() => requestBulkActivation(false)}>
<CircleX size={16} aria-hidden="true" />
{i18nMessage("i18n:govoplan-campaign.deactivate_all_value0.87e5d46f", {
value0: inlineEntries.filter((entry) => entry.active !== false).length
})}
</Button>
</div>
}>
{inlineEntries.length === 0 && Boolean(source.type) &&
@@ -626,6 +664,23 @@ export default function RecipientDataPage({ settings, campaignId }: {settings: A
onClose={() => setRecipientAddressEditorIndex(null)} />
}
<ConfirmDialog
open={Boolean(bulkActivation)}
title={bulkActivation?.active
? "i18n:govoplan-campaign.activate_all_recipients.6be687f0"
: "i18n:govoplan-campaign.deactivate_all_recipients.5939b005"}
message={bulkActivation ? i18nMessage(
bulkActivation.active
? "i18n:govoplan-campaign.activate_value0_currently_inactive_recipients_the_change_r.53787f50"
: "i18n:govoplan-campaign.deactivate_value0_currently_active_recipients_the_change_r.3bee469f",
{ value0: bulkActivation.count }
) : ""}
confirmLabel={bulkActivation?.active
? "i18n:govoplan-campaign.activate_recipients.bc3a7878"
: "i18n:govoplan-campaign.deactivate_recipients.88d80611"}
tone={bulkActivation?.active ? "default" : "danger"}
onConfirm={confirmBulkActivation}
onCancel={() => setBulkActivation(null)} />
{postboxTargetEditorIndex !== null && inlineEntries[postboxTargetEditorIndex] &&
<PostboxTargetsDialog
open
+16
View File
@@ -2,6 +2,14 @@ import type { PlatformTranslations } from "@govoplan/core-webui";
export const generatedTranslations: PlatformTranslations = {
"en": {
"i18n:govoplan-campaign.activate_all_value0.7e911e3e": "Activate all ({value0})",
"i18n:govoplan-campaign.deactivate_all_value0.87e5d46f": "Deactivate all ({value0})",
"i18n:govoplan-campaign.activate_all_recipients.6be687f0": "Activate all recipients",
"i18n:govoplan-campaign.deactivate_all_recipients.5939b005": "Deactivate all recipients",
"i18n:govoplan-campaign.activate_value0_currently_inactive_recipients_the_change_r.53787f50": "Activate {value0} currently inactive recipients? The change remains a draft until you save.",
"i18n:govoplan-campaign.deactivate_value0_currently_active_recipients_the_change_r.3bee469f": "Deactivate {value0} currently active recipients? The change remains a draft until you save.",
"i18n:govoplan-campaign.activate_recipients.bc3a7878": "Activate recipients",
"i18n:govoplan-campaign.deactivate_recipients.88d80611": "Deactivate recipients",
"i18n:govoplan-campaign.guided_workflows": "Guided workflows",
"i18n:govoplan-campaign.no_guided_workflows": "No guided workflows are available.",
"i18n:govoplan-campaign.required_action.f2429497": "Required action",
@@ -1335,6 +1343,14 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-campaign.zipcrypto.03bf7fb4": "ZipCrypto"
},
"de": {
"i18n:govoplan-campaign.activate_all_value0.7e911e3e": "Alle aktivieren ({value0})",
"i18n:govoplan-campaign.deactivate_all_value0.87e5d46f": "Alle deaktivieren ({value0})",
"i18n:govoplan-campaign.activate_all_recipients.6be687f0": "Alle Empfänger aktivieren",
"i18n:govoplan-campaign.deactivate_all_recipients.5939b005": "Alle Empfänger deaktivieren",
"i18n:govoplan-campaign.activate_value0_currently_inactive_recipients_the_change_r.53787f50": "{value0} derzeit inaktive Empfänger aktivieren? Die Änderung bleibt ein Entwurf, bis Sie speichern.",
"i18n:govoplan-campaign.deactivate_value0_currently_active_recipients_the_change_r.3bee469f": "{value0} derzeit aktive Empfänger deaktivieren? Die Änderung bleibt ein Entwurf, bis Sie speichern.",
"i18n:govoplan-campaign.activate_recipients.bc3a7878": "Empfänger aktivieren",
"i18n:govoplan-campaign.deactivate_recipients.88d80611": "Empfänger deaktivieren",
"i18n:govoplan-campaign.guided_workflows": "Geführte Abläufe",
"i18n:govoplan-campaign.no_guided_workflows": "Es sind keine geführten Abläufe verfügbar.",
"i18n:govoplan-campaign.required_action.f2429497": "Erforderliche Aktion",