@medplum/react 1.0.4 → 1.0.5

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.
Files changed (56) hide show
  1. package/dist/cjs/AsyncAutocomplete/AsyncAutocomplete.d.ts +14 -0
  2. package/dist/cjs/DiagnosticReportDisplay/DiagnosticReportDisplay.stories.d.ts +1 -0
  3. package/dist/cjs/FhirPathTable/FhirPathTable.d.ts +2 -2
  4. package/dist/cjs/ValueSetAutocomplete/ValueSetAutocomplete.d.ts +3 -6
  5. package/dist/cjs/auth/ChooseProfileForm.d.ts +2 -1
  6. package/dist/cjs/auth/ChooseScopeForm.d.ts +2 -1
  7. package/dist/cjs/auth/MfaForm.d.ts +7 -0
  8. package/dist/cjs/auth/OktaButton.d.ts +5 -0
  9. package/dist/cjs/auth/SignInForm.d.ts +10 -0
  10. package/dist/cjs/index.d.ts +1 -0
  11. package/dist/cjs/index.js +319 -181
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/index.min.js +1 -1
  14. package/dist/cjs/index.min.js.map +1 -1
  15. package/dist/cjs/stories/referenceLab.d.ts +3 -1
  16. package/dist/esm/AsyncAutocomplete/AsyncAutocomplete.d.ts +14 -0
  17. package/dist/esm/AsyncAutocomplete/AsyncAutocomplete.js +116 -0
  18. package/dist/esm/AsyncAutocomplete/AsyncAutocomplete.js.map +1 -0
  19. package/dist/esm/CodeInput/CodeInput.js +3 -2
  20. package/dist/esm/CodeInput/CodeInput.js.map +1 -1
  21. package/dist/esm/CodeableConceptInput/CodeableConceptInput.js +18 -18
  22. package/dist/esm/CodeableConceptInput/CodeableConceptInput.js.map +1 -1
  23. package/dist/esm/CodingInput/CodingInput.js +3 -2
  24. package/dist/esm/CodingInput/CodingInput.js.map +1 -1
  25. package/dist/esm/DiagnosticReportDisplay/DiagnosticReportDisplay.js +8 -2
  26. package/dist/esm/DiagnosticReportDisplay/DiagnosticReportDisplay.js.map +1 -1
  27. package/dist/esm/DiagnosticReportDisplay/DiagnosticReportDisplay.stories.d.ts +1 -0
  28. package/dist/esm/FhirPathTable/FhirPathTable.d.ts +2 -2
  29. package/dist/esm/FhirPathTable/FhirPathTable.js.map +1 -1
  30. package/dist/esm/QuestionnaireForm/QuestionnaireForm.js +1 -0
  31. package/dist/esm/QuestionnaireForm/QuestionnaireForm.js.map +1 -1
  32. package/dist/esm/ResourceInput/ResourceInput.js +2 -2
  33. package/dist/esm/ResourceInput/ResourceInput.js.map +1 -1
  34. package/dist/esm/StatusBadge/StatusBadge.js +2 -1
  35. package/dist/esm/StatusBadge/StatusBadge.js.map +1 -1
  36. package/dist/esm/ValueSetAutocomplete/ValueSetAutocomplete.d.ts +3 -6
  37. package/dist/esm/ValueSetAutocomplete/ValueSetAutocomplete.js +24 -44
  38. package/dist/esm/ValueSetAutocomplete/ValueSetAutocomplete.js.map +1 -1
  39. package/dist/esm/auth/ChooseProfileForm.d.ts +2 -1
  40. package/dist/esm/auth/ChooseProfileForm.js.map +1 -1
  41. package/dist/esm/auth/ChooseScopeForm.d.ts +2 -1
  42. package/dist/esm/auth/ChooseScopeForm.js.map +1 -1
  43. package/dist/esm/auth/MfaForm.d.ts +7 -0
  44. package/dist/esm/auth/MfaForm.js +34 -0
  45. package/dist/esm/auth/MfaForm.js.map +1 -0
  46. package/dist/esm/auth/OktaButton.d.ts +5 -0
  47. package/dist/esm/auth/SignInForm.d.ts +10 -0
  48. package/dist/esm/auth/SignInForm.js +16 -0
  49. package/dist/esm/auth/SignInForm.js.map +1 -1
  50. package/dist/esm/index.d.ts +1 -0
  51. package/dist/esm/index.js +1 -0
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/index.min.js +1 -1
  54. package/dist/esm/index.min.js.map +1 -1
  55. package/dist/esm/stories/referenceLab.d.ts +3 -1
  56. package/package.json +1 -1
package/dist/cjs/index.js CHANGED
@@ -131,26 +131,41 @@
131
131
  return (React.createElement(core$1.TextInput, { name: props.name, placeholder: "Annotation text", defaultValue: value.text, onChange: (e) => setText(e.currentTarget.value) }));
132
132
  }
133
133
 
134
- function AttachmentDisplay(props) {
135
- const value = props.value;
136
- const { contentType, url, title } = value !== null && value !== void 0 ? value : {};
137
- if (!url) {
138
- return null;
139
- }
140
- return (React.createElement("div", { "data-testid": "attachment-display" },
141
- (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image/')) && (React.createElement("img", { "data-testid": "attachment-image", style: { maxWidth: props.maxWidth }, src: url, alt: value === null || value === void 0 ? void 0 : value.title })),
142
- (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('video/')) && (React.createElement("video", { "data-testid": "attachment-video", style: { maxWidth: props.maxWidth }, controls: true },
143
- React.createElement("source", { type: contentType, src: url }))),
144
- contentType === 'application/pdf' && !(title === null || title === void 0 ? void 0 : title.endsWith('.pdf')) && (React.createElement("div", { "data-testid": "attachment-pdf", style: { maxWidth: props.maxWidth, minHeight: 400 } },
145
- React.createElement("iframe", { width: "100%", height: "400", src: url + '#navpanes=0', allowFullScreen: true, frameBorder: 0, seamless: true }))),
146
- React.createElement("div", { "data-testid": "download-link", style: { padding: '2px 16px 16px 16px' } },
147
- React.createElement(core$1.Anchor, { href: value === null || value === void 0 ? void 0 : value.url, "data-testid": "attachment-details", target: "_blank", rel: "noopener noreferrer" }, (value === null || value === void 0 ? void 0 : value.title) || 'Download'))));
148
- }
149
-
150
- function AttachmentArrayDisplay(props) {
151
- return (React.createElement("div", null, props.values &&
152
- props.values.map((v, index) => (React.createElement("div", { key: 'attatchment-' + index },
153
- React.createElement(AttachmentDisplay, { value: v, maxWidth: props.maxWidth }))))));
134
+ /******************************************************************************
135
+ Copyright (c) Microsoft Corporation.
136
+
137
+ Permission to use, copy, modify, and/or distribute this software for any
138
+ purpose with or without fee is hereby granted.
139
+
140
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
141
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
142
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
143
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
144
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
145
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
146
+ PERFORMANCE OF THIS SOFTWARE.
147
+ ***************************************************************************** */
148
+
149
+ function __rest(s, e) {
150
+ var t = {};
151
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
152
+ t[p] = s[p];
153
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
154
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
155
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
156
+ t[p[i]] = s[p[i]];
157
+ }
158
+ return t;
159
+ }
160
+
161
+ function __awaiter(thisArg, _arguments, P, generator) {
162
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
163
+ return new (P || (P = Promise))(function (resolve, reject) {
164
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
165
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
166
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
167
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
168
+ });
154
169
  }
155
170
 
156
171
  /**
@@ -185,6 +200,137 @@
185
200
  return el instanceof HTMLInputElement && el.type === 'checkbox';
186
201
  }
187
202
 
203
+ function AsyncAutocomplete(props) {
204
+ const { defaultValue, toKey, toOption, loadOptions, onChange, onCreate } = props, rest = __rest(props, ["defaultValue", "toKey", "toOption", "loadOptions", "onChange", "onCreate"]);
205
+ const defaultItems = toDefaultItems(defaultValue);
206
+ const inputRef = React.useRef(null);
207
+ const [lastValue, setLastValue] = React.useState(undefined);
208
+ const [timer, setTimer] = React.useState();
209
+ const [abortController, setAbortController] = React.useState();
210
+ const [autoSubmit, setAutoSubmit] = React.useState();
211
+ const [options, setOptions] = React.useState(defaultItems === null || defaultItems === void 0 ? void 0 : defaultItems.map(toOption));
212
+ const lastValueRef = React.useRef();
213
+ lastValueRef.current = lastValue;
214
+ const timerRef = React.useRef();
215
+ timerRef.current = timer;
216
+ const abortControllerRef = React.useRef();
217
+ abortControllerRef.current = abortController;
218
+ const autoSubmitRef = React.useRef();
219
+ autoSubmitRef.current = autoSubmit;
220
+ const optionsRef = React.useRef();
221
+ optionsRef.current = options;
222
+ const handleTimer = React.useCallback(() => {
223
+ var _a, _b;
224
+ setTimer(undefined);
225
+ const value = ((_b = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.trim()) || '';
226
+ if (value === lastValueRef.current) {
227
+ // Nothing has changed, move on
228
+ return;
229
+ }
230
+ setLastValue(value);
231
+ const newAbortController = new AbortController();
232
+ setAbortController(newAbortController);
233
+ loadOptions(value, newAbortController.signal)
234
+ .then((newValues) => {
235
+ if (!newAbortController.signal.aborted) {
236
+ setOptions(newValues.map(toOption));
237
+ setAbortController(undefined);
238
+ if (autoSubmitRef.current) {
239
+ if (newValues.length > 0) {
240
+ onChange(newValues.slice(0, 1));
241
+ }
242
+ setAutoSubmit(false);
243
+ }
244
+ }
245
+ })
246
+ .catch(console.log);
247
+ }, [loadOptions, onChange, toOption]);
248
+ const handleSearchChange = React.useCallback(() => {
249
+ if (abortControllerRef.current) {
250
+ abortControllerRef.current.abort();
251
+ setAbortController(undefined);
252
+ }
253
+ if (timerRef.current !== undefined) {
254
+ window.clearTimeout(timerRef.current);
255
+ }
256
+ const newTimer = window.setTimeout(() => handleTimer(), 100);
257
+ setTimer(newTimer);
258
+ }, [handleTimer]);
259
+ const handleChange = React.useCallback((values) => {
260
+ var _a, _b;
261
+ const result = [];
262
+ for (const value of values) {
263
+ let item = (_b = (_a = optionsRef.current) === null || _a === void 0 ? void 0 : _a.find((option) => option.value === value)) === null || _b === void 0 ? void 0 : _b.resource;
264
+ if (!item) {
265
+ item = onCreate(value);
266
+ }
267
+ result.push(item);
268
+ }
269
+ onChange(result);
270
+ }, [onChange, onCreate]);
271
+ const handleKeyDown = React.useCallback((e) => {
272
+ if (e.key === 'Enter') {
273
+ if (!timerRef.current && !abortControllerRef.current) {
274
+ killEvent(e);
275
+ if (optionsRef.current && optionsRef.current.length > 0) {
276
+ setOptions(optionsRef.current.slice(0, 1));
277
+ handleChange([optionsRef.current[0].value]);
278
+ }
279
+ }
280
+ else {
281
+ // The user pressed enter, but we don't have results yet.
282
+ // We need to wait for the results to come in.
283
+ setAutoSubmit(true);
284
+ }
285
+ }
286
+ }, [handleChange]);
287
+ const handleCreate = React.useCallback((input) => {
288
+ const option = toOption(onCreate(input));
289
+ setOptions([...optionsRef.current, option]);
290
+ return option;
291
+ }, [onCreate, setOptions, toOption]);
292
+ const handleFilter = React.useCallback((_value, selected) => !selected, []);
293
+ React.useEffect(() => {
294
+ return () => {
295
+ if (abortControllerRef.current) {
296
+ abortControllerRef.current.abort();
297
+ }
298
+ };
299
+ }, []);
300
+ return (React.createElement(core$1.MultiSelect, Object.assign({}, rest, { ref: inputRef, defaultValue: defaultItems.map(toKey), searchable: true, onKeyDown: handleKeyDown, onSearchChange: handleSearchChange, data: options, onFocus: handleTimer, onChange: handleChange, onCreate: handleCreate, rightSectionWidth: 40, rightSection: abortController ? React.createElement(core$1.Loader, { size: 16 }) : null, filter: handleFilter })));
301
+ }
302
+ function toDefaultItems(defaultValue) {
303
+ if (!defaultValue) {
304
+ return [];
305
+ }
306
+ if (Array.isArray(defaultValue)) {
307
+ return defaultValue;
308
+ }
309
+ return [defaultValue];
310
+ }
311
+
312
+ function AttachmentDisplay(props) {
313
+ const value = props.value;
314
+ const { contentType, url, title } = value !== null && value !== void 0 ? value : {};
315
+ if (!url) {
316
+ return null;
317
+ }
318
+ return (React.createElement("div", { "data-testid": "attachment-display" },
319
+ (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image/')) && (React.createElement("img", { "data-testid": "attachment-image", style: { maxWidth: props.maxWidth }, src: url, alt: value === null || value === void 0 ? void 0 : value.title })),
320
+ (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('video/')) && (React.createElement("video", { "data-testid": "attachment-video", style: { maxWidth: props.maxWidth }, controls: true },
321
+ React.createElement("source", { type: contentType, src: url }))),
322
+ contentType === 'application/pdf' && !(title === null || title === void 0 ? void 0 : title.endsWith('.pdf')) && (React.createElement("div", { "data-testid": "attachment-pdf", style: { maxWidth: props.maxWidth, minHeight: 400 } },
323
+ React.createElement("iframe", { width: "100%", height: "400", src: url + '#navpanes=0', allowFullScreen: true, frameBorder: 0, seamless: true }))),
324
+ React.createElement("div", { "data-testid": "download-link", style: { padding: '2px 16px 16px 16px' } },
325
+ React.createElement(core$1.Anchor, { href: value === null || value === void 0 ? void 0 : value.url, "data-testid": "attachment-details", target: "_blank", rel: "noopener noreferrer" }, (value === null || value === void 0 ? void 0 : value.title) || 'Download'))));
326
+ }
327
+
328
+ function AttachmentArrayDisplay(props) {
329
+ return (React.createElement("div", null, props.values &&
330
+ props.values.map((v, index) => (React.createElement("div", { key: 'attatchment-' + index },
331
+ React.createElement(AttachmentDisplay, { value: v, maxWidth: props.maxWidth }))))));
332
+ }
333
+
188
334
  function AttachmentButton(props) {
189
335
  const medplum = useMedplum();
190
336
  const fileInputRef = React.useRef(null);
@@ -292,43 +438,6 @@
292
438
  return (React.createElement(AttachmentButton, { onUpload: setValueWrapper }, (props) => React.createElement(core$1.Button, Object.assign({}, props), "Upload...")));
293
439
  }
294
440
 
295
- /******************************************************************************
296
- Copyright (c) Microsoft Corporation.
297
-
298
- Permission to use, copy, modify, and/or distribute this software for any
299
- purpose with or without fee is hereby granted.
300
-
301
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
302
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
303
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
304
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
305
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
306
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
307
- PERFORMANCE OF THIS SOFTWARE.
308
- ***************************************************************************** */
309
-
310
- function __rest(s, e) {
311
- var t = {};
312
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
313
- t[p] = s[p];
314
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
315
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
316
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
317
- t[p[i]] = s[p[i]];
318
- }
319
- return t;
320
- }
321
-
322
- function __awaiter(thisArg, _arguments, P, generator) {
323
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
324
- return new (P || (P = Promise))(function (resolve, reject) {
325
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
326
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
327
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
328
- step((generator = generator.apply(thisArg, _arguments || [])).next());
329
- });
330
- }
331
-
332
441
  const useStyles$d = core$1.createStyles(() => ({
333
442
  root: {
334
443
  '@media (max-width: 800px)': {
@@ -774,12 +883,48 @@
774
883
  React.createElement(core$1.Button, { type: "submit" }, "Set scope")))));
775
884
  }
776
885
 
886
+ function MfaForm(props) {
887
+ const medplum = useMedplum();
888
+ const [errorMessage, setErrorMessage] = React.useState(undefined);
889
+ return (React.createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => {
890
+ setErrorMessage(undefined);
891
+ medplum
892
+ .post('auth/mfa/verify', {
893
+ login: props.login,
894
+ token: formData.token,
895
+ })
896
+ .then(props.handleAuthResponse)
897
+ .catch((err) => setErrorMessage(core.normalizeErrorString(err)));
898
+ } },
899
+ React.createElement(core$1.Stack, null,
900
+ React.createElement(core$1.Center, { sx: { flexDirection: 'column' } },
901
+ React.createElement(Logo, { size: 32 }),
902
+ React.createElement(core$1.Title, null, "Enter MFA code")),
903
+ errorMessage && (React.createElement(core$1.Alert, { icon: React.createElement(icons.IconAlertCircle, { size: 16 }), title: "Error", color: "red" }, errorMessage)),
904
+ React.createElement(core$1.Stack, null,
905
+ React.createElement(core$1.TextInput, { name: "token", label: "MFA code", required: true })),
906
+ React.createElement(core$1.Group, { position: "right", mt: "xl" },
907
+ React.createElement(core$1.Button, { type: "submit" }, "Submit code")))));
908
+ }
909
+
910
+ /**
911
+ * The SignInForm component allows users to sign in to Medplum.
912
+ *
913
+ * "Signing in" is a multi-step process:
914
+ * 1) Authentication - identify the user
915
+ * 2) MFA - If MFA is enabled, prompt for MFA code
916
+ * 3) Choose profile - If the user has multiple profiles, prompt to choose one
917
+ * 4) Choose scope - If the user has multiple scopes, prompt to choose one
918
+ * 5) Success - Return to the caller with either a code or a redirect
919
+ */
777
920
  function SignInForm(props) {
778
921
  const { chooseScopes, onSuccess, onForgotPassword, onRegister, onCode } = props, baseLoginRequest = __rest(props, ["chooseScopes", "onSuccess", "onForgotPassword", "onRegister", "onCode"]);
779
922
  const medplum = useMedplum();
780
923
  const [login, setLogin] = React.useState(undefined);
924
+ const [mfaRequired, setAuthenticatorRequired] = React.useState(false);
781
925
  const [memberships, setMemberships] = React.useState(undefined);
782
926
  function handleAuthResponse(response) {
927
+ setAuthenticatorRequired(!!response.mfaRequired);
783
928
  if (response.login) {
784
929
  setLogin(response.login);
785
930
  }
@@ -817,6 +962,9 @@
817
962
  if (!login) {
818
963
  return (React.createElement(AuthenticationForm, Object.assign({ generatePkce: !onCode, onForgotPassword: onForgotPassword, onRegister: onRegister, handleAuthResponse: handleAuthResponse }, baseLoginRequest), props.children));
819
964
  }
965
+ else if (mfaRequired) {
966
+ return React.createElement(MfaForm, { login: login, handleAuthResponse: handleAuthResponse });
967
+ }
820
968
  else if (memberships) {
821
969
  return React.createElement(ChooseProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
822
970
  }
@@ -1269,59 +1417,39 @@
1269
1417
  return obj;
1270
1418
  }
1271
1419
 
1272
- function valueSetElementToAutocompleteItem(element) {
1420
+ function toKey(element) {
1421
+ return element.code;
1422
+ }
1423
+ function toOption(element) {
1273
1424
  return {
1274
1425
  value: element.code,
1275
1426
  label: getDisplay(element),
1276
- element,
1427
+ resource: element,
1428
+ };
1429
+ }
1430
+ function createValue(input) {
1431
+ return {
1432
+ code: input,
1433
+ display: input,
1277
1434
  };
1278
1435
  }
1279
1436
  function ValueSetAutocomplete(props) {
1280
1437
  const medplum = useMedplum();
1281
- const { property, defaultValue } = props;
1282
- const [textValues, setTextValues] = React.useState((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.code) ? [defaultValue.code] : []);
1283
- const [data, setData] = React.useState((defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.code) ? [valueSetElementToAutocompleteItem(defaultValue)] : []);
1284
- const dataRef = React.useRef();
1285
- dataRef.current = data;
1286
- const loadValues = React.useCallback((input) => __awaiter(this, void 0, void 0, function* () {
1438
+ const { elementDefinition } = props, rest = __rest(props, ["elementDefinition"]);
1439
+ const loadValues = React.useCallback((input, signal) => __awaiter(this, void 0, void 0, function* () {
1287
1440
  var _a, _b;
1288
- const system = (_a = property.binding) === null || _a === void 0 ? void 0 : _a.valueSet;
1289
- const valueSet = yield medplum.searchValueSet(system, input);
1441
+ const system = (_a = elementDefinition.binding) === null || _a === void 0 ? void 0 : _a.valueSet;
1442
+ const valueSet = yield medplum.searchValueSet(system, input, { signal });
1290
1443
  const valueSetElements = (_b = valueSet.expansion) === null || _b === void 0 ? void 0 : _b.contains;
1291
- const newData = [...dataRef.current];
1444
+ const newData = [];
1292
1445
  for (const valueSetElement of valueSetElements) {
1293
- if (valueSetElement.code && !newData.some((item) => item.value === valueSetElement.code)) {
1294
- newData.push(valueSetElementToAutocompleteItem(valueSetElement));
1295
- }
1296
- }
1297
- setData(newData);
1298
- }), [medplum, property, dataRef]);
1299
- function handleChange(values) {
1300
- setTextValues(values);
1301
- const textValue = values[0];
1302
- let currentItem = undefined;
1303
- if (textValue) {
1304
- currentItem = dataRef.current.find((item) => item.value === values[0]);
1305
- if (!currentItem) {
1306
- const newElement = { code: textValue, display: textValue };
1307
- currentItem = valueSetElementToAutocompleteItem(newElement);
1308
- setData([...dataRef.current, currentItem]);
1446
+ if (valueSetElement.code && !newData.some((item) => item.code === valueSetElement.code)) {
1447
+ newData.push(valueSetElement);
1309
1448
  }
1310
1449
  }
1311
- if (props.onChange) {
1312
- props.onChange(currentItem === null || currentItem === void 0 ? void 0 : currentItem.element);
1313
- }
1314
- }
1315
- React.useEffect(() => {
1316
- loadValues('').catch(console.log);
1317
- }, [loadValues]);
1318
- return (React.createElement(core$1.MultiSelect, { data: data, placeholder: props.placeholder, searchable: true, creatable: true, clearable: true, value: textValues, filter: (value, selected, item) => {
1319
- var _a, _b;
1320
- return !!(textValues.length === 0 &&
1321
- !selected &&
1322
- (((_a = item.element.display) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(value.toLowerCase().trim())) ||
1323
- ((_b = item.element.code) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(value.toLowerCase().trim()))));
1324
- }, onChange: handleChange, getCreateLabel: (query) => `+ Create ${query}`, onCreate: (query) => valueSetElementToAutocompleteItem({ code: query, display: query }) }));
1450
+ return newData;
1451
+ }), [medplum, elementDefinition]);
1452
+ return (React.createElement(AsyncAutocomplete, Object.assign({}, rest, { creatable: true, clearable: true, toKey: toKey, toOption: toOption, loadOptions: loadValues, getCreateLabel: (query) => `+ Create ${query}`, onCreate: createValue })));
1325
1453
  }
1326
1454
  function getDisplay(item) {
1327
1455
  return item.display || item.code || '';
@@ -1329,46 +1457,47 @@
1329
1457
 
1330
1458
  function CodeableConceptInput(props) {
1331
1459
  const [value, setValue] = React.useState(props.defaultValue);
1332
- function handleChange(newValue) {
1333
- const newConcept = newValue && valueSetElementToCodeableConcept(newValue);
1460
+ function handleChange(newValues) {
1461
+ const newConcept = valueSetElementToCodeableConcept(newValues);
1334
1462
  setValue(newConcept);
1335
1463
  if (props.onChange) {
1336
1464
  props.onChange(newConcept);
1337
1465
  }
1338
1466
  }
1339
- return (React.createElement(ValueSetAutocomplete, { property: props.property, name: props.name, placeholder: props.placeholder, defaultValue: value && codeableConceptToValueSetElement(value), onChange: handleChange }));
1467
+ return (React.createElement(ValueSetAutocomplete, { elementDefinition: props.property, name: props.name, placeholder: props.placeholder, defaultValue: value && codeableConceptToValueSetElement(value), onChange: handleChange }));
1340
1468
  }
1341
1469
  function codeableConceptToValueSetElement(concept) {
1342
- var _a, _b, _c, _d, _e, _f;
1343
- return {
1344
- system: (_b = (_a = concept.coding) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.system,
1345
- code: (_d = (_c = concept.coding) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.code,
1346
- display: (_f = (_e = concept.coding) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.display,
1347
- };
1470
+ var _a;
1471
+ return (_a = concept.coding) === null || _a === void 0 ? void 0 : _a.map((c) => ({
1472
+ system: c.system,
1473
+ code: c.code,
1474
+ display: c.display,
1475
+ }));
1348
1476
  }
1349
- function valueSetElementToCodeableConcept(element) {
1477
+ function valueSetElementToCodeableConcept(elements) {
1478
+ if (elements.length === 0) {
1479
+ return undefined;
1480
+ }
1350
1481
  return {
1351
- text: element.display,
1352
- coding: [
1353
- {
1354
- system: element.system,
1355
- code: element.code,
1356
- display: element.display,
1357
- },
1358
- ],
1482
+ coding: elements.map((e) => ({
1483
+ system: e.system,
1484
+ code: e.code,
1485
+ display: e.display,
1486
+ })),
1359
1487
  };
1360
1488
  }
1361
1489
 
1362
1490
  function CodeInput(props) {
1363
1491
  const [value, setValue] = React.useState(props.defaultValue);
1364
- function handleChange(newValue) {
1492
+ function handleChange(newValues) {
1493
+ const newValue = newValues[0];
1365
1494
  const newCode = valueSetElementToCode(newValue);
1366
1495
  setValue(newCode);
1367
1496
  if (props.onChange) {
1368
1497
  props.onChange(newCode);
1369
1498
  }
1370
1499
  }
1371
- return (React.createElement(ValueSetAutocomplete, { property: props.property, name: props.name, placeholder: props.placeholder, defaultValue: codeToValueSetElement(value), onChange: handleChange }));
1500
+ return (React.createElement(ValueSetAutocomplete, { elementDefinition: props.property, name: props.name, placeholder: props.placeholder, defaultValue: codeToValueSetElement(value), onChange: handleChange }));
1372
1501
  }
1373
1502
  function codeToValueSetElement(code) {
1374
1503
  return code ? { code } : undefined;
@@ -1379,14 +1508,15 @@
1379
1508
 
1380
1509
  function CodingInput(props) {
1381
1510
  const [value, setValue] = React.useState(props.defaultValue);
1382
- function handleChange(newValue) {
1511
+ function handleChange(newValues) {
1512
+ const newValue = newValues[0];
1383
1513
  const newConcept = newValue && valueSetElementToCoding(newValue);
1384
1514
  setValue(newConcept);
1385
1515
  if (props.onChange) {
1386
1516
  props.onChange(newConcept);
1387
1517
  }
1388
1518
  }
1389
- return (React.createElement(ValueSetAutocomplete, { property: props.property, name: props.name, placeholder: props.placeholder, defaultValue: value && codingToValueSetElement(value), onChange: handleChange }));
1519
+ return (React.createElement(ValueSetAutocomplete, { elementDefinition: props.property, name: props.name, placeholder: props.placeholder, defaultValue: value && codingToValueSetElement(value), onChange: handleChange }));
1390
1520
  }
1391
1521
  function codingToValueSetElement(coding) {
1392
1522
  return {
@@ -1694,7 +1824,7 @@
1694
1824
  Observation: 'code',
1695
1825
  RequestGroup: '_id',
1696
1826
  ActivityDefinition: 'name',
1697
- User: 'email',
1827
+ User: 'email:contains',
1698
1828
  };
1699
1829
  function ResourceInput(props) {
1700
1830
  const medplum = useMedplum();
@@ -1712,7 +1842,7 @@
1712
1842
  setLoading(true);
1713
1843
  const searchCode = SEARCH_CODES[props.resourceType] || 'name';
1714
1844
  const searchParams = new URLSearchParams({
1715
- [searchCode]: encodeURIComponent(input),
1845
+ [searchCode]: input,
1716
1846
  _count: '10',
1717
1847
  });
1718
1848
  const resources = yield medplum.searchResources(props.resourceType, searchParams);
@@ -2225,6 +2355,64 @@
2225
2355
  React.createElement(ResourceName, { value: props.value, link: props.link })));
2226
2356
  }
2227
2357
 
2358
+ /*
2359
+ * Request status: https://hl7.org/fhir/valueset-request-status.html
2360
+ * draft, active, on-hold, revoked, completed, entered-in-error, unknown
2361
+ *
2362
+ * Publication status: https://hl7.org/fhir/valueset-publication-status.html
2363
+ * draft, active, retired, unknown
2364
+ *
2365
+ * Observation status: https://www.hl7.org/fhir/valueset-observation-status.html
2366
+ * registered, preliminary, final, amended, corrected, cancelled, entered-in-error, unknown
2367
+ *
2368
+ * DiagnosticReport status: https://hl7.org/fhir/valueset-diagnostic-report-status.html
2369
+ * registered, preliminary, final, amended, corrected, appended, cancelled, entered-in-error, unknown
2370
+ *
2371
+ * Task status: https://hl7.org/fhir/valueset-task-status.html
2372
+ * draft, requested, received, accepted, rejected, ready, cancelled, in-progress, on-hold, failed, completed, entered-in-error
2373
+ *
2374
+ * Appointment status: https://www.hl7.org/fhir/valueset-appointmentstatus.html
2375
+ * proposed, pending, booked, arrived, fulfilled, cancelled, noshow, entered-in-error, chcked-in, waitlist
2376
+ */
2377
+ const statusToColor = {
2378
+ draft: 'blue',
2379
+ active: 'blue',
2380
+ 'on-hold': 'yellow',
2381
+ revoked: 'red',
2382
+ completed: 'green',
2383
+ 'entered-in-error': 'red',
2384
+ unknown: 'gray',
2385
+ retired: 'gray',
2386
+ registered: 'blue',
2387
+ preliminary: 'blue',
2388
+ final: 'green',
2389
+ amended: 'yellow',
2390
+ corrected: 'yellow',
2391
+ cancelled: 'red',
2392
+ requested: 'blue',
2393
+ received: 'blue',
2394
+ accepted: 'blue',
2395
+ rejected: 'red',
2396
+ ready: 'blue',
2397
+ 'in-progress': 'blue',
2398
+ failed: 'red',
2399
+ proposed: 'blue',
2400
+ pending: 'blue',
2401
+ booked: 'blue',
2402
+ arrived: 'blue',
2403
+ fulfilled: 'green',
2404
+ noshow: 'red',
2405
+ 'checked-in': 'blue',
2406
+ waitlist: 'gray',
2407
+ routine: 'gray',
2408
+ urgent: 'red',
2409
+ asap: 'red',
2410
+ stat: 'red',
2411
+ };
2412
+ function StatusBadge(props) {
2413
+ return React.createElement(core$1.Badge, { color: statusToColor[props.status] }, props.status);
2414
+ }
2415
+
2228
2416
  const useStyles$9 = core$1.createStyles((theme) => ({
2229
2417
  table: {
2230
2418
  border: `0.1px solid ${theme.colors.gray[5]}`,
@@ -2293,7 +2481,9 @@
2293
2481
  React.createElement("th", null, "Test"),
2294
2482
  React.createElement("th", null, "Value"),
2295
2483
  React.createElement("th", null, "Reference Range"),
2296
- React.createElement("th", null, "Interpretation"))),
2484
+ React.createElement("th", null, "Interpretation"),
2485
+ React.createElement("th", null, "Category"),
2486
+ React.createElement("th", null, "Status"))),
2297
2487
  React.createElement("tbody", null, (_a = props.value) === null || _a === void 0 ? void 0 : _a.map((observation, index) => (React.createElement(ObservationRow, { key: 'obs-' + index, value: observation }))))));
2298
2488
  }
2299
2489
  function ObservationRow(props) {
@@ -2311,7 +2501,10 @@
2311
2501
  React.createElement(ObservationValueDisplay, { value: observation })),
2312
2502
  React.createElement("td", null,
2313
2503
  React.createElement(ReferenceRangeDisplay, { value: observation.referenceRange })),
2314
- React.createElement("td", null, observation.interpretation && observation.interpretation.length > 0 && (React.createElement(CodeableConceptDisplay, { value: observation.interpretation[0] })))));
2504
+ React.createElement("td", null, observation.interpretation && observation.interpretation.length > 0 && (React.createElement(CodeableConceptDisplay, { value: observation.interpretation[0] }))),
2505
+ React.createElement("td", null, observation.category && observation.category.length > 0 && (React.createElement("ul", null, observation.category.map((concept) => (React.createElement("li", null,
2506
+ React.createElement(CodeableConceptDisplay, { value: concept }))))))),
2507
+ React.createElement("td", null, observation.status && React.createElement(StatusBadge, { status: observation.status }))));
2315
2508
  }
2316
2509
  function ObservationValueDisplay(props) {
2317
2510
  const obs = props.value;
@@ -4586,6 +4779,7 @@
4586
4779
  function setItems(newResponseItems) {
4587
4780
  const newResponse = {
4588
4781
  resourceType: 'QuestionnaireResponse',
4782
+ status: 'completed',
4589
4783
  item: newResponseItems,
4590
4784
  };
4591
4785
  setResponse(newResponse);
@@ -5339,63 +5533,6 @@
5339
5533
  return ((_b = (_a = interval.range) === null || _a === void 0 ? void 0 : _a.low) === null || _b === void 0 ? void 0 : _b.value) === undefined && ((_d = (_c = interval.range) === null || _c === void 0 ? void 0 : _c.high) === null || _d === void 0 ? void 0 : _d.value) === undefined;
5340
5534
  }
5341
5535
 
5342
- /*
5343
- * Request status: https://hl7.org/fhir/valueset-request-status.html
5344
- * draft, active, on-hold, revoked, completed, entered-in-error, unknown
5345
- *
5346
- * Publication status: https://hl7.org/fhir/valueset-publication-status.html
5347
- * draft, active, retired, unknown
5348
- *
5349
- * Observation status: https://www.hl7.org/fhir/valueset-observation-status.html
5350
- * registered, preliminary, final, amended, cancelled, entered-in-error, unknown
5351
- *
5352
- * DiagnosticReport status: https://hl7.org/fhir/valueset-diagnostic-report-status.html
5353
- * registered, preliminary, final, amended, corrected, appended, cancelled, entered-in-error, unknown
5354
- *
5355
- * Task status: https://hl7.org/fhir/valueset-task-status.html
5356
- * draft, requested, received, accepted, rejected, ready, cancelled, in-progress, on-hold, failed, completed, entered-in-error
5357
- *
5358
- * Appointment status: https://www.hl7.org/fhir/valueset-appointmentstatus.html
5359
- * proposed, pending, booked, arrived, fulfilled, cancelled, noshow, entered-in-error, chcked-in, waitlist
5360
- */
5361
- const statusToColor = {
5362
- draft: 'blue',
5363
- active: 'blue',
5364
- 'on-hold': 'yellow',
5365
- revoked: 'red',
5366
- completed: 'green',
5367
- 'entered-in-error': 'red',
5368
- unknown: 'gray',
5369
- retired: 'gray',
5370
- registered: 'blue',
5371
- preliminary: 'blue',
5372
- final: 'green',
5373
- amended: 'yellow',
5374
- cancelled: 'red',
5375
- requested: 'blue',
5376
- received: 'blue',
5377
- accepted: 'blue',
5378
- rejected: 'red',
5379
- ready: 'blue',
5380
- 'in-progress': 'blue',
5381
- failed: 'red',
5382
- proposed: 'blue',
5383
- pending: 'blue',
5384
- booked: 'blue',
5385
- arrived: 'blue',
5386
- fulfilled: 'green',
5387
- noshow: 'red',
5388
- 'checked-in': 'blue',
5389
- waitlist: 'gray',
5390
- routine: 'gray',
5391
- urgent: 'red',
5392
- asap: 'red',
5393
- stat: 'red',
5394
- };
5395
- function StatusBadge(props) {
5396
- return React.createElement(core$1.Badge, { color: statusToColor[props.status] }, props.status);
5397
- }
5398
-
5399
5536
  function RequestGroupDisplay(props) {
5400
5537
  var _a;
5401
5538
  const medplum = useMedplum();
@@ -5965,6 +6102,7 @@
5965
6102
  exports.AddressDisplay = AddressDisplay;
5966
6103
  exports.AddressInput = AddressInput;
5967
6104
  exports.AnnotationInput = AnnotationInput;
6105
+ exports.AsyncAutocomplete = AsyncAutocomplete;
5968
6106
  exports.AttachmentArrayDisplay = AttachmentArrayDisplay;
5969
6107
  exports.AttachmentArrayInput = AttachmentArrayInput;
5970
6108
  exports.AttachmentButton = AttachmentButton;