@medplum/react 0.9.19 → 0.9.22
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/dist/cjs/index.js +318 -36
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +1 -1
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/cjs/styles.css +58 -0
- package/dist/esm/index.js +317 -38
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +1 -1
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/styles.css +58 -0
- package/dist/types/ContactDetailDisplay.d.ts +6 -0
- package/dist/types/ContactDetailInput.d.ts +8 -0
- package/dist/types/ContactPointInput.d.ts +1 -1
- package/dist/types/GoogleButton.d.ts +3 -3
- package/dist/types/Input.d.ts +2 -1
- package/dist/types/SignInForm.d.ts +11 -8
- package/dist/types/index.d.ts +3 -0
- package/dist/types/stories/QuestionnaireForm.stories.d.ts +2 -0
- package/package.json +12 -12
package/dist/cjs/index.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
const className = 'medplum-input';
|
|
26
26
|
const issues = getIssuesForExpression(props.outcome, props.name);
|
|
27
27
|
const invalid = issues && issues.length > 0;
|
|
28
|
-
return (React__default["default"].createElement("input", { id: props.name, name: props.name, type: getInputType(props.type), size: props.size, step: props.step, className: className, defaultValue: props.defaultValue || '', required: props.required, autoCapitalize: props.autoCapitalize, autoComplete: props.autoComplete, autoFocus: props.autoFocus, ref: props.inputRef, "aria-invalid": invalid, "aria-describedby": invalid ? props.name + '-errors' : '', placeholder: props.placeholder, "data-testid": props.testid, disabled: props.disabled, onChange: (e) => {
|
|
28
|
+
return (React__default["default"].createElement("input", { id: props.name, name: props.name, type: getInputType(props.type), size: props.size, step: props.step, className: className, style: props.style, defaultValue: props.defaultValue || '', required: props.required, autoCapitalize: props.autoCapitalize, autoComplete: props.autoComplete, autoFocus: props.autoFocus, ref: props.inputRef, "aria-invalid": invalid, "aria-describedby": invalid ? props.name + '-errors' : '', placeholder: props.placeholder, "data-testid": props.testid, disabled: props.disabled, onChange: (e) => {
|
|
29
29
|
if (props.onChange) {
|
|
30
30
|
props.onChange(e.currentTarget.value);
|
|
31
31
|
}
|
|
@@ -876,6 +876,18 @@
|
|
|
876
876
|
return React__default["default"].createElement(React__default["default"].Fragment, null, builder.join('').trim());
|
|
877
877
|
}
|
|
878
878
|
|
|
879
|
+
function ContactDetailDisplay(props) {
|
|
880
|
+
var _a;
|
|
881
|
+
const contactDetail = props.value;
|
|
882
|
+
if (!contactDetail) {
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
886
|
+
contactDetail.name,
|
|
887
|
+
contactDetail.name && ': ', (_a = contactDetail.telecom) === null || _a === void 0 ? void 0 :
|
|
888
|
+
_a.map((telecom, index) => (React__default["default"].createElement(ContactPointDisplay, { key: 'telecom-' + index, value: telecom })))));
|
|
889
|
+
}
|
|
890
|
+
|
|
879
891
|
function DateTimeDisplay(props) {
|
|
880
892
|
if (!props.value) {
|
|
881
893
|
return null;
|
|
@@ -1024,6 +1036,8 @@
|
|
|
1024
1036
|
return React__default["default"].createElement(CodeableConceptDisplay, { value: value });
|
|
1025
1037
|
case core.PropertyType.Coding:
|
|
1026
1038
|
return React__default["default"].createElement(CodingDisplay, { value: value });
|
|
1039
|
+
case core.PropertyType.ContactDetail:
|
|
1040
|
+
return React__default["default"].createElement(ContactDetailDisplay, { value: value });
|
|
1027
1041
|
case core.PropertyType.ContactPoint:
|
|
1028
1042
|
return React__default["default"].createElement(ContactPointDisplay, { value: value });
|
|
1029
1043
|
case core.PropertyType.HumanName:
|
|
@@ -1193,19 +1207,34 @@
|
|
|
1193
1207
|
const ref = React.useRef();
|
|
1194
1208
|
ref.current = contactPoint;
|
|
1195
1209
|
function setContactPointWrapper(newValue) {
|
|
1210
|
+
if (newValue && Object.keys(newValue).length === 0) {
|
|
1211
|
+
newValue = undefined;
|
|
1212
|
+
}
|
|
1196
1213
|
setContactPoint(newValue);
|
|
1197
1214
|
if (props.onChange) {
|
|
1198
1215
|
props.onChange(newValue);
|
|
1199
1216
|
}
|
|
1200
1217
|
}
|
|
1201
1218
|
function setSystem(system) {
|
|
1202
|
-
|
|
1219
|
+
const newValue = Object.assign(Object.assign({}, ref.current), { system });
|
|
1220
|
+
if (!system) {
|
|
1221
|
+
delete newValue.system;
|
|
1222
|
+
}
|
|
1223
|
+
setContactPointWrapper(newValue);
|
|
1203
1224
|
}
|
|
1204
1225
|
function setUse(use) {
|
|
1205
|
-
|
|
1226
|
+
const newValue = Object.assign(Object.assign({}, ref.current), { use });
|
|
1227
|
+
if (!use) {
|
|
1228
|
+
delete newValue.use;
|
|
1229
|
+
}
|
|
1230
|
+
setContactPointWrapper(newValue);
|
|
1206
1231
|
}
|
|
1207
1232
|
function setValue(value) {
|
|
1208
|
-
|
|
1233
|
+
const newValue = Object.assign(Object.assign({}, ref.current), { value });
|
|
1234
|
+
if (!value) {
|
|
1235
|
+
delete newValue.value;
|
|
1236
|
+
}
|
|
1237
|
+
setContactPointWrapper(newValue);
|
|
1209
1238
|
}
|
|
1210
1239
|
return (React__default["default"].createElement(InputRow, null,
|
|
1211
1240
|
React__default["default"].createElement(Select, { defaultValue: contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.system, onChange: setSystem, testid: "system" },
|
|
@@ -1226,6 +1255,36 @@
|
|
|
1226
1255
|
React__default["default"].createElement(Input, { placeholder: "Value", defaultValue: contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.value, onChange: setValue })));
|
|
1227
1256
|
}
|
|
1228
1257
|
|
|
1258
|
+
function ContactDetailInput(props) {
|
|
1259
|
+
var _a;
|
|
1260
|
+
const [contactPoint, setContactDetail] = React.useState(props.defaultValue);
|
|
1261
|
+
const ref = React.useRef();
|
|
1262
|
+
ref.current = contactPoint;
|
|
1263
|
+
function setContactDetailWrapper(newValue) {
|
|
1264
|
+
setContactDetail(newValue);
|
|
1265
|
+
if (props.onChange) {
|
|
1266
|
+
props.onChange(newValue);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
function setName(name) {
|
|
1270
|
+
const newValue = Object.assign(Object.assign({}, ref.current), { name });
|
|
1271
|
+
if (!name) {
|
|
1272
|
+
delete newValue.name;
|
|
1273
|
+
}
|
|
1274
|
+
setContactDetailWrapper(newValue);
|
|
1275
|
+
}
|
|
1276
|
+
function setTelecom(telecom) {
|
|
1277
|
+
const newValue = Object.assign(Object.assign({}, ref.current), { telecom: telecom && [telecom] });
|
|
1278
|
+
if (!telecom) {
|
|
1279
|
+
delete newValue.telecom;
|
|
1280
|
+
}
|
|
1281
|
+
setContactDetailWrapper(newValue);
|
|
1282
|
+
}
|
|
1283
|
+
return (React__default["default"].createElement(InputRow, null,
|
|
1284
|
+
React__default["default"].createElement(Input, { name: props.name + '-name', placeholder: "Name", style: { width: 180 }, defaultValue: contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.name, onChange: setName }),
|
|
1285
|
+
React__default["default"].createElement(ContactPointInput, { name: props.name + '-telecom', defaultValue: (_a = contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.telecom) === null || _a === void 0 ? void 0 : _a[0], onChange: setTelecom })));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1229
1288
|
/**
|
|
1230
1289
|
* The DateTimeInput component is a wrapper around the HTML5 input type="datetime-local".
|
|
1231
1290
|
* The main purpose is to reconcile time zones.
|
|
@@ -1640,6 +1699,8 @@
|
|
|
1640
1699
|
return React__default["default"].createElement(CodeableConceptInput, { property: property, name: name, defaultValue: value, onChange: props.onChange });
|
|
1641
1700
|
case core.PropertyType.Coding:
|
|
1642
1701
|
return React__default["default"].createElement(CodingInput, { property: property, name: name, defaultValue: value, onChange: props.onChange });
|
|
1702
|
+
case core.PropertyType.ContactDetail:
|
|
1703
|
+
return React__default["default"].createElement(ContactDetailInput, { name: name, defaultValue: value, onChange: props.onChange });
|
|
1643
1704
|
case core.PropertyType.ContactPoint:
|
|
1644
1705
|
return React__default["default"].createElement(ContactPointInput, { name: name, defaultValue: value, onChange: props.onChange });
|
|
1645
1706
|
case core.PropertyType.Extension:
|
|
@@ -2123,8 +2184,7 @@
|
|
|
2123
2184
|
setHistory({});
|
|
2124
2185
|
return;
|
|
2125
2186
|
}
|
|
2126
|
-
|
|
2127
|
-
medplum.post('fhir/R4', batchRequest).then(handleBatchResponse);
|
|
2187
|
+
medplum.executeBatch(buildSearchRequests(resource)).then(handleBatchResponse);
|
|
2128
2188
|
}, [medplum, resource, buildSearchRequests]);
|
|
2129
2189
|
React.useEffect(() => {
|
|
2130
2190
|
loadTimeline();
|
|
@@ -4552,31 +4612,64 @@
|
|
|
4552
4612
|
return (React__default["default"].createElement(TextArea, { name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueString, onChange: (newValue) => onChangeAnswer({ valueString: newValue }) }));
|
|
4553
4613
|
case exports.QuestionnaireItemType.url:
|
|
4554
4614
|
return (React__default["default"].createElement(Input, { type: "url", name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueUri, onChange: (newValue) => onChangeAnswer({ valueUri: newValue }) }));
|
|
4555
|
-
case exports.QuestionnaireItemType.choice:
|
|
4556
|
-
case exports.QuestionnaireItemType.openChoice:
|
|
4557
|
-
return (React__default["default"].createElement("div", null, item.answerOption &&
|
|
4558
|
-
item.answerOption.map((option, index) => {
|
|
4559
|
-
const valueElementDefinition = core.globalSchema.types['QuestionnaireItemAnswerOption'].properties['value[x]'];
|
|
4560
|
-
const optionValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemAnswerOption', value: option }, 'value');
|
|
4561
|
-
const initialValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemInitial', value: initial }, 'value');
|
|
4562
|
-
const propertyName = 'value' + core.capitalize(optionValue.type);
|
|
4563
|
-
const optionName = `${name}-option-${index}`;
|
|
4564
|
-
return (React__default["default"].createElement("div", { key: optionName, className: "medplum-questionnaire-option-row" },
|
|
4565
|
-
React__default["default"].createElement("div", { className: "medplum-questionnaire-option-checkbox" },
|
|
4566
|
-
React__default["default"].createElement("input", { type: "radio", id: optionName, name: name, value: optionValue.value, defaultChecked: initialValue && core.stringify(optionValue) === core.stringify(initialValue), onChange: () => onChangeAnswer({ [propertyName]: optionValue.value }) })),
|
|
4567
|
-
React__default["default"].createElement("div", null,
|
|
4568
|
-
React__default["default"].createElement("label", { htmlFor: optionName },
|
|
4569
|
-
React__default["default"].createElement(ResourcePropertyDisplay, { property: valueElementDefinition, propertyType: optionValue.type, value: optionValue.value })))));
|
|
4570
|
-
})));
|
|
4571
4615
|
case exports.QuestionnaireItemType.attachment:
|
|
4572
4616
|
return (React__default["default"].createElement(AttachmentInput, { name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueAttachment, onChange: (newValue) => onChangeAnswer({ valueAttachment: newValue }) }));
|
|
4573
4617
|
case exports.QuestionnaireItemType.reference:
|
|
4574
4618
|
return (React__default["default"].createElement(ReferenceInput, { name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueReference, onChange: (newValue) => onChangeAnswer({ valueReference: newValue }) }));
|
|
4575
4619
|
case exports.QuestionnaireItemType.quantity:
|
|
4576
4620
|
return (React__default["default"].createElement(QuantityInput, { name: name, defaultValue: initial === null || initial === void 0 ? void 0 : initial.valueQuantity, onChange: (newValue) => onChangeAnswer({ valueQuantity: newValue }) }));
|
|
4621
|
+
case exports.QuestionnaireItemType.choice:
|
|
4622
|
+
case exports.QuestionnaireItemType.openChoice:
|
|
4623
|
+
if (isDropDownChoice(item)) {
|
|
4624
|
+
return (React__default["default"].createElement(QuestionnaireChoiceDropDownInput, { name: name, item: item, initial: initial, onChangeAnswer: onChangeAnswer }));
|
|
4625
|
+
}
|
|
4626
|
+
else {
|
|
4627
|
+
return (React__default["default"].createElement(QuestionnaireChoiceRadioInput, { name: name, item: item, initial: initial, onChangeAnswer: onChangeAnswer }));
|
|
4628
|
+
}
|
|
4577
4629
|
}
|
|
4578
4630
|
return null;
|
|
4579
4631
|
}
|
|
4632
|
+
function QuestionnaireChoiceDropDownInput(props) {
|
|
4633
|
+
const { name, item, initial } = props;
|
|
4634
|
+
const valueElementDefinition = core.globalSchema.types['QuestionnaireItemAnswerOption'].properties['value[x]'];
|
|
4635
|
+
const initialValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemInitial', value: initial }, 'value');
|
|
4636
|
+
return (React__default["default"].createElement("select", { id: name, name: name, className: "medplum-select", onChange: (e) => {
|
|
4637
|
+
const index = e.currentTarget.selectedIndex;
|
|
4638
|
+
if (index === 0) {
|
|
4639
|
+
props.onChangeAnswer({});
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
const option = item.answerOption[index - 1];
|
|
4643
|
+
const optionValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemAnswerOption', value: option }, 'value');
|
|
4644
|
+
const propertyName = 'value' + core.capitalize(optionValue.type);
|
|
4645
|
+
props.onChangeAnswer({ [propertyName]: optionValue.value });
|
|
4646
|
+
} },
|
|
4647
|
+
React__default["default"].createElement("option", null),
|
|
4648
|
+
item.answerOption &&
|
|
4649
|
+
item.answerOption.map((option, index) => {
|
|
4650
|
+
const optionValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemAnswerOption', value: option }, 'value');
|
|
4651
|
+
const optionName = `${name}-option-${index}`;
|
|
4652
|
+
return (React__default["default"].createElement("option", { key: optionName, value: optionValue.value, selected: initialValue && core.stringify(optionValue) === core.stringify(initialValue) },
|
|
4653
|
+
React__default["default"].createElement(ResourcePropertyDisplay, { property: valueElementDefinition, propertyType: optionValue.type, value: optionValue.value })));
|
|
4654
|
+
})));
|
|
4655
|
+
}
|
|
4656
|
+
function QuestionnaireChoiceRadioInput(props) {
|
|
4657
|
+
const { name, item, initial, onChangeAnswer } = props;
|
|
4658
|
+
const valueElementDefinition = core.globalSchema.types['QuestionnaireItemAnswerOption'].properties['value[x]'];
|
|
4659
|
+
const initialValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemInitial', value: initial }, 'value');
|
|
4660
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null, item.answerOption &&
|
|
4661
|
+
item.answerOption.map((option, index) => {
|
|
4662
|
+
const optionValue = core.getTypedPropertyValue({ type: 'QuestionnaireItemAnswerOption', value: option }, 'value');
|
|
4663
|
+
const propertyName = 'value' + core.capitalize(optionValue.type);
|
|
4664
|
+
const optionName = `${name}-option-${index}`;
|
|
4665
|
+
return (React__default["default"].createElement("div", { key: optionName, className: "medplum-questionnaire-option-row" },
|
|
4666
|
+
React__default["default"].createElement("div", { className: "medplum-questionnaire-option-checkbox" },
|
|
4667
|
+
React__default["default"].createElement("input", { type: "radio", id: optionName, name: name, value: optionValue.value, defaultChecked: initialValue && core.stringify(optionValue) === core.stringify(initialValue), onChange: () => onChangeAnswer({ [propertyName]: optionValue.value }) })),
|
|
4668
|
+
React__default["default"].createElement("div", null,
|
|
4669
|
+
React__default["default"].createElement("label", { htmlFor: optionName },
|
|
4670
|
+
React__default["default"].createElement(ResourcePropertyDisplay, { property: valueElementDefinition, propertyType: optionValue.type, value: optionValue.value })))));
|
|
4671
|
+
})));
|
|
4672
|
+
}
|
|
4580
4673
|
function buildInitialResponse(questionnaire) {
|
|
4581
4674
|
const response = {
|
|
4582
4675
|
resourceType: 'QuestionnaireResponse',
|
|
@@ -4602,6 +4695,14 @@
|
|
|
4602
4695
|
// have the same properties.
|
|
4603
4696
|
return Object.assign({}, answer);
|
|
4604
4697
|
}
|
|
4698
|
+
function isDropDownChoice(item) {
|
|
4699
|
+
var _a;
|
|
4700
|
+
return !!((_a = item.extension) === null || _a === void 0 ? void 0 : _a.some((e) => {
|
|
4701
|
+
var _a, _b, _c;
|
|
4702
|
+
return e.url === 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl' &&
|
|
4703
|
+
((_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';
|
|
4704
|
+
}));
|
|
4705
|
+
}
|
|
4605
4706
|
|
|
4606
4707
|
function QuestionnaireBuilder(props) {
|
|
4607
4708
|
const medplum = useMedplum();
|
|
@@ -4815,7 +4916,7 @@
|
|
|
4815
4916
|
const [responseBundle, setResponseBundle] = React.useState();
|
|
4816
4917
|
React.useEffect(() => {
|
|
4817
4918
|
if (requestGroup && !startedLoading) {
|
|
4818
|
-
medplum.
|
|
4919
|
+
medplum.executeBatch(buildBatchRequest(requestGroup)).then(setResponseBundle);
|
|
4819
4920
|
setStartedLoading(true);
|
|
4820
4921
|
}
|
|
4821
4922
|
}, [medplum, requestGroup, startedLoading]);
|
|
@@ -5170,6 +5271,165 @@
|
|
|
5170
5271
|
return `/${resource.resourceType}/${resource.id}/_history/${(_a = resource.meta) === null || _a === void 0 ? void 0 : _a.versionId}`;
|
|
5171
5272
|
}
|
|
5172
5273
|
|
|
5274
|
+
/**
|
|
5275
|
+
* Returns a month display string (e.g. "January 2020").
|
|
5276
|
+
* @param date Any date within the month.
|
|
5277
|
+
* @returns The month display string (e.g. "January 2020")
|
|
5278
|
+
*/
|
|
5279
|
+
function getMonthString(date) {
|
|
5280
|
+
return date.toLocaleString('default', { month: 'long' }) + ' ' + date.getFullYear();
|
|
5281
|
+
}
|
|
5282
|
+
function CalendarInput(props) {
|
|
5283
|
+
const [month, setMonth] = React.useState(getStartMonth);
|
|
5284
|
+
function moveMonth(delta) {
|
|
5285
|
+
setMonth((currMonth) => {
|
|
5286
|
+
const prevMonth = new Date(currMonth.getTime());
|
|
5287
|
+
prevMonth.setMonth(currMonth.getMonth() + delta);
|
|
5288
|
+
return prevMonth;
|
|
5289
|
+
});
|
|
5290
|
+
}
|
|
5291
|
+
const grid = React.useMemo(() => buildGrid(month, props.slots), [month, props.slots]);
|
|
5292
|
+
return (React__default["default"].createElement("div", null,
|
|
5293
|
+
React__default["default"].createElement(InputRow, null,
|
|
5294
|
+
React__default["default"].createElement("p", { style: { flex: 1 } }, getMonthString(month)),
|
|
5295
|
+
React__default["default"].createElement("p", null,
|
|
5296
|
+
React__default["default"].createElement(Button, { label: "Previous month", onClick: () => moveMonth(-1) }, "<"),
|
|
5297
|
+
React__default["default"].createElement(Button, { label: "Next month", onClick: () => moveMonth(1) }, ">"))),
|
|
5298
|
+
React__default["default"].createElement("table", { className: "medplum-calendar-table" },
|
|
5299
|
+
React__default["default"].createElement("thead", null,
|
|
5300
|
+
React__default["default"].createElement("tr", null,
|
|
5301
|
+
React__default["default"].createElement("th", null, "SUN"),
|
|
5302
|
+
React__default["default"].createElement("th", null, "MON"),
|
|
5303
|
+
React__default["default"].createElement("th", null, "TUE"),
|
|
5304
|
+
React__default["default"].createElement("th", null, "WED"),
|
|
5305
|
+
React__default["default"].createElement("th", null, "THU"),
|
|
5306
|
+
React__default["default"].createElement("th", null, "FRI"),
|
|
5307
|
+
React__default["default"].createElement("th", null, "SAT"))),
|
|
5308
|
+
React__default["default"].createElement("tbody", null, grid.map((week, weekIndex) => (React__default["default"].createElement("tr", { key: 'week-' + weekIndex }, week.map((day, dayIndex) => (React__default["default"].createElement("td", { key: 'day-' + dayIndex }, day && (React__default["default"].createElement("button", { disabled: !day.available, onClick: () => props.onClick(day.date) }, day.date.getDate()))))))))))));
|
|
5309
|
+
}
|
|
5310
|
+
function getStartMonth() {
|
|
5311
|
+
const result = new Date();
|
|
5312
|
+
result.setDate(1);
|
|
5313
|
+
result.setHours(0, 0, 0, 0);
|
|
5314
|
+
return result;
|
|
5315
|
+
}
|
|
5316
|
+
function buildGrid(startDate, slots) {
|
|
5317
|
+
const d = new Date(startDate.getFullYear(), startDate.getMonth());
|
|
5318
|
+
const grid = [];
|
|
5319
|
+
let row = [];
|
|
5320
|
+
// Fill leading empty days
|
|
5321
|
+
for (let i = 0; i < d.getDay(); i++) {
|
|
5322
|
+
row.push(undefined);
|
|
5323
|
+
}
|
|
5324
|
+
while (d.getMonth() === startDate.getMonth()) {
|
|
5325
|
+
row.push({
|
|
5326
|
+
date: new Date(d.getTime()),
|
|
5327
|
+
// available: isAvailable(d),
|
|
5328
|
+
available: isDayAvailable(d, slots),
|
|
5329
|
+
});
|
|
5330
|
+
if (d.getDay() === 6) {
|
|
5331
|
+
grid.push(row);
|
|
5332
|
+
row = [];
|
|
5333
|
+
}
|
|
5334
|
+
d.setDate(d.getDate() + 1);
|
|
5335
|
+
}
|
|
5336
|
+
// Fill trailing empty days
|
|
5337
|
+
if (d.getDay() !== 0) {
|
|
5338
|
+
for (let i = d.getDay(); i < 7; i++) {
|
|
5339
|
+
row.push(undefined);
|
|
5340
|
+
}
|
|
5341
|
+
grid.push(row);
|
|
5342
|
+
}
|
|
5343
|
+
return grid;
|
|
5344
|
+
}
|
|
5345
|
+
/**
|
|
5346
|
+
* Returns true if the given date is available for booking.
|
|
5347
|
+
* @param day The day to check.
|
|
5348
|
+
* @param slots The list of available slots.
|
|
5349
|
+
* @returns True if there are any available slots for the day.
|
|
5350
|
+
*/
|
|
5351
|
+
function isDayAvailable(day, slots) {
|
|
5352
|
+
// Note that slot start and end time may or may not be in UTC.
|
|
5353
|
+
for (const slot of slots) {
|
|
5354
|
+
const slotStart = new Date(slot.start);
|
|
5355
|
+
if (slotStart.getFullYear() === day.getFullYear() &&
|
|
5356
|
+
slotStart.getMonth() === day.getMonth() &&
|
|
5357
|
+
slotStart.getDate() === day.getDate()) {
|
|
5358
|
+
return true;
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
return false;
|
|
5362
|
+
}
|
|
5363
|
+
|
|
5364
|
+
function Scheduler(props) {
|
|
5365
|
+
var _a;
|
|
5366
|
+
const medplum = useMedplum();
|
|
5367
|
+
const schedule = useResource(props.schedule);
|
|
5368
|
+
const [slots, setSlots] = React.useState();
|
|
5369
|
+
const slotsRef = React.useRef();
|
|
5370
|
+
slotsRef.current = slots;
|
|
5371
|
+
const [date, setDate] = React.useState();
|
|
5372
|
+
const [slot, setSlot] = React.useState();
|
|
5373
|
+
const [info, setInfo] = React.useState();
|
|
5374
|
+
const [form, setForm] = React.useState();
|
|
5375
|
+
React.useEffect(() => {
|
|
5376
|
+
if (schedule) {
|
|
5377
|
+
medplum.search('Slot', 'schedule=' + core.getReferenceString(schedule)).then((bundle) => {
|
|
5378
|
+
setSlots(bundle.entry.map((entry) => entry.resource));
|
|
5379
|
+
});
|
|
5380
|
+
}
|
|
5381
|
+
else {
|
|
5382
|
+
setSlots(undefined);
|
|
5383
|
+
}
|
|
5384
|
+
}, [medplum, schedule]);
|
|
5385
|
+
if (!schedule || !slots) {
|
|
5386
|
+
return null;
|
|
5387
|
+
}
|
|
5388
|
+
const actor = (_a = schedule.actor) === null || _a === void 0 ? void 0 : _a[0];
|
|
5389
|
+
return (React__default["default"].createElement("div", { className: "medplum-calendar-container", "data-testid": "scheduler" },
|
|
5390
|
+
React__default["default"].createElement("div", { className: "medplum-calendar-info-pane" },
|
|
5391
|
+
actor && React__default["default"].createElement(Avatar, { value: actor, size: "large" }),
|
|
5392
|
+
actor && (React__default["default"].createElement("h1", null,
|
|
5393
|
+
React__default["default"].createElement(ResourceName, { value: actor }))),
|
|
5394
|
+
React__default["default"].createElement("p", null, "1 hour"),
|
|
5395
|
+
date && React__default["default"].createElement("p", null, date.toLocaleDateString()),
|
|
5396
|
+
slot && React__default["default"].createElement("p", null, formatTime(new Date(slot.start)))),
|
|
5397
|
+
React__default["default"].createElement("div", { className: "medplum-calendar-selection-pane" },
|
|
5398
|
+
!date && (React__default["default"].createElement("div", null,
|
|
5399
|
+
React__default["default"].createElement("h3", null, "Select date"),
|
|
5400
|
+
React__default["default"].createElement(CalendarInput, { slots: slots, onClick: setDate }))),
|
|
5401
|
+
date && !slot && (React__default["default"].createElement("div", null,
|
|
5402
|
+
React__default["default"].createElement("h3", null, "Select time"),
|
|
5403
|
+
slots.map((s) => {
|
|
5404
|
+
const slotStart = new Date(s.start);
|
|
5405
|
+
return (slotStart.getTime() > date.getTime() &&
|
|
5406
|
+
slotStart.getTime() < date.getTime() + 24 * 3600 * 1000 && (React__default["default"].createElement("div", { key: s.id },
|
|
5407
|
+
React__default["default"].createElement(Button, { style: { width: 150 }, onClick: () => setSlot(s) }, formatTime(slotStart)))));
|
|
5408
|
+
}))),
|
|
5409
|
+
date && slot && !info && (React__default["default"].createElement("div", null,
|
|
5410
|
+
React__default["default"].createElement("h3", null, "Enter your info"),
|
|
5411
|
+
React__default["default"].createElement(FormSection, { title: "Name", htmlFor: "name" },
|
|
5412
|
+
React__default["default"].createElement(Input, { name: "name" })),
|
|
5413
|
+
React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email" },
|
|
5414
|
+
React__default["default"].createElement(Input, { name: "email" })),
|
|
5415
|
+
React__default["default"].createElement(Button, { primary: true, onClick: () => setInfo('info') }, "Next"))),
|
|
5416
|
+
date && slot && info && !form && (React__default["default"].createElement("div", null,
|
|
5417
|
+
React__default["default"].createElement("h3", null, "Custom questions"),
|
|
5418
|
+
React__default["default"].createElement(FormSection, { title: "Question 1", htmlFor: "q1" },
|
|
5419
|
+
React__default["default"].createElement(Input, { name: "q1" })),
|
|
5420
|
+
React__default["default"].createElement(FormSection, { title: "Question 2", htmlFor: "q2" },
|
|
5421
|
+
React__default["default"].createElement(Input, { name: "email" })),
|
|
5422
|
+
React__default["default"].createElement(FormSection, { title: "Question 3", htmlFor: "q3" },
|
|
5423
|
+
React__default["default"].createElement(Input, { name: "email" })),
|
|
5424
|
+
React__default["default"].createElement(Button, { primary: true, onClick: () => setForm('form') }, "Next"))),
|
|
5425
|
+
date && slot && info && form && (React__default["default"].createElement("div", null,
|
|
5426
|
+
React__default["default"].createElement("h3", null, "You're all set!"),
|
|
5427
|
+
React__default["default"].createElement("p", null, "Check your email for a calendar invite."))))));
|
|
5428
|
+
}
|
|
5429
|
+
function formatTime(date) {
|
|
5430
|
+
return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
|
|
5431
|
+
}
|
|
5432
|
+
|
|
5173
5433
|
function ServiceRequestTimeline(props) {
|
|
5174
5434
|
return (React__default["default"].createElement(ResourceTimeline, { value: props.serviceRequest, buildSearchRequests: (resource) => ({
|
|
5175
5435
|
resourceType: 'Bundle',
|
|
@@ -5232,7 +5492,7 @@
|
|
|
5232
5492
|
|
|
5233
5493
|
function GoogleButton(props) {
|
|
5234
5494
|
const medplum = useMedplum();
|
|
5235
|
-
const {
|
|
5495
|
+
const { handleGoogleCredential } = props;
|
|
5236
5496
|
const googleClientId = getGoogleClientId(props.googleClientId);
|
|
5237
5497
|
const parentRef = React.useRef(null);
|
|
5238
5498
|
const [scriptLoaded, setScriptLoaded] = React.useState(typeof google !== 'undefined');
|
|
@@ -5246,7 +5506,7 @@
|
|
|
5246
5506
|
if (!initialized) {
|
|
5247
5507
|
google.accounts.id.initialize({
|
|
5248
5508
|
client_id: googleClientId,
|
|
5249
|
-
callback:
|
|
5509
|
+
callback: handleGoogleCredential,
|
|
5250
5510
|
});
|
|
5251
5511
|
setInitialized(true);
|
|
5252
5512
|
}
|
|
@@ -5254,7 +5514,7 @@
|
|
|
5254
5514
|
google.accounts.id.renderButton(parentRef.current, {});
|
|
5255
5515
|
setButtonRendered(true);
|
|
5256
5516
|
}
|
|
5257
|
-
}, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered,
|
|
5517
|
+
}, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered, handleGoogleCredential]);
|
|
5258
5518
|
if (!googleClientId) {
|
|
5259
5519
|
return null;
|
|
5260
5520
|
}
|
|
@@ -5285,19 +5545,24 @@
|
|
|
5285
5545
|
setMemberships(response.memberships);
|
|
5286
5546
|
}
|
|
5287
5547
|
if (response.code) {
|
|
5288
|
-
|
|
5289
|
-
.
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5548
|
+
if (props.onCode) {
|
|
5549
|
+
props.onCode(response.code);
|
|
5550
|
+
}
|
|
5551
|
+
else {
|
|
5552
|
+
medplum
|
|
5553
|
+
.processCode(response.code)
|
|
5554
|
+
.then(() => {
|
|
5555
|
+
if (props.onSuccess) {
|
|
5556
|
+
props.onSuccess();
|
|
5557
|
+
}
|
|
5558
|
+
})
|
|
5559
|
+
.catch(console.log);
|
|
5560
|
+
}
|
|
5296
5561
|
}
|
|
5297
5562
|
}
|
|
5298
5563
|
return (React__default["default"].createElement(Document, { width: 450 }, (() => {
|
|
5299
5564
|
if (!login) {
|
|
5300
|
-
return (React__default["default"].createElement(AuthenticationForm, { googleClientId: props.googleClientId, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
|
|
5565
|
+
return (React__default["default"].createElement(AuthenticationForm, { clientId: props.clientId, scope: props.scope, nonce: props.nonce, googleClientId: props.googleClientId, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
|
|
5301
5566
|
}
|
|
5302
5567
|
else if (memberships) {
|
|
5303
5568
|
return React__default["default"].createElement(ProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
|
|
@@ -5314,6 +5579,9 @@
|
|
|
5314
5579
|
return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => {
|
|
5315
5580
|
medplum
|
|
5316
5581
|
.startLogin({
|
|
5582
|
+
clientId: props.clientId,
|
|
5583
|
+
scope: props.scope,
|
|
5584
|
+
nonce: props.nonce,
|
|
5317
5585
|
email: formData.email,
|
|
5318
5586
|
password: formData.password,
|
|
5319
5587
|
remember: formData.remember === 'true',
|
|
@@ -5340,7 +5608,18 @@
|
|
|
5340
5608
|
React__default["default"].createElement("div", null,
|
|
5341
5609
|
React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Sign in"))),
|
|
5342
5610
|
React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
|
|
5343
|
-
React__default["default"].createElement(GoogleButton, { googleClientId: props.googleClientId,
|
|
5611
|
+
React__default["default"].createElement(GoogleButton, { googleClientId: props.googleClientId, handleGoogleCredential: (response) => {
|
|
5612
|
+
medplum
|
|
5613
|
+
.startGoogleLogin({
|
|
5614
|
+
clientId: props.clientId,
|
|
5615
|
+
scope: props.scope,
|
|
5616
|
+
nonce: props.nonce,
|
|
5617
|
+
googleClientId: response.clientId,
|
|
5618
|
+
googleCredential: response.credential,
|
|
5619
|
+
})
|
|
5620
|
+
.then(props.handleAuthResponse)
|
|
5621
|
+
.catch(setOutcome);
|
|
5622
|
+
} }))));
|
|
5344
5623
|
}
|
|
5345
5624
|
function ProfileForm(props) {
|
|
5346
5625
|
const medplum = useMedplum();
|
|
@@ -5426,6 +5705,8 @@
|
|
|
5426
5705
|
exports.CodeInput = CodeInput;
|
|
5427
5706
|
exports.CodeableConceptDisplay = CodeableConceptDisplay;
|
|
5428
5707
|
exports.CodeableConceptInput = CodeableConceptInput;
|
|
5708
|
+
exports.ContactDetailDisplay = ContactDetailDisplay;
|
|
5709
|
+
exports.ContactDetailInput = ContactDetailInput;
|
|
5429
5710
|
exports.ContactPointDisplay = ContactPointDisplay;
|
|
5430
5711
|
exports.ContactPointInput = ContactPointInput;
|
|
5431
5712
|
exports.DateTimeDisplay = DateTimeDisplay;
|
|
@@ -5477,6 +5758,7 @@
|
|
|
5477
5758
|
exports.ResourcePropertyInput = ResourcePropertyInput;
|
|
5478
5759
|
exports.ResourceTable = ResourceTable;
|
|
5479
5760
|
exports.ResourceTimeline = ResourceTimeline;
|
|
5761
|
+
exports.Scheduler = Scheduler;
|
|
5480
5762
|
exports.Scrollable = Scrollable;
|
|
5481
5763
|
exports.SearchChangeEvent = SearchChangeEvent;
|
|
5482
5764
|
exports.SearchClickEvent = SearchClickEvent;
|