@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 +28 -0
- package/dist/index.d.mts +176 -1
- package/dist/index.d.ts +176 -1
- package/dist/index.js +752 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
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 };
|