uhc-portal-components-react 0.2.37 → 0.2.39

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,22 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.2.39 - 2026-11-04
6
+
7
+ - Let hosts offer country, optional region, organization legal identifier and
8
+ sector discovery for an existing professional. When several authoritative
9
+ roles resolve, require one to be selected before rendering device PIN setup.
10
+
11
+ ## 0.2.38 - 2026-11-03
12
+
13
+ - Keep the language dropdown inside the shared person menu on public and
14
+ authenticated portal routes.
15
+ - Preserve the same account panel before sign-in while omitting account data
16
+ and private preferences until a verified session exists.
17
+ - Let signed-in hosts opt into showing language alongside their account
18
+ preferences, and align the subject-card directory dependency with the
19
+ reusable provider selector contract.
3
20
  ## 0.2.37 - 2026-11-03
4
21
 
5
22
  - Split existing-professional registration into evidence verification and
package/README.md CHANGED
@@ -135,10 +135,12 @@ The package deliberately exposes modules instead of a cloned application:
135
135
  individual member; the host refreshes the authorized-subject list after its
136
136
  BFF accepts the invitation.
137
137
  - `ExistingProfessionalProfilePanel`: existing employees scan or upload a
138
- signed professional-credential QR, or use the explicit DID/URN plus sector
139
- fallback. When the host supplies `onResolve`, the screen verifies and shows
140
- the server-resolved role before it offers local device-PIN setup and DCR.
141
- This screen never accepts an employee invitation.
138
+ signed professional-credential QR. A host may instead supply country and
139
+ regional-jurisdiction options so the employee enters an organization legal
140
+ identifier and sector without knowing a DID, URN or route. When the host
141
+ supplies `onResolve`, the screen shows the server-resolved role choices
142
+ before it offers local device-PIN setup and DCR. This screen never accepts an
143
+ employee invitation or treats a browser role choice as authority.
142
144
  - `SubjectAccessRequestPanel`: controlled provider-enabled QR, numeric-code,
143
145
  `did:web`, microchip, tattoo, email or telephone locator. Resolution and
144
146
  returned identifier evidence never grant subject access.
@@ -13,6 +13,12 @@ export type ExistingProfessionalProfileCopy = Readonly<{
13
13
  cameraActive: string;
14
14
  credentialQrUnreadable: string;
15
15
  professionalIdentifierLabel: string;
16
+ countryLabel?: string;
17
+ countryPlaceholder?: string;
18
+ regionalIdentifierLabel?: string;
19
+ regionLabel?: string;
20
+ regionPlaceholder?: string;
21
+ organizationIdentifierLabel?: string;
16
22
  sectorLabel: string;
17
23
  pinLabel: string;
18
24
  pinConfirmationLabel: string;
@@ -34,6 +40,14 @@ export type ExistingProfessionalProfileInput = Readonly<{
34
40
  professionalIdentifier: string;
35
41
  sector: string;
36
42
  pin: string;
43
+ }> | Readonly<{
44
+ method: 'organization';
45
+ country: string;
46
+ region?: string;
47
+ organizationIdentifier: string;
48
+ sector: string;
49
+ selectedProfile: string;
50
+ pin: string;
37
51
  }>;
38
52
  export type ExistingProfessionalEvidenceInput = Readonly<{
39
53
  method: 'credential';
@@ -42,9 +56,16 @@ export type ExistingProfessionalEvidenceInput = Readonly<{
42
56
  method: 'identifier';
43
57
  professionalIdentifier: string;
44
58
  sector: string;
59
+ }> | Readonly<{
60
+ method: 'organization';
61
+ country: string;
62
+ region?: string;
63
+ organizationIdentifier: string;
64
+ sector: string;
45
65
  }>;
46
66
  export type ResolvedExistingProfessionalProfile = Omit<ProfileProtectionTargetV2, 'eyebrow'> & Readonly<{
47
67
  eyebrow?: string;
68
+ selectionValue?: string;
48
69
  }>;
49
70
  export type ExistingProfessionalProfileRequest = Readonly<{
50
71
  professionalCredential: string;
@@ -53,6 +74,13 @@ export type ExistingProfessionalProfileRequest = Readonly<{
53
74
  professionalIdentifier: string;
54
75
  sectorHint: string;
55
76
  pin: string;
77
+ }> | Readonly<{
78
+ country: string;
79
+ region?: string;
80
+ organizationIdentifier: string;
81
+ sectorHint: string;
82
+ selectedProfile: string;
83
+ pin: string;
56
84
  }>;
57
85
  /** Shared BFF serialization keeps professional evidence distinct from an invitation code. */
58
86
  export declare function toExistingProfessionalProfileRequest(input: ExistingProfessionalProfileInput): ExistingProfessionalProfileRequest;
@@ -60,14 +88,20 @@ export type ExistingProfessionalSectorOption = Readonly<{
60
88
  value: string;
61
89
  label: string;
62
90
  }>;
91
+ export type ExistingProfessionalJurisdictionOption = Readonly<{
92
+ value: string;
93
+ label: string;
94
+ }>;
63
95
  export type ExistingProfessionalProfilePanelProps = Readonly<{
64
96
  copy: ExistingProfessionalProfileCopy;
65
97
  sectors: readonly ExistingProfessionalSectorOption[];
98
+ countries?: readonly ExistingProfessionalJurisdictionOption[];
99
+ regionsByCountry?: Readonly<Record<string, readonly ExistingProfessionalJurisdictionOption[]>>;
66
100
  busy: boolean;
67
101
  error?: string;
68
102
  onScanProfessionalCredential?: () => Promise<string | undefined>;
69
103
  onReadProfessionalCredentialImage?: (file: File) => Promise<string | undefined>;
70
- onResolve?: (input: ExistingProfessionalEvidenceInput) => Promise<ResolvedExistingProfessionalProfile>;
104
+ onResolve?: (input: ExistingProfessionalEvidenceInput) => Promise<ResolvedExistingProfessionalProfile | readonly ResolvedExistingProfessionalProfile[]>;
71
105
  onSubmit: (input: ExistingProfessionalProfileInput) => void | Promise<void>;
72
106
  }>;
73
107
  /**
@@ -77,5 +111,5 @@ export type ExistingProfessionalProfilePanelProps = Readonly<{
77
111
  * is the preferred path; a single DID-or-URN field plus sector is the explicit
78
112
  * manual fallback and never becomes browser-selected authority by itself.
79
113
  */
80
- export declare function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }: ExistingProfessionalProfilePanelProps): import("react").JSX.Element;
114
+ export declare function ExistingProfessionalProfilePanel({ copy, sectors, countries, regionsByCountry, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }: ExistingProfessionalProfilePanelProps): import("react").JSX.Element;
81
115
  //# sourceMappingURL=ExistingProfessionalProfilePanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExistingProfessionalProfilePanel.d.ts","sourceRoot":"","sources":["../src/ExistingProfessionalProfilePanel.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExistingProfessionalProfilePanel.d.ts","sourceRoot":"","sources":["../src/ExistingProfessionalProfilePanel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAA;AAS/D,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,0BAA0B,EAAE,MAAM,CAAA;IAClC,8BAA8B,EAAE,MAAM,CAAA;IACtC,2BAA2B,EAAE,MAAM,CAAA;IACnC,8BAA8B,EAAE,MAAM,CAAA;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,2BAA2B,EAAE,MAAM,CAAA;IACnC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,2BAA2B,CAAC,EAAE,MAAM,CAAA;IACpC,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,MAAM,MAAM,gCAAgC,GACxC,QAAQ,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC/E,QAAQ,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC/F,QAAQ,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEhK,MAAM,MAAM,iCAAiC,GACzC,QAAQ,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAA;CAAE,CAAC,GAClE,QAAQ,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAClF,QAAQ,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE1H,MAAM,MAAM,mCAAmC,GAAG,IAAI,CAAC,yBAAyB,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEtJ,MAAM,MAAM,kCAAkC,GAC1C,QAAQ,CAAC;IAAE,sBAAsB,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,QAAQ,CAAC;IAAE,sBAAsB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7E,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE5I,6FAA6F;AAC7F,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,gCAAgC,GAAG,kCAAkC,CAWhI;AAED,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AACzF,MAAM,MAAM,sCAAsC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE/F,MAAM,MAAM,qCAAqC,GAAG,QAAQ,CAAC;IAC3D,IAAI,EAAE,+BAA+B,CAAA;IACrC,OAAO,EAAE,SAAS,gCAAgC,EAAE,CAAA;IACpD,SAAS,CAAC,EAAE,SAAS,sCAAsC,EAAE,CAAA;IAC7D,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,sCAAsC,EAAE,CAAC,CAAC,CAAA;IAC9F,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4BAA4B,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAChE,iCAAiC,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAC/E,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,iCAAiC,KAAK,OAAO,CAAC,mCAAmC,GAAG,SAAS,mCAAmC,EAAE,CAAC,CAAA;IACvJ,QAAQ,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5E,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAc,EAAE,gBAAqB,EAAE,IAAI,EAAE,KAAK,EAAE,4BAA4B,EAAE,iCAAiC,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,qCAAqC,+BAiNlP"}
@@ -6,9 +6,18 @@ import { ProfileIdentityTarget } from './ProfileIdentityTarget.js';
6
6
  const decodeQrImage = jsQrModule;
7
7
  /** Shared BFF serialization keeps professional evidence distinct from an invitation code. */
8
8
  export function toExistingProfessionalProfileRequest(input) {
9
- return input.method === 'credential'
10
- ? Object.freeze({ professionalCredential: input.professionalCredential, pin: input.pin })
11
- : Object.freeze({ professionalIdentifier: input.professionalIdentifier, sectorHint: input.sector, pin: input.pin });
9
+ if (input.method === 'credential')
10
+ return Object.freeze({ professionalCredential: input.professionalCredential, pin: input.pin });
11
+ if (input.method === 'identifier')
12
+ return Object.freeze({ professionalIdentifier: input.professionalIdentifier, sectorHint: input.sector, pin: input.pin });
13
+ return Object.freeze({
14
+ country: input.country,
15
+ ...(input.region ? { region: input.region } : {}),
16
+ organizationIdentifier: input.organizationIdentifier,
17
+ sectorHint: input.sector,
18
+ selectedProfile: input.selectedProfile,
19
+ pin: input.pin,
20
+ });
12
21
  }
13
22
  /**
14
23
  * Registers this portal device for an already-active professional profile.
@@ -17,14 +26,19 @@ export function toExistingProfessionalProfileRequest(input) {
17
26
  * is the preferred path; a single DID-or-URN field plus sector is the explicit
18
27
  * manual fallback and never becomes browser-selected authority by itself.
19
28
  */
20
- export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }) {
29
+ export function ExistingProfessionalProfilePanel({ copy, sectors, countries = [], regionsByCountry = {}, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }) {
21
30
  const [method, setMethod] = useState('credential');
22
31
  const [professionalCredential, setProfessionalCredential] = useState('');
23
32
  const [professionalIdentifier, setProfessionalIdentifier] = useState('');
24
33
  const [sector, setSector] = useState(sectors[0]?.value || '');
34
+ const [country, setCountry] = useState('');
35
+ const [regional, setRegional] = useState(false);
36
+ const [region, setRegion] = useState('');
37
+ const [organizationIdentifier, setOrganizationIdentifier] = useState('');
25
38
  const [pin, setPin] = useState('');
26
39
  const [pinConfirmation, setPinConfirmation] = useState('');
27
- const [resolvedProfile, setResolvedProfile] = useState();
40
+ const [resolvedProfiles, setResolvedProfiles] = useState([]);
41
+ const [selectedProfile, setSelectedProfile] = useState();
28
42
  const [resolving, setResolving] = useState(false);
29
43
  const [localError, setLocalError] = useState('');
30
44
  const [cameraActive, setCameraActive] = useState(false);
@@ -116,6 +130,14 @@ export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, o
116
130
  }
117
131
  return { method, professionalCredential: professionalCredential.trim() };
118
132
  }
133
+ if (countries.length)
134
+ return {
135
+ method: 'organization',
136
+ country,
137
+ ...(regional ? { region } : {}),
138
+ organizationIdentifier: organizationIdentifier.trim(),
139
+ sector,
140
+ };
119
141
  return { method, professionalIdentifier: professionalIdentifier.trim(), sector };
120
142
  }
121
143
  async function submit(event) {
@@ -123,11 +145,14 @@ export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, o
123
145
  const evidence = evidenceInput();
124
146
  if (!evidence)
125
147
  return;
126
- if (onResolve && !resolvedProfile) {
148
+ if (onResolve && resolvedProfiles.length === 0) {
127
149
  setLocalError('');
128
150
  setResolving(true);
129
151
  try {
130
- setResolvedProfile(await onResolve(evidence));
152
+ const resolution = await onResolve(evidence);
153
+ const profiles = Array.isArray(resolution) ? resolution : [resolution];
154
+ setResolvedProfiles(profiles);
155
+ setSelectedProfile(profiles.length === 1 ? profiles[0] : undefined);
131
156
  }
132
157
  catch (reason) {
133
158
  setLocalError(reason instanceof Error ? reason.message : copy.verificationFailed || 'professional_profile_verification_failed');
@@ -146,15 +171,24 @@ export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, o
146
171
  void onSubmit({ ...evidence, pin });
147
172
  return;
148
173
  }
174
+ if (evidence.method === 'organization') {
175
+ if (!selectedProfile?.selectionValue) {
176
+ setLocalError(copy.verificationFailed || 'professional_profile_selection_required');
177
+ return;
178
+ }
179
+ void onSubmit({ ...evidence, selectedProfile: selectedProfile.selectionValue, pin });
180
+ return;
181
+ }
149
182
  void onSubmit({ ...evidence, pin });
150
183
  }
151
- if (onResolve && resolvedProfile)
152
- return _jsxs("section", { className: "uhcp-professional-activation uhcp-professional-pin-setup", "aria-labelledby": "uhcp-existing-professional-pin-title", children: [_jsxs("header", { children: [_jsx("h1", { id: "uhcp-existing-professional-pin-title", children: copy.verifiedProfile || copy.title }), _jsx("p", { children: copy.description })] }), _jsx(ProfileIdentityTarget, { target: { eyebrow: copy.verifiedProfile || '', ...resolvedProfile } }), _jsxs("form", { onSubmit: submit, children: [_jsxs("label", { children: [_jsx("span", { children: copy.pinLabel }), _jsx("input", { name: "newPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pin, onChange: event => setPin(event.currentTarget.value), required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.pinConfirmationLabel }), _jsx("input", { name: "confirmPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pinConfirmation, onChange: event => setPinConfirmation(event.currentTarget.value), required: true })] }), (localError || error) ? _jsx("p", { role: "alert", children: localError || error }) : null, _jsx("button", { type: "submit", disabled: busy, children: busy ? copy.submitting : copy.submit }), _jsx("button", { type: "button", className: "uhcp-secondary-action", disabled: busy, onClick: () => { setResolvedProfile(undefined); setPin(''); setPinConfirmation(''); setLocalError(''); }, children: copy.changeEvidence || copy.manualMethodLabel })] })] });
184
+ if (onResolve && resolvedProfiles.length)
185
+ return _jsxs("section", { className: "uhcp-professional-activation uhcp-professional-pin-setup", "aria-labelledby": "uhcp-existing-professional-pin-title", children: [_jsxs("header", { children: [_jsx("h1", { id: "uhcp-existing-professional-pin-title", children: copy.verifiedProfile || copy.title }), _jsx("p", { children: copy.description })] }), resolvedProfiles.length > 1 ? _jsx("div", { className: "uhcp-resolved-professional-options", role: "group", "aria-label": copy.verifiedProfile || copy.title, children: resolvedProfiles.map(profile => _jsx("button", { type: "button", "aria-pressed": selectedProfile === profile, onClick: () => setSelectedProfile(profile), children: _jsx(ProfileIdentityTarget, { target: { eyebrow: copy.verifiedProfile || '', ...profile } }) }, profile.selectionValue || profile.title)) }) : selectedProfile ? _jsx(ProfileIdentityTarget, { target: { eyebrow: copy.verifiedProfile || '', ...selectedProfile } }) : null, _jsxs("form", { onSubmit: submit, children: [selectedProfile ? _jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: copy.pinLabel }), _jsx("input", { name: "newPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pin, onChange: event => setPin(event.currentTarget.value), required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.pinConfirmationLabel }), _jsx("input", { name: "confirmPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pinConfirmation, onChange: event => setPinConfirmation(event.currentTarget.value), required: true })] })] }) : null, (localError || error) ? _jsx("p", { role: "alert", children: localError || error }) : null, selectedProfile ? _jsx("button", { type: "submit", disabled: busy, children: busy ? copy.submitting : copy.submit }) : null, _jsx("button", { type: "button", className: "uhcp-secondary-action", disabled: busy, onClick: () => { setResolvedProfiles([]); setSelectedProfile(undefined); setPin(''); setPinConfirmation(''); setLocalError(''); }, children: copy.changeEvidence || copy.manualMethodLabel })] })] });
153
186
  return _jsx("section", { className: "uhcp-professional-activation", "aria-labelledby": "uhcp-existing-professional-title", children: _jsxs("form", { onSubmit: submit, children: [_jsxs("header", { children: [copy.eyebrow ? _jsx("p", { className: "uhcp-eyebrow", children: copy.eyebrow }) : null, _jsx("h1", { id: "uhcp-existing-professional-title", children: copy.title }), _jsx("p", { children: copy.description })] }), _jsxs("fieldset", { className: "uhcp-professional-evidence-methods", children: [_jsxs("label", { children: [_jsx("input", { type: "radio", name: "professionalEvidenceMethod", value: "credential", checked: method === 'credential', onChange: () => { setMethod('credential'); setLocalError(''); } }), _jsx("span", { children: copy.credentialMethodLabel })] }), _jsxs("label", { children: [_jsx("input", { type: "radio", name: "professionalEvidenceMethod", value: "identifier", checked: method === 'identifier', onChange: () => { setMethod('identifier'); setLocalError(''); } }), _jsx("span", { children: copy.manualMethodLabel })] })] }), method === 'credential'
154
187
  ? _jsxs("div", { className: "uhcp-professional-credential-capture", children: [_jsx("button", { type: "button", disabled: busy, onClick: () => void captureCredential(scanWithCamera).catch(() => setLocalError(copy.professionalCredentialRequired)), children: copy.scanProfessionalCredential }), _jsxs("label", { children: [_jsx("span", { children: copy.uploadProfessionalCredentialQr }), _jsx("input", { name: "professionalCredentialQrImage", type: "file", accept: "image/png,image/jpeg,image/webp", disabled: busy, onChange: event => {
155
188
  const file = event.currentTarget.files?.[0];
156
189
  if (file)
157
190
  void captureCredential(() => readCredentialImage(file)).catch(() => setLocalError(copy.credentialQrUnreadable));
158
191
  } })] }), cameraActive ? _jsxs(_Fragment, { children: [_jsx("video", { ref: videoRef, playsInline: true, muted: true, "aria-label": copy.cameraActive }), _jsx("p", { role: "status", children: copy.cameraActive }), _jsx("button", { type: "button", onClick: () => stopCamera(), children: copy.closeCamera })] }) : _jsx("video", { ref: videoRef, hidden: true, playsInline: true, muted: true }), _jsx("canvas", { ref: canvasRef, hidden: true, "aria-hidden": "true" }), professionalCredential ? _jsx("p", { role: "status", children: copy.professionalCredentialReady }) : null] })
159
- : _jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: copy.professionalIdentifierLabel }), _jsx("input", { name: "professionalIdentifier", value: professionalIdentifier, onChange: event => setProfessionalIdentifier(event.currentTarget.value), autoComplete: "off", required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.sectorLabel }), _jsx("select", { name: "professionalSector", value: sector, onChange: event => setSector(event.currentTarget.value), required: true, children: sectors.map(option => _jsx("option", { value: option.value, children: option.label }, option.value)) })] })] }), !onResolve ? _jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: copy.pinLabel }), _jsx("input", { name: "newPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pin, onChange: event => setPin(event.currentTarget.value), required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.pinConfirmationLabel }), _jsx("input", { name: "confirmPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pinConfirmation, onChange: event => setPinConfirmation(event.currentTarget.value), required: true })] })] }) : null, (localError || error) ? _jsx("p", { role: "alert", children: localError || error }) : null, _jsx("button", { type: "submit", disabled: busy || resolving, children: resolving ? copy.verifying || copy.submitting : onResolve ? copy.verify || copy.submit : busy ? copy.submitting : copy.submit })] }) });
192
+ : _jsxs(_Fragment, { children: [countries.length ? _jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: copy.countryLabel || 'Country' }), _jsxs("select", { name: "professionalCountry", value: country, onChange: event => { setCountry(event.currentTarget.value); setRegional(false); setRegion(''); }, required: true, children: [_jsx("option", { value: "", disabled: true, children: copy.countryPlaceholder || copy.countryLabel || 'Country' }), countries.map(option => _jsx("option", { value: option.value, children: option.label }, option.value))] })] }), _jsxs("label", { className: "uhcp-professional-regional-identifier", children: [_jsx("input", { type: "checkbox", checked: regional, disabled: !country || !(regionsByCountry[country]?.length), onChange: event => { setRegional(event.currentTarget.checked); if (!event.currentTarget.checked)
193
+ setRegion(''); } }), _jsx("span", { children: copy.regionalIdentifierLabel || 'The legal identifier is regional' })] }), regional ? _jsxs("label", { children: [_jsx("span", { children: copy.regionLabel || 'Region' }), _jsxs("select", { name: "professionalRegion", value: region, onChange: event => setRegion(event.currentTarget.value), required: true, children: [_jsx("option", { value: "", disabled: true, children: copy.regionPlaceholder || copy.regionLabel || 'Region' }), (regionsByCountry[country] || []).map(option => _jsx("option", { value: option.value, children: option.label }, option.value))] })] }) : null, _jsxs("label", { children: [_jsx("span", { children: copy.organizationIdentifierLabel || 'Organization legal identifier' }), _jsx("input", { name: "organizationIdentifier", value: organizationIdentifier, onChange: event => setOrganizationIdentifier(event.currentTarget.value), autoComplete: "off", required: true })] })] }) : _jsxs("label", { children: [_jsx("span", { children: copy.professionalIdentifierLabel }), _jsx("input", { name: "professionalIdentifier", value: professionalIdentifier, onChange: event => setProfessionalIdentifier(event.currentTarget.value), autoComplete: "off", required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.sectorLabel }), _jsx("select", { name: "professionalSector", value: sector, onChange: event => setSector(event.currentTarget.value), required: true, children: sectors.map(option => _jsx("option", { value: option.value, children: option.label }, option.value)) })] })] }), !onResolve ? _jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: copy.pinLabel }), _jsx("input", { name: "newPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pin, onChange: event => setPin(event.currentTarget.value), required: true })] }), _jsxs("label", { children: [_jsx("span", { children: copy.pinConfirmationLabel }), _jsx("input", { name: "confirmPin", type: "password", inputMode: "numeric", minLength: 6, maxLength: 12, autoComplete: "new-password", value: pinConfirmation, onChange: event => setPinConfirmation(event.currentTarget.value), required: true })] })] }) : null, (localError || error) ? _jsx("p", { role: "alert", children: localError || error }) : null, _jsx("button", { type: "submit", disabled: busy || resolving, children: resolving ? copy.verifying || copy.submitting : onResolve ? copy.verify || copy.submit : busy ? copy.submitting : copy.submit })] }) });
160
194
  }
@@ -6,6 +6,7 @@ export type PortalHeaderProps = Readonly<{
6
6
  accountIdentifier?: string | null;
7
7
  languageHref?: string;
8
8
  languageOptions?: readonly PortalLanguageOption[];
9
+ languageInPreferences?: boolean;
9
10
  guestActionHref: string;
10
11
  accountOptions?: readonly PortalAccountOption[];
11
12
  onSelectAccount?: (id: string) => void;
@@ -21,5 +22,5 @@ export type PortalHeaderProps = Readonly<{
21
22
  * Authentication is deliberately supplied as data and callbacks. The
22
23
  * component never imports Firebase and never treats browser state as a role.
23
24
  */
24
- export declare function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions, guestActionHref, accountOptions, onSelectAccount, menuActions, onSignOut, onThemeChange, storageNamespace, currentSubject, }: PortalHeaderProps): import("react").JSX.Element;
25
+ export declare function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions, languageInPreferences, guestActionHref, accountOptions, onSelectAccount, menuActions, onSignOut, onThemeChange, storageNamespace, currentSubject, }: PortalHeaderProps): import("react").JSX.Element;
25
26
  //# sourceMappingURL=PortalHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PortalHeader.d.ts","sourceRoot":"","sources":["../src/PortalHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAEpB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAQvB,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,2BAA2B,CAAA;AAElC,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,eAAe,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACjD,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,gCAAgC,CAAA;CAClD,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,YAAY,EACZ,eAAoB,EACpB,eAAe,EACf,cAAmB,EACnB,eAAe,EACf,WAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAA+B,EAC/B,cAAc,GACf,EAAE,iBAAiB,+BAgMnB"}
1
+ {"version":3,"file":"PortalHeader.d.ts","sourceRoot":"","sources":["../src/PortalHeader.tsx"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAEpB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAQvB,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,2BAA2B,CAAA;AAElC,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,eAAe,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACjD,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,gCAAgC,CAAA;CAClD,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,YAAY,EACZ,eAAoB,EACpB,qBAA6B,EAC7B,eAAe,EACf,cAAmB,EACnB,eAAe,EACf,WAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAA+B,EAC/B,cAAc,GACf,EAAE,iBAAiB,+BAwMnB"}
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { portalUiDensities, portalUiThemes, } from './contracts.js';
5
5
  import { resolvePortalUiDensity, resolvePortalUiTheme, resolveRenderedPortalTheme, } from './preferences.js';
@@ -12,13 +12,15 @@ import { PortalCurrentSubjectControl, PortalCurrentSubjectSlot, } from './Portal
12
12
  * Authentication is deliberately supplied as data and callbacks. The
13
13
  * component never imports Firebase and never treats browser state as a role.
14
14
  */
15
- export function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions = [], guestActionHref, accountOptions = [], onSelectAccount, menuActions = [], onSignOut, onThemeChange, storageNamespace = 'uhc-portal', currentSubject, }) {
15
+ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, languageOptions = [], languageInPreferences = false, guestActionHref, accountOptions = [], onSelectAccount, menuActions = [], onSignOut, onThemeChange, storageNamespace = 'uhc-portal', currentSubject, }) {
16
16
  const [open, setOpen] = useState(false);
17
17
  const [density, setDensity] = useState('medium');
18
18
  const [theme, setTheme] = useState('system');
19
19
  const ref = useRef(null);
20
20
  const densityKey = `${storageNamespace}.density`;
21
21
  const themeKey = `${storageNamespace}.theme`;
22
+ const signedIn = Boolean(accountIdentifier);
23
+ const menuLabel = signedIn ? copy.accountAndPreferences : copy.chooseAccess;
22
24
  useEffect(() => {
23
25
  const savedDensity = resolvePortalUiDensity(localStorage.getItem(densityKey));
24
26
  const savedTheme = resolvePortalUiTheme(localStorage.getItem(themeKey));
@@ -69,12 +71,15 @@ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, lan
69
71
  }
70
72
  return (_jsxs("header", { className: "uhcp-header", style: brand.variables, children: [brand.mark && brand.markHref ? (_jsxs("div", { className: "uhcp-brand", children: [_jsx("a", { className: "uhcp-brand-mark-link", href: brand.markHref, "aria-label": brand.productName, children: _jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" }) }), _jsx("a", { className: "uhcp-brand-copy", href: brand.homeHref, "aria-label": copy.home, children: _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { className: accountIdentifier ? 'uhcp-brand-account' : undefined, children: accountIdentifier || brand.organizationName })] }) })] })) : (_jsxs("a", { className: "uhcp-brand", href: brand.homeHref, "aria-label": copy.home, children: [brand.mark ? (_jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" })) : null, _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { className: accountIdentifier ? 'uhcp-brand-account' : undefined, children: accountIdentifier || brand.organizationName })] })] })), currentSubject
71
73
  ? _jsx(PortalCurrentSubjectControl, { ...currentSubject })
72
- : _jsx(PortalCurrentSubjectSlot, {}), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsx("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": copy.accountAndPreferences, "aria-expanded": open, children: _jsx(ProfileIdentityIcon, {}) }), open && (_jsxs("section", { className: "uhcp-menu", "aria-label": copy.accountAndPreferences, children: [_jsxs("div", { className: "uhcp-account-state", children: [_jsx("strong", { children: copy.accountAndPreferences }), _jsx("small", { children: accountIdentifier || copy.notSignedIn })] }), accountOptions.length > 0 && (_jsx("div", { className: "uhcp-account-options", children: accountOptions.map((option) => (_jsxs("button", { type: "button", disabled: option.disabled, "aria-pressed": Boolean(option.selected), onClick: () => {
74
+ : _jsx(PortalCurrentSubjectSlot, {}), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsx("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": menuLabel, "aria-expanded": open, children: _jsx(ProfileIdentityIcon, {}) }), open && (_jsxs("section", { className: "uhcp-menu", "aria-label": menuLabel, children: [_jsxs("div", { className: "uhcp-account-state", children: [_jsx("strong", { children: menuLabel }), _jsx("small", { children: accountIdentifier || copy.notSignedIn })] }), signedIn && accountOptions.length > 0 && (_jsx("div", { className: "uhcp-account-options", children: accountOptions.map((option) => (_jsxs("button", { type: "button", disabled: option.disabled, "aria-pressed": Boolean(option.selected), onClick: () => {
73
75
  onSelectAccount?.(option.id);
74
76
  setOpen(false);
75
- }, children: [_jsx("span", { children: option.label }), option.description && _jsx("small", { children: option.description })] }, option.id))) })), _jsx(PreferenceGroup, { label: copy.appearance, children: portalUiThemes.map((value) => (_jsxs("button", { type: "button", "aria-pressed": theme === value, onClick: () => changeTheme(value), children: [_jsx("span", { "aria-hidden": "true", children: value === 'light' ? '☀' : value === 'dark' ? '☾' : '◫' }), copy.themes[value]] }, value))) }), _jsx(PreferenceGroup, { label: copy.textSize, children: portalUiDensities.map((value) => (_jsx("button", { type: "button", "aria-pressed": density === value, onClick: () => changeDensity(value), children: value === 'small' ? 'S' : value === 'medium' ? 'M' : 'L' }, value))) }), _jsxs("div", { className: "uhcp-menu-actions", children: [languageOptions.length > 0
76
- ? languageOptions.map((option) => (_jsxs("a", { href: option.href, hrefLang: option.id, "aria-current": option.current ? 'page' : undefined, onClick: () => setOpen(false), children: ["\u25CE ", option.label] }, option.id)))
77
- : languageHref && _jsxs("a", { href: languageHref, onClick: () => setOpen(false), children: ["\u25CE ", copy.language] }), menuActions.map((action) => action.href
77
+ }, children: [_jsx("span", { children: option.label }), option.description && _jsx("small", { children: option.description })] }, option.id))) })), signedIn ? _jsxs(_Fragment, { children: [_jsx(PreferenceGroup, { label: copy.appearance, children: portalUiThemes.map((value) => (_jsxs("button", { type: "button", "aria-pressed": theme === value, onClick: () => changeTheme(value), children: [_jsx("span", { "aria-hidden": "true", children: value === 'light' ? '☀' : value === 'dark' ? '☾' : '◫' }), copy.themes[value]] }, value))) }), _jsx(PreferenceGroup, { label: copy.textSize, children: portalUiDensities.map((value) => (_jsx("button", { type: "button", "aria-pressed": density === value, onClick: () => changeDensity(value), children: value === 'small' ? 'S' : value === 'medium' ? 'M' : 'L' }, value))) })] }) : null, _jsxs("div", { className: "uhcp-menu-actions", children: [(!signedIn || languageInPreferences) && languageOptions.length > 0
78
+ ? _jsxs("label", { className: "uhcp-language-select", children: [_jsx("span", { children: copy.language }), _jsx("select", { "aria-label": copy.language, value: languageOptions.find((option) => option.current)?.href ?? languageOptions[0]?.href, onChange: (event) => {
79
+ setOpen(false);
80
+ window.location.assign(event.currentTarget.value);
81
+ }, children: languageOptions.map((option) => (_jsx("option", { value: option.href, children: option.label }, option.id))) })] })
82
+ : (!signedIn || languageInPreferences) && languageHref && _jsxs("a", { href: languageHref, onClick: () => setOpen(false), children: ["\u25CE ", copy.language] }), signedIn && menuActions.map((action) => action.href
78
83
  ? (_jsx("a", { href: action.href, onClick: () => setOpen(false), children: action.label }, action.id))
79
84
  : (_jsx("button", { type: "button", onClick: () => {
80
85
  setOpen(false);
package/dist/styles.css CHANGED
@@ -169,6 +169,8 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
169
169
  width: 100%;
170
170
  }
171
171
  .uhcp-menu-actions a:hover, .uhcp-menu-actions button:hover { background: var(--uhcp-surface-muted); color: var(--uhcp-turquoise); }
172
+ .uhcp-language-select { align-items: center; color: var(--uhcp-text); display: grid; font-weight: 700; gap: .45rem; grid-template-columns: auto minmax(0, 1fr); padding: .45rem .7rem; }
173
+ .uhcp-language-select select { background: var(--uhcp-panel); border: 1px solid var(--uhcp-border); border-radius: .75rem; color: var(--uhcp-text); font: inherit; min-width: 0; padding: .6rem 2rem .6rem .7rem; width: 100%; }
172
174
  .uhcp-profile-gate {
173
175
  background: var(--uhcp-panel);
174
176
  border: 1px solid var(--uhcp-border);
@@ -216,6 +218,11 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
216
218
  .uhcp-professional-evidence-methods { border: 0; display: grid; gap: .75rem; margin: 0; padding: 0; }
217
219
  .uhcp-professional-evidence-methods label { align-items: center; border: 1px solid var(--uhcp-border); border-radius: .9rem; cursor: pointer; display: grid; gap: .75rem; grid-template-columns: 1.25rem 1fr; padding: .9rem 1rem; }
218
220
  .uhcp-professional-evidence-methods input[type='radio'] { accent-color: var(--uhcp-turquoise); height: 1.1rem; margin: 0; width: 1.1rem; }
221
+ .uhcp-professional-regional-identifier { align-items: center !important; display: flex !important; gap: .55rem; }
222
+ .uhcp-professional-regional-identifier input { accent-color: var(--uhcp-turquoise); min-height: auto !important; width: auto !important; }
223
+ .uhcp-resolved-professional-options { display: grid; gap: .75rem; }
224
+ .uhcp-resolved-professional-options > button { background: transparent; border: 1px solid var(--uhcp-border); border-radius: 1rem; color: inherit; cursor: pointer; padding: 0; text-align: left; }
225
+ .uhcp-resolved-professional-options > button[aria-pressed='true'] { border-color: var(--uhcp-turquoise); box-shadow: 0 0 0 2px color-mix(in srgb, var(--uhcp-turquoise) 25%, transparent); }
219
226
  .uhcp-professional-pin-setup { display: grid; gap: 1rem; }
220
227
  .uhcp-professional-pin-setup > header h1, .uhcp-professional-pin-setup > header p { margin: 0; }
221
228
  .uhcp-professional-pin-setup > header p { margin-top: .45rem; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Brand-neutral React portal screens, account preferences and protected-profile UI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "fhir-data-utils-ts": "0.6.21",
35
35
  "gdc-common-utils-ts": "2.9.30",
36
36
  "jsqr": "1.4.0",
37
- "sos-data-utils-ts": "0.6.15",
37
+ "sos-data-utils-ts": "0.6.21",
38
38
  "terminology-components-react": "0.2.0"
39
39
  },
40
40
  "devDependencies": {