@medplum/react 0.9.29 → 0.9.32

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 (59) hide show
  1. package/dist/cjs/auth/AuthenticationForm.d.ts +14 -0
  2. package/dist/cjs/auth/ChooseProfileForm.d.ts +8 -0
  3. package/dist/cjs/auth/NewProjectForm.d.ts +7 -0
  4. package/dist/cjs/auth/NewUserForm.d.ts +10 -0
  5. package/dist/cjs/auth/RegisterForm.d.ts +12 -0
  6. package/dist/cjs/{SignInForm.d.ts → auth/SignInForm.d.ts} +1 -1
  7. package/dist/cjs/index.d.ts +3 -2
  8. package/dist/cjs/index.js +609 -548
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/index.min.js +1 -1
  11. package/dist/cjs/index.min.js.map +1 -1
  12. package/dist/cjs/styles.css +197 -197
  13. package/dist/esm/Autocomplete.js +1 -0
  14. package/dist/esm/Autocomplete.js.map +1 -1
  15. package/dist/esm/CodeInput.js +1 -1
  16. package/dist/esm/CodeInput.js.map +1 -1
  17. package/dist/esm/CodeableConceptInput.js +1 -1
  18. package/dist/esm/CodeableConceptInput.js.map +1 -1
  19. package/dist/esm/CodingInput.js +1 -1
  20. package/dist/esm/CodingInput.js.map +1 -1
  21. package/dist/esm/GoogleButton.js +2 -2
  22. package/dist/esm/GoogleButton.js.map +1 -1
  23. package/dist/esm/MedplumLink.js +29 -14
  24. package/dist/esm/MedplumLink.js.map +1 -1
  25. package/dist/esm/ResourceHistoryTable.js +15 -11
  26. package/dist/esm/ResourceHistoryTable.js.map +1 -1
  27. package/dist/esm/ResourceTimeline.js +3 -1
  28. package/dist/esm/ResourceTimeline.js.map +1 -1
  29. package/dist/esm/auth/AuthenticationForm.d.ts +14 -0
  30. package/dist/esm/{SignInForm.js → auth/AuthenticationForm.js} +12 -76
  31. package/dist/esm/auth/AuthenticationForm.js.map +1 -0
  32. package/dist/esm/auth/ChooseProfileForm.d.ts +8 -0
  33. package/dist/esm/auth/ChooseProfileForm.js +32 -0
  34. package/dist/esm/auth/ChooseProfileForm.js.map +1 -0
  35. package/dist/esm/auth/NewProjectForm.d.ts +7 -0
  36. package/dist/esm/auth/NewProjectForm.js +42 -0
  37. package/dist/esm/auth/NewProjectForm.js.map +1 -0
  38. package/dist/esm/auth/NewUserForm.d.ts +10 -0
  39. package/dist/esm/auth/NewUserForm.js +87 -0
  40. package/dist/esm/auth/NewUserForm.js.map +1 -0
  41. package/dist/esm/auth/RegisterForm.d.ts +12 -0
  42. package/dist/esm/auth/RegisterForm.js +39 -0
  43. package/dist/esm/auth/RegisterForm.js.map +1 -0
  44. package/dist/esm/{SignInForm.d.ts → auth/SignInForm.d.ts} +1 -1
  45. package/dist/esm/auth/SignInForm.js +52 -0
  46. package/dist/esm/auth/SignInForm.js.map +1 -0
  47. package/dist/esm/index.d.ts +3 -2
  48. package/dist/esm/index.js +3 -2
  49. package/dist/esm/index.js.map +1 -1
  50. package/dist/esm/index.min.js +1 -1
  51. package/dist/esm/index.min.js.map +1 -1
  52. package/dist/esm/styles.css +197 -197
  53. package/package.json +16 -18
  54. package/dist/cjs/RegisterForm.d.ts +0 -18
  55. package/dist/esm/RegisterForm.d.ts +0 -18
  56. package/dist/esm/RegisterForm.js +0 -121
  57. package/dist/esm/RegisterForm.js.map +0 -1
  58. package/dist/esm/SignInForm.js.map +0 -1
  59. package/stats.html +0 -4034
package/dist/cjs/index.js CHANGED
@@ -329,6 +329,578 @@
329
329
  return React__default["default"].createElement(UploadButton, { onUpload: setValueWrapper });
330
330
  }
331
331
 
332
+ function Document(props) {
333
+ return (React__default["default"].createElement("main", { className: "medplum-document" },
334
+ React__default["default"].createElement("article", { style: { maxWidth: props.width } }, props.children)));
335
+ }
336
+
337
+ /******************************************************************************
338
+ Copyright (c) Microsoft Corporation.
339
+
340
+ Permission to use, copy, modify, and/or distribute this software for any
341
+ purpose with or without fee is hereby granted.
342
+
343
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
344
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
345
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
346
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
347
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
348
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
349
+ PERFORMANCE OF THIS SOFTWARE.
350
+ ***************************************************************************** */
351
+
352
+ function __awaiter(thisArg, _arguments, P, generator) {
353
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
354
+ return new (P || (P = Promise))(function (resolve, reject) {
355
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
356
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
357
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
358
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
359
+ });
360
+ }
361
+
362
+ /**
363
+ * Parses an HTML form and returns the result as a JavaScript object.
364
+ * @param form The HTML form element.
365
+ */
366
+ function parseForm(form) {
367
+ const result = {};
368
+ for (const element of Array.from(form.elements)) {
369
+ if (element instanceof HTMLInputElement) {
370
+ parseInputElement(result, element);
371
+ }
372
+ else if (element instanceof HTMLTextAreaElement) {
373
+ result[element.name] = element.value;
374
+ }
375
+ else if (element instanceof HTMLSelectElement) {
376
+ parseSelectElement(result, element);
377
+ }
378
+ }
379
+ return result;
380
+ }
381
+ /**
382
+ * Parses an HTML input element.
383
+ * Sets the name/value pair in the result,
384
+ * but only if the element is enabled and checked.
385
+ * @param el The input element.
386
+ * @param result The result builder.
387
+ */
388
+ function parseInputElement(result, el) {
389
+ if (el.disabled) {
390
+ // Ignore disabled elements
391
+ return;
392
+ }
393
+ if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {
394
+ // Ignore unchecked radio or checkbox elements
395
+ return;
396
+ }
397
+ result[el.name] = el.value;
398
+ }
399
+ /**
400
+ * Parses an HTML select element.
401
+ * Sets the name/value pair if one is selected.
402
+ * @param result The result builder.
403
+ * @param el The select element.
404
+ */
405
+ function parseSelectElement(result, el) {
406
+ result[el.name] = el.value;
407
+ }
408
+
409
+ function Form(props) {
410
+ return (React__default["default"].createElement("form", { style: props.style, "data-testid": props.testid, onSubmit: (e) => {
411
+ e.preventDefault();
412
+ const formData = parseForm(e.target);
413
+ if (props.onSubmit) {
414
+ props.onSubmit(formData);
415
+ }
416
+ } }, props.children));
417
+ }
418
+
419
+ function FormSection(props) {
420
+ const issues = getIssuesForExpression(props.outcome, props.htmlFor);
421
+ const invalid = issues && issues.length > 0;
422
+ return (React__default["default"].createElement("fieldset", { className: "medplum-form-section" },
423
+ props.title && React__default["default"].createElement("label", { htmlFor: props.htmlFor }, props.title),
424
+ props.description && React__default["default"].createElement("p", null, props.description),
425
+ props.children,
426
+ invalid && (React__default["default"].createElement("div", { id: props.htmlFor + '-errors', className: "medplum-input-error" }, issues === null || issues === void 0 ? void 0 : issues.map((issue) => {
427
+ var _a, _b;
428
+ return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
429
+ })))));
430
+ }
431
+
432
+ function Logo(props) {
433
+ return (React__default["default"].createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 491 491", style: { width: props.size, height: props.size } },
434
+ React__default["default"].createElement("title", null, "Medplum Logo"),
435
+ React__default["default"].createElement("path", { fill: props.fill || '#ad7136', d: "M282 67c6-16 16-29 29-40L289 0c-22 17-37 41-43 68l17 23 19-24z" }),
436
+ React__default["default"].createElement("path", { fill: props.fill || '#654b87', d: "M311 63c-17 0-33 4-48 11-16-7-32-11-49-11-87 0-158 96-158 214s71 214 158 214c17 0 33-4 49-11 15 7 31 11 48 11 87 0 158-96 158-214S398 63 311 63z" }),
437
+ React__default["default"].createElement("path", { fill: props.fill || '#463068', d: "M231 489l-17 2c-87 0-158-96-158-214S127 63 214 63l17 1c-39 12-70 102-70 213s31 201 70 212z" }),
438
+ React__default["default"].createElement("path", { fill: props.fill || '#70d65b', d: "M207 220a176 176 0 01-177 43A176 176 0 01251 43l1 5c17 59 2 125-45 172z" }),
439
+ React__default["default"].createElement("path", { fill: props.fill || '#58b741', d: "M252 48A421 421 0 0057 270l-27-7A176 176 0 01251 43l1 5z" })));
440
+ }
441
+
442
+ function NewProjectForm(props) {
443
+ const medplum = useMedplum();
444
+ const [outcome, setOutcome] = React.useState();
445
+ return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => __awaiter(this, void 0, void 0, function* () {
446
+ try {
447
+ props.handleAuthResponse(yield medplum.startNewProject({
448
+ login: props.login,
449
+ projectName: formData.projectName,
450
+ }));
451
+ }
452
+ catch (err) {
453
+ setOutcome(err);
454
+ }
455
+ }) },
456
+ React__default["default"].createElement("div", { className: "medplum-center" },
457
+ React__default["default"].createElement(Logo, { size: 32 }),
458
+ React__default["default"].createElement("h1", null, "Create project")),
459
+ React__default["default"].createElement(FormSection, { title: "Project Name", htmlFor: "projectName", outcome: outcome },
460
+ React__default["default"].createElement(Input, { name: "projectName", type: "text", testid: "projectName", placeholder: "My Project", required: true, outcome: outcome })),
461
+ React__default["default"].createElement("p", { style: { fontSize: '12px', color: '#888' } },
462
+ "By clicking submit you agree to the Medplum ",
463
+ React__default["default"].createElement("a", { href: "https://www.medplum.com/privacy" }, "Privacy\u00A0Policy"),
464
+ ' and ',
465
+ React__default["default"].createElement("a", { href: "https://www.medplum.com/terms" }, "Terms\u00A0of\u00A0Service"),
466
+ "."),
467
+ React__default["default"].createElement("div", { className: "medplum-signin-buttons" },
468
+ React__default["default"].createElement("div", null),
469
+ React__default["default"].createElement("div", null,
470
+ React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Create project")))));
471
+ }
472
+
473
+ /**
474
+ * Dynamically creates a script tag for the specified JavaScript file.
475
+ * @param src The JavaScript file URL.
476
+ */
477
+ function createScriptTag(src, onload) {
478
+ const head = document.getElementsByTagName('head')[0];
479
+ const script = document.createElement('script');
480
+ script.async = true;
481
+ script.src = src;
482
+ script.onload = onload || null;
483
+ head.appendChild(script);
484
+ }
485
+
486
+ function GoogleButton(props) {
487
+ const medplum = useMedplum();
488
+ const { googleClientId, handleGoogleCredential } = props;
489
+ const parentRef = React.useRef(null);
490
+ const [scriptLoaded, setScriptLoaded] = React.useState(typeof google !== 'undefined');
491
+ const [initialized, setInitialized] = React.useState(false);
492
+ const [buttonRendered, setButtonRendered] = React.useState(false);
493
+ React.useEffect(() => {
494
+ if (typeof google === 'undefined') {
495
+ createScriptTag('https://accounts.google.com/gsi/client', () => setScriptLoaded(true));
496
+ return;
497
+ }
498
+ if (!initialized) {
499
+ google.accounts.id.initialize({
500
+ client_id: googleClientId,
501
+ callback: handleGoogleCredential,
502
+ });
503
+ setInitialized(true);
504
+ }
505
+ if (parentRef.current && !buttonRendered) {
506
+ google.accounts.id.renderButton(parentRef.current, {});
507
+ setButtonRendered(true);
508
+ }
509
+ }, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered, handleGoogleCredential]);
510
+ if (!googleClientId) {
511
+ return null;
512
+ }
513
+ return React__default["default"].createElement("div", { ref: parentRef });
514
+ }
515
+ function getGoogleClientId(clientId) {
516
+ var _a, _b;
517
+ if (clientId) {
518
+ return clientId;
519
+ }
520
+ 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 : [];
522
+ if (authorizedOrigins.includes(origin)) {
523
+ return "undefined";
524
+ }
525
+ return undefined;
526
+ }
527
+
528
+ /**
529
+ * Dynamically loads the recaptcha script.
530
+ * We do not want to load the script on page load unless the user needs it.
531
+ * @param siteKey The reCAPTCHA site key, available from the reCAPTCHA admin page.
532
+ */
533
+ function initRecaptcha(siteKey) {
534
+ if (typeof grecaptcha === 'undefined') {
535
+ createScriptTag('https://www.google.com/recaptcha/api.js?render=' + siteKey);
536
+ }
537
+ }
538
+ /**
539
+ * Starts a request to generate a recapcha token.
540
+ * @param siteKey The reCAPTCHA site key, available from the reCAPTCHA admin page.
541
+ * @returns Promise to a recaptcha token for the current user.
542
+ */
543
+ function getRecaptcha(siteKey) {
544
+ return new Promise((resolve, reject) => {
545
+ grecaptcha.ready(() => __awaiter(this, void 0, void 0, function* () {
546
+ try {
547
+ resolve(yield grecaptcha.execute(siteKey, { action: 'submit' }));
548
+ }
549
+ catch (err) {
550
+ reject(err);
551
+ }
552
+ }));
553
+ });
554
+ }
555
+
556
+ function NewUserForm(props) {
557
+ const googleClientId = getGoogleClientId(props.googleClientId);
558
+ const recaptchaSiteKey = props.recaptchaSiteKey;
559
+ const medplum = useMedplum();
560
+ const [outcome, setOutcome] = React.useState();
561
+ const issues = getIssuesForExpression(outcome, undefined);
562
+ React.useEffect(() => initRecaptcha(recaptchaSiteKey), [recaptchaSiteKey]);
563
+ return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => __awaiter(this, void 0, void 0, function* () {
564
+ try {
565
+ const recaptchaToken = yield getRecaptcha(recaptchaSiteKey);
566
+ props.handleAuthResponse(yield medplum.startNewUser({
567
+ projectId: props.projectId,
568
+ firstName: formData.firstName,
569
+ lastName: formData.lastName,
570
+ email: formData.email,
571
+ password: formData.password,
572
+ remember: formData.remember === 'true',
573
+ recaptchaSiteKey,
574
+ recaptchaToken,
575
+ }));
576
+ }
577
+ catch (err) {
578
+ setOutcome(err);
579
+ }
580
+ }) },
581
+ React__default["default"].createElement("div", { className: "medplum-center" }, props.children),
582
+ issues && (React__default["default"].createElement("div", { className: "medplum-input-error" }, issues.map((issue) => {
583
+ var _a, _b;
584
+ return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
585
+ }))),
586
+ googleClientId && (React__default["default"].createElement(React__default["default"].Fragment, null,
587
+ React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
588
+ React__default["default"].createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: (response) => __awaiter(this, void 0, void 0, function* () {
589
+ try {
590
+ props.handleAuthResponse(yield medplum.startGoogleLogin({
591
+ googleClientId: response.clientId,
592
+ googleCredential: response.credential,
593
+ createUser: true,
594
+ }));
595
+ }
596
+ catch (err) {
597
+ setOutcome(err);
598
+ }
599
+ }) })),
600
+ React__default["default"].createElement("div", { className: "medplum-signin-separator" }, "or"))),
601
+ React__default["default"].createElement(FormSection, { title: "First Name", htmlFor: "firstName", outcome: outcome },
602
+ React__default["default"].createElement(Input, { name: "firstName", type: "text", testid: "firstName", placeholder: "First name", required: true, autoFocus: true, outcome: outcome })),
603
+ React__default["default"].createElement(FormSection, { title: "Last Name", htmlFor: "lastName", outcome: outcome },
604
+ React__default["default"].createElement(Input, { name: "lastName", type: "text", testid: "lastName", placeholder: "Last name", required: true, outcome: outcome })),
605
+ React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email", outcome: outcome },
606
+ React__default["default"].createElement(Input, { name: "email", type: "email", testid: "email", placeholder: "name@domain.com", required: true, outcome: outcome })),
607
+ React__default["default"].createElement(FormSection, { title: "Password", htmlFor: "password", outcome: outcome },
608
+ React__default["default"].createElement(Input, { name: "password", type: "password", testid: "password", autoComplete: "off", required: true, outcome: outcome })),
609
+ React__default["default"].createElement("p", { style: { fontSize: '12px', color: '#888' } },
610
+ "By clicking submit you agree to the Medplum ",
611
+ React__default["default"].createElement("a", { href: "https://www.medplum.com/privacy" }, "Privacy\u00A0Policy"),
612
+ ' and ',
613
+ React__default["default"].createElement("a", { href: "https://www.medplum.com/terms" }, "Terms\u00A0of\u00A0Service"),
614
+ "."),
615
+ React__default["default"].createElement("p", { style: { fontSize: '12px', color: '#888' } },
616
+ "This site is protected by reCAPTCHA and the Google",
617
+ ' ',
618
+ React__default["default"].createElement("a", { href: "https://policies.google.com/privacy" }, "Privacy\u00A0Policy"),
619
+ ' and ',
620
+ React__default["default"].createElement("a", { href: "https://policies.google.com/terms" }, "Terms\u00A0of\u00A0Service"),
621
+ " apply."),
622
+ React__default["default"].createElement("div", { className: "medplum-signin-buttons" },
623
+ React__default["default"].createElement("div", null,
624
+ React__default["default"].createElement("input", { type: "checkbox", id: "remember", name: "remember", value: "true" }),
625
+ React__default["default"].createElement("label", { htmlFor: "remember" }, "Remember me")),
626
+ React__default["default"].createElement("div", null,
627
+ React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Create account")))));
628
+ }
629
+
630
+ function RegisterForm(props) {
631
+ const { type, projectId, googleClientId, recaptchaSiteKey, onSuccess } = props;
632
+ const medplum = useMedplum();
633
+ const [login, setLogin] = React.useState(undefined);
634
+ const [outcome, setOutcome] = React.useState();
635
+ React.useEffect(() => {
636
+ if (type === 'patient' && login) {
637
+ medplum
638
+ .startNewPatient({ login, projectId: projectId })
639
+ .then((response) => medplum.processCode(response.code))
640
+ .then(() => onSuccess())
641
+ .catch((err) => setOutcome(err));
642
+ }
643
+ }, [medplum, type, projectId, login, onSuccess]);
644
+ function handleAuthResponse(response) {
645
+ if (response.code) {
646
+ medplum
647
+ .processCode(response.code)
648
+ .then(() => onSuccess())
649
+ .catch(console.log);
650
+ }
651
+ else if (response.login) {
652
+ setLogin(response.login);
653
+ }
654
+ }
655
+ return (React__default["default"].createElement(Document, { width: 450 },
656
+ outcome && React__default["default"].createElement("pre", null, JSON.stringify(outcome, null, 2)),
657
+ !login && (React__default["default"].createElement(NewUserForm, { projectId: projectId, googleClientId: googleClientId, recaptchaSiteKey: recaptchaSiteKey, handleAuthResponse: handleAuthResponse }, props.children)),
658
+ login && type === 'project' && React__default["default"].createElement(NewProjectForm, { login: login, handleAuthResponse: handleAuthResponse })));
659
+ }
660
+
661
+ function MedplumLink(props) {
662
+ const navigate = reactRouterDom.useNavigate();
663
+ let href = getHref(props.to);
664
+ if (props.suffix) {
665
+ href += '/' + props.suffix;
666
+ }
667
+ return (React__default["default"].createElement("a", { href: href, id: props.id, "aria-label": props.label, "data-testid": props.testid || 'link', className: props.className, onClick: (e) => {
668
+ killEvent(e);
669
+ if (props.onClick) {
670
+ props.onClick();
671
+ }
672
+ else if (props.to) {
673
+ navigate(href);
674
+ }
675
+ } }, props.children));
676
+ }
677
+ function getHref(to) {
678
+ if (to) {
679
+ if (typeof to === 'string') {
680
+ return getStringHref(to);
681
+ }
682
+ else if ('resourceType' in to) {
683
+ return getResourceHref(to);
684
+ }
685
+ else if ('reference' in to) {
686
+ return getReferenceHref(to);
687
+ }
688
+ }
689
+ return '#';
690
+ }
691
+ function getStringHref(to) {
692
+ if (to.startsWith('http://') || to.startsWith('https://') || to.startsWith('/')) {
693
+ return to;
694
+ }
695
+ return '/' + to;
696
+ }
697
+ function getResourceHref(to) {
698
+ return `/${to.resourceType}/${to.id}`;
699
+ }
700
+ function getReferenceHref(to) {
701
+ return `/${to.reference}`;
702
+ }
703
+
704
+ function AuthenticationForm(props) {
705
+ const medplum = useMedplum();
706
+ const googleClientId = getGoogleClientId(props.googleClientId);
707
+ const [outcome, setOutcome] = React.useState();
708
+ const issues = getIssuesForExpression(outcome, undefined);
709
+ return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => {
710
+ medplum
711
+ .startLogin({
712
+ projectId: props.projectId,
713
+ clientId: props.clientId,
714
+ scope: props.scope,
715
+ nonce: props.nonce,
716
+ email: formData.email,
717
+ password: formData.password,
718
+ remember: formData.remember === 'true',
719
+ })
720
+ .then(props.handleAuthResponse)
721
+ .catch(setOutcome);
722
+ } },
723
+ React__default["default"].createElement("div", { className: "medplum-center" }, props.children),
724
+ issues && (React__default["default"].createElement("div", { className: "medplum-input-error" }, issues.map((issue) => {
725
+ var _a, _b;
726
+ return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
727
+ }))),
728
+ googleClientId && (React__default["default"].createElement(React__default["default"].Fragment, null,
729
+ React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
730
+ React__default["default"].createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: (response) => {
731
+ medplum
732
+ .startGoogleLogin({
733
+ projectId: props.projectId,
734
+ clientId: props.clientId,
735
+ scope: props.scope,
736
+ nonce: props.nonce,
737
+ googleClientId: response.clientId,
738
+ googleCredential: response.credential,
739
+ })
740
+ .then(props.handleAuthResponse)
741
+ .catch(setOutcome);
742
+ } })),
743
+ React__default["default"].createElement("div", { className: "medplum-signin-separator" }, "or"))),
744
+ React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email", outcome: outcome },
745
+ React__default["default"].createElement(Input, { name: "email", type: "email", testid: "email", required: true, autoFocus: true, outcome: outcome })),
746
+ React__default["default"].createElement(FormSection, { title: "Password", htmlFor: "password", outcome: outcome },
747
+ React__default["default"].createElement(Input, { name: "password", type: "password", testid: "password", autoComplete: "off", required: true, outcome: outcome })),
748
+ React__default["default"].createElement("div", { className: "medplum-signin-buttons" },
749
+ (props.onForgotPassword || props.onRegister) && (React__default["default"].createElement("div", null,
750
+ props.onForgotPassword && (React__default["default"].createElement(MedplumLink, { testid: "forgotpassword", onClick: props.onForgotPassword }, "Forgot password")),
751
+ props.onRegister && (React__default["default"].createElement(MedplumLink, { testid: "register", onClick: props.onRegister }, "Register")))),
752
+ React__default["default"].createElement("div", null,
753
+ React__default["default"].createElement("input", { type: "checkbox", id: "remember", name: "remember", value: "true" }),
754
+ React__default["default"].createElement("label", { htmlFor: "remember" }, "Remember me")),
755
+ React__default["default"].createElement("div", null,
756
+ React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Sign in")))));
757
+ }
758
+
759
+ const system = {
760
+ resourceType: 'Device',
761
+ id: 'system',
762
+ deviceName: [
763
+ {
764
+ name: 'System',
765
+ },
766
+ ],
767
+ };
768
+ /**
769
+ * React Hook to use a FHIR reference.
770
+ * Handles the complexity of resolving references and caching resources.
771
+ * @param value The resource or reference to resource.
772
+ * @returns The resolved resource.
773
+ */
774
+ function useResource(value) {
775
+ const medplum = useMedplum();
776
+ const [resource, setResource] = React.useState(getInitialResource(medplum, value));
777
+ React.useEffect(() => {
778
+ let subscribed = true;
779
+ if (!resource && value && 'reference' in value && value.reference) {
780
+ medplum
781
+ .readReference(value)
782
+ .then((r) => {
783
+ if (subscribed) {
784
+ setResource(r);
785
+ }
786
+ })
787
+ .catch(() => setResource(undefined));
788
+ }
789
+ return (() => (subscribed = false));
790
+ }, [medplum, resource, value]);
791
+ return resource;
792
+ }
793
+ /**
794
+ * Returns the initial resource value based on the input value.
795
+ * If the input value is a resource, returns the resource.
796
+ * If the input value is a reference to system, returns the system resource.
797
+ * If the input value is a reference to a resource available in the cache, returns the resource.
798
+ * Otherwise, returns undefined.
799
+ * @param medplum The medplum client.
800
+ * @param value The resource or reference to resource.
801
+ * @returns An initial resource if available; undefined otherwise.
802
+ */
803
+ function getInitialResource(medplum, value) {
804
+ if (!value) {
805
+ return undefined;
806
+ }
807
+ if ('resourceType' in value) {
808
+ return value;
809
+ }
810
+ if ('reference' in value) {
811
+ if (value.reference === 'system') {
812
+ return system;
813
+ }
814
+ return medplum.getCachedReference(value);
815
+ }
816
+ return undefined;
817
+ }
818
+
819
+ function Avatar(props) {
820
+ var _a, _b;
821
+ const resource = useResource(props.value);
822
+ const className = props.size ? 'medplum-avatar ' + props.size : 'medplum-avatar';
823
+ const text = resource ? core.getDisplayString(resource) : (_a = props.alt) !== null && _a !== void 0 ? _a : '';
824
+ const initials = text && getInitials(text);
825
+ const imageUrl = (_b = (resource && core.getImageSrc(resource))) !== null && _b !== void 0 ? _b : props.src;
826
+ const innerContent = imageUrl ? React__default["default"].createElement("img", { src: imageUrl, alt: text }) : initials;
827
+ return (React__default["default"].createElement("div", { className: className, style: { backgroundColor: props.color }, "data-testid": "avatar" }, props.link && resource ? React__default["default"].createElement(MedplumLink, { to: resource }, innerContent) : innerContent));
828
+ }
829
+ function getInitials(text) {
830
+ return text
831
+ .split(' ')
832
+ .map((n) => n[0])
833
+ .join('');
834
+ }
835
+
836
+ function ChooseProfileForm(props) {
837
+ const medplum = useMedplum();
838
+ return (React__default["default"].createElement("div", null,
839
+ React__default["default"].createElement("div", { className: "medplum-center" },
840
+ React__default["default"].createElement(Logo, { size: 32 }),
841
+ React__default["default"].createElement("h1", null, "Choose profile")),
842
+ props.memberships.map((membership) => {
843
+ var _a, _b, _c;
844
+ return (React__default["default"].createElement("div", { className: "medplum-nav-menu-profile", key: membership.id, onClick: () => {
845
+ medplum
846
+ .post('auth/profile', {
847
+ login: props.login,
848
+ profile: membership.id,
849
+ })
850
+ .then(props.handleAuthResponse)
851
+ .catch(console.log);
852
+ } },
853
+ React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-icon" },
854
+ React__default["default"].createElement(Avatar, { alt: (_a = membership.profile) === null || _a === void 0 ? void 0 : _a.display })),
855
+ React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-label" }, (_b = membership.profile) === null || _b === void 0 ? void 0 :
856
+ _b.display,
857
+ React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-help-text" }, (_c = membership.project) === null || _c === void 0 ? void 0 : _c.display))));
858
+ })));
859
+ }
860
+
861
+ function SignInForm(props) {
862
+ const medplum = useMedplum();
863
+ const [login, setLogin] = React.useState(undefined);
864
+ const [memberships, setMemberships] = React.useState(undefined);
865
+ function handleAuthResponse(response) {
866
+ if (response.login) {
867
+ setLogin(response.login);
868
+ }
869
+ if (response.memberships) {
870
+ setMemberships(response.memberships);
871
+ }
872
+ if (response.code) {
873
+ if (props.onCode) {
874
+ props.onCode(response.code);
875
+ }
876
+ else {
877
+ medplum
878
+ .processCode(response.code)
879
+ .then(() => {
880
+ if (props.onSuccess) {
881
+ props.onSuccess();
882
+ }
883
+ })
884
+ .catch(console.log);
885
+ }
886
+ }
887
+ }
888
+ return (React__default["default"].createElement(Document, { width: 450 }, (() => {
889
+ 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));
891
+ }
892
+ else if (memberships) {
893
+ return React__default["default"].createElement(ChooseProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
894
+ }
895
+ else if (props.projectId === 'new') {
896
+ return React__default["default"].createElement(NewProjectForm, { login: login, handleAuthResponse: handleAuthResponse });
897
+ }
898
+ else {
899
+ return React__default["default"].createElement("div", null, "Success");
900
+ }
901
+ })()));
902
+ }
903
+
332
904
  function Autocomplete(props) {
333
905
  var _a, _b;
334
906
  const inputRef = React.useRef(null);
@@ -392,6 +964,7 @@
392
964
  }
393
965
  }
394
966
  function handleBlur() {
967
+ tryAddOption();
395
968
  setFocused(false);
396
969
  dismissOnDelay();
397
970
  }
@@ -604,111 +1177,6 @@
604
1177
  React__default["default"].createElement("div", { className: "medplum-autocomplete-label" }, "Create new...")))))));
605
1178
  }
606
1179
 
607
- function MedplumLink(props) {
608
- const navigate = reactRouterDom.useNavigate();
609
- let href = '#';
610
- if (props.to) {
611
- if (typeof props.to === 'string') {
612
- href = props.to;
613
- }
614
- else if ('resourceType' in props.to) {
615
- href = `/${props.to.resourceType}/${props.to.id}`;
616
- }
617
- else if ('reference' in props.to) {
618
- href = `/${props.to.reference}`;
619
- }
620
- if (props.suffix) {
621
- href += '/' + props.suffix;
622
- }
623
- }
624
- return (React__default["default"].createElement("a", { href: href, id: props.id, "aria-label": props.label, "data-testid": props.testid || 'link', className: props.className, onClick: (e) => {
625
- killEvent(e);
626
- if (props.onClick) {
627
- props.onClick();
628
- }
629
- else if (props.to) {
630
- navigate(href);
631
- }
632
- } }, props.children));
633
- }
634
-
635
- const system = {
636
- resourceType: 'Device',
637
- id: 'system',
638
- deviceName: [
639
- {
640
- name: 'System',
641
- },
642
- ],
643
- };
644
- /**
645
- * React Hook to use a FHIR reference.
646
- * Handles the complexity of resolving references and caching resources.
647
- * @param value The resource or reference to resource.
648
- * @returns The resolved resource.
649
- */
650
- function useResource(value) {
651
- const medplum = useMedplum();
652
- const [resource, setResource] = React.useState(getInitialResource(medplum, value));
653
- React.useEffect(() => {
654
- let subscribed = true;
655
- if (!resource && value && 'reference' in value && value.reference) {
656
- medplum
657
- .readReference(value)
658
- .then((r) => {
659
- if (subscribed) {
660
- setResource(r);
661
- }
662
- })
663
- .catch(() => setResource(undefined));
664
- }
665
- return (() => (subscribed = false));
666
- }, [medplum, resource, value]);
667
- return resource;
668
- }
669
- /**
670
- * Returns the initial resource value based on the input value.
671
- * If the input value is a resource, returns the resource.
672
- * If the input value is a reference to system, returns the system resource.
673
- * If the input value is a reference to a resource available in the cache, returns the resource.
674
- * Otherwise, returns undefined.
675
- * @param medplum The medplum client.
676
- * @param value The resource or reference to resource.
677
- * @returns An initial resource if available; undefined otherwise.
678
- */
679
- function getInitialResource(medplum, value) {
680
- if (!value) {
681
- return undefined;
682
- }
683
- if ('resourceType' in value) {
684
- return value;
685
- }
686
- if ('reference' in value) {
687
- if (value.reference === 'system') {
688
- return system;
689
- }
690
- return medplum.getCachedReference(value);
691
- }
692
- return undefined;
693
- }
694
-
695
- function Avatar(props) {
696
- var _a, _b;
697
- const resource = useResource(props.value);
698
- const className = props.size ? 'medplum-avatar ' + props.size : 'medplum-avatar';
699
- const text = resource ? core.getDisplayString(resource) : (_a = props.alt) !== null && _a !== void 0 ? _a : '';
700
- const initials = text && getInitials(text);
701
- const imageUrl = (_b = (resource && core.getImageSrc(resource))) !== null && _b !== void 0 ? _b : props.src;
702
- const innerContent = imageUrl ? React__default["default"].createElement("img", { src: imageUrl, alt: text }) : initials;
703
- return (React__default["default"].createElement("div", { className: className, style: { backgroundColor: props.color }, "data-testid": "avatar" }, props.link && resource ? React__default["default"].createElement(MedplumLink, { to: resource }, innerContent) : innerContent));
704
- }
705
- function getInitials(text) {
706
- return text
707
- .split(' ')
708
- .map((n) => n[0])
709
- .join('');
710
- }
711
-
712
1180
  function CheckboxFormSection(props) {
713
1181
  return (React__default["default"].createElement("div", { className: "medplum-checkbox-form-section" },
714
1182
  React__default["default"].createElement("div", { className: "medplum-checkbox-form-section-checkbox-container" }, props.children),
@@ -727,19 +1195,6 @@
727
1195
  'modifierExtension',
728
1196
  ];
729
1197
 
730
- function FormSection(props) {
731
- const issues = getIssuesForExpression(props.outcome, props.htmlFor);
732
- const invalid = issues && issues.length > 0;
733
- return (React__default["default"].createElement("fieldset", { className: "medplum-form-section" },
734
- props.title && React__default["default"].createElement("label", { htmlFor: props.htmlFor }, props.title),
735
- props.description && React__default["default"].createElement("p", null, props.description),
736
- props.children,
737
- invalid && (React__default["default"].createElement("div", { id: props.htmlFor + '-errors', className: "medplum-input-error" }, issues === null || issues === void 0 ? void 0 : issues.map((issue) => {
738
- var _a, _b;
739
- return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
740
- })))));
741
- }
742
-
743
1198
  function ResourceForm(props) {
744
1199
  const medplum = useMedplum();
745
1200
  const defaultValue = useResource(props.defaultValue);
@@ -1106,7 +1561,7 @@
1106
1561
  return medplum.searchValueSet(system, input).then((valueSet) => {
1107
1562
  return valueSet.expansion.contains.map(valueSetElementToCodeableConcept);
1108
1563
  });
1109
- }, buildUnstructured: buildUnstructured, getId: getId, getDisplay: getDisplay, name: props.name, defaultValue: defaultValue, onChange: (values) => {
1564
+ }, buildUnstructured: buildUnstructured, getId: getId, getDisplay: getDisplay, name: props.name, defaultValue: defaultValue, loadOnFocus: true, onChange: (values) => {
1110
1565
  if (props.onChange) {
1111
1566
  props.onChange(values[0]);
1112
1567
  }
@@ -1154,7 +1609,7 @@
1154
1609
  contains.forEach((e) => (cachedDisplayValues[e.code] = e.display));
1155
1610
  return contains.map((e) => e.code);
1156
1611
  });
1157
- }, buildUnstructured: (str) => str, getId: (item) => item, getDisplay: (item) => React__default["default"].createElement(React__default["default"].Fragment, null, cachedDisplayValues[item] || item), name: props.name, defaultValue: defaultValue, onChange: (values) => {
1612
+ }, buildUnstructured: (str) => str, getId: (item) => item, getDisplay: (item) => React__default["default"].createElement(React__default["default"].Fragment, null, cachedDisplayValues[item] || item), name: props.name, defaultValue: defaultValue, loadOnFocus: true, onChange: (values) => {
1158
1613
  if (props.onChange) {
1159
1614
  props.onChange(values[0]);
1160
1615
  }
@@ -1177,7 +1632,7 @@
1177
1632
  display: e.display,
1178
1633
  }));
1179
1634
  });
1180
- }, buildUnstructured: (str) => ({ code: str }), getId: (item) => item.code, getDisplay: (item) => React__default["default"].createElement(CodingDisplay, { value: item }), name: props.name, defaultValue: defaultValue, onChange: (values) => {
1635
+ }, buildUnstructured: (str) => ({ code: str }), getId: (item) => item.code, getDisplay: (item) => React__default["default"].createElement(CodingDisplay, { value: item }), name: props.name, defaultValue: defaultValue, loadOnFocus: true, onChange: (values) => {
1181
1636
  if (props.onChange) {
1182
1637
  props.onChange(values[0]);
1183
1638
  }
@@ -1472,31 +1927,6 @@
1472
1927
  React__default["default"].createElement(QuantityInput, { name: props.name + '-denominator', defaultValue: value === null || value === void 0 ? void 0 : value.denominator, onChange: (v) => setValueWrapper(Object.assign(Object.assign({}, value), { denominator: v })) })));
1473
1928
  }
1474
1929
 
1475
- /******************************************************************************
1476
- Copyright (c) Microsoft Corporation.
1477
-
1478
- Permission to use, copy, modify, and/or distribute this software for any
1479
- purpose with or without fee is hereby granted.
1480
-
1481
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1482
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1483
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1484
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1485
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1486
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1487
- PERFORMANCE OF THIS SOFTWARE.
1488
- ***************************************************************************** */
1489
-
1490
- function __awaiter(thisArg, _arguments, P, generator) {
1491
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
1492
- return new (P || (P = Promise))(function (resolve, reject) {
1493
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
1494
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
1495
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
1496
- step((generator = generator.apply(thisArg, _arguments || [])).next());
1497
- });
1498
- }
1499
-
1500
1930
  function ResourceName(props) {
1501
1931
  const resource = useResource(props.value);
1502
1932
  if (!resource) {
@@ -1968,77 +2398,20 @@
1968
2398
  }
1969
2399
  if (obs === null || obs === void 0 ? void 0 : obs.valueString) {
1970
2400
  return React__default["default"].createElement(React__default["default"].Fragment, null, obs.valueString);
1971
- }
1972
- if (obs && 'component' in obs && (obs === null || obs === void 0 ? void 0 : obs.component)) {
1973
- return (React__default["default"].createElement(React__default["default"].Fragment, null, obs.component
1974
- .map((component, index) => (React__default["default"].createElement(ObservationValueDisplay, { key: `obs-${index}`, value: component })))
1975
- .reduce((prev, curr) => [prev, ' / ', curr])));
1976
- }
1977
- return null;
1978
- }
1979
- function ReferenceRangeDisplay(props) {
1980
- const range = props.value && props.value.length > 0 && props.value[0];
1981
- if (!range) {
1982
- return null;
1983
- }
1984
- return React__default["default"].createElement(RangeDisplay, { value: range });
1985
- }
1986
-
1987
- /**
1988
- * Parses an HTML form and returns the result as a JavaScript object.
1989
- * @param form The HTML form element.
1990
- */
1991
- function parseForm(form) {
1992
- const result = {};
1993
- for (const element of Array.from(form.elements)) {
1994
- if (element instanceof HTMLInputElement) {
1995
- parseInputElement(result, element);
1996
- }
1997
- else if (element instanceof HTMLTextAreaElement) {
1998
- result[element.name] = element.value;
1999
- }
2000
- else if (element instanceof HTMLSelectElement) {
2001
- parseSelectElement(result, element);
2002
- }
2003
- }
2004
- return result;
2005
- }
2006
- /**
2007
- * Parses an HTML input element.
2008
- * Sets the name/value pair in the result,
2009
- * but only if the element is enabled and checked.
2010
- * @param el The input element.
2011
- * @param result The result builder.
2012
- */
2013
- function parseInputElement(result, el) {
2014
- if (el.disabled) {
2015
- // Ignore disabled elements
2016
- return;
2017
- }
2018
- if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {
2019
- // Ignore unchecked radio or checkbox elements
2020
- return;
2021
- }
2022
- result[el.name] = el.value;
2023
- }
2024
- /**
2025
- * Parses an HTML select element.
2026
- * Sets the name/value pair if one is selected.
2027
- * @param result The result builder.
2028
- * @param el The select element.
2029
- */
2030
- function parseSelectElement(result, el) {
2031
- result[el.name] = el.value;
2032
- }
2033
-
2034
- function Form(props) {
2035
- return (React__default["default"].createElement("form", { style: props.style, "data-testid": props.testid, onSubmit: (e) => {
2036
- e.preventDefault();
2037
- const formData = parseForm(e.target);
2038
- if (props.onSubmit) {
2039
- props.onSubmit(formData);
2040
- }
2041
- } }, props.children));
2401
+ }
2402
+ if (obs && 'component' in obs && (obs === null || obs === void 0 ? void 0 : obs.component)) {
2403
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, obs.component
2404
+ .map((component, index) => (React__default["default"].createElement(ObservationValueDisplay, { key: `obs-${index}`, value: component })))
2405
+ .reduce((prev, curr) => [prev, ' / ', curr])));
2406
+ }
2407
+ return null;
2408
+ }
2409
+ function ReferenceRangeDisplay(props) {
2410
+ const range = props.value && props.value.length > 0 && props.value[0];
2411
+ if (!range) {
2412
+ return null;
2413
+ }
2414
+ return React__default["default"].createElement(RangeDisplay, { value: range });
2042
2415
  }
2043
2416
 
2044
2417
  function Loading() {
@@ -2330,7 +2703,9 @@
2330
2703
  }
2331
2704
  if (bundle.entry) {
2332
2705
  for (const entry of bundle.entry) {
2333
- newItems.push(entry.resource);
2706
+ if (entry.resource) {
2707
+ newItems.push(entry.resource);
2708
+ }
2334
2709
  }
2335
2710
  }
2336
2711
  }
@@ -2517,11 +2892,6 @@
2517
2892
  }) }));
2518
2893
  }
2519
2894
 
2520
- function Document(props) {
2521
- return (React__default["default"].createElement("main", { className: "medplum-document" },
2522
- React__default["default"].createElement("article", { style: { maxWidth: props.width } }, props.children)));
2523
- }
2524
-
2525
2895
  function EncounterTimeline(props) {
2526
2896
  return (React__default["default"].createElement(ResourceTimeline, { value: props.encounter, buildSearchRequests: (resource) => ({
2527
2897
  resourceType: 'Bundle',
@@ -4300,16 +4670,6 @@
4300
4670
  React__default["default"].createElement(MedplumLink, { to: "/changepassword" }, "Change password"))))));
4301
4671
  }
4302
4672
 
4303
- function Logo(props) {
4304
- return (React__default["default"].createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 491 491", style: { width: props.size, height: props.size } },
4305
- React__default["default"].createElement("title", null, "Medplum Logo"),
4306
- React__default["default"].createElement("path", { fill: props.fill || '#ad7136', d: "M282 67c6-16 16-29 29-40L289 0c-22 17-37 41-43 68l17 23 19-24z" }),
4307
- React__default["default"].createElement("path", { fill: props.fill || '#654b87', d: "M311 63c-17 0-33 4-48 11-16-7-32-11-49-11-87 0-158 96-158 214s71 214 158 214c17 0 33-4 49-11 15 7 31 11 48 11 87 0 158-96 158-214S398 63 311 63z" }),
4308
- React__default["default"].createElement("path", { fill: props.fill || '#463068', d: "M231 489l-17 2c-87 0-158-96-158-214S127 63 214 63l17 1c-39 12-70 102-70 213s31 201 70 212z" }),
4309
- React__default["default"].createElement("path", { fill: props.fill || '#70d65b', d: "M207 220a176 176 0 01-177 43A176 176 0 01251 43l1 5c17 59 2 125-45 172z" }),
4310
- React__default["default"].createElement("path", { fill: props.fill || '#58b741', d: "M252 48A421 421 0 0057 270l-27-7A176 176 0 01251 43l1 5z" })));
4311
- }
4312
-
4313
4673
  const searches = [
4314
4674
  '$/_history',
4315
4675
  'Communication?subject=$',
@@ -5048,195 +5408,6 @@
5048
5408
  return options.map((option) => (Object.assign(Object.assign({}, option), { id: option.id || generateId() })));
5049
5409
  }
5050
5410
 
5051
- /**
5052
- * Dynamically creates a script tag for the specified JavaScript file.
5053
- * @param src The JavaScript file URL.
5054
- */
5055
- function createScriptTag(src, onload) {
5056
- const head = document.getElementsByTagName('head')[0];
5057
- const script = document.createElement('script');
5058
- script.async = true;
5059
- script.src = src;
5060
- script.onload = onload || null;
5061
- head.appendChild(script);
5062
- }
5063
-
5064
- function GoogleButton(props) {
5065
- const medplum = useMedplum();
5066
- const { googleClientId, handleGoogleCredential } = props;
5067
- const parentRef = React.useRef(null);
5068
- const [scriptLoaded, setScriptLoaded] = React.useState(typeof google !== 'undefined');
5069
- const [initialized, setInitialized] = React.useState(false);
5070
- const [buttonRendered, setButtonRendered] = React.useState(false);
5071
- React.useEffect(() => {
5072
- if (typeof google === 'undefined') {
5073
- createScriptTag('https://accounts.google.com/gsi/client', () => setScriptLoaded(true));
5074
- return;
5075
- }
5076
- if (!initialized) {
5077
- google.accounts.id.initialize({
5078
- client_id: googleClientId,
5079
- callback: handleGoogleCredential,
5080
- });
5081
- setInitialized(true);
5082
- }
5083
- if (parentRef.current && !buttonRendered) {
5084
- google.accounts.id.renderButton(parentRef.current, {});
5085
- setButtonRendered(true);
5086
- }
5087
- }, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered, handleGoogleCredential]);
5088
- if (!googleClientId) {
5089
- return null;
5090
- }
5091
- return React__default["default"].createElement("div", { ref: parentRef });
5092
- }
5093
- function getGoogleClientId(clientId) {
5094
- var _a, _b;
5095
- if (clientId) {
5096
- return clientId;
5097
- }
5098
- const origin = window.location.protocol + '//' + window.location.host;
5099
- 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") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
5100
- if (authorizedOrigins.includes(origin)) {
5101
- return "921088377005-3j1sa10vr6hj86jgmdfh2l53v3mp7lfi.apps.googleusercontent.com";
5102
- }
5103
- return undefined;
5104
- }
5105
-
5106
- /**
5107
- * Dynamically loads the recaptcha script.
5108
- * We do not want to load the script on page load unless the user needs it.
5109
- * @param siteKey The reCAPTCHA site key, available from the reCAPTCHA admin page.
5110
- */
5111
- function initRecaptcha(siteKey) {
5112
- if (typeof grecaptcha === 'undefined') {
5113
- createScriptTag('https://www.google.com/recaptcha/api.js?render=' + siteKey);
5114
- }
5115
- }
5116
- /**
5117
- * Starts a request to generate a recapcha token.
5118
- * @param siteKey The reCAPTCHA site key, available from the reCAPTCHA admin page.
5119
- * @returns Promise to a recaptcha token for the current user.
5120
- */
5121
- function getRecaptcha(siteKey) {
5122
- return new Promise((resolve, reject) => {
5123
- grecaptcha.ready(() => __awaiter(this, void 0, void 0, function* () {
5124
- try {
5125
- resolve(yield grecaptcha.execute(siteKey, { action: 'submit' }));
5126
- }
5127
- catch (err) {
5128
- reject(err);
5129
- }
5130
- }));
5131
- });
5132
- }
5133
-
5134
- function RegisterForm(props) {
5135
- const medplum = useMedplum();
5136
- const googleClientId = getGoogleClientId(props.googleClientId);
5137
- const recaptchaSiteKey = props.recaptchaSiteKey;
5138
- const [outcome, setOutcome] = React.useState();
5139
- const issues = getIssuesForExpression(outcome, undefined);
5140
- React.useEffect(() => initRecaptcha(recaptchaSiteKey), [recaptchaSiteKey]);
5141
- function handleAuthResponse(registerRequest, partialLogin) {
5142
- return __awaiter(this, void 0, void 0, function* () {
5143
- try {
5144
- let login;
5145
- if (props.type === 'patient') {
5146
- login = yield medplum.startNewPatient(registerRequest, partialLogin);
5147
- }
5148
- else {
5149
- login = yield medplum.startNewProject(registerRequest, partialLogin);
5150
- }
5151
- yield medplum.processCode(login.code);
5152
- props.onSuccess();
5153
- }
5154
- catch (err) {
5155
- setOutcome(err);
5156
- }
5157
- });
5158
- }
5159
- return (React__default["default"].createElement(Document, { width: 450 },
5160
- React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => __awaiter(this, void 0, void 0, function* () {
5161
- try {
5162
- const recaptchaToken = yield getRecaptcha(recaptchaSiteKey);
5163
- const registerRequest = {
5164
- projectId: props.projectId,
5165
- projectName: formData.projectName,
5166
- firstName: formData.firstName,
5167
- lastName: formData.lastName,
5168
- email: formData.email,
5169
- password: formData.password,
5170
- remember: formData.remember === 'true',
5171
- recaptchaSiteKey,
5172
- recaptchaToken,
5173
- };
5174
- const userLogin = yield medplum.startNewUser(registerRequest);
5175
- yield handleAuthResponse(registerRequest, userLogin);
5176
- }
5177
- catch (err) {
5178
- setOutcome(err);
5179
- }
5180
- }) },
5181
- React__default["default"].createElement("div", { className: "medplum-center" }, props.children),
5182
- issues && (React__default["default"].createElement("div", { className: "medplum-input-error" }, issues.map((issue) => {
5183
- var _a, _b;
5184
- return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
5185
- }))),
5186
- googleClientId && (React__default["default"].createElement(React__default["default"].Fragment, null,
5187
- React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
5188
- React__default["default"].createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: (response) => __awaiter(this, void 0, void 0, function* () {
5189
- try {
5190
- const loginRequest = {
5191
- googleClientId: response.clientId,
5192
- googleCredential: response.credential,
5193
- };
5194
- const userLogin = yield medplum.startGoogleLogin(loginRequest);
5195
- const googleClaims = core.parseJWTPayload(loginRequest.googleCredential);
5196
- const registerRequest = {
5197
- projectId: props.projectId,
5198
- firstName: googleClaims.given_name,
5199
- lastName: googleClaims.family_name,
5200
- email: googleClaims.email,
5201
- };
5202
- yield handleAuthResponse(registerRequest, userLogin);
5203
- }
5204
- catch (err) {
5205
- setOutcome(err);
5206
- }
5207
- }) })),
5208
- React__default["default"].createElement("div", { className: "medplum-signin-separator" }, "or"))),
5209
- React__default["default"].createElement(FormSection, { title: "First Name", htmlFor: "firstName", outcome: outcome },
5210
- React__default["default"].createElement(Input, { name: "firstName", type: "text", testid: "firstName", placeholder: "First name", required: true, autoFocus: true, outcome: outcome })),
5211
- React__default["default"].createElement(FormSection, { title: "Last Name", htmlFor: "lastName", outcome: outcome },
5212
- React__default["default"].createElement(Input, { name: "lastName", type: "text", testid: "lastName", placeholder: "Last name", required: true, outcome: outcome })),
5213
- props.type === 'project' && (React__default["default"].createElement(FormSection, { title: "Project Name", htmlFor: "projectName", outcome: outcome },
5214
- React__default["default"].createElement(Input, { name: "projectName", type: "text", testid: "projectName", placeholder: "My Project", required: true, outcome: outcome }))),
5215
- React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email", outcome: outcome },
5216
- React__default["default"].createElement(Input, { name: "email", type: "email", testid: "email", placeholder: "name@domain.com", required: true, outcome: outcome })),
5217
- React__default["default"].createElement(FormSection, { title: "Password", htmlFor: "password", outcome: outcome },
5218
- React__default["default"].createElement(Input, { name: "password", type: "password", testid: "password", autoComplete: "off", required: true, outcome: outcome })),
5219
- React__default["default"].createElement("p", { style: { fontSize: '12px', color: '#888' } },
5220
- "By clicking submit you agree to the Medplum ",
5221
- React__default["default"].createElement("a", { href: "https://www.medplum.com/privacy" }, "Privacy\u00A0Policy"),
5222
- ' and ',
5223
- React__default["default"].createElement("a", { href: "https://www.medplum.com/terms" }, "Terms\u00A0of\u00A0Service"),
5224
- "."),
5225
- React__default["default"].createElement("p", { style: { fontSize: '12px', color: '#888' } },
5226
- "This site is protected by reCAPTCHA and the Google",
5227
- ' ',
5228
- React__default["default"].createElement("a", { href: "https://policies.google.com/privacy" }, "Privacy\u00A0Policy"),
5229
- ' and ',
5230
- React__default["default"].createElement("a", { href: "https://policies.google.com/terms" }, "Terms\u00A0of\u00A0Service"),
5231
- " apply."),
5232
- React__default["default"].createElement("div", { className: "medplum-signin-buttons" },
5233
- React__default["default"].createElement("div", null,
5234
- React__default["default"].createElement("input", { type: "checkbox", id: "remember", name: "remember", value: "true" }),
5235
- React__default["default"].createElement("label", { htmlFor: "remember" }, "Remember me")),
5236
- React__default["default"].createElement("div", null,
5237
- React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Create account"))))));
5238
- }
5239
-
5240
5411
  function StatusBadge(props) {
5241
5412
  return React__default["default"].createElement("span", { className: `medplum-status medplum-status-${props.status}` }, props.status);
5242
5413
  }
@@ -5587,19 +5758,23 @@
5587
5758
  React__default["default"].createElement("th", null, "Author"),
5588
5759
  React__default["default"].createElement("th", null, "Date"),
5589
5760
  React__default["default"].createElement("th", null, "Version"))),
5590
- React__default["default"].createElement("tbody", null, (_a = value.entry) === null || _a === void 0 ? void 0 : _a.map((entry) => {
5591
- var _a, _b;
5592
- return (React__default["default"].createElement(HistoryRow, { key: (_b = (_a = entry.resource) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.versionId, version: entry.resource }));
5593
- }))));
5761
+ React__default["default"].createElement("tbody", null, (_a = value.entry) === null || _a === void 0 ? void 0 : _a.map((entry, index) => (React__default["default"].createElement(HistoryRow, { key: 'entry-' + index, entry: entry }))))));
5594
5762
  }
5595
5763
  function HistoryRow(props) {
5596
5764
  var _a, _b, _c;
5597
- return (React__default["default"].createElement("tr", null,
5598
- React__default["default"].createElement("td", null,
5599
- React__default["default"].createElement(ResourceBadge, { value: (_a = props.version.meta) === null || _a === void 0 ? void 0 : _a.author, link: true })),
5600
- React__default["default"].createElement("td", null, core.formatDateTime((_b = props.version.meta) === null || _b === void 0 ? void 0 : _b.lastUpdated)),
5601
- React__default["default"].createElement("td", null,
5602
- React__default["default"].createElement(MedplumLink, { to: getVersionUrl(props.version) }, (_c = props.version.meta) === null || _c === void 0 ? void 0 : _c.versionId))));
5765
+ const { response, resource } = props.entry;
5766
+ if (resource) {
5767
+ return (React__default["default"].createElement("tr", null,
5768
+ React__default["default"].createElement("td", null,
5769
+ React__default["default"].createElement(ResourceBadge, { value: (_a = resource.meta) === null || _a === void 0 ? void 0 : _a.author, link: true })),
5770
+ React__default["default"].createElement("td", null, core.formatDateTime((_b = resource.meta) === null || _b === void 0 ? void 0 : _b.lastUpdated)),
5771
+ React__default["default"].createElement("td", null,
5772
+ React__default["default"].createElement(MedplumLink, { to: getVersionUrl(resource) }, (_c = resource.meta) === null || _c === void 0 ? void 0 : _c.versionId))));
5773
+ }
5774
+ else {
5775
+ return (React__default["default"].createElement("tr", null,
5776
+ React__default["default"].createElement("td", { colSpan: 3 }, core.normalizeErrorString(response === null || response === void 0 ? void 0 : response.outcome))));
5777
+ }
5603
5778
  }
5604
5779
  function getVersionUrl(resource) {
5605
5780
  var _a;
@@ -5820,122 +5995,6 @@
5820
5995
  }) }));
5821
5996
  }
5822
5997
 
5823
- function SignInForm(props) {
5824
- const medplum = useMedplum();
5825
- const [login, setLogin] = React.useState(undefined);
5826
- const [memberships, setMemberships] = React.useState(undefined);
5827
- function handleAuthResponse(response) {
5828
- if (response.login) {
5829
- setLogin(response.login);
5830
- }
5831
- if (response.memberships) {
5832
- setMemberships(response.memberships);
5833
- }
5834
- if (response.code) {
5835
- if (props.onCode) {
5836
- props.onCode(response.code);
5837
- }
5838
- else {
5839
- medplum
5840
- .processCode(response.code)
5841
- .then(() => {
5842
- if (props.onSuccess) {
5843
- props.onSuccess();
5844
- }
5845
- })
5846
- .catch(console.log);
5847
- }
5848
- }
5849
- }
5850
- return (React__default["default"].createElement(Document, { width: 450 }, (() => {
5851
- if (!login) {
5852
- 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));
5853
- }
5854
- else if (memberships) {
5855
- return React__default["default"].createElement(ProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
5856
- }
5857
- else {
5858
- return React__default["default"].createElement("div", null, "Success");
5859
- }
5860
- })()));
5861
- }
5862
- function AuthenticationForm(props) {
5863
- const medplum = useMedplum();
5864
- const googleClientId = getGoogleClientId(props.googleClientId);
5865
- const [outcome, setOutcome] = React.useState();
5866
- const issues = getIssuesForExpression(outcome, undefined);
5867
- return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => {
5868
- medplum
5869
- .startLogin({
5870
- clientId: props.clientId,
5871
- scope: props.scope,
5872
- nonce: props.nonce,
5873
- email: formData.email,
5874
- password: formData.password,
5875
- remember: formData.remember === 'true',
5876
- })
5877
- .then(props.handleAuthResponse)
5878
- .catch(setOutcome);
5879
- } },
5880
- React__default["default"].createElement("div", { className: "medplum-center" }, props.children),
5881
- issues && (React__default["default"].createElement("div", { className: "medplum-input-error" }, issues.map((issue) => {
5882
- var _a, _b;
5883
- return (React__default["default"].createElement("div", { "data-testid": "text-field-error", key: (_a = issue.details) === null || _a === void 0 ? void 0 : _a.text }, (_b = issue.details) === null || _b === void 0 ? void 0 : _b.text));
5884
- }))),
5885
- googleClientId && (React__default["default"].createElement(React__default["default"].Fragment, null,
5886
- React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
5887
- React__default["default"].createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: (response) => {
5888
- medplum
5889
- .startGoogleLogin({
5890
- clientId: props.clientId,
5891
- scope: props.scope,
5892
- nonce: props.nonce,
5893
- googleClientId: response.clientId,
5894
- googleCredential: response.credential,
5895
- })
5896
- .then(props.handleAuthResponse)
5897
- .catch(setOutcome);
5898
- } })),
5899
- React__default["default"].createElement("div", { className: "medplum-signin-separator" }, "or"))),
5900
- React__default["default"].createElement(FormSection, { title: "Email", htmlFor: "email", outcome: outcome },
5901
- React__default["default"].createElement(Input, { name: "email", type: "email", testid: "email", required: true, autoFocus: true, outcome: outcome })),
5902
- React__default["default"].createElement(FormSection, { title: "Password", htmlFor: "password", outcome: outcome },
5903
- React__default["default"].createElement(Input, { name: "password", type: "password", testid: "password", autoComplete: "off", required: true, outcome: outcome })),
5904
- React__default["default"].createElement("div", { className: "medplum-signin-buttons" },
5905
- (props.onForgotPassword || props.onRegister) && (React__default["default"].createElement("div", null,
5906
- props.onForgotPassword && (React__default["default"].createElement(MedplumLink, { testid: "forgotpassword", onClick: props.onForgotPassword }, "Forgot password")),
5907
- props.onRegister && (React__default["default"].createElement(MedplumLink, { testid: "register", onClick: props.onRegister }, "Register")))),
5908
- React__default["default"].createElement("div", null,
5909
- React__default["default"].createElement("input", { type: "checkbox", id: "remember", name: "remember", value: "true" }),
5910
- React__default["default"].createElement("label", { htmlFor: "remember" }, "Remember me")),
5911
- React__default["default"].createElement("div", null,
5912
- React__default["default"].createElement(Button, { type: "submit", testid: "submit" }, "Sign in")))));
5913
- }
5914
- function ProfileForm(props) {
5915
- const medplum = useMedplum();
5916
- return (React__default["default"].createElement("div", null,
5917
- React__default["default"].createElement("div", { className: "medplum-center" },
5918
- React__default["default"].createElement(Logo, { size: 32 }),
5919
- React__default["default"].createElement("h1", null, "Choose profile")),
5920
- props.memberships.map((membership) => {
5921
- var _a, _b, _c;
5922
- return (React__default["default"].createElement("div", { className: "medplum-nav-menu-profile", key: membership.id, onClick: () => {
5923
- medplum
5924
- .post('auth/profile', {
5925
- login: props.login,
5926
- profile: membership.id,
5927
- })
5928
- .then(props.handleAuthResponse)
5929
- .catch(console.log);
5930
- } },
5931
- React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-icon" },
5932
- React__default["default"].createElement(Avatar, { alt: (_a = membership.profile) === null || _a === void 0 ? void 0 : _a.display })),
5933
- React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-label" }, (_b = membership.profile) === null || _b === void 0 ? void 0 :
5934
- _b.display,
5935
- React__default["default"].createElement("div", { className: "medplum-nav-menu-profile-help-text" }, (_c = membership.project) === null || _c === void 0 ? void 0 : _c.display))));
5936
- })));
5937
- }
5938
-
5939
5998
  function Tab(props) {
5940
5999
  let className = 'medplum-tab';
5941
6000
  if (props.selected) {
@@ -6002,6 +6061,8 @@
6002
6061
  exports.ContactPointInput = ContactPointInput;
6003
6062
  exports.DateTimeInput = DateTimeInput;
6004
6063
  exports.DefaultResourceTimeline = DefaultResourceTimeline;
6064
+ exports.DescriptionList = DescriptionList;
6065
+ exports.DescriptionListEntry = DescriptionListEntry;
6005
6066
  exports.DiagnosticReportDisplay = DiagnosticReportDisplay;
6006
6067
  exports.Document = Document;
6007
6068
  exports.ElementDefinitionInputSelector = ElementDefinitionInputSelector;