uhc-portal-components-react 0.1.40 → 0.1.42
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 +25 -0
- package/dist/AnimalControllerAttestationPanel.d.ts +43 -0
- package/dist/AnimalControllerAttestationPanel.d.ts.map +1 -0
- package/dist/AnimalControllerAttestationPanel.js +37 -0
- package/dist/BrowserClinicalSpeechInput.d.ts +25 -0
- package/dist/BrowserClinicalSpeechInput.d.ts.map +1 -0
- package/dist/BrowserClinicalSpeechInput.js +31 -0
- package/dist/ClinicalBundleDraftEditor.d.ts +55 -0
- package/dist/ClinicalBundleDraftEditor.d.ts.map +1 -0
- package/dist/ClinicalBundleDraftEditor.js +138 -0
- package/dist/PortalCurrentSubject.d.ts +29 -0
- package/dist/PortalCurrentSubject.d.ts.map +1 -0
- package/dist/PortalCurrentSubject.js +60 -0
- package/dist/PortalHeader.d.ts.map +1 -1
- package/dist/PortalHeader.js +2 -1
- package/dist/PortalShell.d.ts.map +1 -1
- package/dist/PortalShell.js +2 -1
- package/dist/ProfessionalAccessPanel.d.ts +50 -0
- package/dist/ProfessionalAccessPanel.d.ts.map +1 -0
- package/dist/ProfessionalAccessPanel.js +13 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/styles.css +53 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.42 - 2026-10-07
|
|
6
|
+
|
|
7
|
+
- Add a product-neutral, JSON-Schema-driven IPS Bundle draft editor with local
|
|
8
|
+
add/edit/remove staging, shared terminology and speech hooks, and one explicit
|
|
9
|
+
final document submission boundary. Typed ResearchSubject references and
|
|
10
|
+
professional author/attester provenance remain host-controlled.
|
|
11
|
+
|
|
12
|
+
## 0.1.41 - 2026-10-05
|
|
13
|
+
|
|
14
|
+
- Add one brand-neutral professional access panel shared by Partner and Test
|
|
15
|
+
Network Google/email-OTP/passkey entry and by an isolated six-digit Demo
|
|
16
|
+
adapter. Demo presentation cannot switch shared GW or DataConv services into
|
|
17
|
+
an in-memory mode through a browser-authored header.
|
|
18
|
+
- Hide passkey entry when the host has not supplied a real WebAuthn adapter;
|
|
19
|
+
an informational click is not represented as authentication.
|
|
20
|
+
- Add a host-controlled animal controller attestation panel for pending
|
|
21
|
+
owner/caregiver invitations and privacy-safe accepted proof readback. The
|
|
22
|
+
component never replaces a controller, sends a notification or grants
|
|
23
|
+
clinical access on its own.
|
|
24
|
+
- Add a shared current-subject registration provider so Personal and
|
|
25
|
+
Professional can render the selected person or animal in the common header
|
|
26
|
+
without duplicating portal state.
|
|
27
|
+
- Add a browser speech-recognition adapter whose final transcript remains
|
|
28
|
+
editable and whose injected constructor is testable outside a browser.
|
|
29
|
+
|
|
5
30
|
## 0.1.40
|
|
6
31
|
|
|
7
32
|
- Present subject Communications as an accessible bidirectional channel with
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type AnimalControllerAttestationRelationship = 'owner' | 'caregiver';
|
|
2
|
+
export type AnimalControllerAttestationInput = Readonly<{
|
|
3
|
+
controllerName: string;
|
|
4
|
+
controllerEmail: string;
|
|
5
|
+
relationship: AnimalControllerAttestationRelationship;
|
|
6
|
+
professionalAttestation: true;
|
|
7
|
+
}>;
|
|
8
|
+
export type AnimalControllerAttestationState = Readonly<{
|
|
9
|
+
status: 'draft' | 'pending' | 'accepted';
|
|
10
|
+
controllerName?: string;
|
|
11
|
+
relationship?: AnimalControllerAttestationRelationship;
|
|
12
|
+
proofIdentifier?: string;
|
|
13
|
+
proofVerified?: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
export type AnimalControllerAttestationCopy = Readonly<{
|
|
16
|
+
title: string;
|
|
17
|
+
intro: string;
|
|
18
|
+
controllerName: string;
|
|
19
|
+
controllerEmail: string;
|
|
20
|
+
relationship: string;
|
|
21
|
+
owner: string;
|
|
22
|
+
caregiver: string;
|
|
23
|
+
attestation: string;
|
|
24
|
+
submit: string;
|
|
25
|
+
pending: string;
|
|
26
|
+
accepted: string;
|
|
27
|
+
verified: string;
|
|
28
|
+
rejected: string;
|
|
29
|
+
proofLabel: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Shared presentation for proposing and verifying an animal-controller link.
|
|
33
|
+
* The host must authenticate the professional, persist the pending proposal,
|
|
34
|
+
* deliver and accept the invitation, and verify any proof. This component
|
|
35
|
+
* never replaces a controller or grants access by itself.
|
|
36
|
+
*/
|
|
37
|
+
export declare function AnimalControllerAttestationPanel({ copy, state, canEdit, onSubmit }: Readonly<{
|
|
38
|
+
copy: AnimalControllerAttestationCopy;
|
|
39
|
+
state: AnimalControllerAttestationState;
|
|
40
|
+
canEdit: boolean;
|
|
41
|
+
onSubmit?: (input: AnimalControllerAttestationInput) => Promise<void> | void;
|
|
42
|
+
}>): import("react").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=AnimalControllerAttestationPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnimalControllerAttestationPanel.d.ts","sourceRoot":"","sources":["../src/AnimalControllerAttestationPanel.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,uCAAuC,GAAG,OAAO,GAAG,WAAW,CAAA;AAC3E,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,uCAAuC,CAAA;IACrD,uBAAuB,EAAE,IAAI,CAAA;CAC9B,CAAC,CAAA;AACF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,CAAA;IACxC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,uCAAuC,CAAA;IACtD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAC,CAAA;AACF,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC;IAC5F,IAAI,EAAE,+BAA+B,CAAA;IACrC,KAAK,EAAE,gCAAgC,CAAA;IACvC,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC7E,CAAC,+BA0CD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Shared presentation for proposing and verifying an animal-controller link.
|
|
5
|
+
* The host must authenticate the professional, persist the pending proposal,
|
|
6
|
+
* deliver and accept the invitation, and verify any proof. This component
|
|
7
|
+
* never replaces a controller or grants access by itself.
|
|
8
|
+
*/
|
|
9
|
+
export function AnimalControllerAttestationPanel({ copy, state, canEdit, onSubmit }) {
|
|
10
|
+
const [attested, setAttested] = useState(false);
|
|
11
|
+
const [busy, setBusy] = useState(false);
|
|
12
|
+
async function submit(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
if (!onSubmit || !attested)
|
|
15
|
+
return;
|
|
16
|
+
const data = new FormData(event.currentTarget);
|
|
17
|
+
setBusy(true);
|
|
18
|
+
try {
|
|
19
|
+
await onSubmit({
|
|
20
|
+
controllerName: requiredText(data, 'controllerName'),
|
|
21
|
+
controllerEmail: requiredText(data, 'controllerEmail').toLowerCase(),
|
|
22
|
+
relationship: requiredText(data, 'relationship'),
|
|
23
|
+
professionalAttestation: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setBusy(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return _jsxs("section", { className: "uhcp-animal-controller-attestation", "aria-labelledby": "animal-controller-attestation-title", children: [_jsxs("header", { children: [_jsx("h2", { id: "animal-controller-attestation-title", children: copy.title }), _jsx("p", { children: copy.intro })] }), state.status === 'accepted' ? _jsxs(_Fragment, { children: [_jsx("p", { role: "status", children: copy.accepted }), _jsxs("dl", { children: [_jsxs("div", { children: [_jsx("dt", { children: copy.controllerName }), _jsx("dd", { children: state.controllerName })] }), _jsxs("div", { children: [_jsx("dt", { children: copy.relationship }), _jsx("dd", { children: state.relationship === 'caregiver' ? copy.caregiver : copy.owner })] }), state.proofIdentifier ? _jsxs("div", { children: [_jsx("dt", { children: copy.proofLabel }), _jsx("dd", { children: state.proofIdentifier })] }) : null] }), _jsx("p", { className: state.proofVerified ? 'uhcp-proof-verified' : 'uhcp-proof-rejected', children: state.proofVerified ? copy.verified : copy.rejected })] }) : canEdit && onSubmit ? _jsxs("form", { onSubmit: submit, children: [_jsxs("label", { children: [copy.controllerName, _jsx("input", { name: "controllerName", required: true, maxLength: 160, defaultValue: state.controllerName })] }), _jsxs("label", { children: [copy.controllerEmail, _jsx("input", { name: "controllerEmail", type: "email", required: true, autoComplete: "email" })] }), _jsxs("label", { children: [copy.relationship, _jsxs("select", { name: "relationship", defaultValue: state.relationship ?? 'owner', children: [_jsx("option", { value: "owner", children: copy.owner }), _jsx("option", { value: "caregiver", children: copy.caregiver })] })] }), _jsxs("label", { className: "uhcp-attestation-check", children: [_jsx("input", { type: "checkbox", checked: attested, onChange: event => setAttested(event.currentTarget.checked) }), copy.attestation] }), _jsx("button", { type: "submit", disabled: !attested || busy, children: copy.submit }), state.status === 'pending' ? _jsx("p", { role: "status", children: copy.pending }) : null] }) : state.status === 'pending' ? _jsx("p", { role: "status", children: copy.pending }) : null] });
|
|
31
|
+
}
|
|
32
|
+
function requiredText(data, name) {
|
|
33
|
+
const value = data.get(name);
|
|
34
|
+
if (typeof value !== 'string' || !value.trim())
|
|
35
|
+
throw new Error(`animal_controller_${name}_required`);
|
|
36
|
+
return value.trim();
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type SpeechResultEvent = Readonly<{
|
|
2
|
+
results: ArrayLike<ArrayLike<Readonly<{
|
|
3
|
+
transcript: string;
|
|
4
|
+
}>>>;
|
|
5
|
+
}>;
|
|
6
|
+
type SpeechErrorEvent = Readonly<{
|
|
7
|
+
error?: string;
|
|
8
|
+
}>;
|
|
9
|
+
type SpeechRecognitionLike = {
|
|
10
|
+
lang: string;
|
|
11
|
+
continuous: boolean;
|
|
12
|
+
interimResults: boolean;
|
|
13
|
+
maxAlternatives: number;
|
|
14
|
+
onresult: ((event: SpeechResultEvent) => void) | null;
|
|
15
|
+
onerror: ((event: SpeechErrorEvent) => void) | null;
|
|
16
|
+
onend: (() => void) | null;
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): void;
|
|
19
|
+
};
|
|
20
|
+
type SpeechRecognitionConstructor = new () => SpeechRecognitionLike;
|
|
21
|
+
/** Product-neutral browser microphone adapter; the transcript stays editable. */
|
|
22
|
+
export declare function transcribeBrowserClinicalSpeech(locale: string, suppliedConstructor?: SpeechRecognitionConstructor): Promise<string>;
|
|
23
|
+
export declare function supportsBrowserClinicalSpeech(): boolean;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=BrowserClinicalSpeechInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrowserClinicalSpeechInput.d.ts","sourceRoot":"","sources":["../src/BrowserClinicalSpeechInput.ts"],"names":[],"mappings":"AAAA,KAAK,iBAAiB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,CAAA;AACtG,KAAK,gBAAgB,GAAG,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AACpD,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,OAAO,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IACrD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IACnD,KAAK,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1B,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;CACb,CAAA;AACD,KAAK,4BAA4B,GAAG,UAAU,qBAAqB,CAAA;AAEnE,iFAAiF;AACjF,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBnI;AAED,wBAAgB,6BAA6B,IAAI,OAAO,CAIvD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Product-neutral browser microphone adapter; the transcript stays editable. */
|
|
2
|
+
export function transcribeBrowserClinicalSpeech(locale, suppliedConstructor) {
|
|
3
|
+
const browser = typeof window === 'undefined'
|
|
4
|
+
? undefined
|
|
5
|
+
: window;
|
|
6
|
+
const Constructor = suppliedConstructor ?? browser?.SpeechRecognition ?? browser?.webkitSpeechRecognition;
|
|
7
|
+
if (!Constructor)
|
|
8
|
+
return Promise.reject(new Error('clinical_speech_input_unavailable'));
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const recognition = new Constructor();
|
|
11
|
+
let settled = false;
|
|
12
|
+
recognition.lang = locale;
|
|
13
|
+
recognition.continuous = false;
|
|
14
|
+
recognition.interimResults = false;
|
|
15
|
+
recognition.maxAlternatives = 1;
|
|
16
|
+
recognition.onresult = (event) => {
|
|
17
|
+
settled = true;
|
|
18
|
+
resolve(Array.from(event.results).map((result) => result[0]?.transcript || '').join(' ').trim());
|
|
19
|
+
};
|
|
20
|
+
recognition.onerror = (event) => { settled = true; reject(new Error(event.error || 'clinical_speech_input_failed')); };
|
|
21
|
+
recognition.onend = () => { if (!settled)
|
|
22
|
+
reject(new Error('clinical_speech_input_empty')); };
|
|
23
|
+
recognition.start();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function supportsBrowserClinicalSpeech() {
|
|
27
|
+
if (typeof window === 'undefined')
|
|
28
|
+
return false;
|
|
29
|
+
const browser = window;
|
|
30
|
+
return Boolean(browser.SpeechRecognition || browser.webkitSpeechRecognition);
|
|
31
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import 'gdc-common-utils-ts/utils/allergy-intolerance-entry-editor';
|
|
2
|
+
import 'gdc-common-utils-ts/utils/condition-entry-editor';
|
|
3
|
+
import 'gdc-common-utils-ts/utils/immunization-entry-editor';
|
|
4
|
+
import 'gdc-common-utils-ts/utils/medication-statement-entry-editor';
|
|
5
|
+
import 'gdc-common-utils-ts/utils/observation-entry-editor';
|
|
6
|
+
import 'gdc-common-utils-ts/utils/procedure-entry-editor';
|
|
7
|
+
import type { MultichannelOperationSchema } from 'sos-data-utils-ts/multichannel-operation-schemas';
|
|
8
|
+
import { type ClinicalArtifactAuthoringContext, type ClinicalArtifactEditorCopy, type ClinicalArtifactSchema, type ClinicalTerminologySearch, type ClinicalTerminologyCandidate } from './ClinicalArtifactEditor.js';
|
|
9
|
+
export type ClinicalBundleDraftSchema = Readonly<{
|
|
10
|
+
schema: ClinicalArtifactSchema;
|
|
11
|
+
operationSchema: MultichannelOperationSchema;
|
|
12
|
+
sectionLabel: string;
|
|
13
|
+
artifactLabel: string;
|
|
14
|
+
}>;
|
|
15
|
+
export type ClinicalBundleDraft = Readonly<{
|
|
16
|
+
resourceType: 'Bundle';
|
|
17
|
+
type: 'document';
|
|
18
|
+
entry: readonly unknown[];
|
|
19
|
+
}>;
|
|
20
|
+
export type ClinicalBundleDraftSubmitOutcome = Readonly<{
|
|
21
|
+
state: 'confirmed' | 'pending' | 'retryable' | 'rejected';
|
|
22
|
+
message?: string;
|
|
23
|
+
}>;
|
|
24
|
+
export type ClinicalBundleDraftEditorCopy = Readonly<{
|
|
25
|
+
title: string;
|
|
26
|
+
description: string;
|
|
27
|
+
empty: string;
|
|
28
|
+
add: string;
|
|
29
|
+
edit: string;
|
|
30
|
+
remove: string;
|
|
31
|
+
submit: string;
|
|
32
|
+
submitting: string;
|
|
33
|
+
drafted: string;
|
|
34
|
+
editor: ClinicalArtifactEditorCopy;
|
|
35
|
+
}>;
|
|
36
|
+
export type ClinicalBundleDraftEditorProps = Readonly<{
|
|
37
|
+
schemas: readonly ClinicalBundleDraftSchema[];
|
|
38
|
+
authoringContext: ClinicalArtifactAuthoringContext;
|
|
39
|
+
copy: ClinicalBundleDraftEditorCopy;
|
|
40
|
+
locale?: string;
|
|
41
|
+
timeZone?: string;
|
|
42
|
+
taxonomyId?: string;
|
|
43
|
+
fieldLabel: (claim: string) => string;
|
|
44
|
+
searchTerminology?: (input: ClinicalTerminologySearch) => Promise<readonly ClinicalTerminologyCandidate[]>;
|
|
45
|
+
onSubmitDraft: (bundle: ClinicalBundleDraft) => Promise<ClinicalBundleDraftSubmitOutcome>;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Shared local-first IPS authoring workspace.
|
|
49
|
+
*
|
|
50
|
+
* The component stages schema-validated claims-first operations in one Bundle.
|
|
51
|
+
* It owns no authorization or persistence: the host supplies subject and
|
|
52
|
+
* provenance and only `onSubmitDraft` may cross a persistence boundary.
|
|
53
|
+
*/
|
|
54
|
+
export declare function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, searchTerminology, onSubmitDraft, }: ClinicalBundleDraftEditorProps): import("react").JSX.Element;
|
|
55
|
+
//# sourceMappingURL=ClinicalBundleDraftEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClinicalBundleDraftEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalBundleDraftEditor.tsx"],"names":[],"mappings":"AAOA,OAAO,4DAA4D,CAAA;AACnE,OAAO,kDAAkD,CAAA;AACzD,OAAO,qDAAqD,CAAA;AAC5D,OAAO,6DAA6D,CAAA;AACpE,OAAO,oDAAoD,CAAA;AAC3D,OAAO,kDAAkD,CAAA;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAA;AACnG,OAAO,EAEL,KAAK,gCAAgC,EAErC,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EAClC,MAAM,6BAA6B,CAAA;AAGpC,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,MAAM,EAAE,sBAAsB,CAAA;IAC9B,eAAe,EAAE,2BAA2B,CAAA;IAC5C,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,YAAY,EAAE,QAAQ,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;CAC1B,CAAC,CAAA;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAA;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,0BAA0B,CAAA;CACnC,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7C,gBAAgB,EAAE,gCAAgC,CAAA;IAClD,IAAI,EAAE,6BAA6B,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,OAAO,CAAC,SAAS,4BAA4B,EAAE,CAAC,CAAA;IAC1G,aAAa,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO,CAAC,gCAAgC,CAAC,CAAA;CAC1F,CAAC,CAAA;AAYF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,gBAAgB,EAChB,IAAI,EACJ,MAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,GACd,EAAE,8BAA8B,+BAuFhC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import { BundleEditor } from 'gdc-common-utils-ts/utils/bundle-editor-core';
|
|
5
|
+
import { BundleTypes } from 'gdc-common-utils-ts/models/bundle-editor-types';
|
|
6
|
+
import { HealthcareDocumentTypes } from 'gdc-common-utils-ts/constants/healthcare';
|
|
7
|
+
import { ClinicalResourceEntryEditor } from 'gdc-common-utils-ts/utils/clinical-resource-entry-editor';
|
|
8
|
+
import 'gdc-common-utils-ts/utils/allergy-intolerance-entry-editor';
|
|
9
|
+
import 'gdc-common-utils-ts/utils/condition-entry-editor';
|
|
10
|
+
import 'gdc-common-utils-ts/utils/immunization-entry-editor';
|
|
11
|
+
import 'gdc-common-utils-ts/utils/medication-statement-entry-editor';
|
|
12
|
+
import 'gdc-common-utils-ts/utils/observation-entry-editor';
|
|
13
|
+
import 'gdc-common-utils-ts/utils/procedure-entry-editor';
|
|
14
|
+
import { ClinicalArtifactEditor, } from './ClinicalArtifactEditor.js';
|
|
15
|
+
import { ClinicalActionButton } from './ClinicalActionButton.js';
|
|
16
|
+
/**
|
|
17
|
+
* Shared local-first IPS authoring workspace.
|
|
18
|
+
*
|
|
19
|
+
* The component stages schema-validated claims-first operations in one Bundle.
|
|
20
|
+
* It owns no authorization or persistence: the host supplies subject and
|
|
21
|
+
* provenance and only `onSubmitDraft` may cross a persistence boundary.
|
|
22
|
+
*/
|
|
23
|
+
export function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale = 'en', timeZone, taxonomyId, fieldLabel, searchTerminology, onSubmitDraft, }) {
|
|
24
|
+
const [artifacts, setArtifacts] = useState([]);
|
|
25
|
+
const [editor, setEditor] = useState();
|
|
26
|
+
const [notice, setNotice] = useState('');
|
|
27
|
+
const [error, setError] = useState('');
|
|
28
|
+
const [submitting, setSubmitting] = useState(false);
|
|
29
|
+
const sections = useMemo(() => groupSchemas(schemas), [schemas]);
|
|
30
|
+
function stageArtifact(draft) {
|
|
31
|
+
if (!editor)
|
|
32
|
+
throw new TypeError('clinical_bundle_draft_editor_schema_required');
|
|
33
|
+
setArtifacts((current) => {
|
|
34
|
+
const next = current.filter((candidate) => candidate.draft.id !== draft.id);
|
|
35
|
+
return [...next, { schema: editor.schema, draft }];
|
|
36
|
+
});
|
|
37
|
+
setNotice(copy.drafted);
|
|
38
|
+
return { resourceType: 'Bundle', type: 'batch', data: [] };
|
|
39
|
+
}
|
|
40
|
+
async function submitDraft() {
|
|
41
|
+
if (artifacts.length === 0 || submitting)
|
|
42
|
+
return;
|
|
43
|
+
setSubmitting(true);
|
|
44
|
+
setError('');
|
|
45
|
+
try {
|
|
46
|
+
const outcome = await onSubmitDraft(combineDrafts(artifacts, authoringContext, copy.title));
|
|
47
|
+
if (outcome.state === 'confirmed') {
|
|
48
|
+
setArtifacts([]);
|
|
49
|
+
setNotice(outcome.message || '');
|
|
50
|
+
}
|
|
51
|
+
else if (outcome.state === 'rejected') {
|
|
52
|
+
setError(outcome.message || copy.editor.readbackMissing);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
setNotice(outcome.message || copy.editor.pending || copy.editor.readbackMissing);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (reason) {
|
|
59
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
setSubmitting(false);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return _jsxs("section", { className: "uhcp-clinical-bundle-draft", "data-clinical-bundle-draft": true, children: [_jsxs("header", { children: [_jsx("h2", { children: copy.title }), _jsx("p", { children: copy.description })] }), notice ? _jsx("p", { role: "status", children: notice }) : null, error ? _jsx("p", { role: "alert", className: "uhcp-clinical-editor-error", children: error }) : null, sections.map(({ label, schemas: sectionSchemas }) => {
|
|
66
|
+
const sectionArtifacts = artifacts.filter((artifact) => artifact.schema.sectionLabel === label);
|
|
67
|
+
return _jsxs("details", { open: true, children: [_jsxs("summary", { children: [label, " (", sectionArtifacts.length, ")"] }), _jsx("div", { className: "uhcp-clinical-bundle-draft-add", children: sectionSchemas.map((candidate) => _jsx(ClinicalActionButton, { kind: "add", label: `${copy.add} ${candidate.artifactLabel}`, onClick: () => setEditor({ schema: candidate }) }, schemaKey(candidate))) }), sectionArtifacts.length === 0 ? _jsx("p", { children: copy.empty }) : _jsx("ul", { children: sectionArtifacts.map((artifact) => {
|
|
68
|
+
const title = artifactTitle(artifact.draft);
|
|
69
|
+
return _jsxs("li", { children: [_jsxs("span", { children: [_jsx("strong", { children: title }), _jsx("small", { children: artifact.draft.resourceType })] }), _jsxs("span", { children: [_jsx(ClinicalActionButton, { kind: "edit", label: `${copy.edit} ${title}`, onClick: () => setEditor({ schema: artifact.schema, draft: artifact.draft }) }), _jsx(ClinicalActionButton, { kind: "delete", label: `${copy.remove} ${title}`, onClick: () => setArtifacts((current) => current.filter((candidate) => candidate.draft.id !== artifact.draft.id)) })] })] }, artifact.draft.id);
|
|
70
|
+
}) })] }, label);
|
|
71
|
+
}), artifacts.length === 0 && sections.length === 0 ? _jsx("p", { children: copy.empty }) : null, _jsx("footer", { children: _jsx("button", { type: "button", disabled: artifacts.length === 0 || submitting, onClick: () => void submitDraft(), children: submitting ? copy.submitting : copy.submit }) }), editor ? _jsx(ClinicalArtifactEditor, { request: editorRequest(editor), schema: editor.schema.schema, operationSchema: editor.schema.operationSchema, authoringContext: authoringContext, copy: copy.editor, locale: locale, timeZone: timeZone, taxonomyId: taxonomyId, fieldLabel: fieldLabel, searchTerminology: searchTerminology, saveArtifact: async (draft) => stageArtifact(draft), onCancel: () => setEditor(undefined), onReadback: () => setEditor(undefined) }) : null] });
|
|
72
|
+
}
|
|
73
|
+
function editorRequest(editor) {
|
|
74
|
+
return {
|
|
75
|
+
resourceType: editor.schema.schema.resourceType,
|
|
76
|
+
sectionCode: editor.schema.schema.sectionCode ?? '',
|
|
77
|
+
mode: 'create',
|
|
78
|
+
...(editor.draft ? { artifactId: editor.draft.id, initialClaims: editor.draft.claims } : { initialClaims: editor.schema.schema.initialClaims }),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function combineDrafts(artifacts, authoringContext, documentTitle) {
|
|
82
|
+
const editor = new BundleEditor()
|
|
83
|
+
.setBundleType(BundleTypes.document)
|
|
84
|
+
.setCompositionSubject(authoringContext.subjectReference)
|
|
85
|
+
.setCompositionType(HealthcareDocumentTypes.IPS.attributeValue)
|
|
86
|
+
.setCompositionTitle(documentTitle)
|
|
87
|
+
.setCompositionDate(authoringContext.createdAt)
|
|
88
|
+
.setCompositionAuthorList([authoringContext.dataAuthorReference]);
|
|
89
|
+
if (authoringContext.attestationState === 'confirmed') {
|
|
90
|
+
if (!authoringContext.attesterReference || !authoringContext.attesterMode || !authoringContext.attestedAt) {
|
|
91
|
+
throw new TypeError('clinical_bundle_draft_attester_required');
|
|
92
|
+
}
|
|
93
|
+
editor.setCompositionAttesterList([{
|
|
94
|
+
reference: authoringContext.attesterReference,
|
|
95
|
+
mode: authoringContext.attesterMode,
|
|
96
|
+
time: authoringContext.attestedAt,
|
|
97
|
+
}]);
|
|
98
|
+
}
|
|
99
|
+
for (const { draft } of artifacts) {
|
|
100
|
+
const entry = editor.newEntryAs(draft.resourceType, draft.id);
|
|
101
|
+
for (const [claim, value] of Object.entries(operationClaims(draft)))
|
|
102
|
+
entry.setClaim(claim, value);
|
|
103
|
+
if (!(entry instanceof ClinicalResourceEntryEditor)) {
|
|
104
|
+
throw new TypeError(`clinical_bundle_draft_resource_unsupported:${draft.resourceType}`);
|
|
105
|
+
}
|
|
106
|
+
entry.setSectionList([draft.sectionCode]);
|
|
107
|
+
}
|
|
108
|
+
return editor.buildDocument();
|
|
109
|
+
}
|
|
110
|
+
function operationClaims(draft) {
|
|
111
|
+
const entries = draft.changesBundle?.data ?? draft.changesBundle?.entry ?? [];
|
|
112
|
+
const first = entries[0];
|
|
113
|
+
if (!first || typeof first !== 'object' || !('resource' in first)) {
|
|
114
|
+
throw new TypeError('clinical_bundle_draft_operation_entry_required');
|
|
115
|
+
}
|
|
116
|
+
const resource = first.resource;
|
|
117
|
+
if (!resource || typeof resource !== 'object' || !('meta' in resource)) {
|
|
118
|
+
throw new TypeError('clinical_bundle_draft_operation_claims_required');
|
|
119
|
+
}
|
|
120
|
+
const meta = resource.meta;
|
|
121
|
+
if (!meta || typeof meta !== 'object' || !('claims' in meta) || !meta.claims || typeof meta.claims !== 'object') {
|
|
122
|
+
throw new TypeError('clinical_bundle_draft_operation_claims_required');
|
|
123
|
+
}
|
|
124
|
+
return meta.claims;
|
|
125
|
+
}
|
|
126
|
+
function groupSchemas(schemas) {
|
|
127
|
+
const sections = new Map();
|
|
128
|
+
for (const schema of schemas)
|
|
129
|
+
sections.set(schema.sectionLabel, [...(sections.get(schema.sectionLabel) ?? []), schema]);
|
|
130
|
+
return [...sections].map(([label, grouped]) => ({ label, schemas: grouped }));
|
|
131
|
+
}
|
|
132
|
+
function schemaKey(schema) {
|
|
133
|
+
return `${schema.schema.sectionCode ?? ''}:${schema.operationSchema['x-operation-id']}`;
|
|
134
|
+
}
|
|
135
|
+
function artifactTitle(draft) {
|
|
136
|
+
const preferred = Object.entries(draft.claims).find(([claim, value]) => typeof value === 'string' && value.trim() && (claim.endsWith('-text') || claim.endsWith('.note') || claim.endsWith('.description')))?.[1];
|
|
137
|
+
return typeof preferred === 'string' ? preferred : draft.resourceType;
|
|
138
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type PortalCurrentSubject = Readonly<{
|
|
3
|
+
id: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
photoUrl?: string;
|
|
6
|
+
secondaryLabel?: string;
|
|
7
|
+
}>;
|
|
8
|
+
export type PortalCurrentSubjectLabels = Readonly<{
|
|
9
|
+
current: string;
|
|
10
|
+
change: string;
|
|
11
|
+
}>;
|
|
12
|
+
export type PortalCurrentSubjectRegistration = Readonly<{
|
|
13
|
+
subject?: PortalCurrentSubject;
|
|
14
|
+
labels: PortalCurrentSubjectLabels;
|
|
15
|
+
canChange: boolean;
|
|
16
|
+
onChange?: () => void;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function PortalCurrentSubjectProvider({ children }: Readonly<{
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}>): import("react").JSX.Element;
|
|
21
|
+
export declare function usePortalCurrentSubjectRegistration(registration?: PortalCurrentSubjectRegistration): void;
|
|
22
|
+
export declare function PortalCurrentSubjectSlot(): import("react").JSX.Element | null;
|
|
23
|
+
export declare function PortalCurrentSubjectControl({ subject, labels, canChange, onChange, }: Readonly<{
|
|
24
|
+
subject?: PortalCurrentSubject;
|
|
25
|
+
labels: PortalCurrentSubjectLabels;
|
|
26
|
+
canChange: boolean;
|
|
27
|
+
onChange?: () => void;
|
|
28
|
+
}>): import("react").JSX.Element | null;
|
|
29
|
+
//# sourceMappingURL=PortalCurrentSubject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortalCurrentSubject.d.ts","sourceRoot":"","sources":["../src/PortalCurrentSubject.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE9F,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAC,CAAA;AAEF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,CAAC,CAAA;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,OAAO,CAAC,EAAE,oBAAoB,CAAA;IAC9B,MAAM,EAAE,0BAA0B,CAAA;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,CAAA;AASF,wBAAgB,4BAA4B,CAAC,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC,+BAG3F;AAED,wBAAgB,mCAAmC,CAAC,YAAY,CAAC,EAAE,gCAAgC,QAkBlG;AAED,wBAAgB,wBAAwB,uCAGvC;AAED,wBAAgB,2BAA2B,CAAC,EAC1C,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,QAAQ,CAAC;IACV,OAAO,CAAC,EAAE,oBAAoB,CAAA;IAC9B,MAAM,EAAE,0BAA0B,CAAA;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,sCAyCD"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
4
|
+
const PortalCurrentSubjectContext = createContext(undefined);
|
|
5
|
+
export function PortalCurrentSubjectProvider({ children }) {
|
|
6
|
+
const [registration, setRegistration] = useState();
|
|
7
|
+
return _jsx(PortalCurrentSubjectContext.Provider, { value: { registration, setRegistration }, children: children });
|
|
8
|
+
}
|
|
9
|
+
export function usePortalCurrentSubjectRegistration(registration) {
|
|
10
|
+
const context = useContext(PortalCurrentSubjectContext);
|
|
11
|
+
const setRegistration = context?.setRegistration;
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!setRegistration)
|
|
14
|
+
return;
|
|
15
|
+
setRegistration(registration);
|
|
16
|
+
return () => setRegistration(undefined);
|
|
17
|
+
}, [
|
|
18
|
+
setRegistration,
|
|
19
|
+
registration?.subject?.id,
|
|
20
|
+
registration?.subject?.displayName,
|
|
21
|
+
registration?.subject?.photoUrl,
|
|
22
|
+
registration?.subject?.secondaryLabel,
|
|
23
|
+
registration?.labels.current,
|
|
24
|
+
registration?.labels.change,
|
|
25
|
+
registration?.canChange,
|
|
26
|
+
registration?.onChange,
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
export function PortalCurrentSubjectSlot() {
|
|
30
|
+
const registration = useContext(PortalCurrentSubjectContext)?.registration;
|
|
31
|
+
return registration ? _jsx(PortalCurrentSubjectControl, { ...registration }) : null;
|
|
32
|
+
}
|
|
33
|
+
export function PortalCurrentSubjectControl({ subject, labels, canChange, onChange, }) {
|
|
34
|
+
const [open, setOpen] = useState(false);
|
|
35
|
+
const container = useRef(null);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const closeOutside = (event) => {
|
|
38
|
+
if (!container.current?.contains(event.target))
|
|
39
|
+
setOpen(false);
|
|
40
|
+
};
|
|
41
|
+
const closeEscape = (event) => {
|
|
42
|
+
if (event.key === 'Escape')
|
|
43
|
+
setOpen(false);
|
|
44
|
+
};
|
|
45
|
+
document.addEventListener('mousedown', closeOutside);
|
|
46
|
+
window.addEventListener('keydown', closeEscape);
|
|
47
|
+
return () => {
|
|
48
|
+
document.removeEventListener('mousedown', closeOutside);
|
|
49
|
+
window.removeEventListener('keydown', closeEscape);
|
|
50
|
+
};
|
|
51
|
+
}, []);
|
|
52
|
+
if (!subject)
|
|
53
|
+
return null;
|
|
54
|
+
return _jsxs("div", { className: "uhcp-current-subject", ref: container, children: [_jsxs("button", { type: "button", className: "uhcp-current-subject-trigger", "aria-label": `${labels.current}: ${subject.displayName}`, "aria-expanded": open, onClick: () => setOpen((value) => !value), children: [_jsx(SubjectAvatar, { subject: subject }), _jsx("span", { children: subject.displayName }), _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), open ? _jsxs("section", { className: "uhcp-current-subject-menu", "aria-label": labels.current, children: [_jsxs("div", { className: "uhcp-current-subject-summary", children: [_jsx(SubjectAvatar, { subject: subject }), _jsxs("span", { children: [_jsx("small", { children: labels.current }), _jsx("strong", { children: subject.displayName }), subject.secondaryLabel ? _jsx("span", { children: subject.secondaryLabel }) : null] })] }), canChange && onChange ? _jsx("button", { type: "button", onClick: () => { setOpen(false); onChange(); }, children: labels.change }) : null] }) : null] });
|
|
55
|
+
}
|
|
56
|
+
function SubjectAvatar({ subject }) {
|
|
57
|
+
if (subject.photoUrl)
|
|
58
|
+
return _jsx("img", { className: "uhcp-current-subject-avatar", src: subject.photoUrl, alt: "" });
|
|
59
|
+
return _jsx("span", { className: "uhcp-current-subject-avatar uhcp-current-subject-initial", "aria-hidden": "true", children: subject.displayName.trim().slice(0, 1).toLocaleUpperCase() || '•' });
|
|
60
|
+
}
|
|
@@ -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,EAGrB,MAAM,gBAAgB,CAAA;
|
|
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,EAGrB,MAAM,gBAAgB,CAAA;AASvB,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,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,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,gBAA+B,GAChC,EAAE,iBAAiB,+BAoKnB"}
|
package/dist/PortalHeader.js
CHANGED
|
@@ -4,6 +4,7 @@ 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 { PortalCurrentSubjectSlot } from './PortalCurrentSubject.js';
|
|
7
8
|
/**
|
|
8
9
|
* Shared account/preferences header.
|
|
9
10
|
*
|
|
@@ -60,7 +61,7 @@ export function PortalHeader({ brand, copy, accountIdentifier, languageHref, lan
|
|
|
60
61
|
setTheme(value);
|
|
61
62
|
localStorage.setItem(themeKey, value);
|
|
62
63
|
}
|
|
63
|
-
return (_jsxs("header", { className: "uhcp-header", style: brand.variables, children: [_jsxs("a", { className: "uhcp-brand", href: brand.homeHref, "aria-label": copy.home, children: [_jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" }), _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { children: brand.organizationName })] })] }), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsxs("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": copy.accountAndPreferences, "aria-expanded": open, children: [_jsx(PersonIcon, {}), accountIdentifier && _jsx("span", { className: "uhcp-account-id", children: accountIdentifier }), _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), 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: () => {
|
|
64
|
+
return (_jsxs("header", { className: "uhcp-header", style: brand.variables, children: [_jsxs("a", { className: "uhcp-brand", href: brand.homeHref, "aria-label": copy.home, children: [_jsx(PortalBrandArtwork, { artwork: brand.mark, className: "uhcp-brand-mark" }), _jsxs("span", { children: [_jsx("strong", { children: brand.productName }), _jsx("small", { children: brand.organizationName })] })] }), _jsx(PortalCurrentSubjectSlot, {}), _jsxs("div", { className: "uhcp-account-menu", ref: ref, children: [_jsxs("button", { type: "button", className: "uhcp-trigger", onClick: () => setOpen((value) => !value), "aria-label": copy.accountAndPreferences, "aria-expanded": open, children: [_jsx(PersonIcon, {}), accountIdentifier && _jsx("span", { className: "uhcp-account-id", children: accountIdentifier }), _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), 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: () => {
|
|
64
65
|
onSelectAccount?.(option.id);
|
|
65
66
|
setOpen(false);
|
|
66
67
|
}, 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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PortalShell.d.ts","sourceRoot":"","sources":["../src/PortalShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"PortalShell.d.ts","sourceRoot":"","sources":["../src/PortalShell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,4BAA4B,CAAA;AAGnC,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,MAAM,EAAE,iBAAiB,CAAA;IACzB,iBAAiB,CAAC,EAAE,IAAI,CAAC,0BAA0B,EAAE,UAAU,CAAC,CAAA;IAChE,QAAQ,EAAE,SAAS,CAAA;IACnB,UAAU,CAAC,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtD,eAAe,CAAC,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC,CAAA;CACpE,CAAC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,eAAe,GAChB,EAAE,gBAAgB,+BA2BlB"}
|
package/dist/PortalShell.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PortalHeader, } from './PortalHeader.js';
|
|
3
3
|
import { ProfileProtectionGate, } from './ProfileProtectionGate.js';
|
|
4
|
+
import { PortalCurrentSubjectProvider } from './PortalCurrentSubject.js';
|
|
4
5
|
/**
|
|
5
6
|
* Brand-neutral portal chrome shared by personal and professional products.
|
|
6
7
|
*
|
|
@@ -17,5 +18,5 @@ export function PortalShell({ header, profileProtection, children, backAction, a
|
|
|
17
18
|
const content = profileProtection
|
|
18
19
|
? _jsx(ProfileProtectionGate, { ...profileProtection, children: children })
|
|
19
20
|
: children;
|
|
20
|
-
return (_jsxs("div", { className: "uhcp-shell", "data-portal-family": header.brand.family, style: header.brand.variables, children: [_jsx(PortalHeader, { ...header }), _jsx("main", { className: "uhcp-main", children: content }), backAction && (_jsx("a", { className: "uhcp-floating-action uhcp-floating-back", href: backAction.href, "aria-label": backAction.label, children: _jsx("span", { "aria-hidden": "true", children: "\u2190" }) })), assistantAction && (_jsxs("button", { className: "uhcp-floating-action uhcp-floating-assistant", type: "button", onClick: assistantAction.onSelect, "aria-label": assistantAction.label, children: [_jsx("span", { "aria-hidden": "true", children: "\u2726" }), _jsx("span", { children: assistantAction.label })] }))] }));
|
|
21
|
+
return (_jsx(PortalCurrentSubjectProvider, { children: _jsxs("div", { className: "uhcp-shell", "data-portal-family": header.brand.family, style: header.brand.variables, children: [_jsx(PortalHeader, { ...header }), _jsx("main", { className: "uhcp-main", children: content }), backAction && (_jsx("a", { className: "uhcp-floating-action uhcp-floating-back", href: backAction.href, "aria-label": backAction.label, children: _jsx("span", { "aria-hidden": "true", children: "\u2190" }) })), assistantAction && (_jsxs("button", { className: "uhcp-floating-action uhcp-floating-assistant", type: "button", onClick: assistantAction.onSelect, "aria-label": assistantAction.label, children: [_jsx("span", { "aria-hidden": "true", children: "\u2726" }), _jsx("span", { children: assistantAction.label })] }))] }) }));
|
|
21
22
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FormEvent } from 'react';
|
|
2
|
+
export type ProfessionalAccessPanelCopy = Readonly<{
|
|
3
|
+
eyebrow: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
google: string;
|
|
7
|
+
separator: string;
|
|
8
|
+
email: string;
|
|
9
|
+
sendEmailCode: string;
|
|
10
|
+
emailCode: string;
|
|
11
|
+
verifyEmailCode: string;
|
|
12
|
+
changeEmail: string;
|
|
13
|
+
emailCodeSent: string;
|
|
14
|
+
passkeyHint: string;
|
|
15
|
+
passkey: string;
|
|
16
|
+
demoEmail: string;
|
|
17
|
+
demoCode: string;
|
|
18
|
+
demoSubmit: string;
|
|
19
|
+
demoHint: string;
|
|
20
|
+
busy: string;
|
|
21
|
+
}>;
|
|
22
|
+
export type ProfessionalAccessPanelProps = Readonly<{
|
|
23
|
+
mode: 'trusted' | 'demo';
|
|
24
|
+
copy: ProfessionalAccessPanelCopy;
|
|
25
|
+
email: string;
|
|
26
|
+
emailCode: string;
|
|
27
|
+
emailCodeRequested?: boolean;
|
|
28
|
+
busy?: boolean;
|
|
29
|
+
elapsedSeconds?: number;
|
|
30
|
+
error?: string;
|
|
31
|
+
googleEnabled?: boolean;
|
|
32
|
+
actionsEnabled?: boolean;
|
|
33
|
+
onEmailChange(value: string): void;
|
|
34
|
+
onEmailCodeChange(value: string): void;
|
|
35
|
+
onGoogle?(): void | Promise<void>;
|
|
36
|
+
onRequestEmailCode?(event: FormEvent<HTMLFormElement>): void | Promise<void>;
|
|
37
|
+
onVerifyEmailCode?(event: FormEvent<HTMLFormElement>): void | Promise<void>;
|
|
38
|
+
onChangeEmail?(): void;
|
|
39
|
+
onPasskey?(): void | Promise<void>;
|
|
40
|
+
onDemo?(event: FormEvent<HTMLFormElement>): void | Promise<void>;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Brand-neutral professional sign-in presentation.
|
|
44
|
+
*
|
|
45
|
+
* The host owns Firebase, OTP delivery, WebAuthn, sessions and authorization.
|
|
46
|
+
* Demo is deliberately a separate adapter: this component never sends a
|
|
47
|
+
* browser header that can switch a shared GW or DataConv deployment to memory.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ProfessionalAccessPanel({ mode, copy, email, emailCode, emailCodeRequested, busy, elapsedSeconds, error, googleEnabled, actionsEnabled, onEmailChange, onEmailCodeChange, onGoogle, onRequestEmailCode, onVerifyEmailCode, onChangeEmail, onPasskey, onDemo, }: ProfessionalAccessPanelProps): import("react").JSX.Element;
|
|
50
|
+
//# sourceMappingURL=ProfessionalAccessPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProfessionalAccessPanel.d.ts","sourceRoot":"","sources":["../src/ProfessionalAccessPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,CAAA;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;IACxB,IAAI,EAAE,2BAA2B,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC,QAAQ,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,kBAAkB,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5E,iBAAiB,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E,aAAa,CAAC,IAAI,IAAI,CAAA;IACtB,SAAS,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,MAAM,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjE,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,SAAS,EACT,kBAA0B,EAC1B,IAAY,EACZ,cAAkB,EAClB,KAAK,EACL,aAAoB,EACpB,cAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,MAAM,GACP,EAAE,4BAA4B,+BAgC9B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Brand-neutral professional sign-in presentation.
|
|
5
|
+
*
|
|
6
|
+
* The host owns Firebase, OTP delivery, WebAuthn, sessions and authorization.
|
|
7
|
+
* Demo is deliberately a separate adapter: this component never sends a
|
|
8
|
+
* browser header that can switch a shared GW or DataConv deployment to memory.
|
|
9
|
+
*/
|
|
10
|
+
export function ProfessionalAccessPanel({ mode, copy, email, emailCode, emailCodeRequested = false, busy = false, elapsedSeconds = 0, error, googleEnabled = true, actionsEnabled = true, onEmailChange, onEmailCodeChange, onGoogle, onRequestEmailCode, onVerifyEmailCode, onChangeEmail, onPasskey, onDemo, }) {
|
|
11
|
+
const progress = busy ? _jsxs("p", { className: "uhcp-auth-progress", role: "status", children: [_jsx("span", { "aria-hidden": "true" }), " ", copy.busy, " ", elapsedSeconds, " s"] }) : null;
|
|
12
|
+
return _jsxs("section", { className: "uhcp-professional-access", "aria-busy": busy || undefined, children: [_jsx("p", { className: "uhcp-eyebrow", children: copy.eyebrow }), _jsx("h1", { children: copy.title }), _jsx("p", { children: copy.description }), mode === 'trusted' ? _jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: onGoogle, disabled: !actionsEnabled || !googleEnabled || busy || !onGoogle, children: copy.google }), _jsx("div", { className: "uhcp-auth-separator", children: _jsx("span", { children: copy.separator }) }), !emailCodeRequested ? _jsxs("form", { onSubmit: onRequestEmailCode, children: [_jsxs("label", { children: [copy.email, _jsx("input", { type: "email", autoComplete: "username webauthn", value: email, onChange: event => onEmailChange(event.target.value), required: true, disabled: busy })] }), _jsx("button", { type: "submit", disabled: !actionsEnabled || busy || !onRequestEmailCode, children: copy.sendEmailCode }), onPasskey ? _jsxs(_Fragment, { children: [_jsx("p", { className: "uhcp-muted", children: copy.passkeyHint }), _jsx("button", { type: "button", className: "uhcp-secondary", disabled: !actionsEnabled || busy, onClick: onPasskey, children: copy.passkey })] }) : null] }) : _jsxs("form", { onSubmit: onVerifyEmailCode, children: [_jsx("p", { className: "uhcp-muted", children: copy.emailCodeSent.replace('{email}', email) }), _jsxs("label", { children: [copy.emailCode, _jsx("input", { type: "text", inputMode: "numeric", autoComplete: "one-time-code", pattern: "[0-9]{6}", minLength: 6, maxLength: 6, value: emailCode, onChange: event => onEmailCodeChange(event.target.value.replace(/\D/g, '').slice(0, 6)), required: true, disabled: busy })] }), _jsx("button", { type: "submit", disabled: !actionsEnabled || busy || emailCode.length !== 6 || !onVerifyEmailCode, children: copy.verifyEmailCode }), _jsx("button", { type: "button", className: "uhcp-secondary", disabled: busy || !onChangeEmail, onClick: onChangeEmail, children: copy.changeEmail })] })] }) : _jsxs("form", { onSubmit: onDemo, children: [_jsxs("label", { children: [copy.demoEmail, _jsx("input", { type: "email", autoComplete: "off", value: email, onChange: event => onEmailChange(event.target.value), required: true, disabled: busy })] }), _jsxs("label", { children: [copy.demoCode, _jsx("input", { type: "text", inputMode: "numeric", autoComplete: "one-time-code", pattern: "[0-9]{6}", minLength: 6, maxLength: 6, value: emailCode, onChange: event => onEmailCodeChange(event.target.value.replace(/\D/g, '').slice(0, 6)), required: true, disabled: busy })] }), _jsx("p", { className: "uhcp-muted", children: copy.demoHint }), _jsx("button", { type: "submit", disabled: !actionsEnabled || busy || emailCode.length !== 6 || !onDemo, children: copy.demoSubmit })] }), progress, error ? _jsx("p", { className: "uhcp-error", role: "alert", children: error }) : null] });
|
|
13
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts.js';
|
|
|
2
2
|
export * from './preferences.js';
|
|
3
3
|
export * from './PortalBrandArtwork.js';
|
|
4
4
|
export * from './PortalHeader.js';
|
|
5
|
+
export * from './PortalCurrentSubject.js';
|
|
5
6
|
export * from './PortalShell.js';
|
|
6
7
|
export * from './PortalEntry.js';
|
|
7
8
|
export * from './ProfileProtectionGate.js';
|
|
@@ -12,7 +13,9 @@ export * from './ClinicalDocumentEncounterExplorer.js';
|
|
|
12
13
|
export * from './AllClinicalSections.js';
|
|
13
14
|
export * from './ClinicalTimeline.js';
|
|
14
15
|
export * from './ClinicalArtifactEditor.js';
|
|
16
|
+
export * from './ClinicalBundleDraftEditor.js';
|
|
15
17
|
export * from './ClinicalSpeechInput.js';
|
|
18
|
+
export * from './BrowserClinicalSpeechInput.js';
|
|
16
19
|
export * from './ClinicalActionButton.js';
|
|
17
20
|
export * from './VitalSignArtifactEditor.js';
|
|
18
21
|
export * from './InvitationAcceptancePanel.js';
|
|
@@ -29,6 +32,8 @@ export * from './IdentityEvidenceWorkspace.js';
|
|
|
29
32
|
export * from './IdentityEvidenceDocumentPanel.js';
|
|
30
33
|
export * from './IdentityEvidenceDeclarationForm.js';
|
|
31
34
|
export * from './AnimalIdentityOnboarding.js';
|
|
35
|
+
export * from './AnimalControllerAttestationPanel.js';
|
|
32
36
|
export * from './EmergencyTaskHub.js';
|
|
33
37
|
export * from './SubjectCommunicationCenter.js';
|
|
38
|
+
export * from './ProfessionalAccessPanel.js';
|
|
34
39
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,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,0BAA0B,CAAA;AACxC,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,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,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,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,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,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts.js';
|
|
|
2
2
|
export * from './preferences.js';
|
|
3
3
|
export * from './PortalBrandArtwork.js';
|
|
4
4
|
export * from './PortalHeader.js';
|
|
5
|
+
export * from './PortalCurrentSubject.js';
|
|
5
6
|
export * from './PortalShell.js';
|
|
6
7
|
export * from './PortalEntry.js';
|
|
7
8
|
export * from './ProfileProtectionGate.js';
|
|
@@ -12,7 +13,9 @@ export * from './ClinicalDocumentEncounterExplorer.js';
|
|
|
12
13
|
export * from './AllClinicalSections.js';
|
|
13
14
|
export * from './ClinicalTimeline.js';
|
|
14
15
|
export * from './ClinicalArtifactEditor.js';
|
|
16
|
+
export * from './ClinicalBundleDraftEditor.js';
|
|
15
17
|
export * from './ClinicalSpeechInput.js';
|
|
18
|
+
export * from './BrowserClinicalSpeechInput.js';
|
|
16
19
|
export * from './ClinicalActionButton.js';
|
|
17
20
|
export * from './VitalSignArtifactEditor.js';
|
|
18
21
|
export * from './InvitationAcceptancePanel.js';
|
|
@@ -29,5 +32,7 @@ export * from './IdentityEvidenceWorkspace.js';
|
|
|
29
32
|
export * from './IdentityEvidenceDocumentPanel.js';
|
|
30
33
|
export * from './IdentityEvidenceDeclarationForm.js';
|
|
31
34
|
export * from './AnimalIdentityOnboarding.js';
|
|
35
|
+
export * from './AnimalControllerAttestationPanel.js';
|
|
32
36
|
export * from './EmergencyTaskHub.js';
|
|
33
37
|
export * from './SubjectCommunicationCenter.js';
|
|
38
|
+
export * from './ProfessionalAccessPanel.js';
|
package/dist/styles.css
CHANGED
|
@@ -464,6 +464,21 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
464
464
|
.uhcp-clinical-icon-action:disabled { cursor:not-allowed; opacity:.42; }
|
|
465
465
|
.uhcp-clinical-icon-action svg { fill:none; height:1.2rem; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:1.8; width:1.2rem; }
|
|
466
466
|
.uhcp-clinical-icon-action-delete:hover { border-color:var(--uhcp-attention); color:var(--uhcp-attention); }
|
|
467
|
+
.uhcp-clinical-bundle-draft { background:var(--uhcp-panel); border:1px solid var(--uhcp-border); border-radius:1.5rem; color:var(--uhcp-text); display:grid; gap:1rem; padding:1.25rem; }
|
|
468
|
+
.uhcp-clinical-bundle-draft>header h2,.uhcp-clinical-bundle-draft>header p { margin:.15rem 0; }
|
|
469
|
+
.uhcp-clinical-bundle-draft>header p,.uhcp-clinical-bundle-draft details>p { color:var(--uhcp-muted); }
|
|
470
|
+
.uhcp-clinical-bundle-draft details { background:var(--uhcp-panel-muted); border:1px solid var(--uhcp-border); border-radius:1rem; padding:.85rem 1rem; }
|
|
471
|
+
.uhcp-clinical-bundle-draft summary { cursor:pointer; font-weight:800; }
|
|
472
|
+
.uhcp-clinical-bundle-draft-add { display:flex; flex-wrap:wrap; gap:.5rem; margin-top:.8rem; }
|
|
473
|
+
.uhcp-clinical-bundle-draft ul { display:grid; gap:.55rem; list-style:none; margin:.8rem 0 0; padding:0; }
|
|
474
|
+
.uhcp-clinical-bundle-draft li { align-items:center; background:var(--uhcp-panel); border:1px solid var(--uhcp-border); border-radius:.8rem; display:flex; gap:1rem; justify-content:space-between; padding:.7rem; }
|
|
475
|
+
.uhcp-clinical-bundle-draft li>span { display:flex; gap:.45rem; }
|
|
476
|
+
.uhcp-clinical-bundle-draft li>span:first-child { align-items:flex-start; flex-direction:column; min-width:0; }
|
|
477
|
+
.uhcp-clinical-bundle-draft li strong { overflow-wrap:anywhere; }
|
|
478
|
+
.uhcp-clinical-bundle-draft li small { color:var(--uhcp-muted); }
|
|
479
|
+
.uhcp-clinical-bundle-draft>footer { display:flex; justify-content:flex-end; }
|
|
480
|
+
.uhcp-clinical-bundle-draft>footer button { background:linear-gradient(135deg,var(--uhcp-action-start),var(--uhcp-action-end)); border:0; border-radius:.75rem; color:#fff; cursor:pointer; font:inherit; font-weight:800; padding:.75rem 1rem; }
|
|
481
|
+
.uhcp-clinical-bundle-draft>footer button:disabled { cursor:not-allowed; opacity:.5; }
|
|
467
482
|
.uhcp-clinical-title { font-weight: 800; margin: 0; }
|
|
468
483
|
.uhcp-clinical-type { color: var(--uhcp-muted); font-size: .72rem; font-weight: 800; letter-spacing: .06em; margin: .35rem 0 0; text-transform: uppercase; }
|
|
469
484
|
.uhcp-clinical-date, .uhcp-clinical-empty { color: var(--uhcp-muted); margin: .35rem 0 0; }
|
|
@@ -669,10 +684,33 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
669
684
|
.uhcp-animal-onboarding-fields label { display: grid; gap: .35rem; }
|
|
670
685
|
.uhcp-animal-onboarding-fields input, .uhcp-animal-onboarding-fields select { box-sizing: border-box; min-height: 2.8rem; width: 100%; }
|
|
671
686
|
.uhcp-animal-onboarding-submit { justify-self: start; }
|
|
687
|
+
.uhcp-animal-controller-attestation { border: 1px solid var(--uhcp-border, #d9dee7); border-radius: 1.25rem; display: grid; gap: 1rem; padding: 1rem; }
|
|
688
|
+
.uhcp-animal-controller-attestation header h2, .uhcp-animal-controller-attestation header p { margin: 0; }
|
|
689
|
+
.uhcp-animal-controller-attestation header p { color: var(--uhcp-muted, #5f6875); margin-top: .35rem; }
|
|
690
|
+
.uhcp-animal-controller-attestation form { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
|
|
691
|
+
.uhcp-animal-controller-attestation form > label:not(.uhcp-attestation-check) { display: grid; gap: .35rem; }
|
|
692
|
+
.uhcp-animal-controller-attestation input, .uhcp-animal-controller-attestation select, .uhcp-animal-controller-attestation button { box-sizing: border-box; min-height: 2.8rem; width: 100%; }
|
|
693
|
+
.uhcp-animal-controller-attestation .uhcp-attestation-check { align-items: center; display: flex; gap: .55rem; grid-column: 1 / -1; }
|
|
694
|
+
.uhcp-animal-controller-attestation .uhcp-attestation-check input { min-height: 1rem; width: auto; }
|
|
695
|
+
.uhcp-animal-controller-attestation dl { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); margin: 0; }
|
|
696
|
+
.uhcp-animal-controller-attestation dt { color: var(--uhcp-muted, #5f6875); font-size: .8rem; }
|
|
697
|
+
.uhcp-animal-controller-attestation dd { font-weight: 700; margin: .2rem 0 0; overflow-wrap: anywhere; }
|
|
698
|
+
.uhcp-proof-verified { color: var(--uhcp-emerald, #087f5b); font-weight: 800; }
|
|
699
|
+
.uhcp-proof-rejected { color: var(--uhcp-danger, #b42318); font-weight: 800; }
|
|
672
700
|
.uhcp-emergency-task-hub > header h1 { font-size: clamp(1.35rem, 3vw, 1.9rem); margin: 0; }
|
|
673
701
|
.uhcp-subject-identity-card { border: 1px solid var(--uhcp-border, #d9dee7); border-radius: 1rem; display: grid; gap: 1rem; padding: 1rem; }
|
|
674
702
|
.uhcp-subject-identity-card > header { align-items: center; display: grid; gap: .8rem; grid-template-columns: auto 1fr auto; }
|
|
675
703
|
.uhcp-subject-identity-card img, .uhcp-subject-identity-avatar { align-items: center; aspect-ratio: 1; border-radius: 50%; display: flex; height: 3.5rem; justify-content: center; object-fit: cover; width: 3.5rem; }
|
|
704
|
+
.uhcp-current-subject { margin-inline-start:auto; position:relative; }
|
|
705
|
+
.uhcp-current-subject-trigger { align-items:center; background:var(--uhcp-surface, #fff); border:1px solid var(--uhcp-border, #d8e0e8); border-radius:999px; color:inherit; cursor:pointer; display:flex; gap:.55rem; min-height:2.75rem; padding:.3rem .7rem .3rem .3rem; }
|
|
706
|
+
.uhcp-current-subject-avatar { align-items:center; aspect-ratio:1; background:var(--uhcp-accent-soft, #e9f3ff); border-radius:50%; display:flex; flex:0 0 auto; height:2.15rem; justify-content:center; object-fit:cover; width:2.15rem; }
|
|
707
|
+
.uhcp-current-subject-initial { font-size:.9rem; font-weight:850; }
|
|
708
|
+
.uhcp-current-subject-trigger > span:nth-child(2) { font-size:.88rem; font-weight:800; max-width:10rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
709
|
+
.uhcp-current-subject-menu { background:var(--uhcp-surface, #fff); border:1px solid var(--uhcp-border, #d8e0e8); border-radius:1rem; box-shadow:0 18px 50px rgb(20 42 66 / 18%); display:grid; gap:.75rem; inset-inline-end:0; min-width:17rem; padding:1rem; position:absolute; top:calc(100% + .55rem); z-index:60; }
|
|
710
|
+
.uhcp-current-subject-summary { align-items:center; display:flex; gap:.75rem; }
|
|
711
|
+
.uhcp-current-subject-summary > span { display:grid; min-width:0; }
|
|
712
|
+
.uhcp-current-subject-summary small,.uhcp-current-subject-summary span span { color:var(--uhcp-muted, #637083); }
|
|
713
|
+
.uhcp-current-subject-menu > button { background:transparent; border:1px solid var(--uhcp-border, #d8e0e8); border-radius:.75rem; color:inherit; cursor:pointer; font:inherit; font-weight:750; padding:.7rem .9rem; text-align:start; }
|
|
676
714
|
.uhcp-subject-identity-avatar { background: var(--uhcp-accent-soft, #e9f3ff); font-size: 1.4rem; font-weight: 850; }
|
|
677
715
|
.uhcp-subject-identity-card h3 { margin: .15rem 0 0; }
|
|
678
716
|
.uhcp-subject-identity-card dl { display: grid; gap: .65rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); margin: 0; }
|
|
@@ -701,3 +739,18 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
701
739
|
.subject-communication-composer-actions { display: flex; justify-content: flex-end; gap: .65rem; }
|
|
702
740
|
.subject-communication-composer-actions button { border: 1px solid currentColor; border-radius: 999px; padding: .55rem .9rem; background: transparent; color: inherit; font: inherit; cursor: pointer; }
|
|
703
741
|
.subject-communication-composer-actions button[type="submit"] { background: currentColor; color: Canvas; }
|
|
742
|
+
.uhcp-professional-access { display: grid; gap: 1rem; margin-inline: auto; max-width: 38rem; }
|
|
743
|
+
.uhcp-professional-access h1, .uhcp-professional-access p { margin-block: 0; }
|
|
744
|
+
.uhcp-professional-access form { display: grid; gap: .9rem; }
|
|
745
|
+
.uhcp-professional-access label { display: grid; font-weight: 700; gap: .4rem; }
|
|
746
|
+
.uhcp-professional-access input { background: var(--uhcp-surface, #fff); border: 1px solid var(--uhcp-line, #cbd5e1); border-radius: .8rem; color: inherit; font: inherit; min-height: 2.8rem; padding-inline: .85rem; }
|
|
747
|
+
.uhcp-professional-access button { background: linear-gradient(135deg, var(--uhcp-action-start, #0f9ca8), var(--uhcp-action-end, #2563a9)); border: 0; border-radius: 999px; color: #fff; cursor: pointer; font: inherit; font-weight: 800; min-height: 2.8rem; padding: .7rem 1rem; }
|
|
748
|
+
.uhcp-professional-access button:disabled { cursor: wait; opacity: .55; }
|
|
749
|
+
.uhcp-professional-access .uhcp-secondary { background: transparent; border: 1px solid var(--uhcp-line, #cbd5e1); color: inherit; }
|
|
750
|
+
.uhcp-eyebrow { color: var(--uhcp-action-end, #2563a9); font-size: .78rem; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
|
|
751
|
+
.uhcp-muted { color: var(--uhcp-muted, #526174); line-height: 1.55; }
|
|
752
|
+
.uhcp-auth-separator { align-items: center; color: var(--uhcp-muted, #526174); display: flex; gap: .75rem; }
|
|
753
|
+
.uhcp-auth-separator::before, .uhcp-auth-separator::after { background: var(--uhcp-line, #cbd5e1); content: ''; flex: 1; height: 1px; }
|
|
754
|
+
.uhcp-auth-progress { align-items: center; display: flex; font-weight: 750; gap: .6rem; }
|
|
755
|
+
.uhcp-auth-progress > span { animation: uhcp-auth-spin .8s linear infinite; border: 3px solid var(--uhcp-line, #cbd5e1); border-radius: 50%; border-top-color: var(--uhcp-action-end, #2563a9); height: 1rem; width: 1rem; }
|
|
756
|
+
@keyframes uhcp-auth-spin { to { transform: rotate(360deg); } }
|
package/package.json
CHANGED