vintasend 0.4.0 → 0.4.7

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 (284) hide show
  1. package/README.md +5 -0
  2. package/dist/examples/vintasend-medplum-example/.env.example +11 -0
  3. package/dist/examples/vintasend-medplum-example/IMPLEMENTATION_PLAN_FILE_ATTACHMENTS.md +597 -0
  4. package/dist/examples/vintasend-medplum-example/README.md +190 -0
  5. package/dist/examples/vintasend-medplum-example/TUTORIAL_EMAIL_NOTIFICATIONS.md +2596 -0
  6. package/dist/examples/vintasend-medplum-example/bots/handlers/send-pending-notifications-bot.ts +39 -0
  7. package/dist/examples/vintasend-medplum-example/bots/handlers/send-task-assignment-email.ts +41 -0
  8. package/dist/examples/vintasend-medplum-example/bots/handlers/task-due-soon-notification-bot.ts +86 -0
  9. package/dist/examples/vintasend-medplum-example/bots/index.ts +53 -0
  10. package/dist/examples/vintasend-medplum-example/bots/services/emails/schedule-task-due-soon-email.ts +84 -0
  11. package/dist/examples/vintasend-medplum-example/bots/services/emails/send-task-assignment-email.test.ts +388 -0
  12. package/dist/examples/vintasend-medplum-example/bots/services/emails/send-task-assignment-email.ts +113 -0
  13. package/dist/examples/vintasend-medplum-example/bots/shared/task-due-soon-helpers.ts +115 -0
  14. package/dist/examples/vintasend-medplum-example/bots/task-assignment-bot.ts +41 -0
  15. package/dist/examples/vintasend-medplum-example/compiled-notification-templates.json +6 -0
  16. package/dist/examples/vintasend-medplum-example/esbuild-script.mjs +71 -0
  17. package/dist/examples/vintasend-medplum-example/index.html +14 -0
  18. package/dist/examples/vintasend-medplum-example/lib/constants.ts +32 -0
  19. package/dist/examples/vintasend-medplum-example/lib/extensions.ts +1 -0
  20. package/dist/examples/vintasend-medplum-example/lib/file-upload.test.ts +389 -0
  21. package/dist/examples/vintasend-medplum-example/lib/file-upload.ts +222 -0
  22. package/dist/examples/vintasend-medplum-example/lib/medplum-singleton.ts +18 -0
  23. package/dist/examples/vintasend-medplum-example/lib/notification-service.test.ts +293 -0
  24. package/dist/examples/vintasend-medplum-example/lib/notification-service.ts +284 -0
  25. package/dist/examples/vintasend-medplum-example/lib/patients.ts +20 -0
  26. package/dist/examples/vintasend-medplum-example/notification-templates/emails/task-assignment/body.html.pug +37 -0
  27. package/dist/examples/vintasend-medplum-example/notification-templates/emails/task-assignment/subject.txt.pug +4 -0
  28. package/dist/examples/vintasend-medplum-example/notification-templates/emails/task-due-soon/body.html.pug +34 -0
  29. package/dist/examples/vintasend-medplum-example/notification-templates/emails/task-due-soon/subject.txt.pug +4 -0
  30. package/dist/examples/vintasend-medplum-example/package.json +75 -0
  31. package/dist/examples/vintasend-medplum-example/plugins/gql-plugin.mjs +31 -0
  32. package/dist/examples/vintasend-medplum-example/postcss.config.mjs +21 -0
  33. package/dist/examples/vintasend-medplum-example/public/favicon.ico +0 -0
  34. package/dist/examples/vintasend-medplum-example/public/img/integrations/acuity.png +0 -0
  35. package/dist/examples/vintasend-medplum-example/public/img/integrations/auth0.png +0 -0
  36. package/dist/examples/vintasend-medplum-example/public/img/integrations/azure.png +0 -0
  37. package/dist/examples/vintasend-medplum-example/public/img/integrations/calcom.png +0 -0
  38. package/dist/examples/vintasend-medplum-example/public/img/integrations/candid.png +0 -0
  39. package/dist/examples/vintasend-medplum-example/public/img/integrations/claude.png +0 -0
  40. package/dist/examples/vintasend-medplum-example/public/img/integrations/datadog.png +0 -0
  41. package/dist/examples/vintasend-medplum-example/public/img/integrations/deepseek.png +0 -0
  42. package/dist/examples/vintasend-medplum-example/public/img/integrations/entra.png +0 -0
  43. package/dist/examples/vintasend-medplum-example/public/img/integrations/epic.png +0 -0
  44. package/dist/examples/vintasend-medplum-example/public/img/integrations/google.png +0 -0
  45. package/dist/examples/vintasend-medplum-example/public/img/integrations/healthgorilla.png +0 -0
  46. package/dist/examples/vintasend-medplum-example/public/img/integrations/healthie.png +0 -0
  47. package/dist/examples/vintasend-medplum-example/public/img/integrations/labcorp.png +0 -0
  48. package/dist/examples/vintasend-medplum-example/public/img/integrations/okta.png +0 -0
  49. package/dist/examples/vintasend-medplum-example/public/img/integrations/openai.png +0 -0
  50. package/dist/examples/vintasend-medplum-example/public/img/integrations/particle.png +0 -0
  51. package/dist/examples/vintasend-medplum-example/public/img/integrations/quest.png +0 -0
  52. package/dist/examples/vintasend-medplum-example/public/img/integrations/recaptcha.png +0 -0
  53. package/dist/examples/vintasend-medplum-example/public/img/integrations/snowflake.png +0 -0
  54. package/dist/examples/vintasend-medplum-example/public/img/integrations/stedi.png +0 -0
  55. package/dist/examples/vintasend-medplum-example/public/img/integrations/stripe.png +0 -0
  56. package/dist/examples/vintasend-medplum-example/public/img/integrations/sumo.png +0 -0
  57. package/dist/examples/vintasend-medplum-example/scripts/README.md +162 -0
  58. package/dist/examples/vintasend-medplum-example/scripts/client.ts +18 -0
  59. package/dist/examples/vintasend-medplum-example/scripts/deploy-bots.ts +171 -0
  60. package/dist/examples/vintasend-medplum-example/src/App.tsx +185 -0
  61. package/dist/examples/vintasend-medplum-example/src/components/ChargeItem/ChargeItemList.test.tsx +350 -0
  62. package/dist/examples/vintasend-medplum-example/src/components/ChargeItem/ChargeItemList.tsx +241 -0
  63. package/dist/examples/vintasend-medplum-example/src/components/ChargeItem/ChargeItemPanel.test.tsx +616 -0
  64. package/dist/examples/vintasend-medplum-example/src/components/ChargeItem/ChargeItemPanel.tsx +138 -0
  65. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionItem.test.tsx +92 -0
  66. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionItem.tsx +47 -0
  67. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionList.test.tsx +464 -0
  68. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionList.tsx +186 -0
  69. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionModal.test.tsx +80 -0
  70. package/dist/examples/vintasend-medplum-example/src/components/Conditions/ConditionModal.tsx +82 -0
  71. package/dist/examples/vintasend-medplum-example/src/components/DoseSpotIcon.test.tsx +100 -0
  72. package/dist/examples/vintasend-medplum-example/src/components/DoseSpotIcon.tsx +20 -0
  73. package/dist/examples/vintasend-medplum-example/src/components/IntegrationCard.module.css +3 -0
  74. package/dist/examples/vintasend-medplum-example/src/components/IntegrationCard.tsx +62 -0
  75. package/dist/examples/vintasend-medplum-example/src/components/MessageWithLinks.tsx +47 -0
  76. package/dist/examples/vintasend-medplum-example/src/components/PerformingLabInput.test.tsx +299 -0
  77. package/dist/examples/vintasend-medplum-example/src/components/PerformingLabInput.tsx +52 -0
  78. package/dist/examples/vintasend-medplum-example/src/components/ResourceFormWithRequiredProfile.tsx +82 -0
  79. package/dist/examples/vintasend-medplum-example/src/components/encounter/BillingTab.test.tsx +1016 -0
  80. package/dist/examples/vintasend-medplum-example/src/components/encounter/BillingTab.tsx +298 -0
  81. package/dist/examples/vintasend-medplum-example/src/components/encounter/EncounterChart.test.tsx +732 -0
  82. package/dist/examples/vintasend-medplum-example/src/components/encounter/EncounterChart.tsx +282 -0
  83. package/dist/examples/vintasend-medplum-example/src/components/encounter/EncounterHeader.test.tsx +268 -0
  84. package/dist/examples/vintasend-medplum-example/src/components/encounter/EncounterHeader.tsx +224 -0
  85. package/dist/examples/vintasend-medplum-example/src/components/encounter/SignAddendum.test.tsx +255 -0
  86. package/dist/examples/vintasend-medplum-example/src/components/encounter/SignAddendum.tsx +212 -0
  87. package/dist/examples/vintasend-medplum-example/src/components/encounter/SignLockDialog.test.tsx +120 -0
  88. package/dist/examples/vintasend-medplum-example/src/components/encounter/SignLockDialog.tsx +57 -0
  89. package/dist/examples/vintasend-medplum-example/src/components/encounter/VisitDetailsPanel.test.tsx +224 -0
  90. package/dist/examples/vintasend-medplum-example/src/components/encounter/VisitDetailsPanel.tsx +100 -0
  91. package/dist/examples/vintasend-medplum-example/src/components/labs/CoverageInput.test.tsx +431 -0
  92. package/dist/examples/vintasend-medplum-example/src/components/labs/CoverageInput.tsx +130 -0
  93. package/dist/examples/vintasend-medplum-example/src/components/labs/LabListItem.module.css +31 -0
  94. package/dist/examples/vintasend-medplum-example/src/components/labs/LabListItem.test.tsx +234 -0
  95. package/dist/examples/vintasend-medplum-example/src/components/labs/LabListItem.tsx +143 -0
  96. package/dist/examples/vintasend-medplum-example/src/components/labs/LabOrderDetails.module.css +11 -0
  97. package/dist/examples/vintasend-medplum-example/src/components/labs/LabOrderDetails.test.tsx +875 -0
  98. package/dist/examples/vintasend-medplum-example/src/components/labs/LabOrderDetails.tsx +943 -0
  99. package/dist/examples/vintasend-medplum-example/src/components/labs/LabResultDetails.test.tsx +413 -0
  100. package/dist/examples/vintasend-medplum-example/src/components/labs/LabResultDetails.tsx +203 -0
  101. package/dist/examples/vintasend-medplum-example/src/components/labs/LabSelectEmpty.tsx +22 -0
  102. package/dist/examples/vintasend-medplum-example/src/components/labs/README.md +104 -0
  103. package/dist/examples/vintasend-medplum-example/src/components/labs/TestMetadataCardInput.test.tsx +318 -0
  104. package/dist/examples/vintasend-medplum-example/src/components/labs/TestMetadataCardInput.tsx +87 -0
  105. package/dist/examples/vintasend-medplum-example/src/components/messages/ChatList.test.tsx +126 -0
  106. package/dist/examples/vintasend-medplum-example/src/components/messages/ChatList.tsx +38 -0
  107. package/dist/examples/vintasend-medplum-example/src/components/messages/ChatListItem.module.css +23 -0
  108. package/dist/examples/vintasend-medplum-example/src/components/messages/ChatListItem.test.tsx +167 -0
  109. package/dist/examples/vintasend-medplum-example/src/components/messages/ChatListItem.tsx +53 -0
  110. package/dist/examples/vintasend-medplum-example/src/components/messages/NewTopicDialog.test.tsx +94 -0
  111. package/dist/examples/vintasend-medplum-example/src/components/messages/NewTopicDialog.tsx +165 -0
  112. package/dist/examples/vintasend-medplum-example/src/components/messages/ParticipantFilter.module.css +8 -0
  113. package/dist/examples/vintasend-medplum-example/src/components/messages/ParticipantFilter.test.tsx +523 -0
  114. package/dist/examples/vintasend-medplum-example/src/components/messages/ParticipantFilter.tsx +230 -0
  115. package/dist/examples/vintasend-medplum-example/src/components/messages/ThreadInbox.module.css +23 -0
  116. package/dist/examples/vintasend-medplum-example/src/components/messages/ThreadInbox.test.tsx +567 -0
  117. package/dist/examples/vintasend-medplum-example/src/components/messages/ThreadInbox.tsx +358 -0
  118. package/dist/examples/vintasend-medplum-example/src/components/plandefinition/AddPlanDefinition.module.css +40 -0
  119. package/dist/examples/vintasend-medplum-example/src/components/plandefinition/AddPlanDefinition.tsx +257 -0
  120. package/dist/examples/vintasend-medplum-example/src/components/schedule/CreateVisit.module.css +7 -0
  121. package/dist/examples/vintasend-medplum-example/src/components/schedule/CreateVisit.test.tsx +279 -0
  122. package/dist/examples/vintasend-medplum-example/src/components/schedule/CreateVisit.tsx +156 -0
  123. package/dist/examples/vintasend-medplum-example/src/components/spaces/HistoryList.module.css +45 -0
  124. package/dist/examples/vintasend-medplum-example/src/components/spaces/HistoryList.test.tsx +90 -0
  125. package/dist/examples/vintasend-medplum-example/src/components/spaces/HistoryList.tsx +84 -0
  126. package/dist/examples/vintasend-medplum-example/src/components/spaces/ResourceBox.module.css +26 -0
  127. package/dist/examples/vintasend-medplum-example/src/components/spaces/ResourceBox.tsx +90 -0
  128. package/dist/examples/vintasend-medplum-example/src/components/spaces/ResourcePanel.test.tsx +305 -0
  129. package/dist/examples/vintasend-medplum-example/src/components/spaces/ResourcePanel.tsx +46 -0
  130. package/dist/examples/vintasend-medplum-example/src/components/spaces/SpacesInbox.module.css +262 -0
  131. package/dist/examples/vintasend-medplum-example/src/components/spaces/SpacesInbox.test.tsx +622 -0
  132. package/dist/examples/vintasend-medplum-example/src/components/spaces/SpacesInbox.tsx +286 -0
  133. package/dist/examples/vintasend-medplum-example/src/components/tasks/NewTaskModal.tsx +275 -0
  134. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskAttachmentList.tsx +132 -0
  135. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskBoard.module.css +45 -0
  136. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskBoard.test.tsx +749 -0
  137. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskBoard.tsx +416 -0
  138. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskDetailPanel.test.tsx +278 -0
  139. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskDetailPanel.tsx +133 -0
  140. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskDetailsModal.module.css +16 -0
  141. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskDetailsModal.test.tsx +255 -0
  142. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskDetailsModal.tsx +203 -0
  143. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskFileUpload.tsx +129 -0
  144. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskFilterMenu.test.tsx +156 -0
  145. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskFilterMenu.tsx +142 -0
  146. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskFilterMenu.utils.ts +28 -0
  147. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskInputNote.test.tsx +134 -0
  148. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskInputNote.tsx +250 -0
  149. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskListItem.module.css +23 -0
  150. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskListItem.test.tsx +149 -0
  151. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskListItem.tsx +53 -0
  152. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskNoteItem.test.tsx +68 -0
  153. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskNoteItem.tsx +46 -0
  154. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskProperties.test.tsx +555 -0
  155. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskProperties.tsx +170 -0
  156. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskSelectEmpty.test.tsx +32 -0
  157. package/dist/examples/vintasend-medplum-example/src/components/tasks/TaskSelectEmpty.tsx +34 -0
  158. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/SimpleTask.test.tsx +47 -0
  159. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/SimpleTask.tsx +29 -0
  160. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskPanel.test.tsx +285 -0
  161. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskPanel.tsx +129 -0
  162. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskQuestionnaireForm.test.tsx +455 -0
  163. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskQuestionnaireForm.tsx +167 -0
  164. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskServiceRequest.test.tsx +435 -0
  165. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskServiceRequest.tsx +116 -0
  166. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskStatusPanel.module.css +38 -0
  167. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskStatusPanel.test.tsx +200 -0
  168. package/dist/examples/vintasend-medplum-example/src/components/tasks/encounter/TaskStatusPanel.tsx +84 -0
  169. package/dist/examples/vintasend-medplum-example/src/components/utils.test.ts +176 -0
  170. package/dist/examples/vintasend-medplum-example/src/components/utils.ts +17 -0
  171. package/dist/examples/vintasend-medplum-example/src/config/constants.ts +3 -0
  172. package/dist/examples/vintasend-medplum-example/src/hooks/useDebouncedUpdateResource.test.tsx +166 -0
  173. package/dist/examples/vintasend-medplum-example/src/hooks/useDebouncedUpdateResource.ts +28 -0
  174. package/dist/examples/vintasend-medplum-example/src/hooks/useEncounter.test.tsx +94 -0
  175. package/dist/examples/vintasend-medplum-example/src/hooks/useEncounter.ts +11 -0
  176. package/dist/examples/vintasend-medplum-example/src/hooks/useEncounterChart.test.tsx +477 -0
  177. package/dist/examples/vintasend-medplum-example/src/hooks/useEncounterChart.ts +191 -0
  178. package/dist/examples/vintasend-medplum-example/src/hooks/usePatient.test.tsx +100 -0
  179. package/dist/examples/vintasend-medplum-example/src/hooks/usePatient.ts +18 -0
  180. package/dist/examples/vintasend-medplum-example/src/hooks/useThreadInbox.test.tsx +379 -0
  181. package/dist/examples/vintasend-medplum-example/src/hooks/useThreadInbox.ts +194 -0
  182. package/dist/examples/vintasend-medplum-example/src/index.css +8 -0
  183. package/dist/examples/vintasend-medplum-example/src/main.tsx +57 -0
  184. package/dist/examples/vintasend-medplum-example/src/pages/SearchPage.module.css +6 -0
  185. package/dist/examples/vintasend-medplum-example/src/pages/SearchPage.test.tsx +295 -0
  186. package/dist/examples/vintasend-medplum-example/src/pages/SearchPage.tsx +124 -0
  187. package/dist/examples/vintasend-medplum-example/src/pages/SignInPage.test.tsx +77 -0
  188. package/dist/examples/vintasend-medplum-example/src/pages/SignInPage.tsx +22 -0
  189. package/dist/examples/vintasend-medplum-example/src/pages/encounter/EncounterChartPage.test.tsx +87 -0
  190. package/dist/examples/vintasend-medplum-example/src/pages/encounter/EncounterChartPage.tsx +27 -0
  191. package/dist/examples/vintasend-medplum-example/src/pages/encounter/EncounterModal.module.css +16 -0
  192. package/dist/examples/vintasend-medplum-example/src/pages/encounter/EncounterModal.test.tsx +287 -0
  193. package/dist/examples/vintasend-medplum-example/src/pages/encounter/EncounterModal.tsx +151 -0
  194. package/dist/examples/vintasend-medplum-example/src/pages/integrations/DoseSpotFavoritesPage.test.tsx +519 -0
  195. package/dist/examples/vintasend-medplum-example/src/pages/integrations/DoseSpotFavoritesPage.tsx +179 -0
  196. package/dist/examples/vintasend-medplum-example/src/pages/integrations/FavoriteMedicationsTable.tsx +76 -0
  197. package/dist/examples/vintasend-medplum-example/src/pages/integrations/IntegrationsPage.test.tsx +234 -0
  198. package/dist/examples/vintasend-medplum-example/src/pages/integrations/IntegrationsPage.tsx +222 -0
  199. package/dist/examples/vintasend-medplum-example/src/pages/labs/OrderLabsPage.test.tsx +356 -0
  200. package/dist/examples/vintasend-medplum-example/src/pages/labs/OrderLabsPage.tsx +275 -0
  201. package/dist/examples/vintasend-medplum-example/src/pages/messages/MessagesPage.module.css +8 -0
  202. package/dist/examples/vintasend-medplum-example/src/pages/messages/MessagesPage.test.tsx +103 -0
  203. package/dist/examples/vintasend-medplum-example/src/pages/messages/MessagesPage.tsx +78 -0
  204. package/dist/examples/vintasend-medplum-example/src/pages/patient/CommunicationTab.test.tsx +84 -0
  205. package/dist/examples/vintasend-medplum-example/src/pages/patient/CommunicationTab.tsx +82 -0
  206. package/dist/examples/vintasend-medplum-example/src/pages/patient/DoseSpotAdvancedOptions.test.tsx +364 -0
  207. package/dist/examples/vintasend-medplum-example/src/pages/patient/DoseSpotAdvancedOptions.tsx +149 -0
  208. package/dist/examples/vintasend-medplum-example/src/pages/patient/DoseSpotTab.test.tsx +159 -0
  209. package/dist/examples/vintasend-medplum-example/src/pages/patient/DoseSpotTab.tsx +37 -0
  210. package/dist/examples/vintasend-medplum-example/src/pages/patient/EditTab.test.tsx +140 -0
  211. package/dist/examples/vintasend-medplum-example/src/pages/patient/EditTab.tsx +72 -0
  212. package/dist/examples/vintasend-medplum-example/src/pages/patient/ExportTab.test.tsx +57 -0
  213. package/dist/examples/vintasend-medplum-example/src/pages/patient/ExportTab.tsx +14 -0
  214. package/dist/examples/vintasend-medplum-example/src/pages/patient/IntakeFormPage.test.tsx +241 -0
  215. package/dist/examples/vintasend-medplum-example/src/pages/patient/IntakeFormPage.tsx +710 -0
  216. package/dist/examples/vintasend-medplum-example/src/pages/patient/LabsPage.module.css +37 -0
  217. package/dist/examples/vintasend-medplum-example/src/pages/patient/LabsPage.test.tsx +428 -0
  218. package/dist/examples/vintasend-medplum-example/src/pages/patient/LabsPage.tsx +334 -0
  219. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientPage.module.css +24 -0
  220. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientPage.test.tsx +154 -0
  221. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientPage.tsx +115 -0
  222. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientPage.utils.test.ts +223 -0
  223. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientPage.utils.ts +89 -0
  224. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientSearchPage.test.tsx +147 -0
  225. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientSearchPage.tsx +79 -0
  226. package/dist/examples/vintasend-medplum-example/src/pages/patient/PatientTabsNavigation.tsx +35 -0
  227. package/dist/examples/vintasend-medplum-example/src/pages/patient/TasksTab.test.tsx +185 -0
  228. package/dist/examples/vintasend-medplum-example/src/pages/patient/TasksTab.tsx +115 -0
  229. package/dist/examples/vintasend-medplum-example/src/pages/patient/TimelineTab.tsx +14 -0
  230. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceCreatePage.test.tsx +170 -0
  231. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceCreatePage.tsx +117 -0
  232. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceDetailPage.tsx +28 -0
  233. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceEditPage.test.tsx +131 -0
  234. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceEditPage.tsx +65 -0
  235. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceHistoryPage.test.tsx +108 -0
  236. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourceHistoryPage.tsx +16 -0
  237. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourcePage.module.css +7 -0
  238. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourcePage.test.tsx +37 -0
  239. package/dist/examples/vintasend-medplum-example/src/pages/resource/ResourcePage.tsx +44 -0
  240. package/dist/examples/vintasend-medplum-example/src/pages/resource/useResourceType.ts +44 -0
  241. package/dist/examples/vintasend-medplum-example/src/pages/resource/utils.ts +9 -0
  242. package/dist/examples/vintasend-medplum-example/src/pages/schedule/SchedulePage.test.tsx +302 -0
  243. package/dist/examples/vintasend-medplum-example/src/pages/schedule/SchedulePage.tsx +416 -0
  244. package/dist/examples/vintasend-medplum-example/src/pages/spaces/ChatInput.tsx +91 -0
  245. package/dist/examples/vintasend-medplum-example/src/pages/spaces/SpacesPage.module.css +6 -0
  246. package/dist/examples/vintasend-medplum-example/src/pages/spaces/SpacesPage.test.tsx +102 -0
  247. package/dist/examples/vintasend-medplum-example/src/pages/spaces/SpacesPage.tsx +44 -0
  248. package/dist/examples/vintasend-medplum-example/src/pages/tasks/TasksPage.module.css +7 -0
  249. package/dist/examples/vintasend-medplum-example/src/pages/tasks/TasksPage.test.tsx +133 -0
  250. package/dist/examples/vintasend-medplum-example/src/pages/tasks/TasksPage.tsx +91 -0
  251. package/dist/examples/vintasend-medplum-example/src/test-utils/render.tsx +20 -0
  252. package/dist/examples/vintasend-medplum-example/src/test.setup.ts +49 -0
  253. package/dist/examples/vintasend-medplum-example/src/types/encounter.ts +8 -0
  254. package/dist/examples/vintasend-medplum-example/src/types/spaces.ts +10 -0
  255. package/dist/examples/vintasend-medplum-example/src/utils/chargeitems.test.ts +141 -0
  256. package/dist/examples/vintasend-medplum-example/src/utils/chargeitems.ts +59 -0
  257. package/dist/examples/vintasend-medplum-example/src/utils/claims.test.ts +153 -0
  258. package/dist/examples/vintasend-medplum-example/src/utils/claims.ts +65 -0
  259. package/dist/examples/vintasend-medplum-example/src/utils/communication-search.ts +47 -0
  260. package/dist/examples/vintasend-medplum-example/src/utils/coverage.test.ts +48 -0
  261. package/dist/examples/vintasend-medplum-example/src/utils/coverage.ts +33 -0
  262. package/dist/examples/vintasend-medplum-example/src/utils/documentReference.test.ts +102 -0
  263. package/dist/examples/vintasend-medplum-example/src/utils/documentReference.ts +55 -0
  264. package/dist/examples/vintasend-medplum-example/src/utils/encounter.test.ts +169 -0
  265. package/dist/examples/vintasend-medplum-example/src/utils/encounter.ts +261 -0
  266. package/dist/examples/vintasend-medplum-example/src/utils/intake-form.test.ts +154 -0
  267. package/dist/examples/vintasend-medplum-example/src/utils/intake-form.ts +272 -0
  268. package/dist/examples/vintasend-medplum-example/src/utils/intake-utils.test.ts +1137 -0
  269. package/dist/examples/vintasend-medplum-example/src/utils/intake-utils.ts +827 -0
  270. package/dist/examples/vintasend-medplum-example/src/utils/notifications.test.ts +27 -0
  271. package/dist/examples/vintasend-medplum-example/src/utils/notifications.ts +15 -0
  272. package/dist/examples/vintasend-medplum-example/src/utils/spaceMessaging.ts +249 -0
  273. package/dist/examples/vintasend-medplum-example/src/utils/spacePersistence.test.ts +450 -0
  274. package/dist/examples/vintasend-medplum-example/src/utils/spacePersistence.ts +147 -0
  275. package/dist/examples/vintasend-medplum-example/src/utils/task-search.ts +63 -0
  276. package/dist/examples/vintasend-medplum-example/src/vite-env.d.ts +3 -0
  277. package/dist/examples/vintasend-medplum-example/tsconfig.bots.json +4 -0
  278. package/dist/examples/vintasend-medplum-example/tsconfig.json +19 -0
  279. package/dist/examples/vintasend-medplum-example/vercel.json +3 -0
  280. package/dist/examples/vintasend-medplum-example/vite.config.ts +44 -0
  281. package/dist/services/notification-backends/base-notification-backend.d.ts +5 -0
  282. package/dist/services/notification-service.js +11 -1
  283. package/dist/services/notification-template-renderers/base-email-template-renderer.d.ts +5 -0
  284. package/package.json +1 -1
@@ -0,0 +1,113 @@
1
+ import { MedplumClient } from '@medplum/core';
2
+ import { Task } from '@medplum/fhirtypes';
3
+ import { MedplumSingleton } from '../../../lib/medplum-singleton';
4
+ import { getNotificationService, SendGridConfig } from '../../../lib/notification-service';
5
+ import { formatPatientNameWithPreferredName } from '../../../lib/patients';
6
+ import { getTaskAttachments } from '../../../lib/file-upload';
7
+
8
+ export async function sendTaskAssignmentEmail(
9
+ medplum: MedplumClient,
10
+ task: Task,
11
+ taskLinkBaseUrl: string,
12
+ sendgridConfig: SendGridConfig
13
+ ) {
14
+ /* sends a task assignment email to a practitioner */
15
+
16
+ if (!task.owner?.reference) {
17
+ // eslint-disable-next-line no-console
18
+ console.error('[sendTaskAssignmentEmail] Task has no owner reference');
19
+ throw new Error('Task must have an owner reference');
20
+ }
21
+
22
+ const referenceString = task.owner.reference;
23
+
24
+ // Validate format (should be "ResourceType/id")
25
+ if (!referenceString.includes('/')) {
26
+ // eslint-disable-next-line no-console
27
+ console.error('[sendTaskAssignmentEmail] Invalid referenceString format:', referenceString);
28
+ throw new Error(`Invalid referenceString format: ${referenceString}`);
29
+ }
30
+
31
+ // Skip sending email if task is assigned to a Group
32
+ const [resourceType] = referenceString.split('/');
33
+ if (resourceType === 'Group') {
34
+ // eslint-disable-next-line no-console
35
+ console.log('[sendTaskAssignmentEmail] Task assigned to Group, skipping email notification');
36
+ return;
37
+ }
38
+
39
+ MedplumSingleton.setInstance(medplum);
40
+ const vintasend = getNotificationService(medplum, sendgridConfig);
41
+
42
+ try {
43
+ const taskTitle = task.code?.text || task.description || 'New Task';
44
+
45
+ if (!task.id) {
46
+ // eslint-disable-next-line no-console
47
+ console.error('[sendTaskAssignmentEmail] Task has no id');
48
+ throw new Error('Task must have an id to send task assignment email');
49
+ }
50
+
51
+ const taskLink = `${taskLinkBaseUrl}/Task/${task.id}`;
52
+ const taskIsUrgent = task.priority === 'urgent';
53
+
54
+ let requesterName = 'someone';
55
+ if (task.requester?.reference) {
56
+ try {
57
+ const requester = await medplum.readReference(task.requester);
58
+ if ('name' in requester && requester.name && Array.isArray(requester.name)) {
59
+ requesterName = formatPatientNameWithPreferredName(requester.name[0]);
60
+ }
61
+ } catch (error) {
62
+ // eslint-disable-next-line no-console
63
+ console.error('[sendTaskAssignmentEmail] Error fetching requester:', error);
64
+ }
65
+ }
66
+
67
+ // Retrieve task attachments
68
+ const taskAttachments = await getTaskAttachments(medplum, task);
69
+
70
+ // eslint-disable-next-line no-console
71
+ console.log(`[sendTaskAssignmentEmail] Found ${taskAttachments.length} attachments for task ${task.id}`);
72
+
73
+ // Convert Media resources to VintaSend attachment references
74
+ // Use fileId to reference existing Media resources instead of re-uploading
75
+ const attachments = taskAttachments
76
+ .filter((media) => media.id) // Only include media with IDs
77
+ .map((media) => ({
78
+ fileId: media.id as string,
79
+ description: media.content?.title,
80
+ }));
81
+
82
+ // eslint-disable-next-line no-console
83
+ console.log(`[sendTaskAssignmentEmail] Prepared ${attachments.length} attachment references`);
84
+
85
+ await vintasend.createNotification({
86
+ userId: referenceString,
87
+ notificationType: 'EMAIL' as const,
88
+ title: 'Task Assignment',
89
+ contextName: 'taskAssignment' as const,
90
+ contextParameters: {
91
+ userId: referenceString,
92
+ taskTitle,
93
+ taskDescription: task.description || '',
94
+ taskIsUrgent,
95
+ taskLink,
96
+ requesterName,
97
+ attachmentCount: attachments.length,
98
+ },
99
+ sendAfter: new Date(),
100
+ bodyTemplate: 'emails/task-assignment/body.html.pug',
101
+ subjectTemplate: 'emails/task-assignment/subject.txt.pug',
102
+ attachments, // Add attachments to the notification
103
+ extraParams: {},
104
+ });
105
+
106
+ // eslint-disable-next-line no-console
107
+ console.log('[sendTaskAssignmentEmail] Email sent successfully to:', referenceString);
108
+ } catch (error) {
109
+ // eslint-disable-next-line no-console
110
+ console.error('[sendTaskAssignmentEmail] Error creating/sending notification:', error);
111
+ throw error;
112
+ }
113
+ }
@@ -0,0 +1,115 @@
1
+ import { Task } from '@medplum/fhirtypes';
2
+
3
+ export type TaskFinalStatus = 'completed' | 'cancelled' | 'failed' | 'rejected' | 'entered-in-error';
4
+
5
+ export type TaskDueSoonCheck =
6
+ | { kind: 'ok'; hoursUntilDue: number }
7
+ | { kind: 'invalidResource' }
8
+ | { kind: 'noDueDate' }
9
+ | { kind: 'invalidDueDate'; dueDate: string }
10
+ | { kind: 'finalState'; status: string }
11
+ | { kind: 'noOwner' }
12
+ | { kind: 'tooSoon'; hoursUntilDue: number };
13
+
14
+ const FINAL_STATES: readonly TaskFinalStatus[] = [
15
+ 'completed',
16
+ 'cancelled',
17
+ 'failed',
18
+ 'rejected',
19
+ 'entered-in-error',
20
+ ] as const;
21
+
22
+ export function getHoursUntil(date: string | Date): number {
23
+ const target = new Date(date);
24
+ const now = new Date();
25
+ return (target.getTime() - now.getTime()) / (1000 * 60 * 60);
26
+ }
27
+
28
+ export function getTaskDueSoonSchedulingReason(task: Task | undefined): TaskDueSoonCheck {
29
+ if (!task || task.resourceType !== 'Task') {
30
+ return { kind: 'invalidResource' };
31
+ }
32
+
33
+ const dueDate = task.restriction?.period?.end;
34
+ if (!dueDate) {
35
+ return { kind: 'noDueDate' };
36
+ }
37
+
38
+ if (task.status && FINAL_STATES.includes(task.status as TaskFinalStatus)) {
39
+ return { kind: 'finalState', status: task.status };
40
+ }
41
+
42
+ if (!task.owner) {
43
+ return { kind: 'noOwner' };
44
+ }
45
+
46
+ const hoursUntilDue = getHoursUntil(dueDate);
47
+ if (Number.isNaN(hoursUntilDue)) {
48
+ return { kind: 'invalidDueDate', dueDate };
49
+ }
50
+
51
+ if (hoursUntilDue < 24) {
52
+ return { kind: 'tooSoon', hoursUntilDue };
53
+ }
54
+
55
+ return { kind: 'ok', hoursUntilDue };
56
+ }
57
+
58
+ export function assertTaskOwnerReference(task: Task): string {
59
+ const reference = task.owner?.reference;
60
+ if (!reference) {
61
+ // eslint-disable-next-line no-console
62
+ console.error('[TaskDueSoonHelper] Task has no owner reference');
63
+ throw new Error('Task must have an owner reference');
64
+ }
65
+ return reference;
66
+ }
67
+
68
+ export function getValidTaskDueDate(task: Task): Date {
69
+ const raw = task.restriction?.period?.end;
70
+ if (!raw) {
71
+ // eslint-disable-next-line no-console
72
+ console.error('[TaskDueSoonHelper] Task has no due date');
73
+ throw new Error('Task must have a due date (restriction.period.end)');
74
+ }
75
+
76
+ const dueDate = new Date(raw);
77
+ if (Number.isNaN(dueDate.getTime())) {
78
+ // eslint-disable-next-line no-console
79
+ console.error('[TaskDueSoonHelper] Task has invalid due date:', raw);
80
+ throw new Error('Task must have a valid due date (restriction.period.end)');
81
+ }
82
+ return dueDate;
83
+ }
84
+
85
+ export function parseOwnerReference(
86
+ referenceString: string
87
+ ): { resourceType: string; id: string } | null {
88
+ if (!referenceString.includes('/')) {
89
+ // eslint-disable-next-line no-console
90
+ console.error('[TaskDueSoonHelper] Invalid referenceString format:', referenceString);
91
+ return null;
92
+ }
93
+ const [resourceType, id] = referenceString.split('/');
94
+ if (!resourceType || !id) {
95
+ // eslint-disable-next-line no-console
96
+ console.error('[TaskDueSoonHelper] Invalid referenceString components:', referenceString);
97
+ return null;
98
+ }
99
+ if (resourceType === 'Group') {
100
+ // eslint-disable-next-line no-console
101
+ console.log('[TaskDueSoonHelper] Task assigned to Group, skipping email notification');
102
+ return null;
103
+ }
104
+ return { resourceType, id };
105
+ }
106
+
107
+ export function computeReminderTime(dueDate: Date, hoursBefore: number): Date | null {
108
+ const sendAfter = new Date(dueDate.getTime() - hoursBefore * 60 * 60 * 1000);
109
+ if (sendAfter <= new Date()) {
110
+ // eslint-disable-next-line no-console
111
+ console.log('[TaskDueSoonHelper] Reminder time is in the past or within window, skipping');
112
+ return null;
113
+ }
114
+ return sendAfter;
115
+ }
@@ -0,0 +1,41 @@
1
+ import { BotEvent, MedplumClient } from '@medplum/core';
2
+ import { Task } from '@medplum/fhirtypes';
3
+ import { sendTaskAssignmentEmail } from './services/emails/send-task-assignment-email';
4
+ import { buildSendGridConfig } from '../lib/notification-service';
5
+
6
+ /**
7
+ * Medplum Bot: Task Assignment Email Notification
8
+ *
9
+ * This bot is triggered by a subscription when a Task is created or updated
10
+ * with an owner. It sends an email notification to the assigned practitioner.
11
+ *
12
+ * Subscription Criteria: Task?owner:exists=true
13
+ * Triggers: create, update
14
+ */
15
+
16
+ export async function handler(medplum: MedplumClient, event: BotEvent): Promise<Task> {
17
+ const task = event.input as Task;
18
+ const sendGridVariables = buildSendGridConfig(event);
19
+
20
+ console.log(`[TaskAssignmentBot] Processing task: ${task.id}`);
21
+ console.log(`[TaskAssignmentBot] Owner: ${task.owner?.reference}`);
22
+
23
+ // Only send email if task has an owner
24
+ if (task.owner?.reference) {
25
+ const appBaseUrl = process.env.APP_BASE_URL || 'https://your-app-url.com';
26
+
27
+ try {
28
+ await sendTaskAssignmentEmail(medplum, task, appBaseUrl, sendGridVariables);
29
+ console.log(`[TaskAssignmentBot] Email notification sent successfully for task: ${task.id}`);
30
+ } catch (error) {
31
+ console.error(`[TaskAssignmentBot] Failed to send email for task: ${task.id}`, error);
32
+ // Don't throw - we don't want the subscription to fail
33
+ // The notification will be logged in Medplum as failed
34
+ }
35
+ } else {
36
+ console.log(`[TaskAssignmentBot] Task ${task.id} has no owner, skipping email notification`);
37
+ }
38
+
39
+ // Return the task unchanged
40
+ return task;
41
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "emails/task-assignment/body.html.pug": "doctype html\nhtml\n head\n meta(charset='utf-8')\n style.\n body {\n white-space: pre-line;\n }\n body\n h1 Task Assigned\n\n p Hello #{firstName},\n\n p You have been assigned a new task by #{requesterName}.\n\n p\n strong Task:\n | #{taskTitle}\n if taskDescription\n p\n strong Description:\n | #{taskDescription}\n \n if attachmentCount > 0\n p\n strong Attachments:\n | #{attachmentCount} file(s) attached\n p Files are attached to this email for your reference.\n \n if taskIsUrgent\n p\n strong URGENT\n\n p\n a(href=taskLink) View Task\n\n p Please review the task details and take appropriate action.\n",
3
+ "emails/task-assignment/subject.txt.pug": "if taskIsUrgent\n | [URGENT] Task assigned to you: #{taskTitle}\nelse\n | Task assigned to you: #{taskTitle}",
4
+ "emails/task-due-soon/body.html.pug": "doctype html\nhtml\n head\n meta(charset='utf-8')\n style.\n body {\n white-space: pre-line;\n }\n body\n h1 Task Due Reminder\n\n p Hello #{firstName},\n\n p This is a reminder that you have a task that is due in approximately 24 hours.\n\n p\n strong Task:\n | #{taskTitle}\n if taskDescription\n p\n strong Description:\n | #{taskDescription}\n p\n strong Due Date:\n | #{dueDate}\n if taskIsUrgent\n p\n strong URGENT\n\n p\n a(href=taskLink) View Task\n\n p Please make sure to complete this task before the due date.\n\n",
5
+ "emails/task-due-soon/subject.txt.pug": "if taskIsUrgent\n | [URGENT] Task due soon: #{taskTitle}\nelse\n | Reminder: Task due soon - #{taskTitle}\n"
6
+ }
@@ -0,0 +1,71 @@
1
+ /* global console */
2
+ /* global process */
3
+ /* eslint no-console: "off" */
4
+ /*eslint no-process-exit: "off"*/
5
+
6
+ import botLayer from '@medplum/bot-layer/package.json' with { type: 'json' };
7
+ import { sentryEsbuildPlugin } from '@sentry/esbuild-plugin';
8
+ import esbuild from 'esbuild';
9
+ import fastGlob from 'fast-glob';
10
+ import { gqlPlugin } from './plugins/gql-plugin.mjs';
11
+
12
+ // Find all TypeScript files in your source directory
13
+ const entryPoints = fastGlob
14
+ .sync(['./src/**/*.ts', './bots/**/*.ts', './lib/**/*.ts'])
15
+ .filter((file) => !file.endsWith('.test.ts') && !file.endsWith('.test.tsx') && !file.endsWith('test.ts') && !file.endsWith('test.setup.ts'));
16
+
17
+ const botLayerDeps = Object.keys(botLayer.dependencies);
18
+
19
+ const plugins = [gqlPlugin];
20
+
21
+ // Only include Sentry plugin if environment variables are present
22
+ if (process.env.SENTRY_ORG && process.env.SENTRY_PROJECT && process.env.SENTRY_AUTH_TOKEN) {
23
+ plugins.push(
24
+ sentryEsbuildPlugin({
25
+ org: process.env.SENTRY_ORG,
26
+ project: process.env.SENTRY_PROJECT,
27
+ authToken: process.env.SENTRY_AUTH_TOKEN,
28
+ sourcemaps: {
29
+ filesToDeleteAfterUpload: ['dist/**/*.map'],
30
+ },
31
+ })
32
+ );
33
+ }
34
+
35
+ // Add additional external packages
36
+ const additionalExternals = [
37
+ 'react-transition-group',
38
+ 'react-remove-scroll',
39
+ 'react-transition-group/*',
40
+ 'react-remove-scroll/*',
41
+ ];
42
+
43
+ // Define the esbuild options
44
+ const esbuildOptions = {
45
+ entryPoints: entryPoints,
46
+ bundle: true, // Bundle imported functions
47
+ outdir: './dist', // Output directory for compiled files
48
+ platform: 'node', // or 'node', depending on your target platform
49
+ plugins: plugins,
50
+ loader: {
51
+ '.ts': 'ts', // Load TypeScript files
52
+ },
53
+ resolveExtensions: ['.ts', '.js', '.json'],
54
+ external: [...botLayerDeps, ...additionalExternals], // Add the additional externals here
55
+ format: 'cjs', // Set output format as ECMAScript modules
56
+ target: 'es2020', // Set the target ECMAScript version
57
+ tsconfig: 'tsconfig.json',
58
+ footer: { js: 'Object.assign(exports, module.exports);' }, // Required for VM Context Bots
59
+ sourcemap: true,
60
+ };
61
+
62
+ // Build using esbuild
63
+ esbuild
64
+ .build(esbuildOptions)
65
+ .then(() => {
66
+ console.log('Build completed successfully!');
67
+ })
68
+ .catch((error) => {
69
+ console.error('Build failed:', JSON.stringify(error, null, 2));
70
+ process.exit(1);
71
+ });
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Medplum Provider</title>
7
+ <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script src="https://accounts.google.com/gsi/client" async></script>
12
+ <script type="module" src="/src/main.tsx"></script>
13
+ </body>
14
+ </html>
@@ -0,0 +1,32 @@
1
+ export const BOTS_SYSTEM = 'http://vintasend-medplum-example.com/bots';
2
+
3
+ /**
4
+ * Task attachment configuration
5
+ */
6
+ export const TASK_ATTACHMENT_INPUT_TYPE = {
7
+ system: 'http://vintasend-medplum-example.com/task-input-types',
8
+ code: 'attachment',
9
+ display: 'File Attachment',
10
+ };
11
+
12
+ /**
13
+ * Maximum file size for attachments (10MB)
14
+ */
15
+ export const MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
16
+
17
+ /**
18
+ * Allowed file types for task attachments
19
+ */
20
+ export const ALLOWED_FILE_TYPES = [
21
+ 'application/pdf',
22
+ 'image/jpeg',
23
+ 'image/png',
24
+ 'image/gif',
25
+ 'image/webp',
26
+ 'application/msword',
27
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
28
+ 'application/vnd.ms-excel',
29
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
30
+ 'text/plain',
31
+ 'text/csv',
32
+ ];
@@ -0,0 +1 @@
1
+ export const PREFERRED_NAME_EXTENSION_URL = 'http://vintasend-medplum-example.com/preferred-name';