uhc-portal-components-react 0.1.45 → 0.1.47

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
@@ -2,6 +2,30 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.47 - 2026-10-08
6
+
7
+ - Show the localized FHIR resource type in the shared Bundle selector and in
8
+ the clinical-entry dialog so users always know which resource they are
9
+ adding.
10
+ - Keep terminology lookup on the human-readable clinical concept while hiding
11
+ the legacy split code-system and code-value inputs when the canonical coded
12
+ claim is available.
13
+ - Render governed coded options, including Observation categories, with their
14
+ localized display and a compact system/code suffix while persisting the full
15
+ canonical token.
16
+
17
+ ## 0.1.46 - 2026-10-08
18
+
19
+ - Create clinical Bundle entries through explicit section and resource-type
20
+ selectors, followed by one accessible `+ Add entry` action. Keep the staged
21
+ entries grouped in collapsible sections with their existing edit and delete
22
+ actions.
23
+ - Let every schema-driven clinical resource form stage several PDF, PNG or
24
+ JPEG documents. Each file becomes an independent claims-first
25
+ `DocumentReference` with SHA-256 content hash and is linked from the parent
26
+ resource through its canonical `contained-reference-list`; files remain
27
+ local until the final Bundle submission.
28
+
5
29
  ## 0.1.45 - 2026-10-07
6
30
 
7
31
  - Present governed terminology sources as a compact dropdown with checkboxes,
@@ -22,6 +22,8 @@ export type ClinicalArtifactEditorRequest = Readonly<{
22
22
  }>;
23
23
  /** Authoritative claims-first provider Bundle hydrated through BundleReader. */
24
24
  initialBundle?: Readonly<Record<string, unknown>>;
25
+ /** Documents already staged with this local entry. */
26
+ initialAttachments?: readonly ClinicalArtifactAttachment[];
25
27
  }>;
26
28
  export type ClinicalArtifactDraft = Readonly<{
27
29
  resourceType: string;
@@ -33,6 +35,15 @@ export type ClinicalArtifactDraft = Readonly<{
33
35
  operationValues?: MultichannelOperationValues;
34
36
  /** Claims-first batch produced by the shared BundleEditor. */
35
37
  changesBundle?: ClinicalArtifactChangesBundle;
38
+ /** Local document evidence linked to this entry when the enclosing Bundle is built. */
39
+ attachments?: readonly ClinicalArtifactAttachment[];
40
+ }>;
41
+ export type ClinicalArtifactAttachment = Readonly<{
42
+ id: string;
43
+ name: string;
44
+ contentType: 'application/pdf' | 'image/jpeg' | 'image/png';
45
+ contentHash: string;
46
+ dataBase64: string;
36
47
  }>;
37
48
  export type ClinicalArtifactChangesBundle = Readonly<{
38
49
  resourceType: string;
@@ -79,6 +90,7 @@ export type ClinicalTerminologySourceSearch = (input: Readonly<{
79
90
  export type ClinicalArtifactEditorCopy = Readonly<{
80
91
  title: string;
81
92
  description: string;
93
+ resourceType?: string;
82
94
  save: string;
83
95
  saving: string;
84
96
  cancel: string;
@@ -89,6 +101,9 @@ export type ClinicalArtifactEditorCopy = Readonly<{
89
101
  readbackMissing: string;
90
102
  pending?: string;
91
103
  retry?: string;
104
+ attachments?: string;
105
+ addAttachments?: string;
106
+ removeAttachment?: string;
92
107
  dateTime?: ClinicalDateTimeFieldLabels;
93
108
  }>;
94
109
  export type ClinicalArtifactSaveOutcome = Readonly<{
@@ -114,6 +129,7 @@ export type ClinicalArtifactEditorProps = Readonly<{
114
129
  timeZone?: string;
115
130
  taxonomyId?: string;
116
131
  fieldLabel: (claim: string) => string;
132
+ resourceTypeLabel?: string;
117
133
  searchTerminology?: (input: ClinicalTerminologySearch) => Promise<readonly ClinicalTerminologyCandidate[]>;
118
134
  loadTerminologySources?: ClinicalTerminologySourceSearch;
119
135
  saveArtifact: (draft: ClinicalArtifactDraft) => Promise<ClinicalResourceBundleLike | ClinicalArtifactSaveOutcome | undefined>;
@@ -125,5 +141,5 @@ export type ClinicalArtifactEditorProps = Readonly<{
125
141
  * persistence and terminology service access. A successful form submission is
126
142
  * published only after the host returns a fresh provider readback Bundle.
127
143
  */
128
- export declare function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, searchTerminology, loadTerminologySources, saveArtifact, onCancel, onReadback, }: ClinicalArtifactEditorProps): import("react").JSX.Element;
144
+ export declare function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, resourceTypeLabel, searchTerminology, loadTerminologySources, saveArtifact, onCancel, onReadback, }: ClinicalArtifactEditorProps): import("react").JSX.Element;
129
145
  //# sourceMappingURL=ClinicalArtifactEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClinicalArtifactEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactEditor.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClinicalArtifactEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalArtifactEditor.tsx"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import { ClinicalSpeechInputButton } from './ClinicalSpeechInput.js';
10
10
  * persistence and terminology service access. A successful form submission is
11
11
  * published only after the host returns a fresh provider readback Bundle.
12
12
  */
13
- export function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale = 'en', timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone, taxonomyId, fieldLabel, searchTerminology, loadTerminologySources, saveArtifact, onCancel, onReadback, }) {
13
+ export function ClinicalArtifactEditor({ request, schema, operationSchema, authoringContext, copy, locale = 'en', timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone, taxonomyId, fieldLabel, resourceTypeLabel, searchTerminology, loadTerminologySources, saveArtifact, onCancel, onReadback, }) {
14
14
  const titleId = useId();
15
15
  const descriptionId = useId();
16
16
  const acceptedClaims = useMemo(() => operationSchema
@@ -57,6 +57,7 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
57
57
  const [selectedTerminologySources, setSelectedTerminologySources] = useState([]);
58
58
  const [terminologySourcesClaim, setTerminologySourcesClaim] = useState('');
59
59
  const [saving, setSaving] = useState(false);
60
+ const [attachments, setAttachments] = useState(request.initialAttachments ?? []);
60
61
  const [error, setError] = useState('');
61
62
  const [pending, setPending] = useState('');
62
63
  const draftId = useRef(request.artifactId ?? crypto.randomUUID());
@@ -130,6 +131,7 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
130
131
  sectionCode: request.sectionCode,
131
132
  mode: request.mode,
132
133
  claims,
134
+ attachments,
133
135
  ...(operationSchema ? buildOperationDraft(operationSchema, authoringContext, request, draftId.current, claims) : {}),
134
136
  });
135
137
  if (!result)
@@ -161,7 +163,7 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
161
163
  return next.length ? next : current;
162
164
  });
163
165
  }
164
- return _jsx(ClinicalEditorLayer, { onDismiss: onCancel, children: _jsxs("section", { className: "uhcp-clinical-editor", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": descriptionId, children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { id: titleId, children: copy.title }), _jsx("p", { id: descriptionId, children: copy.description })] }), _jsx("button", { type: "button", onClick: onCancel, "aria-label": copy.close, children: "\u00D7" })] }), _jsxs("form", { onSubmit: (event) => { event.preventDefault(); void submit(); }, children: [fields.map((claim) => {
166
+ return _jsx(ClinicalEditorLayer, { onDismiss: onCancel, children: _jsxs("section", { className: "uhcp-clinical-editor", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": descriptionId, children: [_jsxs("header", { children: [_jsxs("div", { children: [_jsx("h2", { id: titleId, children: copy.title }), _jsxs("p", { className: "uhcp-clinical-editor-resource-type", children: [copy.resourceType || 'FHIR resource', ": ", resourceTypeLabel || request.resourceType] }), _jsx("p", { id: descriptionId, children: copy.description })] }), _jsx("button", { type: "button", onClick: onCancel, "aria-label": copy.close, children: "\u00D7" })] }), _jsxs("form", { onSubmit: (event) => { event.preventDefault(); void submit(); }, children: [fields.map((claim) => {
165
167
  const parameter = claim.slice(request.resourceType.length + 1);
166
168
  const label = fieldLabel(claim);
167
169
  const terminologyEnabled = Boolean(searchTerminology && terminologyCodeClaim(request.resourceType, claim));
@@ -191,7 +193,54 @@ export function ClinicalArtifactEditor({ request, schema, operationSchema, autho
191
193
  setCandidates([]);
192
194
  setActiveTerminologyClaim(undefined);
193
195
  }, children: [candidate.display, _jsxs("small", { children: [candidate.system, " \u00B7 ", candidate.code] })] }) }, `${candidate.system}|${candidate.code}`)) }) : null] }) : null] }, claim);
194
- }), 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 })] })] })] }) });
196
+ }), _jsxs("fieldset", { className: "uhcp-clinical-editor-attachments", children: [_jsx("legend", { children: copy.attachments || 'Documents' }), _jsxs("label", { children: [_jsx("span", { children: copy.addAttachments || 'Add PDF or image' }), _jsx("input", { type: "file", multiple: true, accept: "application/pdf,image/png,image/jpeg,.pdf,.png,.jpg,.jpeg", "aria-label": copy.addAttachments || 'Add PDF or image', onChange: (event) => {
197
+ const files = [...(event.currentTarget.files ?? [])];
198
+ event.currentTarget.value = '';
199
+ void Promise.all(files.map(readClinicalAttachment)).then((next) => {
200
+ setAttachments((current) => [...current, ...next]);
201
+ }).catch((reason) => setError(reason instanceof Error ? reason.message : String(reason)));
202
+ } })] }), attachments.length ? _jsx("ul", { children: attachments.map((attachment) => _jsxs("li", { children: [_jsx("span", { children: attachment.name }), _jsx("button", { type: "button", "aria-label": `${copy.removeAttachment || 'Remove document'} ${attachment.name}`, onClick: () => setAttachments(current => current.filter(candidate => candidate.id !== attachment.id)), children: "\u00D7" })] }, attachment.id)) }) : null] }), 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 })] })] })] }) });
203
+ }
204
+ async function readClinicalAttachment(file) {
205
+ const contentType = normalizeClinicalAttachmentType(file);
206
+ const bytes = new Uint8Array(await readFileBytes(file));
207
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));
208
+ return Object.freeze({
209
+ id: crypto.randomUUID(),
210
+ name: file.name,
211
+ contentType,
212
+ contentHash: `sha256:${hex(digest)}`,
213
+ dataBase64: bytesToBase64(bytes),
214
+ });
215
+ }
216
+ function normalizeClinicalAttachmentType(file) {
217
+ if (file.type === 'application/pdf' || file.type === 'image/png' || file.type === 'image/jpeg')
218
+ return file.type;
219
+ const extension = file.name.toLowerCase().split('.').pop();
220
+ if (extension === 'pdf')
221
+ return 'application/pdf';
222
+ if (extension === 'png')
223
+ return 'image/png';
224
+ if (extension === 'jpg' || extension === 'jpeg')
225
+ return 'image/jpeg';
226
+ throw new TypeError('clinical_attachment_type_unsupported');
227
+ }
228
+ function readFileBytes(file) {
229
+ return new Promise((resolve, reject) => {
230
+ const reader = new FileReader();
231
+ reader.onerror = () => reject(reader.error ?? new Error('clinical_attachment_read_failed'));
232
+ reader.onload = () => reader.result instanceof ArrayBuffer ? resolve(reader.result) : reject(new Error('clinical_attachment_read_failed'));
233
+ reader.readAsArrayBuffer(file);
234
+ });
235
+ }
236
+ function bytesToBase64(bytes) {
237
+ let binary = '';
238
+ for (let index = 0; index < bytes.length; index += 1)
239
+ binary += String.fromCharCode(bytes[index] ?? 0);
240
+ return btoa(binary);
241
+ }
242
+ function hex(bytes) {
243
+ return [...bytes].map(value => value.toString(16).padStart(2, '0')).join('');
195
244
  }
196
245
  const DEFAULT_DATE_TIME_LABELS = Object.freeze({
197
246
  date: 'Date',
@@ -302,6 +351,9 @@ function isHiddenCodingCompanion(resourceType, claim, acceptedClaims) {
302
351
  return true;
303
352
  if (acceptedClaims.includes(`${claim}-text`))
304
353
  return true;
354
+ const canonicalCodingClaim = claim.replace(/-(?:system|value)$/, '');
355
+ if (canonicalCodingClaim !== claim && acceptedClaims.includes(canonicalCodingClaim) && acceptedClaims.includes(`${canonicalCodingClaim}-text`))
356
+ return true;
305
357
  return resourceType === 'MedicationStatement' && claim === 'MedicationStatement.code'
306
358
  && acceptedClaims.includes('MedicationStatement.medication-text');
307
359
  }
@@ -1,5 +1,6 @@
1
1
  import 'gdc-common-utils-ts/utils/allergy-intolerance-entry-editor';
2
2
  import 'gdc-common-utils-ts/utils/condition-entry-editor';
3
+ import 'gdc-common-utils-ts/utils/document-reference-entry-editor';
3
4
  import 'gdc-common-utils-ts/utils/immunization-entry-editor';
4
5
  import 'gdc-common-utils-ts/utils/medication-statement-entry-editor';
5
6
  import 'gdc-common-utils-ts/utils/observation-entry-editor';
@@ -26,6 +27,9 @@ export type ClinicalBundleDraftEditorCopy = Readonly<{
26
27
  description: string;
27
28
  empty: string;
28
29
  add: string;
30
+ section?: string;
31
+ resourceType?: string;
32
+ addEntry?: string;
29
33
  edit: string;
30
34
  remove: string;
31
35
  submit: string;
@@ -41,6 +45,7 @@ export type ClinicalBundleDraftEditorProps = Readonly<{
41
45
  timeZone?: string;
42
46
  taxonomyId?: string;
43
47
  fieldLabel: (claim: string) => string;
48
+ resourceTypeLabel?: (resourceType: string) => string;
44
49
  searchTerminology?: (input: ClinicalTerminologySearch) => Promise<readonly ClinicalTerminologyCandidate[]>;
45
50
  loadTerminologySources?: ClinicalTerminologySourceSearch;
46
51
  /** Optional claims-first document Bundle loaded into the local draft; loading it never persists. */
@@ -54,5 +59,5 @@ export type ClinicalBundleDraftEditorProps = Readonly<{
54
59
  * It owns no authorization or persistence: the host supplies subject and
55
60
  * provenance and only `onSubmitDraft` may cross a persistence boundary.
56
61
  */
57
- export declare function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, searchTerminology, loadTerminologySources, initialBundle, onSubmitDraft, }: ClinicalBundleDraftEditorProps): import("react").JSX.Element;
62
+ export declare function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale, timeZone, taxonomyId, fieldLabel, resourceTypeLabel, searchTerminology, loadTerminologySources, initialBundle, onSubmitDraft, }: ClinicalBundleDraftEditorProps): import("react").JSX.Element;
58
63
  //# sourceMappingURL=ClinicalBundleDraftEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClinicalBundleDraftEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalBundleDraftEditor.tsx"],"names":[],"mappings":"AAOA,OAAO,4DAA4D,CAAA;AACnE,OAAO,kDAAkD,CAAA;AACzD,OAAO,qDAAqD,CAAA;AAC5D,OAAO,6DAA6D,CAAA;AACpE,OAAO,oDAAoD,CAAA;AAC3D,OAAO,kDAAkD,CAAA;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAA;AACnG,OAAO,EAEL,KAAK,gCAAgC,EAErC,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACrC,MAAM,6BAA6B,CAAA;AAGpC,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,MAAM,EAAE,sBAAsB,CAAA;IAC9B,eAAe,EAAE,2BAA2B,CAAA;IAC5C,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,YAAY,EAAE,QAAQ,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;CAC1B,CAAC,CAAA;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAA;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,0BAA0B,CAAA;CACnC,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7C,gBAAgB,EAAE,gCAAgC,CAAA;IAClD,IAAI,EAAE,6BAA6B,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,OAAO,CAAC,SAAS,4BAA4B,EAAE,CAAC,CAAA;IAC1G,sBAAsB,CAAC,EAAE,+BAA+B,CAAA;IACxD,oGAAoG;IACpG,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO,CAAC,gCAAgC,CAAC,CAAA;CAC1F,CAAC,CAAA;AAYF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,gBAAgB,EAChB,IAAI,EACJ,MAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,aAAa,GACd,EAAE,8BAA8B,+BAwFhC"}
1
+ {"version":3,"file":"ClinicalBundleDraftEditor.d.ts","sourceRoot":"","sources":["../src/ClinicalBundleDraftEditor.tsx"],"names":[],"mappings":"AAQA,OAAO,4DAA4D,CAAA;AACnE,OAAO,kDAAkD,CAAA;AACzD,OAAO,2DAA2D,CAAA;AAClE,OAAO,qDAAqD,CAAA;AAC5D,OAAO,6DAA6D,CAAA;AACpE,OAAO,oDAAoD,CAAA;AAC3D,OAAO,kDAAkD,CAAA;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAA;AACnG,OAAO,EAEL,KAAK,gCAAgC,EAErC,KAAK,0BAA0B,EAE/B,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACrC,MAAM,6BAA6B,CAAA;AAGpC,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,MAAM,EAAE,sBAAsB,CAAA;IAC9B,eAAe,EAAE,2BAA2B,CAAA;IAC5C,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,YAAY,EAAE,QAAQ,CAAA;IACtB,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;CAC1B,CAAC,CAAA;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAA;IACzD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAEF,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,0BAA0B,CAAA;CACnC,CAAC,CAAA;AAEF,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACpD,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7C,gBAAgB,EAAE,gCAAgC,CAAA;IAClD,IAAI,EAAE,6BAA6B,CAAA;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACpD,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,OAAO,CAAC,SAAS,4BAA4B,EAAE,CAAC,CAAA;IAC1G,sBAAsB,CAAC,EAAE,+BAA+B,CAAA;IACxD,oGAAoG;IACpG,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,OAAO,CAAC,gCAAgC,CAAC,CAAA;CAC1F,CAAC,CAAA;AAYF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,gBAAgB,EAChB,IAAI,EACJ,MAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,aAAa,GACd,EAAE,8BAA8B,+BAiGhC"}
@@ -5,8 +5,10 @@ import { BundleEditor } from 'gdc-common-utils-ts/utils/bundle-editor-core';
5
5
  import { BundleTypes } from 'gdc-common-utils-ts/models/bundle-editor-types';
6
6
  import { HealthcareDocumentTypes } from 'gdc-common-utils-ts/constants/healthcare';
7
7
  import { ClinicalResourceEntryEditor } from 'gdc-common-utils-ts/utils/clinical-resource-entry-editor';
8
+ import { buildDocumentReferenceFlatEntry } from 'fhir-data-utils-ts/flat-claim-resource-graph';
8
9
  import 'gdc-common-utils-ts/utils/allergy-intolerance-entry-editor';
9
10
  import 'gdc-common-utils-ts/utils/condition-entry-editor';
11
+ import 'gdc-common-utils-ts/utils/document-reference-entry-editor';
10
12
  import 'gdc-common-utils-ts/utils/immunization-entry-editor';
11
13
  import 'gdc-common-utils-ts/utils/medication-statement-entry-editor';
12
14
  import 'gdc-common-utils-ts/utils/observation-entry-editor';
@@ -20,13 +22,17 @@ import { ClinicalActionButton } from './ClinicalActionButton.js';
20
22
  * It owns no authorization or persistence: the host supplies subject and
21
23
  * provenance and only `onSubmitDraft` may cross a persistence boundary.
22
24
  */
23
- export function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale = 'en', timeZone, taxonomyId, fieldLabel, searchTerminology, loadTerminologySources, initialBundle, onSubmitDraft, }) {
25
+ export function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, locale = 'en', timeZone, taxonomyId, fieldLabel, resourceTypeLabel, searchTerminology, loadTerminologySources, initialBundle, onSubmitDraft, }) {
24
26
  const [artifacts, setArtifacts] = useState(() => importedArtifacts(initialBundle, schemas));
25
27
  const [editor, setEditor] = useState();
26
28
  const [notice, setNotice] = useState('');
27
29
  const [error, setError] = useState('');
28
30
  const [submitting, setSubmitting] = useState(false);
29
31
  const sections = useMemo(() => groupSchemas(schemas), [schemas]);
32
+ const [selectedSection, setSelectedSection] = useState(() => groupSchemas(schemas)[0]?.label ?? '');
33
+ const [selectedSchemaKey, setSelectedSchemaKey] = useState(() => schemas[0] ? schemaKey(schemas[0]) : '');
34
+ const selectedSectionSchemas = sections.find(section => section.label === selectedSection)?.schemas ?? [];
35
+ const selectedSchema = selectedSectionSchemas.find(candidate => schemaKey(candidate) === selectedSchemaKey) ?? selectedSectionSchemas[0];
30
36
  function stageArtifact(draft) {
31
37
  if (!editor)
32
38
  throw new TypeError('clinical_bundle_draft_editor_schema_required');
@@ -62,20 +68,25 @@ export function ClinicalBundleDraftEditor({ schemas, authoringContext, copy, loc
62
68
  setSubmitting(false);
63
69
  }
64
70
  }
65
- return _jsxs("section", { className: "uhcp-clinical-bundle-draft", "data-clinical-bundle-draft": true, children: [_jsxs("header", { children: [_jsx("h2", { children: copy.title }), _jsx("p", { children: copy.description })] }), notice ? _jsx("p", { role: "status", children: notice }) : null, error ? _jsx("p", { role: "alert", className: "uhcp-clinical-editor-error", children: error }) : null, sections.map(({ label, schemas: sectionSchemas }) => {
71
+ return _jsxs("section", { className: "uhcp-clinical-bundle-draft", "data-clinical-bundle-draft": true, children: [_jsxs("header", { children: [_jsx("h2", { children: copy.title }), _jsx("p", { children: copy.description })] }), notice ? _jsx("p", { role: "status", children: notice }) : null, error ? _jsx("p", { role: "alert", className: "uhcp-clinical-editor-error", children: error }) : null, sections.length ? _jsxs("div", { className: "uhcp-clinical-bundle-draft-create", children: [_jsxs("label", { children: [copy.section || 'Section', _jsx("select", { value: selectedSection, onChange: (event) => {
72
+ const nextSection = event.currentTarget.value;
73
+ const firstSchema = sections.find(section => section.label === nextSection)?.schemas[0];
74
+ setSelectedSection(nextSection);
75
+ setSelectedSchemaKey(firstSchema ? schemaKey(firstSchema) : '');
76
+ }, children: sections.map(section => _jsx("option", { value: section.label, children: section.label }, section.label)) })] }), _jsxs("label", { children: [copy.resourceType || 'Resource type', _jsx("select", { value: selectedSchema ? schemaKey(selectedSchema) : '', onChange: (event) => setSelectedSchemaKey(event.currentTarget.value), children: selectedSectionSchemas.map(candidate => _jsx("option", { value: schemaKey(candidate), children: resourceTypeLabel?.(candidate.schema.resourceType) || candidate.artifactLabel }, schemaKey(candidate))) })] }), _jsxs("button", { type: "button", disabled: !selectedSchema, onClick: () => selectedSchema && setEditor({ schema: selectedSchema }), children: ["+ ", copy.addEntry || copy.add] })] }) : null, sections.map(({ label }) => {
66
77
  const sectionArtifacts = artifacts.filter((artifact) => artifact.schema.sectionLabel === label);
67
- return _jsxs("details", { open: true, children: [_jsxs("summary", { children: [label, " (", sectionArtifacts.length, ")"] }), _jsx("div", { className: "uhcp-clinical-bundle-draft-add", children: sectionSchemas.map((candidate) => _jsx(ClinicalActionButton, { kind: "add", label: `${copy.add} ${candidate.artifactLabel}`, onClick: () => setEditor({ schema: candidate }) }, schemaKey(candidate))) }), sectionArtifacts.length === 0 ? _jsx("p", { children: copy.empty }) : _jsx("ul", { children: sectionArtifacts.map((artifact) => {
78
+ return _jsxs("details", { open: true, children: [_jsxs("summary", { children: [label, " (", sectionArtifacts.length, ")"] }), sectionArtifacts.length === 0 ? _jsx("p", { children: copy.empty }) : _jsx("ul", { children: sectionArtifacts.map((artifact) => {
68
79
  const title = artifactTitle(artifact.draft);
69
80
  return _jsxs("li", { children: [_jsxs("span", { children: [_jsx("strong", { children: title }), _jsx("small", { children: artifact.draft.resourceType })] }), _jsxs("span", { children: [_jsx(ClinicalActionButton, { kind: "edit", label: `${copy.edit} ${title}`, onClick: () => setEditor({ schema: artifact.schema, draft: artifact.draft }) }), _jsx(ClinicalActionButton, { kind: "delete", label: `${copy.remove} ${title}`, onClick: () => setArtifacts((current) => current.filter((candidate) => candidate.draft.id !== artifact.draft.id)) })] })] }, artifact.draft.id);
70
81
  }) })] }, label);
71
- }), artifacts.length === 0 && sections.length === 0 ? _jsx("p", { children: copy.empty }) : null, _jsx("footer", { children: _jsx("button", { type: "button", disabled: artifacts.length === 0 || submitting, onClick: () => void submitDraft(), children: submitting ? copy.submitting : copy.submit }) }), editor ? _jsx(ClinicalArtifactEditor, { request: editorRequest(editor), schema: editor.schema.schema, operationSchema: editor.schema.operationSchema, authoringContext: authoringContext, copy: copy.editor, locale: locale, timeZone: timeZone, taxonomyId: taxonomyId, fieldLabel: fieldLabel, searchTerminology: searchTerminology, loadTerminologySources: loadTerminologySources, saveArtifact: async (draft) => stageArtifact(draft), onCancel: () => setEditor(undefined), onReadback: () => setEditor(undefined) }) : null] });
82
+ }), artifacts.length === 0 && sections.length === 0 ? _jsx("p", { children: copy.empty }) : null, _jsx("footer", { children: _jsx("button", { type: "button", disabled: artifacts.length === 0 || submitting, onClick: () => void submitDraft(), children: submitting ? copy.submitting : copy.submit }) }), editor ? _jsx(ClinicalArtifactEditor, { request: editorRequest(editor), schema: editor.schema.schema, operationSchema: editor.schema.operationSchema, authoringContext: authoringContext, copy: copy.editor, locale: locale, timeZone: timeZone, taxonomyId: taxonomyId, fieldLabel: fieldLabel, resourceTypeLabel: resourceTypeLabel?.(editor.schema.schema.resourceType) || editor.schema.artifactLabel, searchTerminology: searchTerminology, loadTerminologySources: loadTerminologySources, saveArtifact: async (draft) => stageArtifact(draft), onCancel: () => setEditor(undefined), onReadback: () => setEditor(undefined) }) : null] });
72
83
  }
73
84
  function editorRequest(editor) {
74
85
  return {
75
86
  resourceType: editor.schema.schema.resourceType,
76
87
  sectionCode: editor.schema.schema.sectionCode ?? '',
77
88
  mode: 'create',
78
- ...(editor.draft ? { artifactId: editor.draft.id, initialClaims: editor.draft.claims } : { initialClaims: editor.schema.schema.initialClaims }),
89
+ ...(editor.draft ? { artifactId: editor.draft.id, initialClaims: editor.draft.claims, initialAttachments: editor.draft.attachments } : { initialClaims: editor.schema.schema.initialClaims }),
79
90
  };
80
91
  }
81
92
  function combineDrafts(artifacts, authoringContext, documentTitle) {
@@ -97,13 +108,33 @@ function combineDrafts(artifacts, authoringContext, documentTitle) {
97
108
  }]);
98
109
  }
99
110
  for (const { draft } of artifacts) {
111
+ const attachments = draft.attachments ?? [];
112
+ const claims = {
113
+ ...operationClaims(draft),
114
+ ...(attachments.length ? { [`${draft.resourceType}.contained-reference-list`]: attachments.map(attachment => `DocumentReference/${attachment.id}`).join(',') } : {}),
115
+ };
100
116
  const entry = editor.newEntryAs(draft.resourceType, draft.id);
101
- for (const [claim, value] of Object.entries(operationClaims(draft)))
117
+ for (const [claim, value] of Object.entries(claims))
102
118
  entry.setClaim(claim, value);
103
119
  if (!(entry instanceof ClinicalResourceEntryEditor)) {
104
120
  throw new TypeError(`clinical_bundle_draft_resource_unsupported:${draft.resourceType}`);
105
121
  }
106
122
  entry.setSectionList([draft.sectionCode]);
123
+ for (const attachment of attachments) {
124
+ const document = buildDocumentReferenceFlatEntry({
125
+ entryId: attachment.id,
126
+ identifier: attachment.id,
127
+ contentType: attachment.contentType,
128
+ contentHash: attachment.contentHash,
129
+ title: attachment.name,
130
+ dataBase64: attachment.dataBase64,
131
+ });
132
+ const documentEntry = editor.newEntryAs(document.resourceType, document.entryId);
133
+ for (const [claim, value] of Object.entries(document.claims))
134
+ documentEntry.setClaim(claim, value);
135
+ documentEntry.setClaim('DocumentReference.subject', authoringContext.subjectReference);
136
+ documentEntry.setClaim('DocumentReference.author', authoringContext.dataAuthorReference);
137
+ }
107
138
  }
108
139
  return editor.buildDocument();
109
140
  }
package/dist/styles.css CHANGED
@@ -478,7 +478,11 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
478
478
  .uhcp-clinical-bundle-draft>header p,.uhcp-clinical-bundle-draft details>p { color:var(--uhcp-muted); }
479
479
  .uhcp-clinical-bundle-draft details { background:var(--uhcp-panel-muted); border:1px solid var(--uhcp-border); border-radius:1rem; padding:.85rem 1rem; }
480
480
  .uhcp-clinical-bundle-draft summary { cursor:pointer; font-weight:800; }
481
- .uhcp-clinical-bundle-draft-add { display:flex; flex-wrap:wrap; gap:.5rem; margin-top:.8rem; }
481
+ .uhcp-clinical-bundle-draft-create { align-items:end; background:var(--uhcp-panel-muted); border:1px solid var(--uhcp-border); border-radius:1rem; display:grid; gap:.8rem; grid-template-columns:minmax(10rem,1fr) minmax(10rem,1fr) auto; padding:1rem; }
482
+ .uhcp-clinical-bundle-draft-create label { display:grid; gap:.35rem; font-weight:700; }
483
+ .uhcp-clinical-bundle-draft-create select { background:var(--uhcp-panel); border:1px solid var(--uhcp-border); border-radius:.7rem; color:var(--uhcp-text); font:inherit; min-height:2.75rem; padding:.55rem .7rem; }
484
+ .uhcp-clinical-bundle-draft-create button { background:linear-gradient(135deg,var(--uhcp-action-start),var(--uhcp-action-end)); border:0; border-radius:.75rem; color:#fff; cursor:pointer; font:inherit; font-weight:800; min-height:2.75rem; padding:.65rem 1rem; }
485
+ .uhcp-clinical-bundle-draft-create button:disabled { cursor:not-allowed; opacity:.5; }
482
486
  .uhcp-clinical-bundle-draft ul { display:grid; gap:.55rem; list-style:none; margin:.8rem 0 0; padding:0; }
483
487
  .uhcp-clinical-bundle-draft li { align-items:center; background:var(--uhcp-panel); border:1px solid var(--uhcp-border); border-radius:.8rem; display:flex; gap:1rem; justify-content:space-between; padding:.7rem; }
484
488
  .uhcp-clinical-bundle-draft li>span { display:flex; gap:.45rem; }
@@ -488,6 +492,13 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
488
492
  .uhcp-clinical-bundle-draft>footer { display:flex; justify-content:flex-end; }
489
493
  .uhcp-clinical-bundle-draft>footer button { background:linear-gradient(135deg,var(--uhcp-action-start),var(--uhcp-action-end)); border:0; border-radius:.75rem; color:#fff; cursor:pointer; font:inherit; font-weight:800; padding:.75rem 1rem; }
490
494
  .uhcp-clinical-bundle-draft>footer button:disabled { cursor:not-allowed; opacity:.5; }
495
+ .uhcp-clinical-editor-attachments { border:1px solid var(--uhcp-border); border-radius:.85rem; display:grid; gap:.65rem; grid-column:1/-1; padding:.85rem; }
496
+ .uhcp-clinical-editor-attachments label { display:grid; gap:.35rem; }
497
+ .uhcp-clinical-editor-attachments input[type=file] { border:1px dashed var(--uhcp-border); border-radius:.7rem; padding:.65rem; }
498
+ .uhcp-clinical-editor-attachments ul { display:grid; gap:.4rem; list-style:none; margin:0; padding:0; }
499
+ .uhcp-clinical-editor-attachments li { align-items:center; background:var(--uhcp-panel-muted); border-radius:.65rem; display:flex; justify-content:space-between; padding:.5rem .65rem; }
500
+ .uhcp-clinical-editor-attachments li button { background:transparent; border:0; color:var(--uhcp-text); cursor:pointer; font-size:1.2rem; }
501
+ @media (max-width:720px) { .uhcp-clinical-bundle-draft-create { grid-template-columns:1fr; } }
491
502
  .uhcp-clinical-title { font-weight: 800; margin: 0; }
492
503
  .uhcp-clinical-type { color: var(--uhcp-muted); font-size: .72rem; font-weight: 800; letter-spacing: .06em; margin: .35rem 0 0; text-transform: uppercase; }
493
504
  .uhcp-clinical-date, .uhcp-clinical-empty { color: var(--uhcp-muted); margin: .35rem 0 0; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
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.16",
34
+ "fhir-data-utils-ts": "0.6.17",
35
35
  "gdc-common-utils-ts": "2.9.30",
36
36
  "sos-data-utils-ts": "0.6.3"
37
37
  },