uhc-portal-components-react 0.2.13 → 0.2.15

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
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Add the shared two-sided SOSCHAIN Smart Identity Card artifact. Face A keeps
6
+ a DID-only QR and localized subject label; face B formats the seven-digit
7
+ animal species context and 21-digit telephone identifier and renders an
8
+ SHC-like identity QR only when the owning provider supplies it already
9
+ signed.
10
+
11
+ ## 0.2.14 - 2026-10-15
12
+
13
+ - Compose the existing coded-concept V2 field from the neutral
14
+ `TerminologyCodePickerV2` while preserving its public props, CSS classes,
15
+ explicit-search behavior and selected flat claims.
16
+ - Keep the original editor path unchanged.
17
+
5
18
  ## 0.2.13 - 2026-10-11
6
19
 
7
20
  - Present the animal identifier as `Microchip (ISO 11784/11785)` in both
package/README.md CHANGED
@@ -197,3 +197,11 @@ route destinations; it must not fork shared flow or authorization behavior.
197
197
  See [`docs/PERSONAL_FLOW_EXTRACTION.md`](docs/PERSONAL_FLOW_EXTRACTION.md) and
198
198
  the canonical cross-portal
199
199
  [`docs/HISTORICAL_PORTAL_COMPONENT_RECOVERY_MATRIX.md`](docs/HISTORICAL_PORTAL_COMPONENT_RECOVERY_MATRIX.md).
200
+ ### Two-sided SOSCHAIN identity card
201
+
202
+ `SoschainSmartIdentityCardArtifact` renders the common printable card used by
203
+ SOSChain, PetChain and VetChain. The host supplies both QR nodes: face A must
204
+ encode only the canonical card `did:web`; face B may encode only an already
205
+ signed provider-issued identity credential. Presentation code never issues or
206
+ signs the credential. When no signed value is available, the reverse side
207
+ fails closed with an explicit unavailable state.
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  export type AnimalCardArtifactProps = Readonly<{
2
3
  brand: string;
3
4
  title: string;
@@ -14,4 +15,26 @@ export type AnimalCardArtifactProps = Readonly<{
14
15
  * identifiers and QR payload; this component neither issues nor verifies them.
15
16
  */
16
17
  export declare function AnimalCardArtifact({ brand, title, provider, subject, species, cardNumber, cardDid, qrImageUrl, logoUrl, }: AnimalCardArtifactProps): import("react").JSX.Element;
18
+ export type SoschainSmartIdentityCardArtifactProps = Readonly<{
19
+ /** Human cards use the fixed public label `Personal`; animal cards use the supplied localized species label. */
20
+ subjectKind?: 'animal' | 'personal';
21
+ subjectLabel?: string;
22
+ cardDid: string;
23
+ speciesId7?: string;
24
+ cardNumber21?: string;
25
+ /** Host-rendered QR whose exact payload is `cardDid`. */
26
+ didQr: ReactNode;
27
+ /** Host-rendered QR from a provider-issued, signed SHC-like identity credential. */
28
+ identityQr?: ReactNode;
29
+ logoUrl: string;
30
+ signedIdentityUnavailableLabel?: string;
31
+ }>;
32
+ /** Formats the canonical 21 digits for visual and telephone readback only. */
33
+ export declare function formatSoschainCardNumber21(value: string): string;
34
+ /**
35
+ * Two-sided physical identity artifact shared by SOSChain, PetChain and
36
+ * VetChain. The host owns both QR payloads. Face A is deliberately DID-only;
37
+ * face B never invents or signs a credential in presentation code.
38
+ */
39
+ export declare function SoschainSmartIdentityCardArtifact({ subjectKind, subjectLabel, cardDid, speciesId7, cardNumber21, didQr, identityQr, logoUrl, signedIdentityUnavailableLabel, }: SoschainSmartIdentityCardArtifactProps): import("react").JSX.Element;
17
40
  //# sourceMappingURL=AnimalCardArtifact.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnimalCardArtifact.d.ts","sourceRoot":"","sources":["../src/AnimalCardArtifact.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,GACR,EAAE,uBAAuB,+BAsBzB"}
1
+ {"version":3,"file":"AnimalCardArtifact.d.ts","sourceRoot":"","sources":["../src/AnimalCardArtifact.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,GACR,EAAE,uBAAuB,+BAsBzB;AAED,MAAM,MAAM,sCAAsC,GAAG,QAAQ,CAAC;IAC5D,gHAAgH;IAChH,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;IACnC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yDAAyD;IACzD,KAAK,EAAE,SAAS,CAAA;IAChB,oFAAoF;IACpF,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B,CAAC,EAAE,MAAM,CAAA;CACxC,CAAC,CAAA;AAEF,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,EAChD,WAAsB,EACtB,YAAY,EACZ,OAAO,EACP,UAAU,EACV,YAAY,EACZ,KAAK,EACL,UAAU,EACV,OAAO,EACP,8BAA8D,GAC/D,EAAE,sCAAsC,+BA+BxC"}
@@ -6,3 +6,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  export function AnimalCardArtifact({ brand, title, provider, subject, species, cardNumber, cardDid, qrImageUrl, logoUrl, }) {
7
7
  return _jsxs("article", { className: "uhcp-animal-card", "aria-label": `${title}: ${subject}`, children: [_jsxs("header", { className: "uhcp-animal-card-header", children: [_jsxs("div", { className: "uhcp-animal-card-brand", children: [logoUrl ? _jsx("img", { src: logoUrl, alt: "" }) : _jsx("span", { "aria-hidden": "true", children: "V" }), _jsxs("div", { children: [_jsx("strong", { children: brand }), _jsx("small", { children: title })] })] }), _jsx("span", { className: "uhcp-animal-card-chip", "aria-hidden": "true" })] }), _jsxs("div", { className: "uhcp-animal-card-body", children: [_jsxs("section", { children: [_jsx("span", { children: species }), _jsx("h3", { children: subject }), _jsxs("dl", { children: [_jsxs("div", { children: [_jsx("dt", { children: "Card" }), _jsx("dd", { children: cardNumber })] }), _jsxs("div", { children: [_jsx("dt", { children: "Provider" }), _jsx("dd", { children: provider })] })] })] }), _jsx("img", { className: "uhcp-animal-card-qr", src: qrImageUrl, alt: `${title} QR` })] }), _jsx("footer", { children: _jsx("code", { children: cardDid }) })] });
8
8
  }
9
+ /** Formats the canonical 21 digits for visual and telephone readback only. */
10
+ export function formatSoschainCardNumber21(value) {
11
+ const digits = String(value || '').replace(/[ -]/g, '');
12
+ if (!/^\d{21}$/.test(digits))
13
+ throw new TypeError('cardNumber21 must contain exactly 21 digits.');
14
+ return `${digits.slice(0, 5)} ${digits.slice(5, 10)} ${digits.slice(10, 15)} ${digits.slice(15, 20)} ${digits.slice(20)}`;
15
+ }
16
+ /**
17
+ * Two-sided physical identity artifact shared by SOSChain, PetChain and
18
+ * VetChain. The host owns both QR payloads. Face A is deliberately DID-only;
19
+ * face B never invents or signs a credential in presentation code.
20
+ */
21
+ export function SoschainSmartIdentityCardArtifact({ subjectKind = 'animal', subjectLabel, cardDid, speciesId7, cardNumber21, didQr, identityQr, logoUrl, signedIdentityUnavailableLabel = 'Signed identity unavailable', }) {
22
+ const publicLabel = subjectKind === 'personal' ? 'Personal' : String(subjectLabel || 'Animal').trim();
23
+ const formattedCardNumber = cardNumber21 ? formatSoschainCardNumber21(cardNumber21) : undefined;
24
+ return _jsxs("div", { className: "uhcp-smart-id-card-set", "aria-label": `SOSCHAIN Smart Identity Card: ${publicLabel}`, children: [_jsxs("article", { className: "uhcp-smart-id-card uhcp-smart-id-card-front", "data-card-face": "front", children: [_jsxs("header", { className: "uhcp-smart-id-brand", children: [_jsx("img", { src: logoUrl, alt: "" }), _jsx("strong", { children: "SOSCHAIN" }), _jsx("span", { children: publicLabel })] }), _jsx("div", { className: "uhcp-smart-id-front-qr", "aria-label": "DID QR code", children: didQr }), _jsx("code", { children: cardDid })] }), _jsxs("article", { className: "uhcp-smart-id-card uhcp-smart-id-card-back", "data-card-face": "back", children: [_jsxs("header", { children: [_jsx("strong", { children: "SOSCHAIN SMART IDENTITY CARD" }), _jsx("span", { children: publicLabel })] }), _jsxs("div", { className: "uhcp-smart-id-back-body", children: [_jsxs("dl", { children: [speciesId7 ? _jsxs("div", { children: [_jsx("dt", { children: "Species" }), _jsx("dd", { children: speciesId7 })] }) : null, formattedCardNumber ? _jsxs("div", { children: [_jsx("dt", { children: "Card" }), _jsx("dd", { children: formattedCardNumber })] }) : null] }), identityQr
25
+ ? _jsx("div", { className: "uhcp-smart-id-identity-qr", "aria-label": "Signed identity QR code", children: identityQr })
26
+ : _jsx("p", { className: "uhcp-smart-id-unavailable", role: "status", children: signedIdentityUnavailableLabel })] })] })] });
27
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ClinicalArtifactPropertiesV2.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactPropertiesV2.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClinicalArtifactPropertiesV2.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactPropertiesV2.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useState } from 'react';
4
+ import { TerminologyCodePickerV2 } from 'terminology-components-react/code-picker-v2';
4
5
  import { composeFhirDateTimeFromLocalParts, splitFhirDateTimeForInput, } from 'fhir-data-utils-ts/fhir-temporal-presentation';
5
6
  import { ClinicalSpeechInputButton } from './ClinicalSpeechInput.js';
6
7
  export function DateTimePropertyV2({ label, value, required, locale, timeZone, copy, onChange }) {
@@ -13,62 +14,7 @@ export function DateTimePropertyV2({ label, value, required, locale, timeZone, c
13
14
  return _jsxs("fieldset", { className: "uhcp-clinical-property-v2 uhcp-date-time-property-v2", children: [_jsxs("legend", { children: [label, required ? ' *' : ''] }), _jsxs("label", { children: [_jsx("span", { children: copy.date }), _jsx("input", { "aria-label": `${label}: ${copy.date}`, type: "date", required: required, value: parts.date, onChange: event => update({ ...parts, date: event.currentTarget.value }) })] }), _jsx("button", { type: "button", onClick: () => update({ ...parts, date: current.date }), children: copy.useToday }), _jsxs("label", { children: [_jsx("span", { children: copy.time }), _jsx("input", { "aria-label": `${label}: ${copy.time}`, type: "time", step: "60", value: parts.time.slice(0, 5), onChange: event => update({ ...parts, time: event.currentTarget.value }) })] }), _jsx("button", { type: "button", onClick: () => update({ ...parts, time: current.time.slice(0, 5) }), children: copy.useCurrentTime })] });
14
15
  }
15
16
  export function CodedConceptPropertyV2({ resourceType, textClaim, codeClaim, displayClaim, label, locale, taxonomyId, text, code, display, required, copy, searchTerminology, loadTerminologySources, onChange }) {
16
- const [query, setQuery] = useState(text || display);
17
- const [sources, setSources] = useState([]);
18
- const [selectedSources, setSelectedSources] = useState([]);
19
- const [candidates, setCandidates] = useState([]);
20
- const [busy, setBusy] = useState(false);
21
- const [failed, setFailed] = useState(false);
22
- const [searched, setSearched] = useState(false);
23
- useEffect(() => {
24
- if (!loadTerminologySources)
25
- return;
26
- let current = true;
27
- void loadTerminologySources({ resourceType, claim: codeClaim, language: locale, taxonomyId }).then(next => {
28
- if (!current)
29
- return;
30
- setSources(next);
31
- const defaults = next.filter(source => source.default).map(source => source.id);
32
- setSelectedSources(defaults.length ? defaults : next[0] ? [next[0].id] : []);
33
- }).catch(() => current && setSources([]));
34
- return () => { current = false; };
35
- }, [codeClaim, loadTerminologySources, locale, resourceType, taxonomyId]);
36
- const selectedToken = useMemo(() => code, [code]);
37
- const search = async () => {
38
- if (!searchTerminology || query.trim().length < 2 || busy)
39
- return;
40
- setBusy(true);
41
- setFailed(false);
42
- setSearched(false);
43
- try {
44
- let governedSources = selectedSources;
45
- if (!governedSources.length && loadTerminologySources) {
46
- const loaded = await loadTerminologySources({ resourceType, claim: codeClaim, language: locale, taxonomyId });
47
- const defaults = loaded.filter(source => source.default).map(source => source.id);
48
- governedSources = defaults.length ? defaults : loaded[0] ? [loaded[0].id] : [];
49
- setSources(loaded);
50
- setSelectedSources(governedSources);
51
- }
52
- setCandidates(await searchTerminology({ resourceType, claim: codeClaim, language: locale, text: query.trim(), taxonomyId, limit: 20, ...(governedSources.length ? { sources: governedSources } : {}) }));
53
- setSearched(true);
54
- }
55
- catch {
56
- setCandidates([]);
57
- setFailed(true);
58
- }
59
- finally {
60
- setBusy(false);
61
- }
62
- };
63
- return _jsxs("div", { className: "uhcp-clinical-property-v2 uhcp-coded-concept-property-v2", children: [_jsxs("span", { className: "uhcp-clinical-editor-field-heading", children: [_jsxs("label", { htmlFor: `${codeClaim}-query`, children: [label, required ? ' *' : ''] }), textClaim ? _jsx(ClinicalSpeechInputButton, { resourceType: resourceType, claim: textClaim, locale: locale, fieldLabel: label, onTranscript: transcript => { setQuery(transcript); onChange({ text: transcript, code: '', display: '' }); } }) : null] }), _jsxs("div", { className: "uhcp-coded-concept-query-v2", children: [_jsx("input", { id: `${codeClaim}-query`, "aria-label": label, required: required, readOnly: Boolean(code), value: code ? (text || query || display) : query, onChange: event => { setQuery(event.currentTarget.value); setCandidates([]); setSearched(false); setFailed(false); onChange({ text: event.currentTarget.value, code: '', display: '' }); } }), _jsx("button", { type: "button", className: "uhcp-icon-button-v2", "aria-label": copy.searchTerminology, title: copy.searchTerminology, disabled: Boolean(code) || busy || query.trim().length < 2, onClick: () => void search(), children: _jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", children: _jsx("path", { d: "m21 21-4.35-4.35m1.35-5.65a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z" }) }) }), code ? _jsx("button", { type: "button", className: "uhcp-clear-code-v2", "aria-label": copy.clearCoding, title: copy.clearCoding, onClick: () => { setQuery(''); setCandidates([]); onChange({ text: '', code: '', display: '' }); }, children: "\u00D7" }) : null] }), sources.length ? _jsxs("fieldset", { className: "uhcp-coding-sources-v2", children: [_jsx("legend", { children: copy.terminologySources }), sources.map(source => _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: selectedSources.includes(source.id), onChange: event => { const checked = event.currentTarget.checked; setSelectedSources(current => checked ? [...new Set([...current, source.id])] : current.filter(value => value !== source.id)); setCandidates([]); setSearched(false); setFailed(false); } }), source.label || source.id] }, source.id))] }) : null, busy ? _jsx("small", { role: "status", children: copy.terminologyLoading }) : null, failed ? _jsx("small", { role: "alert", children: copy.terminologyUnavailable }) : null, searched && !busy && !failed && candidates.length === 0 ? _jsx("small", { role: "status", children: copy.terminologyEmpty || copy.terminologyUnavailable }) : null, candidates.length ? _jsxs("label", { children: [copy.selectCandidate, _jsxs("select", { "aria-label": copy.selectCandidate, value: selectedToken, onChange: event => {
64
- const candidate = candidates.find(value => `${value.system}|${value.code}` === event.currentTarget.value);
65
- if (!candidate)
66
- return;
67
- const localText = candidate.text || query || candidate.display;
68
- setQuery(localText);
69
- onChange({ text: localText, code: `${candidate.system}|${candidate.code}`, display: candidate.display });
70
- setCandidates([]);
71
- }, children: [_jsx("option", { value: "" }), candidates.map(candidate => _jsxs("option", { value: `${candidate.system}|${candidate.code}`, children: [candidate.text && candidate.text !== candidate.display ? `${candidate.text} — ${candidate.display}` : candidate.display, " (", candidate.code, ")"] }, `${candidate.system}|${candidate.code}`))] })] }) : null, code ? _jsxs("div", { className: "uhcp-selected-code-v2", children: [_jsxs("label", { children: [copy.canonicalCode, _jsx("input", { "aria-label": copy.canonicalCode, readOnly: true, value: code })] }), _jsxs("label", { children: [copy.canonicalDisplay, _jsx("input", { "aria-label": copy.canonicalDisplay, readOnly: true, value: display })] })] }) : null] });
17
+ return _jsx(TerminologyCodePickerV2, { resourceType: resourceType, param: codeClaim, language: locale, ...(taxonomyId ? { taxonomyId } : {}), label: label, text: text, code: code, display: display, required: required, copy: { search: copy.searchTerminology, loading: copy.terminologyLoading, unavailable: copy.terminologyUnavailable, empty: copy.terminologyEmpty, sources: copy.terminologySources, select: copy.selectCandidate, clear: copy.clearCoding, canonicalCode: copy.canonicalCode, canonicalDisplay: copy.canonicalDisplay }, ...(searchTerminology ? { search: async (input) => (await searchTerminology({ resourceType: input.resourceType, claim: input.param, language: input.language, text: input.text, limit: input.limit, ...(input.taxonomyId ? { taxonomyId: input.taxonomyId } : {}), ...(input.sources ? { sources: input.sources } : {}) })).map(candidate => ({ ...candidate, text: candidate.text || candidate.display, language: input.language })) } : {}), ...(loadTerminologySources ? { loadSources: input => loadTerminologySources({ resourceType: input.resourceType, claim: input.param, language: input.language, ...(input.taxonomyId ? { taxonomyId: input.taxonomyId } : {}) }) } : {}), onChange: onChange, headingAction: textClaim ? _jsx(ClinicalSpeechInputButton, { resourceType: resourceType, claim: textClaim, locale: locale, fieldLabel: label, onTranscript: transcript => onChange({ text: transcript, code: '', display: '' }) }) : null, classNames: { root: 'uhcp-clinical-property-v2 uhcp-coded-concept-property-v2', heading: 'uhcp-clinical-editor-field-heading', query: 'uhcp-coded-concept-query-v2', searchButton: 'uhcp-icon-button-v2', clearButton: 'uhcp-clear-code-v2', sources: 'uhcp-coding-sources-v2', selected: 'uhcp-selected-code-v2' } });
72
18
  }
73
19
  export function CodedSelectPropertyV2({ label, value, required, options, onChange }) {
74
20
  return _jsxs("label", { className: "uhcp-clinical-property-v2", children: [label, required ? ' *' : '', _jsxs("select", { "aria-label": label, required: required, value: value, onChange: event => onChange(event.currentTarget.value), children: [_jsx("option", { value: "" }), options.map(option => _jsx("option", { value: option.value, children: option.label }, option.value))] })] });
package/dist/styles.css CHANGED
@@ -833,6 +833,30 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
833
833
  .uhcp-animal-card footer { min-width: 0; }
834
834
  .uhcp-animal-card footer code { color: inherit; display: block; font-size: .68rem; opacity: .8; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
835
835
  @media(max-width: 480px) { .uhcp-animal-card { aspect-ratio: auto; min-height: 17rem; } .uhcp-animal-card-qr { height: 6.25rem; width: 6.25rem; } }
836
+ .uhcp-smart-id-card-set { display: grid; gap: 1rem; width: min(100%, 33.7rem); }
837
+ .uhcp-smart-id-card { aspect-ratio: 1.586; border: 1px solid #cbd5e1; border-radius: 1.15rem; box-shadow: 0 1rem 2.5rem rgb(2 30 51 / 18%); box-sizing: border-box; overflow: hidden; padding: .75rem; position: relative; width: 100%; }
838
+ .uhcp-smart-id-card-front { background: linear-gradient(145deg, #061c35 0%, #0a4169 58%, #087f8d 100%); color: #fff; display: grid; gap: .35rem; grid-template-rows: auto 1fr auto; }
839
+ .uhcp-smart-id-card-front::after { background: radial-gradient(circle, rgb(255 255 255 / 16%), transparent 70%); content: ''; height: 18rem; inset: -8rem -7rem auto auto; pointer-events: none; position: absolute; width: 18rem; }
840
+ .uhcp-smart-id-brand { align-items: center; display: grid; gap: .45rem; grid-template-columns: 2rem auto 1fr; position: relative; z-index: 1; }
841
+ .uhcp-smart-id-brand img { background: #fff; border-radius: .48rem; box-sizing: border-box; height: 2rem; object-fit: contain; padding: .16rem; width: 2rem; }
842
+ .uhcp-smart-id-brand strong { font-size: .85rem; letter-spacing: .08em; }
843
+ .uhcp-smart-id-brand span { font-size: .72rem; font-weight: 800; justify-self: end; text-align: end; }
844
+ .uhcp-smart-id-front-qr { align-self: center; background: #fff; border: .35rem solid #fff; border-radius: .55rem; box-sizing: border-box; height: 7rem; justify-self: center; width: 7rem; }
845
+ .uhcp-smart-id-front-qr > * { display: block; height: 100%; width: 100%; }
846
+ .uhcp-smart-id-card-front code { color: inherit; display: block; font-size: .5rem; opacity: .82; overflow: hidden; text-align: center; text-overflow: ellipsis; white-space: nowrap; }
847
+ .uhcp-smart-id-card-back { background: #fff; color: #0f2942; display: grid; gap: .5rem; grid-template-rows: auto 1fr; }
848
+ .uhcp-smart-id-card-back > header { align-items: baseline; border-bottom: 2px solid #0f9ca8; display: flex; gap: .5rem; justify-content: space-between; padding-bottom: .35rem; }
849
+ .uhcp-smart-id-card-back > header strong { font-size: .68rem; letter-spacing: .08em; }
850
+ .uhcp-smart-id-card-back > header span { color: #087f8d; font-size: .68rem; font-weight: 850; text-align: end; }
851
+ .uhcp-smart-id-back-body { align-items: center; display: grid; gap: .65rem; grid-template-columns: minmax(0, 1fr) 7rem; min-height: 0; }
852
+ .uhcp-smart-id-back-body dl { display: grid; gap: .5rem; margin: 0; }
853
+ .uhcp-smart-id-back-body dt { color: #526174; font-size: .55rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
854
+ .uhcp-smart-id-back-body dd { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: .72rem; font-weight: 850; margin: .12rem 0 0; overflow-wrap: anywhere; }
855
+ .uhcp-smart-id-identity-qr { background: #fff; box-sizing: border-box; height: 7rem; width: 7rem; }
856
+ .uhcp-smart-id-identity-qr > * { display: block; height: 100%; width: 100%; }
857
+ .uhcp-smart-id-unavailable { align-items: center; background: #f1f5f9; border: 1px dashed #94a3b8; border-radius: .6rem; box-sizing: border-box; color: #526174; display: flex; font-size: .62rem; height: 7rem; justify-content: center; margin: 0; padding: .4rem; text-align: center; width: 7rem; }
858
+ @media(max-width: 480px) { .uhcp-smart-id-back-body { grid-template-columns: 1fr 6.5rem; } .uhcp-smart-id-identity-qr, .uhcp-smart-id-unavailable { height: 6.5rem; width: 6.5rem; } }
859
+ @media print { .uhcp-smart-id-card-set { gap: 0; } .uhcp-smart-id-card { break-after: page; box-shadow: none; height: 53.98mm; width: 85.6mm; } .uhcp-smart-id-card:last-child { break-after: auto; } }
836
860
  .uhcp-animal-controller-attestation { border: 1px solid var(--uhcp-border, #d9dee7); border-radius: 1.25rem; display: grid; gap: 1rem; padding: 1rem; }
837
861
  .uhcp-animal-controller-attestation header h2, .uhcp-animal-controller-attestation header p { margin: 0; }
838
862
  .uhcp-animal-controller-attestation header p { color: var(--uhcp-muted, #5f6875); margin-top: .35rem; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
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",
@@ -33,7 +33,8 @@
33
33
  "dependencies": {
34
34
  "fhir-data-utils-ts": "0.6.18",
35
35
  "gdc-common-utils-ts": "2.9.30",
36
- "sos-data-utils-ts": "0.6.3"
36
+ "sos-data-utils-ts": "0.6.3",
37
+ "terminology-components-react": "0.2.0"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@testing-library/react": "^16.3.2",