FormDialog
Declarative dialog + form — the 'New X / Edit X' pattern without hand-rolled state. Required and validate() errors render inline; a thrown onSubmit shows a form-level error and keeps the dialog open; resolving closes it. Field types: text, email, password, url, number, textarea, json, checkbox, select, checkbox-group.
Create role
<FormDialog
trigger={<Button>New role</Button>}
title="New role"
submitLabel="Create"
fields={[
{ name: "key", label: "Key", required: true, autoFocus: true },
{ name: "perms", label: "Permissions", type: "checkbox-group", options: perms },
]}
onSubmit={async (values) => { await api.createRole(values); }}
/>