Release v0.1.3

This commit is contained in:
2026-06-26 01:39:19 +02:00
parent 23318c709a
commit ab2343aa88
22 changed files with 2176 additions and 197 deletions

View File

@@ -1,3 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { Button } from "@govoplan/core-webui";
import { Dialog } from "@govoplan/core-webui";
import { DismissibleAlert } from "@govoplan/core-webui";
@@ -74,19 +75,49 @@ export function UndefinedPlaceholderDecisionDialog({
field,
contextLabel = "template",
removeLabel = "Remove placeholder",
localFields = [],
globalFields = [],
onCancel,
onRemove,
onReplace,
onAddField,
addDisabled = false
}: {
field: UndefinedPlaceholder | null;
contextLabel?: string;
removeLabel?: string;
localFields?: TemplateFieldOption[];
globalFields?: TemplateFieldOption[];
onCancel: () => void;
onRemove: (field: UndefinedPlaceholder) => void;
onReplace?: (field: UndefinedPlaceholder, namespace: TemplateNamespace, name: string) => void;
onAddField: (field: UndefinedPlaceholder) => void;
addDisabled?: boolean;
}) {
const replacementOptions = useMemo(
() => [
...localFields.map((item) => ({ namespace: "local" as const, ...item })),
...globalFields.map((item) => ({ namespace: "global" as const, ...item }))
],
[globalFields, localFields]
);
const [replacement, setReplacement] = useState("");
const firstReplacement = replacementOptions[0];
const effectiveReplacement = replacement || (firstReplacement ? `${firstReplacement.namespace}:${firstReplacement.name}` : "");
useEffect(() => {
setReplacement(firstReplacement ? `${firstReplacement.namespace}:${firstReplacement.name}` : "");
}, [field?.raw, firstReplacement?.namespace, firstReplacement?.name]);
function replaceWithSelected() {
if (!field || !onReplace || !effectiveReplacement) return;
const separator = effectiveReplacement.indexOf(":");
if (separator < 0) return;
const namespace = effectiveReplacement.slice(0, separator) as TemplateNamespace;
const name = effectiveReplacement.slice(separator + 1);
onReplace(field, namespace, name);
}
return (
<Dialog
open={Boolean(field)}
@@ -98,6 +129,7 @@ export function UndefinedPlaceholderDecisionDialog({
<>
<Button onClick={onCancel}>Continue editing</Button>
<Button onClick={() => onRemove(field)}>{removeLabel}</Button>
{onReplace && replacementOptions.length > 0 && <Button onClick={replaceWithSelected}>Replace</Button>}
<Button variant="primary" onClick={() => onAddField(field)} disabled={!field.name || field.reason === "invalid-namespace" || addDisabled}>Add field</Button>
</>
) : undefined}
@@ -111,6 +143,18 @@ export function UndefinedPlaceholderDecisionDialog({
{field.reason === "missing-field" && (
<p className="muted">You can add <strong>{field.name}</strong> as a campaign field, remove this placeholder, or continue editing.</p>
)}
{onReplace && replacementOptions.length > 0 && (
<label className="template-replacement-field">
<span>Replace by existing field</span>
<select value={effectiveReplacement} onChange={(event) => setReplacement(event.target.value)}>
{replacementOptions.map((option) => (
<option key={`${option.namespace}:${option.name}`} value={`${option.namespace}:${option.name}`}>
{option.namespace}:{option.label || option.name}
</option>
))}
</select>
</label>
)}
</>
)}
</Dialog>