@dloizides/ui-access-admin 1.0.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to `@dloizides/ui-access-admin` are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
6
6
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.1]
9
+
10
+ ### Fixed
11
+
12
+ - **`UserManagementPanel` — the create/edit Role & Tenant pickers did nothing on click.** They use
13
+ `ModalDropdown`, whose responsive default renders the INLINE menu on desktop web — a popover that
14
+ `createPortal`s to `document.body`. Inside the panel's `ModalShell` (an RN `Modal`) that portal
15
+ lands OUTSIDE the shell's focus-trapping layer, so it rendered behind the modal and was dismissed
16
+ the instant it opened (the trigger's `aria-expanded` flipped straight back to `false`). `PickerField`
17
+ now forces `variant={DropdownVariant.Modal}`, which renders the options in their own RN-`Modal`
18
+ layer that stacks ABOVE the shell — visible and tappable. Added `PickerField.test.tsx` to pin it.
19
+
20
+ ## [1.2.0]
21
+
22
+ ### Added
23
+
24
+ - **`UserManagementPanel`** — a brand-agnostic, cross-portal in-app user-management screen:
25
+ a users table (search + roles/tenant/active columns) with create / edit-role-&-tenant /
26
+ reset-password modals. Backend-agnostic by design — it takes an injected `UserManagementApi`
27
+ (`listUsers` / `createUser` / `updateUser` / `resetPassword`) plus caller capabilities
28
+ (`isPlatformAdmin`, `assignableRoles`, `tenants`), so any product plugs in its own identity
29
+ backend (AML on OpenIddict, others on Keycloak). Scope-aware in the UI: the role picker offers
30
+ only `assignableRoles`, and the tenant picker is shown only to a platform admin (a tenant admin
31
+ submits `tenantId: null` and the backend resolves it). Colours from the host `UiProvider` theme,
32
+ strings via the host `t` against new `accessAdmin.users.*` keys in `ACCESS_ADMIN_I18N`. Reuses
33
+ the kit primitives (`ui-tables`, `ui-forms`, `ui-layout` `ModalDropdown`/`ModalShell`,
34
+ `ui-buttons`, `ui-feedback`) — no new primitives.
35
+
8
36
  ## [1.0.0] - 2026-07-22
9
37
 
10
38
  Initial release. Promoted out of `kefi-web`'s organizer promoter roster, which had the
package/dist/index.d.mts CHANGED
@@ -300,6 +300,109 @@ interface AccountLinkPanelProps {
300
300
  }
301
301
  declare const AccountLinkPanel: ({ subject, boundAccountId, accounts, bindings, linkAccount, describeError, onClose, onResult, testIDPrefix, }: AccountLinkPanelProps) => React.ReactElement;
302
302
 
303
+ /**
304
+ * The injected-API contract for {@link UserManagementPanel}.
305
+ *
306
+ * ── BACKEND-AGNOSTIC BY DESIGN ────────────────────────────────────────────────
307
+ * Every portal manages users, but no two agree on the wire: AML speaks to
308
+ * OpenIddict, kefi/finreg speak to Keycloak, and each has its own tenant model.
309
+ * So this panel knows about exactly one shape — a `UserManagementUser` — and takes
310
+ * its four writes as injected async callbacks. A host maps its own API (endpoints,
311
+ * client, cache invalidation) to `UserManagementApi`; the package supplies the
312
+ * screen, the modals and the scope rules, never the transport.
313
+ *
314
+ * ── THE SCOPE IS PROPS, NOT POLICY ────────────────────────────────────────────
315
+ * The panel is scope-aware purely from `isPlatformAdmin` + `assignableRoles` +
316
+ * `tenants`: it NEVER offers a role outside `assignableRoles`, and NEVER shows a
317
+ * tenant control to a non-platform admin. That is a UI guardrail, not the
318
+ * enforcement — the backend still authorizes every write. Hiding the control just
319
+ * stops a tenant admin being offered a lever they cannot pull.
320
+ */
321
+ /** Translate a package i18n key to already-localized display text. */
322
+ type UserManagementTranslate = (key: string) => string;
323
+ /**
324
+ * One user, normalized by the host from whatever its directory returns.
325
+ *
326
+ * Deliberately flat and provider-neutral: no Keycloak `attributes`, no OpenIddict
327
+ * claims, no product role enum — just the columns every portal's user table shows.
328
+ */
329
+ interface UserManagementUser {
330
+ /** Stable id, used as the React key and passed to every write. */
331
+ id: string;
332
+ email: string;
333
+ displayName: string;
334
+ /** The roles this user currently holds. Names are data — rendered verbatim. */
335
+ roles: string[];
336
+ /** The tenant this user belongs to, or null for a platform-level account. */
337
+ tenantId: string | null;
338
+ active: boolean;
339
+ /** ISO timestamp the user was created, when the directory reports it. */
340
+ createdAt?: string;
341
+ }
342
+ /** What the host is asked to do. Each rejects with an `Error` on failure. */
343
+ interface UserManagementApi {
344
+ listUsers(): Promise<UserManagementUser[]>;
345
+ createUser(input: {
346
+ email: string;
347
+ password: string;
348
+ displayName: string;
349
+ role: string;
350
+ tenantId: string | null;
351
+ }): Promise<void>;
352
+ updateUser(id: string, patch: {
353
+ displayName?: string;
354
+ role?: string;
355
+ tenantId?: string | null;
356
+ active?: boolean;
357
+ }): Promise<void>;
358
+ resetPassword(id: string, password: string): Promise<void>;
359
+ }
360
+ /** One selectable tenant for the platform-admin tenant picker. */
361
+ interface UserManagementTenant {
362
+ id: string;
363
+ label: string;
364
+ }
365
+ interface UserManagementPanelProps {
366
+ /** The host's user-management API, by injection. */
367
+ api: UserManagementApi;
368
+ /**
369
+ * True → this admin may assign platform-level roles and pick any tenant.
370
+ * False (a tenant admin) → no tenant control at all; writes land in their tenant.
371
+ */
372
+ isPlatformAdmin: boolean;
373
+ /**
374
+ * The roles this caller is allowed to assign (the product supplies these from its
375
+ * own `TenantManagedRoleSet.Assignable`). The pickers NEVER offer anything else.
376
+ */
377
+ assignableRoles: string[];
378
+ /** Tenants for the picker. Only meaningful — and only shown — for a platform admin. */
379
+ tenants?: UserManagementTenant[];
380
+ /** Host translate against the `accessAdmin.users.*` keys in `ACCESS_ADMIN_I18N`. */
381
+ t: UserManagementTranslate;
382
+ /** Root testID; children derive from it. Defaults to `user-management-panel`. */
383
+ testID?: string;
384
+ }
385
+
386
+ /**
387
+ * UserManagementPanel — the brand-agnostic, cross-portal in-app user administration
388
+ * surface: a users table plus create / edit / reset-password flows, wired to the
389
+ * host's API by injection.
390
+ *
391
+ * ── WHAT THE HOST OWNS ────────────────────────────────────────────────────────
392
+ * The four writes (`api`), the scope (`isPlatformAdmin`, `assignableRoles`,
393
+ * `tenants`) and the words (`t`). Everything else — the table, the modals, the
394
+ * search, the loading / empty / error states and the success / failure toasts — is
395
+ * the package's. Colours come from the host's `UiProvider` theme.
396
+ *
397
+ * ── SCOPE IS ENFORCED IN TWO PLACES ───────────────────────────────────────────
398
+ * This panel is the UI half. It never OFFERS a role outside `assignableRoles` and
399
+ * never shows a tenant control to a non-platform admin, so a tenant admin is not
400
+ * handed a lever they cannot pull. The backend still authorizes every write — the
401
+ * hidden control is a guardrail, not the gate.
402
+ */
403
+
404
+ declare const UserManagementPanel: ({ api, isPlatformAdmin, assignableRoles, tenants, t, testID, }: UserManagementPanelProps) => React.ReactElement;
405
+
303
406
  /** Translate function — mirrors the fleet's `FM(key, p1?, p2?, p3?)` shape. */
304
407
  type AccessTranslate = (key: string, p1?: string, p2?: string, p3?: string) => string;
305
408
  interface UseCapabilityLinkMintOptions {
@@ -528,6 +631,77 @@ declare const ACCESS_ADMIN_I18N: {
528
631
  readonly accountUnlinkSuccess: "accessAdmin.account.unlinkSuccess";
529
632
  readonly accountClose: "accessAdmin.account.close";
530
633
  readonly accountCloseHint: "accessAdmin.account.closeHint";
634
+ /** Panel heading. */
635
+ readonly usersHeading: "accessAdmin.users.heading";
636
+ /** Supporting line under the heading. */
637
+ readonly usersNote: "accessAdmin.users.note";
638
+ /** Placeholder in the search box. */
639
+ readonly usersSearchPlaceholder: "accessAdmin.users.searchPlaceholder";
640
+ /** Accessible name of the search box. */
641
+ readonly usersSearchLabel: "accessAdmin.users.searchLabel";
642
+ readonly usersSearchHint: "accessAdmin.users.searchHint";
643
+ /** Word shown next to the live result count in the FilterBar. */
644
+ readonly usersResultsLabel: "accessAdmin.users.resultsLabel";
645
+ readonly usersCreate: "accessAdmin.users.create";
646
+ readonly usersCreateHint: "accessAdmin.users.createHint";
647
+ readonly usersColEmail: "accessAdmin.users.colEmail";
648
+ readonly usersColDisplayName: "accessAdmin.users.colDisplayName";
649
+ readonly usersColRoles: "accessAdmin.users.colRoles";
650
+ readonly usersColTenant: "accessAdmin.users.colTenant";
651
+ readonly usersColActive: "accessAdmin.users.colActive";
652
+ readonly usersColCreated: "accessAdmin.users.colCreated";
653
+ readonly usersColActions: "accessAdmin.users.colActions";
654
+ readonly usersActiveYes: "accessAdmin.users.activeYes";
655
+ readonly usersActiveNo: "accessAdmin.users.activeNo";
656
+ /** Tenant cell for a user bound to no tenant (a platform-level account). */
657
+ readonly usersTenantNone: "accessAdmin.users.tenantNone";
658
+ /** Roles cell for a user with no roles yet. */
659
+ readonly usersNoRoles: "accessAdmin.users.noRoles";
660
+ /** Created cell when the API did not return a timestamp. */
661
+ readonly usersCreatedUnknown: "accessAdmin.users.createdUnknown";
662
+ readonly usersEdit: "accessAdmin.users.edit";
663
+ readonly usersEditHint: "accessAdmin.users.editHint";
664
+ readonly usersReset: "accessAdmin.users.reset";
665
+ readonly usersResetHint: "accessAdmin.users.resetHint";
666
+ readonly usersLoading: "accessAdmin.users.loading";
667
+ readonly usersEmpty: "accessAdmin.users.empty";
668
+ /** Shown when the user list FAILED to load — never conflated with "there are none". */
669
+ readonly usersLoadError: "accessAdmin.users.loadError";
670
+ readonly usersRetry: "accessAdmin.users.retry";
671
+ readonly usersRetryHint: "accessAdmin.users.retryHint";
672
+ readonly usersCreateTitle: "accessAdmin.users.createTitle";
673
+ readonly usersEditTitle: "accessAdmin.users.editTitle";
674
+ readonly usersResetTitle: "accessAdmin.users.resetTitle";
675
+ readonly usersFieldEmail: "accessAdmin.users.fieldEmail";
676
+ readonly usersEmailPlaceholder: "accessAdmin.users.emailPlaceholder";
677
+ readonly usersFieldPassword: "accessAdmin.users.fieldPassword";
678
+ readonly usersPasswordPlaceholder: "accessAdmin.users.passwordPlaceholder";
679
+ readonly usersFieldDisplayName: "accessAdmin.users.fieldDisplayName";
680
+ readonly usersDisplayNamePlaceholder: "accessAdmin.users.displayNamePlaceholder";
681
+ readonly usersFieldRole: "accessAdmin.users.fieldRole";
682
+ readonly usersRolePlaceholder: "accessAdmin.users.rolePlaceholder";
683
+ readonly usersRolePickerHint: "accessAdmin.users.rolePickerHint";
684
+ readonly usersFieldTenant: "accessAdmin.users.fieldTenant";
685
+ readonly usersTenantPlaceholder: "accessAdmin.users.tenantPlaceholder";
686
+ readonly usersTenantPickerHint: "accessAdmin.users.tenantPickerHint";
687
+ readonly usersFieldActive: "accessAdmin.users.fieldActive";
688
+ readonly usersActiveToggleHint: "accessAdmin.users.activeToggleHint";
689
+ readonly usersFieldNewPassword: "accessAdmin.users.fieldNewPassword";
690
+ readonly usersNewPasswordPlaceholder: "accessAdmin.users.newPasswordPlaceholder";
691
+ readonly usersSubmitCreate: "accessAdmin.users.submitCreate";
692
+ readonly usersSubmitCreateHint: "accessAdmin.users.submitCreateHint";
693
+ readonly usersSubmitSave: "accessAdmin.users.submitSave";
694
+ readonly usersSubmitSaveHint: "accessAdmin.users.submitSaveHint";
695
+ readonly usersSubmitReset: "accessAdmin.users.submitReset";
696
+ readonly usersSubmitResetHint: "accessAdmin.users.submitResetHint";
697
+ readonly usersCancel: "accessAdmin.users.cancel";
698
+ readonly usersCancelHint: "accessAdmin.users.cancelHint";
699
+ readonly usersCreateSuccess: "accessAdmin.users.createSuccess";
700
+ readonly usersCreateFailed: "accessAdmin.users.createFailed";
701
+ readonly usersUpdateSuccess: "accessAdmin.users.updateSuccess";
702
+ readonly usersUpdateFailed: "accessAdmin.users.updateFailed";
703
+ readonly usersResetSuccess: "accessAdmin.users.resetSuccess";
704
+ readonly usersResetFailed: "accessAdmin.users.resetFailed";
531
705
  };
532
706
  /** The shape of {@link ACCESS_ADMIN_I18N} — useful for typing an override map. */
533
707
  type AccessAdminI18n = typeof ACCESS_ADMIN_I18N;
@@ -539,6 +713,7 @@ type AccessAdminI18n = typeof ACCESS_ADMIN_I18N;
539
713
  declare const ACCESS_ADMIN_TEST_IDS: {
540
714
  readonly linkPanel: "access-link-panel";
541
715
  readonly accountPanel: "account-link-panel";
716
+ readonly usersPanel: "user-management-panel";
542
717
  };
543
718
 
544
- export { ACCESS_ADMIN_I18N, ACCESS_ADMIN_TEST_IDS, type AccessAdminI18n, type AccessAdminPalette, AccessLinkPanel, type AccessLinkPanelProps, type AccessResultReporter, AccessResultTone, type AccessSubject, type AccessTranslate, AccountLinkPanel, type AccountLinkPanelProps, type CapabilityLink, type CapabilityLinkMintState, type DescribeError, type ExpiryPolicy, type LinkSubjectAccount, type LinkableAccount, type MintCapabilityLink, type MintCapabilityLinkInput, type SubjectAccountLinkState, type UseCapabilityLinkMintOptions, type UseSubjectAccountLinkOptions, accountDisplayLabel, activeLinksForSubject, buildAccountOptions, buildCapabilityLinkMailto, capabilityExpiryDate, capabilityExpiryDays, daysUntilAnchor, formatLinkDate, linksForSubject, useAccessAdminPalette, useCapabilityLinkMint, useSubjectAccountLink };
719
+ export { ACCESS_ADMIN_I18N, ACCESS_ADMIN_TEST_IDS, type AccessAdminI18n, type AccessAdminPalette, AccessLinkPanel, type AccessLinkPanelProps, type AccessResultReporter, AccessResultTone, type AccessSubject, type AccessTranslate, AccountLinkPanel, type AccountLinkPanelProps, type CapabilityLink, type CapabilityLinkMintState, type DescribeError, type ExpiryPolicy, type LinkSubjectAccount, type LinkableAccount, type MintCapabilityLink, type MintCapabilityLinkInput, type SubjectAccountLinkState, type UseCapabilityLinkMintOptions, type UseSubjectAccountLinkOptions, type UserManagementApi, UserManagementPanel, type UserManagementPanelProps, type UserManagementTenant, type UserManagementTranslate, type UserManagementUser, accountDisplayLabel, activeLinksForSubject, buildAccountOptions, buildCapabilityLinkMailto, capabilityExpiryDate, capabilityExpiryDays, daysUntilAnchor, formatLinkDate, linksForSubject, useAccessAdminPalette, useCapabilityLinkMint, useSubjectAccountLink };
package/dist/index.d.ts CHANGED
@@ -300,6 +300,109 @@ interface AccountLinkPanelProps {
300
300
  }
301
301
  declare const AccountLinkPanel: ({ subject, boundAccountId, accounts, bindings, linkAccount, describeError, onClose, onResult, testIDPrefix, }: AccountLinkPanelProps) => React.ReactElement;
302
302
 
303
+ /**
304
+ * The injected-API contract for {@link UserManagementPanel}.
305
+ *
306
+ * ── BACKEND-AGNOSTIC BY DESIGN ────────────────────────────────────────────────
307
+ * Every portal manages users, but no two agree on the wire: AML speaks to
308
+ * OpenIddict, kefi/finreg speak to Keycloak, and each has its own tenant model.
309
+ * So this panel knows about exactly one shape — a `UserManagementUser` — and takes
310
+ * its four writes as injected async callbacks. A host maps its own API (endpoints,
311
+ * client, cache invalidation) to `UserManagementApi`; the package supplies the
312
+ * screen, the modals and the scope rules, never the transport.
313
+ *
314
+ * ── THE SCOPE IS PROPS, NOT POLICY ────────────────────────────────────────────
315
+ * The panel is scope-aware purely from `isPlatformAdmin` + `assignableRoles` +
316
+ * `tenants`: it NEVER offers a role outside `assignableRoles`, and NEVER shows a
317
+ * tenant control to a non-platform admin. That is a UI guardrail, not the
318
+ * enforcement — the backend still authorizes every write. Hiding the control just
319
+ * stops a tenant admin being offered a lever they cannot pull.
320
+ */
321
+ /** Translate a package i18n key to already-localized display text. */
322
+ type UserManagementTranslate = (key: string) => string;
323
+ /**
324
+ * One user, normalized by the host from whatever its directory returns.
325
+ *
326
+ * Deliberately flat and provider-neutral: no Keycloak `attributes`, no OpenIddict
327
+ * claims, no product role enum — just the columns every portal's user table shows.
328
+ */
329
+ interface UserManagementUser {
330
+ /** Stable id, used as the React key and passed to every write. */
331
+ id: string;
332
+ email: string;
333
+ displayName: string;
334
+ /** The roles this user currently holds. Names are data — rendered verbatim. */
335
+ roles: string[];
336
+ /** The tenant this user belongs to, or null for a platform-level account. */
337
+ tenantId: string | null;
338
+ active: boolean;
339
+ /** ISO timestamp the user was created, when the directory reports it. */
340
+ createdAt?: string;
341
+ }
342
+ /** What the host is asked to do. Each rejects with an `Error` on failure. */
343
+ interface UserManagementApi {
344
+ listUsers(): Promise<UserManagementUser[]>;
345
+ createUser(input: {
346
+ email: string;
347
+ password: string;
348
+ displayName: string;
349
+ role: string;
350
+ tenantId: string | null;
351
+ }): Promise<void>;
352
+ updateUser(id: string, patch: {
353
+ displayName?: string;
354
+ role?: string;
355
+ tenantId?: string | null;
356
+ active?: boolean;
357
+ }): Promise<void>;
358
+ resetPassword(id: string, password: string): Promise<void>;
359
+ }
360
+ /** One selectable tenant for the platform-admin tenant picker. */
361
+ interface UserManagementTenant {
362
+ id: string;
363
+ label: string;
364
+ }
365
+ interface UserManagementPanelProps {
366
+ /** The host's user-management API, by injection. */
367
+ api: UserManagementApi;
368
+ /**
369
+ * True → this admin may assign platform-level roles and pick any tenant.
370
+ * False (a tenant admin) → no tenant control at all; writes land in their tenant.
371
+ */
372
+ isPlatformAdmin: boolean;
373
+ /**
374
+ * The roles this caller is allowed to assign (the product supplies these from its
375
+ * own `TenantManagedRoleSet.Assignable`). The pickers NEVER offer anything else.
376
+ */
377
+ assignableRoles: string[];
378
+ /** Tenants for the picker. Only meaningful — and only shown — for a platform admin. */
379
+ tenants?: UserManagementTenant[];
380
+ /** Host translate against the `accessAdmin.users.*` keys in `ACCESS_ADMIN_I18N`. */
381
+ t: UserManagementTranslate;
382
+ /** Root testID; children derive from it. Defaults to `user-management-panel`. */
383
+ testID?: string;
384
+ }
385
+
386
+ /**
387
+ * UserManagementPanel — the brand-agnostic, cross-portal in-app user administration
388
+ * surface: a users table plus create / edit / reset-password flows, wired to the
389
+ * host's API by injection.
390
+ *
391
+ * ── WHAT THE HOST OWNS ────────────────────────────────────────────────────────
392
+ * The four writes (`api`), the scope (`isPlatformAdmin`, `assignableRoles`,
393
+ * `tenants`) and the words (`t`). Everything else — the table, the modals, the
394
+ * search, the loading / empty / error states and the success / failure toasts — is
395
+ * the package's. Colours come from the host's `UiProvider` theme.
396
+ *
397
+ * ── SCOPE IS ENFORCED IN TWO PLACES ───────────────────────────────────────────
398
+ * This panel is the UI half. It never OFFERS a role outside `assignableRoles` and
399
+ * never shows a tenant control to a non-platform admin, so a tenant admin is not
400
+ * handed a lever they cannot pull. The backend still authorizes every write — the
401
+ * hidden control is a guardrail, not the gate.
402
+ */
403
+
404
+ declare const UserManagementPanel: ({ api, isPlatformAdmin, assignableRoles, tenants, t, testID, }: UserManagementPanelProps) => React.ReactElement;
405
+
303
406
  /** Translate function — mirrors the fleet's `FM(key, p1?, p2?, p3?)` shape. */
304
407
  type AccessTranslate = (key: string, p1?: string, p2?: string, p3?: string) => string;
305
408
  interface UseCapabilityLinkMintOptions {
@@ -528,6 +631,77 @@ declare const ACCESS_ADMIN_I18N: {
528
631
  readonly accountUnlinkSuccess: "accessAdmin.account.unlinkSuccess";
529
632
  readonly accountClose: "accessAdmin.account.close";
530
633
  readonly accountCloseHint: "accessAdmin.account.closeHint";
634
+ /** Panel heading. */
635
+ readonly usersHeading: "accessAdmin.users.heading";
636
+ /** Supporting line under the heading. */
637
+ readonly usersNote: "accessAdmin.users.note";
638
+ /** Placeholder in the search box. */
639
+ readonly usersSearchPlaceholder: "accessAdmin.users.searchPlaceholder";
640
+ /** Accessible name of the search box. */
641
+ readonly usersSearchLabel: "accessAdmin.users.searchLabel";
642
+ readonly usersSearchHint: "accessAdmin.users.searchHint";
643
+ /** Word shown next to the live result count in the FilterBar. */
644
+ readonly usersResultsLabel: "accessAdmin.users.resultsLabel";
645
+ readonly usersCreate: "accessAdmin.users.create";
646
+ readonly usersCreateHint: "accessAdmin.users.createHint";
647
+ readonly usersColEmail: "accessAdmin.users.colEmail";
648
+ readonly usersColDisplayName: "accessAdmin.users.colDisplayName";
649
+ readonly usersColRoles: "accessAdmin.users.colRoles";
650
+ readonly usersColTenant: "accessAdmin.users.colTenant";
651
+ readonly usersColActive: "accessAdmin.users.colActive";
652
+ readonly usersColCreated: "accessAdmin.users.colCreated";
653
+ readonly usersColActions: "accessAdmin.users.colActions";
654
+ readonly usersActiveYes: "accessAdmin.users.activeYes";
655
+ readonly usersActiveNo: "accessAdmin.users.activeNo";
656
+ /** Tenant cell for a user bound to no tenant (a platform-level account). */
657
+ readonly usersTenantNone: "accessAdmin.users.tenantNone";
658
+ /** Roles cell for a user with no roles yet. */
659
+ readonly usersNoRoles: "accessAdmin.users.noRoles";
660
+ /** Created cell when the API did not return a timestamp. */
661
+ readonly usersCreatedUnknown: "accessAdmin.users.createdUnknown";
662
+ readonly usersEdit: "accessAdmin.users.edit";
663
+ readonly usersEditHint: "accessAdmin.users.editHint";
664
+ readonly usersReset: "accessAdmin.users.reset";
665
+ readonly usersResetHint: "accessAdmin.users.resetHint";
666
+ readonly usersLoading: "accessAdmin.users.loading";
667
+ readonly usersEmpty: "accessAdmin.users.empty";
668
+ /** Shown when the user list FAILED to load — never conflated with "there are none". */
669
+ readonly usersLoadError: "accessAdmin.users.loadError";
670
+ readonly usersRetry: "accessAdmin.users.retry";
671
+ readonly usersRetryHint: "accessAdmin.users.retryHint";
672
+ readonly usersCreateTitle: "accessAdmin.users.createTitle";
673
+ readonly usersEditTitle: "accessAdmin.users.editTitle";
674
+ readonly usersResetTitle: "accessAdmin.users.resetTitle";
675
+ readonly usersFieldEmail: "accessAdmin.users.fieldEmail";
676
+ readonly usersEmailPlaceholder: "accessAdmin.users.emailPlaceholder";
677
+ readonly usersFieldPassword: "accessAdmin.users.fieldPassword";
678
+ readonly usersPasswordPlaceholder: "accessAdmin.users.passwordPlaceholder";
679
+ readonly usersFieldDisplayName: "accessAdmin.users.fieldDisplayName";
680
+ readonly usersDisplayNamePlaceholder: "accessAdmin.users.displayNamePlaceholder";
681
+ readonly usersFieldRole: "accessAdmin.users.fieldRole";
682
+ readonly usersRolePlaceholder: "accessAdmin.users.rolePlaceholder";
683
+ readonly usersRolePickerHint: "accessAdmin.users.rolePickerHint";
684
+ readonly usersFieldTenant: "accessAdmin.users.fieldTenant";
685
+ readonly usersTenantPlaceholder: "accessAdmin.users.tenantPlaceholder";
686
+ readonly usersTenantPickerHint: "accessAdmin.users.tenantPickerHint";
687
+ readonly usersFieldActive: "accessAdmin.users.fieldActive";
688
+ readonly usersActiveToggleHint: "accessAdmin.users.activeToggleHint";
689
+ readonly usersFieldNewPassword: "accessAdmin.users.fieldNewPassword";
690
+ readonly usersNewPasswordPlaceholder: "accessAdmin.users.newPasswordPlaceholder";
691
+ readonly usersSubmitCreate: "accessAdmin.users.submitCreate";
692
+ readonly usersSubmitCreateHint: "accessAdmin.users.submitCreateHint";
693
+ readonly usersSubmitSave: "accessAdmin.users.submitSave";
694
+ readonly usersSubmitSaveHint: "accessAdmin.users.submitSaveHint";
695
+ readonly usersSubmitReset: "accessAdmin.users.submitReset";
696
+ readonly usersSubmitResetHint: "accessAdmin.users.submitResetHint";
697
+ readonly usersCancel: "accessAdmin.users.cancel";
698
+ readonly usersCancelHint: "accessAdmin.users.cancelHint";
699
+ readonly usersCreateSuccess: "accessAdmin.users.createSuccess";
700
+ readonly usersCreateFailed: "accessAdmin.users.createFailed";
701
+ readonly usersUpdateSuccess: "accessAdmin.users.updateSuccess";
702
+ readonly usersUpdateFailed: "accessAdmin.users.updateFailed";
703
+ readonly usersResetSuccess: "accessAdmin.users.resetSuccess";
704
+ readonly usersResetFailed: "accessAdmin.users.resetFailed";
531
705
  };
532
706
  /** The shape of {@link ACCESS_ADMIN_I18N} — useful for typing an override map. */
533
707
  type AccessAdminI18n = typeof ACCESS_ADMIN_I18N;
@@ -539,6 +713,7 @@ type AccessAdminI18n = typeof ACCESS_ADMIN_I18N;
539
713
  declare const ACCESS_ADMIN_TEST_IDS: {
540
714
  readonly linkPanel: "access-link-panel";
541
715
  readonly accountPanel: "account-link-panel";
716
+ readonly usersPanel: "user-management-panel";
542
717
  };
543
718
 
544
- export { ACCESS_ADMIN_I18N, ACCESS_ADMIN_TEST_IDS, type AccessAdminI18n, type AccessAdminPalette, AccessLinkPanel, type AccessLinkPanelProps, type AccessResultReporter, AccessResultTone, type AccessSubject, type AccessTranslate, AccountLinkPanel, type AccountLinkPanelProps, type CapabilityLink, type CapabilityLinkMintState, type DescribeError, type ExpiryPolicy, type LinkSubjectAccount, type LinkableAccount, type MintCapabilityLink, type MintCapabilityLinkInput, type SubjectAccountLinkState, type UseCapabilityLinkMintOptions, type UseSubjectAccountLinkOptions, accountDisplayLabel, activeLinksForSubject, buildAccountOptions, buildCapabilityLinkMailto, capabilityExpiryDate, capabilityExpiryDays, daysUntilAnchor, formatLinkDate, linksForSubject, useAccessAdminPalette, useCapabilityLinkMint, useSubjectAccountLink };
719
+ export { ACCESS_ADMIN_I18N, ACCESS_ADMIN_TEST_IDS, type AccessAdminI18n, type AccessAdminPalette, AccessLinkPanel, type AccessLinkPanelProps, type AccessResultReporter, AccessResultTone, type AccessSubject, type AccessTranslate, AccountLinkPanel, type AccountLinkPanelProps, type CapabilityLink, type CapabilityLinkMintState, type DescribeError, type ExpiryPolicy, type LinkSubjectAccount, type LinkableAccount, type MintCapabilityLink, type MintCapabilityLinkInput, type SubjectAccountLinkState, type UseCapabilityLinkMintOptions, type UseSubjectAccountLinkOptions, type UserManagementApi, UserManagementPanel, type UserManagementPanelProps, type UserManagementTenant, type UserManagementTranslate, type UserManagementUser, accountDisplayLabel, activeLinksForSubject, buildAccountOptions, buildCapabilityLinkMailto, capabilityExpiryDate, capabilityExpiryDays, daysUntilAnchor, formatLinkDate, linksForSubject, useAccessAdminPalette, useCapabilityLinkMint, useSubjectAccountLink };