fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
  let isPrimary = false;
  let primaryTypes = Object.entries(getState().types)
    .filter(([k, v]) => v.primaryKey)
    .map(([k, v]) => k);
  if (id) {
    const field = await Field.findOne({ id });
    if (field) {
      isPrimary = !!field.primary_key;
    }
  }
  const roleOptions = (await User.get_roles()).map((r) => ({
    value: r.id,
    label: r.role,
  }));
  return new Form({
    action: "/field",
    validator: (vs) => {
      if (vs.calculated && vs.type === "File")
        return req.__("Calculated fields cannot have File type");
      if (vs.calculated && !vs.stored && vs.type.startsWith("Key to"))
        return req.__("Calculated non-stored fields cannot have Key type");
    },
    fields: [
      new Field({
        label: req.__("Label"),
        name: "label",
        sublabel: req.__("Name of the field"),
        type: "String",
        attributes: { autofocus: true },
        help: {
          topic: "Field label",
          context: {},
        },
        validator(s) {
          if (!s || s === "") return req.__("Missing label");
          if (!id && existing_names.includes(Field.labelToName(s)))
            return req.__("Column %s already exists", s);
          if (Field.labelToName(s) === "row")
            return req.__("Not a valid field name");
          try {
            new Function(Field.labelToName(s), "return;");
          } catch {
            return req.__("Not a valid field name");
          }
        },
      }),
      new Field({
        label: req.__("Type"),
        name: "type",
        sublabel: req.__(
          "The type determines the kind of data that can be stored in the field"
        ),
        input_type: "select",
        help: {
          topic: "Field types",
          context: {},
        },
        options: isPrimary
          ? primaryTypes
          : getState().type_names.concat(fkey_opts || []),
        disabled:
          !!id &&
          !getState().getConfig("development_mode", false) &&
          (hasData || db.isSQLite),
      }),
      // description
      new Field({
        label: req.__("Description"),
        name: "description",
        sublabel: req.__(
          "Description allows to give more information about field"
        ),
        input_type: "text",
      }),

      new Field({
        label: req.__("Calculated"),
        name: "calculated",
        sublabel: req.__("Calculated from other fields with a formula"),
        type: "Bool",
        disabled: !!id,
        help: {
          topic: "Calculated fields",
        },
      }),
      new Field({
        label: req.__("Required"),
        name: "required",
        type: "Bool",
        sublabel: req.__("There must be a value in every row"),
        disabled: !!id && db.isSQLite,
        showIf: { calculated: false },
      }),
      new Field({
        label: req.__("Unique"),
        name: "is_unique",
        sublabel: req.__(
          "Different rows must have different values for this field"
        ),
        showIf: { calculated: false },
        type: "Bool",
      }),
      new Field({
        label: req.__("Error message"),
        name: "unique_error_msg",
        sublabel: req.__("Error shown to user if uniqueness is violated"),
        showIf: { calculated: false, is_unique: true },
        type: "String",
      }),

      new Field({
        label: req.__("Stored"),
        name: "stored",
        sublabel: req.__("Calculated field will be stored in Database"),
        type: "Bool",
        disabled: !!id,
        showIf: { calculated: true },
        help: {
          topic: "Calculated fields",
        },
      }),
      new Field({
        label: req.__("Protected"),
        name: "protected",
        sublabel: req.__("Set role to access"),
        type: "Bool",
        showIf: { calculated: false },
        help: {
          topic: "Protected fields",
        },
      }),
      {
        label: req.__("Minimum role to write"),
        name: "min_role_write",
        input_type: "select",
        sublabel: req.__(
          "User must have this role or higher to update or create field values"
        ),
        options: roleOptions,
        showIf: { protected: true },
      },
    ],
  });
}
@param {object} req
@param {*} fkey_opts
@param {*} existing_names
@param {*} id
@param {*} hasData
@returns {Promise<Form>}