uhc-portal-components-react 0.2.37 → 0.2.40
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 +23 -0
- package/README.md +7 -4
- package/dist/ExistingProfessionalProfilePanel.d.ts +39 -4
- package/dist/ExistingProfessionalProfilePanel.d.ts.map +1 -1
- package/dist/ExistingProfessionalProfilePanel.js +47 -13
- package/dist/PortalHeader.d.ts +2 -1
- package/dist/PortalHeader.d.ts.map +1 -1
- package/dist/PortalHeader.js +11 -6
- package/dist/styles.css +7 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.40 - 2026-11-04
|
|
6
|
+
|
|
7
|
+
- Keep credential QR, organization discovery and professional DID/URN as three
|
|
8
|
+
explicit evidence choices. Organization country and legal-identifier fields
|
|
9
|
+
no longer replace the manual DID/URN route.
|
|
10
|
+
|
|
11
|
+
## 0.2.39 - 2026-11-04
|
|
12
|
+
|
|
13
|
+
- Let hosts offer country, optional region, organization legal identifier and
|
|
14
|
+
sector discovery for an existing professional. When several authoritative
|
|
15
|
+
roles resolve, require one to be selected before rendering device PIN setup.
|
|
16
|
+
|
|
17
|
+
## 0.2.38 - 2026-11-03
|
|
18
|
+
|
|
19
|
+
- Keep the language dropdown inside the shared person menu on public and
|
|
20
|
+
authenticated portal routes.
|
|
21
|
+
- Preserve the same account panel before sign-in while omitting account data
|
|
22
|
+
and private preferences until a verified session exists.
|
|
23
|
+
- Let signed-in hosts opt into showing language alongside their account
|
|
24
|
+
preferences, and align the subject-card directory dependency with the
|
|
25
|
+
reusable provider selector contract.
|
|
3
26
|
## 0.2.37 - 2026-11-03
|
|
4
27
|
|
|
5
28
|
- Split existing-professional registration into evidence verification and
|
package/README.md
CHANGED
|
@@ -135,10 +135,13 @@ 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
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
signed professional-credential QR. A host may also expose two separate
|
|
139
|
+
alternatives: join an existing organization by country, optional region,
|
|
140
|
+
legal identifier and sector, or enter a known professional DID/URN and
|
|
141
|
+
sector. Organization discovery never replaces the DID/URN fallback. When
|
|
142
|
+
the host supplies `onResolve`, the screen shows the server-resolved role
|
|
143
|
+
choices before it offers local device-PIN setup and DCR. This screen never
|
|
144
|
+
accepts an employee invitation or treats a browser role choice as authority.
|
|
142
145
|
- `SubjectAccessRequestPanel`: controlled provider-enabled QR, numeric-code,
|
|
143
146
|
`did:web`, microchip, tattoo, email or telephone locator. Resolution and
|
|
144
147
|
returned identifier evidence never grant subject access.
|
|
@@ -4,6 +4,7 @@ export type ExistingProfessionalProfileCopy = Readonly<{
|
|
|
4
4
|
title: string;
|
|
5
5
|
description: string;
|
|
6
6
|
credentialMethodLabel: string;
|
|
7
|
+
organizationMethodLabel: string;
|
|
7
8
|
manualMethodLabel: string;
|
|
8
9
|
scanProfessionalCredential: string;
|
|
9
10
|
uploadProfessionalCredentialQr: string;
|
|
@@ -13,6 +14,12 @@ export type ExistingProfessionalProfileCopy = Readonly<{
|
|
|
13
14
|
cameraActive: string;
|
|
14
15
|
credentialQrUnreadable: string;
|
|
15
16
|
professionalIdentifierLabel: string;
|
|
17
|
+
countryLabel?: string;
|
|
18
|
+
countryPlaceholder?: string;
|
|
19
|
+
regionalIdentifierLabel?: string;
|
|
20
|
+
regionLabel?: string;
|
|
21
|
+
regionPlaceholder?: string;
|
|
22
|
+
organizationIdentifierLabel?: string;
|
|
16
23
|
sectorLabel: string;
|
|
17
24
|
pinLabel: string;
|
|
18
25
|
pinConfirmationLabel: string;
|
|
@@ -34,6 +41,14 @@ export type ExistingProfessionalProfileInput = Readonly<{
|
|
|
34
41
|
professionalIdentifier: string;
|
|
35
42
|
sector: string;
|
|
36
43
|
pin: string;
|
|
44
|
+
}> | Readonly<{
|
|
45
|
+
method: 'organization';
|
|
46
|
+
country: string;
|
|
47
|
+
region?: string;
|
|
48
|
+
organizationIdentifier: string;
|
|
49
|
+
sector: string;
|
|
50
|
+
selectedProfile: string;
|
|
51
|
+
pin: string;
|
|
37
52
|
}>;
|
|
38
53
|
export type ExistingProfessionalEvidenceInput = Readonly<{
|
|
39
54
|
method: 'credential';
|
|
@@ -42,9 +57,16 @@ export type ExistingProfessionalEvidenceInput = Readonly<{
|
|
|
42
57
|
method: 'identifier';
|
|
43
58
|
professionalIdentifier: string;
|
|
44
59
|
sector: string;
|
|
60
|
+
}> | Readonly<{
|
|
61
|
+
method: 'organization';
|
|
62
|
+
country: string;
|
|
63
|
+
region?: string;
|
|
64
|
+
organizationIdentifier: string;
|
|
65
|
+
sector: string;
|
|
45
66
|
}>;
|
|
46
67
|
export type ResolvedExistingProfessionalProfile = Omit<ProfileProtectionTargetV2, 'eyebrow'> & Readonly<{
|
|
47
68
|
eyebrow?: string;
|
|
69
|
+
selectionValue?: string;
|
|
48
70
|
}>;
|
|
49
71
|
export type ExistingProfessionalProfileRequest = Readonly<{
|
|
50
72
|
professionalCredential: string;
|
|
@@ -53,6 +75,13 @@ export type ExistingProfessionalProfileRequest = Readonly<{
|
|
|
53
75
|
professionalIdentifier: string;
|
|
54
76
|
sectorHint: string;
|
|
55
77
|
pin: string;
|
|
78
|
+
}> | Readonly<{
|
|
79
|
+
country: string;
|
|
80
|
+
region?: string;
|
|
81
|
+
organizationIdentifier: string;
|
|
82
|
+
sectorHint: string;
|
|
83
|
+
selectedProfile: string;
|
|
84
|
+
pin: string;
|
|
56
85
|
}>;
|
|
57
86
|
/** Shared BFF serialization keeps professional evidence distinct from an invitation code. */
|
|
58
87
|
export declare function toExistingProfessionalProfileRequest(input: ExistingProfessionalProfileInput): ExistingProfessionalProfileRequest;
|
|
@@ -60,22 +89,28 @@ export type ExistingProfessionalSectorOption = Readonly<{
|
|
|
60
89
|
value: string;
|
|
61
90
|
label: string;
|
|
62
91
|
}>;
|
|
92
|
+
export type ExistingProfessionalJurisdictionOption = Readonly<{
|
|
93
|
+
value: string;
|
|
94
|
+
label: string;
|
|
95
|
+
}>;
|
|
63
96
|
export type ExistingProfessionalProfilePanelProps = Readonly<{
|
|
64
97
|
copy: ExistingProfessionalProfileCopy;
|
|
65
98
|
sectors: readonly ExistingProfessionalSectorOption[];
|
|
99
|
+
countries?: readonly ExistingProfessionalJurisdictionOption[];
|
|
100
|
+
regionsByCountry?: Readonly<Record<string, readonly ExistingProfessionalJurisdictionOption[]>>;
|
|
66
101
|
busy: boolean;
|
|
67
102
|
error?: string;
|
|
68
103
|
onScanProfessionalCredential?: () => Promise<string | undefined>;
|
|
69
104
|
onReadProfessionalCredentialImage?: (file: File) => Promise<string | undefined>;
|
|
70
|
-
onResolve?: (input: ExistingProfessionalEvidenceInput) => Promise<ResolvedExistingProfessionalProfile>;
|
|
105
|
+
onResolve?: (input: ExistingProfessionalEvidenceInput) => Promise<ResolvedExistingProfessionalProfile | readonly ResolvedExistingProfessionalProfile[]>;
|
|
71
106
|
onSubmit: (input: ExistingProfessionalProfileInput) => void | Promise<void>;
|
|
72
107
|
}>;
|
|
73
108
|
/**
|
|
74
109
|
* Registers this portal device for an already-active professional profile.
|
|
75
110
|
* Professional evidence, employee invitations and device registration are
|
|
76
111
|
* separate artifacts. This screen accepts no invitation. A signed credential
|
|
77
|
-
* is the preferred path
|
|
78
|
-
*
|
|
112
|
+
* is the preferred path. Organization coordinates and a DID-or-URN are two
|
|
113
|
+
* explicit alternatives; neither becomes browser-selected authority by itself.
|
|
79
114
|
*/
|
|
80
|
-
export declare function ExistingProfessionalProfilePanel({ copy, sectors, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }: ExistingProfessionalProfilePanelProps): import("react").JSX.Element;
|
|
115
|
+
export declare function ExistingProfessionalProfilePanel({ copy, sectors, countries, regionsByCountry, busy, error, onScanProfessionalCredential, onReadProfessionalCredentialImage, onResolve, onSubmit }: ExistingProfessionalProfilePanelProps): import("react").JSX.Element;
|
|
81
116
|
//# sourceMappingURL=ExistingProfessionalProfilePanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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,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,CAAA;
|
|
1
|
+
{"version":3,"file":"ExistingProfessionalProfilePanel.d.ts","sourceRoot":"","sources":["../src/ExistingProfessionalProfilePanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,25 +6,39 @@ 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
15
24
|
* Professional evidence, employee invitations and device registration are
|
|
16
25
|
* separate artifacts. This screen accepts no invitation. A signed credential
|
|
17
|
-
* is the preferred path
|
|
18
|
-
*
|
|
26
|
+
* is the preferred path. Organization coordinates and a DID-or-URN are two
|
|
27
|
+
* explicit alternatives; neither 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 [
|
|
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 (method === 'organization')
|
|
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 &&
|
|
148
|
+
if (onResolve && resolvedProfiles.length === 0) {
|
|
127
149
|
setLocalError('');
|
|
128
150
|
setResolving(true);
|
|
129
151
|
try {
|
|
130
|
-
|
|
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 &&
|
|
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 || '', ...
|
|
153
|
-
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'
|
|
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 })] }), countries.length ? _jsxs("label", { children: [_jsx("input", { type: "radio", name: "professionalEvidenceMethod", value: "organization", checked: method === 'organization', onChange: () => { setMethod('organization'); setLocalError(''); } }), _jsx("span", { children: copy.organizationMethodLabel })] }) : null, _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.
|
|
192
|
+
: _jsxs(_Fragment, { children: [method === 'organization' ? _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
|
}
|
package/dist/PortalHeader.d.ts
CHANGED
|
@@ -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,+
|
|
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"}
|
package/dist/PortalHeader.js
CHANGED
|
@@ -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":
|
|
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
|
-
?
|
|
77
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.40",
|
|
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.
|
|
37
|
+
"sos-data-utils-ts": "0.6.21",
|
|
38
38
|
"terminology-components-react": "0.2.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|