@medplum/react 0.9.32 → 0.9.35

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/QuestionnaireForm.d.ts +4 -1
  3. package/dist/cjs/Scheduler.d.ts +2 -1
  4. package/dist/cjs/SearchControlField.d.ts +2 -3
  5. package/dist/cjs/SearchFieldEditor.d.ts +1 -2
  6. package/dist/cjs/SearchFilterEditor.d.ts +1 -2
  7. package/dist/cjs/SearchFilterValueDialog.d.ts +1 -2
  8. package/dist/cjs/SearchFilterValueInput.d.ts +0 -2
  9. package/dist/cjs/SearchPopupMenu.d.ts +1 -2
  10. package/dist/cjs/auth/AuthenticationForm.d.ts +2 -0
  11. package/dist/cjs/auth/SignInForm.d.ts +2 -0
  12. package/dist/cjs/index.js +125 -71
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/index.min.js +1 -1
  15. package/dist/cjs/index.min.js.map +1 -1
  16. package/dist/cjs/stories/QuestionnaireForm.stories.d.ts +1 -0
  17. package/dist/cjs/styles.css +51 -51
  18. package/dist/esm/GoogleButton.js +2 -2
  19. package/dist/esm/GoogleButton.js.map +1 -1
  20. package/dist/esm/Popup.js +5 -1
  21. package/dist/esm/Popup.js.map +1 -1
  22. package/dist/esm/QuestionnaireBuilder.js +1 -1
  23. package/dist/esm/QuestionnaireBuilder.js.map +1 -1
  24. package/dist/esm/QuestionnaireForm.d.ts +4 -1
  25. package/dist/esm/QuestionnaireForm.js +49 -10
  26. package/dist/esm/QuestionnaireForm.js.map +1 -1
  27. package/dist/esm/Scheduler.d.ts +2 -1
  28. package/dist/esm/Scheduler.js +18 -24
  29. package/dist/esm/Scheduler.js.map +1 -1
  30. package/dist/esm/SearchControl.js +10 -14
  31. package/dist/esm/SearchControl.js.map +1 -1
  32. package/dist/esm/SearchControlField.d.ts +2 -3
  33. package/dist/esm/SearchControlField.js +6 -8
  34. package/dist/esm/SearchControlField.js.map +1 -1
  35. package/dist/esm/SearchFieldEditor.d.ts +1 -2
  36. package/dist/esm/SearchFieldEditor.js +2 -2
  37. package/dist/esm/SearchFieldEditor.js.map +1 -1
  38. package/dist/esm/SearchFilterEditor.d.ts +1 -2
  39. package/dist/esm/SearchFilterEditor.js +5 -6
  40. package/dist/esm/SearchFilterEditor.js.map +1 -1
  41. package/dist/esm/SearchFilterValueDialog.d.ts +1 -2
  42. package/dist/esm/SearchFilterValueDialog.js +1 -1
  43. package/dist/esm/SearchFilterValueDialog.js.map +1 -1
  44. package/dist/esm/SearchFilterValueInput.d.ts +0 -2
  45. package/dist/esm/SearchFilterValueInput.js +1 -1
  46. package/dist/esm/SearchFilterValueInput.js.map +1 -1
  47. package/dist/esm/SearchPopupMenu.d.ts +1 -2
  48. package/dist/esm/SearchPopupMenu.js.map +1 -1
  49. package/dist/esm/ServiceRequestTimeline.js +3 -3
  50. package/dist/esm/ServiceRequestTimeline.js.map +1 -1
  51. package/dist/esm/auth/AuthenticationForm.d.ts +2 -0
  52. package/dist/esm/auth/AuthenticationForm.js +4 -0
  53. package/dist/esm/auth/AuthenticationForm.js.map +1 -1
  54. package/dist/esm/auth/SignInForm.d.ts +2 -0
  55. package/dist/esm/auth/SignInForm.js +1 -1
  56. package/dist/esm/auth/SignInForm.js.map +1 -1
  57. package/dist/esm/index.js +1 -1
  58. package/dist/esm/index.min.js +1 -1
  59. package/dist/esm/index.min.js.map +1 -1
  60. package/dist/esm/stories/QuestionnaireForm.stories.d.ts +1 -0
  61. package/dist/esm/styles.css +51 -51
  62. package/dist/esm/utils/blame.js +1 -0
  63. package/dist/esm/utils/blame.js.map +1 -1
  64. package/dist/esm/utils/outcomes.js +19 -1
  65. package/dist/esm/utils/outcomes.js.map +1 -1
  66. package/package.json +13 -13
  67. package/stats.html +4034 -0
package/dist/cjs/index.js CHANGED
@@ -18,7 +18,25 @@
18
18
 
19
19
  function getIssuesForExpression(outcome, expression) {
20
20
  var _a;
21
- return (_a = outcome === null || outcome === void 0 ? void 0 : outcome.issue) === null || _a === void 0 ? void 0 : _a.filter((issue) => { var _a; return ((_a = issue.expression) === null || _a === void 0 ? void 0 : _a[0]) === expression; });
21
+ return (_a = outcome === null || outcome === void 0 ? void 0 : outcome.issue) === null || _a === void 0 ? void 0 : _a.filter((issue) => { var _a; return isExpressionMatch((_a = issue.expression) === null || _a === void 0 ? void 0 : _a[0], expression); });
22
+ }
23
+ function isExpressionMatch(expr1, expr2) {
24
+ // Expression can be either "fieldName" or "resourceType.fieldName"
25
+ if (expr1 === expr2) {
26
+ return true;
27
+ }
28
+ if (!expr1 || !expr2) {
29
+ return false;
30
+ }
31
+ const dot1 = expr1.indexOf('.');
32
+ if (dot1 >= 0 && expr1.substring(dot1 + 1) === expr2) {
33
+ return true;
34
+ }
35
+ const dot2 = expr2.indexOf('.');
36
+ if (dot2 >= 0 && expr2.substring(dot2 + 1) === expr1) {
37
+ return true;
38
+ }
39
+ return false;
22
40
  }
23
41
 
24
42
  function Input(props) {
@@ -518,9 +536,9 @@
518
536
  return clientId;
519
537
  }
520
538
  const origin = window.location.protocol + '//' + window.location.host;
521
- const authorizedOrigins = (_b = (_a = "undefined") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
539
+ const authorizedOrigins = (_b = (_a = "http://localhost:3000,http://127.0.0.1:3000,http://localhost:6006,http://127.0.0.1:6006,https://app.medplum.com,https://docs.medplum.com,https://storybook.medplum.com,https://graphiql.medplum.com,https://www.medplum.com") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
522
540
  if (authorizedOrigins.includes(origin)) {
523
- return "undefined";
541
+ return "921088377005-3j1sa10vr6hj86jgmdfh2l53v3mp7lfi.apps.googleusercontent.com";
524
542
  }
525
543
  return undefined;
526
544
  }
@@ -713,6 +731,8 @@
713
731
  clientId: props.clientId,
714
732
  scope: props.scope,
715
733
  nonce: props.nonce,
734
+ codeChallenge: props.codeChallenge,
735
+ codeChallengeMethod: props.codeChallengeMethod,
716
736
  email: formData.email,
717
737
  password: formData.password,
718
738
  remember: formData.remember === 'true',
@@ -734,6 +754,8 @@
734
754
  clientId: props.clientId,
735
755
  scope: props.scope,
736
756
  nonce: props.nonce,
757
+ codeChallenge: props.codeChallenge,
758
+ codeChallengeMethod: props.codeChallengeMethod,
737
759
  googleClientId: response.clientId,
738
760
  googleCredential: response.credential,
739
761
  })
@@ -887,7 +909,7 @@
887
909
  }
888
910
  return (React__default["default"].createElement(Document, { width: 450 }, (() => {
889
911
  if (!login) {
890
- return (React__default["default"].createElement(AuthenticationForm, { projectId: props.projectId, clientId: props.clientId, scope: props.scope, nonce: props.nonce, googleClientId: props.googleClientId, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
912
+ return (React__default["default"].createElement(AuthenticationForm, { projectId: props.projectId, clientId: props.clientId, scope: props.scope, nonce: props.nonce, googleClientId: props.googleClientId, codeChallenge: props.codeChallenge, codeChallengeMethod: props.codeChallengeMethod, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
891
913
  }
892
914
  else if (memberships) {
893
915
  return React__default["default"].createElement(ChooseProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
@@ -2546,11 +2568,14 @@
2546
2568
  ((_b = propsRef.current) === null || _b === void 0 ? void 0 : _b.autoClose) &&
2547
2569
  (ref === null || ref === void 0 ? void 0 : ref.current) &&
2548
2570
  !ref.current.contains(e.target)) {
2571
+ killEvent(e);
2549
2572
  props.onClose();
2550
2573
  }
2551
2574
  }
2552
2575
  document.addEventListener('click', handleClick, true);
2553
- return () => document.removeEventListener('click', handleClick, true);
2576
+ return () => {
2577
+ document.removeEventListener('click', handleClick, true);
2578
+ };
2554
2579
  }, [props]);
2555
2580
  // Listen for changes in the location
2556
2581
  // If the browser navigates to a new page, close the popup
@@ -2952,27 +2977,25 @@
2952
2977
 
2953
2978
  /**
2954
2979
  * Returns the collection of field definitions for the search request.
2955
- * @param typeSchema The schema for the resource type
2956
2980
  * @param search The search request definition.
2957
2981
  * @returns An array of field definitions.
2958
2982
  */
2959
- function getFieldDefinitions(schema, search) {
2983
+ function getFieldDefinitions(search) {
2960
2984
  const resourceType = search.resourceType;
2961
2985
  const fields = [];
2962
2986
  for (const name of search.fields || ['id', '_lastUpdated']) {
2963
- fields.push(getFieldDefinition(schema, resourceType, name));
2987
+ fields.push(getFieldDefinition(resourceType, name));
2964
2988
  }
2965
2989
  return fields;
2966
2990
  }
2967
2991
  /**
2968
2992
  * Return the field definition for a given field name.
2969
2993
  * Field names can be either property names or search parameter codes.
2970
- * @param typeSchema The schema for the resource type
2971
2994
  * @param resourceType The resource type.
2972
2995
  * @param name The search field name (either property name or search parameter code).
2973
2996
  * @returns The field definition.
2974
2997
  */
2975
- function getFieldDefinition(schema, resourceType, name) {
2998
+ function getFieldDefinition(resourceType, name) {
2976
2999
  var _a;
2977
3000
  if (name === '_lastUpdated') {
2978
3001
  return {
@@ -3004,7 +3027,7 @@
3004
3027
  ],
3005
3028
  };
3006
3029
  }
3007
- const typeSchema = schema.types[resourceType];
3030
+ const typeSchema = core.globalSchema.types[resourceType];
3008
3031
  const exactElementDefinition = typeSchema.properties[name];
3009
3032
  const exactSearchParam = (_a = typeSchema.searchParams) === null || _a === void 0 ? void 0 : _a[name.toLowerCase()];
3010
3033
  // Best case: Exact match of element definition or search parameter.
@@ -3035,7 +3058,7 @@
3035
3058
  // Patient.email is a search parameter for the Patient.telecom element.
3036
3059
  // So we need to walk backwards to find the element definition.
3037
3060
  if (exactSearchParam) {
3038
- const details = core.getSearchParameterDetails(schema, resourceType, exactSearchParam);
3061
+ const details = core.getSearchParameterDetails(resourceType, exactSearchParam);
3039
3062
  return { name, elementDefinition: details.elementDefinition, searchParams: [exactSearchParam] };
3040
3063
  }
3041
3064
  // Worst case: no element definition and no search parameter.
@@ -3651,7 +3674,7 @@
3651
3674
  return null;
3652
3675
  }
3653
3676
  const resourceType = props.search.resourceType;
3654
- const typeDef = props.schema.types[resourceType];
3677
+ const typeDef = core.globalSchema.types[resourceType];
3655
3678
  const selected = (_a = state.search.fields) !== null && _a !== void 0 ? _a : [];
3656
3679
  const available = getFieldsList(typeDef)
3657
3680
  .filter((field) => !(selected === null || selected === void 0 ? void 0 : selected.includes(field)))
@@ -3726,7 +3749,7 @@
3726
3749
 
3727
3750
  function SearchFilterValueInput(props) {
3728
3751
  var _a;
3729
- const details = core.getSearchParameterDetails(props.schema, props.resourceType, props.searchParam);
3752
+ const details = core.getSearchParameterDetails(props.resourceType, props.searchParam);
3730
3753
  const name = 'filter-value';
3731
3754
  switch (details.type) {
3732
3755
  case core.SearchParameterType.REFERENCE:
@@ -3787,9 +3810,8 @@
3787
3810
  if (!props.visible) {
3788
3811
  return null;
3789
3812
  }
3790
- const schema = props.schema;
3791
3813
  const resourceType = props.search.resourceType;
3792
- const searchParams = schema.types[resourceType].searchParams;
3814
+ const searchParams = core.globalSchema.types[resourceType].searchParams;
3793
3815
  const filters = search.filters || [];
3794
3816
  return (React__default["default"].createElement(Dialog, { title: "Filters", visible: props.visible, onOk: () => props.onOk(searchRef.current), onCancel: props.onCancel },
3795
3817
  React__default["default"].createElement("div", { className: "medplum-filter-editor" },
@@ -3808,7 +3830,7 @@
3808
3830
  React__default["default"].createElement("tbody", null,
3809
3831
  filters.map((filter, index) => {
3810
3832
  if (index === editingIndex) {
3811
- return (React__default["default"].createElement(FilterRowInput, { key: `filter-${index}-${filters.length}-input`, schema: schema, resourceType: resourceType, searchParams: searchParams, defaultValue: filter, okText: "Save", onOk: (newFilter) => {
3833
+ return (React__default["default"].createElement(FilterRowInput, { key: `filter-${index}-${filters.length}-input`, resourceType: resourceType, searchParams: searchParams, defaultValue: filter, okText: "Save", onOk: (newFilter) => {
3812
3834
  const newFilters = [...filters];
3813
3835
  newFilters[index] = newFilter;
3814
3836
  setSearch(setFilters(searchRef.current, newFilters));
@@ -3819,7 +3841,7 @@
3819
3841
  return (React__default["default"].createElement(FilterRowDisplay, { key: `filter-${index}-${filters.length}-display`, resourceType: resourceType, searchParams: searchParams, filter: filter, onEdit: () => setEditingIndex(index), onDelete: () => setSearch(deleteFilter(searchRef.current, index)) }));
3820
3842
  }
3821
3843
  }),
3822
- React__default["default"].createElement(FilterRowInput, { schema: schema, resourceType: resourceType, searchParams: searchParams, okText: "Add", onOk: onAddFilter }))))));
3844
+ React__default["default"].createElement(FilterRowInput, { resourceType: resourceType, searchParams: searchParams, okText: "Add", onOk: onAddFilter }))))));
3823
3845
  }
3824
3846
  function FilterRowDisplay(props) {
3825
3847
  const { filter } = props;
@@ -3856,7 +3878,7 @@
3856
3878
  React__default["default"].createElement("td", null, operators && (React__default["default"].createElement(Select, { testid: "filter-operation", defaultValue: value.operator, onChange: setFilterOperator },
3857
3879
  React__default["default"].createElement("option", { value: "" }),
3858
3880
  operators.map((operator) => (React__default["default"].createElement("option", { key: operator, value: operator }, getOpString(operator))))))),
3859
- React__default["default"].createElement("td", null, searchParam && value.operator && (React__default["default"].createElement(SearchFilterValueInput, { schema: props.schema, resourceType: props.resourceType, searchParam: searchParam, defaultValue: value.value, onChange: setFilterValue }))),
3881
+ React__default["default"].createElement("td", null, searchParam && value.operator && (React__default["default"].createElement(SearchFilterValueInput, { resourceType: props.resourceType, searchParam: searchParam, defaultValue: value.value, onChange: setFilterValue }))),
3860
3882
  React__default["default"].createElement("td", null,
3861
3883
  value.code && value.operator && value.value && (React__default["default"].createElement(Button, { size: "small", onClick: () => {
3862
3884
  props.onOk(valueRef.current);
@@ -3877,7 +3899,7 @@
3877
3899
  return (React__default["default"].createElement(Dialog, { title: props.title, visible: props.visible, onOk: onOk, onCancel: props.onCancel },
3878
3900
  React__default["default"].createElement("div", { style: { width: 500 } },
3879
3901
  React__default["default"].createElement(Form, { onSubmit: onOk },
3880
- React__default["default"].createElement(SearchFilterValueInput, { schema: props.schema, resourceType: props.resourceType, searchParam: props.searchParam, defaultValue: value, autoFocus: true, onChange: setValue })))));
3902
+ React__default["default"].createElement(SearchFilterValueInput, { resourceType: props.resourceType, searchParam: props.searchParam, defaultValue: value, autoFocus: true, onChange: setValue })))));
3881
3903
  }
3882
3904
 
3883
3905
  function MenuSeparator() {
@@ -4073,7 +4095,7 @@
4073
4095
  function SearchControl(props) {
4074
4096
  var _a, _b, _c, _d;
4075
4097
  const medplum = useMedplum();
4076
- const [schema, setSchema] = React.useState();
4098
+ const [schemaLoaded, setSchemaLoaded] = React.useState(false);
4077
4099
  const [outcome, setOutcome] = React.useState();
4078
4100
  const { search, onLoad } = props;
4079
4101
  const [state, setState] = React.useState({
@@ -4091,7 +4113,7 @@
4091
4113
  React.useEffect(() => {
4092
4114
  setOutcome(undefined);
4093
4115
  medplum
4094
- .search(search.resourceType, core.formatSearchQuery(Object.assign(Object.assign({}, search), { total: 'accurate' })))
4116
+ .search(search.resourceType, core.formatSearchQuery(Object.assign(Object.assign({}, search), { total: 'accurate', fields: undefined })))
4095
4117
  .then((response) => {
4096
4118
  setState(Object.assign(Object.assign({}, stateRef.current), { searchResponse: response }));
4097
4119
  if (onLoad) {
@@ -4185,19 +4207,15 @@
4185
4207
  React.useEffect(() => {
4186
4208
  medplum
4187
4209
  .requestSchema(props.search.resourceType)
4188
- .then((newSchema) => {
4189
- // The schema could have the same object identity,
4190
- // so need to use the spread operator to kick React re-render.
4191
- setSchema(Object.assign({}, newSchema));
4192
- })
4210
+ .then(() => setSchemaLoaded(true))
4193
4211
  .catch(console.log);
4194
4212
  }, [medplum, props.search.resourceType]);
4195
- const typeSchema = (_a = schema === null || schema === void 0 ? void 0 : schema.types) === null || _a === void 0 ? void 0 : _a[props.search.resourceType];
4213
+ const typeSchema = schemaLoaded && ((_a = core.globalSchema === null || core.globalSchema === void 0 ? void 0 : core.globalSchema.types) === null || _a === void 0 ? void 0 : _a[props.search.resourceType]);
4196
4214
  if (!typeSchema) {
4197
4215
  return React__default["default"].createElement(Loading, null);
4198
4216
  }
4199
4217
  const checkboxColumn = props.checkboxesEnabled;
4200
- const fields = getFieldDefinitions(schema, search);
4218
+ const fields = getFieldDefinitions(search);
4201
4219
  const resourceType = search.resourceType;
4202
4220
  const lastResult = state.searchResponse;
4203
4221
  const entries = lastResult === null || lastResult === void 0 ? void 0 : lastResult.entry;
@@ -4221,9 +4239,9 @@
4221
4239
  props.onBulk && (React__default["default"].createElement(Button, { size: "small", onClick: () => props.onBulk(Object.keys(state.selected)) }, "Bulk..."))),
4222
4240
  lastResult && (React__default["default"].createElement("div", null,
4223
4241
  React__default["default"].createElement("span", { className: "medplum-search-summary" },
4224
- getStart(search, lastResult.total),
4242
+ getStart$1(search, lastResult.total),
4225
4243
  "-",
4226
- getEnd(search, lastResult.total),
4244
+ getEnd$1(search, lastResult.total),
4227
4245
  " of",
4228
4246
  ' ', (_d = lastResult.total) === null || _d === void 0 ? void 0 :
4229
4247
  _d.toLocaleString()),
@@ -4247,7 +4265,7 @@
4247
4265
  (resources === null || resources === void 0 ? void 0 : resources.length) === 0 && (React__default["default"].createElement("div", { "data-testid": "empty-search", className: "medplum-empty-search" }, "No results")),
4248
4266
  outcome && (React__default["default"].createElement("div", { "data-testid": "search-error", className: "medplum-empty-search" },
4249
4267
  React__default["default"].createElement("pre", { style: { textAlign: 'left' } }, JSON.stringify(outcome, undefined, 2)))),
4250
- React__default["default"].createElement(SearchPopupMenu, { schema: schema, search: props.search, visible: state.popupVisible, x: state.popupX, y: state.popupY, searchParams: state.popupSearchParams, onPrompt: (searchParam, filter) => {
4268
+ React__default["default"].createElement(SearchPopupMenu, { search: props.search, visible: state.popupVisible, x: state.popupX, y: state.popupY, searchParams: state.popupSearchParams, onPrompt: (searchParam, filter) => {
4251
4269
  setState(Object.assign(Object.assign({}, stateRef.current), { popupVisible: false, filterDialogVisible: true, filterDialogSearchParam: searchParam, filterDialogFilter: filter }));
4252
4270
  }, onChange: (result) => {
4253
4271
  emitSearchChange(result);
@@ -4255,19 +4273,19 @@
4255
4273
  }, onClose: () => {
4256
4274
  setState(Object.assign(Object.assign({}, stateRef.current), { popupVisible: false, popupSearchParams: undefined }));
4257
4275
  } }),
4258
- React__default["default"].createElement(SearchFieldEditor, { schema: schema, search: props.search, visible: stateRef.current.fieldEditorVisible, onOk: (result) => {
4276
+ React__default["default"].createElement(SearchFieldEditor, { search: props.search, visible: stateRef.current.fieldEditorVisible, onOk: (result) => {
4259
4277
  emitSearchChange(result);
4260
4278
  setState(Object.assign(Object.assign({}, stateRef.current), { fieldEditorVisible: false }));
4261
4279
  }, onCancel: () => {
4262
4280
  setState(Object.assign(Object.assign({}, stateRef.current), { fieldEditorVisible: false }));
4263
4281
  } }),
4264
- React__default["default"].createElement(SearchFilterEditor, { schema: schema, search: props.search, visible: stateRef.current.filterEditorVisible, onOk: (result) => {
4282
+ React__default["default"].createElement(SearchFilterEditor, { search: props.search, visible: stateRef.current.filterEditorVisible, onOk: (result) => {
4265
4283
  emitSearchChange(result);
4266
4284
  setState(Object.assign(Object.assign({}, stateRef.current), { filterEditorVisible: false }));
4267
4285
  }, onCancel: () => {
4268
4286
  setState(Object.assign(Object.assign({}, stateRef.current), { filterEditorVisible: false }));
4269
4287
  } }),
4270
- React__default["default"].createElement(SearchFilterValueDialog, { visible: stateRef.current.filterDialogVisible, title: 'Input', schema: schema, resourceType: resourceType, searchParam: state.filterDialogSearchParam, filter: state.filterDialogFilter, defaultValue: '', onOk: (filter) => {
4288
+ React__default["default"].createElement(SearchFilterValueDialog, { visible: stateRef.current.filterDialogVisible, title: 'Input', resourceType: resourceType, searchParam: state.filterDialogSearchParam, filter: state.filterDialogFilter, defaultValue: '', onOk: (filter) => {
4271
4289
  emitSearchChange(addFilter(props.search, filter.code, filter.operator, filter.value));
4272
4290
  setState(Object.assign(Object.assign({}, stateRef.current), { filterDialogVisible: false }));
4273
4291
  }, onCancel: () => {
@@ -4290,11 +4308,11 @@
4290
4308
  return (React__default["default"].createElement("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "rgba(0, 0, 0, 0.3)", strokeWidth: 2 },
4291
4309
  React__default["default"].createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 6h16M4 12h16m-7 6h7" })));
4292
4310
  }
4293
- function getStart(search, total) {
4311
+ function getStart$1(search, total) {
4294
4312
  var _a;
4295
4313
  return Math.min(total, ((_a = search.offset) !== null && _a !== void 0 ? _a : 0) + 1);
4296
4314
  }
4297
- function getEnd(search, total) {
4315
+ function getEnd$1(search, total) {
4298
4316
  var _a, _b;
4299
4317
  return Math.min(total, (((_a = search.offset) !== null && _a !== void 0 ? _a : 0) + 1) * ((_b = search.count) !== null && _b !== void 0 ? _b : core.DEFAULT_SEARCH_COUNT));
4300
4318
  }
@@ -5015,17 +5033,24 @@
5015
5033
  const [schema, setSchema] = React.useState();
5016
5034
  const questionnaire = useResource(props.questionnaire);
5017
5035
  const [response, setResponse] = React.useState();
5036
+ const [answers, setAnswers] = React.useState({});
5018
5037
  React.useEffect(() => {
5019
- medplum.requestSchema('Questionnaire').then(setSchema).catch(console.log);
5038
+ medplum
5039
+ .requestSchema('Questionnaire')
5040
+ .then(() => medplum.requestSchema('QuestionnaireResponse'))
5041
+ .then(setSchema)
5042
+ .catch(console.log);
5020
5043
  }, [medplum]);
5021
5044
  React.useEffect(() => {
5022
5045
  setResponse(questionnaire ? buildInitialResponse(questionnaire) : undefined);
5023
5046
  }, [questionnaire]);
5024
5047
  function setItems(newResponseItems) {
5025
- setResponse({
5048
+ const newResponse = {
5026
5049
  resourceType: 'QuestionnaireResponse',
5027
5050
  item: newResponseItems,
5028
- });
5051
+ };
5052
+ setResponse(newResponse);
5053
+ setAnswers(core.getQuestionnaireAnswers(newResponse));
5029
5054
  }
5030
5055
  if (!schema || !questionnaire) {
5031
5056
  return null;
@@ -5036,8 +5061,8 @@
5036
5061
  }
5037
5062
  } },
5038
5063
  questionnaire.title && React__default["default"].createElement("h1", null, questionnaire.title),
5039
- questionnaire.item && React__default["default"].createElement(QuestionnaireFormItemArray, { items: questionnaire.item, onChange: setItems }),
5040
- React__default["default"].createElement(Button, { type: "submit", size: "large" }, "OK")));
5064
+ questionnaire.item && (React__default["default"].createElement(QuestionnaireFormItemArray, { items: questionnaire.item, answers: answers, onChange: setItems })),
5065
+ React__default["default"].createElement(Button, { type: "submit", size: "large" }, props.submitButtonText || 'OK')));
5041
5066
  }
5042
5067
  function QuestionnaireFormItemArray(props) {
5043
5068
  const [responseItems, setResponseItems] = React.useState(buildInitialResponseItems(props.items));
@@ -5048,11 +5073,14 @@
5048
5073
  props.onChange(newResponseItems);
5049
5074
  }
5050
5075
  return (React__default["default"].createElement(React__default["default"].Fragment, null, props.items.map((item, index) => {
5076
+ if (!isQuestionEnabled(item, props.answers)) {
5077
+ return null;
5078
+ }
5051
5079
  if (item.type === exports.QuestionnaireItemType.display) {
5052
5080
  return React__default["default"].createElement("p", { key: item.linkId }, item.text);
5053
5081
  }
5054
5082
  if (item.type === exports.QuestionnaireItemType.group) {
5055
- return (React__default["default"].createElement(QuestionnaireFormItem, { key: item.linkId, item: item, onChange: (newResponseItem) => setResponseItem(index, newResponseItem) }));
5083
+ return (React__default["default"].createElement(QuestionnaireFormItem, { key: item.linkId, item: item, answers: props.answers, onChange: (newResponseItem) => setResponseItem(index, newResponseItem) }));
5056
5084
  }
5057
5085
  if (item.type === exports.QuestionnaireItemType.boolean) {
5058
5086
  const initial = item.initial && item.initial.length > 0 ? item.initial[0] : undefined;
@@ -5064,7 +5092,7 @@
5064
5092
  }) })));
5065
5093
  }
5066
5094
  return (React__default["default"].createElement(FormSection, { key: item.linkId, htmlFor: item.linkId, title: item.text || '' },
5067
- React__default["default"].createElement(QuestionnaireFormItem, { item: item, onChange: (newResponseItem) => setResponseItem(index, newResponseItem) })));
5095
+ React__default["default"].createElement(QuestionnaireFormItem, { item: item, answers: props.answers, onChange: (newResponseItem) => setResponseItem(index, newResponseItem) })));
5068
5096
  })));
5069
5097
  }
5070
5098
  function QuestionnaireFormItem(props) {
@@ -5096,7 +5124,7 @@
5096
5124
  case exports.QuestionnaireItemType.group:
5097
5125
  return (React__default["default"].createElement("div", null,
5098
5126
  React__default["default"].createElement("h3", null, item.text),
5099
- item.item && React__default["default"].createElement(QuestionnaireFormItemArray, { items: item.item, onChange: onChangeItem })));
5127
+ item.item && (React__default["default"].createElement(QuestionnaireFormItemArray, { items: item.item, answers: props.answers, onChange: onChangeItem }))));
5100
5128
  case exports.QuestionnaireItemType.boolean:
5101
5129
  return (React__default["default"].createElement(Checkbox, { name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueBoolean, onChange: (newValue) => onChangeAnswer({ valueBoolean: newValue }) }));
5102
5130
  case exports.QuestionnaireItemType.decimal:
@@ -5207,6 +5235,35 @@
5207
5235
  ((_c = (_b = (_a = e.valueCodeableConcept) === null || _a === void 0 ? void 0 : _a.coding) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.code) === 'drop-down';
5208
5236
  }));
5209
5237
  }
5238
+ function isQuestionEnabled(item, answers) {
5239
+ if (!item.enableWhen) {
5240
+ return true;
5241
+ }
5242
+ const enableBehavior = item.enableBehavior || 'any';
5243
+ for (const enableWhen of item.enableWhen) {
5244
+ const expectedAnswer = core.getTypedPropertyValue({
5245
+ type: 'QuestionnaireItemEnableWhen',
5246
+ value: enableWhen,
5247
+ }, 'answer[x]');
5248
+ const actualAnswer = core.getTypedPropertyValue({
5249
+ type: 'QuestionnaireResponseItemAnswer',
5250
+ value: answers[enableWhen.question],
5251
+ }, 'value[x]');
5252
+ const match = core.deepEquals(expectedAnswer, actualAnswer);
5253
+ if (enableBehavior === 'any' && match) {
5254
+ return true;
5255
+ }
5256
+ if (enableBehavior === 'all' && !match) {
5257
+ return false;
5258
+ }
5259
+ }
5260
+ if (enableBehavior === 'any') {
5261
+ return false;
5262
+ }
5263
+ else {
5264
+ return true;
5265
+ }
5266
+ }
5210
5267
 
5211
5268
  function QuestionnaireBuilder(props) {
5212
5269
  const medplum = useMedplum();
@@ -5303,7 +5360,7 @@
5303
5360
  isChoiceQuestion(item) && (React__default["default"].createElement(AnswerBuilder, { options: item.answerOption, onChange: (newOptions) => changeProperty('answerOption', newOptions) })))) : (React__default["default"].createElement(React__default["default"].Fragment, null,
5304
5361
  resource.title && React__default["default"].createElement("h1", null, resource.title),
5305
5362
  item.text && React__default["default"].createElement("p", null, item.text),
5306
- !isContainer && React__default["default"].createElement(QuestionnaireFormItem, { item: item, onChange: () => undefined }))),
5363
+ !isContainer && React__default["default"].createElement(QuestionnaireFormItem, { item: item, answers: {}, onChange: () => undefined }))),
5307
5364
  item.item &&
5308
5365
  item.item.map((i) => (React__default["default"].createElement("div", { key: i.id },
5309
5366
  React__default["default"].createElement(ItemBuilder, { item: i, selectedKey: props.selectedKey, setSelectedKey: props.setSelectedKey, hoverKey: props.hoverKey, setHoverKey: props.setHoverKey, onChange: changeItem, onRemove: () => removeItem(i) })))),
@@ -5593,6 +5650,7 @@
5593
5650
  function blame(history) {
5594
5651
  // Convert to array of array of lines
5595
5652
  const versions = history.entry
5653
+ .filter((entry) => !!entry.resource)
5596
5654
  .map((entry) => {
5597
5655
  var _a;
5598
5656
  return ({
@@ -5876,22 +5934,23 @@
5876
5934
  var _a;
5877
5935
  const medplum = useMedplum();
5878
5936
  const schedule = useResource(props.schedule);
5937
+ const questionnaire = useResource(props.questionnaire);
5879
5938
  const [slots, setSlots] = React.useState();
5880
5939
  const slotsRef = React.useRef();
5881
5940
  slotsRef.current = slots;
5882
5941
  const [month, setMonth] = React.useState(getStartMonth());
5883
5942
  const [date, setDate] = React.useState();
5884
5943
  const [slot, setSlot] = React.useState();
5885
- const [info, setInfo] = React.useState();
5886
- const [form, setForm] = React.useState();
5944
+ const [response, setResponse] = React.useState();
5887
5945
  React.useEffect(() => {
5888
5946
  if (schedule) {
5889
5947
  setSlots([]);
5890
5948
  medplum
5891
5949
  .searchResources('Slot', new URLSearchParams([
5950
+ ['_count', (30 * 24).toString()],
5892
5951
  ['schedule', core.getReferenceString(schedule)],
5893
- ['start', 'gt' + month.toISOString()],
5894
- ['start', 'lt' + new Date(month.getTime() + 31 * 24 * 60 * 60 * 1000).toISOString()],
5952
+ ['start', 'gt' + getStart(month)],
5953
+ ['start', 'lt' + getEnd(month)],
5895
5954
  ]))
5896
5955
  .then(setSlots)
5897
5956
  .catch(console.log);
@@ -5900,7 +5959,7 @@
5900
5959
  setSlots(undefined);
5901
5960
  }
5902
5961
  }, [medplum, schedule, month]);
5903
- if (!schedule || !slots) {
5962
+ if (!schedule || !slots || !questionnaire) {
5904
5963
  return null;
5905
5964
  }
5906
5965
  const actor = (_a = schedule.actor) === null || _a === void 0 ? void 0 : _a[0];
@@ -5924,26 +5983,20 @@
5924
5983
  slotStart.getTime() < date.getTime() + 24 * 3600 * 1000 && (React__default["default"].createElement("div", { key: s.id },
5925
5984
  React__default["default"].createElement(Button, { style: { width: 150 }, onClick: () => setSlot(s) }, formatTime(slotStart)))));
5926
5985
  }))),
5927
- date && slot && !info && (React__default["default"].createElement("div", null,
5928
- React__default["default"].createElement("h3", null, "Enter your info"),
5929
- React__default["default"].createElement(FormSection, { title: "Name", htmlFor: "name" },
5930
- React__default["default"].createElement(Input, { name: "name" })),
5931
- React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email" },
5932
- React__default["default"].createElement(Input, { name: "email" })),
5933
- React__default["default"].createElement(Button, { primary: true, onClick: () => setInfo('info') }, "Next"))),
5934
- date && slot && info && !form && (React__default["default"].createElement("div", null,
5935
- React__default["default"].createElement("h3", null, "Custom questions"),
5936
- React__default["default"].createElement(FormSection, { title: "Question 1", htmlFor: "q1" },
5937
- React__default["default"].createElement(Input, { name: "q1" })),
5938
- React__default["default"].createElement(FormSection, { title: "Question 2", htmlFor: "q2" },
5939
- React__default["default"].createElement(Input, { name: "email" })),
5940
- React__default["default"].createElement(FormSection, { title: "Question 3", htmlFor: "q3" },
5941
- React__default["default"].createElement(Input, { name: "email" })),
5942
- React__default["default"].createElement(Button, { primary: true, onClick: () => setForm('form') }, "Next"))),
5943
- date && slot && info && form && (React__default["default"].createElement("div", null,
5986
+ date && slot && !response && (React__default["default"].createElement(QuestionnaireForm, { questionnaire: questionnaire, submitButtonText: 'Next', onSubmit: setResponse })),
5987
+ date && slot && response && (React__default["default"].createElement("div", null,
5944
5988
  React__default["default"].createElement("h3", null, "You're all set!"),
5945
5989
  React__default["default"].createElement("p", null, "Check your email for a calendar invite."))))));
5946
5990
  }
5991
+ function getStart(month) {
5992
+ return formatSlotInstant(month.getTime());
5993
+ }
5994
+ function getEnd(month) {
5995
+ return formatSlotInstant(month.getTime() + 31 * 24 * 60 * 60 * 1000);
5996
+ }
5997
+ function formatSlotInstant(time) {
5998
+ return new Date(Math.max(Date.now(), time)).toISOString();
5999
+ }
5947
6000
  function formatTime(date) {
5948
6001
  return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
5949
6002
  }
@@ -5962,19 +6015,19 @@
5962
6015
  {
5963
6016
  request: {
5964
6017
  method: 'GET',
5965
- url: `Communication?based-on=${core.getReferenceString(resource)}`,
6018
+ url: `Communication?based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
5966
6019
  },
5967
6020
  },
5968
6021
  {
5969
6022
  request: {
5970
6023
  method: 'GET',
5971
- url: `Media?_count=100&based-on=${core.getReferenceString(resource)}`,
6024
+ url: `Media?_count=100&based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
5972
6025
  },
5973
6026
  },
5974
6027
  {
5975
6028
  request: {
5976
6029
  method: 'GET',
5977
- url: `DiagnosticReport?based-on=${core.getReferenceString(resource)}`,
6030
+ url: `DiagnosticReport?based-on=${core.getReferenceString(resource)}&_sort=-_lastUpdated`,
5978
6031
  },
5979
6032
  },
5980
6033
  ],
@@ -6163,6 +6216,7 @@
6163
6216
  exports.hasFilterOnField = hasFilterOnField;
6164
6217
  exports.initRecaptcha = initRecaptcha;
6165
6218
  exports.isChoiceQuestion = isChoiceQuestion;
6219
+ exports.isQuestionEnabled = isQuestionEnabled;
6166
6220
  exports.isSortDescending = isSortDescending;
6167
6221
  exports.movePage = movePage;
6168
6222
  exports.parseForm = parseForm;