uhc-portal-components-react 0.1.10 → 0.1.14

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,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.14
4
+
5
+ - Add a brand-neutral, three-step identity-evidence workspace that keeps the
6
+ selected subject separate from the controller's reusable identifiers.
7
+ - Model self upload, authorized organization-worker upload and professional
8
+ attestation as independent host-confirmed capabilities; uploading evidence
9
+ never marks its signature or identity as verified in browser state.
10
+
11
+ ## 0.1.13
12
+
13
+ - Render versioned legal Markdown as semantic headings, paragraphs, lists,
14
+ links and inline code instead of exposing source markers.
15
+
16
+ ## 0.1.12
17
+
18
+ - Add a brand-neutral full-page account settings surface for security actions,
19
+ theme, text size, language and legal links, with a product-extension slot for
20
+ jurisdiction and data-use controls.
21
+
22
+ ## Unreleased
23
+
24
+ - Correct the reuse contract: the package currently provides presentation
25
+ primitives, not the full UHC Personal route journeys. Document the extraction
26
+ required before PetChain can reuse login, protected profiles, individuals,
27
+ members/licences, clinical data and permissions without cloning the app.
28
+ - Add a brand-neutral protected-index and emergency-copy panel. Hosts supply
29
+ backend-confirmed records; protected storage, individual authorship and
30
+ emergency availability remain separate states, and professional release is
31
+ never inferred by the browser.
32
+
3
33
  ## 0.1.10 - 2026-08-03
4
34
 
5
35
  - Add the reusable organization workforce administration surface with total,
package/README.md CHANGED
@@ -66,6 +66,11 @@ The package deliberately exposes modules instead of a cloned application:
66
66
  employee lifecycle and invitation-status UI. Invitations remain host-owned
67
67
  application records; the component does not reserve licenses or create GW
68
68
  employees.
69
+ - `IdentityEvidenceWorkspace`: plain-language subject, controller-identifier
70
+ and evidence steps. The host supplies authorized subjects, identifiers and
71
+ exact capabilities for self upload, organization-worker upload or
72
+ professional attestation; ICA or another trusted backend verifies the
73
+ signature after upload.
69
74
 
70
75
  Clinical label semantics stay in `gdc-common-utils-ts` / the SDK reader. Hosts
71
76
  pass human titles to `ClinicalSectionSummary`: local `CodeableConcept.text`
@@ -93,8 +98,13 @@ token immediately; otherwise it records a `Communication` request for the
93
98
  individual controller to review. The GW remains authoritative for the exact
94
99
  intersection of requested and consented sections.
95
100
 
96
- UHC Personal and VetChain Personal must compose the same personal route
97
- modules. UHC Professional and VetChain Professional must compose the same
98
- professional route modules. A brand adapter may change only tokens, logo,
99
- product names, descriptions and route destinations; it must not fork flow,
100
- profile or authorization behavior.
101
+ UHC Personal and PetChain Personal must ultimately compose the same reusable
102
+ personal flow modules. UHC Professional and VetChain Professional must do the
103
+ same for professional flows. This package does **not yet expose those complete
104
+ route modules**: login, versioned terms, profile protection, individual/member
105
+ management, licences, clinical data, care and permissions still live in the
106
+ UHC host application and must be extracted behind host-owned adapters. A brand
107
+ adapter may change tokens, logo, product vocabulary, animal-specific forms and
108
+ route destinations; it must not fork shared flow or authorization behavior.
109
+
110
+ See [`docs/PERSONAL_FLOW_EXTRACTION.md`](docs/PERSONAL_FLOW_EXTRACTION.md).
@@ -0,0 +1,60 @@
1
+ export type IdentityEvidenceStatus = 'verified' | 'pending' | 'rejected';
2
+ export type IdentityEvidenceUploadMode = 'self' | 'organization-worker' | 'professional-attestation';
3
+ export type IdentityEvidenceSubject = Readonly<{
4
+ id: string;
5
+ displayName: string;
6
+ kind: 'person' | 'animal';
7
+ }>;
8
+ export type IdentityEvidenceIdentifier = Readonly<{
9
+ id: string;
10
+ typeLabel: string;
11
+ jurisdictionLabel: string;
12
+ displayValue: string;
13
+ status: IdentityEvidenceStatus;
14
+ }>;
15
+ export type IdentityEvidenceCapabilities = Readonly<{
16
+ editSubject: boolean;
17
+ addIdentifier: boolean;
18
+ selfUpload: boolean;
19
+ organizationUpload: boolean;
20
+ professionalAttestation: boolean;
21
+ }>;
22
+ export type IdentityEvidenceWorkspaceCopy = Readonly<{
23
+ title: string;
24
+ intro: string;
25
+ selectedSubject: string;
26
+ subjectStepTitle: string;
27
+ subjectStepBody: string;
28
+ editSubject: string;
29
+ controllerStepTitle: string;
30
+ controllerStepBody: string;
31
+ noIdentifiers: string;
32
+ addIdentifier: string;
33
+ evidenceStepTitle: string;
34
+ evidenceStepBody: string;
35
+ selfUpload: string;
36
+ selfUploadHelp: string;
37
+ organizationUpload: string;
38
+ organizationUploadHelp: string;
39
+ professionalAttestation: string;
40
+ statusLabels: Readonly<Record<IdentityEvidenceStatus, string>>;
41
+ }>;
42
+ /**
43
+ * Brand-neutral identity-evidence journey for one backend-authorized subject.
44
+ *
45
+ * The host owns subject resolution, persistence and capabilities. This view
46
+ * keeps the managed subject separate from the controller's identifiers and
47
+ * keeps uploader, signer and verifier roles independent. Selecting an upload
48
+ * route never changes evidence status in browser state.
49
+ */
50
+ export declare function IdentityEvidenceWorkspace({ copy, subject, identifiers, capabilities, onEditSubject, onAddIdentifier, onSelectIdentifier, onUploadEvidence, }: Readonly<{
51
+ copy: IdentityEvidenceWorkspaceCopy;
52
+ subject: IdentityEvidenceSubject;
53
+ identifiers: readonly IdentityEvidenceIdentifier[];
54
+ capabilities: IdentityEvidenceCapabilities;
55
+ onEditSubject?: () => void;
56
+ onAddIdentifier?: () => void;
57
+ onSelectIdentifier?: (identifierId: string) => void;
58
+ onUploadEvidence?: (mode: IdentityEvidenceUploadMode) => void;
59
+ }>): import("react").JSX.Element;
60
+ //# sourceMappingURL=IdentityEvidenceWorkspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdentityEvidenceWorkspace.d.ts","sourceRoot":"","sources":["../src/IdentityEvidenceWorkspace.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;AAExE,MAAM,MAAM,0BAA0B,GAClC,MAAM,GACN,qBAAqB,GACrB,0BAA0B,CAAA;AAE9B,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAC1B,CAAC,CAAA;AAEF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,sBAAsB,CAAA;CAC/B,CAAC,CAAA;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,WAAW,EAAE,OAAO,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;IACnB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,uBAAuB,EAAE,OAAO,CAAA;CACjC,CAAC,CAAA;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,CAAA;CAC/D,CAAC,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,OAAO,EACP,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,EAAE,QAAQ,CAAC;IACV,IAAI,EAAE,6BAA6B,CAAA;IACnC,OAAO,EAAE,uBAAuB,CAAA;IAChC,WAAW,EAAE,SAAS,0BAA0B,EAAE,CAAA;IAClD,YAAY,EAAE,4BAA4B,CAAA;IAC1C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IACnD,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAA;CAC9D,CAAC,+BAkGD"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Brand-neutral identity-evidence journey for one backend-authorized subject.
4
+ *
5
+ * The host owns subject resolution, persistence and capabilities. This view
6
+ * keeps the managed subject separate from the controller's identifiers and
7
+ * keeps uploader, signer and verifier roles independent. Selecting an upload
8
+ * route never changes evidence status in browser state.
9
+ */
10
+ export function IdentityEvidenceWorkspace({ copy, subject, identifiers, capabilities, onEditSubject, onAddIdentifier, onSelectIdentifier, onUploadEvidence, }) {
11
+ return (_jsxs("section", { className: "uhcp-identity-workspace", "aria-labelledby": "identity-evidence-workspace-title", children: [_jsxs("header", { className: "uhcp-identity-workspace-heading", children: [_jsx("h2", { id: "identity-evidence-workspace-title", children: copy.title }), _jsx("p", { children: copy.intro })] }), _jsxs("ol", { className: "uhcp-identity-steps", children: [_jsxs("li", { className: "uhcp-identity-step", children: [_jsx(StepNumber, { value: 1 }), _jsxs("div", { className: "uhcp-identity-step-content", children: [_jsx("h3", { children: copy.subjectStepTitle }), _jsx("p", { children: copy.subjectStepBody }), _jsxs("div", { className: "uhcp-identity-subject", children: [_jsx("span", { children: copy.selectedSubject }), _jsx("strong", { children: subject.displayName })] }), capabilities.editSubject && onEditSubject ? (_jsx("button", { type: "button", className: "uhcp-identity-secondary-action", onClick: onEditSubject, children: copy.editSubject })) : null] })] }), _jsxs("li", { className: "uhcp-identity-step", children: [_jsx(StepNumber, { value: 2 }), _jsxs("div", { className: "uhcp-identity-step-content", children: [_jsx("h3", { children: copy.controllerStepTitle }), _jsx("p", { children: copy.controllerStepBody }), identifiers.length === 0 ? (_jsx("p", { className: "uhcp-identity-empty", children: copy.noIdentifiers })) : (_jsx("ul", { className: "uhcp-identity-identifiers", children: identifiers.map((identifier) => (_jsx("li", { children: _jsxs("button", { type: "button", onClick: () => onSelectIdentifier?.(identifier.id), disabled: !onSelectIdentifier, "aria-label": `${identifier.typeLabel}, ${identifier.jurisdictionLabel}, ${identifier.displayValue}`, children: [_jsxs("span", { children: [_jsx("strong", { children: identifier.typeLabel }), _jsxs("small", { children: [identifier.jurisdictionLabel, " \u00B7 ", identifier.displayValue] })] }), _jsx("span", { className: `uhcp-identity-status uhcp-identity-status-${identifier.status}`, children: copy.statusLabels[identifier.status] })] }) }, identifier.id))) })), capabilities.addIdentifier && onAddIdentifier ? (_jsx("button", { type: "button", className: "uhcp-identity-secondary-action", onClick: onAddIdentifier, children: copy.addIdentifier })) : null] })] }), _jsxs("li", { className: "uhcp-identity-step", children: [_jsx(StepNumber, { value: 3 }), _jsxs("div", { className: "uhcp-identity-step-content", children: [_jsx("h3", { children: copy.evidenceStepTitle }), _jsx("p", { children: copy.evidenceStepBody }), _jsxs("div", { className: "uhcp-identity-upload-actions", children: [capabilities.selfUpload ? (_jsx(UploadAction, { title: copy.selfUpload, help: copy.selfUploadHelp, onClick: () => onUploadEvidence?.('self'), disabled: !onUploadEvidence })) : null, capabilities.organizationUpload ? (_jsx(UploadAction, { title: copy.organizationUpload, help: copy.organizationUploadHelp, onClick: () => onUploadEvidence?.('organization-worker'), disabled: !onUploadEvidence })) : null, capabilities.professionalAttestation ? (_jsx(UploadAction, { title: copy.professionalAttestation, onClick: () => onUploadEvidence?.('professional-attestation'), disabled: !onUploadEvidence })) : null] })] })] })] })] }));
12
+ }
13
+ function StepNumber({ value }) {
14
+ return _jsx("span", { className: "uhcp-identity-step-number", "aria-hidden": "true", children: value });
15
+ }
16
+ function UploadAction({ title, help, onClick, disabled, }) {
17
+ return (_jsxs("button", { type: "button", className: "uhcp-identity-upload-action", "aria-label": title, onClick: onClick, disabled: disabled, children: [_jsx("strong", { children: title }), help ? _jsx("small", { children: help }) : null] }));
18
+ }
@@ -0,0 +1,6 @@
1
+ import { type JSX } from 'react';
2
+ /** Renders the deliberately small Markdown subset used by versioned legal notices. */
3
+ export declare function LegalMarkdownDocument({ source }: Readonly<{
4
+ source: string;
5
+ }>): JSX.Element;
6
+ //# sourceMappingURL=LegalMarkdownDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LegalMarkdownDocument.d.ts","sourceRoot":"","sources":["../src/LegalMarkdownDocument.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAkB,MAAM,OAAO,CAAA;AAa1D,sFAAsF;AACtF,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,eAmC7E"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ function inlineMarkdown(value) {
4
+ const parts = value.split(/(`[^`]+`|\[[^\]]+\]\(https?:\/\/[^)]+\)|\*\*[^*]+\*\*)/g).filter(Boolean);
5
+ return parts.map((part, index) => {
6
+ if (part.startsWith('`') && part.endsWith('`'))
7
+ return _jsx("code", { children: part.slice(1, -1) }, index);
8
+ const link = part.match(/^\[([^\]]+)\]\((https?:\/\/[^)]+)\)$/);
9
+ if (link)
10
+ return _jsx("a", { href: link[2], rel: "noreferrer", children: link[1] }, index);
11
+ if (part.startsWith('**') && part.endsWith('**'))
12
+ return _jsx("strong", { children: part.slice(2, -2) }, index);
13
+ return _jsx(Fragment, { children: part }, index);
14
+ });
15
+ }
16
+ /** Renders the deliberately small Markdown subset used by versioned legal notices. */
17
+ export function LegalMarkdownDocument({ source }) {
18
+ const lines = source.replace(/\r\n/g, '\n').split('\n');
19
+ const blocks = [];
20
+ let index = 0;
21
+ while (index < lines.length) {
22
+ const line = lines[index].trim();
23
+ if (!line) {
24
+ index += 1;
25
+ continue;
26
+ }
27
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
28
+ if (heading) {
29
+ const level = Math.min(6, heading[1].length + 1);
30
+ const Heading = `h${level}`;
31
+ blocks.push(_jsx(Heading, { children: inlineMarkdown(heading[2]) }, `h-${index}`));
32
+ index += 1;
33
+ continue;
34
+ }
35
+ if (/^-\s+/.test(line)) {
36
+ const items = [];
37
+ while (index < lines.length && /^-\s+/.test(lines[index].trim())) {
38
+ items.push(_jsx("li", { children: inlineMarkdown(lines[index].trim().replace(/^-\s+/, '')) }, index));
39
+ index += 1;
40
+ }
41
+ blocks.push(_jsx("ul", { children: items }, `ul-${index}`));
42
+ continue;
43
+ }
44
+ const paragraph = [line];
45
+ index += 1;
46
+ while (index < lines.length) {
47
+ const next = lines[index].trim();
48
+ if (!next || /^(#{1,6})\s+/.test(next) || /^-\s+/.test(next))
49
+ break;
50
+ paragraph.push(next);
51
+ index += 1;
52
+ }
53
+ blocks.push(_jsx("p", { children: inlineMarkdown(paragraph.join(' ')) }, `p-${index}`));
54
+ }
55
+ return _jsx("div", { className: "uhcp-legal-document", children: blocks });
56
+ }
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type PortalLanguageOption, type PortalMenuAction, type PortalUiDensity, type PortalUiTheme } from './contracts.js';
3
+ export type PortalSettingsCopy = Readonly<{
4
+ title: string;
5
+ description: string;
6
+ security: string;
7
+ appearance: string;
8
+ theme: string;
9
+ textSize: string;
10
+ language: string;
11
+ legal: string;
12
+ themes: Record<PortalUiTheme, string>;
13
+ densities: Record<PortalUiDensity, string>;
14
+ }>;
15
+ export type PortalSettingsLink = Readonly<{
16
+ id: string;
17
+ label: string;
18
+ href: string;
19
+ }>;
20
+ export type PortalSettingsPanelProps = Readonly<{
21
+ copy: PortalSettingsCopy;
22
+ storageNamespace: string;
23
+ languageOptions: readonly PortalLanguageOption[];
24
+ securityActions?: readonly PortalMenuAction[];
25
+ legalLinks: readonly PortalSettingsLink[];
26
+ children?: ReactNode;
27
+ }>;
28
+ /** Shared settings surface. Product-specific privacy controls enter as children. */
29
+ export declare function PortalSettingsPanel({ copy, storageNamespace, languageOptions, securityActions, legalLinks, children, }: PortalSettingsPanelProps): import("react").JSX.Element;
30
+ //# sourceMappingURL=PortalSettingsPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortalSettingsPanel.d.ts","sourceRoot":"","sources":["../src/PortalSettingsPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3D,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAGvB,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACrC,SAAS,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;CAC3C,CAAC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEtF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,IAAI,EAAE,kBAAkB,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAChD,eAAe,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC7C,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACzC,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAC,CAAA;AAEF,oFAAoF;AACpF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,gBAAgB,EAChB,eAAe,EACf,eAAoB,EACpB,UAAU,EACV,QAAQ,GACT,EAAE,wBAAwB,+BAwC1B"}
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import { portalUiDensities, portalUiThemes, } from './contracts.js';
5
+ import { resolvePortalUiDensity, resolvePortalUiTheme, resolveRenderedPortalTheme } from './preferences.js';
6
+ /** Shared settings surface. Product-specific privacy controls enter as children. */
7
+ export function PortalSettingsPanel({ copy, storageNamespace, languageOptions, securityActions = [], legalLinks, children, }) {
8
+ const [theme, setTheme] = useState('system');
9
+ const [density, setDensity] = useState('medium');
10
+ const themeKey = `${storageNamespace}.theme`;
11
+ const densityKey = `${storageNamespace}.density`;
12
+ useEffect(() => {
13
+ setTheme(resolvePortalUiTheme(localStorage.getItem(themeKey)));
14
+ setDensity(resolvePortalUiDensity(localStorage.getItem(densityKey)));
15
+ }, [densityKey, themeKey]);
16
+ function changeTheme(value) {
17
+ setTheme(value);
18
+ localStorage.setItem(themeKey, value);
19
+ const rendered = resolveRenderedPortalTheme(value, matchMedia('(prefers-color-scheme: dark)').matches);
20
+ document.documentElement.dataset.theme = rendered;
21
+ document.documentElement.style.colorScheme = rendered;
22
+ }
23
+ function changeDensity(value) {
24
+ setDensity(value);
25
+ localStorage.setItem(densityKey, value);
26
+ document.documentElement.dataset.density = value;
27
+ }
28
+ return (_jsxs("main", { className: "uhcp-settings", children: [_jsxs("header", { children: [_jsx("h1", { children: copy.title }), _jsx("p", { children: copy.description })] }), _jsxs("section", { children: [_jsx("h2", { children: copy.security }), _jsx("div", { className: "uhcp-settings-links", children: securityActions.map((action) => action.href
29
+ ? _jsx("a", { href: action.href, children: action.label }, action.id)
30
+ : _jsx("button", { type: "button", onClick: () => void action.onSelect?.(), children: action.label }, action.id)) })] }), children, _jsxs("section", { children: [_jsx("h2", { children: copy.appearance }), _jsxs("fieldset", { children: [_jsx("legend", { children: copy.theme }), portalUiThemes.map((value) => _jsx("button", { type: "button", "aria-pressed": theme === value, onClick: () => changeTheme(value), children: copy.themes[value] }, value))] }), _jsxs("fieldset", { children: [_jsx("legend", { children: copy.textSize }), portalUiDensities.map((value) => _jsx("button", { type: "button", "aria-pressed": density === value, onClick: () => changeDensity(value), children: copy.densities[value] }, value))] })] }), _jsxs("section", { children: [_jsx("h2", { children: copy.language }), _jsx("div", { className: "uhcp-settings-links", children: languageOptions.map((option) => _jsx("a", { href: option.href, hrefLang: option.id, "aria-current": option.current ? 'page' : undefined, children: option.label }, option.id)) })] }), _jsxs("section", { children: [_jsx("h2", { children: copy.legal }), _jsx("div", { className: "uhcp-settings-links", children: legalLinks.map((link) => _jsx("a", { href: link.href, children: link.label }, link.id)) })] })] }));
31
+ }
@@ -0,0 +1,32 @@
1
+ export type ProtectedIndexEmergencyItem = Readonly<{
2
+ identifier: string;
3
+ title: string;
4
+ categoryLabel: string;
5
+ sourceLabel?: string;
6
+ creatorLabel?: string;
7
+ /** True only after the backend confirms emergency-copy consent and policy. */
8
+ emergencyAvailable: boolean;
9
+ }>;
10
+ export type ProtectedIndexEmergencyCopy = Readonly<{
11
+ title: string;
12
+ body: string;
13
+ securePinTitle: string;
14
+ securePinBody: string;
15
+ emergencyPinTitle: string;
16
+ emergencyPinBody: string;
17
+ emergencyOnly: string;
18
+ protectedOnly: string;
19
+ empty: string;
20
+ sourcePrefix: string;
21
+ creatorPrefix: string;
22
+ }>;
23
+ /**
24
+ * Presents backend-confirmed protected-index copies and their independent
25
+ * emergency availability. The component never infers emergency consent from
26
+ * individual authorship, storage, a selected card or browser state.
27
+ */
28
+ export declare function ProtectedIndexEmergencyData({ copy, items, }: Readonly<{
29
+ copy: ProtectedIndexEmergencyCopy;
30
+ items: readonly ProtectedIndexEmergencyItem[];
31
+ }>): import("react").JSX.Element;
32
+ //# sourceMappingURL=ProtectedIndexEmergencyData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProtectedIndexEmergencyData.d.ts","sourceRoot":"","sources":["../src/ProtectedIndexEmergencyData.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,kBAAkB,EAAE,OAAO,CAAA;CAC5B,CAAC,CAAA;AAEF,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,EAC1C,IAAI,EACJ,KAAK,GACN,EAAE,QAAQ,CAAC;IACV,IAAI,EAAE,2BAA2B,CAAA;IACjC,KAAK,EAAE,SAAS,2BAA2B,EAAE,CAAA;CAC9C,CAAC,+BAkCD"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Presents backend-confirmed protected-index copies and their independent
4
+ * emergency availability. The component never infers emergency consent from
5
+ * individual authorship, storage, a selected card or browser state.
6
+ */
7
+ export function ProtectedIndexEmergencyData({ copy, items, }) {
8
+ return (_jsxs("section", { className: "uhcp-emergency-copy", "aria-labelledby": "protected-index-emergency-title", children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { id: "protected-index-emergency-title", children: copy.title }), _jsx("p", { children: copy.body })] }), _jsxs("div", { className: "uhcp-emergency-copy-legend", "aria-label": copy.title, children: [_jsx(Pin, { kind: "secure", label: copy.securePinTitle, help: copy.securePinBody }), _jsx(Pin, { kind: "emergency", label: copy.emergencyPinTitle, help: copy.emergencyPinBody })] })] }), items.length === 0 ? _jsx("p", { className: "uhcp-emergency-copy-empty", children: copy.empty }) : (_jsx("ul", { className: "uhcp-emergency-copy-list", children: items.map((item) => (_jsxs("li", { children: [_jsxs("div", { className: "uhcp-emergency-copy-item-heading", children: [_jsx("span", { className: "uhcp-emergency-copy-category", children: item.categoryLabel }), _jsxs("span", { className: "uhcp-emergency-copy-pins", children: [_jsx(Pin, { kind: "secure", label: copy.securePinTitle, help: copy.securePinBody, compact: true }), item.emergencyAvailable ? _jsx(Pin, { kind: "emergency", label: copy.emergencyPinTitle, help: copy.emergencyPinBody, compact: true }) : null] })] }), _jsx("strong", { children: item.title }), _jsx("small", { children: item.emergencyAvailable ? copy.emergencyOnly : copy.protectedOnly }), item.sourceLabel ? _jsxs("span", { children: [copy.sourcePrefix, ": ", item.sourceLabel] }) : null, item.creatorLabel ? _jsxs("span", { children: [copy.creatorPrefix, ": ", item.creatorLabel] }) : null] }, item.identifier))) }))] }));
9
+ }
10
+ function Pin({ kind, label, help, compact = false, }) {
11
+ return (_jsxs("span", { className: `uhcp-emergency-copy-pin uhcp-emergency-copy-pin-${kind}`, role: "img", "aria-label": label, title: help, children: [_jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M12 17v5m-5-9 2-2V5L7 3h10l-2 2v6l2 2H7Z" }) }), compact ? null : _jsx("span", { children: label })] }));
12
+ }
@@ -1,4 +1,5 @@
1
- export type SoschainWorkspaceArea = 'overview' | 'places' | 'products' | 'communications';
1
+ import { type ProtectedIndexEmergencyCopy, type ProtectedIndexEmergencyItem } from './ProtectedIndexEmergencyData.js';
2
+ export type SoschainWorkspaceArea = 'overview' | 'emergency-data' | 'places' | 'products' | 'communications';
2
3
  export type SoschainCardSummary = Readonly<{
3
4
  identifier: string;
4
5
  displayName: string;
@@ -33,6 +34,9 @@ export type SoschainWorkspaceCopy = Readonly<{
33
34
  areas: Record<SoschainWorkspaceArea, string>;
34
35
  overviewTitle: string;
35
36
  overviewBody: string;
37
+ emergencyDataTitle: string;
38
+ emergencyDataBody: string;
39
+ emergencyData: ProtectedIndexEmergencyCopy;
36
40
  placesTitle: string;
37
41
  placesBody: string;
38
42
  productsTitle: string;
@@ -56,6 +60,7 @@ export type SoschainWorkspaceProps = Readonly<{
56
60
  places: readonly SoschainAssociationSummary[];
57
61
  products: readonly SoschainAssociationSummary[];
58
62
  communications: readonly SoschainCommunicationSummary[];
63
+ emergencyData?: readonly ProtectedIndexEmergencyItem[];
59
64
  canManageAssociations: boolean;
60
65
  addPlaceHref?: string;
61
66
  addProductHref?: string;
@@ -68,5 +73,5 @@ export type SoschainWorkspaceProps = Readonly<{
68
73
  * communications. This presentational component never derives authority from
69
74
  * a selected role, card, tab or browser-controlled value.
70
75
  */
71
- export declare function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, canManageAssociations, addPlaceHref, addProductHref, importCardHref, }: SoschainWorkspaceProps): import("react").JSX.Element;
76
+ export declare function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, emergencyData, canManageAssociations, addPlaceHref, addProductHref, importCardHref, }: SoschainWorkspaceProps): import("react").JSX.Element;
72
77
  //# sourceMappingURL=SoschainWorkspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SoschainWorkspace.d.ts","sourceRoot":"","sources":["../src/SoschainWorkspace.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SoschainWorkspace.d.ts","sourceRoot":"","sources":["../src/SoschainWorkspace.tsx"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ProtectedIndexEmergencyData } from './ProtectedIndexEmergencyData.js';
2
3
  function AssociationList({ items, empty }) {
3
4
  if (items.length === 0)
4
5
  return _jsx("p", { className: "uhcp-sos-empty", children: empty });
@@ -11,7 +12,7 @@ function AssociationList({ items, empty }) {
11
12
  * communications. This presentational component never derives authority from
12
13
  * a selected role, card, tab or browser-controlled value.
13
14
  */
14
- export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, canManageAssociations, addPlaceHref, addProductHref, importCardHref, }) {
15
+ export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, emergencyData = [], canManageAssociations, addPlaceHref, addProductHref, importCardHref, }) {
15
16
  if (!selectedCard) {
16
17
  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 }), importCardHref && copy.importCard ? _jsxs("a", { href: importCardHref, children: [copy.importCard, " \u2192"] }) : null] }), 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
  }
@@ -21,13 +22,17 @@ export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaH
21
22
  ? copy.productsTitle
22
23
  : activeArea === 'communications'
23
24
  ? copy.communicationsTitle
24
- : copy.overviewTitle;
25
+ : activeArea === 'emergency-data'
26
+ ? copy.emergencyDataTitle
27
+ : copy.overviewTitle;
25
28
  const body = activeArea === 'places'
26
29
  ? copy.placesBody
27
30
  : activeArea === 'products'
28
31
  ? copy.productsBody
29
32
  : activeArea === 'communications'
30
33
  ? 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))) })))] })] }));
34
+ : activeArea === 'emergency-data'
35
+ ? copy.emergencyDataBody
36
+ : copy.overviewBody;
37
+ 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' && activeArea !== 'emergency-data' && (_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 === 'emergency-data' && _jsx(ProtectedIndexEmergencyData, { copy: copy.emergencyData, items: emergencyData }), 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
38
  }
package/dist/index.d.ts CHANGED
@@ -11,4 +11,8 @@ export * from './InvitationAcceptancePanel.js';
11
11
  export * from './SubjectAccessRequestPanel.js';
12
12
  export * from './SoschainWorkspace.js';
13
13
  export * from './OrganizationWorkforceAdministration.js';
14
+ export * from './ProtectedIndexEmergencyData.js';
15
+ export * from './PortalSettingsPanel.js';
16
+ export * from './LegalMarkdownDocument.js';
17
+ export * from './IdentityEvidenceWorkspace.js';
14
18
  //# 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;AACrC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,0CAA0C,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;AACtC,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA"}
package/dist/index.js CHANGED
@@ -11,3 +11,7 @@ export * from './InvitationAcceptancePanel.js';
11
11
  export * from './SubjectAccessRequestPanel.js';
12
12
  export * from './SoschainWorkspace.js';
13
13
  export * from './OrganizationWorkforceAdministration.js';
14
+ export * from './ProtectedIndexEmergencyData.js';
15
+ export * from './PortalSettingsPanel.js';
16
+ export * from './LegalMarkdownDocument.js';
17
+ export * from './IdentityEvidenceWorkspace.js';
package/dist/styles.css CHANGED
@@ -172,6 +172,115 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
172
172
  padding: 1.25rem;
173
173
  }
174
174
 
175
+ .uhcp-identity-workspace {
176
+ background: var(--uhcp-panel);
177
+ border: 1px solid var(--uhcp-border);
178
+ border-radius: 2rem;
179
+ color: var(--uhcp-text);
180
+ padding: clamp(1.25rem, 3vw, 2rem);
181
+ }
182
+ .uhcp-identity-workspace h2,
183
+ .uhcp-identity-workspace h3,
184
+ .uhcp-identity-workspace p { margin: 0; }
185
+ .uhcp-identity-workspace-heading { max-width: 46rem; }
186
+ .uhcp-identity-workspace-heading h2 { font-size: clamp(1.5rem, 3vw, 2rem); }
187
+ .uhcp-identity-workspace-heading p,
188
+ .uhcp-identity-step-content > p { color: var(--uhcp-muted); line-height: 1.6; margin-top: .5rem; }
189
+ .uhcp-identity-steps { display: grid; gap: 0; list-style: none; margin: 1.75rem 0 0; padding: 0; }
190
+ .uhcp-identity-step { display: grid; gap: 1rem; grid-template-columns: 2.25rem minmax(0, 1fr); position: relative; }
191
+ .uhcp-identity-step:not(:last-child)::before {
192
+ background: var(--uhcp-border);
193
+ content: '';
194
+ height: calc(100% - 2.25rem);
195
+ left: 1.09rem;
196
+ position: absolute;
197
+ top: 2.25rem;
198
+ width: 2px;
199
+ }
200
+ .uhcp-identity-step-number {
201
+ align-items: center;
202
+ background: var(--uhcp-surface-accent);
203
+ border: 1px solid color-mix(in srgb, var(--uhcp-turquoise) 42%, var(--uhcp-border));
204
+ border-radius: 999px;
205
+ color: var(--uhcp-turquoise);
206
+ display: flex;
207
+ font-weight: 900;
208
+ height: 2.25rem;
209
+ justify-content: center;
210
+ position: relative;
211
+ width: 2.25rem;
212
+ z-index: 1;
213
+ }
214
+ .uhcp-identity-step-content { padding: .25rem 0 1.75rem; }
215
+ .uhcp-identity-step:last-child .uhcp-identity-step-content { padding-bottom: 0; }
216
+ .uhcp-identity-subject {
217
+ background: var(--uhcp-panel-muted);
218
+ border-radius: 1rem;
219
+ display: grid;
220
+ gap: .2rem;
221
+ margin-top: 1rem;
222
+ padding: .9rem 1rem;
223
+ }
224
+ .uhcp-identity-subject span,
225
+ .uhcp-identity-subject small { color: var(--uhcp-muted); font-size: .78rem; }
226
+ .uhcp-identity-empty { color: var(--uhcp-muted); margin-top: 1rem !important; }
227
+ .uhcp-identity-identifiers { display: grid; gap: .65rem; list-style: none; margin: 1rem 0 0; padding: 0; }
228
+ .uhcp-identity-identifiers button {
229
+ align-items: center;
230
+ background: var(--uhcp-panel-muted);
231
+ border: 1px solid transparent;
232
+ border-radius: 1rem;
233
+ color: var(--uhcp-text);
234
+ display: flex;
235
+ font: inherit;
236
+ gap: 1rem;
237
+ justify-content: space-between;
238
+ padding: .9rem 1rem;
239
+ text-align: left;
240
+ width: 100%;
241
+ }
242
+ .uhcp-identity-identifiers button:not(:disabled) { cursor: pointer; }
243
+ .uhcp-identity-identifiers button:not(:disabled):hover { border-color: var(--uhcp-turquoise); }
244
+ .uhcp-identity-identifiers button:disabled { opacity: 1; }
245
+ .uhcp-identity-identifiers button > span:first-child { display: grid; gap: .25rem; }
246
+ .uhcp-identity-identifiers small { color: var(--uhcp-muted); }
247
+ .uhcp-identity-status { border-radius: 999px; font-size: .72rem; font-weight: 800; padding: .35rem .6rem; }
248
+ .uhcp-identity-status-verified { background: color-mix(in srgb, var(--uhcp-emerald) 15%, transparent); color: var(--uhcp-emerald); }
249
+ .uhcp-identity-status-pending { background: color-mix(in srgb, var(--uhcp-attention) 18%, transparent); color: color-mix(in srgb, var(--uhcp-attention) 72%, var(--uhcp-text)); }
250
+ .uhcp-identity-status-rejected { background: color-mix(in srgb, #c64747 14%, transparent); color: #c64747; }
251
+ .uhcp-identity-secondary-action {
252
+ background: transparent;
253
+ border: 1px solid var(--uhcp-border);
254
+ border-radius: .85rem;
255
+ color: var(--uhcp-text);
256
+ cursor: pointer;
257
+ font: inherit;
258
+ font-weight: 800;
259
+ margin-top: 1rem;
260
+ min-height: 2.75rem;
261
+ padding: .65rem 1rem;
262
+ }
263
+ .uhcp-identity-secondary-action:hover { border-color: var(--uhcp-turquoise); color: var(--uhcp-turquoise); }
264
+ .uhcp-identity-upload-actions { display: grid; gap: .75rem; margin-top: 1rem; }
265
+ .uhcp-identity-upload-action {
266
+ background: var(--uhcp-panel-muted);
267
+ border: 1px solid var(--uhcp-border);
268
+ border-radius: 1rem;
269
+ color: var(--uhcp-text);
270
+ cursor: pointer;
271
+ display: grid;
272
+ font: inherit;
273
+ gap: .25rem;
274
+ padding: 1rem;
275
+ text-align: left;
276
+ }
277
+ .uhcp-identity-upload-action:hover { border-color: var(--uhcp-turquoise); }
278
+ .uhcp-identity-upload-action:disabled { cursor: not-allowed; opacity: .6; }
279
+ .uhcp-identity-upload-action small { color: var(--uhcp-muted); line-height: 1.45; }
280
+ @media (min-width: 760px) {
281
+ .uhcp-identity-upload-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
282
+ }
283
+
175
284
  .uhcp-clinical-summary {
176
285
  display: grid;
177
286
  gap: 1rem;
@@ -327,3 +436,22 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
327
436
  .uhcp-sos-overview-grid { grid-template-columns: 1fr; }
328
437
  .uhcp-sos-panel > header { display: grid; }
329
438
  }
439
+ .uhcp-emergency-copy { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: 2rem; color: var(--uhcp-text); padding: clamp(1.25rem, 4vw, 2.25rem); }
440
+ .uhcp-emergency-copy > header { align-items: flex-start; display: flex; gap: 1.5rem; justify-content: space-between; }
441
+ .uhcp-emergency-copy h2 { margin: 0 0 .45rem; }
442
+ .uhcp-emergency-copy header p { color: var(--uhcp-muted); line-height: 1.6; margin: 0; max-width: 48rem; }
443
+ .uhcp-emergency-copy-legend { display: grid; flex: 0 0 auto; gap: .5rem; }
444
+ .uhcp-emergency-copy-pin { align-items: center; border-radius: 999px; display: inline-flex; font-size: .75rem; font-weight: 850; gap: .35rem; padding: .35rem .55rem; }
445
+ .uhcp-emergency-copy-pin svg { fill: none; height: 1rem; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; width: 1rem; }
446
+ .uhcp-emergency-copy-pin-secure { background: color-mix(in srgb, var(--uhcp-turquoise) 16%, transparent); color: color-mix(in srgb, var(--uhcp-turquoise) 72%, var(--uhcp-text)); }
447
+ .uhcp-emergency-copy-pin-emergency { background: color-mix(in srgb, var(--uhcp-attention) 18%, transparent); color: color-mix(in srgb, var(--uhcp-attention) 78%, var(--uhcp-text)); }
448
+ .uhcp-emergency-copy-list { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); list-style: none; margin: 1.5rem 0 0; padding: 0; }
449
+ .uhcp-emergency-copy-list li { background: var(--uhcp-panel-muted); border: 1px solid var(--uhcp-border); border-radius: 1.25rem; display: grid; gap: .45rem; padding: 1rem; }
450
+ .uhcp-emergency-copy-item-heading { align-items: center; display: flex; gap: .6rem; justify-content: space-between; }
451
+ .uhcp-emergency-copy-category { color: var(--uhcp-emerald); font-size: .72rem; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; }
452
+ .uhcp-emergency-copy-pins { display: inline-flex; gap: .3rem; }
453
+ .uhcp-emergency-copy-list small, .uhcp-emergency-copy-list li > span, .uhcp-emergency-copy-empty { color: var(--uhcp-muted); }
454
+ .uhcp-emergency-copy-empty { margin: 1.5rem 0 0; }
455
+ @media (max-width: 620px) { .uhcp-emergency-copy > header { display: grid; } .uhcp-emergency-copy-legend { width: 100%; } }
456
+ .uhcp-settings{max-width:72rem;margin:0 auto;padding:2rem 1rem 4rem;display:grid;gap:1rem}.uhcp-settings>header,.uhcp-settings>section{background:var(--uhcp-surface,#fff);border:1px solid var(--uhcp-border,#dbe4ea);border-radius:1rem;padding:1.25rem}.uhcp-settings h1,.uhcp-settings h2{margin-top:0}.uhcp-settings fieldset{border:0;padding:0;margin:1rem 0;display:flex;gap:.5rem;flex-wrap:wrap}.uhcp-settings legend{width:100%;font-weight:700;margin-bottom:.5rem}.uhcp-settings button,.uhcp-settings-links a{border:1px solid var(--uhcp-border,#c9d7df);border-radius:.7rem;background:transparent;color:inherit;padding:.65rem .85rem;text-decoration:none}.uhcp-settings button[aria-pressed=true]{background:var(--uhcp-accent,#dceff7);border-color:var(--uhcp-accent-strong,#2489a7)}.uhcp-settings-links{display:flex;gap:.6rem;flex-wrap:wrap}
457
+ .uhcp-legal-document{color:var(--uhcp-text);line-height:1.7}.uhcp-legal-document h2{font-size:clamp(1.6rem,4vw,2.35rem);line-height:1.15;margin:0 0 1.25rem}.uhcp-legal-document h3{font-size:1.2rem;line-height:1.3;margin:2rem 0 .65rem}.uhcp-legal-document p{margin:0 0 1rem}.uhcp-legal-document ul{margin:0 0 1rem;padding-left:1.4rem}.uhcp-legal-document code{background:var(--uhcp-panel-muted);border-radius:.3rem;padding:.08rem .3rem}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.1.10",
3
+ "version": "0.1.14",
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",