@medplum/react 0.9.21 → 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 CHANGED
@@ -5271,6 +5271,165 @@
5271
5271
  return `/${resource.resourceType}/${resource.id}/_history/${(_a = resource.meta) === null || _a === void 0 ? void 0 : _a.versionId}`;
5272
5272
  }
5273
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
+
5274
5433
  function ServiceRequestTimeline(props) {
5275
5434
  return (React__default["default"].createElement(ResourceTimeline, { value: props.serviceRequest, buildSearchRequests: (resource) => ({
5276
5435
  resourceType: 'Bundle',
@@ -5599,6 +5758,7 @@
5599
5758
  exports.ResourcePropertyInput = ResourcePropertyInput;
5600
5759
  exports.ResourceTable = ResourceTable;
5601
5760
  exports.ResourceTimeline = ResourceTimeline;
5761
+ exports.Scheduler = Scheduler;
5602
5762
  exports.Scrollable = Scrollable;
5603
5763
  exports.SearchChangeEvent = SearchChangeEvent;
5604
5764
  exports.SearchClickEvent = SearchClickEvent;