uhc-portal-components-react 0.1.14 → 0.1.16

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.16
4
+
5
+ - Add `IdentityEvidenceDocumentPanel`, a brand-neutral signed-PDF upload and
6
+ verification surface used by PetChain, UHC UNID and SOSChain.
7
+ - Keep rendered status provider-owned: selecting or uploading a PDF never
8
+ changes pending evidence to verified in browser state.
9
+ - Add `IdentityEvidenceDeclarationForm` for governed legal-name, jurisdiction,
10
+ identifier and relationship options with one high-level preparation
11
+ callback and no session, certificate or gateway fields.
12
+
13
+ ## 0.1.15
14
+
15
+ - Add an accessible, animated `PortalLoadingSkeleton` that authenticated
16
+ portals render while provider data is pending, before any empty or stale
17
+ business state is eligible to appear.
18
+
3
19
  ## 0.1.14
4
20
 
5
21
  - Add a brand-neutral, three-step identity-evidence workspace that keeps the
package/README.md CHANGED
@@ -71,6 +71,13 @@ The package deliberately exposes modules instead of a cloned application:
71
71
  exact capabilities for self upload, organization-worker upload or
72
72
  professional attestation; ICA or another trusted backend verifies the
73
73
  signature after upload.
74
+ - `IdentityEvidenceDocumentPanel`: selects and uploads one signed PDF for the
75
+ chosen identity, requests trusted verification and renders only the status
76
+ returned by the provider facade. It contains no certificate or gateway
77
+ plumbing.
78
+ - `IdentityEvidenceDeclarationForm`: collects the business fields used to
79
+ prepare a declaration. Hosts supply governed options and one high-level
80
+ callback; the form never authors claims or handles certificates.
74
81
 
75
82
  Clinical label semantics stay in `gdc-common-utils-ts` / the SDK reader. Hosts
76
83
  pass human titles to `ClinicalSectionSummary`: local `CodeableConcept.text`
@@ -0,0 +1,43 @@
1
+ export type IdentityEvidenceOption = Readonly<{
2
+ value: string;
3
+ label: string;
4
+ }>;
5
+ export type IdentityEvidenceDeclarationInput = Readonly<{
6
+ fullLegalName: string;
7
+ countryCode: string;
8
+ regional: boolean;
9
+ subdivisionCode?: string;
10
+ identifierKind: string;
11
+ identifierValue: string;
12
+ controllerCapacity: string;
13
+ }>;
14
+ export type IdentityEvidenceDeclarationFormCopy = Readonly<{
15
+ title: string;
16
+ body: string;
17
+ fullLegalName: string;
18
+ country: string;
19
+ identifierKind: string;
20
+ identifierValue: string;
21
+ regional: string;
22
+ region: string;
23
+ capacity: string;
24
+ prepare: string;
25
+ preparing: string;
26
+ required: string;
27
+ }>;
28
+ /**
29
+ * Brand-neutral declaration form shared by UHC, PetChain and SOSChain.
30
+ *
31
+ * The host supplies governed options and one high-level preparation callback.
32
+ * This component neither authors resource claims nor handles certificates,
33
+ * sessions, gateway messages or verification decisions.
34
+ */
35
+ export declare function IdentityEvidenceDeclarationForm({ copy, countries, regions, identifierKinds, capacities, onPrepare, }: Readonly<{
36
+ copy: IdentityEvidenceDeclarationFormCopy;
37
+ countries: readonly IdentityEvidenceOption[];
38
+ regions: readonly IdentityEvidenceOption[];
39
+ identifierKinds: readonly IdentityEvidenceOption[];
40
+ capacities: readonly IdentityEvidenceOption[];
41
+ onPrepare: (input: IdentityEvidenceDeclarationInput) => Promise<void>;
42
+ }>): import("react").JSX.Element;
43
+ //# sourceMappingURL=IdentityEvidenceDeclarationForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdentityEvidenceDeclarationForm.d.ts","sourceRoot":"","sources":["../src/IdentityEvidenceDeclarationForm.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE/E,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAAC,CAAA;AAEF,MAAM,MAAM,mCAAmC,GAAG,QAAQ,CAAC;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,EAC9C,IAAI,EACJ,SAAS,EACT,OAAO,EACP,eAAe,EACf,UAAU,EACV,SAAS,GACV,EAAE,QAAQ,CAAC;IACV,IAAI,EAAE,mCAAmC,CAAA;IACzC,SAAS,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAC5C,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAC1C,eAAe,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAClD,UAAU,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAC7C,SAAS,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtE,CAAC,+BA+CD"}
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ /**
4
+ * Brand-neutral declaration form shared by UHC, PetChain and SOSChain.
5
+ *
6
+ * The host supplies governed options and one high-level preparation callback.
7
+ * This component neither authors resource claims nor handles certificates,
8
+ * sessions, gateway messages or verification decisions.
9
+ */
10
+ export function IdentityEvidenceDeclarationForm({ copy, countries, regions, identifierKinds, capacities, onPrepare, }) {
11
+ const [fullLegalName, setFullLegalName] = useState('');
12
+ const [identifierValue, setIdentifierValue] = useState('');
13
+ const [countryCode, setCountryCode] = useState(countries[0]?.value || '');
14
+ const [identifierKind, setIdentifierKind] = useState(identifierKinds[0]?.value || '');
15
+ const [controllerCapacity, setControllerCapacity] = useState(capacities[0]?.value || '');
16
+ const [regional, setRegional] = useState(false);
17
+ const [subdivisionCode, setSubdivisionCode] = useState('');
18
+ const [busy, setBusy] = useState(false);
19
+ const [error, setError] = useState('');
20
+ async function prepare() {
21
+ if (!fullLegalName.trim() || !identifierValue.trim() || !countryCode || !identifierKind || !controllerCapacity || (regional && !subdivisionCode)) {
22
+ setError(copy.required);
23
+ return;
24
+ }
25
+ setBusy(true);
26
+ setError('');
27
+ try {
28
+ await onPrepare({
29
+ fullLegalName: fullLegalName.trim(),
30
+ countryCode,
31
+ regional,
32
+ ...(regional ? { subdivisionCode } : {}),
33
+ identifierKind,
34
+ identifierValue: identifierValue.trim(),
35
+ controllerCapacity,
36
+ });
37
+ }
38
+ finally {
39
+ setBusy(false);
40
+ }
41
+ }
42
+ return _jsxs("section", { className: "uhcp-identity-declaration", "aria-labelledby": "identity-declaration-form-title", children: [_jsxs("header", { children: [_jsx("h3", { id: "identity-declaration-form-title", children: copy.title }), _jsx("p", { children: copy.body })] }), _jsxs("div", { className: "uhcp-identity-declaration-fields", children: [_jsxs("label", { children: [copy.fullLegalName, _jsx("input", { value: fullLegalName, onChange: (event) => setFullLegalName(event.target.value) })] }), _jsxs("label", { children: [copy.country, _jsx("select", { value: countryCode, onChange: (event) => setCountryCode(event.target.value), children: options(countries) })] }), _jsxs("label", { children: [copy.identifierKind, _jsx("select", { value: identifierKind, onChange: (event) => setIdentifierKind(event.target.value), children: options(identifierKinds) })] }), _jsxs("label", { children: [copy.identifierValue, _jsx("input", { value: identifierValue, onChange: (event) => setIdentifierValue(event.target.value) })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: regional, onChange: (event) => { setRegional(event.target.checked); if (!event.target.checked)
43
+ setSubdivisionCode(''); } }), copy.regional] }), _jsxs("label", { children: [copy.region, _jsxs("select", { value: subdivisionCode, disabled: !regional, onChange: (event) => setSubdivisionCode(event.target.value), children: [_jsx("option", { value: "" }), options(regions)] })] }), _jsxs("label", { children: [copy.capacity, _jsx("select", { value: controllerCapacity, onChange: (event) => setControllerCapacity(event.target.value), children: options(capacities) })] })] }), error ? _jsx("p", { role: "alert", children: error }) : null, _jsx("button", { type: "button", onClick: () => void prepare(), disabled: busy, children: busy ? copy.preparing : copy.prepare })] });
44
+ }
45
+ function options(values) {
46
+ return values.map((option) => _jsx("option", { value: option.value, children: option.label }, option.value));
47
+ }
@@ -0,0 +1,34 @@
1
+ import type { IdentityEvidenceIdentifier, IdentityEvidenceStatus } from './IdentityEvidenceWorkspace.js';
2
+ export type IdentityEvidenceDocument = Readonly<{
3
+ id: string;
4
+ fileName: string;
5
+ status: IdentityEvidenceStatus;
6
+ }>;
7
+ export type IdentityEvidenceDocumentPanelCopy = Readonly<{
8
+ title: string;
9
+ body: string;
10
+ choosePdf: string;
11
+ upload: string;
12
+ uploading: string;
13
+ verify: string;
14
+ verifying: string;
15
+ noDocument: string;
16
+ invalidPdf: string;
17
+ statusLabels: Readonly<Record<IdentityEvidenceStatus, string>>;
18
+ }>;
19
+ /**
20
+ * Reusable signed-document panel for UHC, PetChain and SOSChain.
21
+ *
22
+ * The component only chooses a PDF and calls the host's high-level methods.
23
+ * It never parses certificates, builds gateway claims or promotes a local
24
+ * digest to verified evidence. The rendered status always comes from provider
25
+ * readback through `documents`.
26
+ */
27
+ export declare function IdentityEvidenceDocumentPanel({ copy, selectedIdentifier, documents, onUpload, onVerifySignaturePdf, }: Readonly<{
28
+ copy: IdentityEvidenceDocumentPanelCopy;
29
+ selectedIdentifier?: IdentityEvidenceIdentifier;
30
+ documents: readonly IdentityEvidenceDocument[];
31
+ onUpload: (controllerIdentifierId: string, pdf: File) => Promise<void>;
32
+ onVerifySignaturePdf: (evidenceId: string) => Promise<void>;
33
+ }>): import("react").JSX.Element;
34
+ //# sourceMappingURL=IdentityEvidenceDocumentPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdentityEvidenceDocumentPanel.d.ts","sourceRoot":"","sources":["../src/IdentityEvidenceDocumentPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAExG,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,sBAAsB,CAAA;CAC/B,CAAC,CAAA;AAEF,MAAM,MAAM,iCAAiC,GAAG,QAAQ,CAAC;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,CAAA;CAC/D,CAAC,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,EAC5C,IAAI,EACJ,kBAAkB,EAClB,SAAS,EACT,QAAQ,EACR,oBAAoB,GACrB,EAAE,QAAQ,CAAC;IACV,IAAI,EAAE,iCAAiC,CAAA;IACvC,kBAAkB,CAAC,EAAE,0BAA0B,CAAA;IAC/C,SAAS,EAAE,SAAS,wBAAwB,EAAE,CAAA;IAC9C,QAAQ,EAAE,CAAC,sBAAsB,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACtE,oBAAoB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5D,CAAC,+BAgED"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ /**
4
+ * Reusable signed-document panel for UHC, PetChain and SOSChain.
5
+ *
6
+ * The component only chooses a PDF and calls the host's high-level methods.
7
+ * It never parses certificates, builds gateway claims or promotes a local
8
+ * digest to verified evidence. The rendered status always comes from provider
9
+ * readback through `documents`.
10
+ */
11
+ export function IdentityEvidenceDocumentPanel({ copy, selectedIdentifier, documents, onUpload, onVerifySignaturePdf, }) {
12
+ const [file, setFile] = useState();
13
+ const [busy, setBusy] = useState();
14
+ const [error, setError] = useState('');
15
+ async function upload() {
16
+ if (!selectedIdentifier || !file || file.type !== 'application/pdf') {
17
+ setError(copy.invalidPdf);
18
+ return;
19
+ }
20
+ setBusy('upload');
21
+ setError('');
22
+ try {
23
+ await onUpload(selectedIdentifier.id, file);
24
+ }
25
+ finally {
26
+ setBusy(undefined);
27
+ }
28
+ }
29
+ async function verify(evidenceId) {
30
+ setBusy('verify');
31
+ setError('');
32
+ try {
33
+ await onVerifySignaturePdf(evidenceId);
34
+ }
35
+ finally {
36
+ setBusy(undefined);
37
+ }
38
+ }
39
+ return (_jsxs("section", { className: "uhcp-identity-documents", "aria-labelledby": "identity-evidence-documents-title", children: [_jsxs("header", { children: [_jsx("h3", { id: "identity-evidence-documents-title", children: copy.title }), _jsx("p", { children: copy.body })] }), _jsxs("label", { children: [_jsx("span", { children: copy.choosePdf }), _jsx("input", { type: "file", accept: "application/pdf,.pdf", "aria-label": copy.choosePdf, onChange: (event) => setFile(event.target.files?.[0]) })] }), _jsx("button", { type: "button", onClick: () => void upload(), disabled: busy !== undefined || !selectedIdentifier, children: busy === 'upload' ? copy.uploading : copy.upload }), error ? _jsx("p", { role: "alert", children: error }) : null, documents.length ? (_jsx("ul", { children: documents.map((document) => (_jsxs("li", { children: [_jsxs("span", { children: [_jsx("strong", { children: document.fileName }), _jsx("small", { children: copy.statusLabels[document.status] })] }), document.status === 'pending' ? (_jsx("button", { type: "button", onClick: () => void verify(document.id), disabled: busy !== undefined, children: busy === 'verify' ? copy.verifying : copy.verify })) : null] }, document.id))) })) : _jsx("p", { children: copy.noDocument })] }));
40
+ }
@@ -0,0 +1,14 @@
1
+ export type PortalLoadingSkeletonProps = Readonly<{
2
+ label: string;
3
+ rows?: number;
4
+ compact?: boolean;
5
+ }>;
6
+ /**
7
+ * Shared neutral loading surface for authenticated portal data.
8
+ *
9
+ * Hosts render this instead of business empty states while a BFF/provider
10
+ * read is pending. It contains no inferred card, identity or authorization
11
+ * data and therefore cannot flash stale or misleading content.
12
+ */
13
+ export declare function PortalLoadingSkeleton({ label, rows, compact, }: PortalLoadingSkeletonProps): import("react").JSX.Element;
14
+ //# sourceMappingURL=PortalLoadingSkeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortalLoadingSkeleton.d.ts","sourceRoot":"","sources":["../src/PortalLoadingSkeleton.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,IAAQ,EACR,OAAe,GAChB,EAAE,0BAA0B,+BAsB5B"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Shared neutral loading surface for authenticated portal data.
4
+ *
5
+ * Hosts render this instead of business empty states while a BFF/provider
6
+ * read is pending. It contains no inferred card, identity or authorization
7
+ * data and therefore cannot flash stale or misleading content.
8
+ */
9
+ export function PortalLoadingSkeleton({ label, rows = 3, compact = false, }) {
10
+ return (_jsxs("section", { className: `uhcp-loading-skeleton${compact ? ' uhcp-loading-skeleton-compact' : ''}`, role: "status", "aria-busy": "true", "aria-live": "polite", children: [_jsx("span", { className: "uhcp-loading-label", children: label }), _jsx("div", { className: "uhcp-loading-heading", "aria-hidden": "true" }), _jsx("div", { className: "uhcp-loading-copy", "aria-hidden": "true" }), _jsx("div", { className: "uhcp-loading-grid", "aria-hidden": "true", children: Array.from({ length: Math.max(1, rows) }, (_, index) => (_jsxs("div", { className: "uhcp-loading-row", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }, index))) })] }));
11
+ }
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './PortalHeader.js';
5
5
  export * from './PortalShell.js';
6
6
  export * from './PortalEntry.js';
7
7
  export * from './ProfileProtectionGate.js';
8
+ export * from './PortalLoadingSkeleton.js';
8
9
  export * from './ClinicalSectionSummary.js';
9
10
  export * from './IpsBundleSummary.js';
10
11
  export * from './InvitationAcceptancePanel.js';
@@ -15,4 +16,6 @@ export * from './ProtectedIndexEmergencyData.js';
15
16
  export * from './PortalSettingsPanel.js';
16
17
  export * from './LegalMarkdownDocument.js';
17
18
  export * from './IdentityEvidenceWorkspace.js';
19
+ export * from './IdentityEvidenceDocumentPanel.js';
20
+ export * from './IdentityEvidenceDeclarationForm.js';
18
21
  //# 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;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,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,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;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,sCAAsC,CAAA"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './PortalHeader.js';
5
5
  export * from './PortalShell.js';
6
6
  export * from './PortalEntry.js';
7
7
  export * from './ProfileProtectionGate.js';
8
+ export * from './PortalLoadingSkeleton.js';
8
9
  export * from './ClinicalSectionSummary.js';
9
10
  export * from './IpsBundleSummary.js';
10
11
  export * from './InvitationAcceptancePanel.js';
@@ -15,3 +16,5 @@ export * from './ProtectedIndexEmergencyData.js';
15
16
  export * from './PortalSettingsPanel.js';
16
17
  export * from './LegalMarkdownDocument.js';
17
18
  export * from './IdentityEvidenceWorkspace.js';
19
+ export * from './IdentityEvidenceDocumentPanel.js';
20
+ export * from './IdentityEvidenceDeclarationForm.js';
package/dist/styles.css CHANGED
@@ -277,8 +277,29 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
277
277
  .uhcp-identity-upload-action:hover { border-color: var(--uhcp-turquoise); }
278
278
  .uhcp-identity-upload-action:disabled { cursor: not-allowed; opacity: .6; }
279
279
  .uhcp-identity-upload-action small { color: var(--uhcp-muted); line-height: 1.45; }
280
+ .uhcp-identity-documents { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: 1.5rem; color: var(--uhcp-text); display: grid; gap: 1rem; padding: 1.25rem; }
281
+ .uhcp-identity-documents h3, .uhcp-identity-documents p { margin: 0; }
282
+ .uhcp-identity-documents header p, .uhcp-identity-documents small { color: var(--uhcp-muted); }
283
+ .uhcp-identity-documents label { display: grid; font-weight: 800; gap: .5rem; }
284
+ .uhcp-identity-documents input { border: 1px solid var(--uhcp-border); border-radius: .85rem; color: var(--uhcp-text); padding: .75rem; }
285
+ .uhcp-identity-documents > button, .uhcp-identity-documents li button { background: linear-gradient(135deg, var(--uhcp-action-start), var(--uhcp-action-end)); border: 0; border-radius: .85rem; color: #fff; cursor: pointer; font: inherit; font-weight: 800; min-height: 2.75rem; padding: .65rem 1rem; }
286
+ .uhcp-identity-documents button:disabled { cursor: not-allowed; opacity: .55; }
287
+ .uhcp-identity-documents ul { display: grid; gap: .65rem; list-style: none; margin: 0; padding: 0; }
288
+ .uhcp-identity-documents li { align-items: center; background: var(--uhcp-panel-muted); border-radius: 1rem; display: flex; gap: 1rem; justify-content: space-between; padding: .85rem; }
289
+ .uhcp-identity-documents li span { display: grid; gap: .25rem; }
290
+ .uhcp-identity-documents [role='alert'] { color: #c64747; font-weight: 800; }
291
+ .uhcp-identity-declaration { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: 1.5rem; color: var(--uhcp-text); display: grid; gap: 1rem; padding: 1.25rem; }
292
+ .uhcp-identity-declaration h3, .uhcp-identity-declaration p { margin: 0; }
293
+ .uhcp-identity-declaration header p { color: var(--uhcp-muted); line-height: 1.55; margin-top: .4rem; }
294
+ .uhcp-identity-declaration-fields { display: grid; gap: 1rem; }
295
+ .uhcp-identity-declaration label { display: grid; font-weight: 800; gap: .5rem; }
296
+ .uhcp-identity-declaration label:has(input[type='checkbox']) { align-items: center; display: flex; }
297
+ .uhcp-identity-declaration input:not([type='checkbox']), .uhcp-identity-declaration select { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: .85rem; color: var(--uhcp-text); font: inherit; min-height: 2.75rem; padding: .7rem .8rem; }
298
+ .uhcp-identity-declaration > button { background: linear-gradient(135deg, var(--uhcp-action-start), var(--uhcp-action-end)); border: 0; border-radius: .85rem; color: #fff; cursor: pointer; font: inherit; font-weight: 800; min-height: 2.75rem; padding: .65rem 1rem; }
299
+ .uhcp-identity-declaration [role='alert'] { color: #c64747; font-weight: 800; }
280
300
  @media (min-width: 760px) {
281
301
  .uhcp-identity-upload-actions { grid-template-columns: repeat(2, minmax(0, 1fr)); }
302
+ .uhcp-identity-declaration-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
282
303
  }
283
304
 
284
305
  .uhcp-clinical-summary {
@@ -455,3 +476,4 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
455
476
  @media (max-width: 620px) { .uhcp-emergency-copy > header { display: grid; } .uhcp-emergency-copy-legend { width: 100%; } }
456
477
  .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
478
  .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}
479
+ .uhcp-loading-skeleton{background:var(--uhcp-panel);border:1px solid var(--uhcp-border);border-radius:2rem;display:grid;gap:1rem;margin:0 auto;max-width:1160px;overflow:hidden;padding:clamp(1.25rem,4vw,2.5rem);position:relative}.uhcp-loading-skeleton::after{animation:uhcp-loading-sweep 1.45s ease-in-out infinite;background:linear-gradient(90deg,transparent,color-mix(in srgb,#fff 42%,transparent),transparent);content:"";inset:0;position:absolute;transform:translateX(-100%)}.uhcp-loading-label{height:1px;overflow:hidden;position:absolute;width:1px;clip-path:inset(50%)}.uhcp-loading-heading,.uhcp-loading-copy,.uhcp-loading-row span{animation:uhcp-loading-pulse 1.45s ease-in-out infinite;background:color-mix(in srgb,var(--uhcp-muted) 17%,var(--uhcp-panel-muted));border-radius:999px}.uhcp-loading-heading{height:2.25rem;max-width:23rem;width:62%}.uhcp-loading-copy{height:1rem;max-width:38rem;width:86%}.uhcp-loading-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));margin-top:.5rem}.uhcp-loading-row{background:var(--uhcp-panel-muted);border:1px solid var(--uhcp-border);border-radius:1.35rem;display:grid;gap:.8rem;padding:1.25rem}.uhcp-loading-row span{display:block;height:.8rem}.uhcp-loading-row span:nth-child(1){width:35%}.uhcp-loading-row span:nth-child(2){height:1.2rem;width:72%}.uhcp-loading-row span:nth-child(3){width:90%}.uhcp-loading-skeleton-compact{border-radius:1.25rem;padding:1.25rem}.uhcp-loading-skeleton-compact .uhcp-loading-heading{height:1.5rem}@keyframes uhcp-loading-pulse{0%,100%{opacity:.55}50%{opacity:1}}@keyframes uhcp-loading-sweep{to{transform:translateX(100%)}}@media(prefers-reduced-motion:reduce){.uhcp-loading-skeleton::after,.uhcp-loading-heading,.uhcp-loading-copy,.uhcp-loading-row span{animation:none}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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",