@openmrs/esm-appointments-app 8.2.2-pre.4806 → 8.2.2-pre.4812

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 (44) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/dist/2753.js +1 -1
  3. package/dist/2753.js.map +1 -1
  4. package/dist/2923.js +1 -1
  5. package/dist/310.js +1 -1
  6. package/dist/3260.js +1 -1
  7. package/dist/3260.js.map +1 -1
  8. package/dist/3574.js +1 -1
  9. package/dist/3581.js +1 -1
  10. package/dist/3952.js +1 -1
  11. package/dist/3952.js.map +1 -1
  12. package/dist/529.js +1 -1
  13. package/dist/5910.js +1 -1
  14. package/dist/6179.js +1 -1
  15. package/dist/646.js +1 -1
  16. package/dist/681.js +1 -1
  17. package/dist/681.js.map +1 -1
  18. package/dist/8607.js +1 -1
  19. package/dist/8607.js.map +1 -1
  20. package/dist/8615.js +2 -0
  21. package/dist/8615.js.map +1 -0
  22. package/dist/9190.js +1 -1
  23. package/dist/9269.js +1 -1
  24. package/dist/main.js +1 -1
  25. package/dist/openmrs-esm-appointments-app.js.buildmanifest.json +56 -56
  26. package/dist/routes.json +1 -1
  27. package/package.json +1 -1
  28. package/src/appointments/appointment-tabs.component.tsx +1 -1
  29. package/src/appointments/common-components/appointments-table.component.tsx +22 -1
  30. package/src/appointments/common-components/appointments-table.scss +26 -0
  31. package/src/appointments/scheduled/appointments-list.component.tsx +9 -2
  32. package/src/appointments/scheduled/early-appointments.component.tsx +1 -1
  33. package/src/appointments/scheduled/scheduled-appointments.component.tsx +2 -2
  34. package/src/appointments/utils.tsx +3 -3
  35. package/src/appointments.component.tsx +2 -2
  36. package/src/appointments.test.tsx +1 -6
  37. package/src/header/appointments-header.component.tsx +26 -17
  38. package/src/header/appointments-header.scss +0 -11
  39. package/src/hooks/useClinicalMetrics.ts +2 -2
  40. package/src/metrics/appointments-metrics.component.tsx +3 -3
  41. package/translations/en.json +2 -2
  42. package/dist/8199.js +0 -2
  43. package/dist/8199.js.map +0 -1
  44. /package/dist/{8199.js.LICENSE.txt → 8615.js.LICENSE.txt} +0 -0
@@ -76,7 +76,7 @@
76
76
  "auxiliaryFiles": [
77
77
  "main.js.map"
78
78
  ],
79
- "hash": "47e9afa204692480",
79
+ "hash": "324564b6857fa239",
80
80
  "childrenByOrder": {}
81
81
  },
82
82
  {
@@ -143,7 +143,7 @@
143
143
  "auxiliaryFiles": [
144
144
  "310.js.map"
145
145
  ],
146
- "hash": "5522307347b4a404",
146
+ "hash": "cc3dbaaa3697652a",
147
147
  "childrenByOrder": {}
148
148
  },
149
149
  {
@@ -194,7 +194,7 @@
194
194
  "auxiliaryFiles": [
195
195
  "529.js.map"
196
196
  ],
197
- "hash": "45b7b6ad2ffc8312",
197
+ "hash": "1204bd6eb44a653c",
198
198
  "childrenByOrder": {}
199
199
  },
200
200
  {
@@ -241,7 +241,7 @@
241
241
  "auxiliaryFiles": [
242
242
  "646.js.map"
243
243
  ],
244
- "hash": "603c3b66f3a0ec2d",
244
+ "hash": "236f43bea98d5898",
245
245
  "childrenByOrder": {}
246
246
  },
247
247
  {
@@ -250,9 +250,9 @@
250
250
  "entry": false,
251
251
  "recorded": false,
252
252
  "reason": "split chunk (cache group: default)",
253
- "size": 68064,
253
+ "size": 68137,
254
254
  "sizes": {
255
- "javascript": 68064
255
+ "javascript": 68137
256
256
  },
257
257
  "names": [],
258
258
  "idHints": [],
@@ -266,7 +266,7 @@
266
266
  "auxiliaryFiles": [
267
267
  "681.js.map"
268
268
  ],
269
- "hash": "6bf67281ff24bf8d",
269
+ "hash": "562e30022ae30ad0",
270
270
  "childrenByOrder": {}
271
271
  },
272
272
  {
@@ -486,9 +486,9 @@
486
486
  "entry": false,
487
487
  "recorded": false,
488
488
  "reason": "reused as split chunk (cache group: default)",
489
- "size": 167248,
489
+ "size": 166608,
490
490
  "sizes": {
491
- "javascript": 167248
491
+ "javascript": 166608
492
492
  },
493
493
  "names": [],
494
494
  "idHints": [],
@@ -502,7 +502,7 @@
502
502
  "auxiliaryFiles": [
503
503
  "2753.js.map"
504
504
  ],
505
- "hash": "0526b3b117252c2a",
505
+ "hash": "f4d4088863422ff7",
506
506
  "childrenByOrder": {}
507
507
  },
508
508
  {
@@ -572,7 +572,7 @@
572
572
  "auxiliaryFiles": [
573
573
  "2923.js.map"
574
574
  ],
575
- "hash": "1c63e5781604a9c4",
575
+ "hash": "6eaac8a3845d5031",
576
576
  "childrenByOrder": {}
577
577
  },
578
578
  {
@@ -581,9 +581,9 @@
581
581
  "entry": false,
582
582
  "recorded": false,
583
583
  "reason": "split chunk (cache group: default)",
584
- "size": 134784,
584
+ "size": 137732,
585
585
  "sizes": {
586
- "javascript": 134784
586
+ "javascript": 137732
587
587
  },
588
588
  "names": [],
589
589
  "idHints": [],
@@ -597,7 +597,7 @@
597
597
  "auxiliaryFiles": [
598
598
  "3260.js.map"
599
599
  ],
600
- "hash": "2d4ea01b6e6c4fe7",
600
+ "hash": "7ca1610aacfa1c8d",
601
601
  "childrenByOrder": {}
602
602
  },
603
603
  {
@@ -629,9 +629,9 @@
629
629
  "initial": false,
630
630
  "entry": false,
631
631
  "recorded": false,
632
- "size": 6545,
632
+ "size": 6619,
633
633
  "sizes": {
634
- "javascript": 6545
634
+ "javascript": 6619
635
635
  },
636
636
  "names": [],
637
637
  "idHints": [],
@@ -643,7 +643,7 @@
643
643
  "3574.js"
644
644
  ],
645
645
  "auxiliaryFiles": [],
646
- "hash": "2bacc9d49359e79b",
646
+ "hash": "122d890a7300d845",
647
647
  "childrenByOrder": {}
648
648
  },
649
649
  {
@@ -667,7 +667,7 @@
667
667
  "auxiliaryFiles": [
668
668
  "3581.js.map"
669
669
  ],
670
- "hash": "cca678716be7f710",
670
+ "hash": "5515b477ae0e84ac",
671
671
  "childrenByOrder": {}
672
672
  },
673
673
  {
@@ -742,9 +742,9 @@
742
742
  "initial": false,
743
743
  "entry": false,
744
744
  "recorded": false,
745
- "size": 7604,
745
+ "size": 7829,
746
746
  "sizes": {
747
- "javascript": 7604
747
+ "javascript": 7829
748
748
  },
749
749
  "names": [],
750
750
  "idHints": [],
@@ -758,7 +758,7 @@
758
758
  "auxiliaryFiles": [
759
759
  "3952.js.map"
760
760
  ],
761
- "hash": "7c2705618fe60645",
761
+ "hash": "0664f1260b8f005c",
762
762
  "childrenByOrder": {}
763
763
  },
764
764
  {
@@ -1074,7 +1074,7 @@
1074
1074
  "auxiliaryFiles": [
1075
1075
  "5910.js.map"
1076
1076
  ],
1077
- "hash": "6f1be25d29e57082",
1077
+ "hash": "638eb1a87faa2f41",
1078
1078
  "childrenByOrder": {}
1079
1079
  },
1080
1080
  {
@@ -1140,7 +1140,7 @@
1140
1140
  "auxiliaryFiles": [
1141
1141
  "6179.js.map"
1142
1142
  ],
1143
- "hash": "36e9ef89c72bc1ec",
1143
+ "hash": "d81a6bf0c71d664a",
1144
1144
  "childrenByOrder": {}
1145
1145
  },
1146
1146
  {
@@ -1312,33 +1312,6 @@
1312
1312
  "hash": "71388d7a2d62d9b3",
1313
1313
  "childrenByOrder": {}
1314
1314
  },
1315
- {
1316
- "rendered": true,
1317
- "initial": false,
1318
- "entry": false,
1319
- "recorded": false,
1320
- "reason": "split chunk (cache group: defaultVendors)",
1321
- "size": 2766488,
1322
- "sizes": {
1323
- "javascript": 2766488
1324
- },
1325
- "names": [],
1326
- "idHints": [
1327
- "vendors"
1328
- ],
1329
- "runtime": [
1330
- "@openmrs/esm-appointments-app",
1331
- "main"
1332
- ],
1333
- "files": [
1334
- "8199.js"
1335
- ],
1336
- "auxiliaryFiles": [
1337
- "8199.js.map"
1338
- ],
1339
- "hash": "c372b981c3e0c247",
1340
- "childrenByOrder": {}
1341
- },
1342
1315
  {
1343
1316
  "rendered": true,
1344
1317
  "initial": false,
@@ -1437,9 +1410,9 @@
1437
1410
  "initial": false,
1438
1411
  "entry": false,
1439
1412
  "recorded": false,
1440
- "size": 2166,
1413
+ "size": 2391,
1441
1414
  "sizes": {
1442
- "javascript": 2166
1415
+ "javascript": 2391
1443
1416
  },
1444
1417
  "names": [],
1445
1418
  "idHints": [],
@@ -1453,7 +1426,34 @@
1453
1426
  "auxiliaryFiles": [
1454
1427
  "8607.js.map"
1455
1428
  ],
1456
- "hash": "ca5ea82f827a6124",
1429
+ "hash": "05250d2611fbeb76",
1430
+ "childrenByOrder": {}
1431
+ },
1432
+ {
1433
+ "rendered": true,
1434
+ "initial": false,
1435
+ "entry": false,
1436
+ "recorded": false,
1437
+ "reason": "split chunk (cache group: defaultVendors)",
1438
+ "size": 2747132,
1439
+ "sizes": {
1440
+ "javascript": 2747132
1441
+ },
1442
+ "names": [],
1443
+ "idHints": [
1444
+ "vendors"
1445
+ ],
1446
+ "runtime": [
1447
+ "@openmrs/esm-appointments-app",
1448
+ "main"
1449
+ ],
1450
+ "files": [
1451
+ "8615.js"
1452
+ ],
1453
+ "auxiliaryFiles": [
1454
+ "8615.js.map"
1455
+ ],
1456
+ "hash": "490b9a5cbee87430",
1457
1457
  "childrenByOrder": {}
1458
1458
  },
1459
1459
  {
@@ -1553,7 +1553,7 @@
1553
1553
  "auxiliaryFiles": [
1554
1554
  "9190.js.map"
1555
1555
  ],
1556
- "hash": "d5bd64f357d15e83",
1556
+ "hash": "4d76c8a34af8a0f4",
1557
1557
  "childrenByOrder": {}
1558
1558
  },
1559
1559
  {
@@ -1599,7 +1599,7 @@
1599
1599
  "auxiliaryFiles": [
1600
1600
  "9269.js.map"
1601
1601
  ],
1602
- "hash": "72bc4a4443a32860",
1602
+ "hash": "b24bdb573a29497d",
1603
1603
  "childrenByOrder": {}
1604
1604
  },
1605
1605
  {
@@ -1717,7 +1717,7 @@
1717
1717
  "auxiliaryFiles": [
1718
1718
  "9890.js.map"
1719
1719
  ],
1720
- "hash": "9926c3a2bda44fcd",
1720
+ "hash": "5b07c240725e2ac4",
1721
1721
  "childrenByOrder": {}
1722
1722
  }
1723
1723
  ]
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0"},"extensions":[{"name":"home-appointments","slot":"homepage-widgets-slot","component":"homeAppointments","order":1},{"name":"clinical-appointments-dashboard-link","slot":"homepage-dashboard-slot","component":"appointmentsDashboardLink","meta":{"name":"appointments","slot":"clinical-appointments-dashboard-slot","title":"Appointments"}},{"component":"root","name":"clinical-appointments-dashboard","slot":"clinical-appointments-dashboard-slot"},{"name":"appointments-calendar-dashboard-link","slot":"calendar-dashboard-slot","component":"appointmentsCalendarDashboardLink"},{"name":"todays-appointments-dashboard","slot":"todays-appointment-slot","component":"homeAppointments"},{"name":"expected-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"checked-in-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"completed-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"missed-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"cancelled-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"early-appointments-panel","component":"earlyAppointments"},{"name":"appointments-form-workspace","component":"appointmentsFormWorkspace","meta":{"title":{"key":"createNewAppointment","default":"Create new appointment"}}},{"name":"patient-appointments-summary-dashboard","component":"patientAppointmentsSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"hideDashboardTitle":true,"path":"Appointments","slot":"patient-chart-appointments-dashboard-slot","title":"Appointments"}},{"name":"patientAppointments-details-widget","component":"patientAppointmentsDetailedSummary","slot":"patient-chart-appointments-dashboard-slot","meta":{"columnSpan":1}},{"name":"patient-upcoming-appointment-widget","component":"patientUpcomingAppointmentsWidget","slot":"visit-form-top-slot"},{"name":"edit-appointments-form","component":"appointmentsForm","meta":{"title":{"key":"editAppointment","default":"Edit Appointment"}}},{"name":"search-patient","component":"searchPatient"},{"name":"create-appointment","component":"appointmentsForm","meta":{"title":{"key":"appointmentForm","default":"Appointment Form"}}},{"name":"add-appointment","component":"appointmentsForm","meta":{"title":{"key":"createNewAppointment","default":"Create new appointment"}}},{"name":"home-appointments-tile","slot":"home-metrics-tiles-slot","component":"homeAppointmentsTile"}],"modals":[{"name":"end-appointment-modal","component":"endAppointmentModal"},{"name":"patient-appointment-cancel-confirmation-dialog","component":"patientAppointmentsCancelConfirmationDialog"}],"version":"8.2.2-pre.4806"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0"},"extensions":[{"name":"home-appointments","slot":"homepage-widgets-slot","component":"homeAppointments","order":1},{"name":"clinical-appointments-dashboard-link","slot":"homepage-dashboard-slot","component":"appointmentsDashboardLink","meta":{"name":"appointments","slot":"clinical-appointments-dashboard-slot","title":"Appointments"}},{"component":"root","name":"clinical-appointments-dashboard","slot":"clinical-appointments-dashboard-slot"},{"name":"appointments-calendar-dashboard-link","slot":"calendar-dashboard-slot","component":"appointmentsCalendarDashboardLink"},{"name":"todays-appointments-dashboard","slot":"todays-appointment-slot","component":"homeAppointments"},{"name":"expected-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"checked-in-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"completed-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"missed-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"cancelled-appointments-panel","slot":"scheduled-appointments-panels-slot","component":"appointmentsList"},{"name":"early-appointments-panel","component":"earlyAppointments"},{"name":"appointments-form-workspace","component":"appointmentsFormWorkspace","meta":{"title":{"key":"createNewAppointment","default":"Create new appointment"}}},{"name":"patient-appointments-summary-dashboard","component":"patientAppointmentsSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"hideDashboardTitle":true,"path":"Appointments","slot":"patient-chart-appointments-dashboard-slot","title":"Appointments"}},{"name":"patientAppointments-details-widget","component":"patientAppointmentsDetailedSummary","slot":"patient-chart-appointments-dashboard-slot","meta":{"columnSpan":1}},{"name":"patient-upcoming-appointment-widget","component":"patientUpcomingAppointmentsWidget","slot":"visit-form-top-slot"},{"name":"edit-appointments-form","component":"appointmentsForm","meta":{"title":{"key":"editAppointment","default":"Edit Appointment"}}},{"name":"search-patient","component":"searchPatient"},{"name":"create-appointment","component":"appointmentsForm","meta":{"title":{"key":"appointmentForm","default":"Appointment Form"}}},{"name":"add-appointment","component":"appointmentsForm","meta":{"title":{"key":"createNewAppointment","default":"Create new appointment"}}},{"name":"home-appointments-tile","slot":"home-metrics-tiles-slot","component":"homeAppointmentsTile"}],"modals":[{"name":"end-appointment-modal","component":"endAppointmentModal"},{"name":"patient-appointment-cancel-confirmation-dialog","component":"patientAppointmentsCancelConfirmationDialog"}],"version":"8.2.2-pre.4812"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-appointments-app",
3
- "version": "8.2.2-pre.4806",
3
+ "version": "8.2.2-pre.4812",
4
4
  "description": "Appointments front-end module for O3",
5
5
  "browser": "dist/openmrs-esm-appointments-app.js",
6
6
  "main": "src/index.ts",
@@ -8,7 +8,7 @@ import UnscheduledAppointments from './unscheduled/unscheduled-appointments.comp
8
8
  import styles from './appointment-tabs.scss';
9
9
 
10
10
  interface AppointmentTabsProps {
11
- appointmentServiceType: string;
11
+ appointmentServiceType: string[];
12
12
  }
13
13
 
14
14
  const AppointmentTabs: React.FC<AppointmentTabsProps> = ({ appointmentServiceType }) => {
@@ -53,9 +53,15 @@ interface AppointmentsTableProps {
53
53
  appointments: Array<Appointment>;
54
54
  isLoading: boolean;
55
55
  tableHeading: string;
56
+ hasActiveFilters?: boolean;
56
57
  }
57
58
 
58
- const AppointmentsTable: React.FC<AppointmentsTableProps> = ({ appointments, isLoading, tableHeading }) => {
59
+ const AppointmentsTable: React.FC<AppointmentsTableProps> = ({
60
+ appointments,
61
+ isLoading,
62
+ tableHeading,
63
+ hasActiveFilters,
64
+ }) => {
59
65
  const { t } = useTranslation();
60
66
  const [pageSize, setPageSize] = useState(25);
61
67
  const [searchString, setSearchString] = useState('');
@@ -113,6 +119,21 @@ const AppointmentsTable: React.FC<AppointmentsTableProps> = ({ appointments, isL
113
119
  return <DataTableSkeleton role="progressbar" row={5} />;
114
120
  }
115
121
 
122
+ if (hasActiveFilters && !appointments?.length) {
123
+ return (
124
+ <div className={styles.filterEmptyState}>
125
+ <Layer level={0}>
126
+ <Tile className={styles.filterEmptyStateTile}>
127
+ <p className={styles.filterEmptyStateContent}>
128
+ {t('noMatchingAppointments', 'No matching appointments found')}
129
+ </p>
130
+ <p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
131
+ </Tile>
132
+ </Layer>
133
+ </div>
134
+ );
135
+ }
136
+
116
137
  if (!appointments?.length) {
117
138
  return (
118
139
  <EmptyState
@@ -131,3 +131,29 @@ html[dir='rtl'] {
131
131
  }
132
132
  }
133
133
  }
134
+
135
+ .filterEmptyState {
136
+ display: flex;
137
+ justify-content: center;
138
+ align-items: center;
139
+ margin: layout.$spacing-05 0;
140
+ padding: layout.$spacing-08 0;
141
+ text-align: center;
142
+ border: 1px solid $ui-03;
143
+ }
144
+
145
+ .filterEmptyStateTile {
146
+ margin: auto;
147
+ width: fit-content;
148
+ }
149
+
150
+ .filterEmptyStateContent {
151
+ @include type.type-style('heading-compact-02');
152
+ color: $text-02;
153
+ margin-bottom: layout.$spacing-03;
154
+ }
155
+
156
+ .filterEmptyStateHelper {
157
+ @include type.type-style('body-compact-01');
158
+ color: $text-02;
159
+ }
@@ -4,7 +4,7 @@ import { useAppointmentList } from '../../hooks/useAppointmentList';
4
4
  import AppointmentsTable from '../common-components/appointments-table.component';
5
5
 
6
6
  interface AppointmentsListProps {
7
- appointmentServiceType?: string;
7
+ appointmentServiceType?: string[];
8
8
  status?: string;
9
9
  title: string;
10
10
  date: string;
@@ -27,7 +27,14 @@ const AppointmentsList: React.FC<AppointmentsListProps> = ({
27
27
  const activeAppointments = filterCancelled
28
28
  ? appointments.filter((appointment) => appointment.status !== 'Cancelled')
29
29
  : appointments;
30
- return <AppointmentsTable appointments={activeAppointments} isLoading={isLoading} tableHeading={title} />;
30
+ return (
31
+ <AppointmentsTable
32
+ appointments={activeAppointments}
33
+ isLoading={isLoading}
34
+ tableHeading={title}
35
+ hasActiveFilters={appointmentServiceType?.length > 0 || filterCancelled}
36
+ />
37
+ );
31
38
  };
32
39
 
33
40
  export default AppointmentsList;
@@ -5,7 +5,7 @@ import AppointmentsTable from '../common-components/appointments-table.component
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  interface EarlyAppointmentsProps {
8
- appointmentServiceType?: string;
8
+ appointmentServiceType?: string[];
9
9
  date: string;
10
10
  }
11
11
 
@@ -18,7 +18,7 @@ import styles from './scheduled-appointments.scss';
18
18
  dayjs.extend(isSameOrBefore);
19
19
 
20
20
  interface ScheduledAppointmentsProps {
21
- appointmentServiceType?: string;
21
+ appointmentServiceType?: string[];
22
22
  }
23
23
 
24
24
  type DateType = 'pastDate' | 'today' | 'futureDate';
@@ -141,7 +141,7 @@ function ExtensionWrapper({
141
141
  }: {
142
142
  extension: ConnectedExtension;
143
143
  currentTab: string;
144
- appointmentServiceType: string;
144
+ appointmentServiceType: string[];
145
145
  date: string;
146
146
  dateType: DateType;
147
147
  showExtensionTab: (extension: string) => void;
@@ -73,8 +73,8 @@ export function useAppointmentSearchResults(data: Appointment[], searchString: s
73
73
  }, [searchString, data]);
74
74
  }
75
75
 
76
- export function filterByServiceType(appointmentList: any[], appointmentServiceType: string) {
77
- return appointmentServiceType
78
- ? appointmentList.filter(({ service }) => service?.uuid === appointmentServiceType)
76
+ export function filterByServiceType(appointmentList: any[], appointmentServiceType: string[]) {
77
+ return appointmentServiceType?.length > 0
78
+ ? appointmentList.filter(({ service }) => appointmentServiceType.includes(service.uuid))
79
79
  : appointmentList;
80
80
  }
@@ -10,7 +10,7 @@ import SelectedDateContext from './hooks/selectedDateContext';
10
10
 
11
11
  const Appointments: React.FC = () => {
12
12
  const { t } = useTranslation();
13
- const [appointmentServiceType, setAppointmentServiceType] = useState('');
13
+ const [appointmentServiceType, setAppointmentServiceType] = useState<string[]>([]);
14
14
  const [selectedDate, setSelectedDate] = useState(dayjs().startOf('day').format(omrsDateFormat));
15
15
 
16
16
  const params = useParams();
@@ -23,7 +23,7 @@ const Appointments: React.FC = () => {
23
23
 
24
24
  useEffect(() => {
25
25
  if (params.serviceType) {
26
- setAppointmentServiceType(params.serviceType);
26
+ setAppointmentServiceType([params.serviceType]);
27
27
  }
28
28
  }, [params.serviceType]);
29
29
 
@@ -11,12 +11,7 @@ describe('Appointments', () => {
11
11
 
12
12
  expect(screen.getByRole('button', { name: /appointments calendar/i })).toBeInTheDocument();
13
13
  expect(screen.getByPlaceholderText(/dd-mmm-yyyy/i)).toBeInTheDocument();
14
- expect(
15
- screen.getByRole('combobox', {
16
- name: /select service type/i,
17
- }),
18
- ).toBeInTheDocument();
19
- expect(screen.getByRole('listbox', { name: /view/i })).toBeInTheDocument();
14
+ expect(screen.getByText(/filter appointments by service type/i)).toBeInTheDocument();
20
15
  expect(screen.getByText(/appointment metrics/i)).toBeInTheDocument();
21
16
  expect(screen.getByText(/scheduled appointments/i)).toBeInTheDocument();
22
17
  expect(screen.getByText(/highest volume service/i)).toBeInTheDocument();
@@ -1,7 +1,7 @@
1
- import React, { useContext } from 'react';
1
+ import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
2
2
  import dayjs from 'dayjs';
3
3
  import { useTranslation } from 'react-i18next';
4
- import { DatePicker, DatePickerInput, Dropdown } from '@carbon/react';
4
+ import { DatePicker, DatePickerInput, MultiSelect } from '@carbon/react';
5
5
  import { PageHeader, PageHeaderContent, AppointmentsPictogram } from '@openmrs/esm-framework';
6
6
  import { omrsDateFormat } from '../constants';
7
7
  import { useAppointmentServices } from '../hooks/useAppointmentService';
@@ -10,7 +10,7 @@ import styles from './appointments-header.scss';
10
10
 
11
11
  interface AppointmentHeaderProps {
12
12
  title: string;
13
- appointmentServiceType?: string;
13
+ appointmentServiceType?: string[];
14
14
  onChange?: (evt) => void;
15
15
  }
16
16
 
@@ -19,6 +19,22 @@ const AppointmentsHeader: React.FC<AppointmentHeaderProps> = ({ title, appointme
19
19
  const { selectedDate, setSelectedDate } = useContext(SelectedDateContext);
20
20
  const { serviceTypes } = useAppointmentServices();
21
21
 
22
+ const [selectedItems, setSelectedItems] = useState([]);
23
+
24
+ const handleMultiSelectChange = useCallback(
25
+ ({ selectedItems }) => {
26
+ const selectedUuids = selectedItems.map((item) => item.id);
27
+ setSelectedItems(selectedUuids);
28
+ onChange?.(selectedUuids);
29
+ },
30
+ [onChange],
31
+ );
32
+
33
+ const serviceTypeOptions = useMemo(
34
+ () => serviceTypes?.map((item) => ({ id: item.uuid, label: item.name })) ?? [],
35
+ [serviceTypes],
36
+ );
37
+
22
38
  return (
23
39
  <PageHeader className={styles.header} data-testid="appointments-header">
24
40
  <PageHeaderContent illustration={<AppointmentsPictogram />} title={title} />
@@ -37,20 +53,13 @@ const AppointmentsHeader: React.FC<AppointmentHeaderProps> = ({ title, appointme
37
53
  />
38
54
  </DatePicker>
39
55
  {typeof onChange === 'function' && (
40
- <Dropdown
41
- aria-label={t('selectServiceType', 'Select service type')}
42
- className={styles.dropdown}
43
- direction="bottom"
44
- id="serviceDropdown"
45
- items={[{ name: 'All', uuid: '' }, ...serviceTypes]}
46
- itemToString={(item) => (item ? item.name : '')}
47
- label={t('selectServiceType', 'Select service type')}
48
- onChange={({ selectedItem }) => onChange(selectedItem?.uuid)}
49
- selectedItem={
50
- serviceTypes.find((service) => service.uuid === appointmentServiceType) || { name: 'All', uuid: '' }
51
- }
52
- size="sm"
53
- titleText={t('view', 'View')}
56
+ <MultiSelect
57
+ id="serviceTypeMultiSelect"
58
+ label={t('filterAppointmentsByServiceType', 'Filter appointments by service type')}
59
+ items={serviceTypeOptions}
60
+ itemToString={(item) => (item ? item.label : '')}
61
+ onChange={handleMultiSelectChange}
62
+ initialSelectedItems={serviceTypeOptions.length > 0 ? [serviceTypeOptions[0].id] : []}
54
63
  type="inline"
55
64
  />
56
65
  )}
@@ -22,17 +22,6 @@
22
22
  row-gap: layout.$spacing-01;
23
23
  }
24
24
 
25
- .dropdown {
26
- :global(.cds--list-box__field) {
27
- width: 14rem;
28
- overflow: hidden;
29
- }
30
-
31
- :global(.cds--dropdown--inline .cds--list-box__menu) {
32
- left: -(layout.$spacing-10);
33
- }
34
- }
35
-
36
25
  // Overriding styles for RTL support
37
26
  html[dir='rtl'] {
38
27
  .date-and-location {
@@ -61,7 +61,7 @@ export function useAllAppointmentsByDate() {
61
61
  };
62
62
  }
63
63
 
64
- export const useScheduledAppointment = (serviceUuid: string) => {
64
+ export const useScheduledAppointment = (serviceUuid: string[]) => {
65
65
  const { selectedDate } = useContext(SelectedDateContext);
66
66
  const url = `${restBaseUrl}/appointment/all?forDate=${selectedDate}`;
67
67
 
@@ -70,7 +70,7 @@ export const useScheduledAppointment = (serviceUuid: string) => {
70
70
  }>(url, openmrsFetch);
71
71
 
72
72
  const totalScheduledAppointments = !isEmpty(serviceUuid)
73
- ? data?.data?.filter((appt) => appt?.service?.uuid === serviceUuid)?.length ?? 0
73
+ ? data?.data?.filter((appt) => serviceUuid.includes(appt?.service?.uuid))?.length ?? 0
74
74
  : data?.data?.length ?? 0;
75
75
 
76
76
  return {
@@ -9,7 +9,7 @@ import styles from './appointments-metrics.scss';
9
9
  import SelectedDateContext from '../hooks/selectedDateContext';
10
10
 
11
11
  interface AppointmentMetricsProps {
12
- appointmentServiceType: string;
12
+ appointmentServiceType: string[];
13
13
  }
14
14
 
15
15
  const AppointmentsMetrics: React.FC<AppointmentMetricsProps> = ({ appointmentServiceType }) => {
@@ -27,10 +27,10 @@ const AppointmentsMetrics: React.FC<AppointmentMetricsProps> = ({ appointmentSer
27
27
  const { appointmentList: pendingAppointments } = useAppointmentList('Scheduled');
28
28
 
29
29
  const filteredArrivedAppointments = appointmentServiceType
30
- ? arrivedAppointments.filter(({ service }) => service.uuid === appointmentServiceType)
30
+ ? arrivedAppointments.filter(({ service }) => appointmentServiceType.includes(service.uuid))
31
31
  : arrivedAppointments;
32
32
  const filteredPendingAppointments = appointmentServiceType
33
- ? pendingAppointments.filter(({ service }) => service.uuid === appointmentServiceType)
33
+ ? pendingAppointments.filter(({ service }) => appointmentServiceType.includes(service.uuid))
34
34
  : pendingAppointments;
35
35
 
36
36
  if (error) {
@@ -89,6 +89,7 @@
89
89
  "endTime": "End Time",
90
90
  "errorCreatingAppointmentService": "Error creating appointment service",
91
91
  "expected": "Expected",
92
+ "filterAppointmentsByServiceType": "Filter appointments by service type",
92
93
  "filterTable": "Filter table",
93
94
  "gender": "Gender",
94
95
  "highestServiceVolume": "Highest volume service: {{time}}",
@@ -108,6 +109,7 @@
108
109
  "noContent": "No Content",
109
110
  "noCurrentAppointments": "There are no appointments scheduled for today to display for this patient",
110
111
  "noEncountersFound": "No encounters found",
112
+ "noMatchingAppointments": "No matching appointments found",
111
113
  "noPastAppointments": "There are no past appointments to display for this patient",
112
114
  "noPreviousVisitFound": "No previous visit found",
113
115
  "notArrived": "Not arrived",
@@ -143,7 +145,6 @@
143
145
  "selectOption": "Select an option",
144
146
  "selectProvider": "Select a provider",
145
147
  "selectService": "Select a service",
146
- "selectServiceType": "Select service type",
147
148
  "service": "Service",
148
149
  "serviceName": "Service name",
149
150
  "serviceType": "Service Type",
@@ -161,7 +162,6 @@
161
162
  "upcoming": "Upcoming",
162
163
  "upcomingAppointments": "Upcoming appointments",
163
164
  "updateError": "Error updating upcoming appointment",
164
- "view": "View",
165
165
  "vitals": "Vitals",
166
166
  "week": "Week",
167
167
  "yes": "Yes"