@openmrs/esm-appointments-app 10.0.2 → 10.0.3-pre.2

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 (392) hide show
  1. package/.turbo/turbo-build.log +6 -5
  2. package/dist/1339.js +1 -0
  3. package/dist/1339.js.map +1 -0
  4. package/dist/1465.js +1 -0
  5. package/dist/1465.js.map +1 -0
  6. package/dist/1480.js +1 -0
  7. package/dist/1480.js.map +1 -0
  8. package/dist/1646.js +1 -0
  9. package/dist/1646.js.map +1 -0
  10. package/dist/1789.js +1 -0
  11. package/dist/1789.js.map +1 -0
  12. package/dist/1869.js +1 -0
  13. package/dist/1869.js.map +1 -0
  14. package/dist/1877.js +1 -0
  15. package/dist/1877.js.map +1 -0
  16. package/dist/1884.js +1 -0
  17. package/dist/1884.js.map +1 -0
  18. package/dist/1962.js +1 -0
  19. package/dist/1962.js.map +1 -0
  20. package/dist/2317.js +1 -0
  21. package/dist/2317.js.map +1 -0
  22. package/dist/2416.js +1 -0
  23. package/dist/2416.js.map +1 -0
  24. package/dist/2495.js +1 -0
  25. package/dist/2495.js.map +1 -0
  26. package/dist/2539.js +1 -0
  27. package/dist/2539.js.map +1 -0
  28. package/dist/2620.js +1 -0
  29. package/dist/2620.js.map +1 -0
  30. package/dist/2717.js +1 -0
  31. package/dist/2717.js.map +1 -0
  32. package/dist/282.js +1 -0
  33. package/dist/282.js.map +1 -0
  34. package/dist/2881.js +1 -0
  35. package/dist/2881.js.map +1 -0
  36. package/dist/3198.js +11 -0
  37. package/dist/3198.js.map +1 -0
  38. package/dist/3220.js +1 -0
  39. package/dist/3220.js.map +1 -0
  40. package/dist/3378.js +1 -0
  41. package/dist/3378.js.map +1 -0
  42. package/dist/3720.js +1 -0
  43. package/dist/3720.js.map +1 -0
  44. package/dist/3930.js +1 -0
  45. package/dist/3930.js.map +1 -0
  46. package/dist/3963.js +1 -0
  47. package/dist/3963.js.map +1 -0
  48. package/dist/3989.js +1 -0
  49. package/dist/3989.js.map +1 -0
  50. package/dist/4106.js +1 -0
  51. package/dist/4106.js.map +1 -0
  52. package/dist/4111.js +1 -0
  53. package/dist/4111.js.map +1 -0
  54. package/dist/4307.js +1 -0
  55. package/dist/4307.js.map +1 -0
  56. package/dist/434.js +1 -0
  57. package/dist/434.js.map +1 -0
  58. package/dist/4348.js +1 -0
  59. package/dist/4348.js.map +1 -0
  60. package/dist/4383.js +1 -0
  61. package/dist/4383.js.map +1 -0
  62. package/dist/4658.js +1 -0
  63. package/dist/4658.js.map +1 -0
  64. package/dist/466.js +1 -0
  65. package/dist/466.js.map +1 -0
  66. package/dist/4928.js +1 -0
  67. package/dist/4928.js.map +1 -0
  68. package/dist/5117.js +1 -0
  69. package/dist/5117.js.map +1 -0
  70. package/dist/5132.js +1 -0
  71. package/dist/5132.js.map +1 -0
  72. package/dist/5145.js +1 -0
  73. package/dist/5145.js.map +1 -0
  74. package/dist/5503.js +1 -0
  75. package/dist/5503.js.map +1 -0
  76. package/dist/556.js +1 -0
  77. package/dist/556.js.map +1 -0
  78. package/dist/5640.js +1 -0
  79. package/dist/5640.js.map +1 -0
  80. package/dist/5644.js +1 -0
  81. package/dist/5644.js.map +1 -0
  82. package/dist/5940.js +1 -0
  83. package/dist/5940.js.map +1 -0
  84. package/dist/6047.js +1 -0
  85. package/dist/6047.js.map +1 -0
  86. package/dist/6098.js +38 -0
  87. package/dist/6098.js.map +1 -0
  88. package/dist/6236.js +1 -0
  89. package/dist/6236.js.map +1 -0
  90. package/dist/635.js +1 -0
  91. package/dist/635.js.map +1 -0
  92. package/dist/6371.js +1 -0
  93. package/dist/6371.js.map +1 -0
  94. package/dist/6377.js +1 -0
  95. package/dist/6377.js.map +1 -0
  96. package/dist/6444.js +1 -0
  97. package/dist/6444.js.map +1 -0
  98. package/dist/6508.js +1 -0
  99. package/dist/6508.js.map +1 -0
  100. package/dist/6724.js +1 -0
  101. package/dist/6724.js.map +1 -0
  102. package/dist/6789.js +1 -0
  103. package/dist/6789.js.map +1 -0
  104. package/dist/689.js +1 -0
  105. package/dist/689.js.map +1 -0
  106. package/dist/6904.js +1 -0
  107. package/dist/6904.js.map +1 -0
  108. package/dist/7045.js +1 -0
  109. package/dist/7045.js.map +1 -0
  110. package/dist/7138.js +1 -0
  111. package/dist/7138.js.map +1 -0
  112. package/dist/7159.js +1 -0
  113. package/dist/7159.js.map +1 -0
  114. package/dist/7175.js +1 -0
  115. package/dist/7175.js.map +1 -0
  116. package/dist/7182.js +1 -0
  117. package/dist/7182.js.map +1 -0
  118. package/dist/7357.js +1 -0
  119. package/dist/7357.js.map +1 -0
  120. package/dist/7609.js +1 -0
  121. package/dist/7609.js.map +1 -0
  122. package/dist/7654.js +1 -0
  123. package/dist/7654.js.map +1 -0
  124. package/dist/7742.js +1 -0
  125. package/dist/7742.js.map +1 -0
  126. package/dist/7912.js +1 -0
  127. package/dist/7912.js.map +1 -0
  128. package/dist/8063.js +1 -0
  129. package/dist/8063.js.map +1 -0
  130. package/dist/8346.js +1 -0
  131. package/dist/8346.js.map +1 -0
  132. package/dist/8358.js +1 -0
  133. package/dist/8358.js.map +1 -0
  134. package/dist/8359.js +1 -0
  135. package/dist/8359.js.map +1 -0
  136. package/dist/8695.js +1 -0
  137. package/dist/8695.js.map +1 -0
  138. package/dist/8937.js +1 -0
  139. package/dist/8937.js.map +1 -0
  140. package/dist/8981.js +1 -0
  141. package/dist/8981.js.map +1 -0
  142. package/dist/903.js +1 -0
  143. package/dist/903.js.map +1 -0
  144. package/dist/9061.js +1 -0
  145. package/dist/9061.js.map +1 -0
  146. package/dist/9072.js +1 -0
  147. package/dist/9072.js.map +1 -0
  148. package/dist/9282.js +1 -0
  149. package/dist/9282.js.map +1 -0
  150. package/dist/9399.js +1 -0
  151. package/dist/9399.js.map +1 -0
  152. package/dist/9443.js +1 -0
  153. package/dist/9443.js.map +1 -0
  154. package/dist/9581.js +1 -0
  155. package/dist/9581.js.map +1 -0
  156. package/dist/9712.js +1 -0
  157. package/dist/9712.js.map +1 -0
  158. package/dist/9771.js +1 -0
  159. package/dist/9771.js.map +1 -0
  160. package/dist/9806.js +1 -0
  161. package/dist/9806.js.map +1 -0
  162. package/dist/main.js +6 -5
  163. package/dist/main.js.map +1 -1
  164. package/dist/openmrs-esm-appointments-app.js +6 -5
  165. package/dist/openmrs-esm-appointments-app.js.buildmanifest.json +625 -620
  166. package/dist/openmrs-esm-appointments-app.js.map +1 -1
  167. package/dist/routes.json +1 -1
  168. package/package.json +3 -2
  169. package/src/appointments/appointment-tabs.component.tsx +38 -16
  170. package/src/appointments/common-components/appointments-actions.component.tsx +39 -47
  171. package/src/appointments/common-components/appointments-actions.test.tsx +52 -99
  172. package/src/appointments/common-components/appointments-table.component.tsx +109 -135
  173. package/src/appointments/common-components/appointments-table.scss +10 -6
  174. package/src/appointments/common-components/appointments-table.test.tsx +2 -9
  175. package/src/appointments/common-components/batch-change-appointment-statuses.modal.tsx +1 -1
  176. package/src/appointments/common-components/batch-change-appointment-statuses.test.tsx +8 -6
  177. package/src/appointments/common-components/checkin-button.component.tsx +60 -29
  178. package/src/appointments/common-components/end-appointment.modal.tsx +1 -1
  179. package/src/appointments/common-components/end-appointment.test.tsx +1 -1
  180. package/src/appointments/scheduled/early-appointments.component.tsx +6 -7
  181. package/src/appointments/scheduled/scheduled-appointments.component.tsx +26 -206
  182. package/src/appointments/utils.tsx +29 -16
  183. package/src/appointments.component.tsx +5 -13
  184. package/src/appointments.test.tsx +16 -4
  185. package/src/calendar/appointments-calendar-view.component.tsx +3 -12
  186. package/src/calendar/appointments-calendar-view.test.tsx +6 -1
  187. package/src/calendar/header/calendar-header.component.tsx +2 -2
  188. package/src/calendar/monthly/monthly-calendar-view.component.tsx +2 -2
  189. package/src/calendar/monthly/monthly-header.component.tsx +9 -8
  190. package/src/calendar/monthly/monthly-workload-view.component.tsx +2 -2
  191. package/src/config-schema.ts +17 -9
  192. package/src/constants.ts +12 -1
  193. package/src/dashboard.meta.ts +1 -1
  194. package/src/form/appointments-form.resource.ts +1 -120
  195. package/src/form/appointments-form.workspace.tsx +15 -18
  196. package/src/header/appointments-header.component.tsx +27 -9
  197. package/src/helpers/functions.ts +1 -50
  198. package/src/home/home-appointments.component.tsx +17 -4
  199. package/src/hooks/useActiveVisits.ts +14 -0
  200. package/src/hooks/useAppointmentList.ts +12 -29
  201. package/src/hooks/useAppointmentService.ts +6 -1
  202. package/src/hooks/useAppointmentsAppContext.ts +19 -0
  203. package/src/hooks/useAppointmentsCalendar.ts +2 -1
  204. package/src/hooks/useMutateAppointments.ts +24 -0
  205. package/src/hooks/usePatientAppointmentHistory.ts +3 -2
  206. package/src/hooks/useSelectedDate.ts +24 -0
  207. package/src/hooks/useUnscheduledAppointments.ts +5 -1
  208. package/src/index.ts +0 -21
  209. package/src/metrics/metrics-cards/highest-volume-service.extension.tsx +27 -6
  210. package/src/metrics/metrics-cards/metrics-card.component.tsx +2 -1
  211. package/src/metrics/metrics-cards/providers-booked.extension.tsx +14 -6
  212. package/src/metrics/metrics-cards/scheduled-appointments.extension.tsx +20 -16
  213. package/src/metrics/metrics-container.component.tsx +1 -5
  214. package/src/metrics/metrics-header.component.tsx +2 -2
  215. package/src/patient-appointments/patient-appointments-detailed-summary.extension.tsx +1 -1
  216. package/src/patient-appointments/patient-upcoming-appointments-card.component.tsx +3 -4
  217. package/src/routes.json +3 -27
  218. package/src/store.ts +24 -41
  219. package/src/types/index.ts +7 -0
  220. package/src/workload/monthly-view-workload/monthly-view.component.tsx +2 -2
  221. package/src/workload/workload.component.tsx +3 -3
  222. package/src/workload/workload.resource.ts +1 -1
  223. package/translations/am.json +15 -6
  224. package/translations/ar.json +15 -6
  225. package/translations/ar_SY.json +15 -6
  226. package/translations/bn.json +15 -6
  227. package/translations/cs.json +15 -6
  228. package/translations/de.json +177 -168
  229. package/translations/en.json +15 -6
  230. package/translations/en_US.json +15 -6
  231. package/translations/es.json +15 -6
  232. package/translations/es_MX.json +15 -6
  233. package/translations/fr.json +15 -6
  234. package/translations/he.json +15 -6
  235. package/translations/hi.json +15 -6
  236. package/translations/hi_IN.json +15 -6
  237. package/translations/id.json +15 -6
  238. package/translations/it.json +15 -6
  239. package/translations/ka.json +15 -6
  240. package/translations/km.json +15 -6
  241. package/translations/ku.json +15 -6
  242. package/translations/ky.json +15 -6
  243. package/translations/lg.json +15 -6
  244. package/translations/ne.json +15 -6
  245. package/translations/pl.json +15 -6
  246. package/translations/pt.json +15 -6
  247. package/translations/pt_BR.json +15 -6
  248. package/translations/qu.json +15 -6
  249. package/translations/ro_RO.json +15 -6
  250. package/translations/ru_RU.json +15 -6
  251. package/translations/si.json +15 -6
  252. package/translations/sq.json +15 -6
  253. package/translations/sw.json +15 -6
  254. package/translations/sw_KE.json +15 -6
  255. package/translations/tr.json +15 -6
  256. package/translations/tr_TR.json +15 -6
  257. package/translations/uk.json +15 -6
  258. package/translations/uz.json +15 -6
  259. package/translations/uz@Latn.json +15 -6
  260. package/translations/uz_UZ.json +15 -6
  261. package/translations/vi.json +15 -6
  262. package/translations/zh.json +63 -54
  263. package/translations/zh_CN.json +19 -10
  264. package/translations/zh_TW.json +15 -6
  265. package/dist/1187.js +0 -1
  266. package/dist/1187.js.map +0 -1
  267. package/dist/126.js +0 -1
  268. package/dist/1499.js +0 -1
  269. package/dist/1499.js.map +0 -1
  270. package/dist/15.js +0 -1
  271. package/dist/1522.js +0 -1
  272. package/dist/1522.js.map +0 -1
  273. package/dist/1564.js +0 -1
  274. package/dist/1567.js +0 -1
  275. package/dist/1777.js +0 -1
  276. package/dist/1777.js.map +0 -1
  277. package/dist/1845.js +0 -1
  278. package/dist/1899.js +0 -1
  279. package/dist/1899.js.map +0 -1
  280. package/dist/1953.js +0 -1
  281. package/dist/2056.js +0 -11
  282. package/dist/2056.js.map +0 -1
  283. package/dist/2104.js +0 -1
  284. package/dist/2104.js.map +0 -1
  285. package/dist/215.js +0 -1
  286. package/dist/2178.js +0 -1
  287. package/dist/2417.js +0 -1
  288. package/dist/2417.js.map +0 -1
  289. package/dist/2566.js +0 -1
  290. package/dist/2586.js +0 -1
  291. package/dist/2586.js.map +0 -1
  292. package/dist/2759.js +0 -1
  293. package/dist/276.js +0 -1
  294. package/dist/276.js.map +0 -1
  295. package/dist/3089.js +0 -1
  296. package/dist/3089.js.map +0 -1
  297. package/dist/3127.js +0 -1
  298. package/dist/3127.js.map +0 -1
  299. package/dist/3230.js +0 -1
  300. package/dist/3277.js +0 -1
  301. package/dist/3277.js.map +0 -1
  302. package/dist/3441.js +0 -1
  303. package/dist/3565.js +0 -1
  304. package/dist/3571.js +0 -1
  305. package/dist/3571.js.map +0 -1
  306. package/dist/3746.js +0 -1
  307. package/dist/3925.js +0 -1
  308. package/dist/3946.js +0 -1
  309. package/dist/4085.js +0 -1
  310. package/dist/4085.js.map +0 -1
  311. package/dist/4108.js +0 -1
  312. package/dist/4108.js.map +0 -1
  313. package/dist/4448.js +0 -1
  314. package/dist/4448.js.map +0 -1
  315. package/dist/4744.js +0 -1
  316. package/dist/4744.js.map +0 -1
  317. package/dist/4809.js +0 -1
  318. package/dist/486.js +0 -1
  319. package/dist/486.js.map +0 -1
  320. package/dist/4894.js +0 -1
  321. package/dist/4970.js +0 -1
  322. package/dist/4970.js.map +0 -1
  323. package/dist/5130.js +0 -1
  324. package/dist/5187.js +0 -1
  325. package/dist/5218.js +0 -1
  326. package/dist/5218.js.map +0 -1
  327. package/dist/5327.js +0 -1
  328. package/dist/5327.js.map +0 -1
  329. package/dist/5388.js +0 -1
  330. package/dist/5388.js.map +0 -1
  331. package/dist/5491.js +0 -1
  332. package/dist/5491.js.map +0 -1
  333. package/dist/5595.js +0 -1
  334. package/dist/5657.js +0 -38
  335. package/dist/5657.js.map +0 -1
  336. package/dist/5961.js +0 -1
  337. package/dist/6133.js +0 -1
  338. package/dist/634.js +0 -1
  339. package/dist/634.js.map +0 -1
  340. package/dist/6456.js +0 -1
  341. package/dist/6466.js +0 -1
  342. package/dist/6613.js +0 -1
  343. package/dist/6783.js +0 -1
  344. package/dist/703.js +0 -1
  345. package/dist/703.js.map +0 -1
  346. package/dist/7251.js +0 -1
  347. package/dist/7251.js.map +0 -1
  348. package/dist/7348.js +0 -1
  349. package/dist/7433.js +0 -1
  350. package/dist/7433.js.map +0 -1
  351. package/dist/7513.js +0 -1
  352. package/dist/7513.js.map +0 -1
  353. package/dist/7543.js +0 -1
  354. package/dist/7607.js +0 -1
  355. package/dist/772.js +0 -1
  356. package/dist/8139.js +0 -1
  357. package/dist/8139.js.map +0 -1
  358. package/dist/8456.js +0 -1
  359. package/dist/8456.js.map +0 -1
  360. package/dist/8588.js +0 -1
  361. package/dist/8588.js.map +0 -1
  362. package/dist/8599.js +0 -1
  363. package/dist/8727.js +0 -1
  364. package/dist/8847.js +0 -1
  365. package/dist/8919.js +0 -1
  366. package/dist/8919.js.map +0 -1
  367. package/dist/9015.js +0 -1
  368. package/dist/9051.js +0 -1
  369. package/dist/9051.js.map +0 -1
  370. package/dist/906.js +0 -1
  371. package/dist/9065.js +0 -1
  372. package/dist/9182.js +0 -1
  373. package/dist/9260.js +0 -1
  374. package/dist/9260.js.map +0 -1
  375. package/dist/9327.js +0 -1
  376. package/dist/9327.js.map +0 -1
  377. package/dist/9339.js +0 -1
  378. package/dist/9453.js +0 -1
  379. package/dist/9589.js +0 -1
  380. package/dist/9589.js.map +0 -1
  381. package/dist/9650.js +0 -1
  382. package/dist/9650.js.map +0 -1
  383. package/dist/9833.js +0 -1
  384. package/dist/9833.js.map +0 -1
  385. package/dist/9920.js +0 -1
  386. package/dist/9938.js +0 -1
  387. package/dist/9943.js +0 -1
  388. package/dist/9943.js.map +0 -1
  389. package/src/appointments/scheduled/appointments-list.component.tsx +0 -51
  390. package/src/hooks/useClinicalMetrics.ts +0 -94
  391. package/src/hooks/useTodaysVisits.ts +0 -19
  392. package/src/scheduled-appointments-config-schema.ts +0 -177
@@ -5,42 +5,64 @@ import { useConfig } from '@openmrs/esm-framework';
5
5
  import { type ConfigObject } from '../config-schema';
6
6
  import ScheduledAppointments from './scheduled/scheduled-appointments.component';
7
7
  import UnscheduledAppointments from './unscheduled/unscheduled-appointments.component';
8
+ import EarlyAppointments from './scheduled/early-appointments.component';
8
9
  import styles from './appointment-tabs.scss';
9
10
 
10
- interface AppointmentTabsProps {
11
- appointmentServiceTypes: Array<string>;
12
- }
13
-
14
- const AppointmentTabs: React.FC<AppointmentTabsProps> = ({ appointmentServiceTypes }) => {
11
+ /**
12
+ * By default, this component shows just a table of sheduled appoingments. If the config option
13
+ * `showUnscheduledAppointmentsTab` or `showEarlyAppointmentsTab` is enabled (or both), then it shows tabs
14
+ * to render the respective tables.
15
+ */
16
+ const AppointmentTabs: React.FC<{}> = () => {
15
17
  const { t } = useTranslation();
16
- const { showUnscheduledAppointmentsTab } = useConfig<ConfigObject>();
18
+ const { showUnscheduledAppointmentsTab, showEarlyAppointmentsTab } = useConfig<ConfigObject>();
17
19
  const [activeTabIndex, setActiveTabIndex] = useState(0);
18
20
 
19
21
  const handleTabChange = ({ selectedIndex }: { selectedIndex: number }) => {
20
22
  setActiveTabIndex(selectedIndex);
21
23
  };
22
24
 
25
+ type TabLabelAndComponent = {
26
+ label: string;
27
+ component: React.ReactNode;
28
+ };
29
+
30
+ const tabsToShow: TabLabelAndComponent[] = [
31
+ { label: t('scheduled', 'Scheduled'), component: <ScheduledAppointments /> },
32
+ ];
33
+
34
+ if (showUnscheduledAppointmentsTab) {
35
+ tabsToShow.push({ label: t('unscheduled', 'Unscheduled'), component: <UnscheduledAppointments /> });
36
+ }
37
+
38
+ if (showEarlyAppointmentsTab) {
39
+ tabsToShow.push({ label: t('early', 'Early'), component: <EarlyAppointments /> });
40
+ }
41
+
42
+ // only show tabs if there are more than 1, otherwise just show <ScheduledAppointments />
23
43
  return (
24
44
  <div className={styles.appointmentList} data-testid="appointment-list">
25
- {showUnscheduledAppointmentsTab ? (
45
+ {tabsToShow.length > 1 ? (
26
46
  <div className={styles.tabs}>
27
47
  <Tabs selectedIndex={activeTabIndex} onChange={handleTabChange}>
28
48
  <TabList style={{ paddingLeft: '1rem' }} aria-label="Appointment tabs" contained>
29
- <Tab className={styles.tab}>{t('scheduled', 'Scheduled')}</Tab>
30
- <Tab className={styles.tab}>{t('unscheduled', 'Unscheduled')}</Tab>
49
+ {tabsToShow.map((tab) => (
50
+ <Tab key={tab.label} className={styles.tab}>
51
+ {tab.label}
52
+ </Tab>
53
+ ))}
31
54
  </TabList>
32
55
  <TabPanels>
33
- <TabPanel className={styles.tabPanel}>
34
- <ScheduledAppointments appointmentServiceTypes={appointmentServiceTypes} />
35
- </TabPanel>
36
- <TabPanel className={styles.tabPanel}>
37
- <UnscheduledAppointments />
38
- </TabPanel>
56
+ {tabsToShow.map((tab) => (
57
+ <TabPanel key={tab.label} className={styles.tabPanel}>
58
+ {tab.component}
59
+ </TabPanel>
60
+ ))}
39
61
  </TabPanels>
40
62
  </Tabs>
41
63
  </div>
42
64
  ) : (
43
- <ScheduledAppointments appointmentServiceTypes={appointmentServiceTypes} />
65
+ <ScheduledAppointments />
44
66
  )}
45
67
  </div>
46
68
  );
@@ -1,45 +1,42 @@
1
1
  import React from 'react';
2
- import dayjs from 'dayjs';
3
- import isToday from 'dayjs/plugin/isToday';
4
- import utc from 'dayjs/plugin/utc';
5
2
  import { Button } from '@carbon/react';
6
- import { TaskComplete } from '@carbon/react/icons';
7
3
  import { useTranslation } from 'react-i18next';
8
4
  import { navigate, showModal, useConfig } from '@openmrs/esm-framework';
9
5
  import { type Appointment, AppointmentStatus } from '../../types';
10
6
  import { type ConfigObject } from '../../config-schema';
11
- import { useTodaysVisits } from '../../hooks/useTodaysVisits';
12
7
  import CheckInButton from './checkin-button.component';
13
8
  import styles from './appointments-actions.scss';
14
9
 
15
- dayjs.extend(utc);
16
- dayjs.extend(isToday);
17
-
18
10
  interface AppointmentsActionsProps {
19
11
  appointment: Appointment;
12
+ hasActiveVisit: boolean;
13
+ mutateVisits: () => void;
20
14
  }
21
15
 
22
- const AppointmentsActions: React.FC<AppointmentsActionsProps> = ({ appointment }) => {
16
+ const AppointmentsActions: React.FC<AppointmentsActionsProps> = ({ appointment, hasActiveVisit, mutateVisits }) => {
23
17
  const { t } = useTranslation();
24
18
  const { checkInButton, checkOutButton } = useConfig<ConfigObject>();
25
- const { visits, mutateVisit } = useTodaysVisits(); // TODO doesn't work if visit didn't start today? what about inpatient?
19
+
26
20
  const patientUuid = appointment.patient.uuid;
27
- const visitDate = dayjs(appointment.startDateTime);
28
- const hasActiveVisitToday = visits?.some(
29
- (visit) => visit?.patient?.uuid === patientUuid && visit?.startDatetime && !visit?.stopDatetime,
30
- );
31
- const isTodaysAppointment = visitDate.isToday();
21
+
32
22
  const isCheckedIn = appointment.status === AppointmentStatus.CHECKEDIN;
33
23
  const isCompleted = appointment.status === AppointmentStatus.COMPLETED;
34
24
  const isCancelled = appointment.status === AppointmentStatus.CANCELLED;
25
+ const isScheduled = appointment.status === AppointmentStatus.SCHEDULED;
35
26
 
36
27
  const handleCheckout = () => {
37
28
  if (checkOutButton.customUrl) {
38
- navigate({ to: checkOutButton.customUrl, templateParams: { patientUuid, appointmentUuid: appointment.uuid } });
29
+ navigate({
30
+ to: checkOutButton.customUrl,
31
+ templateParams: {
32
+ patientUuid,
33
+ appointmentUuid: appointment.uuid,
34
+ },
35
+ });
39
36
  } else {
40
37
  const dispose = showModal('end-appointment-modal', {
41
38
  closeModal: () => {
42
- mutateVisit();
39
+ mutateVisits();
43
40
  dispose();
44
41
  },
45
42
  patientUuid,
@@ -48,39 +45,34 @@ const AppointmentsActions: React.FC<AppointmentsActionsProps> = ({ appointment }
48
45
  }
49
46
  };
50
47
 
51
- const renderVisitStatus = () => {
52
- switch (true) {
53
- case isCancelled:
54
- return (
55
- <Button kind="danger--ghost" iconDescription={t('cancelled', 'Cancelled')} size="sm">
56
- {t('cancelled', 'Cancelled')}
57
- </Button>
58
- );
59
- case isCompleted:
60
- return (
61
- <Button kind="ghost" renderIcon={TaskComplete} iconDescription={t('checkedOut', 'Checked out')} size="sm">
62
- {t('checkedOut', 'Checked out')}
63
- </Button>
64
- );
65
- case checkOutButton.enabled && isCheckedIn:
66
- return (
67
- <Button onClick={handleCheckout} kind="danger--tertiary" size="sm">
68
- {t('checkOut', 'Check out')}
69
- </Button>
70
- );
71
- case checkInButton.enabled && (!hasActiveVisitToday || checkInButton.showIfActiveVisit) && isTodaysAppointment: {
72
- return <CheckInButton patientUuid={patientUuid} appointment={appointment} />;
73
- }
74
- default:
75
- return null;
48
+ const renderActions = () => {
49
+ if (isCancelled || isCompleted) {
50
+ return null;
76
51
  }
52
+
53
+ if (checkOutButton.enabled && isCheckedIn) {
54
+ return (
55
+ <Button onClick={handleCheckout} kind="danger--tertiary" size="sm">
56
+ {t('checkOut', 'Check out')}
57
+ </Button>
58
+ );
59
+ }
60
+
61
+ if (isScheduled && checkInButton.enabled) {
62
+ return (
63
+ <CheckInButton
64
+ patientUuid={patientUuid}
65
+ appointment={appointment}
66
+ hasActiveVisit={hasActiveVisit}
67
+ mutateVisits={mutateVisits}
68
+ />
69
+ );
70
+ }
71
+
72
+ return null;
77
73
  };
78
74
 
79
- return (
80
- <div className={styles.container}>
81
- <>{renderVisitStatus()}</>
82
- </div>
83
- );
75
+ return <div className={styles.container}>{renderActions()}</div>;
84
76
  };
85
77
 
86
78
  export default AppointmentsActions;
@@ -1,10 +1,14 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
4
- import { useTodaysVisits } from '../../hooks/useTodaysVisits';
5
4
  import { type ConfigObject, configSchema } from '../../config-schema';
6
5
  import { type Appointment, AppointmentKind, AppointmentStatus } from '../../types';
7
6
  import AppointmentActions from './appointments-actions.component';
7
+ import { changeAppointmentStatus } from '../../patient-appointments/patient-appointments.resource';
8
+ import userEvent from '@testing-library/user-event';
9
+
10
+ jest.mock('../../patient-appointments/patient-appointments.resource');
11
+ const mockChangeAppointmentStatus = changeAppointmentStatus as jest.Mock;
8
12
 
9
13
  const appointment: Appointment = {
10
14
  uuid: '7cd38a6d-377e-491b-8284-b04cf8b8c6d8',
@@ -51,168 +55,117 @@ const appointment: Appointment = {
51
55
  };
52
56
 
53
57
  const defaultProps = {
54
- visits: [],
55
58
  appointment: appointment,
56
- scheduleType: 'Pending',
57
- mutate: () => {},
59
+ hasActiveVisit: false,
58
60
  };
59
61
 
60
62
  const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
61
- const mockUseTodaysVisits = jest.mocked(useTodaysVisits);
62
-
63
- jest.mock('../../hooks/useTodaysVisits', () => ({
64
- ...jest.requireActual('../../hooks/useTodaysVisits'),
65
- useTodaysVisits: jest.fn(),
66
- }));
67
63
 
68
64
  describe('AppointmentActions', () => {
69
65
  afterAll(() => {
70
66
  jest.useRealTimers();
71
67
  });
72
68
 
73
- it('renders the check in button when appointment is today and the patient has not checked in and check in button enabled', () => {
69
+ it('renders the check in button when appointment is scheduled and check in button is enabled', () => {
74
70
  appointment.status = AppointmentStatus.SCHEDULED;
75
71
 
76
72
  mockUseConfig.mockReturnValue({
77
73
  ...getDefaultsFromConfigSchema(configSchema),
78
- checkInButton: { enabled: true, showIfActiveVisit: false, customUrl: '' },
74
+ checkInButton: { enabled: true, customUrl: '' },
79
75
  checkOutButton: { enabled: true, customUrl: '' },
80
76
  });
81
77
 
82
- mockUseTodaysVisits.mockReturnValue({
83
- visits: [],
84
- error: null,
85
- isLoading: false,
86
- mutateVisit: jest.fn(),
87
- });
88
-
89
- const props = { ...defaultProps };
90
- render(<AppointmentActions {...props} />);
78
+ render(<AppointmentActions {...defaultProps} />);
91
79
 
92
80
  expect(screen.getByText(/check in/i)).toBeInTheDocument();
93
81
  });
94
82
 
95
- it('does not render the check in button when appointment is today and the patient has not checked in but the check-in button is disabled', () => {
83
+ it('does not render the check in button when appointment is scheduled but the check-in button is disabled', () => {
96
84
  appointment.status = AppointmentStatus.SCHEDULED;
97
85
 
98
86
  mockUseConfig.mockReturnValue({
99
87
  ...getDefaultsFromConfigSchema(configSchema),
100
- checkInButton: { enabled: false, showIfActiveVisit: false, customUrl: '' },
88
+ checkInButton: { enabled: false, customUrl: '' },
101
89
  checkOutButton: { enabled: true, customUrl: '' },
102
90
  });
103
91
 
104
- mockUseTodaysVisits.mockReturnValue({
105
- visits: [],
106
- error: null,
107
- isLoading: false,
108
- mutateVisit: jest.fn(),
109
- });
110
-
111
- const props = { ...defaultProps };
112
- render(<AppointmentActions {...props} />);
92
+ render(<AppointmentActions {...defaultProps} />);
113
93
 
114
94
  expect(screen.queryByText(/check in/i)).not.toBeInTheDocument();
115
95
  });
116
96
 
117
- it('renders the checked out button when the patient has checked out', () => {
97
+ it('does not render any action buttons when the appointment is completed', () => {
118
98
  appointment.status = AppointmentStatus.COMPLETED;
119
99
 
120
100
  mockUseConfig.mockReturnValue({
121
101
  ...getDefaultsFromConfigSchema(configSchema),
122
- checkInButton: { enabled: true, showIfActiveVisit: false, customUrl: '' },
102
+ checkInButton: { enabled: true, customUrl: '' },
123
103
  checkOutButton: { enabled: true, customUrl: '' },
124
104
  });
125
105
 
126
- mockUseTodaysVisits.mockReturnValue({
127
- visits: [
128
- {
129
- patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
130
- startDatetime: new Date().toISOString(),
131
- stopDatetime: new Date().toISOString(),
132
- uuid: '',
133
- encounters: [],
134
- visitType: {
135
- uuid: '',
136
- display: 'Facility Visit',
137
- },
138
- },
139
- ],
140
- error: null,
141
- isLoading: false,
142
- mutateVisit: jest.fn(),
106
+ render(<AppointmentActions {...defaultProps} />);
107
+
108
+ expect(screen.queryByText(/check in/i)).not.toBeInTheDocument();
109
+ expect(screen.queryByText(/check out/i)).not.toBeInTheDocument();
110
+ });
111
+
112
+ it('does not render any action buttons when the appointment is missed', () => {
113
+ appointment.status = AppointmentStatus.MISSED;
114
+
115
+ mockUseConfig.mockReturnValue({
116
+ ...getDefaultsFromConfigSchema(configSchema),
117
+ checkInButton: { enabled: true, customUrl: '' },
118
+ checkOutButton: { enabled: true, customUrl: '' },
143
119
  });
144
120
 
145
- const props = { ...defaultProps };
146
- render(<AppointmentActions {...props} />);
121
+ render(<AppointmentActions {...defaultProps} />);
147
122
 
148
- expect(screen.getByText('Checked out')).toBeInTheDocument();
123
+ expect(screen.queryByText(/check in/i)).not.toBeInTheDocument();
124
+ expect(screen.queryByText(/check out/i)).not.toBeInTheDocument();
149
125
  });
150
126
 
151
- it('renders the check out button when the patient has an active visit and today is the appointment date and the check out button enabled', () => {
127
+ it('renders the check out button when the appointment status is checked in and the check out button is enabled', () => {
152
128
  appointment.status = AppointmentStatus.CHECKEDIN;
153
129
 
154
130
  mockUseConfig.mockReturnValue({
155
131
  ...getDefaultsFromConfigSchema(configSchema),
156
- checkInButton: { enabled: true, showIfActiveVisit: false, customUrl: '' },
132
+ checkInButton: { enabled: true, customUrl: '' },
157
133
  checkOutButton: { enabled: true, customUrl: '' },
158
134
  });
159
135
 
160
- mockUseTodaysVisits.mockReturnValue({
161
- visits: [
162
- {
163
- patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
164
- startDatetime: new Date().toISOString(),
165
- stopDatetime: null,
166
- uuid: '',
167
- encounters: [],
168
- visitType: {
169
- uuid: '',
170
- display: 'Facility Visit',
171
- },
172
- },
173
- ],
174
- error: null,
175
- isLoading: false,
176
- mutateVisit: jest.fn(),
177
- });
178
-
179
- const props = { ...defaultProps, scheduleType: 'Scheduled' };
180
- render(<AppointmentActions {...props} />);
136
+ render(<AppointmentActions {...defaultProps} />);
181
137
 
182
138
  expect(screen.getByText(/check out/i)).toBeInTheDocument();
183
139
  });
184
140
 
185
- it('does not render check out button when the patient has an active visit and today is the appointment date but the check out button is disabled', () => {
141
+ it('does not render the check out button when the appointment status is checked in but the check out button is disabled', () => {
186
142
  appointment.status = AppointmentStatus.CHECKEDIN;
187
143
 
188
144
  mockUseConfig.mockReturnValue({
189
145
  ...getDefaultsFromConfigSchema(configSchema),
190
- checkInButton: { enabled: true, showIfActiveVisit: false, customUrl: '' },
146
+ checkInButton: { enabled: true, customUrl: '' },
191
147
  checkOutButton: { enabled: false, customUrl: '' },
192
148
  });
193
149
 
194
- mockUseTodaysVisits.mockReturnValue({
195
- visits: [
196
- {
197
- patient: { uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e' },
198
- startDatetime: new Date().toISOString(),
199
- stopDatetime: null,
200
- uuid: '',
201
- encounters: [],
202
- visitType: {
203
- uuid: '',
204
- display: 'Facility Visit',
205
- },
206
- },
207
- ],
208
- error: null,
209
- isLoading: false,
210
- mutateVisit: jest.fn(),
150
+ render(<AppointmentActions {...defaultProps} />);
151
+
152
+ expect(screen.queryByText(/check out/i)).not.toBeInTheDocument();
153
+ });
154
+
155
+ it('calls changeAppointmentStatus when active visit exists and check-in clicked', async () => {
156
+ appointment.status = AppointmentStatus.SCHEDULED;
157
+
158
+ mockUseConfig.mockReturnValue({
159
+ ...getDefaultsFromConfigSchema(configSchema),
160
+ checkInButton: { enabled: true, customUrl: '' },
211
161
  });
212
162
 
213
- const props = { ...defaultProps, scheduleType: 'Scheduled' };
214
- render(<AppointmentActions {...props} />);
163
+ mockChangeAppointmentStatus.mockResolvedValue({});
164
+ render(<AppointmentActions {...defaultProps} hasActiveVisit={true} />);
215
165
 
216
- expect(screen.queryByText(/check out/i)).not.toBeInTheDocument();
166
+ const btn = screen.getByRole('button', { name: /check in/i });
167
+ await userEvent.click(btn);
168
+
169
+ expect(mockChangeAppointmentStatus).toHaveBeenCalledWith('CheckedIn', appointment.uuid);
217
170
  });
218
171
  });