chore: sync GovOPlaN module split state
This commit is contained in:
@@ -6,7 +6,7 @@ import { Card } from "@govoplan/core-webui";
|
||||
import { FormField } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { DismissibleAlert, SegmentedControl, i18nMessage } from "@govoplan/core-webui";
|
||||
import LockedVersionNotice from "./components/LockedVersionNotice";
|
||||
import VersionLine from "./components/VersionLine";
|
||||
import CampaignMessagePreviewOverlay, { type CampaignMessagePreviewAttachment } from "./components/MessagePreviewOverlay";
|
||||
@@ -23,7 +23,7 @@ type TemplateBodyMode = "text" | "html" | "both";
|
||||
type BodyEditorMode = "text" | "html";
|
||||
type EditorTarget = "subject" | "text" | "html";
|
||||
|
||||
export default function TemplateDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
||||
export default function TemplateDataPage({ settings, campaignId }: {settings: ApiSettings;campaignId: string;}) {
|
||||
const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId);
|
||||
const [activeBodyEditor, setActiveBodyEditor] = useState<BodyEditorMode>("text");
|
||||
const [activeEditor, setActiveEditor] = useState<EditorTarget>("text");
|
||||
@@ -47,9 +47,9 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
reload,
|
||||
setError,
|
||||
currentStep: "template",
|
||||
unsavedTitle: "Unsaved template changes",
|
||||
unsavedMessage: "The template has unsaved changes. Save them before leaving, or discard them and continue.",
|
||||
loadedLabel: (loadedVersion) => loadedVersion.autosaved_at ? `Loaded autosave ${formatDateTime(loadedVersion.autosaved_at)}` : "Loaded",
|
||||
unsavedTitle: "i18n:govoplan-campaign.unsaved_template_changes.4209cdec",
|
||||
unsavedMessage: "i18n:govoplan-campaign.the_template_has_unsaved_changes_save_them_befor.89f74fc1",
|
||||
loadedLabel: (loadedVersion) => loadedVersion.autosaved_at ? `Loaded autosave ${formatDateTime(loadedVersion.autosaved_at)}` : "i18n:govoplan-campaign.loaded.6db90a0a",
|
||||
onLoaded: () => setPreviewIndex(0)
|
||||
});
|
||||
const template = asRecord(displayDraft.template);
|
||||
@@ -78,10 +78,10 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
const previewEntry = previewSelection.entry;
|
||||
const ignoreEmptyFields = getBool(asRecord(displayDraft.validation_policy), "ignore_empty_fields", false);
|
||||
const templateText = [
|
||||
getText(template, "subject"),
|
||||
templateBodyMode !== "html" ? getText(template, "text") : "",
|
||||
templateBodyMode !== "text" ? getText(template, "html") : ""
|
||||
].join("\n");
|
||||
getText(template, "subject"),
|
||||
templateBodyMode !== "html" ? getText(template, "text") : "",
|
||||
templateBodyMode !== "text" ? getText(template, "html") : ""].
|
||||
join("\n");
|
||||
const usedPlaceholders = useMemo(() => extractTemplatePlaceholders(templateText), [templateText]);
|
||||
const invalidNamespacePlaceholders = useMemo(() => uniquePlaceholders(usedPlaceholders.filter((field) => !field.validNamespace)), [usedPlaceholders]);
|
||||
const undefinedPlaceholders = useMemo(
|
||||
@@ -122,19 +122,19 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
void previewCampaignAttachments(settings, campaignId, version.id, {
|
||||
include_unmatched: false,
|
||||
campaign_json: campaignJsonForAttachmentPreview(displayDraft)
|
||||
})
|
||||
.then((response) => {
|
||||
if (!cancelled) setAttachmentPreviewRules(response.rules);
|
||||
})
|
||||
.catch((reason: unknown) => {
|
||||
if (!cancelled) {
|
||||
setAttachmentPreviewRules([]);
|
||||
setAttachmentPreviewError(reason instanceof Error ? reason.message : String(reason));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setAttachmentPreviewLoading(false);
|
||||
});
|
||||
}).
|
||||
then((response) => {
|
||||
if (!cancelled) setAttachmentPreviewRules(response.rules);
|
||||
}).
|
||||
catch((reason: unknown) => {
|
||||
if (!cancelled) {
|
||||
setAttachmentPreviewRules([]);
|
||||
setAttachmentPreviewError(reason instanceof Error ? reason.message : String(reason));
|
||||
}
|
||||
}).
|
||||
finally(() => {
|
||||
if (!cancelled) setAttachmentPreviewLoading(false);
|
||||
});
|
||||
}, 120);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
@@ -179,15 +179,15 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
const alreadyDefined = existingFields.some((item) => String(item.name || item.id || "") === field.name);
|
||||
if (!alreadyDefined) {
|
||||
patch(["fields"], [
|
||||
...existingFields,
|
||||
{
|
||||
name: field.name,
|
||||
label: humanizeFieldName(field.name),
|
||||
type: "string",
|
||||
required: false,
|
||||
can_override: true
|
||||
}
|
||||
]);
|
||||
...existingFields,
|
||||
{
|
||||
name: field.name,
|
||||
label: humanizeFieldName(field.name),
|
||||
type: "string",
|
||||
required: false,
|
||||
can_override: true
|
||||
}]
|
||||
);
|
||||
}
|
||||
setUndefinedDialog(null);
|
||||
}
|
||||
@@ -228,106 +228,123 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
<div className="content-pad workspace-data-page">
|
||||
<div className="page-heading split workspace-heading">
|
||||
<div>
|
||||
<PageTitle loading={loading}>Template</PageTitle>
|
||||
<PageTitle loading={loading}>i18n:govoplan-campaign.template.3ec1ae06</PageTitle>
|
||||
<VersionLine version={version} versions={data.versions} status={saveState} />
|
||||
</div>
|
||||
<div className="button-row compact-actions">
|
||||
<Button disabled>Manage templates</Button>
|
||||
<Button onClick={reload} disabled={loading}>Reload</Button>
|
||||
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>Save</Button>
|
||||
<Button disabled>i18n:govoplan-campaign.manage_templates.23688071</Button>
|
||||
<Button onClick={reload} disabled={loading}>i18n:govoplan-campaign.reload.cce71553</Button>
|
||||
<Button variant="primary" onClick={() => saveDraft("manual")} disabled={!dirty || locked || !draft}>i18n:govoplan-campaign.save.efc007a3</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{localError && <DismissibleAlert tone="danger" resetKey={localError} floating>{localError}</DismissibleAlert>}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="This page is read-only for the selected version." />}
|
||||
{locked && <LockedVersionNotice settings={settings} campaignId={campaignId} version={version} currentVersionId={data.campaign?.current_version_id} reload={reload} message="i18n:govoplan-campaign.this_page_is_read_only_for_the_selected_version.dacf5743" />}
|
||||
|
||||
<LoadingFrame loading={loading || !draft} label="Loading campaign draft…">
|
||||
<LoadingFrame loading={loading || !draft} label="i18n:govoplan-campaign.loading_campaign_draft.1cf47e50">
|
||||
<>
|
||||
<div className="dashboard-grid template-editor-grid">
|
||||
<Card title="Editable template" actions={<Button onClick={() => setPreviewOpen(true)}>Preview</Button>}>
|
||||
<Card title="i18n:govoplan-campaign.editable_template.5e747a1e" actions={<Button onClick={() => setPreviewOpen(true)}>i18n:govoplan-campaign.preview.f1fbb2b4</Button>}>
|
||||
<div className="form-grid">
|
||||
<FormField label="Subject">
|
||||
<FormField label="i18n:govoplan-campaign.subject.8d183dbd">
|
||||
<input
|
||||
ref={subjectRef}
|
||||
value={getText(template, "subject")}
|
||||
disabled={locked}
|
||||
onFocus={() => setActiveEditor("subject")}
|
||||
onChange={(event) => patchTemplateText("subject", event.target.value)}
|
||||
onChange={(event) => patchTemplateText("subject", event.target.value)} />
|
||||
|
||||
</FormField>
|
||||
<FormField label="i18n:govoplan-campaign.message_body_format.5fec42d2">
|
||||
<SegmentedControl
|
||||
className="template-body-mode"
|
||||
size="content"
|
||||
width="inline"
|
||||
ariaLabel="i18n:govoplan-campaign.message_body_format.5fec42d2"
|
||||
value={templateBodyMode}
|
||||
disabled={locked}
|
||||
onChange={patchTemplateBodyMode}
|
||||
options={[
|
||||
{ id: "text", label: "i18n:govoplan-campaign.text_only.9ccbd022" },
|
||||
{ id: "html", label: "i18n:govoplan-campaign.html_only.1c4fbcb1" },
|
||||
{ id: "both", label: "i18n:govoplan-campaign.both.1f469838" }
|
||||
]}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Message body format">
|
||||
<div className="template-body-mode" role="tablist" aria-label="Message body format">
|
||||
<button type="button" className={templateBodyMode === "text" ? "active" : ""} disabled={locked} onClick={() => patchTemplateBodyMode("text")}>Text only</button>
|
||||
<button type="button" className={templateBodyMode === "html" ? "active" : ""} disabled={locked} onClick={() => patchTemplateBodyMode("html")}>HTML only</button>
|
||||
<button type="button" className={templateBodyMode === "both" ? "active" : ""} disabled={locked} onClick={() => patchTemplateBodyMode("both")}>Both</button>
|
||||
</div>
|
||||
</FormField>
|
||||
{templateBodyMode === "both" && (
|
||||
<div className="template-body-mode template-editor-mode" role="tablist" aria-label="Body editor">
|
||||
<button type="button" className={visibleBodyEditor === "text" ? "active" : ""} onClick={() => { setActiveBodyEditor("text"); setActiveEditor("text"); }}>Plain text</button>
|
||||
<button type="button" className={visibleBodyEditor === "html" ? "active" : ""} onClick={() => { setActiveBodyEditor("html"); setActiveEditor("html"); }}>HTML</button>
|
||||
</div>
|
||||
)}
|
||||
{visibleBodyEditor === "text" && (
|
||||
<FormField label="Plain text body">
|
||||
{templateBodyMode === "both" &&
|
||||
<SegmentedControl
|
||||
className="template-body-mode template-editor-mode"
|
||||
size="content"
|
||||
width="inline"
|
||||
ariaLabel="i18n:govoplan-campaign.body_editor.8615dd7e"
|
||||
value={visibleBodyEditor}
|
||||
onChange={(mode) => {setActiveBodyEditor(mode);setActiveEditor(mode);}}
|
||||
options={[
|
||||
{ id: "text", label: "i18n:govoplan-campaign.plain_text.9580fcbc" },
|
||||
{ id: "html", label: "i18n:govoplan-campaign.html.9f738ce8" }
|
||||
]}
|
||||
/>
|
||||
}
|
||||
{visibleBodyEditor === "text" &&
|
||||
<FormField label="i18n:govoplan-campaign.plain_text_body.030a0da0">
|
||||
<textarea
|
||||
ref={textRef}
|
||||
rows={16}
|
||||
value={getText(template, "text")}
|
||||
disabled={locked}
|
||||
onFocus={() => { setActiveBodyEditor("text"); setActiveEditor("text"); }}
|
||||
onChange={(event) => patchTemplateText("text", event.target.value)}
|
||||
/>
|
||||
ref={textRef}
|
||||
rows={16}
|
||||
value={getText(template, "text")}
|
||||
disabled={locked}
|
||||
onFocus={() => {setActiveBodyEditor("text");setActiveEditor("text");}}
|
||||
onChange={(event) => patchTemplateText("text", event.target.value)} />
|
||||
|
||||
</FormField>
|
||||
)}
|
||||
{visibleBodyEditor === "html" && (
|
||||
<FormField label="HTML body">
|
||||
}
|
||||
{visibleBodyEditor === "html" &&
|
||||
<FormField label="i18n:govoplan-campaign.html_body.77b5ba37">
|
||||
<textarea
|
||||
ref={htmlRef}
|
||||
rows={16}
|
||||
value={getText(template, "html")}
|
||||
disabled={locked}
|
||||
onFocus={() => { setActiveBodyEditor("html"); setActiveEditor("html"); }}
|
||||
onChange={(event) => patchTemplateText("html", event.target.value)}
|
||||
/>
|
||||
ref={htmlRef}
|
||||
rows={16}
|
||||
value={getText(template, "html")}
|
||||
disabled={locked}
|
||||
onFocus={() => {setActiveBodyEditor("html");setActiveEditor("html");}}
|
||||
onChange={(event) => patchTemplateText("html", event.target.value)} />
|
||||
|
||||
</FormField>
|
||||
)}
|
||||
}
|
||||
<div className="button-row template-editor-actions">
|
||||
<Button disabled>Load from library</Button>
|
||||
<Button disabled>Save to library</Button>
|
||||
<Button disabled>i18n:govoplan-campaign.load_from_library.327ada7c</Button>
|
||||
<Button disabled>i18n:govoplan-campaign.save_to_library.396649bf</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="template-side-stack">
|
||||
<Card title="Fields">
|
||||
{invalidNamespacePlaceholders.length > 0 && (
|
||||
<DismissibleAlert tone="warning" resetKey={invalidNamespacePlaceholders.map((field) => field.namespace || field.raw).join(",")}>Undefined placeholder namespace detected: {invalidNamespacePlaceholders.map((field) => field.namespace || field.raw).join(", ")}.</DismissibleAlert>
|
||||
)}
|
||||
{usedPlaceholders.length === 0 && <p className="muted">No template placeholders detected yet.</p>}
|
||||
<p className="muted small-note">Click a field to insert it at the current cursor position as a namespaced placeholder.</p>
|
||||
<Card title="i18n:govoplan-campaign.fields.e8b68527">
|
||||
{invalidNamespacePlaceholders.length > 0 &&
|
||||
<DismissibleAlert tone="warning" resetKey={invalidNamespacePlaceholders.map((field) => field.namespace || field.raw).join(",")}>i18n:govoplan-campaign.undefined_placeholder_namespace_detected.2ef5c282 {invalidNamespacePlaceholders.map((field) => field.namespace || field.raw).join(", ")}.</DismissibleAlert>
|
||||
}
|
||||
{usedPlaceholders.length === 0 && <p className="muted">i18n:govoplan-campaign.no_template_placeholders_detected_yet.56bba9d1</p>}
|
||||
<p className="muted small-note">i18n:govoplan-campaign.click_a_field_to_insert_it_at_the_current_cursor.643aa7bc</p>
|
||||
|
||||
<h3 className="section-mini-heading">Global fields</h3>
|
||||
<h3 className="section-mini-heading">i18n:govoplan-campaign.global_fields.07f84ea4</h3>
|
||||
<TemplateFieldChipList
|
||||
namespace="global"
|
||||
fields={globalFieldOptions}
|
||||
usedPlaceholders={usedPlaceholders}
|
||||
empty="No campaign fields or global values defined."
|
||||
onInsert={insertPlaceholder}
|
||||
/>
|
||||
empty="i18n:govoplan-campaign.no_campaign_fields_or_global_values_defined.3888623d"
|
||||
onInsert={insertPlaceholder} />
|
||||
|
||||
|
||||
<h3 className="section-mini-heading">Local fields</h3>
|
||||
<p className="muted small-note">Address fields use the effective merged or overridden headers. Use <code>to[2]</code> or <code>to[2].email</code> for a specific additional address.</p>
|
||||
<h3 className="section-mini-heading">i18n:govoplan-campaign.local_fields.c81bb4de</h3>
|
||||
<p className="muted small-note">i18n:govoplan-campaign.address_fields_use_the_effective_merged_or_overr.d6930da6 <code>i18n:govoplan-campaign.to_2.8c1db0c7</code> or <code>i18n:govoplan-campaign.to_2_email.5ad61c1a</code> i18n:govoplan-campaign.for_a_specific_additional_address.1199883f</p>
|
||||
<TemplateFieldChipList
|
||||
namespace="local"
|
||||
fields={localFieldOptions}
|
||||
usedPlaceholders={usedPlaceholders}
|
||||
empty="No campaign fields defined."
|
||||
onInsert={insertPlaceholder}
|
||||
/>
|
||||
empty="i18n:govoplan-campaign.no_campaign_fields_defined.8c2ea4b2"
|
||||
onInsert={insertPlaceholder} />
|
||||
|
||||
|
||||
<h3 className="section-mini-heading">Used in template, but undefined</h3>
|
||||
<h3 className="section-mini-heading">i18n:govoplan-campaign.used_in_template_but_undefined.57b5f3da</h3>
|
||||
<UndefinedPlaceholderList items={undefinedPlaceholders} onSelect={setUndefinedDialog} />
|
||||
</Card>
|
||||
</div>
|
||||
@@ -336,84 +353,89 @@ export default function TemplateDataPage({ settings, campaignId }: { settings: A
|
||||
</>
|
||||
</LoadingFrame>
|
||||
|
||||
{previewOpen && (
|
||||
<CampaignMessagePreviewOverlay
|
||||
title="Template preview"
|
||||
bodyMode={visibleBodyEditor}
|
||||
subject={previewSubject}
|
||||
text={templateBodyMode === "html" ? null : previewText}
|
||||
html={templateBodyMode === "text" ? null : previewHtml}
|
||||
metaItems={templatePreviewMetaItems(previewContext)}
|
||||
recipientLabel={activePreviewEntries.length > 0 ? recipientLabel(previewEntry, previewSelection.sourceIndex - 1) : "Global preview"}
|
||||
recipientNote={activePreviewEntries.length > 0 ? `${Math.min(previewIndex, previewEntries.length - 1) + 1} of ${previewEntries.length}` : inlineEntries.length > 0 ? "No recipient preview is available." : "No inline recipients are available yet."}
|
||||
attachments={previewAttachments}
|
||||
navigation={{
|
||||
index: Math.min(previewIndex, previewEntries.length - 1),
|
||||
total: previewEntries.length,
|
||||
onFirst: () => setPreviewIndex(0),
|
||||
onPrevious: () => setPreviewIndex((value) => Math.max(0, value - 1)),
|
||||
onNext: () => setPreviewIndex((value) => Math.min(previewEntries.length - 1, value + 1)),
|
||||
onLast: () => setPreviewIndex(previewEntries.length - 1)
|
||||
}}
|
||||
onClose={() => setPreviewOpen(false)}
|
||||
/>
|
||||
)}
|
||||
{previewOpen &&
|
||||
<CampaignMessagePreviewOverlay
|
||||
title="i18n:govoplan-campaign.template_preview.ea0aa8b2"
|
||||
bodyMode={visibleBodyEditor}
|
||||
subject={previewSubject}
|
||||
text={templateBodyMode === "html" ? null : previewText}
|
||||
html={templateBodyMode === "text" ? null : previewHtml}
|
||||
metaItems={templatePreviewMetaItems(previewContext)}
|
||||
recipientLabel={activePreviewEntries.length > 0 ? recipientLabel(previewEntry, previewSelection.sourceIndex - 1) : "i18n:govoplan-campaign.global_preview.f81f09b2"}
|
||||
recipientNote={activePreviewEntries.length > 0 ? `${Math.min(previewIndex, previewEntries.length - 1) + 1} of ${previewEntries.length}` : inlineEntries.length > 0 ? "i18n:govoplan-campaign.no_recipient_preview_is_available.9e93a97d" : "i18n:govoplan-campaign.no_inline_recipients_are_available_yet.e405bacb"}
|
||||
attachments={previewAttachments}
|
||||
navigation={{
|
||||
index: Math.min(previewIndex, previewEntries.length - 1),
|
||||
total: previewEntries.length,
|
||||
onFirst: () => setPreviewIndex(0),
|
||||
onPrevious: () => setPreviewIndex((value) => Math.max(0, value - 1)),
|
||||
onNext: () => setPreviewIndex((value) => Math.min(previewEntries.length - 1, value + 1)),
|
||||
onLast: () => setPreviewIndex(previewEntries.length - 1)
|
||||
}}
|
||||
onClose={() => setPreviewOpen(false)} />
|
||||
|
||||
}
|
||||
|
||||
<UndefinedPlaceholderDecisionDialog
|
||||
field={undefinedDialog}
|
||||
contextLabel="template"
|
||||
removeLabel="Remove from template"
|
||||
removeLabel="i18n:govoplan-campaign.remove_from_template.5fb2827b"
|
||||
onCancel={() => setUndefinedDialog(null)}
|
||||
onRemove={removePlaceholder}
|
||||
onReplace={replacePlaceholder}
|
||||
onAddField={addUndefinedField}
|
||||
localFields={localFieldOptions}
|
||||
globalFields={globalFieldOptions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
globalFields={globalFieldOptions} />
|
||||
|
||||
</div>);
|
||||
|
||||
}
|
||||
|
||||
function mapResolvedAttachmentsToPreviewBoxes(
|
||||
rules: CampaignAttachmentPreviewRule[],
|
||||
loading: boolean,
|
||||
error: string,
|
||||
draft: Record<string, unknown>
|
||||
): CampaignMessagePreviewAttachment[] {
|
||||
rules: CampaignAttachmentPreviewRule[],
|
||||
loading: boolean,
|
||||
error: string,
|
||||
draft: Record<string, unknown>)
|
||||
: CampaignMessagePreviewAttachment[] {
|
||||
if (loading) {
|
||||
return [{ filename: "Resolving attachment patterns…", detail: "Managed files are being checked for this recipient." }];
|
||||
return [{
|
||||
filename: i18nMessage("i18n:govoplan-campaign.resolving_attachment_patterns.87d7d21b"),
|
||||
detail: i18nMessage("i18n:govoplan-campaign.managed_files_are_being_checked_for_this_recipie.489ecefd")
|
||||
}];
|
||||
}
|
||||
if (error) {
|
||||
return [{ filename: "Attachment preview unavailable", detail: error }];
|
||||
return [{ filename: i18nMessage("i18n:govoplan-campaign.attachment_preview_unavailable.62211756"), detail: error }];
|
||||
}
|
||||
return rules.flatMap((rule) => {
|
||||
const zipProtection = zipProtectionForRule(rule, draft);
|
||||
const detailParts = [
|
||||
rule.source === "global" ? "Global" : "Recipient",
|
||||
rule.label,
|
||||
rule.required ? "required" : "optional",
|
||||
rule.pattern
|
||||
].filter(Boolean);
|
||||
rule.source === "global" ? i18nMessage("i18n:govoplan-campaign.global.5f1184f7") : i18nMessage("i18n:govoplan-campaign.recipient.90343260"),
|
||||
rule.label,
|
||||
rule.required ? i18nMessage("i18n:govoplan-campaign.required.eed6bfb4") : i18nMessage("i18n:govoplan-campaign.optional.0c6c4102"),
|
||||
rule.pattern].
|
||||
filter(Boolean);
|
||||
const detail = detailParts.join(" · ");
|
||||
const fallbackArchiveLabel = i18nMessage("i18n:govoplan-campaign.recipient_attachments_zip.79d436c7");
|
||||
if (rule.matches.length > 0) {
|
||||
return rule.matches.map((match) => ({
|
||||
filename: match.filename || match.display_path,
|
||||
label: rule.label,
|
||||
detail: `${detail}${match.display_path ? ` · ${match.display_path}` : ""}`,
|
||||
detail: i18nMessage("i18n:govoplan-campaign.value_value.a8618e4a", { value0: detail, value1: match.display_path ? ` · ${match.display_path}` : "" }),
|
||||
contentType: match.content_type,
|
||||
sizeBytes: match.size_bytes,
|
||||
archiveGroup: rule.zip_included ? (rule.zip_filename || "recipient-attachments.zip") : null,
|
||||
archiveLabel: rule.zip_included ? (rule.zip_filename || "Recipient attachments ZIP") : null,
|
||||
archiveGroup: rule.zip_included ? rule.zip_filename || "recipient-attachments.zip" : null,
|
||||
archiveLabel: rule.zip_included ? rule.zip_filename || fallbackArchiveLabel : null,
|
||||
protected: zipProtection.protected,
|
||||
protectionNote: zipProtection.note
|
||||
}));
|
||||
}
|
||||
const pattern = rule.pattern || i18nMessage("i18n:govoplan-campaign.attachment_pattern.3540c952");
|
||||
return [{
|
||||
filename: `No file matched ${rule.pattern || "attachment pattern"}`,
|
||||
filename: i18nMessage("i18n:govoplan-campaign.no_file_matched_value", { value0: pattern }),
|
||||
label: rule.label,
|
||||
detail: `${detail}${rule.status !== "ok" ? ` · ${rule.status}` : ""}`,
|
||||
archiveGroup: rule.zip_included ? (rule.zip_filename || "recipient-attachments.zip") : null,
|
||||
archiveLabel: rule.zip_included ? (rule.zip_filename || "Recipient attachments ZIP") : null,
|
||||
detail: i18nMessage("i18n:govoplan-campaign.value_value.a8618e4a", { value0: detail, value1: rule.status !== "ok" ? ` · ${rule.status}` : "" }),
|
||||
archiveGroup: rule.zip_included ? rule.zip_filename || "recipient-attachments.zip" : null,
|
||||
archiveLabel: rule.zip_included ? rule.zip_filename || fallbackArchiveLabel : null,
|
||||
protected: zipProtection.protected,
|
||||
protectionNote: zipProtection.note
|
||||
}];
|
||||
@@ -422,22 +444,22 @@ function mapResolvedAttachmentsToPreviewBoxes(
|
||||
|
||||
function templatePreviewMetaItems(context: Record<string, string>) {
|
||||
return [
|
||||
{ label: "From", value: context["local:from"] || null },
|
||||
{ label: "To", value: context["local:all_to"] || null },
|
||||
{ label: "Reply-To", value: context["local:all_reply_to"] || null },
|
||||
{ label: "Cc", value: context["local:all_cc"] || null },
|
||||
{ label: "Bcc", value: context["local:all_bcc"] || null }
|
||||
];
|
||||
{ label: "i18n:govoplan-campaign.from.3f66052a", value: context["local:from"] || null },
|
||||
{ label: "i18n:govoplan-campaign.to.ae79ea1e", value: context["local:all_to"] || null },
|
||||
{ label: "i18n:govoplan-campaign.reply_to.c1733667", value: context["local:all_reply_to"] || null },
|
||||
{ label: "i18n:govoplan-campaign.cc.1fd6a880", value: context["local:all_cc"] || null },
|
||||
{ label: "i18n:govoplan-campaign.bcc.8431acad", value: context["local:all_bcc"] || null }];
|
||||
|
||||
}
|
||||
|
||||
function zipProtectionForRule(rule: CampaignAttachmentPreviewRule, draft: Record<string, unknown>): { protected: boolean; note: string | null } {
|
||||
function zipProtectionForRule(rule: CampaignAttachmentPreviewRule, draft: Record<string, unknown>): {protected: boolean;note: string | null;} {
|
||||
if (!rule.zip_included) return { protected: false, note: null };
|
||||
const zipConfig = asRecord(asRecord(draft.attachments).zip);
|
||||
const archives = asArray(zipConfig.archives).map(asRecord);
|
||||
const archive = archives.find((item) => {
|
||||
const id = getText(item, "id");
|
||||
const name = getText(item, "name", getText(item, "filename_template"));
|
||||
return (rule.zip_archive_id && id === rule.zip_archive_id) || (rule.zip_filename && name === rule.zip_filename);
|
||||
return rule.zip_archive_id && id === rule.zip_archive_id || rule.zip_filename && name === rule.zip_filename;
|
||||
}) ?? archives.find((item) => getBool(item, "standard")) ?? archives[0];
|
||||
if (!archive) return { protected: false, note: null };
|
||||
const legacyMode = getText(archive, "password_mode");
|
||||
@@ -445,13 +467,18 @@ function zipProtectionForRule(rule: CampaignAttachmentPreviewRule, draft: Record
|
||||
if (!protectedArchive) return { protected: false, note: null };
|
||||
const field = getText(archive, "password_field");
|
||||
const scope = getText(archive, "password_scope") === "global" ? "global" : "local";
|
||||
const method = getText(archive, "method", "aes") === "zip_standard" ? "ZipCrypto" : "AES";
|
||||
const source = field ? `${humanizeScope(scope)} field "${field}"` : "";
|
||||
return { protected: true, note: [source, `Encryption: ${method}`].filter(Boolean).join(" · ") };
|
||||
const method = getText(archive, "method", "aes") === "zip_standard" ? "i18n:govoplan-campaign.zipcrypto.03bf7fb4" : "i18n:govoplan-campaign.aes.41f215a6";
|
||||
const source = field ? i18nMessage("i18n:govoplan-campaign.scope_field_value", { value0: humanizeScope(scope), value1: field }) : "";
|
||||
return {
|
||||
protected: true,
|
||||
note: source ?
|
||||
i18nMessage("i18n:govoplan-campaign.value_encryption_value", { value0: source, value1: method }) :
|
||||
i18nMessage("i18n:govoplan-campaign.encryption_value", { value0: method })
|
||||
};
|
||||
}
|
||||
|
||||
function humanizeScope(scope: string): string {
|
||||
return scope === "global" ? "Global" : "Local";
|
||||
return scope === "global" ? "i18n:govoplan-campaign.global.5f1184f7" : "i18n:govoplan-campaign.local.dc99d54d";
|
||||
}
|
||||
|
||||
function recipientLabel(entry: Record<string, unknown>, index: number): string {
|
||||
@@ -460,7 +487,7 @@ function recipientLabel(entry: Record<string, unknown>, index: number): string {
|
||||
if (name && email) return `${name} <${email}>`;
|
||||
if (name) return name;
|
||||
if (email) return email;
|
||||
return `Recipient ${index + 1}`;
|
||||
return i18nMessage("i18n:govoplan-campaign.recipient_value.d0233fb6", { value0: index + 1 });
|
||||
}
|
||||
|
||||
function normalizeTemplateBodyMode(value: string): TemplateBodyMode {
|
||||
|
||||
Reference in New Issue
Block a user