uhc-portal-components-react 0.2.36 → 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,30 @@
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.
20
+ ## 0.2.37 - 2026-11-03
21
+
22
+ - Split existing-professional registration into evidence verification and
23
+ device-PIN setup so the verified role and organization are visible before a
24
+ local profile can be protected.
25
+ - Reuse one identity icon and resolved-profile target across the portal header,
26
+ profile unlock and professional registration screens.
27
+
3
28
  ## 0.2.36 - 2026-11-03
4
29
 
5
30
  - Add an accessible shared card-provider selector that returns only the opaque
package/README.md CHANGED
@@ -135,9 +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. The host verifies the evidence before DCR; this screen never
140
- 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.
141
144
  - `SubjectAccessRequestPanel`: controlled provider-enabled QR, numeric-code,
142
145
  `did:web`, microchip, tattoo, email or telephone locator. Resolution and
143
146
  returned identifier evidence never grant subject access.
@@ -1,3 +1,4 @@
1
+ import type { ProfileProtectionTargetV2 } from './contracts.js';
1
2
  export type ExistingProfessionalProfileCopy = Readonly<{
2
3
  eyebrow?: string;
3
4
  title: string;
@@ -12,10 +13,21 @@ export type ExistingProfessionalProfileCopy = Readonly<{
12
13
  cameraActive: string;
13
14
  credentialQrUnreadable: string;
14
15
  professionalIdentifierLabel: string;
16
+ countryLabel?: string;
17
+ countryPlaceholder?: string;
18
+ regionalIdentifierLabel?: string;
19
+ regionLabel?: string;
20
+ regionPlaceholder?: string;
21
+ organizationIdentifierLabel?: string;
15
22
  sectorLabel: string;
16
23
  pinLabel: string;
17
24
  pinConfirmationLabel: string;
18
25
  pinMismatch: string;
26
+ verify?: string;
27
+ verifying?: string;
28
+ verifiedProfile?: string;
29
+ changeEvidence?: string;
30
+ verificationFailed?: string;
19
31
  submit: string;
20
32
  submitting: string;
21
33
  }>;
@@ -28,6 +40,32 @@ export type ExistingProfessionalProfileInput = Readonly<{
28
40
  professionalIdentifier: string;
29
41
  sector: string;
30
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;
51
+ }>;
52
+ export type ExistingProfessionalEvidenceInput = Readonly<{
53
+ method: 'credential';
54
+ professionalCredential: string;
55
+ }> | Readonly<{
56
+ method: 'identifier';
57
+ professionalIdentifier: string;
58
+ sector: string;
59
+ }> | Readonly<{
60
+ method: 'organization';
61
+ country: string;
62
+ region?: string;
63
+ organizationIdentifier: string;
64
+ sector: string;
65
+ }>;
66
+ export type ResolvedExistingProfessionalProfile = Omit<ProfileProtectionTargetV2, 'eyebrow'> & Readonly<{
67
+ eyebrow?: string;
68
+ selectionValue?: string;
31
69
  }>;
32
70
  export type ExistingProfessionalProfileRequest = Readonly<{
33
71
  professionalCredential: string;
@@ -36,6 +74,13 @@ export type ExistingProfessionalProfileRequest = Readonly<{
36
74
  professionalIdentifier: string;
37
75
  sectorHint: string;
38
76
  pin: string;
77
+ }> | Readonly<{
78
+ country: string;
79
+ region?: string;
80
+ organizationIdentifier: string;
81
+ sectorHint: string;
82
+ selectedProfile: string;
83
+ pin: string;
39
84
  }>;
40
85
  /** Shared BFF serialization keeps professional evidence distinct from an invitation code. */
41
86
  export declare function toExistingProfessionalProfileRequest(input: ExistingProfessionalProfileInput): ExistingProfessionalProfileRequest;
@@ -43,13 +88,20 @@ export type ExistingProfessionalSectorOption = Readonly<{
43
88
  value: string;
44
89
  label: string;
45
90
  }>;
91
+ export type ExistingProfessionalJurisdictionOption = Readonly<{
92
+ value: string;
93
+ label: string;
94
+ }>;
46
95
  export type ExistingProfessionalProfilePanelProps = Readonly<{
47
96
  copy: ExistingProfessionalProfileCopy;
48
97
  sectors: readonly ExistingProfessionalSectorOption[];
98
+ countries?: readonly ExistingProfessionalJurisdictionOption[];
99
+ regionsByCountry?: Readonly<Record<string, readonly ExistingProfessionalJurisdictionOption[]>>;
49
100
  busy: boolean;
50
101
  error?: string;
51
102
  onScanProfessionalCredential?: () => Promise<string | undefined>;
52
103
  onReadProfessionalCredentialImage?: (file: File) => Promise<string | undefined>;
104
+ onResolve?: (input: ExistingProfessionalEvidenceInput) => Promise<ResolvedExistingProfessionalProfile | readonly ResolvedExistingProfessionalProfile[]>;
53
105
  onSubmit: (input: ExistingProfessionalProfileInput) => void | Promise<void>;
54
106
  }>;
55
107
  /**
@@ -59,5 +111,5 @@ export type ExistingProfessionalProfilePanelProps = Readonly<{
59
111
  * is the preferred path; a single DID-or-URN field plus sector is the explicit
60
112
  * manual fallback and never becomes browser-selected authority by itself.
61
113
  */
62
- export declare function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, 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;
63
115
  //# sourceMappingURL=ExistingProfessionalProfilePanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExistingProfessionalProfilePanel.d.ts","sourceRoot":"","sources":["../src/ExistingProfessionalProfilePanel.tsx"],"names":[],"mappings":"AAWA,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,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,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,CAAA;AAEnG,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,CAAA;AAEjF,6FAA6F;AAC7F,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,gCAAgC,GAAG,kCAAkC,CAIhI;AAED,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEzF,MAAM,MAAM,qCAAqC,GAAG,QAAQ,CAAC;IAC3D,IAAI,EAAE,+BAA+B,CAAA;IACrC,OAAO,EAAE,SAAS,gCAAgC,EAAE,CAAA;IACpD,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,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,IAAI,EAAE,KAAK,EAAE,4BAA4B,EAAE,iCAAiC,EAAE,QAAQ,EAAE,EAAE,qCAAqC,+BAuIhM"}
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"}
@@ -2,12 +2,22 @@
2
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 jsQrModule from 'jsqr';
5
+ import { ProfileIdentityTarget } from './ProfileIdentityTarget.js';
5
6
  const decodeQrImage = jsQrModule;
6
7
  /** Shared BFF serialization keeps professional evidence distinct from an invitation code. */
7
8
  export function toExistingProfessionalProfileRequest(input) {
8
- return input.method === 'credential'
9
- ? Object.freeze({ professionalCredential: input.professionalCredential, pin: input.pin })
10
- : 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
+ });
11
21
  }
12
22
  /**
13
23
  * Registers this portal device for an already-active professional profile.
@@ -16,13 +26,20 @@ export function toExistingProfessionalProfileRequest(input) {
16
26
  * is the preferred path; a single DID-or-URN field plus sector is the explicit
17
27
  * manual fallback and never becomes browser-selected authority by itself.
18
28
  */
19
- export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onSubmit }) {
29
+ export function ExistingProfessionalProfilePanel({ copy, sectors, countries = [], regionsByCountry = {}, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }) {
20
30
  const [method, setMethod] = useState('credential');
21
31
  const [professionalCredential, setProfessionalCredential] = useState('');
22
32
  const [professionalIdentifier, setProfessionalIdentifier] = useState('');
23
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('');
24
38
  const [pin, setPin] = useState('');
25
39
  const [pinConfirmation, setPinConfirmation] = useState('');
40
+ const [resolvedProfiles, setResolvedProfiles] = useState([]);
41
+ const [selectedProfile, setSelectedProfile] = useState();
42
+ const [resolving, setResolving] = useState(false);
26
43
  const [localError, setLocalError] = useState('');
27
44
  const [cameraActive, setCameraActive] = useState(false);
28
45
  const videoRef = useRef(null);
@@ -105,28 +122,73 @@ export function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, o
105
122
  }
106
123
  setProfessionalCredential(credential);
107
124
  }
108
- function submit(event) {
125
+ function evidenceInput() {
126
+ if (method === 'credential') {
127
+ if (!professionalCredential) {
128
+ setLocalError(copy.professionalCredentialRequired);
129
+ return undefined;
130
+ }
131
+ return { method, professionalCredential: professionalCredential.trim() };
132
+ }
133
+ if (countries.length)
134
+ return {
135
+ method: 'organization',
136
+ country,
137
+ ...(regional ? { region } : {}),
138
+ organizationIdentifier: organizationIdentifier.trim(),
139
+ sector,
140
+ };
141
+ return { method, professionalIdentifier: professionalIdentifier.trim(), sector };
142
+ }
143
+ async function submit(event) {
109
144
  event.preventDefault();
145
+ const evidence = evidenceInput();
146
+ if (!evidence)
147
+ return;
148
+ if (onResolve && resolvedProfiles.length === 0) {
149
+ setLocalError('');
150
+ setResolving(true);
151
+ try {
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);
156
+ }
157
+ catch (reason) {
158
+ setLocalError(reason instanceof Error ? reason.message : copy.verificationFailed || 'professional_profile_verification_failed');
159
+ }
160
+ finally {
161
+ setResolving(false);
162
+ }
163
+ return;
164
+ }
110
165
  if (pin !== pinConfirmation) {
111
166
  setLocalError(copy.pinMismatch);
112
167
  return;
113
168
  }
114
169
  setLocalError('');
115
- if (method === 'credential') {
116
- if (!professionalCredential) {
117
- setLocalError(copy.professionalCredentialRequired);
170
+ if (evidence.method === 'credential') {
171
+ void onSubmit({ ...evidence, pin });
172
+ return;
173
+ }
174
+ if (evidence.method === 'organization') {
175
+ if (!selectedProfile?.selectionValue) {
176
+ setLocalError(copy.verificationFailed || 'professional_profile_selection_required');
118
177
  return;
119
178
  }
120
- void onSubmit({ method, professionalCredential: professionalCredential.trim(), pin });
179
+ void onSubmit({ ...evidence, selectedProfile: selectedProfile.selectionValue, pin });
121
180
  return;
122
181
  }
123
- void onSubmit({ method, professionalIdentifier: professionalIdentifier.trim(), sector, pin });
182
+ void onSubmit({ ...evidence, pin });
124
183
  }
125
- 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", { children: [_jsxs("label", { children: [_jsx("input", { type: "radio", name: "professionalEvidenceMethod", value: "credential", checked: method === 'credential', onChange: () => setMethod('credential') }), copy.credentialMethodLabel] }), _jsxs("label", { children: [_jsx("input", { type: "radio", name: "professionalEvidenceMethod", value: "identifier", checked: method === 'identifier', onChange: () => setMethod('identifier') }), copy.manualMethodLabel] })] }), method === 'credential'
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 })] })] });
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'
126
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 => {
127
188
  const file = event.currentTarget.files?.[0];
128
189
  if (file)
129
190
  void captureCredential(() => readCredentialImage(file)).catch(() => setLocalError(copy.credentialQrUnreadable));
130
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] })
131
- : _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)) })] })] }), _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 })] }) });
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 })] }) });
132
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;AAOvB,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,9 +1,10 @@
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';
6
6
  import { PortalBrandArtwork } from './PortalBrandArtwork.js';
7
+ import { ProfileIdentityIcon } from './ProfileIdentityTarget.js';
7
8
  import { PortalCurrentSubjectControl, PortalCurrentSubjectSlot, } from './PortalCurrentSubject.js';
8
9
  /**
9
10
  * Shared account/preferences header.
@@ -11,13 +12,15 @@ import { PortalCurrentSubjectControl, PortalCurrentSubjectSlot, } from './Portal
11
12
  * Authentication is deliberately supplied as data and callbacks. The
12
13
  * component never imports Firebase and never treats browser state as a role.
13
14
  */
14
- 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, }) {
15
16
  const [open, setOpen] = useState(false);
16
17
  const [density, setDensity] = useState('medium');
17
18
  const [theme, setTheme] = useState('system');
18
19
  const ref = useRef(null);
19
20
  const densityKey = `${storageNamespace}.density`;
20
21
  const themeKey = `${storageNamespace}.theme`;
22
+ const signedIn = Boolean(accountIdentifier);
23
+ const menuLabel = signedIn ? copy.accountAndPreferences : copy.chooseAccess;
21
24
  useEffect(() => {
22
25
  const savedDensity = resolvePortalUiDensity(localStorage.getItem(densityKey));
23
26
  const savedTheme = resolvePortalUiTheme(localStorage.getItem(themeKey));
@@ -68,23 +71,23 @@ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, lan
68
71
  }
69
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
70
73
  ? _jsx(PortalCurrentSubjectControl, { ...currentSubject })
71
- : _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(PersonIcon, {}) }), 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: () => {
72
75
  onSelectAccount?.(option.id);
73
76
  setOpen(false);
74
- }, 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
75
- ? 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)))
76
- : 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
77
83
  ? (_jsx("a", { href: action.href, onClick: () => setOpen(false), children: action.label }, action.id))
78
84
  : (_jsx("button", { type: "button", onClick: () => {
79
85
  setOpen(false);
80
86
  void action.onSelect?.();
81
87
  }, children: action.label }, action.id))), accountIdentifier && onSignOut
82
88
  ? _jsxs("button", { type: "button", onClick: () => { setOpen(false); void onSignOut(); }, children: ["\u21AA ", copy.signOut] })
83
- : _jsxs("a", { href: guestActionHref, onClick: () => setOpen(false), children: [_jsx(PersonIcon, {}), " ", copy.chooseAccess] })] })] }))] })] }));
89
+ : _jsxs("a", { href: guestActionHref, onClick: () => setOpen(false), children: [_jsx(ProfileIdentityIcon, {}), " ", copy.chooseAccess] })] })] }))] })] }));
84
90
  }
85
91
  function PreferenceGroup({ label, children }) {
86
92
  return _jsxs("fieldset", { className: "uhcp-preferences", children: [_jsx("legend", { children: label }), _jsx("div", { children: children })] });
87
93
  }
88
- function PersonIcon() {
89
- return (_jsxs("svg", { className: "uhcp-person-icon", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z", stroke: "currentColor", strokeWidth: "1.8" }), _jsx("path", { d: "M4.5 21a7.5 7.5 0 0 1 15 0", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })] }));
90
- }
@@ -0,0 +1,7 @@
1
+ import type { ProfileProtectionTargetV2 } from './contracts.js';
2
+ export declare function ProfileIdentityIcon(): import("react").JSX.Element;
3
+ /** Shared presentation for one host-resolved profile. It never selects or grants a role. */
4
+ export declare function ProfileIdentityTarget({ target }: Readonly<{
5
+ target: ProfileProtectionTargetV2;
6
+ }>): import("react").JSX.Element;
7
+ //# sourceMappingURL=ProfileIdentityTarget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileIdentityTarget.d.ts","sourceRoot":"","sources":["../src/ProfileIdentityTarget.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAA;AAE/D,wBAAgB,mBAAmB,gCAKlC;AAED,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,yBAAyB,CAAA;CAAE,CAAC,+BAkBhG"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function ProfileIdentityIcon() {
3
+ return _jsxs("svg", { "data-testid": "professional-identity-icon", className: "uhcp-person-icon", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z", stroke: "currentColor", strokeWidth: "1.8" }), _jsx("path", { d: "M4.5 21a7.5 7.5 0 0 1 15 0", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })] });
4
+ }
5
+ /** Shared presentation for one host-resolved profile. It never selects or grants a role. */
6
+ export function ProfileIdentityTarget({ target }) {
7
+ return _jsxs("div", { className: "uhcp-profile-target", children: [target.icon
8
+ ? _jsx("img", { src: target.icon.src, alt: target.icon.alt, width: target.icon.width, height: target.icon.height })
9
+ : _jsx("span", { className: "uhcp-profile-target-default-icon", children: _jsx(ProfileIdentityIcon, {}) }), _jsxs("div", { className: "uhcp-profile-target-content", children: [_jsxs("div", { className: "uhcp-profile-target-copy", children: [_jsx("strong", { children: target.title }), target.subtitle && _jsx("span", { children: target.subtitle })] }), target.badges?.length ? _jsx("ul", { className: "uhcp-profile-target-badges uhcp-profile-target-badges-centered", "aria-label": target.title, children: target.badges.map(badge => _jsx("li", { children: badge }, badge)) }) : null, target.details?.length ? _jsx("dl", { className: "uhcp-profile-target-details", children: target.details.map(detail => _jsxs("div", { children: [_jsx("dt", { children: detail.label }), _jsx("dd", { children: detail.value })] }, `${detail.label}:${detail.value}`)) }) : null] })] });
10
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileProtectionGateV2.d.ts","sourceRoot":"","sources":["../src/ProfileProtectionGateV2.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,KAAK,EACV,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,gBAAgB,CAAA;AAEvB,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,KAAK,EAAE,sBAAsB,CAAA;IAC7B,MAAM,EAAE,yBAAyB,CAAA;IACjC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC9E,eAAe,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GACT,EAAE,4BAA4B,+BAwH9B"}
1
+ {"version":3,"file":"ProfileProtectionGateV2.d.ts","sourceRoot":"","sources":["../src/ProfileProtectionGateV2.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA+C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,KAAK,EACV,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,gBAAgB,CAAA;AAGvB,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,KAAK,EAAE,sBAAsB,CAAA;IAC7B,MAAM,EAAE,yBAAyB,CAAA;IACjC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAC9E,eAAe,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAC7B,gBAAgB,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GACT,EAAE,4BAA4B,+BA0G9B"}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
+ import { ProfileIdentityTarget } from './ProfileIdentityTarget.js';
4
5
  /**
5
6
  * Presentation-only profile gate that identifies the host-authorized target.
6
7
  *
@@ -79,5 +80,5 @@ export function ProfileProtectionGateV2({ state, target, copy, onUnlock, onPassk
79
80
  : state === 'unavailable'
80
81
  ? copy.unavailableBody
81
82
  : copy.unlockBody;
82
- return (_jsxs("section", { className: "uhcp-profile-gate uhcp-profile-gate-v2", "aria-live": "polite", children: [_jsxs("div", { className: "uhcp-profile-gate-copy", children: [_jsx("h1", { children: title }), _jsx("p", { children: body })] }), _jsxs("div", { className: "uhcp-profile-target", children: [target.icon && _jsx("img", { src: target.icon.src, alt: target.icon.alt, width: target.icon.width, height: target.icon.height }), _jsxs("div", { className: "uhcp-profile-target-content", children: [_jsxs("div", { className: "uhcp-profile-target-copy", children: [_jsx("strong", { children: target.title }), target.subtitle && _jsx("span", { children: target.subtitle })] }), target.badges?.length ? _jsx("ul", { className: "uhcp-profile-target-badges uhcp-profile-target-badges-centered", "aria-label": target.title, children: target.badges.map(badge => _jsx("li", { children: badge }, badge)) }) : null, target.details?.length ? _jsx("dl", { className: "uhcp-profile-target-details", children: target.details.map(detail => _jsxs("div", { children: [_jsx("dt", { children: detail.label }), _jsx("dd", { children: detail.value })] }, `${detail.label}:${detail.value}`)) }) : null] })] }), state === 'locked' && onPasskeyUnlock && _jsx("button", { type: "button", className: "uhcp-action-unlock", "data-portal-action": "unlock-profile-with-passkey", disabled: busy, onClick: unlockWithPasskey, children: copy.unlockWithPasskey }), state === 'locked' && _jsxs("details", { className: "uhcp-profile-pin-fallback", open: !onPasskeyUnlock, children: [_jsx("summary", { children: onPasskeyUnlock ? copy.usePinInstead : copy.pinLabel }), _jsxs("form", { onSubmit: unlock, children: [_jsxs("label", { children: [copy.pinLabel, _jsx("input", { ref: input, type: "password", inputMode: "numeric", minLength: 6, autoComplete: "current-password", value: pin, onChange: (event) => setPin(event.target.value) })] }), error && _jsx("p", { role: "alert", className: "uhcp-error", children: error }), _jsx("button", { type: "submit", className: "uhcp-action-unlock", "data-portal-action": "unlock-profile", disabled: busy, children: copy.unlock })] }), pinRejected && pinFailureActions ? _jsx("div", { className: "uhcp-profile-pin-recovery", children: pinFailureActions }) : null] }), state === 'unavailable' && onRetry && _jsx("button", { type: "button", onClick: onRetry, children: copy.retry }), secondaryActions && _jsx("div", { className: "uhcp-profile-gate-secondary", children: secondaryActions })] }));
83
+ return (_jsxs("section", { className: "uhcp-profile-gate uhcp-profile-gate-v2", "aria-live": "polite", children: [_jsxs("div", { className: "uhcp-profile-gate-copy", children: [_jsx("h1", { children: title }), _jsx("p", { children: body })] }), _jsx(ProfileIdentityTarget, { target: target }), state === 'locked' && onPasskeyUnlock && _jsx("button", { type: "button", className: "uhcp-action-unlock", "data-portal-action": "unlock-profile-with-passkey", disabled: busy, onClick: unlockWithPasskey, children: copy.unlockWithPasskey }), state === 'locked' && _jsxs("details", { className: "uhcp-profile-pin-fallback", open: !onPasskeyUnlock, children: [_jsx("summary", { children: onPasskeyUnlock ? copy.usePinInstead : copy.pinLabel }), _jsxs("form", { onSubmit: unlock, children: [_jsxs("label", { children: [copy.pinLabel, _jsx("input", { ref: input, type: "password", inputMode: "numeric", minLength: 6, autoComplete: "current-password", value: pin, onChange: (event) => setPin(event.target.value) })] }), error && _jsx("p", { role: "alert", className: "uhcp-error", children: error }), _jsx("button", { type: "submit", className: "uhcp-action-unlock", "data-portal-action": "unlock-profile", disabled: busy, children: copy.unlock })] }), pinRejected && pinFailureActions ? _jsx("div", { className: "uhcp-profile-pin-recovery", children: pinFailureActions }) : null] }), state === 'unavailable' && onRetry && _jsx("button", { type: "button", onClick: onRetry, children: copy.retry }), secondaryActions && _jsx("div", { className: "uhcp-profile-gate-secondary", children: secondaryActions })] }));
83
84
  }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './PortalShell.js';
7
7
  export * from './PortalEntry.js';
8
8
  export * from './ProfileProtectionGate.js';
9
9
  export * from './ProfileProtectionGateV2.js';
10
+ export * from './ProfileIdentityTarget.js';
10
11
  export * from './PortalLoadingSkeleton.js';
11
12
  export * from './ClinicalSectionSummary.js';
12
13
  export * from './IpsBundleSummary.js';
@@ -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,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,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;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,uBAAuB,CAAA;AACrC,cAAc,0BAA0B,CAAA;AACxC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kBAAkB,CAAA;AAChC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,yCAAyC,CAAA;AACvD,cAAc,uCAAuC,CAAA;AACrD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,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,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,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;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,uBAAuB,CAAA;AACrC,cAAc,0BAA0B,CAAA;AACxC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kBAAkB,CAAA;AAChC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,yCAAyC,CAAA;AACvD,cAAc,uCAAuC,CAAA;AACrD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA"}
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export * from './PortalShell.js';
7
7
  export * from './PortalEntry.js';
8
8
  export * from './ProfileProtectionGate.js';
9
9
  export * from './ProfileProtectionGateV2.js';
10
+ export * from './ProfileIdentityTarget.js';
10
11
  export * from './PortalLoadingSkeleton.js';
11
12
  export * from './ClinicalSectionSummary.js';
12
13
  export * from './IpsBundleSummary.js';
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);
@@ -192,6 +194,8 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
192
194
  .uhcp-profile-gate-copy p { margin-top: .45rem; }
193
195
  .uhcp-profile-target { align-items: center; background: var(--uhcp-panel); border: 1px solid color-mix(in srgb, var(--uhcp-turquoise) 32%, var(--uhcp-border)); border-radius: 1rem; display: grid; gap: 1rem; grid-template-columns: auto minmax(0, 1fr); padding: 1rem; }
194
196
  .uhcp-profile-target > img { background: var(--uhcp-panel); border-radius: .75rem; object-fit: contain; }
197
+ .uhcp-profile-target-default-icon { align-items: center; background: var(--uhcp-surface-muted); border: 1px solid var(--uhcp-border); border-radius: 50%; color: var(--uhcp-turquoise); display: flex; height: 4.5rem; justify-content: center; width: 4.5rem; }
198
+ .uhcp-profile-target-default-icon .uhcp-person-icon { height: 2.3rem; width: 2.3rem; }
195
199
  .uhcp-profile-target-content { display: grid; gap: .65rem; min-width: 0; }
196
200
  .uhcp-profile-target-copy { display: grid; gap: .2rem; text-align: left; }
197
201
  .uhcp-profile-target-copy > strong { color: var(--uhcp-text); font-size: 1.2rem; }
@@ -211,6 +215,18 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
211
215
  .uhcp-profile-target-badges-centered { justify-content: center; }
212
216
  }
213
217
  .uhcp-profile-pin-fallback { border-top: 1px solid var(--uhcp-border); padding-top: .8rem; }
218
+ .uhcp-professional-evidence-methods { border: 0; display: grid; gap: .75rem; margin: 0; padding: 0; }
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; }
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); }
226
+ .uhcp-professional-pin-setup { display: grid; gap: 1rem; }
227
+ .uhcp-professional-pin-setup > header h1, .uhcp-professional-pin-setup > header p { margin: 0; }
228
+ .uhcp-professional-pin-setup > header p { margin-top: .45rem; }
229
+ .uhcp-professional-pin-setup .uhcp-secondary-action { background: transparent; border: 1px solid var(--uhcp-border); color: var(--uhcp-text); }
214
230
  .uhcp-profile-pin-fallback summary { color: var(--uhcp-text); cursor: pointer; font-weight: 700; }
215
231
  .uhcp-profile-pin-fallback form { margin-top: .8rem; }
216
232
  .uhcp-profile-pin-recovery { display: flex; justify-content: flex-start; margin-top: .7rem; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.2.36",
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": {