Roles

Search and pick roles for an Auto Form field - one, or as many as you like.

Preview

One role, replaced each time you pick.

Pick as many as you like. Picking one twice removes it.

Usage

import { z } from "zod";
import { AutoForm } from "@vitnode/core/components/form/auto-form";
import { AutoFormRoles } from "@vitnode/core/components/form/fields/input-roles";

One component covers both shapes, because the difference is the value and nothing else - the search, the colour, the language resolution and the empty state are identical, and two copies is how they drift.

const formSchema = z.object({
  roleId: z.number(),
});
<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: "roleId",
      component: props => (
        <AutoFormRoles
          {...props}
          description="One role, replaced each time you pick."
          label="Primary role"
          placeholder="Select a role"
        />
      ),
    },
  ]}
/>

The value is a single id, and picking again replaces it.

Out of the box it searches the AdminCP roles list. The guest role is never offered: it is the role a request has when it has no account, so it is not something to assign to anybody.

Editing an existing record

Same rule as User: the picker can only name ids it has seen, so an edit form passes the roles it already knows about.

<AutoFormRoles {...props} multiple label="Roles" selected={user.roles} />

Keeping a role out of the list

excludeIds drops options another field already owns - a primary-role picker and a secondary-role picker should not both offer the same one:

<AutoFormRoles
  {...props}
  multiple
  excludeIds={[primaryRoleId]}
  label="Additional roles"
/>

Names are resolved per reader

A role carries one name per language. The field renders the active locale's, and falls back to the first translation rather than to the id - a role with no translation in your language is still a role somebody named:

import { roleOptionName } from "@vitnode/core/components/form/fields/input-roles";

roleOptionName(role, "pl"); // "Administrator PL", or the first name it has

Props

PropTypeDefaultWhat it does
multiplebooleanfalsenumber[] instead of number, with chips
labelReactNode-Field label
descriptionReactNode-Help text under the control
placeholderstringSelect an optionShown while nothing is chosen
searchPlaceholderstringSearch...Placeholder inside the search box
selectedRoleOption[][]Roles the field opens on
excludeIdsnumber[][]Roles the picker must not offer
search(value: string) => Promise<RoleOption[]>AdminCP roles listReplaces the lookup
disabledbooleanfalseBlocks opening the picker and removing chips

RoleOption is { id, color, name }, where name is the raw { languageCode, name }[] - the server has no business deciding which language the person clicking reads in.

See also

  • User - the same idea for people.
  • Combobox - when the options are strings rather than records.