uhc-portal-components-react 0.1.11 → 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,7 +1,30 @@
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
+
3
22
  ## Unreleased
4
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.
5
28
  - Add a brand-neutral protected-index and emergency-copy panel. Hosts supply
6
29
  backend-confirmed records; protected storage, individual authorship and
7
30
  emergency availability remain separate states, and professional release is
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
+ }
package/dist/index.d.ts CHANGED
@@ -12,4 +12,7 @@ export * from './SubjectAccessRequestPanel.js';
12
12
  export * from './SoschainWorkspace.js';
13
13
  export * from './OrganizationWorkforceAdministration.js';
14
14
  export * from './ProtectedIndexEmergencyData.js';
15
+ export * from './PortalSettingsPanel.js';
16
+ export * from './LegalMarkdownDocument.js';
17
+ export * from './IdentityEvidenceWorkspace.js';
15
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;AACxD,cAAc,kCAAkC,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
@@ -12,3 +12,6 @@ export * from './SubjectAccessRequestPanel.js';
12
12
  export * from './SoschainWorkspace.js';
13
13
  export * from './OrganizationWorkforceAdministration.js';
14
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;
@@ -344,3 +453,5 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
344
453
  .uhcp-emergency-copy-list small, .uhcp-emergency-copy-list li > span, .uhcp-emergency-copy-empty { color: var(--uhcp-muted); }
345
454
  .uhcp-emergency-copy-empty { margin: 1.5rem 0 0; }
346
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.11",
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",