feat: add governed postbox delivery and report hardening
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { ApiSettings, AuthInfo } from "../../types";
|
||||
import {
|
||||
getCampaignPostboxCatalog,
|
||||
type CampaignPostboxCatalog
|
||||
} from "../../api/campaigns";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
@@ -14,10 +18,15 @@ import VersionLine from "./components/VersionLine";
|
||||
import { ToggleSwitch } from "@govoplan/core-webui";
|
||||
import { hasScope } from "@govoplan/core-webui";
|
||||
import { RetentionPolicyEditor } from "@govoplan/core-webui";
|
||||
import { usePlatformModuleInstalled } from "@govoplan/core-webui";
|
||||
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
||||
import { useCampaignDraftEditor } from "./hooks/useCampaignDraftEditor";
|
||||
import { asRecord, isAuditLockedVersion } from "./utils/campaignView";
|
||||
import { cloneJson, getBool, getNumber, getText, updateNested } from "./utils/draftEditor";
|
||||
import { getDraftFields } from "./utils/fieldDefinitions";
|
||||
import PostboxTargetsDialog, {
|
||||
normalizePostboxTargets
|
||||
} from "./components/PostboxTargetsDialog";
|
||||
|
||||
const behaviorOptions = ["block", "ask", "drop", "continue", "warn"];
|
||||
|
||||
@@ -34,6 +43,14 @@ type GlobalSettingsPageProps = {
|
||||
export default function GlobalSettingsPage({ settings, auth, campaignId, view = "settings" }: GlobalSettingsPageProps) {
|
||||
const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId);
|
||||
const [editorState, setEditorState] = useState<EditorState>({});
|
||||
const [postboxCatalog, setPostboxCatalog] = useState<CampaignPostboxCatalog>({
|
||||
available: false,
|
||||
postboxes: [],
|
||||
templates: [],
|
||||
organization_units: []
|
||||
});
|
||||
const [postboxTargetsOpen, setPostboxTargetsOpen] = useState(false);
|
||||
const postboxModuleInstalled = usePlatformModuleInstalled("postbox");
|
||||
const isPolicyView = view === "policy";
|
||||
|
||||
const version = data.currentVersion;
|
||||
@@ -59,12 +76,53 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
||||
const delivery = asRecord(displayDraft.delivery);
|
||||
const rateLimit = asRecord(delivery.rate_limit);
|
||||
const retry = asRecord(delivery.retry);
|
||||
const postboxDelivery = asRecord(delivery.postbox);
|
||||
const fieldDefinitions = useMemo(
|
||||
() => getDraftFields(displayDraft),
|
||||
[displayDraft]
|
||||
);
|
||||
const statusTracking = asRecord(displayDraft.status_tracking);
|
||||
const optIns = asRecord(editorState.opt_ins);
|
||||
const canReadRetentionPolicy = hasScope(auth, "admin:policies:read");
|
||||
const canWriteRetentionPolicy = hasScope(auth, "admin:policies:write");
|
||||
const pageTitle = isPolicyView ? "i18n:govoplan-campaign.campaign_policies.0b5de1f5" : "i18n:govoplan-campaign.campaign_settings.efffec26";
|
||||
|
||||
useEffect(() => {
|
||||
if (!postboxModuleInstalled) {
|
||||
setPostboxCatalog({
|
||||
available: false,
|
||||
postboxes: [],
|
||||
templates: [],
|
||||
organization_units: []
|
||||
});
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void getCampaignPostboxCatalog(settings, campaignId)
|
||||
.then((catalog) => {
|
||||
if (!cancelled) setPostboxCatalog(catalog);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setPostboxCatalog({
|
||||
available: false,
|
||||
postboxes: [],
|
||||
templates: [],
|
||||
organization_units: []
|
||||
});
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
campaignId,
|
||||
postboxModuleInstalled,
|
||||
settings.accessToken,
|
||||
settings.apiBaseUrl,
|
||||
settings.apiKey
|
||||
]);
|
||||
|
||||
function patchEditor(path: string[], value: unknown) {
|
||||
if (locked) return;
|
||||
setEditorState((current) => updateNested(current, path, value));
|
||||
@@ -232,6 +290,43 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
||||
<FormField label="i18n:govoplan-campaign.max_attempts.f684fef4"><input type="number" min={1} value={getNumber(retry, "max_attempts", 3)} disabled={locked} onChange={(event) => patch(["delivery", "retry", "max_attempts"], Number(event.target.value || 1))} /></FormField>
|
||||
<ToggleSwitch label="i18n:govoplan-campaign.status_tracking.15f61f88" checked={getBool(statusTracking, "enabled", true)} disabled={locked} onChange={(checked) => patch(["status_tracking", "enabled"], checked)} />
|
||||
</div>
|
||||
{postboxModuleInstalled &&
|
||||
<div className="campaign-postbox-delivery-settings">
|
||||
<FormField label="Delivery channels">
|
||||
<select
|
||||
value={getText(delivery, "channel_policy", "mail")}
|
||||
disabled={locked}
|
||||
onChange={(event) => patch(["delivery", "channel_policy"], event.target.value)}
|
||||
>
|
||||
<option value="mail">Mail</option>
|
||||
<option value="postbox">Postbox</option>
|
||||
<option value="mail_and_postbox">Mail and Postbox</option>
|
||||
<option value="mail_then_postbox">Mail, then Postbox on pre-acceptance rejection</option>
|
||||
<option value="postbox_then_mail">Postbox, then Mail on pre-acceptance rejection</option>
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="Postbox classification">
|
||||
<input
|
||||
value={getText(postboxDelivery, "classification", "internal")}
|
||||
disabled={locked}
|
||||
onChange={(event) => patch(["delivery", "postbox", "classification"], event.target.value)}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Default Postbox targets">
|
||||
<Button
|
||||
disabled={locked || !postboxCatalog.available}
|
||||
onClick={() => setPostboxTargetsOpen(true)}
|
||||
>
|
||||
Configure ({normalizePostboxTargets(postboxDelivery.targets).length})
|
||||
</Button>
|
||||
</FormField>
|
||||
{!postboxCatalog.available &&
|
||||
<DismissibleAlert tone="warning" dismissible={false}>
|
||||
Postbox delivery is installed but its delivery catalog is unavailable.
|
||||
</DismissibleAlert>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</Card>
|
||||
|
||||
<Card title="i18n:govoplan-campaign.opt_ins_and_local_assistance.d0d23635" collapsible>
|
||||
@@ -246,6 +341,21 @@ export default function GlobalSettingsPage({ settings, auth, campaignId, view =
|
||||
</>
|
||||
}
|
||||
</LoadingFrame>
|
||||
{postboxTargetsOpen &&
|
||||
<PostboxTargetsDialog
|
||||
open
|
||||
title="Default Postbox targets"
|
||||
catalog={postboxCatalog}
|
||||
fields={fieldDefinitions}
|
||||
targets={normalizePostboxTargets(postboxDelivery.targets)}
|
||||
locked={locked}
|
||||
onSave={(targets) => {
|
||||
patch(["delivery", "postbox", "targets"], targets);
|
||||
setPostboxTargetsOpen(false);
|
||||
}}
|
||||
onClose={() => setPostboxTargetsOpen(false)}
|
||||
/>
|
||||
}
|
||||
</div>);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user