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 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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Brand-neutral React portal shell, account preferences and protected-profile UI for UHC and VetChain portals.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",