foxygit / RPA-Remisser Log in
commits tags

/webui/frontend/src/components/RunDialog.tsx · 4.77 KB

raw
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>
  );
}