uhc-portal-components-react 0.1.39 → 0.1.41
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 +24 -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/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/SubjectCommunicationCenter.d.ts +13 -1
- package/dist/SubjectCommunicationCenter.d.ts.map +1 -1
- package/dist/SubjectCommunicationCenter.js +10 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/styles.css +51 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.41 - 2026-10-05
|
|
6
|
+
|
|
7
|
+
- Add one brand-neutral professional access panel shared by Partner and Test
|
|
8
|
+
Network Google/email-OTP/passkey entry and by an isolated six-digit Demo
|
|
9
|
+
adapter. Demo presentation cannot switch shared GW or DataConv services into
|
|
10
|
+
an in-memory mode through a browser-authored header.
|
|
11
|
+
- Hide passkey entry when the host has not supplied a real WebAuthn adapter;
|
|
12
|
+
an informational click is not represented as authentication.
|
|
13
|
+
- Add a host-controlled animal controller attestation panel for pending
|
|
14
|
+
owner/caregiver invitations and privacy-safe accepted proof readback. The
|
|
15
|
+
component never replaces a controller, sends a notification or grants
|
|
16
|
+
clinical access on its own.
|
|
17
|
+
- Add a shared current-subject registration provider so Personal and
|
|
18
|
+
Professional can render the selected person or animal in the common header
|
|
19
|
+
without duplicating portal state.
|
|
20
|
+
- Add a browser speech-recognition adapter whose final transcript remains
|
|
21
|
+
editable and whose injected constructor is testable outside a browser.
|
|
22
|
+
|
|
23
|
+
## 0.1.40
|
|
24
|
+
|
|
25
|
+
- Present subject Communications as an accessible bidirectional channel with
|
|
26
|
+
incoming/outgoing bubbles, sender avatars and an optional text/Bundle
|
|
27
|
+
composer, while preserving the existing message, call and emergency filters.
|
|
28
|
+
|
|
5
29
|
## 0.1.39
|
|
6
30
|
|
|
7
31
|
- Keep clinical field labels unique to their inputs when speech controls are
|
|
@@ -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,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
|
+
}
|
|
@@ -11,6 +11,17 @@ export type SubjectCommunicationItem = Readonly<{
|
|
|
11
11
|
timestamp?: string;
|
|
12
12
|
status?: string;
|
|
13
13
|
action?: ReactNode;
|
|
14
|
+
direction?: 'incoming' | 'outgoing' | 'system';
|
|
15
|
+
senderLabel?: string;
|
|
16
|
+
avatarUrl?: string;
|
|
17
|
+
}>;
|
|
18
|
+
export type SubjectCommunicationComposer = Readonly<{
|
|
19
|
+
placeholder: string;
|
|
20
|
+
sendLabel: string;
|
|
21
|
+
attachLabel?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
onSend(text: string): void | Promise<void>;
|
|
24
|
+
onAttach?(): void | Promise<void>;
|
|
14
25
|
}>;
|
|
15
26
|
export type SubjectCommunicationCenterCopy = Readonly<{
|
|
16
27
|
heading(subjectLabel: string): string;
|
|
@@ -18,9 +29,10 @@ export type SubjectCommunicationCenterCopy = Readonly<{
|
|
|
18
29
|
empty: string;
|
|
19
30
|
}>;
|
|
20
31
|
/** Brand-neutral, subject-scoped message and internet-call timeline for personal portals. */
|
|
21
|
-
export declare function SubjectCommunicationCenter({ subjectLabel, items, copy }: Readonly<{
|
|
32
|
+
export declare function SubjectCommunicationCenter({ subjectLabel, items, copy, composer }: Readonly<{
|
|
22
33
|
subjectLabel: string;
|
|
23
34
|
items: readonly SubjectCommunicationItem[];
|
|
24
35
|
copy?: SubjectCommunicationCenterCopy;
|
|
36
|
+
composer?: SubjectCommunicationComposer;
|
|
25
37
|
}>): import("react").JSX.Element;
|
|
26
38
|
//# sourceMappingURL=SubjectCommunicationCenter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubjectCommunicationCenter.d.ts","sourceRoot":"","sources":["../src/SubjectCommunicationCenter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAA;AAEzD,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAA;AACxG,MAAM,MAAM,4BAA4B,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAA;AACzE,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,CAAA;AAEnF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,wBAAwB,CAAA;IAC9B,QAAQ,EAAE,4BAA4B,CAAA;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;IACrC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC,CAAA;IAC7D,KAAK,EAAE,MAAM,CAAA;CACd,CAAC,CAAA;AAUF,6FAA6F;AAC7F,wBAAgB,0BAA0B,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,IAAmB,EAAE,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"SubjectCommunicationCenter.d.ts","sourceRoot":"","sources":["../src/SubjectCommunicationCenter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAA;AAEzD,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAA;AACxG,MAAM,MAAM,4BAA4B,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAA;AACzE,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,CAAA;AAEnF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,wBAAwB,CAAA;IAC9B,QAAQ,EAAE,4BAA4B,CAAA;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAA;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,QAAQ,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;IACrC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC,CAAA;IAC7D,KAAK,EAAE,MAAM,CAAA;CACd,CAAC,CAAA;AAUF,6FAA6F;AAC7F,wBAAgB,0BAA0B,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,IAAmB,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC;IAC1G,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAA;IAC1C,IAAI,CAAC,EAAE,8BAA8B,CAAA;IACrC,QAAQ,CAAC,EAAE,4BAA4B,CAAA;CACxC,CAAC,+BAgDD"}
|
|
@@ -7,10 +7,18 @@ const DEFAULT_COPY = {
|
|
|
7
7
|
};
|
|
8
8
|
const FILTERS = ['all', 'messages', 'calls', 'emergency'];
|
|
9
9
|
/** Brand-neutral, subject-scoped message and internet-call timeline for personal portals. */
|
|
10
|
-
export function SubjectCommunicationCenter({ subjectLabel, items, copy = DEFAULT_COPY }) {
|
|
10
|
+
export function SubjectCommunicationCenter({ subjectLabel, items, copy = DEFAULT_COPY, composer }) {
|
|
11
11
|
const [filter, setFilter] = useState('all');
|
|
12
|
+
const [draft, setDraft] = useState('');
|
|
12
13
|
const visibleItems = useMemo(() => items.filter(item => matchesFilter(item, filter)), [filter, items]);
|
|
13
|
-
return _jsxs("section", { className: "subject-communication-center", "aria-labelledby": "subject-communication-center-title", children: [_jsx("h2", { id: "subject-communication-center-title", children: copy.heading(subjectLabel) }), _jsx("div", { className: "subject-communication-filters", "aria-label": copy.heading(subjectLabel), children: FILTERS.map(value => _jsx("button", { type: "button", "aria-pressed": filter === value, onClick: () => setFilter(value), children: copy.filters[value] }, value)) }), visibleItems.length === 0 ? _jsx("p", { className: "subject-communication-empty", children: copy.empty }) : _jsx("ol", { className: "subject-communication-list", children: visibleItems.map(item => _jsxs("li", { "data-kind": item.kind, "data-category": item.category, children: [_jsxs("div", { children: [_jsx("strong", { children: item.title }), item.summary ? _jsx("p", { children: item.summary }) : null] }), _jsxs("div", { className: "subject-communication-meta", children: [item.timestamp ? _jsx("time", { dateTime: item.timestamp, children: item.timestamp }) : null, item.status ? _jsx("span", { children: item.status }) : null, item.action] })] }, item.id)) })
|
|
14
|
+
return _jsxs("section", { className: "subject-communication-center", "aria-labelledby": "subject-communication-center-title", children: [_jsx("h2", { id: "subject-communication-center-title", children: copy.heading(subjectLabel) }), _jsx("div", { className: "subject-communication-filters", "aria-label": copy.heading(subjectLabel), children: FILTERS.map(value => _jsx("button", { type: "button", "aria-pressed": filter === value, onClick: () => setFilter(value), children: copy.filters[value] }, value)) }), visibleItems.length === 0 ? _jsx("p", { className: "subject-communication-empty", children: copy.empty }) : _jsx("ol", { className: "subject-communication-list", role: "log", children: visibleItems.map(item => _jsxs("li", { "data-kind": item.kind, "data-category": item.category, "data-direction": item.direction || 'system', children: [item.avatarUrl ? _jsx("img", { className: "subject-communication-avatar", src: item.avatarUrl, alt: item.senderLabel || item.title }) : null, _jsxs("div", { className: "subject-communication-bubble", children: [item.senderLabel ? _jsx("span", { className: "subject-communication-sender", children: item.senderLabel }) : null, _jsx("strong", { children: item.title }), item.summary ? _jsx("p", { children: item.summary }) : null] }), _jsxs("div", { className: "subject-communication-meta", children: [item.timestamp ? _jsx("time", { dateTime: item.timestamp, children: item.timestamp }) : null, item.status ? _jsx("span", { children: item.status }) : null, item.action] })] }, item.id)) }), composer ? _jsxs("form", { className: "subject-communication-composer", onSubmit: event => {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
const text = draft.trim();
|
|
17
|
+
if (!text || composer.disabled)
|
|
18
|
+
return;
|
|
19
|
+
void composer.onSend(text);
|
|
20
|
+
setDraft('');
|
|
21
|
+
}, children: [_jsx("label", { className: "subject-communication-composer-label", htmlFor: "subject-communication-composer-input", children: composer.placeholder }), _jsx("textarea", { id: "subject-communication-composer-input", "aria-label": composer.placeholder, placeholder: composer.placeholder, value: draft, disabled: composer.disabled, onChange: event => setDraft(event.target.value) }), _jsxs("div", { className: "subject-communication-composer-actions", children: [composer.onAttach && composer.attachLabel ? _jsx("button", { type: "button", disabled: composer.disabled, onClick: () => void composer.onAttach?.(), children: composer.attachLabel }) : null, _jsx("button", { type: "submit", disabled: composer.disabled || !draft.trim(), children: composer.sendLabel })] })] }) : null] });
|
|
14
22
|
}
|
|
15
23
|
function matchesFilter(item, filter) {
|
|
16
24
|
if (filter === 'all')
|
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';
|
|
@@ -13,6 +14,7 @@ export * from './AllClinicalSections.js';
|
|
|
13
14
|
export * from './ClinicalTimeline.js';
|
|
14
15
|
export * from './ClinicalArtifactEditor.js';
|
|
15
16
|
export * from './ClinicalSpeechInput.js';
|
|
17
|
+
export * from './BrowserClinicalSpeechInput.js';
|
|
16
18
|
export * from './ClinicalActionButton.js';
|
|
17
19
|
export * from './VitalSignArtifactEditor.js';
|
|
18
20
|
export * from './InvitationAcceptancePanel.js';
|
|
@@ -29,6 +31,8 @@ export * from './IdentityEvidenceWorkspace.js';
|
|
|
29
31
|
export * from './IdentityEvidenceDocumentPanel.js';
|
|
30
32
|
export * from './IdentityEvidenceDeclarationForm.js';
|
|
31
33
|
export * from './AnimalIdentityOnboarding.js';
|
|
34
|
+
export * from './AnimalControllerAttestationPanel.js';
|
|
32
35
|
export * from './EmergencyTaskHub.js';
|
|
33
36
|
export * from './SubjectCommunicationCenter.js';
|
|
37
|
+
export * from './ProfessionalAccessPanel.js';
|
|
34
38
|
//# 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,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';
|
|
@@ -13,6 +14,7 @@ export * from './AllClinicalSections.js';
|
|
|
13
14
|
export * from './ClinicalTimeline.js';
|
|
14
15
|
export * from './ClinicalArtifactEditor.js';
|
|
15
16
|
export * from './ClinicalSpeechInput.js';
|
|
17
|
+
export * from './BrowserClinicalSpeechInput.js';
|
|
16
18
|
export * from './ClinicalActionButton.js';
|
|
17
19
|
export * from './VitalSignArtifactEditor.js';
|
|
18
20
|
export * from './InvitationAcceptancePanel.js';
|
|
@@ -29,5 +31,7 @@ export * from './IdentityEvidenceWorkspace.js';
|
|
|
29
31
|
export * from './IdentityEvidenceDocumentPanel.js';
|
|
30
32
|
export * from './IdentityEvidenceDeclarationForm.js';
|
|
31
33
|
export * from './AnimalIdentityOnboarding.js';
|
|
34
|
+
export * from './AnimalControllerAttestationPanel.js';
|
|
32
35
|
export * from './EmergencyTaskHub.js';
|
|
33
36
|
export * from './SubjectCommunicationCenter.js';
|
|
37
|
+
export * from './ProfessionalAccessPanel.js';
|
package/dist/styles.css
CHANGED
|
@@ -669,10 +669,33 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
669
669
|
.uhcp-animal-onboarding-fields label { display: grid; gap: .35rem; }
|
|
670
670
|
.uhcp-animal-onboarding-fields input, .uhcp-animal-onboarding-fields select { box-sizing: border-box; min-height: 2.8rem; width: 100%; }
|
|
671
671
|
.uhcp-animal-onboarding-submit { justify-self: start; }
|
|
672
|
+
.uhcp-animal-controller-attestation { border: 1px solid var(--uhcp-border, #d9dee7); border-radius: 1.25rem; display: grid; gap: 1rem; padding: 1rem; }
|
|
673
|
+
.uhcp-animal-controller-attestation header h2, .uhcp-animal-controller-attestation header p { margin: 0; }
|
|
674
|
+
.uhcp-animal-controller-attestation header p { color: var(--uhcp-muted, #5f6875); margin-top: .35rem; }
|
|
675
|
+
.uhcp-animal-controller-attestation form { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
|
|
676
|
+
.uhcp-animal-controller-attestation form > label:not(.uhcp-attestation-check) { display: grid; gap: .35rem; }
|
|
677
|
+
.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%; }
|
|
678
|
+
.uhcp-animal-controller-attestation .uhcp-attestation-check { align-items: center; display: flex; gap: .55rem; grid-column: 1 / -1; }
|
|
679
|
+
.uhcp-animal-controller-attestation .uhcp-attestation-check input { min-height: 1rem; width: auto; }
|
|
680
|
+
.uhcp-animal-controller-attestation dl { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); margin: 0; }
|
|
681
|
+
.uhcp-animal-controller-attestation dt { color: var(--uhcp-muted, #5f6875); font-size: .8rem; }
|
|
682
|
+
.uhcp-animal-controller-attestation dd { font-weight: 700; margin: .2rem 0 0; overflow-wrap: anywhere; }
|
|
683
|
+
.uhcp-proof-verified { color: var(--uhcp-emerald, #087f5b); font-weight: 800; }
|
|
684
|
+
.uhcp-proof-rejected { color: var(--uhcp-danger, #b42318); font-weight: 800; }
|
|
672
685
|
.uhcp-emergency-task-hub > header h1 { font-size: clamp(1.35rem, 3vw, 1.9rem); margin: 0; }
|
|
673
686
|
.uhcp-subject-identity-card { border: 1px solid var(--uhcp-border, #d9dee7); border-radius: 1rem; display: grid; gap: 1rem; padding: 1rem; }
|
|
674
687
|
.uhcp-subject-identity-card > header { align-items: center; display: grid; gap: .8rem; grid-template-columns: auto 1fr auto; }
|
|
675
688
|
.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; }
|
|
689
|
+
.uhcp-current-subject { margin-inline-start:auto; position:relative; }
|
|
690
|
+
.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; }
|
|
691
|
+
.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; }
|
|
692
|
+
.uhcp-current-subject-initial { font-size:.9rem; font-weight:850; }
|
|
693
|
+
.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; }
|
|
694
|
+
.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; }
|
|
695
|
+
.uhcp-current-subject-summary { align-items:center; display:flex; gap:.75rem; }
|
|
696
|
+
.uhcp-current-subject-summary > span { display:grid; min-width:0; }
|
|
697
|
+
.uhcp-current-subject-summary small,.uhcp-current-subject-summary span span { color:var(--uhcp-muted, #637083); }
|
|
698
|
+
.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
699
|
.uhcp-subject-identity-avatar { background: var(--uhcp-accent-soft, #e9f3ff); font-size: 1.4rem; font-weight: 850; }
|
|
677
700
|
.uhcp-subject-identity-card h3 { margin: .15rem 0 0; }
|
|
678
701
|
.uhcp-subject-identity-card dl { display: grid; gap: .65rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); margin: 0; }
|
|
@@ -685,7 +708,34 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
685
708
|
.subject-communication-filters button { border: 1px solid currentColor; border-radius: 999px; padding: .55rem .9rem; background: transparent; font: inherit; cursor: pointer; }
|
|
686
709
|
.subject-communication-filters button[aria-pressed="true"] { background: currentColor; color: Canvas; }
|
|
687
710
|
.subject-communication-list { display: grid; gap: .75rem; margin: 0; padding: 0; list-style: none; }
|
|
688
|
-
.subject-communication-list > li { display: flex; justify-content:
|
|
711
|
+
.subject-communication-list > li { display: flex; justify-content: flex-start; gap: .75rem; align-items: end; }
|
|
712
|
+
.subject-communication-list > li[data-direction="outgoing"] { flex-direction: row-reverse; }
|
|
713
|
+
.subject-communication-list > li[data-direction="system"] { justify-content: center; }
|
|
714
|
+
.subject-communication-avatar { width: 2.5rem; height: 2.5rem; border-radius: 999px; object-fit: cover; flex: 0 0 auto; }
|
|
715
|
+
.subject-communication-bubble { max-width: min(78%, 42rem); border: 1px solid color-mix(in srgb, currentColor 18%, transparent); border-radius: 1rem 1rem 1rem .25rem; padding: .8rem 1rem; background: color-mix(in srgb, Canvas 94%, currentColor 6%); }
|
|
716
|
+
.subject-communication-list > li[data-direction="outgoing"] .subject-communication-bubble { border-radius: 1rem 1rem .25rem 1rem; background: color-mix(in srgb, #66c8d1 18%, Canvas); }
|
|
717
|
+
.subject-communication-sender { display: block; margin-bottom: .2rem; font-size: .8em; font-weight: 700; opacity: .72; }
|
|
689
718
|
.subject-communication-list p { margin: .35rem 0 0; }
|
|
690
719
|
.subject-communication-meta { display: grid; justify-items: end; gap: .35rem; font-size: .875em; }
|
|
691
720
|
.subject-communication-empty { margin: 0; opacity: .72; }
|
|
721
|
+
.subject-communication-composer { display: grid; gap: .65rem; border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent); padding-top: 1rem; }
|
|
722
|
+
.subject-communication-composer-label { font-weight: 700; }
|
|
723
|
+
.subject-communication-composer textarea { min-height: 5rem; resize: vertical; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 1rem; padding: .8rem 1rem; font: inherit; color: inherit; background: Canvas; }
|
|
724
|
+
.subject-communication-composer-actions { display: flex; justify-content: flex-end; gap: .65rem; }
|
|
725
|
+
.subject-communication-composer-actions button { border: 1px solid currentColor; border-radius: 999px; padding: .55rem .9rem; background: transparent; color: inherit; font: inherit; cursor: pointer; }
|
|
726
|
+
.subject-communication-composer-actions button[type="submit"] { background: currentColor; color: Canvas; }
|
|
727
|
+
.uhcp-professional-access { display: grid; gap: 1rem; margin-inline: auto; max-width: 38rem; }
|
|
728
|
+
.uhcp-professional-access h1, .uhcp-professional-access p { margin-block: 0; }
|
|
729
|
+
.uhcp-professional-access form { display: grid; gap: .9rem; }
|
|
730
|
+
.uhcp-professional-access label { display: grid; font-weight: 700; gap: .4rem; }
|
|
731
|
+
.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; }
|
|
732
|
+
.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; }
|
|
733
|
+
.uhcp-professional-access button:disabled { cursor: wait; opacity: .55; }
|
|
734
|
+
.uhcp-professional-access .uhcp-secondary { background: transparent; border: 1px solid var(--uhcp-line, #cbd5e1); color: inherit; }
|
|
735
|
+
.uhcp-eyebrow { color: var(--uhcp-action-end, #2563a9); font-size: .78rem; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
|
|
736
|
+
.uhcp-muted { color: var(--uhcp-muted, #526174); line-height: 1.55; }
|
|
737
|
+
.uhcp-auth-separator { align-items: center; color: var(--uhcp-muted, #526174); display: flex; gap: .75rem; }
|
|
738
|
+
.uhcp-auth-separator::before, .uhcp-auth-separator::after { background: var(--uhcp-line, #cbd5e1); content: ''; flex: 1; height: 1px; }
|
|
739
|
+
.uhcp-auth-progress { align-items: center; display: flex; font-weight: 750; gap: .6rem; }
|
|
740
|
+
.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; }
|
|
741
|
+
@keyframes uhcp-auth-spin { to { transform: rotate(360deg); } }
|
package/package.json
CHANGED