commits
tags
import { useEffect, useRef, useState } from "react";
import type { Flow, FlowInput } from "../lib/api";
import type { Schema } from "../lib/schema";
import { fieldLabel } from "../lib/schema";
import type { Lang, T } from "../lib/i18n";
interface Props {
open: boolean;
t: T;
lang: Lang;
schema: Schema;
flow: Flow;
onChange: (patch: { inputs: FlowInput[]; input_sets: Record<string, Record<string, string>> }) => void;
onClose: () => void;
}
export function InputsEditor({ open, t, lang, schema, flow, onChange, onClose }: Props) {
const ref = useRef<HTMLDialogElement>(null);
const [inputs, setInputs] = useState<FlowInput[]>([]);
const [sets, setSets] = useState<Record<string, Record<string, string>>>({});
useEffect(() => {
const d = ref.current;
if (!d) return;
if (open && !d.open) {
d.showModal();
setInputs((flow.inputs ?? []).map((i) => ({ ...i })));
setSets({ ...(flow.input_sets ?? {}) });
}
if (!open && d.open) d.close();
}, [open, flow.inputs, flow.input_sets]);
const fieldDefs = schema.input_fields ?? [];
function setInput(i: number, patch: Partial<FlowInput>) {
setInputs((prev) => prev.map((x, j) => (j === i ? { ...x, ...patch } : x)));
}
function save() {
const cleaned = inputs.filter((i) => (i.name ?? "").trim());
onChange({ inputs: cleaned, input_sets: sets });
onClose();
}
return (
<dialog ref={ref} onClose={onClose}>
<h2>{t("inputs_title")}</h2>
<p className="hint">{t("inputs_hint")}</p>
<div className="inputs-list">
{inputs.map((inp, i) => (
<div className="input-row" key={i}>
<div className="fields">
{fieldDefs.map((f) => (
<label className="field" key={f.key} title={f.help}>
<span>{fieldLabel(f, lang)}</span>
<input
type="text"
value={String(inp[f.key as keyof FlowInput] ?? "")}
onChange={(e) => setInput(i, { [f.key]: e.target.value } as Partial<FlowInput>)}
/>
</label>
))}
</div>
<button className="danger tiny" onClick={() => setInputs((prev) => prev.filter((_, j) => j !== i))}>
{t("remove")}
</button>
</div>
))}
<button className="secondary" onClick={() => setInputs((prev) => [...prev, { name: "" }])}>
{t("inputs_add")}
</button>
</div>
{Object.keys(sets).length > 0 && (
<>
<strong className="inputs-sets-title">{t("inputs_sets")}</strong>
{Object.entries(sets).map(([name, vals]) => (
<div className="set-row" key={name}>
<span>
{name} <span className="hint">({Object.keys(vals).length})</span>
</span>
<span className="spacer" />
<button
className="secondary tiny"
onClick={() => {
const nn = prompt(t("inputs_set_rename"), name);
if (nn && nn !== name) {
setSets(({ [name]: v, ...rest }) => ({ ...rest, [nn]: v }));
}
}}
>
{t("inputs_set_rename")}
</button>
<button
className="danger tiny"
onClick={() => setSets(({ [name]: _drop, ...rest }) => rest)}
>
{t("remove")}
</button>
</div>
))}
</>
)}
<div className="dialog-actions">
<button type="button" className="secondary" onClick={onClose}>
{t("cancel")}
</button>
<button type="button" onClick={save}>
{t("save")}
</button>
</div>
</dialog>
);
}