Release govoplan-campaign v0.1.28: stabilize saving, review and delivery recovery
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-09-08 01:32:26 +02:00
parent 1b32427813
commit c51fc180fb
111 changed files with 6905 additions and 980 deletions
@@ -0,0 +1,82 @@
import { useEffect, useState } from "react";
import {
Button,
DismissibleAlert,
hasScope,
useGuardedNavigate,
usePlatformModuleInstalled
} from "@govoplan/core-webui";
import type { ApiSettings, AuthInfo } from "../../../types";
import {
getCampaignArchiveEncryptionPolicy,
type CampaignArchiveEncryptionPolicy
} from "../../../api/campaigns";
export const UNAVAILABLE_ARCHIVE_POLICY: CampaignArchiveEncryptionPolicy = {
available: false,
allowed_password_encryption_methods: ["aes"],
allowed_password_delivery_channels: ["separate_mail", "sms", "letter", "phone", "in_person"],
policy_hash: "",
source_path: [],
reason: "Archive-encryption policy is loading. Legacy ZipCrypto remains blocked.",
diagnostics: [],
legacy_label: "Legacy ZipCrypto — Windows-compatible, weak encryption"
};
export default function CampaignArchiveEncryptionPolicyNotice({
settings,
auth,
campaignId,
onPolicyChange
}: {
settings: ApiSettings;
auth: AuthInfo;
campaignId: string;
onPolicyChange?: (policy: CampaignArchiveEncryptionPolicy) => void;
}) {
const navigate = useGuardedNavigate();
const policyInstalled = usePlatformModuleInstalled("policy");
const [policy, setPolicy] = useState(UNAVAILABLE_ARCHIVE_POLICY);
const [loading, setLoading] = useState(true);
const [refresh, setRefresh] = useState(0);
const allowed = policy.available && policy.allowed_password_encryption_methods.includes("zip_standard");
const canReadPolicy = policyInstalled && hasScope(auth, "admin:policies:read");
const canUseLegacy = hasScope(auth, "campaigns:archive:use_legacy_zipcrypto");
useEffect(() => {
let cancelled = false;
setLoading(true);
setPolicy(UNAVAILABLE_ARCHIVE_POLICY);
void getCampaignArchiveEncryptionPolicy(settings, campaignId)
.then((loaded) => { if (!cancelled) setPolicy(loaded); })
.catch((cause) => {
if (!cancelled) setPolicy({
...UNAVAILABLE_ARCHIVE_POLICY,
reason: cause instanceof Error ? cause.message : String(cause)
});
})
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, [campaignId, refresh, settings.accessToken, settings.apiBaseUrl, settings.apiKey]);
useEffect(() => { onPolicyChange?.(policy); }, [onPolicyChange, policy]);
return <DismissibleAlert tone={allowed ? "warning" : "info"} dismissible={false} compact>
<strong>{policy.legacy_label}</strong>: {policy.reason}
{policy.source_path.length > 0 && <p>{policy.source_path.map((step) => step.label).join(" → ")}</p>}
<p>i18n:govoplan-campaign.archive_policy_enable_guidance</p>
{!canUseLegacy && <p>i18n:govoplan-campaign.archive_policy_permission_missing</p>}
{!policyInstalled && <p>i18n:govoplan-campaign.archive_policy_module_missing</p>}
{canReadPolicy && <Button
helpContextId="campaign.archive-encryption"
helpModuleId="campaigns"
onClick={() => navigate("/admin?section=system-campaign-archive-encryption")}
>i18n:govoplan-campaign.archive_policy_open_system</Button>}
{canReadPolicy && <Button
onClick={() => navigate("/admin?section=tenant-campaign-archive-encryption")}
>i18n:govoplan-campaign.archive_policy_open_tenant</Button>}
<Button disabled={loading} onClick={() => setRefresh((value) => value + 1)}>
i18n:govoplan-campaign.archive_policy_reload
</Button>
</DismissibleAlert>;
}