foxygit / RPA-Remisser Log in
commits tags

/webui/frontend/src/components/Fields.tsx · 3.07 KB

raw
import type { ReactNode } from "react";
import type { Field } from "../lib/schema";
import { fieldLabel } from "../lib/schema";
import type { Lang } from "../lib/i18n";
import { ImageField } from "./ImageField";

interface Props {
  values: Record<string, unknown>;
  fields: Field[];
  lang: Lang;
  /** true om fältet "scope" ska visas (steget ligger i en loop, är loop självt,
   *  eller är extract_row). */
  showScope?: boolean;
  onChange: (patch: Record<string, unknown>) => void;
}

/** Renderar schemadrivna fält (steg eller villkor). field_list hanteras separat. */
export function Fields({ values, fields, lang, showScope, onChange }: Props) {
  const effective = (key: string) => {
    if (values[key] !== undefined && values[key] !== "") return String(values[key]);
    const f = fields.find((x) => x.key === key);
    return f?.default !== undefined ? String(f.default) : "";
  };
  const visible = fields.filter((f) => {
    if (f.kind === "field_list") return false;
    if (f.key === "scope") return !!showScope;
    if (f.visible_when) {
      for (const [k, v] of Object.entries(f.visible_when)) {
        if (effective(k) !== String(v)) return false;
      }
    }
    return true;
  });
  if (visible.length === 0) return null;

  return (
    <div className="fields">
      {visible.map((f) => (
        <FieldRow key={f.key} field={f} value={values[f.key]} lang={lang} onChange={(v) => onChange({ [f.key]: v })} />
      ))}
    </div>
  );
}

function FieldRow({
  field,
  value,
  lang,
  onChange,
}: {
  field: Field;
  value: unknown;
  lang: Lang;
  onChange: (v: unknown) => void;
}) {
  const label = fieldLabel(field, lang);
  const title = field.help ?? label;

  if (field.kind === "image") {
    return <ImageField label={label} value={String(value ?? "")} onChange={onChange} />;
  }

  let input: ReactNode;
  if (field.kind === "select") {
    input = (
      <select
        value={String(value ?? field.default ?? field.options?.[0] ?? "")}
        title={title}
        onChange={(e) => onChange(e.target.value)}
      >
        {(field.options ?? []).map((o) => (
          <option key={o} value={o}>
            {o === "" ? "—" : o}
          </option>
        ))}
      </select>
    );
  } else if (field.kind === "bool") {
    input = (
      <input
        type="checkbox"
        checked={value === true || value === "true"}
        title={title}
        onChange={(e) => onChange(e.target.checked)}
      />
    );
  } else if (field.kind === "number") {
    input = (
      <input
        type="number"
        value={value === undefined || value === null ? "" : String(value)}
        title={title}
        onChange={(e) => onChange(e.target.value === "" ? undefined : Number(e.target.value))}
      />
    );
  } else {
    input = (
      <input
        type="text"
        value={value === undefined || value === null ? "" : String(value)}
        title={title}
        onChange={(e) => onChange(e.target.value)}
      />
    );
  }

  return (
    <label className={"field" + (field.kind === "bool" ? " field-bool" : "")}>
      <span>{label}</span>
      {input}
    </label>
  );
}