commits
tags
import { useEffect, useMemo, useRef, useState } from "react";
import type { Flow } from "../lib/api";
import type { T } from "../lib/i18n";
import { defaultsFor, parseOptions, resolveRunVars } from "../lib/runvars";
interface Props {
open: boolean;
t: T;
flow: Flow;
flowName: string | null;
onRun: (initialVars: Record<string, string>) => void;
onSaveSet: (name: string, values: Record<string, string>) => void;
onClose: () => void;
}
export function RunDialog({ open, t, flow, flowName, onRun, onSaveSet, onClose }: Props) {
const ref = useRef<HTMLDialogElement>(null);
const inputs = useMemo(() => flow.inputs ?? [], [flow.inputs]);
const sets = flow.input_sets ?? {};
const setNames = Object.keys(sets);
const [chosenSet, setChosenSet] = useState("");
const [values, setValues] = useState<Record<string, string>>({});
const [adhoc, setAdhoc] = useState<{ name: string; value: string }[]>([]);
useEffect(() => {
const d = ref.current;
if (!d) return;
if (open && !d.open) {
d.showModal();
let seed = defaultsFor(inputs);
try {
const last = JSON.parse(localStorage.getItem("rpa_runvars_" + flowName) || "{}");
seed = { ...seed, ...last };
} catch {
/* ignore */
}
setValues(seed);
setChosenSet("");
setAdhoc([]);
}
if (!open && d.open) d.close();
}, [open, inputs, flowName]);
function pickSet(name: string) {
setChosenSet(name);
if (name && sets[name]) setValues((v) => ({ ...v, ...sets[name] }));
}
function run() {
const vars = resolveRunVars(values, adhoc);
try {
localStorage.setItem("rpa_runvars_" + flowName, JSON.stringify(vars));
} catch {
/* ignore */
}
onRun(vars);
onClose();
}
function saveAsSet() {
const name = prompt(t("runp_set_name_prompt"));
if (name) onSaveSet(name, resolveRunVars(values, adhoc));
}
return (
<dialog ref={ref} onClose={onClose}>
<h2>{t("runp_title")}</h2>
<p className="hint">{t("runp_hint")}</p>
{setNames.length > 0 && (
<label className="field">
<span>{t("runp_set")}</span>
<select value={chosenSet} onChange={(e) => pickSet(e.target.value)}>
<option value="">{t("runp_set_none")}</option>
{setNames.map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</select>
</label>
)}
<div className="fields">
{inputs.map((inp) => {
const opts = parseOptions(inp.options);
const val = values[inp.name] ?? "";
return (
<label className="field" key={inp.name} title={inp.description}>
<span>{inp.label || inp.name}</span>
{opts.length > 0 ? (
<select value={val} onChange={(e) => setValues((v) => ({ ...v, [inp.name]: e.target.value }))}>
{!opts.includes(val) && <option value={val}>{val || "—"}</option>}
{opts.map((o) => (
<option key={o} value={o}>
{o}
</option>
))}
</select>
) : (
<input
type="text"
value={val}
onChange={(e) => setValues((v) => ({ ...v, [inp.name]: e.target.value }))}
/>
)}
</label>
);
})}
</div>
<div className="adhoc">
{adhoc.map((row, i) => (
<div className="adhoc-row" key={i}>
<input
type="text"
placeholder={t("runp_var_name")}
value={row.name}
onChange={(e) => setAdhoc((a) => a.map((r, j) => (j === i ? { ...r, name: e.target.value } : r)))}
/>
<input
type="text"
placeholder={t("runp_var_value")}
value={row.value}
onChange={(e) => setAdhoc((a) => a.map((r, j) => (j === i ? { ...r, value: e.target.value } : r)))}
/>
<button className="danger tiny" onClick={() => setAdhoc((a) => a.filter((_, j) => j !== i))}>
✕
</button>
</div>
))}
<button className="secondary tiny" onClick={() => setAdhoc((a) => [...a, { name: "", value: "" }])}>
{t("runp_add_var")}
</button>
</div>
<div className="dialog-actions">
<button type="button" className="secondary" onClick={saveAsSet}>
{t("runp_save_set")}
</button>
<span className="spacer" />
<button type="button" className="secondary" onClick={onClose}>
{t("cancel")}
</button>
<button type="button" onClick={run}>
{t("run")}
</button>
</div>
</dialog>
);
}