uhc-portal-components-react 0.2.18 → 0.2.19
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 +7 -0
- package/README.md +6 -0
- package/dist/CommunicationsWorkspace.d.ts +18 -0
- package/dist/CommunicationsWorkspace.d.ts.map +1 -0
- package/dist/CommunicationsWorkspace.js +12 -0
- package/dist/NotificationComposer.d.ts +49 -0
- package/dist/NotificationComposer.d.ts.map +1 -0
- package/dist/NotificationComposer.js +82 -0
- package/dist/SelectedSubjectWorkspace.d.ts +32 -0
- package/dist/SelectedSubjectWorkspace.d.ts.map +1 -0
- package/dist/SelectedSubjectWorkspace.js +30 -0
- package/dist/SoschainWorkspace.d.ts +3 -1
- package/dist/SoschainWorkspace.d.ts.map +1 -1
- package/dist/SoschainWorkspace.js +2 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/notification-bff-client.d.ts +44 -0
- package/dist/notification-bff-client.d.ts.map +1 -0
- package/dist/notification-bff-client.js +88 -0
- package/dist/styles.css +37 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Add one neutral notification composer screen for subject-addressed care
|
|
6
|
+
transitions and public-dataset alerts, with safe Markdown preview and host-
|
|
7
|
+
uploaded image/PDF attachment references.
|
|
8
|
+
- Define independent portal deployments as the supported reuse model while
|
|
9
|
+
requiring versioned complete screens, one typed public BFF path contract and
|
|
10
|
+
one capability manifest across web, mobile, telephone, chat and assistant
|
|
11
|
+
hosts.
|
|
5
12
|
- Require confirmation for employee suspension, reactivation and permanent
|
|
6
13
|
deletion; expose only valid lifecycle actions and distinguish warning,
|
|
7
14
|
constructive and destructive actions with yellow, blue and red treatments.
|
package/README.md
CHANGED
|
@@ -193,6 +193,12 @@ management, licences, clinical data, care and permissions still live in the
|
|
|
193
193
|
UHC host application and must be extracted behind host-owned adapters. A brand
|
|
194
194
|
adapter may change tokens, logo, product vocabulary, animal-specific forms and
|
|
195
195
|
route destinations; it must not fork shared flow or authorization behavior.
|
|
196
|
+
Independent deployments are expected: reuse means versioned complete screens,
|
|
197
|
+
one typed BFF path contract and one channel-neutral capability manifest, not a
|
|
198
|
+
mandatory shared deployed shell. Telephone and assistant templates consume
|
|
199
|
+
that same manifest and operation client so a subject-domain or jurisdiction
|
|
200
|
+
deployment changes trusted configuration and adapters rather than copying the
|
|
201
|
+
workflow.
|
|
196
202
|
|
|
197
203
|
See [`docs/PERSONAL_FLOW_EXTRACTION.md`](docs/PERSONAL_FLOW_EXTRACTION.md) and
|
|
198
204
|
the canonical cross-portal
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type CommunicationsWorkspaceTopic = Readonly<{
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
content?: ReactNode;
|
|
7
|
+
}>;
|
|
8
|
+
/** Shared personal-portal navigation; hosts provide channel and topic data through their own BFF adapters. */
|
|
9
|
+
export declare function CommunicationsWorkspace(input: Readonly<{
|
|
10
|
+
copy: Readonly<{
|
|
11
|
+
communicationsLabel: string;
|
|
12
|
+
channels: string;
|
|
13
|
+
infoAndAlerts: string;
|
|
14
|
+
}>;
|
|
15
|
+
channels: ReactNode;
|
|
16
|
+
alertTopics: readonly CommunicationsWorkspaceTopic[];
|
|
17
|
+
}>): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=CommunicationsWorkspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommunicationsWorkspace.d.ts","sourceRoot":"","sources":["../src/CommunicationsWorkspace.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEvD,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAC,CAAA;AAEF,8GAA8G;AAC9G,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC;IACtD,IAAI,EAAE,QAAQ,CAAC;QAAE,mBAAmB,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxF,QAAQ,EAAE,SAAS,CAAA;IACnB,WAAW,EAAE,SAAS,4BAA4B,EAAE,CAAA;CACrD,CAAC,+BAmBD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from 'react';
|
|
4
|
+
/** Shared personal-portal navigation; hosts provide channel and topic data through their own BFF adapters. */
|
|
5
|
+
export function CommunicationsWorkspace(input) {
|
|
6
|
+
const id = useId();
|
|
7
|
+
const [section, setSection] = useState('channels');
|
|
8
|
+
const firstEnabledTopic = input.alertTopics.find((topic) => !topic.disabled);
|
|
9
|
+
const [topicId, setTopicId] = useState(firstEnabledTopic?.id ?? '');
|
|
10
|
+
const activeTopic = input.alertTopics.find((topic) => topic.id === topicId && !topic.disabled);
|
|
11
|
+
return _jsxs("section", { className: "uhcp-communications-workspace", children: [_jsxs("div", { role: "tablist", "aria-label": input.copy.communicationsLabel, className: "uhcp-communications-tabs", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": section === 'channels', "aria-controls": `${id}-channels`, onClick: () => setSection('channels'), children: input.copy.channels }), _jsx("button", { type: "button", role: "tab", "aria-selected": section === 'alerts', "aria-controls": `${id}-alerts`, onClick: () => setSection('alerts'), children: input.copy.infoAndAlerts })] }), section === 'channels' ? _jsx("div", { id: `${id}-channels`, role: "tabpanel", children: input.channels }) : _jsxs("div", { id: `${id}-alerts`, role: "tabpanel", children: [_jsx("div", { role: "tablist", "aria-label": input.copy.infoAndAlerts, className: "uhcp-communications-topic-tabs", children: input.alertTopics.map((topic) => _jsx("button", { type: "button", role: "tab", disabled: topic.disabled, "aria-selected": activeTopic?.id === topic.id, onClick: () => setTopicId(topic.id), children: topic.label }, topic.id)) }), activeTopic?.content ? _jsx("div", { role: "tabpanel", children: activeTopic.content }) : null] })] });
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type NotificationAttachment, type NotificationComposerAudience, type NotificationComposerDraft } from 'sos-data-utils-ts/public-notifications';
|
|
2
|
+
export type NotificationComposerAudienceOption = Readonly<{
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
audience: NotificationComposerAudience;
|
|
7
|
+
}>;
|
|
8
|
+
export type NotificationComposerCopy = Readonly<{
|
|
9
|
+
title: string;
|
|
10
|
+
audience: string;
|
|
11
|
+
subject: string;
|
|
12
|
+
bodyMarkdown: string;
|
|
13
|
+
preview: string;
|
|
14
|
+
attachments: string;
|
|
15
|
+
addAttachments: string;
|
|
16
|
+
removeAttachment: string;
|
|
17
|
+
send: string;
|
|
18
|
+
sending: string;
|
|
19
|
+
sent: string;
|
|
20
|
+
unsupportedAttachment: string;
|
|
21
|
+
attachmentUploadFailed: string;
|
|
22
|
+
sendFailed: string;
|
|
23
|
+
priority?: string;
|
|
24
|
+
validUntil?: string;
|
|
25
|
+
}>;
|
|
26
|
+
export type NotificationComposerFormSchema = Readonly<{
|
|
27
|
+
$id: string;
|
|
28
|
+
required: readonly string[];
|
|
29
|
+
properties: Readonly<Record<string, Readonly<{
|
|
30
|
+
type: string;
|
|
31
|
+
enum?: readonly string[];
|
|
32
|
+
maxLength?: number;
|
|
33
|
+
maxItems?: number;
|
|
34
|
+
}>>>;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Channel-neutral notification editor. The host supplies BFF-authorized
|
|
38
|
+
* audiences plus upload/send adapters; this component never accepts direct
|
|
39
|
+
* recipient addresses, subscriber lists or publishing authority.
|
|
40
|
+
*/
|
|
41
|
+
export declare function NotificationComposer(input: Readonly<{
|
|
42
|
+
audiences: readonly NotificationComposerAudienceOption[];
|
|
43
|
+
copy: NotificationComposerCopy;
|
|
44
|
+
uploadAttachment(file: File): Promise<NotificationAttachment>;
|
|
45
|
+
onSubmit(draft: NotificationComposerDraft): Promise<void>;
|
|
46
|
+
formSchema?: NotificationComposerFormSchema;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
}>): import("react").JSX.Element;
|
|
49
|
+
//# sourceMappingURL=NotificationComposer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationComposer.d.ts","sourceRoot":"","sources":["../src/NotificationComposer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAE/B,MAAM,wCAAwC,CAAA;AAG/C,MAAM,MAAM,kCAAkC,GAAG,QAAQ,CAAC;IACxD,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,4BAA4B,CAAA;CACvC,CAAC,CAAA;AAEF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,qBAAqB,EAAE,MAAM,CAAA;IAC7B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QACxB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAC,CAAC,CAAC,CAAA;CACL,CAAC,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;IACnD,SAAS,EAAE,SAAS,kCAAkC,EAAE,CAAA;IACxD,IAAI,EAAE,wBAAwB,CAAA;IAC9B,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAC7D,QAAQ,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,UAAU,CAAC,EAAE,8BAA8B,CAAA;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAC,+BAmKD"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from 'react';
|
|
4
|
+
import { normalizeNotificationComposerDraft, NotificationAttachmentMediaTypes, PublicAlertPriorities, } from 'sos-data-utils-ts/public-notifications';
|
|
5
|
+
import { LegalMarkdownDocument } from './LegalMarkdownDocument.js';
|
|
6
|
+
/**
|
|
7
|
+
* Channel-neutral notification editor. The host supplies BFF-authorized
|
|
8
|
+
* audiences plus upload/send adapters; this component never accepts direct
|
|
9
|
+
* recipient addresses, subscriber lists or publishing authority.
|
|
10
|
+
*/
|
|
11
|
+
export function NotificationComposer(input) {
|
|
12
|
+
const id = useId();
|
|
13
|
+
const [audienceId, setAudienceId] = useState(input.audiences[0]?.id ?? '');
|
|
14
|
+
const [subject, setSubject] = useState('');
|
|
15
|
+
const [bodyMarkdown, setBodyMarkdown] = useState('');
|
|
16
|
+
const [priority, setPriority] = useState('routine');
|
|
17
|
+
const [validUntil, setValidUntil] = useState('');
|
|
18
|
+
const [attachments, setAttachments] = useState([]);
|
|
19
|
+
const [working, setWorking] = useState(false);
|
|
20
|
+
const [status, setStatus] = useState('');
|
|
21
|
+
const [error, setError] = useState('');
|
|
22
|
+
const audience = input.audiences.find((candidate) => candidate.id === audienceId);
|
|
23
|
+
const disabled = Boolean(input.disabled || working);
|
|
24
|
+
const publicAlertForm = Boolean(input.formSchema);
|
|
25
|
+
const priorityOptions = input.formSchema?.properties.priority?.enum ?? PublicAlertPriorities;
|
|
26
|
+
const subjectMaxLength = input.formSchema?.properties.subject?.maxLength;
|
|
27
|
+
const bodyMaxLength = input.formSchema?.properties.bodyMarkdown?.maxLength;
|
|
28
|
+
const attachmentLimit = input.formSchema?.properties.attachments?.maxItems;
|
|
29
|
+
async function addAttachments(files) {
|
|
30
|
+
if (!files?.length)
|
|
31
|
+
return;
|
|
32
|
+
setError('');
|
|
33
|
+
const selectedFiles = Array.from(files);
|
|
34
|
+
if (attachmentLimit !== undefined && attachments.length + selectedFiles.length > attachmentLimit) {
|
|
35
|
+
setError(input.copy.unsupportedAttachment);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (selectedFiles.some((file) => !NotificationAttachmentMediaTypes.includes(file.type))) {
|
|
39
|
+
setError(input.copy.unsupportedAttachment);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setWorking(true);
|
|
43
|
+
try {
|
|
44
|
+
const uploaded = await Promise.all(selectedFiles.map((file) => input.uploadAttachment(file)));
|
|
45
|
+
setAttachments((current) => Object.freeze([...current, ...uploaded]));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
setError(input.copy.attachmentUploadFailed);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
setWorking(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function submit() {
|
|
55
|
+
if (!audience || !subject.trim() || !bodyMarkdown.trim())
|
|
56
|
+
return;
|
|
57
|
+
setWorking(true);
|
|
58
|
+
setStatus('');
|
|
59
|
+
setError('');
|
|
60
|
+
try {
|
|
61
|
+
const draft = normalizeNotificationComposerDraft({
|
|
62
|
+
audience: audience.audience,
|
|
63
|
+
subject,
|
|
64
|
+
bodyMarkdown,
|
|
65
|
+
attachments,
|
|
66
|
+
...(publicAlertForm ? {
|
|
67
|
+
priority,
|
|
68
|
+
validUntil: new Date(validUntil).toISOString(),
|
|
69
|
+
} : {}),
|
|
70
|
+
});
|
|
71
|
+
await input.onSubmit(draft);
|
|
72
|
+
setStatus(input.copy.sent);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
setError(input.copy.sendFailed);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
setWorking(false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return _jsxs("section", { className: "uhcp-notification-composer", "aria-labelledby": `${id}-title`, "data-form-schema": input.formSchema?.$id, children: [_jsx("header", { children: _jsx("h2", { id: `${id}-title`, children: input.copy.title }) }), _jsx("label", { htmlFor: `${id}-audience`, children: input.copy.audience }), _jsx("select", { id: `${id}-audience`, value: audienceId, disabled: disabled, onChange: (event) => setAudienceId(event.currentTarget.value), children: input.audiences.map((option) => _jsx("option", { value: option.id, children: option.label }, option.id)) }), audience?.description ? _jsx("p", { className: "uhcp-muted", children: audience.description }) : null, _jsx("label", { htmlFor: `${id}-subject`, children: input.copy.subject }), _jsx("input", { id: `${id}-subject`, value: subject, maxLength: subjectMaxLength, disabled: disabled, onChange: (event) => setSubject(event.currentTarget.value) }), publicAlertForm ? _jsxs(_Fragment, { children: [_jsx("label", { htmlFor: `${id}-priority`, children: input.copy.priority || 'Priority' }), _jsx("select", { id: `${id}-priority`, value: priority, disabled: disabled, onChange: (event) => setPriority(event.currentTarget.value), children: priorityOptions.map((value) => _jsx("option", { value: value, children: value }, value)) }), _jsx("label", { htmlFor: `${id}-valid-until`, children: input.copy.validUntil || 'Valid until' }), _jsx("input", { id: `${id}-valid-until`, type: "datetime-local", value: validUntil, required: true, disabled: disabled, onChange: (event) => setValidUntil(event.currentTarget.value) })] }) : null, _jsxs("div", { className: "uhcp-notification-composer-body", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: `${id}-body`, children: input.copy.bodyMarkdown }), _jsx("textarea", { id: `${id}-body`, value: bodyMarkdown, maxLength: bodyMaxLength, disabled: disabled, onChange: (event) => setBodyMarkdown(event.currentTarget.value) })] }), _jsxs("section", { "aria-labelledby": `${id}-preview`, children: [_jsx("h3", { id: `${id}-preview`, children: input.copy.preview }), _jsx(LegalMarkdownDocument, { source: bodyMarkdown })] })] }), _jsxs("fieldset", { disabled: disabled, children: [_jsx("legend", { children: input.copy.attachments }), _jsx("label", { htmlFor: `${id}-attachments`, children: input.copy.addAttachments }), _jsx("input", { id: `${id}-attachments`, type: "file", multiple: true, accept: NotificationAttachmentMediaTypes.join(','), onChange: (event) => void addAttachments(event.currentTarget.files) }), attachments.length > 0 ? _jsx("ul", { children: attachments.map((attachment) => _jsxs("li", { children: [_jsx("span", { children: attachment.fileName }), _jsx("button", { type: "button", onClick: () => setAttachments((current) => current.filter((item) => item.attachmentId !== attachment.attachmentId)), children: input.copy.removeAttachment })] }, attachment.attachmentId)) }) : null] }), _jsx("button", { type: "button", disabled: disabled || !audience || !subject.trim() || !bodyMarkdown.trim() || (publicAlertForm && !validUntil), onClick: () => void submit(), children: working ? input.copy.sending : input.copy.send }), status ? _jsx("p", { role: "status", children: status }) : null, error ? _jsx("p", { role: "alert", children: error }) : null] });
|
|
82
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export declare const SELECTED_SUBJECT_AREAS: readonly ["identity", "health", "insurance", "appointments-tasks", "communications", "contacts-permissions"];
|
|
3
|
+
export type SelectedSubjectArea = (typeof SELECTED_SUBJECT_AREAS)[number];
|
|
4
|
+
export type SelectedSubjectAction = 'search' | 'read' | 'create' | 'update' | 'delete';
|
|
5
|
+
export type SelectedSubjectAreaCapability = Readonly<{
|
|
6
|
+
area: SelectedSubjectArea;
|
|
7
|
+
actions: readonly SelectedSubjectAction[];
|
|
8
|
+
recordCount?: number;
|
|
9
|
+
}>;
|
|
10
|
+
export type SelectedSubjectWorkspaceCopy = Readonly<{
|
|
11
|
+
card: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
identity: string;
|
|
14
|
+
health: string;
|
|
15
|
+
insurance: string;
|
|
16
|
+
appointmentsTasks: string;
|
|
17
|
+
communications: string;
|
|
18
|
+
contactsPermissions: string;
|
|
19
|
+
records: string;
|
|
20
|
+
}>;
|
|
21
|
+
/** Capability-driven professional subject shell; selection never grants an action. */
|
|
22
|
+
export declare function SelectedSubjectWorkspace({ subject, capabilities, copy, content, initialArea }: Readonly<{
|
|
23
|
+
subject: Readonly<{
|
|
24
|
+
id: string;
|
|
25
|
+
display: string;
|
|
26
|
+
}>;
|
|
27
|
+
capabilities: readonly SelectedSubjectAreaCapability[];
|
|
28
|
+
copy: SelectedSubjectWorkspaceCopy;
|
|
29
|
+
content?: Partial<Record<SelectedSubjectArea, ReactNode>>;
|
|
30
|
+
initialArea?: 'card' | SelectedSubjectArea;
|
|
31
|
+
}>): import("react").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=SelectedSubjectWorkspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectedSubjectWorkspace.d.ts","sourceRoot":"","sources":["../src/SelectedSubjectWorkspace.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEpE,eAAO,MAAM,sBAAsB,8GAA+G,CAAA;AAClJ,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AACzE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AACtF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AACpJ,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAc5O,sFAAsF;AACtF,wBAAgB,wBAAwB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,OAAY,EAAE,WAAoB,EAAE,EAAE,QAAQ,CAAC;IACrH,OAAO,EAAE,QAAQ,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,YAAY,EAAE,SAAS,6BAA6B,EAAE,CAAA;IACtD,IAAI,EAAE,4BAA4B,CAAA;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,CAAA;IACzD,WAAW,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAA;CAC3C,CAAC,+BAmBD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
export const SELECTED_SUBJECT_AREAS = ['identity', 'health', 'insurance', 'appointments-tasks', 'communications', 'contacts-permissions'];
|
|
5
|
+
const copyKey = { identity: 'identity', health: 'health', insurance: 'insurance', 'appointments-tasks': 'appointmentsTasks', communications: 'communications', 'contacts-permissions': 'contactsPermissions' };
|
|
6
|
+
function mergeCapabilities(capabilities) {
|
|
7
|
+
const merged = new Map();
|
|
8
|
+
for (const capability of capabilities) {
|
|
9
|
+
if (!capability.actions.length)
|
|
10
|
+
continue;
|
|
11
|
+
const previous = merged.get(capability.area);
|
|
12
|
+
merged.set(capability.area, { area: capability.area, actions: [...new Set([...(previous?.actions ?? []), ...capability.actions])], recordCount: capability.actions.includes('search') ? capability.recordCount ?? previous?.recordCount : previous?.recordCount });
|
|
13
|
+
}
|
|
14
|
+
return merged;
|
|
15
|
+
}
|
|
16
|
+
/** Capability-driven professional subject shell; selection never grants an action. */
|
|
17
|
+
export function SelectedSubjectWorkspace({ subject, capabilities, copy, content = {}, initialArea = 'card' }) {
|
|
18
|
+
const capabilityByArea = useMemo(() => mergeCapabilities(capabilities), [capabilities]);
|
|
19
|
+
const visibleAreas = SELECTED_SUBJECT_AREAS.filter((area) => capabilityByArea.has(area));
|
|
20
|
+
const [selectedArea, setSelectedArea] = useState(initialArea);
|
|
21
|
+
useEffect(() => { if (selectedArea !== 'card' && !capabilityByArea.has(selectedArea))
|
|
22
|
+
setSelectedArea('card'); }, [capabilityByArea, selectedArea]);
|
|
23
|
+
const labelFor = (area) => {
|
|
24
|
+
const label = copy[copyKey[area]];
|
|
25
|
+
const capability = capabilityByArea.get(area);
|
|
26
|
+
return capability?.actions.includes('search') && capability.recordCount !== undefined ? `${label}, ${capability.recordCount} ${copy.records}` : label;
|
|
27
|
+
};
|
|
28
|
+
const selectedCapability = selectedArea === 'card' ? undefined : capabilityByArea.get(selectedArea);
|
|
29
|
+
return _jsxs("section", { className: "uhcp-selected-subject-workspace", "aria-labelledby": "uhcp-selected-subject-title", children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("span", { children: copy.summary }), _jsx("h2", { id: "uhcp-selected-subject-title", children: subject.display })] }), _jsx("code", { children: subject.id })] }), _jsxs("div", { className: "uhcp-selected-subject-tabs", role: "tablist", "aria-label": subject.display, children: [_jsx("button", { type: "button", role: "tab", "aria-selected": selectedArea === 'card', onClick: () => setSelectedArea('card'), children: copy.card }), visibleAreas.map((area) => _jsx("button", { type: "button", role: "tab", "aria-selected": selectedArea === area, onClick: () => setSelectedArea(area), children: labelFor(area) }, area))] }), _jsx("div", { role: "tabpanel", children: selectedArea === 'card' ? _jsxs("div", { children: [_jsx("h3", { children: copy.summary }), _jsx("ul", { children: visibleAreas.map((area) => _jsx("li", { children: labelFor(area) }, area)) })] }) : selectedCapability?.actions.includes('read') ? content[selectedArea] : null })] });
|
|
30
|
+
}
|
|
@@ -82,6 +82,8 @@ export type SoschainWorkspaceProps = Readonly<{
|
|
|
82
82
|
places: readonly SoschainAssociationSummary[];
|
|
83
83
|
products: readonly SoschainAssociationSummary[];
|
|
84
84
|
communications: readonly SoschainCommunicationSummary[];
|
|
85
|
+
/** Shared channels/topics screen composed with host-owned BFF adapters. */
|
|
86
|
+
communicationsContent?: ReactNode;
|
|
85
87
|
/** Host-authorized IPS/clinical projection, normally `IpsBundleSummary`. */
|
|
86
88
|
healthSummary?: ReactNode;
|
|
87
89
|
/** False only when the host has already resolved a current write capability. */
|
|
@@ -109,5 +111,5 @@ export type SoschainWorkspaceProps = Readonly<{
|
|
|
109
111
|
* communications. This presentational component never derives authority from
|
|
110
112
|
* a selected role, card, tab or browser-controlled value.
|
|
111
113
|
*/
|
|
112
|
-
export declare function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, healthSummary, clinicalReadOnly, careJourney, emergencyData, canManageAssociations, addPlaceHref, addProductHref, importCardHref, overviewContent, primaryAreas, moreLabel, showCardIdentifier, onSelectCard, }: SoschainWorkspaceProps): import("react").JSX.Element;
|
|
114
|
+
export declare function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, communicationsContent, healthSummary, clinicalReadOnly, careJourney, emergencyData, canManageAssociations, addPlaceHref, addProductHref, importCardHref, overviewContent, primaryAreas, moreLabel, showCardIdentifier, onSelectCard, }: SoschainWorkspaceProps): import("react").JSX.Element;
|
|
113
115
|
//# sourceMappingURL=SoschainWorkspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SoschainWorkspace.d.ts","sourceRoot":"","sources":["../src/SoschainWorkspace.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SoschainWorkspace.d.ts","sourceRoot":"","sources":["../src/SoschainWorkspace.tsx"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ function AssociationList({ items, empty }) {
|
|
|
21
21
|
* communications. This presentational component never derives authority from
|
|
22
22
|
* a selected role, card, tab or browser-controlled value.
|
|
23
23
|
*/
|
|
24
|
-
export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, healthSummary, clinicalReadOnly = true, careJourney = [], emergencyData = [], canManageAssociations, addPlaceHref, addProductHref, importCardHref, overviewContent, primaryAreas, moreLabel = 'More', showCardIdentifier = true, onSelectCard, }) {
|
|
24
|
+
export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaHrefs, places, products, communications, communicationsContent, healthSummary, clinicalReadOnly = true, careJourney = [], emergencyData = [], canManageAssociations, addPlaceHref, addProductHref, importCardHref, overviewContent, primaryAreas, moreLabel = 'More', showCardIdentifier = true, onSelectCard, }) {
|
|
25
25
|
if (!selectedCard) {
|
|
26
26
|
return (_jsxs("section", { className: "uhcp-sos-wrap", "aria-labelledby": "sos-card-picker-title", children: [_jsxs("header", { className: "uhcp-sos-heading", children: [_jsx("span", { children: copy.workspaceEyebrow }), _jsx("h1", { id: "sos-card-picker-title", children: copy.cardsTitle }), _jsx("p", { children: copy.cardsDescription }), importCardHref && copy.importCard ? _jsxs("a", { href: importCardHref, children: [copy.importCard, " \u2192"] }) : null] }), cards.length === 0 ? (_jsxs("div", { className: "uhcp-sos-empty-panel", children: [_jsx("h2", { children: copy.noCardTitle }), _jsx("p", { children: copy.noCardBody })] })) : (_jsx("ul", { className: "uhcp-sos-card-grid", children: cards.map((card) => (_jsx("li", { children: onSelectCard ? _jsxs("button", { type: "button", onClick: () => onSelectCard(card.identifier), children: [_jsx("span", { children: card.kindLabel }), _jsx("strong", { children: card.displayName }), showCardIdentifier ? _jsx("small", { children: card.identifier }) : null, _jsxs("b", { children: [copy.openCard, " \u2192"] })] }) : _jsxs("a", { href: card.openHref, children: [_jsx("span", { children: card.kindLabel }), _jsx("strong", { children: card.displayName }), showCardIdentifier ? _jsx("small", { children: card.identifier }) : null, _jsxs("b", { children: [copy.openCard, " \u2192"] })] }) }, card.identifier))) }))] }));
|
|
27
27
|
}
|
|
@@ -53,5 +53,5 @@ export function SoschainWorkspace({ copy, cards, selectedCard, activeArea, areaH
|
|
|
53
53
|
: copy.overviewBody;
|
|
54
54
|
const clinicalAreaIsReadOnly = activeArea === 'care-journey' || (activeArea === 'health-summary' && clinicalReadOnly);
|
|
55
55
|
const associationAreaIsReadOnly = !canManageAssociations && (activeArea === 'overview' || activeArea === 'places' || activeArea === 'products');
|
|
56
|
-
return (_jsxs("section", { className: "uhcp-sos-wrap", children: [_jsxs("header", { className: "uhcp-sos-heading", children: [_jsx("span", { children: copy.selectedCard }), _jsx("h1", { children: selectedCard.displayName }), _jsxs("p", { children: [selectedCard.kindLabel, showCardIdentifier ? ` · ${selectedCard.identifier}` : ''] })] }), _jsxs("nav", { className: "uhcp-sos-tabs", "aria-label": copy.workspaceEyebrow, children: [(primaryAreas ?? SOSCHAIN_SUBJECT_WORKSPACE_AREAS).map((area) => (_jsx("a", { href: areaHrefs[area], "aria-current": activeArea === area ? 'page' : undefined, children: copy.areas[area] }, area))), primaryAreas ? _jsxs("details", { className: "uhcp-sos-more", children: [_jsx("summary", { children: moreLabel }), _jsx("div", { children: SOSCHAIN_SUBJECT_WORKSPACE_AREAS.filter((area) => !primaryAreas.includes(area)).map((area) => (_jsx("a", { href: areaHrefs[area], "aria-current": activeArea === area ? 'page' : undefined, children: copy.areas[area] }, area))) })] }) : null] }), _jsxs("div", { className: "uhcp-sos-panel", children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { children: title }), _jsx("p", { children: body })] }), (clinicalAreaIsReadOnly || associationAreaIsReadOnly) && (_jsx("span", { className: "uhcp-sos-readonly", children: copy.readOnly })), canManageAssociations && activeArea === 'places' && addPlaceHref && _jsx("a", { href: addPlaceHref, children: copy.addPlace }), canManageAssociations && activeArea === 'products' && addProductHref && _jsx("a", { href: addProductHref, children: copy.addProduct })] }), activeArea === 'overview' && (overviewContent ?? (_jsxs("div", { className: "uhcp-sos-overview-grid", children: [_jsxs("article", { children: [_jsx("strong", { children: places.length }), _jsx("span", { children: copy.areas.places })] }), _jsxs("article", { children: [_jsx("strong", { children: products.length }), _jsx("span", { children: copy.areas.products })] }), _jsxs("article", { children: [_jsx("strong", { children: communications.length }), _jsx("span", { children: copy.areas.communications })] }), _jsxs("article", { children: [_jsx("strong", { children: careJourney.length }), _jsx("span", { children: copy.areas['care-journey'] })] })] }))), activeArea === 'health-summary' && (healthSummary ?? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyHealthSummary })), activeArea === 'care-journey' && (careJourney.length === 0 ? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyCareJourney }) : (_jsx("ol", { className: "uhcp-sos-timeline uhcp-sos-care-journey", children: careJourney.map((encounter) => (_jsxs("li", { children: [_jsxs("div", { children: [_jsx("span", { children: encounter.statusLabel }), _jsx("time", { dateTime: encounter.periodStart, children: encounter.periodStart })] }), _jsx("strong", { children: encounter.serviceLabel }), encounter.detail ? _jsx("p", { children: encounter.detail }) : null, encounter.locationLabel || encounter.periodEnd ? _jsx("small", { children: [encounter.locationLabel, encounter.periodEnd].filter(Boolean).join(' · ') }) : null] }, encounter.identifier))) }))), activeArea === 'emergency-data' && _jsx(ProtectedIndexEmergencyData, { copy: copy.emergencyData, items: emergencyData }), activeArea === 'places' && _jsx(AssociationList, { items: places, empty: copy.emptyPlaces }), activeArea === 'products' && _jsx(AssociationList, { items: products, empty: copy.emptyProducts }), activeArea === 'communications' && (communications.length === 0 ? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyCommunications }) : (_jsx("ol", { className: "uhcp-sos-timeline", children: communications.map((message) => (_jsxs("li", { children: [_jsxs("div", { children: [_jsx("span", { children: message.categoryLabel }), _jsx("time", { children: message.occurredAt })] }), _jsx("strong", { children: message.subject }), _jsx("p", { children: message.body }), _jsxs("small", { children: [message.sender, message.placeLabel ? ` · ${message.placeLabel}` : ''] })] }, message.identifier))) })))] })] }));
|
|
56
|
+
return (_jsxs("section", { className: "uhcp-sos-wrap", children: [_jsxs("header", { className: "uhcp-sos-heading", children: [_jsx("span", { children: copy.selectedCard }), _jsx("h1", { children: selectedCard.displayName }), _jsxs("p", { children: [selectedCard.kindLabel, showCardIdentifier ? ` · ${selectedCard.identifier}` : ''] })] }), _jsxs("nav", { className: "uhcp-sos-tabs", "aria-label": copy.workspaceEyebrow, children: [(primaryAreas ?? SOSCHAIN_SUBJECT_WORKSPACE_AREAS).map((area) => (_jsx("a", { href: areaHrefs[area], "aria-current": activeArea === area ? 'page' : undefined, children: copy.areas[area] }, area))), primaryAreas ? _jsxs("details", { className: "uhcp-sos-more", children: [_jsx("summary", { children: moreLabel }), _jsx("div", { children: SOSCHAIN_SUBJECT_WORKSPACE_AREAS.filter((area) => !primaryAreas.includes(area)).map((area) => (_jsx("a", { href: areaHrefs[area], "aria-current": activeArea === area ? 'page' : undefined, children: copy.areas[area] }, area))) })] }) : null] }), _jsxs("div", { className: "uhcp-sos-panel", children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { children: title }), _jsx("p", { children: body })] }), (clinicalAreaIsReadOnly || associationAreaIsReadOnly) && (_jsx("span", { className: "uhcp-sos-readonly", children: copy.readOnly })), canManageAssociations && activeArea === 'places' && addPlaceHref && _jsx("a", { href: addPlaceHref, children: copy.addPlace }), canManageAssociations && activeArea === 'products' && addProductHref && _jsx("a", { href: addProductHref, children: copy.addProduct })] }), activeArea === 'overview' && (overviewContent ?? (_jsxs("div", { className: "uhcp-sos-overview-grid", children: [_jsxs("article", { children: [_jsx("strong", { children: places.length }), _jsx("span", { children: copy.areas.places })] }), _jsxs("article", { children: [_jsx("strong", { children: products.length }), _jsx("span", { children: copy.areas.products })] }), _jsxs("article", { children: [_jsx("strong", { children: communications.length }), _jsx("span", { children: copy.areas.communications })] }), _jsxs("article", { children: [_jsx("strong", { children: careJourney.length }), _jsx("span", { children: copy.areas['care-journey'] })] })] }))), activeArea === 'health-summary' && (healthSummary ?? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyHealthSummary })), activeArea === 'care-journey' && (careJourney.length === 0 ? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyCareJourney }) : (_jsx("ol", { className: "uhcp-sos-timeline uhcp-sos-care-journey", children: careJourney.map((encounter) => (_jsxs("li", { children: [_jsxs("div", { children: [_jsx("span", { children: encounter.statusLabel }), _jsx("time", { dateTime: encounter.periodStart, children: encounter.periodStart })] }), _jsx("strong", { children: encounter.serviceLabel }), encounter.detail ? _jsx("p", { children: encounter.detail }) : null, encounter.locationLabel || encounter.periodEnd ? _jsx("small", { children: [encounter.locationLabel, encounter.periodEnd].filter(Boolean).join(' · ') }) : null] }, encounter.identifier))) }))), activeArea === 'emergency-data' && _jsx(ProtectedIndexEmergencyData, { copy: copy.emergencyData, items: emergencyData }), activeArea === 'places' && _jsx(AssociationList, { items: places, empty: copy.emptyPlaces }), activeArea === 'products' && _jsx(AssociationList, { items: products, empty: copy.emptyProducts }), activeArea === 'communications' && (communicationsContent ?? (communications.length === 0 ? _jsx("p", { className: "uhcp-sos-empty", children: copy.emptyCommunications }) : (_jsx("ol", { className: "uhcp-sos-timeline", children: communications.map((message) => (_jsxs("li", { children: [_jsxs("div", { children: [_jsx("span", { children: message.categoryLabel }), _jsx("time", { children: message.occurredAt })] }), _jsx("strong", { children: message.subject }), _jsx("p", { children: message.body }), _jsxs("small", { children: [message.sender, message.placeLabel ? ` · ${message.placeLabel}` : ''] })] }, message.identifier))) }))))] })] }));
|
|
57
57
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -45,4 +45,8 @@ export * from './AnimalControllerAttestationPanel.js';
|
|
|
45
45
|
export * from './EmergencyTaskHub.js';
|
|
46
46
|
export * from './SubjectCommunicationCenter.js';
|
|
47
47
|
export * from './ProfessionalAccessPanel.js';
|
|
48
|
+
export * from './NotificationComposer.js';
|
|
49
|
+
export * from './notification-bff-client.js';
|
|
50
|
+
export * from './CommunicationsWorkspace.js';
|
|
51
|
+
export * from './SelectedSubjectWorkspace.js';
|
|
48
52
|
//# 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,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,sCAAsC,CAAA;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,sCAAsC,CAAA;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,+BAA+B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -45,3 +45,7 @@ export * from './AnimalControllerAttestationPanel.js';
|
|
|
45
45
|
export * from './EmergencyTaskHub.js';
|
|
46
46
|
export * from './SubjectCommunicationCenter.js';
|
|
47
47
|
export * from './ProfessionalAccessPanel.js';
|
|
48
|
+
export * from './NotificationComposer.js';
|
|
49
|
+
export * from './notification-bff-client.js';
|
|
50
|
+
export * from './CommunicationsWorkspace.js';
|
|
51
|
+
export * from './SelectedSubjectWorkspace.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type NotificationAttachment, type NotificationComposerDraft, type PublicNotificationRecord } from 'sos-data-utils-ts/public-notifications';
|
|
2
|
+
type AuthorizedFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
3
|
+
export type NotificationSendResult = Readonly<{
|
|
4
|
+
notificationId: string;
|
|
5
|
+
status: 'accepted';
|
|
6
|
+
}>;
|
|
7
|
+
export type NotificationProfileProof = Readonly<{
|
|
8
|
+
profileId: string;
|
|
9
|
+
profileSessionToken: string;
|
|
10
|
+
}>;
|
|
11
|
+
export type PersonalNotificationContext = Readonly<{
|
|
12
|
+
subjectReference: string;
|
|
13
|
+
subjectKind: 'human';
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
subjectReference: string;
|
|
16
|
+
subjectKind: 'animal';
|
|
17
|
+
species: 'dog' | 'cat';
|
|
18
|
+
}>;
|
|
19
|
+
/** Personal public-topic reader; the BFF derives subscriptions, exclusions and delivery decisions. */
|
|
20
|
+
export declare function createPersonalNotificationBffClient(input: Readonly<{
|
|
21
|
+
authorizedFetch: AuthorizedFetch;
|
|
22
|
+
routeRoot?: '/api/notifications';
|
|
23
|
+
}>): Readonly<{
|
|
24
|
+
listPublic(context: PersonalNotificationContext): Promise<readonly PublicNotificationRecord[]>;
|
|
25
|
+
}>;
|
|
26
|
+
/** Shared transport adapter for browser, mobile-web and assistant hosts. */
|
|
27
|
+
export declare function createNotificationBffClient(input: Readonly<{
|
|
28
|
+
authorizedFetch: AuthorizedFetch;
|
|
29
|
+
routeRoot?: '/api/notifications';
|
|
30
|
+
}>): Readonly<{
|
|
31
|
+
uploadAttachment(file: File): Promise<NotificationAttachment>;
|
|
32
|
+
send(draft: NotificationComposerDraft): Promise<NotificationSendResult>;
|
|
33
|
+
}>;
|
|
34
|
+
/** Public-dataset transport shared by professional portals; the BFF validates the trusted profile proof. */
|
|
35
|
+
export declare function createPublicNotificationBffClient(input: Readonly<{
|
|
36
|
+
authorizedFetch: AuthorizedFetch;
|
|
37
|
+
profileProof: NotificationProfileProof;
|
|
38
|
+
routeRoot?: '/api/notifications/public';
|
|
39
|
+
}>): Readonly<{
|
|
40
|
+
uploadAttachment(file: File): Promise<NotificationAttachment>;
|
|
41
|
+
send(draft: NotificationComposerDraft): Promise<NotificationSendResult>;
|
|
42
|
+
}>;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=notification-bff-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-bff-client.d.ts","sourceRoot":"","sources":["../src/notification-bff-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC9B,MAAM,wCAAwC,CAAA;AAE/C,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE5E,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,UAAU,CAAA;CACnB,CAAC,CAAA;AAEF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAA;IACjB,mBAAmB,EAAE,MAAM,CAAA;CAC5B,CAAC,CAAA;AAEF,MAAM,MAAM,2BAA2B,GACnC,QAAQ,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,GAC5D,QAAQ,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,KAAK,GAAG,KAAK,CAAA;CAAE,CAAC,CAAA;AAEzF,sGAAsG;AACtG,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,QAAQ,CAAC;IAClE,eAAe,EAAE,eAAe,CAAA;IAChC,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,CAAC;wBAG4B,2BAA2B,GAAG,OAAO,CAAC,SAAS,wBAAwB,EAAE,CAAC;GAavG;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,CAAC;IAC1D,eAAe,EAAE,eAAe,CAAA;IAChC,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,CAAC;2BAI+B,IAAI,GAAG,OAAO,CAAC,sBAAsB,CAAC;gBAYjD,yBAAyB,GAAG,OAAO,CAAC,sBAAsB,CAAC;GAchF;AAED,4GAA4G;AAC5G,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,QAAQ,CAAC;IAChE,eAAe,EAAE,eAAe,CAAA;IAChC,YAAY,EAAE,wBAAwB,CAAA;IACtC,SAAS,CAAC,EAAE,2BAA2B,CAAA;CACxC,CAAC;2BAQ+B,IAAI,GAAG,OAAO,CAAC,sBAAsB,CAAC;gBAWjD,yBAAyB,GAAG,OAAO,CAAC,sBAAsB,CAAC;GAchF"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { normalizePublicNotificationRecord, normalizeNotificationComposerDraft, } from 'sos-data-utils-ts/public-notifications';
|
|
2
|
+
/** Personal public-topic reader; the BFF derives subscriptions, exclusions and delivery decisions. */
|
|
3
|
+
export function createPersonalNotificationBffClient(input) {
|
|
4
|
+
const routeRoot = input.routeRoot ?? '/api/notifications';
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
async listPublic(context) {
|
|
7
|
+
const subjectReference = context.subjectReference.trim();
|
|
8
|
+
if (!subjectReference)
|
|
9
|
+
throw new Error('notification_subject_reference_required');
|
|
10
|
+
const query = new URLSearchParams({ subject: subjectReference });
|
|
11
|
+
if (context.subjectKind === 'animal')
|
|
12
|
+
query.set('species', context.species);
|
|
13
|
+
const response = await input.authorizedFetch(`${routeRoot}?${query.toString()}`, { method: 'GET' });
|
|
14
|
+
const payload = await response.json();
|
|
15
|
+
if (!response.ok || !Array.isArray(payload.notifications)) {
|
|
16
|
+
throw new Error(payload.error || 'notification_list_failed');
|
|
17
|
+
}
|
|
18
|
+
return Object.freeze(payload.notifications.map(normalizePublicNotificationRecord));
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Shared transport adapter for browser, mobile-web and assistant hosts. */
|
|
23
|
+
export function createNotificationBffClient(input) {
|
|
24
|
+
const routeRoot = input.routeRoot ?? '/api/notifications';
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
async uploadAttachment(file) {
|
|
27
|
+
const form = new FormData();
|
|
28
|
+
form.set('attachment', file);
|
|
29
|
+
const response = await input.authorizedFetch(`${routeRoot}/attachments`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body: form,
|
|
32
|
+
});
|
|
33
|
+
const payload = await response.json();
|
|
34
|
+
if (!response.ok || !payload.attachment)
|
|
35
|
+
throw new Error(payload.error || 'notification_attachment_upload_failed');
|
|
36
|
+
return Object.freeze(payload.attachment);
|
|
37
|
+
},
|
|
38
|
+
async send(draft) {
|
|
39
|
+
const normalizedDraft = normalizeNotificationComposerDraft(draft);
|
|
40
|
+
const response = await input.authorizedFetch(routeRoot, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'content-type': 'application/json' },
|
|
43
|
+
body: JSON.stringify({ operation: 'send', draft: normalizedDraft }),
|
|
44
|
+
});
|
|
45
|
+
const payload = await response.json();
|
|
46
|
+
if (!response.ok || !payload.notificationId || payload.status !== 'accepted') {
|
|
47
|
+
throw new Error(payload.error || 'notification_send_failed');
|
|
48
|
+
}
|
|
49
|
+
return Object.freeze({ notificationId: payload.notificationId, status: 'accepted' });
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Public-dataset transport shared by professional portals; the BFF validates the trusted profile proof. */
|
|
54
|
+
export function createPublicNotificationBffClient(input) {
|
|
55
|
+
const routeRoot = input.routeRoot ?? '/api/notifications/public';
|
|
56
|
+
const profileId = input.profileProof.profileId.trim();
|
|
57
|
+
const profileSessionToken = input.profileProof.profileSessionToken.trim();
|
|
58
|
+
if (!profileId)
|
|
59
|
+
throw new Error('profile_id_required');
|
|
60
|
+
if (!profileSessionToken)
|
|
61
|
+
throw new Error('wallet_session_required');
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
async uploadAttachment(file) {
|
|
64
|
+
const form = new FormData();
|
|
65
|
+
form.set('profileId', profileId);
|
|
66
|
+
form.set('profileSessionToken', profileSessionToken);
|
|
67
|
+
form.set('file', file);
|
|
68
|
+
const response = await input.authorizedFetch(`${routeRoot}/attachments`, { method: 'POST', body: form });
|
|
69
|
+
const payload = await response.json();
|
|
70
|
+
if (!response.ok || !payload.attachment)
|
|
71
|
+
throw new Error(payload.error || 'notification_attachment_upload_failed');
|
|
72
|
+
return Object.freeze(payload.attachment);
|
|
73
|
+
},
|
|
74
|
+
async send(draft) {
|
|
75
|
+
const normalizedDraft = normalizeNotificationComposerDraft(draft);
|
|
76
|
+
const response = await input.authorizedFetch(routeRoot, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'content-type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ operation: 'send', profileId, profileSessionToken, draft: normalizedDraft }),
|
|
80
|
+
});
|
|
81
|
+
const payload = await response.json();
|
|
82
|
+
if (!response.ok || !payload.notificationId || payload.status !== 'accepted') {
|
|
83
|
+
throw new Error(payload.error || 'public_notification_send_failed');
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze({ notificationId: payload.notificationId, status: 'accepted' });
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
package/dist/styles.css
CHANGED
|
@@ -772,6 +772,7 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
772
772
|
@media (max-width: 620px) { .uhcp-emergency-copy > header { display: grid; } .uhcp-emergency-copy-legend { width: 100%; } }
|
|
773
773
|
.uhcp-settings{max-width:72rem;margin:0 auto;padding:2rem 1rem 4rem;display:grid;gap:1rem}.uhcp-settings>header,.uhcp-settings>section{background:var(--uhcp-surface,#fff);border:1px solid var(--uhcp-border,#dbe4ea);border-radius:1rem;padding:1.25rem}.uhcp-settings h1,.uhcp-settings h2{margin-top:0}.uhcp-settings fieldset{border:0;padding:0;margin:1rem 0;display:flex;gap:.5rem;flex-wrap:wrap}.uhcp-settings legend{width:100%;font-weight:700;margin-bottom:.5rem}.uhcp-settings button,.uhcp-settings-links a{border:1px solid var(--uhcp-border,#c9d7df);border-radius:.7rem;background:transparent;color:inherit;padding:.65rem .85rem;text-decoration:none}.uhcp-settings button[aria-pressed=true]{background:var(--uhcp-accent,#dceff7);border-color:var(--uhcp-accent-strong,#2489a7)}.uhcp-settings-links{display:flex;gap:.6rem;flex-wrap:wrap}
|
|
774
774
|
.uhcp-legal-document{color:var(--uhcp-text);line-height:1.7}.uhcp-legal-document h2{font-size:clamp(1.6rem,4vw,2.35rem);line-height:1.15;margin:0 0 1.25rem}.uhcp-legal-document h3{font-size:1.2rem;line-height:1.3;margin:2rem 0 .65rem}.uhcp-legal-document p{margin:0 0 1rem}.uhcp-legal-document ul{margin:0 0 1rem;padding-left:1.4rem}.uhcp-legal-document code{background:var(--uhcp-panel-muted);border-radius:.3rem;padding:.08rem .3rem}
|
|
775
|
+
.uhcp-notification-composer{background:var(--uhcp-panel);border:1px solid var(--uhcp-border);border-radius:1.5rem;color:var(--uhcp-text);display:grid;gap:.85rem;padding:1.25rem}.uhcp-notification-composer h2,.uhcp-notification-composer h3,.uhcp-notification-composer p{margin:0}.uhcp-notification-composer label,.uhcp-notification-composer legend{font-weight:800}.uhcp-notification-composer input,.uhcp-notification-composer select,.uhcp-notification-composer textarea,.uhcp-notification-composer button{font:inherit}.uhcp-notification-composer>input,.uhcp-notification-composer>select,.uhcp-notification-composer textarea{background:var(--uhcp-panel);border:1px solid var(--uhcp-border);border-radius:.75rem;color:inherit;padding:.75rem}.uhcp-notification-composer textarea{min-height:12rem;resize:vertical;width:100%}.uhcp-notification-composer-body{display:grid;gap:1rem;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.uhcp-notification-composer-body>div{display:grid;gap:.45rem}.uhcp-notification-composer-body>section{background:var(--uhcp-panel-muted);border:1px solid var(--uhcp-border);border-radius:1rem;min-width:0;padding:1rem}.uhcp-notification-composer-body>section h3{font-size:.85rem;margin-bottom:.75rem;text-transform:uppercase}.uhcp-notification-composer fieldset{border:1px solid var(--uhcp-border);border-radius:1rem;display:grid;gap:.75rem;margin:0;padding:1rem}.uhcp-notification-composer fieldset ul{display:grid;gap:.5rem;list-style:none;margin:0;padding:0}.uhcp-notification-composer fieldset li{align-items:center;background:var(--uhcp-panel-muted);border-radius:.7rem;display:flex;gap:.75rem;justify-content:space-between;padding:.6rem .75rem}.uhcp-notification-composer button{border:0;border-radius:.75rem;cursor:pointer;font-weight:800;padding:.7rem 1rem}.uhcp-notification-composer>button{background:var(--uhcp-turquoise);color:#fff;justify-self:start}.uhcp-notification-composer [role='alert']{color:#c64747;font-weight:800}.uhcp-notification-composer [role='status']{color:var(--uhcp-emerald);font-weight:800}@media(max-width:720px){.uhcp-notification-composer-body{grid-template-columns:1fr}}
|
|
775
776
|
.uhcp-consent-directory{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}.uhcp-consent-directory>header{align-items:flex-start;display:flex;gap:1rem;justify-content:space-between}.uhcp-consent-directory h2,.uhcp-consent-directory p{margin:0}.uhcp-consent-directory>header p,.uhcp-consent-list article>p{color:var(--uhcp-muted)}.uhcp-consent-directory button,.uhcp-consent-directory select{background:var(--uhcp-panel);border:1px solid var(--uhcp-border);border-radius:.7rem;color:inherit;font:inherit;padding:.65rem .85rem}.uhcp-consent-list{display:grid;gap:.75rem}.uhcp-consent-list article{background:var(--uhcp-panel-muted);border:1px solid var(--uhcp-border);border-radius:1rem;display:grid;gap:.55rem;padding:1rem}.uhcp-consent-list article>div:first-child{align-items:center;display:flex;flex-wrap:wrap;gap:.55rem}.uhcp-consent-list article span{background:color-mix(in srgb,var(--uhcp-turquoise) 13%,transparent);border-radius:999px;font-size:.8rem;padding:.25rem .5rem}.uhcp-consent-actions{display:flex;flex-wrap:wrap;gap:.5rem}.uhcp-consent-editor{border-top:1px solid var(--uhcp-border);display:grid;gap:1rem;padding-top:1rem}.uhcp-consent-editor>label{display:grid;gap:.35rem}.uhcp-consent-editor fieldset{border:1px solid var(--uhcp-border);border-radius:1rem;display:flex;flex-wrap:wrap;gap:.75rem;margin:0;padding:1rem}.uhcp-consent-editor legend{font-weight:800;padding:0 .35rem}.uhcp-consent-editor fieldset label{align-items:center;display:flex;gap:.35rem}.uhcp-consent-editor>div{display:flex;gap:.6rem}@media(max-width:620px){.uhcp-consent-directory>header{display:grid}.uhcp-consent-editor fieldset{display:grid}}
|
|
776
777
|
.uhcp-loading-skeleton{background:var(--uhcp-panel);border:1px solid var(--uhcp-border);border-radius:2rem;display:grid;gap:1rem;margin:0 auto;max-width:1160px;overflow:hidden;padding:clamp(1.25rem,4vw,2.5rem);position:relative}.uhcp-loading-skeleton::after{animation:uhcp-loading-sweep 1.45s ease-in-out infinite;background:linear-gradient(90deg,transparent,color-mix(in srgb,#fff 42%,transparent),transparent);content:"";inset:0;position:absolute;transform:translateX(-100%)}.uhcp-loading-label{height:1px;overflow:hidden;position:absolute;width:1px;clip-path:inset(50%)}.uhcp-loading-heading,.uhcp-loading-copy,.uhcp-loading-row span{animation:uhcp-loading-pulse 1.45s ease-in-out infinite;background:color-mix(in srgb,var(--uhcp-muted) 17%,var(--uhcp-panel-muted));border-radius:999px}.uhcp-loading-heading{height:2.25rem;max-width:23rem;width:62%}.uhcp-loading-copy{height:1rem;max-width:38rem;width:86%}.uhcp-loading-grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));margin-top:.5rem}.uhcp-loading-row{background:var(--uhcp-panel-muted);border:1px solid var(--uhcp-border);border-radius:1.35rem;display:grid;gap:.8rem;padding:1.25rem}.uhcp-loading-row span{display:block;height:.8rem}.uhcp-loading-row span:nth-child(1){width:35%}.uhcp-loading-row span:nth-child(2){height:1.2rem;width:72%}.uhcp-loading-row span:nth-child(3){width:90%}.uhcp-loading-skeleton-compact{border-radius:1.25rem;padding:1.25rem}.uhcp-loading-skeleton-compact .uhcp-loading-heading{height:1.5rem}@keyframes uhcp-loading-pulse{0%,100%{opacity:.55}50%{opacity:1}}@keyframes uhcp-loading-sweep{to{transform:translateX(100%)}}@media(prefers-reduced-motion:reduce){.uhcp-loading-skeleton::after,.uhcp-loading-heading,.uhcp-loading-copy,.uhcp-loading-row span{animation:none}}
|
|
777
778
|
.uhcp-animal-onboarding { display: grid; gap: 1.25rem; }
|
|
@@ -936,3 +937,39 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
|
|
|
936
937
|
.uhcp-auth-progress { align-items: center; display: flex; font-weight: 750; gap: .6rem; }
|
|
937
938
|
.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; }
|
|
938
939
|
@keyframes uhcp-auth-spin { to { transform: rotate(360deg); } }
|
|
940
|
+
.uhcp-communications-tabs,
|
|
941
|
+
.uhcp-communications-topic-tabs {
|
|
942
|
+
display: flex;
|
|
943
|
+
flex-wrap: wrap;
|
|
944
|
+
gap: 0.75rem;
|
|
945
|
+
margin-bottom: 1rem;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.uhcp-communications-tabs button,
|
|
949
|
+
.uhcp-communications-topic-tabs button {
|
|
950
|
+
min-height: 2.75rem;
|
|
951
|
+
border: 1px solid var(--uhcp-border, #cbd5e1);
|
|
952
|
+
border-radius: 999px;
|
|
953
|
+
padding: 0.625rem 1rem;
|
|
954
|
+
background: var(--uhcp-surface, #fff);
|
|
955
|
+
color: inherit;
|
|
956
|
+
font: inherit;
|
|
957
|
+
font-weight: 700;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
.uhcp-communications-tabs button[aria-selected='true'],
|
|
961
|
+
.uhcp-communications-topic-tabs button[aria-selected='true'] {
|
|
962
|
+
border-color: var(--uhcp-accent, #0f766e);
|
|
963
|
+
background: color-mix(in srgb, var(--uhcp-accent, #0f766e) 12%, transparent);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
.uhcp-communications-topic-tabs button:disabled {
|
|
967
|
+
cursor: not-allowed;
|
|
968
|
+
opacity: 0.55;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.uhcp-selected-subject-workspace { display: grid; gap: 1rem; }
|
|
972
|
+
.uhcp-selected-subject-workspace > header { display: flex; align-items: start; justify-content: space-between; gap: 1rem; }
|
|
973
|
+
.uhcp-selected-subject-tabs { display: flex; flex-wrap: wrap; gap: 0.75rem; }
|
|
974
|
+
.uhcp-selected-subject-tabs button { min-height: 2.75rem; border: 1px solid var(--uhcp-border, #cbd5e1); border-radius: 999px; padding: 0.625rem 1rem; background: var(--uhcp-surface, #fff); color: inherit; font: inherit; font-weight: 700; }
|
|
975
|
+
.uhcp-selected-subject-tabs button[aria-selected='true'] { border-color: var(--uhcp-accent, #0f766e); background: color-mix(in srgb, var(--uhcp-accent, #0f766e) 12%, transparent); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uhc-portal-components-react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
4
4
|
"description": "Brand-neutral React portal shell, account preferences and protected-profile UI for UHC and VetChain portals.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"react-dom": ">=18.3.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"fhir-data-utils-ts": "0.6.
|
|
34
|
+
"fhir-data-utils-ts": "0.6.21",
|
|
35
35
|
"gdc-common-utils-ts": "2.9.30",
|
|
36
|
-
"sos-data-utils-ts": "0.6.
|
|
36
|
+
"sos-data-utils-ts": "0.6.15",
|
|
37
37
|
"terminology-components-react": "0.2.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|