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,261 @@
1
+ // SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { getReferenceString, createReference, formatHumanName, getExtension, HTTP_HL7_ORG } from '@medplum/core';
4
+ import type { MedplumClient } from '@medplum/core';
5
+ import type {
6
+ Appointment,
7
+ ChargeItem,
8
+ ClinicalImpression,
9
+ Coding,
10
+ Encounter,
11
+ HumanName,
12
+ Patient,
13
+ PlanDefinition,
14
+ Practitioner,
15
+ ServiceRequest,
16
+ Task,
17
+ } from '@medplum/fhirtypes';
18
+
19
+ export async function createEncounter(
20
+ medplum: MedplumClient,
21
+ start: Date,
22
+ end: Date,
23
+ classification: Coding,
24
+ patient: Patient,
25
+ planDefinition: PlanDefinition
26
+ ): Promise<Encounter> {
27
+ const appointment = await medplum.createResource({
28
+ resourceType: 'Appointment',
29
+ status: 'booked',
30
+ start: start.toISOString(),
31
+ end: end.toISOString(),
32
+ participant: [
33
+ {
34
+ actor: {
35
+ reference: getReferenceString(patient),
36
+ display: formatHumanName(patient.name?.[0] as HumanName),
37
+ },
38
+ status: 'accepted',
39
+ },
40
+ {
41
+ actor: {
42
+ reference: getReferenceString(medplum.getProfile() as Practitioner),
43
+ display: formatHumanName(medplum.getProfile()?.name as HumanName),
44
+ },
45
+ status: 'accepted',
46
+ },
47
+ ],
48
+ });
49
+
50
+ const encounter: Encounter = await medplum.createResource({
51
+ resourceType: 'Encounter',
52
+ status: 'planned',
53
+ statusHistory: [],
54
+ classHistory: [],
55
+ class: classification,
56
+ subject: createReference(patient),
57
+ appointment: [createReference(appointment)],
58
+ participant: [
59
+ {
60
+ individual: {
61
+ reference: getReferenceString(medplum.getProfile() as Practitioner),
62
+ },
63
+ },
64
+ ],
65
+ });
66
+
67
+ const clinicalImpressionData: ClinicalImpression = {
68
+ resourceType: 'ClinicalImpression',
69
+ status: 'in-progress',
70
+ description: 'Initial clinical impression',
71
+ subject: createReference(patient),
72
+ encounter: createReference(encounter),
73
+ date: new Date().toISOString(),
74
+ };
75
+
76
+ await medplum.createResource(clinicalImpressionData);
77
+
78
+ await medplum.post(medplum.fhirUrl('PlanDefinition', planDefinition.id as string, '$apply'), {
79
+ resourceType: 'Parameters',
80
+ parameter: [
81
+ { name: 'subject', valueString: getReferenceString(patient) },
82
+ { name: 'encounter', valueString: getReferenceString(encounter) },
83
+ { name: 'practitioner', valueString: getReferenceString(medplum.getProfile() as Practitioner) },
84
+ ],
85
+ });
86
+
87
+ await createChargeItemFromPlanDefinition(medplum, encounter, patient, planDefinition);
88
+ await handleChargeItemsFromTasks(medplum, encounter, patient);
89
+
90
+ return encounter;
91
+ }
92
+
93
+ async function createChargeItemFromPlanDefinition(
94
+ medplum: MedplumClient,
95
+ encounter: Encounter,
96
+ patient: Patient,
97
+ planDefinition: PlanDefinition
98
+ ): Promise<void> {
99
+ const serviceBillingCodeExtension = getExtension(
100
+ planDefinition,
101
+ `${HTTP_HL7_ORG}/fhir/uv/order-catalog/StructureDefinition/ServiceBillingCode`
102
+ );
103
+
104
+ const chargeDefinitionExtension = getExtension(
105
+ planDefinition,
106
+ 'http://medplum.com/fhir/StructureDefinition/applicable-charge-definition'
107
+ );
108
+
109
+ if (!serviceBillingCodeExtension?.valueCodeableConcept || !chargeDefinitionExtension?.valueCanonical) {
110
+ console.log('PlanDefinition missing required extensions for charge item creation');
111
+ return;
112
+ }
113
+
114
+ const cptCoding = serviceBillingCodeExtension.valueCodeableConcept.coding?.find(
115
+ (coding) => coding.system === 'http://www.ama-assn.org/go/cpt'
116
+ );
117
+
118
+ if (!cptCoding) {
119
+ return;
120
+ }
121
+
122
+ const chargeItem: ChargeItem = {
123
+ resourceType: 'ChargeItem',
124
+ status: 'planned',
125
+ subject: createReference(patient),
126
+ context: createReference(encounter),
127
+ occurrenceDateTime: new Date().toISOString(),
128
+ code: serviceBillingCodeExtension.valueCodeableConcept,
129
+ extension: [serviceBillingCodeExtension],
130
+ quantity: {
131
+ value: 1,
132
+ },
133
+ definitionCanonical: [chargeDefinitionExtension.valueCanonical],
134
+ };
135
+
136
+ await medplum.createResource(chargeItem);
137
+ }
138
+
139
+ async function handleChargeItemsFromTasks(
140
+ medplum: MedplumClient,
141
+ encounter: Encounter,
142
+ patient: Patient
143
+ ): Promise<void> {
144
+ const tasks = await medplum.search('Task', {
145
+ encounter: getReferenceString(encounter),
146
+ });
147
+
148
+ if (!tasks.entry?.length) {
149
+ return;
150
+ }
151
+
152
+ await Promise.all(
153
+ tasks.entry.map(async (entry) => {
154
+ const task = entry.resource as Task;
155
+ const serviceRequestRef = task.focus?.reference;
156
+
157
+ if (!serviceRequestRef?.startsWith('ServiceRequest/')) {
158
+ return;
159
+ }
160
+
161
+ try {
162
+ const serviceRequest: ServiceRequest = await medplum.readReference({
163
+ reference: serviceRequestRef,
164
+ });
165
+ await createChargeItemFromServiceRequest(medplum, patient, serviceRequest);
166
+ } catch (err) {
167
+ console.error(`Error processing ServiceRequest ${serviceRequestRef}:`, err);
168
+ }
169
+ })
170
+ );
171
+ }
172
+
173
+ async function createChargeItemFromServiceRequest(
174
+ medplum: MedplumClient,
175
+ patient: Patient,
176
+ serviceRequest: ServiceRequest
177
+ ): Promise<void> {
178
+ const chargeDefinitionExtension = getExtension(
179
+ serviceRequest,
180
+ 'http://medplum.com/fhir/StructureDefinition/applicable-charge-definition'
181
+ );
182
+
183
+ if (
184
+ !chargeDefinitionExtension?.valueCanonical ||
185
+ !serviceRequest.code?.coding?.find((c) => c.system === 'http://www.ama-assn.org/go/cpt')
186
+ ) {
187
+ return;
188
+ }
189
+
190
+ const canonicalUrl = chargeDefinitionExtension?.valueCanonical;
191
+ const definitionCanonical = canonicalUrl ? [canonicalUrl] : [];
192
+
193
+ const chargeItem: ChargeItem = {
194
+ resourceType: 'ChargeItem',
195
+ status: 'planned',
196
+ supportingInformation: [
197
+ {
198
+ reference: `ServiceRequest/${serviceRequest.id}`,
199
+ },
200
+ ],
201
+ subject: createReference(patient),
202
+ context: serviceRequest.encounter,
203
+ occurrenceDateTime: serviceRequest.occurrenceDateTime || new Date().toISOString(),
204
+ code: serviceRequest.code || { coding: [] },
205
+ quantity: {
206
+ value: 1,
207
+ },
208
+ definitionCanonical: definitionCanonical,
209
+ };
210
+
211
+ await medplum.createResource(chargeItem);
212
+ }
213
+
214
+ export async function updateEncounterStatus(
215
+ medplum: MedplumClient,
216
+ encounter: Encounter,
217
+ appointment: Appointment | undefined,
218
+ newStatus: Encounter['status']
219
+ ): Promise<Encounter> {
220
+ const updatedEncounter: Encounter = {
221
+ ...encounter,
222
+ status: newStatus,
223
+ ...(newStatus === 'in-progress' &&
224
+ !encounter.period?.start && {
225
+ period: {
226
+ ...encounter.period,
227
+ start: new Date().toISOString(),
228
+ },
229
+ }),
230
+ ...(newStatus === 'finished' &&
231
+ !encounter.period?.end && {
232
+ period: {
233
+ ...encounter.period,
234
+ end: new Date().toISOString(),
235
+ },
236
+ }),
237
+ };
238
+
239
+ if (appointment) {
240
+ const updatedAppointment: Appointment = appointment;
241
+ switch (newStatus) {
242
+ case 'cancelled':
243
+ updatedAppointment.status = 'cancelled';
244
+ break;
245
+ case 'finished':
246
+ updatedAppointment.status = 'fulfilled';
247
+ break;
248
+ case 'in-progress':
249
+ updatedAppointment.status = 'checked-in';
250
+ break;
251
+ case 'arrived':
252
+ updatedAppointment.status = 'arrived';
253
+ break;
254
+ default:
255
+ break;
256
+ }
257
+ await medplum.updateResource(updatedAppointment);
258
+ }
259
+
260
+ return medplum.updateResource(updatedEncounter);
261
+ }
@@ -0,0 +1,154 @@
1
+ // SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import type { Questionnaire, QuestionnaireResponse, QuestionnaireResponseItemAnswer } from '@medplum/fhirtypes';
4
+ import { MockClient } from '@medplum/mock';
5
+ import { describe, expect, test, vi, beforeEach } from 'vitest';
6
+
7
+ const mockAnswers = {
8
+ 'first-name': { valueString: 'Jamie' },
9
+ 'last-name': { valueString: 'Doe' },
10
+ dob: { valueDate: '1990-01-01' },
11
+ phone: { valueString: '555-5555' },
12
+ ssn: { valueString: '123-45-6789' },
13
+ street: { valueString: '1 Main St' },
14
+ city: { valueString: 'Springfield' },
15
+ state: { valueCoding: { code: 'CA' } },
16
+ zip: { valueString: '90210' },
17
+ 'languages-spoken': { valueCoding: { code: 'en' } },
18
+ 'preferred-language': { valueCoding: { code: 'en' } },
19
+ race: { valueCoding: { code: 'race-code', display: 'Race' } },
20
+ ethnicity: { valueCoding: { code: 'eth-code', display: 'Ethnicity' } },
21
+ 'veteran-status': { valueBoolean: true },
22
+ 'consent-for-treatment-signature': { valueBoolean: true },
23
+ 'consent-for-treatment-date': { valueDate: '2024-01-01' },
24
+ 'agreement-to-pay-for-treatment-help': { valueBoolean: true },
25
+ 'agreement-to-pay-for-treatment-date': { valueDate: '2024-01-02' },
26
+ 'notice-of-privacy-practices-signature': { valueBoolean: true },
27
+ 'notice-of-privacy-practices-date': { valueDate: '2024-01-03' },
28
+ 'acknowledgement-for-advance-directives-signature': { valueBoolean: true },
29
+ 'acknowledgement-for-advance-directives-date': { valueDate: '2024-01-04' },
30
+ 'preferred-pharmacy-reference': { valueReference: { reference: 'Organization/pharmacy-1' } },
31
+ } satisfies Record<string, QuestionnaireResponseItemAnswer>;
32
+
33
+ const mockIntakeUtils = vi.hoisted(() => ({
34
+ addAllergy: vi.fn(),
35
+ addCondition: vi.fn(),
36
+ addConsent: vi.fn(),
37
+ addCoverage: vi.fn(),
38
+ addExtension: vi.fn(),
39
+ addFamilyMemberHistory: vi.fn(),
40
+ addImmunization: vi.fn(),
41
+ addLanguage: vi.fn(),
42
+ addMedication: vi.fn(),
43
+ addPharmacy: vi.fn(),
44
+ consentCategoryMapping: {
45
+ med: { coding: [] },
46
+ pay: { coding: [] },
47
+ nopp: { coding: [] },
48
+ acd: { coding: [] },
49
+ },
50
+ consentPolicyRuleMapping: {
51
+ cric: { coding: [] },
52
+ hipaaSelfPay: { coding: [] },
53
+ hipaaNpp: { coding: [] },
54
+ adr: { coding: [] },
55
+ },
56
+ consentScopeMapping: {
57
+ treatment: { coding: [] },
58
+ patientPrivacy: { coding: [] },
59
+ adr: { coding: [] },
60
+ },
61
+ convertDateToDateTime: vi.fn((date?: string) => (date ? `${date}T00:00:00Z` : undefined)),
62
+ extensionURLMapping: {
63
+ race: 'race-url',
64
+ ethnicity: 'ethnicity-url',
65
+ veteran: 'veteran-url',
66
+ },
67
+ getGroupRepeatedAnswers: vi.fn((_, __, groupId) => {
68
+ if (groupId === 'coverage-information') {
69
+ return [
70
+ {
71
+ 'insurance-provider': { valueReference: { reference: 'Organization/org-1' } },
72
+ 'subscriber-id': { valueString: 'sub-1' },
73
+ 'relationship-to-subscriber': { valueCoding: { code: 'self' } },
74
+ },
75
+ ];
76
+ }
77
+ if (groupId === 'allergies') {
78
+ return [{ 'allergy-substance': { valueCoding: { code: 'peanut' } } }];
79
+ }
80
+ return [];
81
+ }),
82
+ getHumanName: vi.fn(() => ({ given: ['Jamie'], family: 'Doe' })),
83
+ getPatientAddress: vi.fn(() => ({ line: ['1 Main St'] })),
84
+ observationCategoryMapping: {
85
+ socialHistory: { coding: [] },
86
+ sdoh: { coding: [] },
87
+ },
88
+ observationCodeMapping: {
89
+ sexualOrientation: { coding: [] },
90
+ housingStatus: { coding: [] },
91
+ educationLevel: { coding: [] },
92
+ smokingStatus: { coding: [] },
93
+ pregnancyStatus: { coding: [] },
94
+ estimatedDeliveryDate: { coding: [] },
95
+ },
96
+ PROFILE_URLS: {
97
+ Patient: 'patient-profile',
98
+ ObservationSexualOrientation: 'obs-profile',
99
+ },
100
+ upsertObservation: vi.fn(),
101
+ }));
102
+
103
+ vi.mock('./intake-utils', () => mockIntakeUtils);
104
+
105
+ import { onboardPatient } from './intake-form';
106
+
107
+ describe('onboardPatient', () => {
108
+ let medplum: MockClient;
109
+
110
+ beforeEach(() => {
111
+ medplum = new MockClient();
112
+ vi.clearAllMocks();
113
+ });
114
+
115
+ test('creates patient and invokes onboarding helpers', async () => {
116
+ const createSpy = vi.spyOn(medplum, 'createResource').mockImplementation(async (resource: any) => {
117
+ if (resource.resourceType === 'Patient') {
118
+ return { ...resource, id: 'patient-1' };
119
+ }
120
+ return resource;
121
+ });
122
+
123
+ const questionnaire = { resourceType: 'Questionnaire' } as Questionnaire;
124
+ const response = buildResponseFromAnswers(mockAnswers);
125
+
126
+ const patient = await onboardPatient(medplum as any, questionnaire, response);
127
+
128
+ expect(patient.id).toBe('patient-1');
129
+ expect(createSpy).toHaveBeenCalledWith(expect.objectContaining({ resourceType: 'Patient' }));
130
+ expect(mockIntakeUtils.addExtension).toHaveBeenCalledWith(
131
+ expect.objectContaining({}),
132
+ 'race-url',
133
+ 'valueCoding',
134
+ mockAnswers['race'],
135
+ 'ombCategory'
136
+ );
137
+ expect(mockIntakeUtils.addLanguage).toHaveBeenCalledTimes(2);
138
+ expect(mockIntakeUtils.addCoverage).toHaveBeenCalledWith(medplum, patient, expect.any(Object));
139
+ expect(mockIntakeUtils.addAllergy).toHaveBeenCalled();
140
+ expect(mockIntakeUtils.addConsent).toHaveBeenCalled();
141
+ expect(mockIntakeUtils.addPharmacy).toHaveBeenCalled();
142
+ });
143
+ });
144
+
145
+ function buildResponseFromAnswers(answers: Record<string, QuestionnaireResponseItemAnswer>): QuestionnaireResponse {
146
+ return {
147
+ resourceType: 'QuestionnaireResponse',
148
+ status: 'completed',
149
+ item: Object.entries(answers).map(([linkId, answer]) => ({
150
+ linkId,
151
+ answer: [answer],
152
+ })),
153
+ };
154
+ }
@@ -0,0 +1,272 @@
1
+ // SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { addProfileToResource, createReference, getQuestionnaireAnswers } from '@medplum/core';
4
+ import type { MedplumClient } from '@medplum/core';
5
+ import type { Organization, Patient, Questionnaire, QuestionnaireResponse, Reference } from '@medplum/fhirtypes';
6
+ import {
7
+ addAllergy,
8
+ addCondition,
9
+ addConsent,
10
+ addCoverage,
11
+ addExtension,
12
+ addFamilyMemberHistory,
13
+ addImmunization,
14
+ addLanguage,
15
+ addMedication,
16
+ addPharmacy,
17
+ consentCategoryMapping,
18
+ consentPolicyRuleMapping,
19
+ consentScopeMapping,
20
+ convertDateToDateTime,
21
+ extensionURLMapping,
22
+ getGroupRepeatedAnswers,
23
+ getHumanName,
24
+ getPatientAddress,
25
+ observationCategoryMapping,
26
+ observationCodeMapping,
27
+ PROFILE_URLS,
28
+ upsertObservation,
29
+ } from './intake-utils';
30
+
31
+ export async function onboardPatient(
32
+ medplum: MedplumClient,
33
+ questionnaire: Questionnaire,
34
+ response: QuestionnaireResponse
35
+ ): Promise<Patient> {
36
+ const answers = getQuestionnaireAnswers(response);
37
+
38
+ let patient: Patient = {
39
+ resourceType: 'Patient',
40
+ };
41
+
42
+ patient = addProfileToResource(patient, PROFILE_URLS.Patient);
43
+
44
+ // Handle demographic information
45
+
46
+ const patientName = getHumanName(answers);
47
+ if (patientName) {
48
+ patient.name = [patientName];
49
+ }
50
+
51
+ if (answers['dob']?.valueDate) {
52
+ patient.birthDate = answers['dob'].valueDate;
53
+ }
54
+
55
+ const patientAddress = getPatientAddress(answers);
56
+ if (patientAddress) {
57
+ patient.address = [patientAddress];
58
+ }
59
+
60
+ if (answers['gender-identity']?.valueCoding?.code) {
61
+ patient.gender = answers['gender-identity'].valueCoding.code as Patient['gender'];
62
+ }
63
+
64
+ if (answers['phone']?.valueString) {
65
+ patient.telecom = [{ system: 'phone', value: answers['phone'].valueString }];
66
+ }
67
+
68
+ if (answers['ssn']?.valueString) {
69
+ patient.identifier = [
70
+ {
71
+ type: {
72
+ coding: [
73
+ {
74
+ system: 'http://terminology.hl7.org/CodeSystem/v2-0203',
75
+ code: 'SS',
76
+ },
77
+ ],
78
+ },
79
+ system: 'http://hl7.org/fhir/sid/us-ssn',
80
+ value: answers['ssn'].valueString,
81
+ },
82
+ ];
83
+ }
84
+
85
+ const emergencyContacts = getGroupRepeatedAnswers(questionnaire, response, 'emergency-contact');
86
+ if (emergencyContacts) {
87
+ patient.contact = [];
88
+ for (const contact of emergencyContacts) {
89
+ patient.contact.push({
90
+ relationship: [
91
+ {
92
+ coding: [
93
+ {
94
+ system: 'http://terminology.hl7.org/CodeSystem/v2-0131',
95
+ code: 'EP',
96
+ display: 'Emergency contact person',
97
+ },
98
+ ],
99
+ },
100
+ ],
101
+ name: getHumanName(contact, 'emergency-contact-'),
102
+ telecom: [{ system: 'phone', value: contact['emergency-contact-phone']?.valueString }],
103
+ });
104
+ }
105
+ }
106
+
107
+ addExtension(patient, extensionURLMapping.race, 'valueCoding', answers['race'], 'ombCategory');
108
+ addExtension(patient, extensionURLMapping.ethnicity, 'valueCoding', answers['ethnicity'], 'ombCategory');
109
+ addExtension(patient, extensionURLMapping.veteran, 'valueBoolean', answers['veteran-status']);
110
+
111
+ addLanguage(patient, answers['languages-spoken']?.valueCoding);
112
+ addLanguage(patient, answers['preferred-language']?.valueCoding, true);
113
+
114
+ // Create the patient resource
115
+
116
+ patient = await medplum.createResource(patient);
117
+
118
+ // NOTE: Updating the questionnaire response does not trigger a loop because the bot subscription
119
+ // is configured for "create"-only event.
120
+ response.subject = createReference(patient);
121
+ await medplum.createResource(response);
122
+
123
+ // Handle observations
124
+
125
+ await upsertObservation(
126
+ medplum,
127
+ patient,
128
+ observationCodeMapping.sexualOrientation,
129
+ observationCategoryMapping.socialHistory,
130
+ 'valueCodeableConcept',
131
+ answers['sexual-orientation']?.valueCoding,
132
+ PROFILE_URLS.ObservationSexualOrientation
133
+ );
134
+
135
+ await upsertObservation(
136
+ medplum,
137
+ patient,
138
+ observationCodeMapping.housingStatus,
139
+ observationCategoryMapping.sdoh,
140
+ 'valueCodeableConcept',
141
+ answers['housing-status']?.valueCoding
142
+ );
143
+
144
+ await upsertObservation(
145
+ medplum,
146
+ patient,
147
+ observationCodeMapping.educationLevel,
148
+ observationCategoryMapping.sdoh,
149
+ 'valueCodeableConcept',
150
+ answers['education-level']?.valueCoding
151
+ );
152
+
153
+ await upsertObservation(
154
+ medplum,
155
+ patient,
156
+ observationCodeMapping.smokingStatus,
157
+ observationCategoryMapping.socialHistory,
158
+ 'valueCodeableConcept',
159
+ answers['smoking-status']?.valueCoding,
160
+ PROFILE_URLS.ObservationSmokingStatus
161
+ );
162
+
163
+ await upsertObservation(
164
+ medplum,
165
+ patient,
166
+ observationCodeMapping.pregnancyStatus,
167
+ observationCategoryMapping.socialHistory,
168
+ 'valueCodeableConcept',
169
+ answers['pregnancy-status']?.valueCoding
170
+ );
171
+
172
+ const estimatedDeliveryDate = convertDateToDateTime(answers['estimated-delivery-date']?.valueDate);
173
+ await upsertObservation(
174
+ medplum,
175
+ patient,
176
+ observationCodeMapping.estimatedDeliveryDate,
177
+ observationCategoryMapping.socialHistory,
178
+ 'valueDateTime',
179
+ estimatedDeliveryDate ? { valueDateTime: estimatedDeliveryDate } : undefined
180
+ );
181
+
182
+ // Handle allergies
183
+
184
+ const allergies = getGroupRepeatedAnswers(questionnaire, response, 'allergies');
185
+ for (const allergy of allergies) {
186
+ await addAllergy(medplum, patient, allergy);
187
+ }
188
+
189
+ // Handle medications
190
+
191
+ const medications = getGroupRepeatedAnswers(questionnaire, response, 'medications');
192
+ for (const medication of medications) {
193
+ await addMedication(medplum, patient, medication);
194
+ }
195
+
196
+ // Handle medical history
197
+
198
+ const medicalHistory = getGroupRepeatedAnswers(questionnaire, response, 'medical-history');
199
+ for (const history of medicalHistory) {
200
+ await addCondition(medplum, patient, history);
201
+ }
202
+
203
+ const familyMemberHistory = getGroupRepeatedAnswers(questionnaire, response, 'family-member-history');
204
+ for (const history of familyMemberHistory) {
205
+ await addFamilyMemberHistory(medplum, patient, history);
206
+ }
207
+
208
+ // Handle vaccination history (immunizations)
209
+
210
+ const vaccinationHistory = getGroupRepeatedAnswers(questionnaire, response, 'vaccination-history');
211
+ for (const vaccine of vaccinationHistory) {
212
+ await addImmunization(medplum, patient, vaccine);
213
+ }
214
+
215
+ // Handle coverage
216
+
217
+ const insuranceProviders = getGroupRepeatedAnswers(questionnaire, response, 'coverage-information');
218
+ for (const provider of insuranceProviders) {
219
+ await addCoverage(medplum, patient, provider);
220
+ }
221
+
222
+ // Handle preferred pharmacy
223
+
224
+ const preferredPharmacyReference = answers['preferred-pharmacy-reference']?.valueReference;
225
+ if (preferredPharmacyReference) {
226
+ await addPharmacy(medplum, patient, preferredPharmacyReference as Reference<Organization>);
227
+ }
228
+
229
+ // Handle consents
230
+
231
+ await addConsent(
232
+ medplum,
233
+ patient,
234
+ !!answers['consent-for-treatment-signature']?.valueBoolean,
235
+ consentScopeMapping.treatment,
236
+ consentCategoryMapping.med,
237
+ consentPolicyRuleMapping.cric,
238
+ convertDateToDateTime(answers['consent-for-treatment-date']?.valueDate)
239
+ );
240
+
241
+ await addConsent(
242
+ medplum,
243
+ patient,
244
+ !!answers['agreement-to-pay-for-treatment-help']?.valueBoolean,
245
+ consentScopeMapping.treatment,
246
+ consentCategoryMapping.pay,
247
+ consentPolicyRuleMapping.hipaaSelfPay,
248
+ convertDateToDateTime(answers['agreement-to-pay-for-treatment-date']?.valueDate)
249
+ );
250
+
251
+ await addConsent(
252
+ medplum,
253
+ patient,
254
+ !!answers['notice-of-privacy-practices-signature']?.valueBoolean,
255
+ consentScopeMapping.patientPrivacy,
256
+ consentCategoryMapping.nopp,
257
+ consentPolicyRuleMapping.hipaaNpp,
258
+ convertDateToDateTime(answers['notice-of-privacy-practices-date']?.valueDate)
259
+ );
260
+
261
+ await addConsent(
262
+ medplum,
263
+ patient,
264
+ !!answers['acknowledgement-for-advance-directives-signature']?.valueBoolean,
265
+ consentScopeMapping.adr,
266
+ consentCategoryMapping.acd,
267
+ consentPolicyRuleMapping.adr,
268
+ convertDateToDateTime(answers['acknowledgement-for-advance-directives-date']?.valueDate)
269
+ );
270
+
271
+ return patient;
272
+ }