uhc-portal-components-react 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.32
|
|
4
|
+
|
|
5
|
+
- Present every JSON-Schema `date-time` and `fhir-date-time` clinical claim as
|
|
6
|
+
separate calendar-date and local-time controls, including accessible actions
|
|
7
|
+
for the current date and time, while preserving canonical FHIR instants.
|
|
8
|
+
- Keep optional status fields genuinely empty until selected so an
|
|
9
|
+
edit/delete/recreate round trip never invents clinical state.
|
|
10
|
+
- Consume the canonical clinical subject-reference fix from
|
|
11
|
+
`fhir-data-utils-ts@0.6.12`.
|
|
12
|
+
|
|
3
13
|
## 0.1.30
|
|
4
14
|
|
|
5
15
|
- Pin the corrected shared clinical reader so IPS cards prefer each resource's
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ClinicalResourceBundleLike } from 'gdc-common-utils-ts/utils/clinical-resource-view';
|
|
2
2
|
import { type MultichannelOperationSchema, type MultichannelOperationValues } from 'sos-data-utils-ts/multichannel-operation-schemas';
|
|
3
|
+
import type { ClinicalDateTimeFieldLabels } from './ClinicalTimeline.js';
|
|
3
4
|
export type ClinicalArtifactSchema = Readonly<{
|
|
4
5
|
resourceType: string;
|
|
5
6
|
sectionCode?: string;
|
|
@@ -75,6 +76,7 @@ export type ClinicalArtifactEditorCopy = Readonly<{
|
|
|
75
76
|
readbackMissing: string;
|
|
76
77
|
pending?: string;
|
|
77
78
|
retry?: string;
|
|
79
|
+
dateTime?: ClinicalDateTimeFieldLabels;
|
|
78
80
|
}>;
|
|
79
81
|
export type ClinicalArtifactSaveOutcome = Readonly<{
|
|
80
82
|
state: 'confirmed';
|
|
@@ -95,6 +97,8 @@ export type ClinicalArtifactEditorProps = Readonly<{
|
|
|
95
97
|
authoringContext?: ClinicalArtifactAuthoringContext;
|
|
96
98
|
copy: ClinicalArtifactEditorCopy;
|
|
97
99
|
locale?: string;
|
|
100
|
+
/** IANA zone used to present and compose separate local date/time controls. */
|
|
101
|
+
timeZone?: string;
|
|
98
102
|
taxonomyId?: string;
|
|
99
103
|
fieldLabel: (claim: string) => string;
|
|
100
104
|
searchTerminology?: (input: ClinicalTerminologySearch) => Promise<readonly ClinicalTerminologyCandidate[]>;
|
|
@@ -107,5 +111,5 @@ export type ClinicalArtifactEditorProps = Readonly<{
|
|
|
107
111
|
* persistence and terminology service access. A successful form submission is
|
|
108
112
|
* published only after the host returns a fresh provider readback Bundle.
|
|
109
113
|
*/
|
|
110
|
-
export declare function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale, taxonomyId, fieldLabel, searchTerminology, saveArtifact, onCancel, onReadback, }: ClinicalArtifactEditorProps): import("react").JSX.Element;
|
|
114
|
+
export declare function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, searchTerminology, saveArtifact, onCancel, onReadback, }: ClinicalArtifactEditorProps): import("react").JSX.Element;
|
|
111
115
|
//# sourceMappingURL=ClinicalArtifactEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClinicalArtifactEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactEditor.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kDAAkD,CAAA;
|
|
1
|
+
{"version":3,"file":"ClinicalArtifactEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactEditor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { clinicalArtifactEditorClaimsFromFhirR4, clinicalArtifactFhirR4ToFlat, } from 'fhir-data-utils-ts';
|
|
4
|
+
import { clinicalArtifactEditorClaimsFromFhirR4, clinicalArtifactFhirR4ToFlat, composeFhirDateTimeFromLocalParts, splitFhirDateTimeForInput, } from 'fhir-data-utils-ts';
|
|
5
5
|
import { buildMultichannelOperationBundle, MultichannelOperationMode, readMultichannelOperationFromBundle, selectMultichannelOperationClaimValues, } from 'sos-data-utils-ts/multichannel-operation-schemas';
|
|
6
6
|
/**
|
|
7
7
|
* Shared human/animal clinical editor. The host owns authorization, transport,
|
|
8
8
|
* persistence and terminology service access. A successful form submission is
|
|
9
9
|
* published only after the host returns a fresh provider readback Bundle.
|
|
10
10
|
*/
|
|
11
|
-
export function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale = 'en', taxonomyId, fieldLabel, searchTerminology, saveArtifact, onCancel, onReadback, }) {
|
|
11
|
+
export function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale = 'en', timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone, taxonomyId, fieldLabel, searchTerminology, saveArtifact, onCancel, onReadback, }) {
|
|
12
12
|
const titleId = useId();
|
|
13
13
|
const descriptionId = useId();
|
|
14
14
|
const acceptedClaims = useMemo(() => operationSchema
|
|
@@ -131,12 +131,15 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
|
|
|
131
131
|
const terminologyEnabled = Boolean(searchTerminology && terminologyCodeClaim(request.resourceType, claim));
|
|
132
132
|
const inputId = `${titleId}-${claim}`;
|
|
133
133
|
const value = claims[claim] ?? '';
|
|
134
|
+
const schemaNode = operationSchema?.properties.claims?.properties?.[claim];
|
|
134
135
|
const update = (event) => {
|
|
135
136
|
setClaims((current) => ({ ...current, [claim]: event.target.value }));
|
|
136
137
|
if (terminologyEnabled)
|
|
137
138
|
setActiveTerminologyClaim(claim);
|
|
138
139
|
};
|
|
139
|
-
|
|
140
|
+
if (isDateTimeSchemaNode(schemaNode))
|
|
141
|
+
return _jsx(ClinicalDateTimeClaimField, { label: label, required: requiredClaims.has(claim), value: value, timeZone: timeZone, labels: copy.dateTime ?? DEFAULT_DATE_TIME_LABELS, onChange: (nextValue) => setClaims((current) => ({ ...current, [claim]: nextValue })) }, claim);
|
|
142
|
+
return _jsxs("label", { className: isMultiline(parameter) ? 'uhcp-clinical-editor-wide' : undefined, htmlFor: inputId, children: [_jsxs("span", { children: [label, requiredClaims.has(claim) ? ' *' : ''] }), statusOptions(request.resourceType, parameter) ? _jsxs("select", { id: inputId, "aria-label": label, value: value, required: requiredClaims.has(claim), onChange: update, children: [!requiredClaims.has(claim) ? _jsx("option", { value: "" }) : null, statusOptions(request.resourceType, parameter).map((option) => _jsx("option", { value: option, children: humanize(option) }, option))] }) : isMultiline(parameter) ? _jsx("textarea", { id: inputId, "aria-label": label, value: value, required: requiredClaims.has(claim), rows: 3, onChange: update })
|
|
140
143
|
: _jsx("input", { id: inputId, "aria-label": label, value: value, required: requiredClaims.has(claim), type: fieldInputType(parameter, operationSchema?.properties.claims?.properties?.[claim]), onChange: update }), terminologyEnabled && activeTerminologyClaim === claim ? _jsxs("div", { className: "uhcp-terminology-results", children: [terminologyBusy ? _jsx("small", { role: "status", children: copy.terminologyLoading }) : null, terminologyError ? _jsx("small", { role: "alert", children: copy.terminologyUnavailable }) : null, candidates.length ? _jsx("ul", { children: candidates.map((candidate) => _jsx("li", { children: _jsxs("button", { type: "button", onClick: () => {
|
|
141
144
|
setClaims((current) => selectedCodingClaims(request.resourceType, claim, candidate, acceptedClaims, current));
|
|
142
145
|
setCandidates([]);
|
|
@@ -144,6 +147,43 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
|
|
|
144
147
|
}, children: [candidate.display, _jsxs("small", { children: [candidate.system, " \u00B7 ", candidate.code] })] }) }, `${candidate.system}|${candidate.code}`)) }) : null] }) : null] }, claim);
|
|
145
148
|
}), error ? _jsx("p", { className: "uhcp-clinical-editor-error", role: "alert", children: error }) : null, pending ? _jsx("p", { className: "uhcp-clinical-editor-pending", role: "status", children: copy.pending || pending }) : null, _jsxs("footer", { children: [_jsx("button", { type: "button", onClick: onCancel, children: copy.cancel }), _jsx("button", { type: "submit", disabled: saving, children: saving ? copy.saving : pending ? (copy.retry || copy.save) : copy.save })] })] })] });
|
|
146
149
|
}
|
|
150
|
+
const DEFAULT_DATE_TIME_LABELS = Object.freeze({
|
|
151
|
+
date: 'Date',
|
|
152
|
+
time: 'Time',
|
|
153
|
+
useToday: 'Use today',
|
|
154
|
+
useCurrentTime: 'Use current time',
|
|
155
|
+
});
|
|
156
|
+
function ClinicalDateTimeClaimField({ label, required, value, timeZone, labels, onChange }) {
|
|
157
|
+
const [parts, setParts] = useState(() => splitTemporalClaim(value, timeZone));
|
|
158
|
+
const current = splitFhirDateTimeForInput(new Date(), timeZone);
|
|
159
|
+
const update = (next) => {
|
|
160
|
+
setParts(next);
|
|
161
|
+
onChange(composeTemporalClaim(next, timeZone));
|
|
162
|
+
};
|
|
163
|
+
return _jsxs("fieldset", { className: "uhcp-clinical-editor-date-time", children: [_jsxs("legend", { children: [label, required ? ' *' : ''] }), _jsxs("label", { children: [_jsx("span", { children: labels.date }), _jsx("input", { "aria-label": `${label}: ${labels.date}`, required: required, type: "date", value: parts.date, onChange: (event) => update({ ...parts, date: event.target.value }) })] }), _jsx("button", { type: "button", title: labels.useToday, "aria-label": `${labels.useToday}: ${label}`, onClick: () => update({ ...parts, date: current.date }), children: "\u25C9" }), _jsxs("label", { children: [_jsx("span", { children: labels.time }), _jsx("input", { "aria-label": `${label}: ${labels.time}`, type: "time", step: "1", value: parts.time, onChange: (event) => update({ ...parts, time: event.target.value }) })] }), _jsx("button", { type: "button", title: labels.useCurrentTime, "aria-label": `${labels.useCurrentTime}: ${label}`, onClick: () => update({ ...parts, time: current.time }), children: "\u25F7" })] });
|
|
164
|
+
}
|
|
165
|
+
function splitTemporalClaim(value, timeZone) {
|
|
166
|
+
if (!value)
|
|
167
|
+
return { date: '', time: '' };
|
|
168
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
169
|
+
return { date: value, time: '' };
|
|
170
|
+
try {
|
|
171
|
+
return splitFhirDateTimeForInput(value, timeZone);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return { date: '', time: '' };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function composeTemporalClaim(parts, timeZone) {
|
|
178
|
+
if (!parts.date)
|
|
179
|
+
return '';
|
|
180
|
+
if (!parts.time)
|
|
181
|
+
return parts.date;
|
|
182
|
+
return composeFhirDateTimeFromLocalParts({ ...parts, timeZone });
|
|
183
|
+
}
|
|
184
|
+
function isDateTimeSchemaNode(schemaNode) {
|
|
185
|
+
return schemaNode?.format === 'date-time' || schemaNode?.format === 'fhir-date-time';
|
|
186
|
+
}
|
|
147
187
|
function isSaveOutcome(value) {
|
|
148
188
|
return typeof value === 'object' && value !== null && 'state' in value;
|
|
149
189
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uhc-portal-components-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
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,7 +31,7 @@
|
|
|
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.12",
|
|
35
35
|
"gdc-common-utils-ts": "2.9.27",
|
|
36
36
|
"sos-data-utils-ts": "0.6.3"
|
|
37
37
|
},
|