viewForm = async (req, tableOptions, roles, pages, values) => {
  const isEdit =
    values && values.id && !getState().getConfig("development_mode", false);
  const hasTable = Object.entries(getState().viewtemplates)
    .filter(([k, v]) => !v.tableless && !v.table_optional)
    .map(([k, v]) => k);
  const tableOptional = Object.entries(getState().viewtemplates)
    .filter(([k, v]) => v.table_optional)
    .map(([k, v]) => k);
  const slugOptions = await Table.allSlugOptions();
  const viewpatternOptions = Object.values(getState().viewtemplates)
    .filter((vt) => !vt.singleton)
    .map((vt) => vt.name);
  return new Form({
    action: addOnDoneRedirect("/viewedit/save", req),
    submitLabel: req.__("Configure") + " &raquo;",
    blurb: req.__("First, please give some basic information about the view."),
    tabs: { tabsStyle: "Accordion" },
    fields: [
      new Field({
        label: req.__("View name"),
        name: "name",
        type: "String",
        attributes: { autofocus: true },
        sublabel: req.__(
          "The view name is part of the URL when it is shown alone."
        ),
      }),
      new Field({
        label: req.__("View pattern"),
        name: "viewtemplate",
        input_type: "select",
        sublabel: req.__(
          "The view pattern sets the foundation of how the view relates to the table and the behaviour of the view"
        ),
        help: {
          topic: "View patterns",
          context: {},
        },
        options: viewpatternOptions,
        attributes: {
          explainers: mapObjectValues(
            getState().viewtemplates,
            ({ description }) => description
          ),
        },
        disabled: isEdit,
      }),
      new Field({
        label: req.__("Table"),
        name: "table_name",
        input_type: "select",
        sublabel: req.__("Display data from this table"),
        options: tableOptions,
        disabled: isEdit,
        showIf: isEdit
          ? hasTable.includes(values.viewtemplate)
            ? undefined
            : { nosuchvar: true }
          : { viewtemplate: hasTable },
      }),
      ...(tableOptional.length
        ? [
            new Field({
              label: req.__("Table"),
              name: "table_name",
              input_type: "select",
              sublabel: req.__("Display data from this table"),
              options: [{ value: "", label: "Table not set" }, ...tableOptions],
              disabled: isEdit,
              showIf: isEdit
                ? tableOptional.includes(values.viewtemplate)
                  ? undefined
                  : { nosuchvar: true }
                : { viewtemplate: tableOptional },
            }),
          ]
        : []),
      new Field({
        name: "min_role",
        label: req.__("Minimum role"),
        sublabel: req.__("Role required to run view"),
        input_type: "select",
        required: true,
        options: roles.map((r) => ({ value: r.id, label: r.role })),
      }),
      new Field({
        label: req.__("Description"),
        name: "description",
        type: "String",
        sublabel: req.__(
          "Description allows you to give more information about the view."
        ),
      }),
      new Field({
        name: "page_title",
        label: req.__("Page title"),
        type: "String",
        parent_field: "attributes",
        tab: "View settings",
        sublabel: req.__(
          "Some view patterns accept interpolations. Ex: <code>{{ name }}</code> or <code>{{ row ? `Edit ${row.name}` : `New person` }}</code>"
        ),
      }),
      new Field({
        name: "page_description",
        label: req.__("Page description"),
        type: "String",
        parent_field: "attributes",
        tab: "View settings",
        sublabel: req.__(
          "For search engines. Some view patterns accept interpolations."
        ),
      }),
      new Field({
        // legacy
        name: "default_render_page",
        label: req.__("Show on page"),
        sublabel: req.__(
          "Requests to render this view directly will instead show the chosen page, if any. The chosen page should embed this view. Use this to decorate the view with additional elements."
        ),
        input_type: "select",
        tab: "View settings",
        options: [
          { value: "", label: "" },
          ...pages.map((p) => ({ value: p.name, label: p.name })),
        ],
      }),
      new Field({
        name: "slug",
        label: req.__("Slug"),
        sublabel: req.__("Field that can be used for a prettier URL structure"),
        type: "String",
        tab: "View settings",
        attributes: {
          calcOptions: [
            "table_name",
            mapObjectValues(slugOptions, (lvs) => lvs.map((lv) => lv.label)),
          ],
        },
        showIf: isEdit
          ? hasTable.includes(values.viewtemplate)
            ? undefined
            : { nosuchvar: true }
          : { viewtemplate: hasTable },
      }),
      new Field({
        name: "no_menu",
        label: req.__("No menu"),
        sublabel: req.__("Omit the menu from this view"),
        tab: "View settings",
        parent_field: "attributes",
        type: "Bool",
      }),
      new Field({
        name: "popup_title",
        label: req.__("Title"),
        type: "String",
        parent_field: "attributes",
        tab: "Popup settings",
        sublabel:
          "Some view patterns accept interpolations. Ex: <code>{{ name }}</code> or <code>{{ row ? `Edit ${row.name}` : `New person` }}</code>",
      }),
      {
        name: "popup_width",
        label: req.__("Popup width"),
        type: "Integer",
        tab: "Popup settings",
        parent_field: "attributes",
        attributes: { asideNext: true },
      },
      {
        name: "popup_width_units",
        label: req.__("Units"),
        type: "String",
        tab: "Popup settings",
        fieldview: "radio_group",
        parent_field: "attributes",
        attributes: {
          inline: true,
          options: ["px", "%", "vw", "em", "rem", "cm"],
        },
      },
      {
        name: "popup_minwidth",
        label: req.__("Popup min width"),
        type: "Integer",
        tab: "Popup settings",
        parent_field: "attributes",
        attributes: { asideNext: true },
      },
      {
        name: "popup_minwidth_units",
        label: req.__("Units"),
        type: "String",
        tab: "Popup settings",
        fieldview: "radio_group",
        parent_field: "attributes",
        attributes: {
          inline: true,
          options: ["px", "%", "vw", "em", "rem", "cm"],
        },
      },
      {
        name: "popup_save_indicator",
        label: req.__("Save indicator"),
        type: "Bool",
        parent_field: "attributes",
        sublabel: req.__(
          "Show an icon in the title bar to indicate when form data is being saved"
        ),
        tab: "Popup settings",
      },
      {
        name: "popup_link_out",
        label: req.__("Link out?"),
        sublabel: req.__("Show a link to open popup contents in new tab"),
        type: "Bool",
        parent_field: "attributes",
        tab: "Popup settings",
      },
      ...(isEdit
        ? [
            new Field({
              name: "viewtemplate",
              input_type: "hidden",
            }),
            new Field({
              name: "table_name",
              input_type: "hidden",
            }),
          ]
        : []),
    ],
    values,
  });
}
@param {object} req
@param {object} tableOptions
@param {object[]} roles
@param {object[]} pages
@param {object} values
@returns {Form}