Settings-01 - Platform Settings
v0.5.0Platform settings page with organization profile, roles, users, and custom models. Fully controlled — all state and handlers passed via props.
Preview
Install & usage
npx shadcn@latest add @signalos/settings-01Pulls the page source plus its widget dependencies (@signalos/tokens, @signalos/utils, @signalos/page-header, @signalos/button, @signalos/card, @signalos/dialog, @signalos/dropdown-menu, @signalos/input, @signalos/label, @signalos/skeleton, @signalos/badge, @signalos/data-table, @signalos/form-dialog, @signalos/text-field, @signalos/select, @signalos/icon-button, @signalos/textarea, @signalos/combobox, @signalos/data-primitives) into your app - you own the copy. Requires access.
usage
// after: npx shadcn@latest add @signalos/settings-01
// mount the pulled page in a route, e.g. src/app/settings/page.tsx
import Settings01 from "@/components/blocks/settings-01/Settings01"
export default function Page() {
return <Settings01 />
}Props
OrgProfile
| Prop | Type | Default | Description |
|---|---|---|---|
| name* | string | - | |
| email* | string | - | |
| role* | string | - |
OrgProfileFormValues
| Prop | Type | Default | Description |
|---|---|---|---|
| name* | string | - | |
| email* | string | - |
AdminRole
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | undefined | - | |
| name* | string | - | |
| description | string | undefined | - |
PlatformUser
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | - | |
| full_name* | string | - | |
| email* | string | - | |
| role* | { readonly id: string; readonly name: string; } | - | |
| active* | boolean | - |
SelectOption
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | - | |
| value* | string | - |
ManagerOption
| Prop | Type | Default | Description |
|---|---|---|---|
string | undefined | - | ||
| value* | string | - | |
| label* | ReactNode | - | |
| icon | ReactNode | - | |
| disabled | boolean | undefined | - | |
| keywords | string[] | undefined | - |
UserFormValues
| Prop | Type | Default | Description |
|---|---|---|---|
| fullName* | string | - | |
| escalationManager* | string | - | |
| role* | string | - | |
| email* | string | - | |
| password* | string | - |
UpdatePasswordFormValues
| Prop | Type | Default | Description |
|---|---|---|---|
| password* | string | - |
CustomModel
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | - | |
| name* | string | - | |
| provider_kind* | CustomModelProviderKind | - | |
| model_id | string | null | undefined | - | |
| endpoint_url | string | null | undefined | - | |
| api_version | string | null | undefined | - | |
| config | Record<string, unknown> | null | undefined | - | |
| has_api_key* | boolean | - | |
| status* | CustomModelStatus | - | |
| error_message | string | null | undefined | - | |
| created_at* | string | - | |
| last_checked_at | string | null | undefined | - |
Settings01Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | undefined | "Platform Settings" | |
| description | string | undefined | "Configure global operational parameters, manage organizational identity, and orchestrate external data flow within the SignalOS ecosystem." | |
| headerMeta | ReactNode | - | Optional content rendered above the title (e.g. an environment or version banner). Omit to render nothing - the block ships no default value here since there is no environment/version string that is correct for every consumer. |
| usersTitle | string | undefined | "Users List" | |
| usersDescription | string | undefined | "Manage users and their access permissions" | |
| customModelsTitle | string | undefined | "Custom Models" | |
| customModelsDescription | string | undefined | "Bring your own LLM providers and keys for use across pipelines." | |
| profile* | OrgProfile | - | |
| dialogOpen* | boolean | - | |
| onDialogOpenChange* | (open: boolean) => void | - | |
| formValues* | OrgProfileFormValues | - | |
| onFormChange* | (field: keyof OrgProfileFormValues, value: string) => void | - | |
| onSave* | () => void | - | |
| isSaving | boolean | undefined | - | Shared saving flag for the organization and role dialogs. |
| orgFormErrors | Partial<Record<keyof OrgProfileFormValues, string>> | undefined | - | |
| roles | AdminRole[] | undefined | - | |
| isLoadingRoles | boolean | undefined | - | |
| deletingRoleId | string | null | undefined | - | Id of the role currently being deleted (shows the row spinner). |
| deleteLoading | boolean | undefined | - | |
| onAddRole | (() => void) | undefined | - | |
| onEditRole | ((role: AdminRole) => void) | undefined | - | |
| onDeleteRole | ((id: string) => void) | undefined | - | |
| roleDialogOpen* | boolean | - | |
| roleDialogMode | "edit" | "create" | undefined | "create" | Whether the role dialog is creating a new role or editing `onEditRole`'s role. |
| onRoleDialogOpenChange* | (open: boolean) => void | - | |
| roleForm* | { name: string; description: string; } | - | |
| onRoleFormChange* | (field: "name" | "description", value: string) => void | - | |
| onRoleSave* | () => void | - | |
| roleFormErrors | Partial<Record<"name" | "description", string>> | undefined | - | |
| isRolesSaving | boolean | undefined | - | Saving flag for the role dialog only. Falls back to `isSaving` when omitted, so callers that don't distinguish the two keep today's behavior. |
| users | PlatformUser[] | undefined | - | |
| isLoadingUsers | boolean | undefined | - | |
| userSearch | string | undefined | - | |
| onUserSearchChange | ((value: string) => void) | undefined | - | |
| selectedUser | PlatformUser | null | undefined | - | |
| onSelectUser | ((user: PlatformUser | null) => void) | undefined | - | |
| onAddUser | (() => void) | undefined | - | |
| userDialogOpen* | boolean | - | |
| onUserDialogOpenChange* | (open: boolean) => void | - | |
| userDialogMode* | UserDialogMode | - | |
| userForm* | UserFormValues | - | |
| managerOptions* | ManagerOption[] | - | |
| onManagerSearchChange | ((value: string) => void) | undefined | - | Called as the user types in the escalation-manager combobox search box. |
| isLoadingManagerOptions | boolean | undefined | false | Shows a loading state in the manager combobox while options are fetched. |
| roleOptions* | SelectOption[] | - | |
| onUserFormChange* | (field: keyof UserFormValues, value: string) => void | - | |
| onUserSave* | () => void | - | |
| isSavingUser | boolean | undefined | - | |
| userFormErrors | Partial<Record<"role" | "email" | "password" | "fullName", string>> | undefined | - | |
| updatePasswordDialogOpen* | boolean | - | |
| onUpdatePasswordDialogOpenChange* | (open: boolean) => void | - | |
| updatePasswordForm* | UpdatePasswordFormValues | - | |
| onUpdatePasswordChange* | (value: string) => void | - | |
| onUpdatePasswordSave* | () => void | - | |
| isSavingPassword | boolean | undefined | - | |
| updatePasswordErrors | { password?: string | undefined; } | undefined | - | |
| userColumns* | ColumnDef<PlatformUser>[] | - | Column definitions for the users table - the caller owns row actions. |
| userPagination | { page: number; pageSize: number; total: number; totalPages: number; pageSizeOptions?: number[] | undefined; onPageChange: (page: number) => void; onRowsPerPageChange: (pageSize: number) => void; } | undefined | - | |
| customModels | CustomModel[] | undefined | - | |
| isLoadingModels | boolean | undefined | - | |
| selectedModel | CustomModel | null | undefined | - | |
| onSelectModel | ((model: CustomModel | null) => void) | undefined | - | |
| onAddModel | (() => void) | undefined | - | |
| modelColumns* | ColumnDef<CustomModel>[] | - | Column definitions for the models table - the caller owns row actions. |
| customModelDialogOpen | boolean | undefined | - | |
| onCustomModelDialogOpenChange | ((open: boolean) => void) | undefined | - | |
| customModelDialogContentClassName | string | undefined | - | Overrides the custom-model dialog's content classes (e.g. to widen it for long provider forms). |
| providers | CustomModelProvider[] | undefined | - | Providers available in the model dialog's provider select. |
| isLoadingProviders | boolean | undefined | - | |
| editingModel | boolean | undefined | - | True when the dialog is editing an existing model (vs creating one). |
| editingModelHasStoredSecrets | boolean | undefined | - | True when the model being edited has a stored API key (has_api_key). |
| modelName* | string | - | |
| providerKind* | "" | CustomModelProviderKind | - | |
| fields* | CustomModelFields | - | Dynamic field values keyed by `ProviderField.key`. |
| selectedProvider | CustomModelProvider | undefined | - | |
| isProviderMissing* | boolean | - | True when the chosen provider's package is not installed server-side. |
| isTesting* | boolean | - | |
| isTestSuccess | boolean | undefined | - | |
| testFeedback | { ok: boolean; message: string; } | null | undefined | - | |
| canTest* | boolean | - | True once the form is complete enough to attempt a connection test. |
| onNameChange* | (value: string) => void | - | |
| onProviderChange* | (value: CustomModelProviderKind) => void | - | |
| onFieldChange* | (key: string, value: string) => void | - | |
| onTest* | () => void | - | |
| onCustomModelSave | (() => void) | undefined | - | |
| isSavingModel* | boolean | - |
ProviderField
| Prop | Type | Default | Description |
|---|---|---|---|
| key* | string | - | |
| label* | string | - | |
| required* | boolean | - | |
| secret* | boolean | - | |
| type* | ProviderFieldType | - | |
| placeholder | string | undefined | - |
CustomModelProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| provider_kind* | CustomModelProviderKind | - | |
| label* | string | - | |
| package* | string | - | |
| installed* | boolean | - | |
| temperature_max* | number | - | |
| fields* | ProviderField[] | - |
Supporting types
export type UserDialogMode = "create" | "edit"
export type CustomModelProviderKind = | "openai_compatible" | "azure_openai" | "bedrock" | "vertex" | "openai" | "anthropic" | "google_genai" | "groq" | "mistral" | "deepseek"
export type CustomModelStatus = "active" | "error" | "inactive"
export type ProviderFieldType = "text" | "url" | "textarea" | "keyvalue"
export type CustomModelFields = Record<string, string>
Changelog
settings-01
0.5.0
- Add
title/descriptionoverrides for the page header,usersTitle/usersDescriptionfor the Users List section, andcustomModelsTitle/customModelsDescriptionfor the Custom Models section - all default to today's copy. - Breaking: remove the hardcoded
"Console v2.4 / Environment: Production"banner. It was fake data baked into every install with no way to override or remove it. Replaced with an optionalheaderMeta?: ReactNodeslot that renders nothing when omitted - pass your own environment/version content if you need one. - Add
roleDialogMode?: "create" | "edit"(default"create", matching today's behavior) soRoleDialogcan show "Edit Role" copy when editing an existing role viaonEditRole. AddisRolesSavingto decouple the role dialog's saving state from the organization dialog'sisSaving. managerOptionsis now typedManagerOption[](aComboboxOptionwith an optionalemail) instead of the plainSelectOption[]- existingSelectOption[]values still satisfy the new type. The escalation-manager combobox now renders an initials avatar + name + email per option instead of a plain label. AddonManagerSearchChangeandisLoadingManagerOptions, wired into the combobox's search and loading state.- Add
customModelDialogContentClassNameto override the custom-model dialog's sizing (defaults to today's size). - Add stable
data-testids:users-section,org-profile-email-value,org-profile-role-value, and therole-modal-*/user-modal-*family on the role and user dialogs' fields, submit, and cancel actions. - Wrap the organization-profile card and roles section in
self-startcontainers so the sticky profile card no longer stretches to the roles column's height.
0.4.0
- Decompose the block: role cards move to
components/RoleCard.tsx+components/RolesSection.tsxand the custom-model dialog tocomponents/CustomModelDialog.tsx; the misspelledcomponets/folder is renamed tocomponents/(consumer paths change accordingly). - Role card section labels now use the shared
SectionLabelfrom@signalos/data-primitives; the inline SVG header icon is replaced with a lucide icon. - Add the missing
"use client"directive and remove non-tokenbg-gray-300skeleton overrides. - Page titles now come from
page-headerdefaults (local overrides removed). - API cleanup (breaking): remove unused props
onCancel,onEditUser,customModelForm,onCustomModelNameChange,onCustomModelProviderChange,onCustomModelFieldChange,onTestConnection,isTestingModel, and the backend payload types that belonged in the app's API layer. All props are documented andreadonlynow;RoleDialog/OrganizationDialogswitch to named exports. Fix the clear-search button's accessible label.
0.3.13
- Add validation for all form fields.
0.3.11
- Add
Textareaprimitive (@signalos/textarea) and wire it intoRoleDialogdescription field.
0.3.3
- Version alignment release (no content change recorded).
0.3.0
- Replace Users and Custom Models list views with the
DataTablewidget. - Add
@signalos/data-tableregistry dependency and@tanstack/react-tablenpm dependency.
0.2.0
- Add Users section with search field, Add User button, user rows, and selected-user detail panel.
- Add Custom Models section with Add Model button, model cards, and selected-model detail panel.
- Add
PlatformUserandCustomModeltypes; extendSettings01Propswith users/models props. - All new sections fully controlled via props — no internal state.
0.1.2
- Fix: add missing imports (lucide-react icons, DropdownMenu sub-components).
- Fix: remove all internal state from component — roles, loading, and delete state now fully controlled via props.
- Add
AdminRoletype and roles-related props toSettings01Props. - Update preview to wire up roles state and handlers.
0.1.1
- Content sync fix to bring source in line with published registry payload.
0.1.0
- Initial release: organization profile card, roles grid, and settings page scaffolding composed from shared widgets.