@dloizides/ui-access-admin 1.0.1

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 ADDED
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@dloizides/ui-access-admin` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
6
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-07-22
9
+
10
+ Initial release. Promoted out of `kefi-web`'s organizer promoter roster, which had the
11
+ only working implementation of scoped capability links in the fleet — and had it welded
12
+ to a `PromoterDto`.
13
+
14
+ ### Added
15
+
16
+ - **`AccessLinkPanel`** — mint / copy / share / revoke a scoped capability link for one
17
+ subject. Scoped to a single subject on purpose: these links are bearer tokens, so a
18
+ screen listing every subject's live link would itself be a high-value secret.
19
+ - **`AccountLinkPanel`** — bind a subject to its sign-in account, or unbind it. Unlinking
20
+ is a separate press from the picker so a mis-tap cannot detach someone from their work.
21
+ - **`useCapabilityLinkMint`** / **`useSubjectAccountLink`** — the headless halves, for
22
+ hosts that want their own chrome.
23
+ - **Expiry policy** (`capabilityExpiryDays`, `capabilityExpiryDate`, `daysUntilAnchor`) —
24
+ `anchorDate + tailDays`, clamped to the host API's accepted range. No code path returns
25
+ "never".
26
+ - **`ACCESS_ADMIN_I18N`** — the machine-readable i18n manifest hosts bind a missing-key
27
+ guard to, following the `LAYOUT_I18N` / `TABLE_I18N` precedent. `accessAdminI18n.test.ts`
28
+ fails the build if a component resolves a raw key literal, or if the map carries an
29
+ unused entry.
30
+ - **`ACCESS_ADMIN_TEST_IDS`** plus a `testIDPrefix` on both panels, so an adopting app
31
+ keeps its existing Playwright selectors.
32
+ - **`useAccessAdminPalette`** — maps the host's `UiProvider` theme onto the colour roles
33
+ the panels need. No colour literals in the package.
34
+
35
+ ### Notes on the extraction
36
+
37
+ - **No host domain types cross the boundary.** `AccessSubject` is `{ id, displayName }`.
38
+ Resolving WHICH subject an API's link row belongs to is the host's job, done once when
39
+ it normalizes its DTOs — which keeps a product's data-model compromise (kefi matches on
40
+ recipient name, because its wire format omits the binding) out of the shared filter.
41
+ - **No API calls, no react-query.** Mint, link and unlink are injected async callbacks.
42
+ - **A failed link-list load is never conflated with an empty one.** `linksFailed` exists
43
+ because "we could not load this" rendered as "this person has no live link" invites
44
+ minting a second live capability and hides that there is something to revoke.
45
+ - **A refused clipboard write after a successful mint is reported as an ERROR**, not a
46
+ warning: the URL is readable exactly once, so a green toast over an empty clipboard
47
+ costs a revoke-and-re-mint.
48
+ - `CheckboxField` was considered for extraction and deliberately **not** taken — the kit
49
+ already ships `FormCheckbox` in `@dloizides/ui-forms`, carrying both the `aria-checked`
50
+ fix and a keyboard-activation fix. Adding a third copy here would have been the bug this
51
+ package exists to avoid.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 dloizides
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # @dloizides/ui-access-admin
2
+
3
+ Brand-agnostic React Native (RN-web) **user & access administration UI** for the
4
+ dloizides.com shared kit.
5
+
6
+ The frontend counterpart to what `Identity.Abstractions` already shares on the backend
7
+ (`IUserManagementService`, `TenantManagedRoleSet`). It ships the two screens every
8
+ product ends up rebuilding:
9
+
10
+ - **`AccessLinkPanel`** — mint, copy, share and revoke a **scoped capability link** for
11
+ one subject, always with an expiry.
12
+ - **`AccountLinkPanel`** — bind a subject to the account it signs in with, or unbind it.
13
+
14
+ Promoted out of `kefi-web`, with every Kefi noun removed.
15
+
16
+ ---
17
+
18
+ ## The two rules that make it reusable
19
+
20
+ **1. No host domain types in the public API.** The package knows about a `AccessSubject`
21
+ (`{ id, displayName }`), a `CapabilityLink`, and a `LinkableAccount`. Nothing else. A
22
+ survey respondent, a menu editor and a party promoter are all subjects.
23
+
24
+ **2. No API calls.** Every write is an injected `async` callback. There is no
25
+ `react-query` dependency and no client. You wire your own endpoints and your own cache
26
+ invalidation.
27
+
28
+ ---
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ npm install @dloizides/ui-access-admin
34
+ ```
35
+
36
+ Peers: `@dloizides/ui-feedback`, `@dloizides/ui-buttons`, `@dloizides/ui-forms`,
37
+ `@dloizides/ui-layout`, `react`, `react-native`.
38
+
39
+ Mount a `UiProvider` (from `@dloizides/ui-feedback`) above these panels — it supplies both
40
+ the theme and `t`.
41
+
42
+ ---
43
+
44
+ ## Quick start
45
+
46
+ ```tsx
47
+ import { AccessLinkPanel, type CapabilityLink } from '@dloizides/ui-access-admin';
48
+
49
+ <AccessLinkPanel
50
+ subject={{ id: promoter.externalId, displayName: promoter.name }}
51
+ policy={{ anchorDate: eventDate, tailDays: 14, minDays: 1, maxDays: 365 }}
52
+ links={links} // normalized to CapabilityLink by YOUR adapter
53
+ linksFailed={linksQuery.isError} // never conflated with "there are none"
54
+ mint={async ({ subject, expiresInDays }) => toCapabilityLink(
55
+ await createLink.mutateAsync(buildRequest(subject, expiresInDays)),
56
+ )}
57
+ describeError={(error) => FM(mutationErrorKey(error))}
58
+ renderLinkActions={(link) => <MyRevokeButton linkId={link.id} />}
59
+ onClose={close}
60
+ onResult={(message, tone) => setBanner({ message, tone })}
61
+ />
62
+ ```
63
+
64
+ ### The expiry policy
65
+
66
+ `capabilityExpiryDays` resolves `anchorDate + tailDays`, clamped into `[minDays, maxDays]`.
67
+
68
+ **There is no code path that returns "never."** A missing, unparseable or past anchor
69
+ yields the bare tail, not an unbounded link. These URLs are bearer tokens pasted into chat
70
+ apps; the expiry is the only thing limiting the blast radius of a forward.
71
+
72
+ ---
73
+
74
+ ## i18n — read this before adopting
75
+
76
+ The package is **fallback-free**. `t` is your resolver, and a missing key renders the raw
77
+ dotted name to the user.
78
+
79
+ `ACCESS_ADMIN_I18N` is a machine-readable manifest of every key the package resolves.
80
+ **Bind your missing-key guard to it**, the way `kefi-web/src/localization/kitTranslationKeys.ts`
81
+ already binds `LAYOUT_I18N` and `TABLE_I18N`:
82
+
83
+ ```ts
84
+ import { ACCESS_ADMIN_I18N } from '@dloizides/ui-access-admin';
85
+
86
+ const ACCESS_ADMIN_KEYS: readonly string[] = Object.values(ACCESS_ADMIN_I18N);
87
+ ```
88
+
89
+ Then an upgrade that adds a string **fails your test suite** instead of shipping raw key
90
+ text. `accessAdminI18n.test.ts` keeps the manifest itself honest: it fails if any component
91
+ resolves a raw literal, and if the map carries an entry nothing uses.
92
+
93
+ The package owns the key **names**; you own the **wording**. That split is what lets one
94
+ product keep warm, specific copy under a key another product reuses with its own.
95
+
96
+ Every key is namespaced under `accessAdmin.*`, so it drops into a locale file as one block.
97
+
98
+ ---
99
+
100
+ ## testIDs
101
+
102
+ Both panels take a `testIDPrefix`; every child derives from it. Adopting apps already have
103
+ Playwright selectors, and a shared component is not a good enough reason to rewrite an E2E
104
+ suite.
105
+
106
+ ---
107
+
108
+ ## Theming
109
+
110
+ Colours come from your `UiProvider` theme (`@dloizides/design-tokens`). The package defines
111
+ **no colour literals** — `useAccessAdminPalette` maps the theme onto the roles the panels
112
+ need, so a rebrand reaches them for free.
113
+
114
+ ---
115
+
116
+ ## API
117
+
118
+ | Export | What it is |
119
+ |---|---|
120
+ | `AccessLinkPanel` | The capability-link panel for one subject |
121
+ | `AccountLinkPanel` | The account picker for one subject |
122
+ | `useCapabilityLinkMint` | Mint-and-copy state (headless) |
123
+ | `useSubjectAccountLink` | Selection + submit state for account linking (headless) |
124
+ | `capabilityExpiryDays` / `capabilityExpiryDate` / `daysUntilAnchor` | The expiry policy |
125
+ | `linksForSubject` / `activeLinksForSubject` / `formatLinkDate` | Pure link helpers |
126
+ | `buildCapabilityLinkMailto` | `mailto:` compose URL (no recipient — see the source) |
127
+ | `buildAccountOptions` / `accountDisplayLabel` | Pure picker helpers |
128
+ | `useAccessAdminPalette` | Theme → colour roles |
129
+ | `ACCESS_ADMIN_I18N` / `ACCESS_ADMIN_TEST_IDS` | The host contracts |
130
+ | `AccessResultTone` | `Success` \| `Error` |
131
+
132
+ ---
133
+
134
+ ## License
135
+
136
+ MIT