uhc-portal-components-react 0.1.6 → 0.1.8
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 +16 -0
- package/README.md +16 -0
- package/dist/InvitationAcceptancePanel.d.ts +25 -0
- package/dist/InvitationAcceptancePanel.d.ts.map +1 -0
- package/dist/InvitationAcceptancePanel.js +16 -0
- package/dist/SoschainWorkspace.d.ts +70 -0
- package/dist/SoschainWorkspace.d.ts.map +1 -0
- package/dist/SoschainWorkspace.js +33 -0
- package/dist/SubjectAccessRequestPanel.d.ts +48 -0
- package/dist/SubjectAccessRequestPanel.d.ts.map +1 -0
- package/dist/SubjectAccessRequestPanel.js +21 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/styles.css +60 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.8 - 2026-08-02
|
|
4
|
+
|
|
5
|
+
- Add the shared subject-first SOSChain card picker and workspace for Places,
|
|
6
|
+
IndividualProducts and emergency/civil-protection communications.
|
|
7
|
+
- Keep authorization host-owned: the component receives server-resolved cards
|
|
8
|
+
and capabilities and never treats a browser selection as authority.
|
|
9
|
+
|
|
10
|
+
## 0.1.7 - 2026-07-30
|
|
11
|
+
|
|
12
|
+
- Add controlled, actor-neutral invitation-acceptance and subject-access
|
|
13
|
+
request panels. The same scanner/manual locator can be hosted by an
|
|
14
|
+
individual-member or professional facade; authorization remains in the BFF
|
|
15
|
+
and GW.
|
|
16
|
+
- Keep requested clinical sections as explicit `system|code` values supplied
|
|
17
|
+
by the host instead of deriving role or permission semantics in React.
|
|
18
|
+
|
|
3
19
|
## 0.1.6 - 2026-07-30
|
|
4
20
|
|
|
5
21
|
- Consume Common Utils 2.3.12 so the shared IPS viewer uses the complete
|
package/README.md
CHANGED
|
@@ -53,6 +53,15 @@ The package deliberately exposes modules instead of a cloned application:
|
|
|
53
53
|
- `IpsBundleSummary`: receives one in-memory IPS document Bundle, obtains all
|
|
54
54
|
`Composition.section` entries through Common Utils and renders every
|
|
55
55
|
section's cards. It performs no ingestion or GW call.
|
|
56
|
+
- `InvitationAcceptancePanel`: controlled invitation-code UI for a related
|
|
57
|
+
individual member; the host refreshes the authorized-subject list after its
|
|
58
|
+
BFF accepts the invitation.
|
|
59
|
+
- `SubjectAccessRequestPanel`: controlled QR, numeric-code or `did:web`
|
|
60
|
+
resolver plus section selector. It deliberately has no scanner role: the
|
|
61
|
+
same component is reusable by an individual member or a professional.
|
|
62
|
+
- `SoschainWorkspace`: subject-card picker and four-area workspace for Places,
|
|
63
|
+
devices/vehicles and read-only emergency communications. The host supplies
|
|
64
|
+
already-authorized cards and capabilities.
|
|
56
65
|
|
|
57
66
|
Clinical label semantics stay in `gdc-common-utils-ts` / the SDK reader. Hosts
|
|
58
67
|
pass human titles to `ClinicalSectionSummary`: local `CodeableConcept.text`
|
|
@@ -73,6 +82,13 @@ The browser may render an optimistic in-memory Bundle while its authenticated
|
|
|
73
82
|
BFF persists the command. Only backend code calls
|
|
74
83
|
`ingestCommunicationAndUpdateIndex(...)`; the component never does.
|
|
75
84
|
|
|
85
|
+
Access panels likewise contain no consent or SMART-token logic. A host first
|
|
86
|
+
resolves the card locator, then asks its authenticated BFF to record the access
|
|
87
|
+
request. If consent is already present, the BFF may obtain a narrowed SMART
|
|
88
|
+
token immediately; otherwise it records a `Communication` request for the
|
|
89
|
+
individual controller to review. The GW remains authoritative for the exact
|
|
90
|
+
intersection of requested and consented sections.
|
|
91
|
+
|
|
76
92
|
UHC Personal and VetChain Personal must compose the same personal route
|
|
77
93
|
modules. UHC Professional and VetChain Professional must compose the same
|
|
78
94
|
professional route modules. A brand adapter may change only tokens, logo,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type InvitationAcceptanceStatus = 'idle' | 'submitting' | 'accepted' | 'error';
|
|
2
|
+
export type InvitationAcceptancePanelCopy = Readonly<{
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
codeLabel: string;
|
|
6
|
+
submit: string;
|
|
7
|
+
submitting: string;
|
|
8
|
+
accepted: string;
|
|
9
|
+
}>;
|
|
10
|
+
export type InvitationAcceptancePanelProps = Readonly<{
|
|
11
|
+
copy: InvitationAcceptancePanelCopy;
|
|
12
|
+
code: string;
|
|
13
|
+
status: InvitationAcceptanceStatus;
|
|
14
|
+
error?: string;
|
|
15
|
+
onCodeChange: (code: string) => void;
|
|
16
|
+
onAccept: () => void | Promise<void>;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Controlled invitation-acceptance form reusable by any personal portal.
|
|
20
|
+
*
|
|
21
|
+
* The component owns no authorization semantics. The host BFF verifies the
|
|
22
|
+
* authenticated actor, accepts the invitation and refreshes related subjects.
|
|
23
|
+
*/
|
|
24
|
+
export declare function InvitationAcceptancePanel({ copy, code, status, error, onCodeChange, onAccept, }: InvitationAcceptancePanelProps): import("react").JSX.Element;
|
|
25
|
+
//# sourceMappingURL=InvitationAcceptancePanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvitationAcceptancePanel.d.ts","sourceRoot":"","sources":["../src/InvitationAcceptancePanel.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,0BAA0B,GAClC,MAAM,GACN,YAAY,GACZ,UAAU,GACV,OAAO,CAAA;AAEX,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,6BAA6B,CAAA;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,0BAA0B,CAAA;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,GACT,EAAE,8BAA8B,+BA+BhC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Controlled invitation-acceptance form reusable by any personal portal.
|
|
4
|
+
*
|
|
5
|
+
* The component owns no authorization semantics. The host BFF verifies the
|
|
6
|
+
* authenticated actor, accepts the invitation and refreshes related subjects.
|
|
7
|
+
*/
|
|
8
|
+
export function InvitationAcceptancePanel({ copy, code, status, error, onCodeChange, onAccept, }) {
|
|
9
|
+
function submit(event) {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
void onAccept();
|
|
12
|
+
}
|
|
13
|
+
return (_jsxs("section", { className: "uhcp-access-panel", "aria-labelledby": "uhcp-invitation-title", children: [_jsx("h2", { id: "uhcp-invitation-title", children: copy.title }), _jsx("p", { children: copy.description }), status === 'accepted'
|
|
14
|
+
? _jsx("p", { role: "status", children: copy.accepted })
|
|
15
|
+
: (_jsxs("form", { onSubmit: submit, children: [_jsxs("label", { children: [_jsx("span", { children: copy.codeLabel }), _jsx("input", { value: code, onChange: (event) => onCodeChange(event.currentTarget.value), required: true, disabled: status === 'submitting' })] }), error && _jsx("p", { role: "alert", children: error }), _jsx("button", { type: "submit", disabled: status === 'submitting' || !code.trim(), children: status === 'submitting' ? copy.submitting : copy.submit })] }))] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export type SoschainWorkspaceArea = 'overview' | 'places' | 'products' | 'communications';
|
|
2
|
+
export type SoschainCardSummary = Readonly<{
|
|
3
|
+
identifier: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
kindLabel: string;
|
|
6
|
+
selected?: boolean;
|
|
7
|
+
openHref: string;
|
|
8
|
+
}>;
|
|
9
|
+
export type SoschainAssociationSummary = Readonly<{
|
|
10
|
+
identifier: string;
|
|
11
|
+
name: string;
|
|
12
|
+
kindLabel: string;
|
|
13
|
+
detail: string;
|
|
14
|
+
statusLabel: string;
|
|
15
|
+
}>;
|
|
16
|
+
export type SoschainCommunicationSummary = Readonly<{
|
|
17
|
+
identifier: string;
|
|
18
|
+
sender: string;
|
|
19
|
+
categoryLabel: string;
|
|
20
|
+
subject: string;
|
|
21
|
+
body: string;
|
|
22
|
+
occurredAt: string;
|
|
23
|
+
placeLabel?: string;
|
|
24
|
+
}>;
|
|
25
|
+
export type SoschainWorkspaceCopy = Readonly<{
|
|
26
|
+
cardsTitle: string;
|
|
27
|
+
cardsDescription: string;
|
|
28
|
+
openCard: string;
|
|
29
|
+
selectedCard: string;
|
|
30
|
+
workspaceEyebrow: string;
|
|
31
|
+
noCardTitle: string;
|
|
32
|
+
noCardBody: string;
|
|
33
|
+
areas: Record<SoschainWorkspaceArea, string>;
|
|
34
|
+
overviewTitle: string;
|
|
35
|
+
overviewBody: string;
|
|
36
|
+
placesTitle: string;
|
|
37
|
+
placesBody: string;
|
|
38
|
+
productsTitle: string;
|
|
39
|
+
productsBody: string;
|
|
40
|
+
communicationsTitle: string;
|
|
41
|
+
communicationsBody: string;
|
|
42
|
+
addPlace: string;
|
|
43
|
+
addProduct: string;
|
|
44
|
+
emptyPlaces: string;
|
|
45
|
+
emptyProducts: string;
|
|
46
|
+
emptyCommunications: string;
|
|
47
|
+
readOnly: string;
|
|
48
|
+
}>;
|
|
49
|
+
export type SoschainWorkspaceProps = Readonly<{
|
|
50
|
+
copy: SoschainWorkspaceCopy;
|
|
51
|
+
cards: readonly SoschainCardSummary[];
|
|
52
|
+
selectedCard?: SoschainCardSummary;
|
|
53
|
+
activeArea: SoschainWorkspaceArea;
|
|
54
|
+
areaHrefs: Record<SoschainWorkspaceArea, string>;
|
|
55
|
+
places: readonly SoschainAssociationSummary[];
|
|
56
|
+
products: readonly SoschainAssociationSummary[];
|
|
57
|
+
communications: readonly SoschainCommunicationSummary[];
|
|
58
|
+
canManageAssociations: boolean;
|
|
59
|
+
addPlaceHref?: string;
|
|
60
|
+
addProductHref?: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Shared subject-first SOSChain workspace for personal and professional apps.
|
|
64
|
+
*
|
|
65
|
+
* The host supplies already-authorized cards, associations, capabilities and
|
|
66
|
+
* communications. This presentational component never derives authority from
|
|
67
|
+
* a selected role, card, tab or browser-controlled value.
|
|
68
|
+
*/
|
|
69
|
+
export declare function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, canManageAssociations, addPlaceHref, addProductHref, }: SoschainWorkspaceProps): import("react").JSX.Element;
|
|
70
|
+
//# sourceMappingURL=SoschainWorkspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SoschainWorkspace.d.ts","sourceRoot":"","sources":["../src/SoschainWorkspace.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,gBAAgB,CAAA;AAEzF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;IAC5C,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,IAAI,EAAE,qBAAqB,CAAA;IAC3B,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACrC,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,UAAU,EAAE,qBAAqB,CAAA;IACjC,SAAS,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;IAChD,MAAM,EAAE,SAAS,0BAA0B,EAAE,CAAA;IAC7C,QAAQ,EAAE,SAAS,0BAA0B,EAAE,CAAA;IAC/C,cAAc,EAAE,SAAS,4BAA4B,EAAE,CAAA;IACvD,qBAAqB,EAAE,OAAO,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAqBF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,KAAK,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,cAAc,GACf,EAAE,sBAAsB,+BAmGxB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function AssociationList({ items, empty }) {
|
|
3
|
+
if (items.length === 0)
|
|
4
|
+
return _jsx("p", { className: "uhcp-sos-empty", children: empty });
|
|
5
|
+
return (_jsx("ul", { className: "uhcp-sos-entity-list", children: items.map((item) => (_jsxs("li", { children: [_jsx("span", { className: "uhcp-sos-kind", children: item.kindLabel }), _jsx("strong", { children: item.name }), _jsx("span", { children: item.detail }), _jsx("small", { children: item.statusLabel })] }, item.identifier))) }));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Shared subject-first SOSChain workspace for personal and professional apps.
|
|
9
|
+
*
|
|
10
|
+
* The host supplies already-authorized cards, associations, capabilities and
|
|
11
|
+
* communications. This presentational component never derives authority from
|
|
12
|
+
* a selected role, card, tab or browser-controlled value.
|
|
13
|
+
*/
|
|
14
|
+
export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, canManageAssociations, addPlaceHref, addProductHref, }) {
|
|
15
|
+
if (!selectedCard) {
|
|
16
|
+
return (_jsxs("section", { className: "uhcp-sos-wrap", "aria-labelledby": "sos-card-picker-title", children: [_jsxs("header", { className: "uhcp-sos-heading", children: [_jsx("span", { children: copy.workspaceEyebrow }), _jsx("h1", { id: "sos-card-picker-title", children: copy.cardsTitle }), _jsx("p", { children: copy.cardsDescription })] }), cards.length === 0 ? (_jsxs("div", { className: "uhcp-sos-empty-panel", children: [_jsx("h2", { children: copy.noCardTitle }), _jsx("p", { children: copy.noCardBody })] })) : (_jsx("ul", { className: "uhcp-sos-card-grid", children: cards.map((card) => (_jsx("li", { children: _jsxs("a", { href: card.openHref, children: [_jsx("span", { children: card.kindLabel }), _jsx("strong", { children: card.displayName }), _jsx("small", { children: card.identifier }), _jsxs("b", { children: [copy.openCard, " \u2192"] })] }) }, card.identifier))) }))] }));
|
|
17
|
+
}
|
|
18
|
+
const title = activeArea === 'places'
|
|
19
|
+
? copy.placesTitle
|
|
20
|
+
: activeArea === 'products'
|
|
21
|
+
? copy.productsTitle
|
|
22
|
+
: activeArea === 'communications'
|
|
23
|
+
? copy.communicationsTitle
|
|
24
|
+
: copy.overviewTitle;
|
|
25
|
+
const body = activeArea === 'places'
|
|
26
|
+
? copy.placesBody
|
|
27
|
+
: activeArea === 'products'
|
|
28
|
+
? copy.productsBody
|
|
29
|
+
: activeArea === 'communications'
|
|
30
|
+
? copy.communicationsBody
|
|
31
|
+
: copy.overviewBody;
|
|
32
|
+
return (_jsxs("section", { className: "uhcp-sos-wrap", children: [_jsxs("header", { className: "uhcp-sos-heading", children: [_jsx("span", { children: copy.selectedCard }), _jsx("h1", { children: selectedCard.displayName }), _jsxs("p", { children: [selectedCard.kindLabel, " \u00B7 ", selectedCard.identifier] })] }), _jsx("nav", { className: "uhcp-sos-tabs", "aria-label": copy.workspaceEyebrow, children: Object.keys(areaHrefs).map((area) => (_jsx("a", { href: areaHrefs[area], "aria-current": activeArea === area ? 'page' : undefined, children: copy.areas[area] }, area))) }), _jsxs("div", { className: "uhcp-sos-panel", children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { children: title }), _jsx("p", { children: body })] }), !canManageAssociations && activeArea !== 'communications' && (_jsx("span", { className: "uhcp-sos-readonly", children: copy.readOnly })), canManageAssociations && activeArea === 'places' && addPlaceHref && _jsx("a", { href: addPlaceHref, children: copy.addPlace }), canManageAssociations && activeArea === 'products' && addProductHref && _jsx("a", { href: addProductHref, children: copy.addProduct })] }), activeArea === 'overview' && (_jsxs("div", { className: "uhcp-sos-overview-grid", children: [_jsxs("article", { children: [_jsx("strong", { children: places.length }), _jsx("span", { children: copy.areas.places })] }), _jsxs("article", { children: [_jsx("strong", { children: products.length }), _jsx("span", { children: copy.areas.products })] }), _jsxs("article", { children: [_jsx("strong", { children: communications.length }), _jsx("span", { children: copy.areas.communications })] })] })), activeArea === 'places' && _jsx(AssociationList, { items: places, empty: copy.emptyPlaces }), activeArea === 'products' && _jsx(AssociationList, { items: products, empty: copy.emptyProducts }), activeArea === 'communications' && (communications.length === 0 ? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyCommunications }) : (_jsx("ol", { className: "uhcp-sos-timeline", children: communications.map((message) => (_jsxs("li", { children: [_jsxs("div", { children: [_jsx("span", { children: message.categoryLabel }), _jsx("time", { children: message.occurredAt })] }), _jsx("strong", { children: message.subject }), _jsx("p", { children: message.body }), _jsxs("small", { children: [message.sender, message.placeLabel ? ` · ${message.placeLabel}` : ''] })] }, message.identifier))) })))] })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type SubjectLocatorKind = 'qr' | 'numeric-code' | 'did-web';
|
|
2
|
+
export type SubjectAccessRequestStatus = 'idle' | 'resolving' | 'resolved' | 'requesting' | 'requested' | 'granted' | 'error';
|
|
3
|
+
export type SubjectAccessSectionOption = Readonly<{
|
|
4
|
+
code: string;
|
|
5
|
+
label: string;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
export type ResolvedSubjectAccessTarget = Readonly<{
|
|
9
|
+
subjectId: string;
|
|
10
|
+
display: string;
|
|
11
|
+
}>;
|
|
12
|
+
export type SubjectAccessRequestPanelCopy = Readonly<{
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
locatorKindLabel: string;
|
|
16
|
+
locatorKinds: Record<SubjectLocatorKind, string>;
|
|
17
|
+
locatorLabel: string;
|
|
18
|
+
resolve: string;
|
|
19
|
+
resolving: string;
|
|
20
|
+
sectionsLabel: string;
|
|
21
|
+
request: string;
|
|
22
|
+
requesting: string;
|
|
23
|
+
requested: string;
|
|
24
|
+
granted: string;
|
|
25
|
+
}>;
|
|
26
|
+
export type SubjectAccessRequestPanelProps = Readonly<{
|
|
27
|
+
copy: SubjectAccessRequestPanelCopy;
|
|
28
|
+
locatorKind: SubjectLocatorKind;
|
|
29
|
+
locator: string;
|
|
30
|
+
status: SubjectAccessRequestStatus;
|
|
31
|
+
target?: ResolvedSubjectAccessTarget;
|
|
32
|
+
sections: readonly SubjectAccessSectionOption[];
|
|
33
|
+
error?: string;
|
|
34
|
+
onLocatorKindChange: (kind: SubjectLocatorKind) => void;
|
|
35
|
+
onLocatorChange: (locator: string) => void;
|
|
36
|
+
onResolve: () => void | Promise<void>;
|
|
37
|
+
onToggleSection: (code: string) => void;
|
|
38
|
+
onRequest: () => void | Promise<void>;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Controlled scanner/manual-locator and access-request surface.
|
|
42
|
+
*
|
|
43
|
+
* “Who scans” is intentionally absent from this contract. An individual
|
|
44
|
+
* member, professional or another later actor facade can reuse the same
|
|
45
|
+
* component; the authenticated BFF and GW decide identity and authorization.
|
|
46
|
+
*/
|
|
47
|
+
export declare function SubjectAccessRequestPanel({ copy, locatorKind, locator, status, target, sections, error, onLocatorKindChange, onLocatorChange, onResolve, onToggleSection, onRequest, }: SubjectAccessRequestPanelProps): import("react").JSX.Element;
|
|
48
|
+
//# sourceMappingURL=SubjectAccessRequestPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubjectAccessRequestPanel.d.ts","sourceRoot":"","sources":["../src/SubjectAccessRequestPanel.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,cAAc,GAAG,SAAS,CAAA;AAClE,MAAM,MAAM,0BAA0B,GAClC,MAAM,GACN,WAAW,GACX,UAAU,GACV,YAAY,GACZ,WAAW,GACX,SAAS,GACT,OAAO,CAAA;AAEX,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAC,CAAA;AAEF,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC,CAAA;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;IAChD,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,6BAA6B,CAAA;IACnC,WAAW,EAAE,kBAAkB,CAAA;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,0BAA0B,CAAA;IAClC,MAAM,CAAC,EAAE,2BAA2B,CAAA;IACpC,QAAQ,EAAE,SAAS,0BAA0B,EAAE,CAAA;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mBAAmB,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACvD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,WAAW,EACX,OAAO,EACP,MAAM,EACN,MAAM,EACN,QAAQ,EACR,KAAK,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,eAAe,EACf,SAAS,GACV,EAAE,8BAA8B,+BA4EhC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Controlled scanner/manual-locator and access-request surface.
|
|
4
|
+
*
|
|
5
|
+
* “Who scans” is intentionally absent from this contract. An individual
|
|
6
|
+
* member, professional or another later actor facade can reuse the same
|
|
7
|
+
* component; the authenticated BFF and GW decide identity and authorization.
|
|
8
|
+
*/
|
|
9
|
+
export function SubjectAccessRequestPanel({ copy, locatorKind, locator, status, target, sections, error, onLocatorKindChange, onLocatorChange, onResolve, onToggleSection, onRequest, }) {
|
|
10
|
+
function resolve(event) {
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
void onResolve();
|
|
13
|
+
}
|
|
14
|
+
function request(event) {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
void onRequest();
|
|
17
|
+
}
|
|
18
|
+
return (_jsxs("section", { className: "uhcp-access-panel", "aria-labelledby": "uhcp-access-request-title", children: [_jsx("h2", { id: "uhcp-access-request-title", children: copy.title }), _jsx("p", { children: copy.description }), _jsxs("form", { onSubmit: resolve, children: [_jsxs("label", { children: [_jsx("span", { children: copy.locatorKindLabel }), _jsx("select", { value: locatorKind, onChange: (event) => onLocatorKindChange(event.currentTarget.value), disabled: status === 'resolving', children: Object.entries(copy.locatorKinds).map(([kind, label]) => _jsx("option", { value: kind, children: label }, kind)) })] }), _jsxs("label", { children: [_jsx("span", { children: copy.locatorLabel }), _jsx("input", { value: locator, onChange: (event) => onLocatorChange(event.currentTarget.value), required: true, disabled: status === 'resolving' })] }), _jsx("button", { type: "submit", disabled: status === 'resolving' || !locator.trim(), children: status === 'resolving' ? copy.resolving : copy.resolve })] }), target && (_jsxs("form", { onSubmit: request, children: [_jsx("p", { children: _jsx("bdi", { children: target.display }) }), _jsxs("fieldset", { children: [_jsx("legend", { children: copy.sectionsLabel }), sections.map((section) => (_jsxs("label", { "data-section-code": section.code, children: [_jsx("input", { type: "checkbox", checked: section.selected, onChange: () => onToggleSection(section.code), disabled: status === 'requesting' || status === 'granted' }), _jsx("span", { children: section.label })] }, section.code)))] }), _jsx("button", { type: "submit", disabled: status === 'requesting'
|
|
19
|
+
|| status === 'granted'
|
|
20
|
+
|| !sections.some((section) => section.selected), children: status === 'requesting' ? copy.requesting : copy.request })] })), status === 'requested' && _jsx("p", { role: "status", children: copy.requested }), status === 'granted' && _jsx("p", { role: "status", children: copy.granted }), error && _jsx("p", { role: "alert", children: error })] }));
|
|
21
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ export * from './PortalEntry.js';
|
|
|
7
7
|
export * from './ProfileProtectionGate.js';
|
|
8
8
|
export * from './ClinicalSectionSummary.js';
|
|
9
9
|
export * from './IpsBundleSummary.js';
|
|
10
|
+
export * from './InvitationAcceptancePanel.js';
|
|
11
|
+
export * from './SubjectAccessRequestPanel.js';
|
|
12
|
+
export * from './SoschainWorkspace.js';
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -7,3 +7,6 @@ export * from './PortalEntry.js';
|
|
|
7
7
|
export * from './ProfileProtectionGate.js';
|
|
8
8
|
export * from './ClinicalSectionSummary.js';
|
|
9
9
|
export * from './IpsBundleSummary.js';
|
|
10
|
+
export * from './InvitationAcceptancePanel.js';
|
|
11
|
+
export * from './SubjectAccessRequestPanel.js';
|
|
12
|
+
export * from './SoschainWorkspace.js';
|
package/dist/styles.css
CHANGED
|
@@ -184,6 +184,33 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
184
184
|
.uhcp-clinical-title { font-weight: 800; margin: 0; }
|
|
185
185
|
.uhcp-clinical-type { color: var(--uhcp-muted); font-size: .72rem; font-weight: 800; letter-spacing: .06em; margin: .35rem 0 0; text-transform: uppercase; }
|
|
186
186
|
.uhcp-clinical-date, .uhcp-clinical-empty { color: var(--uhcp-muted); margin: .35rem 0 0; }
|
|
187
|
+
.uhcp-access-panel { color: var(--uhcp-text); display: grid; gap: 1rem; }
|
|
188
|
+
.uhcp-access-panel h2, .uhcp-access-panel p { margin: 0; }
|
|
189
|
+
.uhcp-access-panel > p { color: var(--uhcp-muted); line-height: 1.5; }
|
|
190
|
+
.uhcp-access-panel form { display: grid; gap: 1rem; }
|
|
191
|
+
.uhcp-access-panel label { display: grid; font-weight: 700; gap: .4rem; }
|
|
192
|
+
.uhcp-access-panel input, .uhcp-access-panel select {
|
|
193
|
+
background: var(--uhcp-panel);
|
|
194
|
+
border: 1px solid var(--uhcp-border);
|
|
195
|
+
border-radius: .75rem;
|
|
196
|
+
color: var(--uhcp-text);
|
|
197
|
+
font: inherit;
|
|
198
|
+
padding: .8rem;
|
|
199
|
+
}
|
|
200
|
+
.uhcp-access-panel fieldset { border: 1px solid var(--uhcp-border); border-radius: 1rem; display: grid; gap: .6rem; margin: 0; padding: 1rem; }
|
|
201
|
+
.uhcp-access-panel fieldset label { align-items: center; display: flex; }
|
|
202
|
+
.uhcp-access-panel button {
|
|
203
|
+
background: linear-gradient(135deg, var(--uhcp-action-start), var(--uhcp-action-end));
|
|
204
|
+
border: 0;
|
|
205
|
+
border-radius: .75rem;
|
|
206
|
+
color: #fff;
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
font: inherit;
|
|
209
|
+
font-weight: 800;
|
|
210
|
+
padding: .8rem 1rem;
|
|
211
|
+
}
|
|
212
|
+
.uhcp-access-panel button:disabled { cursor: not-allowed; opacity: .55; }
|
|
213
|
+
.uhcp-access-panel [role="alert"] { color: #c64747; font-weight: 700; }
|
|
187
214
|
.uhcp-entry-hero {
|
|
188
215
|
align-items: center;
|
|
189
216
|
background:
|
|
@@ -237,3 +264,36 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
237
264
|
@media (prefers-reduced-motion: reduce) {
|
|
238
265
|
.uhcp-access-card { transition: none; }
|
|
239
266
|
}
|
|
267
|
+
.uhcp-sos-wrap { margin: 0 auto; max-width: 1160px; padding: clamp(1.25rem, 4vw, 3rem); }
|
|
268
|
+
.uhcp-sos-heading > span, .uhcp-sos-kind { color: var(--uhcp-emerald); font-size: .75rem; font-weight: 900; letter-spacing: .12em; text-transform: uppercase; }
|
|
269
|
+
.uhcp-sos-heading h1 { font-size: clamp(2rem, 5vw, 3.75rem); letter-spacing: -.04em; margin: .45rem 0; }
|
|
270
|
+
.uhcp-sos-heading p, .uhcp-sos-panel header p { color: var(--uhcp-muted); line-height: 1.6; margin: 0; }
|
|
271
|
+
.uhcp-sos-card-grid, .uhcp-sos-entity-list { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); list-style: none; margin: 2rem 0 0; padding: 0; }
|
|
272
|
+
.uhcp-sos-card-grid a, .uhcp-sos-entity-list li { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: 1.5rem; color: var(--uhcp-text); display: grid; gap: .65rem; padding: 1.4rem; text-decoration: none; }
|
|
273
|
+
.uhcp-sos-card-grid a:hover { border-color: var(--uhcp-turquoise); transform: translateY(-2px); }
|
|
274
|
+
.uhcp-sos-card-grid a:focus-visible, .uhcp-sos-tabs a:focus-visible, .uhcp-sos-panel a:focus-visible { outline: 3px solid var(--uhcp-turquoise); outline-offset: 3px; }
|
|
275
|
+
.uhcp-sos-card-grid span, .uhcp-sos-card-grid small, .uhcp-sos-entity-list span, .uhcp-sos-entity-list small { color: var(--uhcp-muted); }
|
|
276
|
+
.uhcp-sos-card-grid b { color: var(--uhcp-turquoise); }
|
|
277
|
+
.uhcp-sos-tabs { display: flex; gap: .5rem; margin: 2rem 0 1rem; overflow-x: auto; padding: .25rem; }
|
|
278
|
+
.uhcp-sos-tabs a { border-radius: 999px; color: var(--uhcp-muted); font-weight: 800; padding: .75rem 1rem; text-decoration: none; white-space: nowrap; }
|
|
279
|
+
.uhcp-sos-tabs a[aria-current='page'] { background: linear-gradient(135deg, var(--uhcp-action-start), var(--uhcp-action-end)); color: #fff; }
|
|
280
|
+
.uhcp-sos-panel, .uhcp-sos-empty-panel { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: 2rem; padding: clamp(1.25rem, 4vw, 2.25rem); }
|
|
281
|
+
.uhcp-sos-panel > header { align-items: flex-start; display: flex; gap: 1rem; justify-content: space-between; }
|
|
282
|
+
.uhcp-sos-panel h2 { margin: 0 0 .35rem; }
|
|
283
|
+
.uhcp-sos-panel > header > a { background: linear-gradient(135deg, var(--uhcp-action-start), var(--uhcp-action-end)); border-radius: 999px; color: #fff; font-weight: 800; padding: .75rem 1rem; text-decoration: none; white-space: nowrap; }
|
|
284
|
+
.uhcp-sos-readonly { background: var(--uhcp-panel-muted); border-radius: 999px; color: var(--uhcp-muted); font-size: .75rem; font-weight: 800; padding: .55rem .75rem; white-space: nowrap; }
|
|
285
|
+
.uhcp-sos-overview-grid { display: grid; gap: 1rem; grid-template-columns: repeat(3, 1fr); margin-top: 2rem; }
|
|
286
|
+
.uhcp-sos-overview-grid article { background: var(--uhcp-panel-muted); border-radius: 1.25rem; display: grid; gap: .3rem; padding: 1.25rem; }
|
|
287
|
+
.uhcp-sos-overview-grid strong { color: var(--uhcp-turquoise); font-size: 2.25rem; }
|
|
288
|
+
.uhcp-sos-overview-grid span, .uhcp-sos-empty { color: var(--uhcp-muted); }
|
|
289
|
+
.uhcp-sos-timeline { display: grid; gap: 0; list-style: none; margin: 2rem 0 0; padding: 0; }
|
|
290
|
+
.uhcp-sos-timeline li { border-left: 2px solid var(--uhcp-border); padding: 0 0 1.75rem 1.5rem; position: relative; }
|
|
291
|
+
.uhcp-sos-timeline li::before { background: var(--uhcp-turquoise); border-radius: 50%; content: ''; height: .75rem; left: -.44rem; position: absolute; top: .2rem; width: .75rem; }
|
|
292
|
+
.uhcp-sos-timeline div { display: flex; gap: 1rem; justify-content: space-between; }
|
|
293
|
+
.uhcp-sos-timeline div span { color: var(--uhcp-emerald); font-size: .75rem; font-weight: 900; text-transform: uppercase; }
|
|
294
|
+
.uhcp-sos-timeline time, .uhcp-sos-timeline small { color: var(--uhcp-muted); font-size: .8rem; }
|
|
295
|
+
.uhcp-sos-timeline p { line-height: 1.55; }
|
|
296
|
+
@media (max-width: 620px) {
|
|
297
|
+
.uhcp-sos-overview-grid { grid-template-columns: 1fr; }
|
|
298
|
+
.uhcp-sos-panel > header { display: grid; }
|
|
299
|
+
}
|
package/package.json
CHANGED