@dloizides/ui-access-admin
v1.0.1
Published
Brand-agnostic React Native (RN-web) user & access administration UI — scoped capability links (mint / copy / share / revoke, always with an expiry) and subject-to-account linking. Speaks in neutral subjects, takes its API calls by injection.
Maintainers
Readme
@dloizides/ui-access-admin
Brand-agnostic React Native (RN-web) user & access administration UI for the dloizides.com shared kit.
The frontend counterpart to what Identity.Abstractions already shares on the backend
(IUserManagementService, TenantManagedRoleSet). It ships the two screens every
product ends up rebuilding:
AccessLinkPanel— mint, copy, share and revoke a scoped capability link for one subject, always with an expiry.AccountLinkPanel— bind a subject to the account it signs in with, or unbind it.
Promoted out of kefi-web, with every Kefi noun removed.
The two rules that make it reusable
1. No host domain types in the public API. The package knows about a AccessSubject
({ id, displayName }), a CapabilityLink, and a LinkableAccount. Nothing else. A
survey respondent, a menu editor and a party promoter are all subjects.
2. No API calls. Every write is an injected async callback. There is no
react-query dependency and no client. You wire your own endpoints and your own cache
invalidation.
Install
npm install @dloizides/ui-access-adminPeers: @dloizides/ui-feedback, @dloizides/ui-buttons, @dloizides/ui-forms,
@dloizides/ui-layout, react, react-native.
Mount a UiProvider (from @dloizides/ui-feedback) above these panels — it supplies both
the theme and t.
Quick start
import { AccessLinkPanel, type CapabilityLink } from '@dloizides/ui-access-admin';
<AccessLinkPanel
subject={{ id: promoter.externalId, displayName: promoter.name }}
policy={{ anchorDate: eventDate, tailDays: 14, minDays: 1, maxDays: 365 }}
links={links} // normalized to CapabilityLink by YOUR adapter
linksFailed={linksQuery.isError} // never conflated with "there are none"
mint={async ({ subject, expiresInDays }) => toCapabilityLink(
await createLink.mutateAsync(buildRequest(subject, expiresInDays)),
)}
describeError={(error) => FM(mutationErrorKey(error))}
renderLinkActions={(link) => <MyRevokeButton linkId={link.id} />}
onClose={close}
onResult={(message, tone) => setBanner({ message, tone })}
/>The expiry policy
capabilityExpiryDays resolves anchorDate + tailDays, clamped into [minDays, maxDays].
There is no code path that returns "never." A missing, unparseable or past anchor yields the bare tail, not an unbounded link. These URLs are bearer tokens pasted into chat apps; the expiry is the only thing limiting the blast radius of a forward.
i18n — read this before adopting
The package is fallback-free. t is your resolver, and a missing key renders the raw
dotted name to the user.
ACCESS_ADMIN_I18N is a machine-readable manifest of every key the package resolves.
Bind your missing-key guard to it, the way kefi-web/src/localization/kitTranslationKeys.ts
already binds LAYOUT_I18N and TABLE_I18N:
import { ACCESS_ADMIN_I18N } from '@dloizides/ui-access-admin';
const ACCESS_ADMIN_KEYS: readonly string[] = Object.values(ACCESS_ADMIN_I18N);Then an upgrade that adds a string fails your test suite instead of shipping raw key
text. accessAdminI18n.test.ts keeps the manifest itself honest: it fails if any component
resolves a raw literal, and if the map carries an entry nothing uses.
The package owns the key names; you own the wording. That split is what lets one product keep warm, specific copy under a key another product reuses with its own.
Every key is namespaced under accessAdmin.*, so it drops into a locale file as one block.
testIDs
Both panels take a testIDPrefix; every child derives from it. Adopting apps already have
Playwright selectors, and a shared component is not a good enough reason to rewrite an E2E
suite.
Theming
Colours come from your UiProvider theme (@dloizides/design-tokens). The package defines
no colour literals — useAccessAdminPalette maps the theme onto the roles the panels
need, so a rebrand reaches them for free.
API
| Export | What it is |
|---|---|
| AccessLinkPanel | The capability-link panel for one subject |
| AccountLinkPanel | The account picker for one subject |
| useCapabilityLinkMint | Mint-and-copy state (headless) |
| useSubjectAccountLink | Selection + submit state for account linking (headless) |
| capabilityExpiryDays / capabilityExpiryDate / daysUntilAnchor | The expiry policy |
| linksForSubject / activeLinksForSubject / formatLinkDate | Pure link helpers |
| buildCapabilityLinkMailto | mailto: compose URL (no recipient — see the source) |
| buildAccountOptions / accountDisplayLabel | Pure picker helpers |
| useAccessAdminPalette | Theme → colour roles |
| ACCESS_ADMIN_I18N / ACCESS_ADMIN_TEST_IDS | The host contracts |
| AccessResultTone | Success | Error |
License
MIT
