@openmrs/esm-cohort-builder-app 3.0.0

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 (103) hide show
  1. package/LICENSE +401 -0
  2. package/README.md +16 -0
  3. package/dist/130.js +2 -0
  4. package/dist/130.js.LICENSE.txt +9 -0
  5. package/dist/139.js +1 -0
  6. package/dist/247.js +1 -0
  7. package/dist/294.js +2 -0
  8. package/dist/294.js.LICENSE.txt +9 -0
  9. package/dist/434.js +2 -0
  10. package/dist/434.js.LICENSE.txt +12 -0
  11. package/dist/480.js +2 -0
  12. package/dist/480.js.LICENSE.txt +12 -0
  13. package/dist/484.js +1 -0
  14. package/dist/574.js +1 -0
  15. package/dist/595.js +2 -0
  16. package/dist/595.js.LICENSE.txt +3 -0
  17. package/dist/690.js +1 -0
  18. package/dist/873.js +1 -0
  19. package/dist/935.js +2 -0
  20. package/dist/935.js.LICENSE.txt +19 -0
  21. package/dist/964.js +2 -0
  22. package/dist/964.js.LICENSE.txt +14 -0
  23. package/dist/main.js +1 -0
  24. package/dist/openmrs-esm-cohort-builder-app.js +1 -0
  25. package/dist/openmrs-esm-cohort-builder-app.js.buildmanifest.json +426 -0
  26. package/dist/openmrs-esm-cohort-builder-app.old +1 -0
  27. package/package.json +99 -0
  28. package/src/cohort-builder-app-menu-link.tsx +14 -0
  29. package/src/cohort-builder.resources.ts +84 -0
  30. package/src/cohort-builder.scss +126 -0
  31. package/src/cohort-builder.test.tsx +12 -0
  32. package/src/cohort-builder.tsx +213 -0
  33. package/src/cohort-builder.utils.ts +197 -0
  34. package/src/components/composition/composition.component.tsx +109 -0
  35. package/src/components/composition/composition.style.css +3 -0
  36. package/src/components/composition/composition.test.tsx +112 -0
  37. package/src/components/composition/composition.utils.ts +53 -0
  38. package/src/components/empty-data/empty-data.component.tsx +25 -0
  39. package/src/components/empty-data/empty-data.style.scss +19 -0
  40. package/src/components/saved-cohorts/saved-cohorts-options/saved-cohort-options.test.tsx +49 -0
  41. package/src/components/saved-cohorts/saved-cohorts-options/saved-cohorts-options.component.tsx +112 -0
  42. package/src/components/saved-cohorts/saved-cohorts.component.tsx +139 -0
  43. package/src/components/saved-cohorts/saved-cohorts.resources.ts +39 -0
  44. package/src/components/saved-cohorts/saved-cohorts.scss +15 -0
  45. package/src/components/saved-cohorts/saved-cohorts.test.tsx +51 -0
  46. package/src/components/saved-queries/saved-queries-options/saved-queries-options.component.tsx +112 -0
  47. package/src/components/saved-queries/saved-queries-options/saved-queries-options.test.tsx +47 -0
  48. package/src/components/saved-queries/saved-queries.component.tsx +139 -0
  49. package/src/components/saved-queries/saved-queries.resources.ts +39 -0
  50. package/src/components/saved-queries/saved-queries.scss +23 -0
  51. package/src/components/saved-queries/saved-queries.test.tsx +50 -0
  52. package/src/components/search-button-set/search-button-set.css +9 -0
  53. package/src/components/search-button-set/search-button-set.test.tsx +26 -0
  54. package/src/components/search-button-set/search-button-set.tsx +47 -0
  55. package/src/components/search-by-concepts/search-by-concepts.component.tsx +344 -0
  56. package/src/components/search-by-concepts/search-by-concepts.style.scss +48 -0
  57. package/src/components/search-by-concepts/search-by-concepts.test.tsx +129 -0
  58. package/src/components/search-by-concepts/search-concept/search-concept.component.tsx +130 -0
  59. package/src/components/search-by-concepts/search-concept/search-concept.resource.ts +53 -0
  60. package/src/components/search-by-concepts/search-concept/search-concept.style.css +25 -0
  61. package/src/components/search-by-concepts/search-concept/search-concept.test.tsx +89 -0
  62. package/src/components/search-by-demographics/search-by-demographics.component.tsx +209 -0
  63. package/src/components/search-by-demographics/search-by-demographics.style.scss +41 -0
  64. package/src/components/search-by-demographics/search-by-demographics.test.tsx +92 -0
  65. package/src/components/search-by-demographics/search-by-demographics.utils.ts +129 -0
  66. package/src/components/search-by-drug-orders/search-by-drug-orders.component.tsx +185 -0
  67. package/src/components/search-by-drug-orders/search-by-drug-orders.resources.ts +60 -0
  68. package/src/components/search-by-drug-orders/search-by-drug-orders.style.scss +4 -0
  69. package/src/components/search-by-drug-orders/search-by-drug-orders.test.tsx +159 -0
  70. package/src/components/search-by-drug-orders/search-by-drug-orders.utils.ts +118 -0
  71. package/src/components/search-by-encounters/search-by-encounters.component.tsx +188 -0
  72. package/src/components/search-by-encounters/search-by-encounters.resources.ts +51 -0
  73. package/src/components/search-by-encounters/search-by-encounters.style.scss +12 -0
  74. package/src/components/search-by-encounters/search-by-encounters.test.tsx +202 -0
  75. package/src/components/search-by-encounters/search-by-encounters.utils.ts +113 -0
  76. package/src/components/search-by-enrollments/search-by-enrollments.component.tsx +183 -0
  77. package/src/components/search-by-enrollments/search-by-enrollments.resources.ts +31 -0
  78. package/src/components/search-by-enrollments/search-by-enrollments.style.scss +4 -0
  79. package/src/components/search-by-enrollments/search-by-enrollments.test.tsx +158 -0
  80. package/src/components/search-by-enrollments/search-by-enrollments.utils.ts +69 -0
  81. package/src/components/search-by-location/search-by-location.component.tsx +97 -0
  82. package/src/components/search-by-location/search-by-location.style.scss +4 -0
  83. package/src/components/search-by-location/search-by-location.test.tsx +110 -0
  84. package/src/components/search-by-location/search-by-location.utils.ts +40 -0
  85. package/src/components/search-by-person-attributes/search-by-person-attributes.component.tsx +90 -0
  86. package/src/components/search-by-person-attributes/search-by-person-attributes.resource.ts +27 -0
  87. package/src/components/search-by-person-attributes/search-by-person-attributes.style.scss +4 -0
  88. package/src/components/search-by-person-attributes/search-by-person-attributes.test.tsx +133 -0
  89. package/src/components/search-by-person-attributes/search-by-person-attributes.utils.ts +42 -0
  90. package/src/components/search-history/search-history-options/search-history-options.component.tsx +290 -0
  91. package/src/components/search-history/search-history-options/search-history-options.resources.ts +29 -0
  92. package/src/components/search-history/search-history-options/search-history-options.test.tsx +144 -0
  93. package/src/components/search-history/search-history.component.tsx +174 -0
  94. package/src/components/search-history/search-history.style.scss +12 -0
  95. package/src/components/search-history/search-history.test.tsx +106 -0
  96. package/src/components/search-history/search-history.utils.ts +14 -0
  97. package/src/components/search-results-table/search-results-table.component.tsx +105 -0
  98. package/src/components/search-results-table/search-results-table.scss +4 -0
  99. package/src/components/search-results-table/search-results-table.test.tsx +47 -0
  100. package/src/declarations.d.tsx +2 -0
  101. package/src/index.ts +47 -0
  102. package/src/setup-tests.ts +1 -0
  103. package/src/types/index.ts +120 -0
@@ -0,0 +1,188 @@
1
+ import React, { useState } from "react";
2
+
3
+ import {
4
+ DatePicker,
5
+ DatePickerInput,
6
+ Column,
7
+ NumberInput,
8
+ MultiSelect,
9
+ } from "@carbon/react";
10
+ import { showToast } from "@openmrs/esm-framework";
11
+ import dayjs from "dayjs";
12
+ import { useTranslation } from "react-i18next";
13
+
14
+ import { useLocations } from "../../cohort-builder.resources";
15
+ import { SearchByProps, DropdownValue } from "../../types";
16
+ import SearchButtonSet from "../search-button-set/search-button-set";
17
+ import { useEncounterTypes, useForms } from "./search-by-encounters.resources";
18
+ import styles from "./search-by-encounters.style.scss";
19
+ import { getDescription, getQueryDetails } from "./search-by-encounters.utils";
20
+
21
+ const SearchByEncounters: React.FC<SearchByProps> = ({ onSubmit }) => {
22
+ const { t } = useTranslation();
23
+ const [atLeastCount, setAtLeastCount] = useState(0);
24
+ const [atMostCount, setAtMostCount] = useState(0);
25
+ const { encounterTypes, encounterTypesError } = useEncounterTypes();
26
+ const [selectedEncounterTypes, setSelectedEncounterTypes] = useState<
27
+ DropdownValue[]
28
+ >([]);
29
+ const [encounterLocations, setEncounterLocations] = useState<DropdownValue[]>(
30
+ []
31
+ );
32
+ const [encounterForms, setEncounterForms] = useState<DropdownValue[]>([]);
33
+ const { locations, locationsError } = useLocations();
34
+ const { forms, formsError } = useForms();
35
+ const [onOrBefore, setOnOrBefore] = useState("");
36
+ const [onOrAfter, setOnOrAfter] = useState("");
37
+ const [isLoading, setIsLoading] = useState(false);
38
+
39
+ if (locationsError) {
40
+ showToast({
41
+ title: t("error", "Error"),
42
+ kind: "error",
43
+ critical: true,
44
+ description: locationsError?.message,
45
+ });
46
+ }
47
+
48
+ if (formsError) {
49
+ showToast({
50
+ title: t("error", "Error"),
51
+ kind: "error",
52
+ critical: true,
53
+ description: formsError?.message,
54
+ });
55
+ }
56
+
57
+ if (encounterTypesError) {
58
+ showToast({
59
+ title: t("error", "Error"),
60
+ kind: "error",
61
+ critical: true,
62
+ description: encounterTypesError?.message,
63
+ });
64
+ }
65
+
66
+ const reset = () => {
67
+ setAtLeastCount(0);
68
+ setAtMostCount(0);
69
+ setOnOrBefore("");
70
+ setOnOrAfter("");
71
+ };
72
+
73
+ const submit = async () => {
74
+ setIsLoading(true);
75
+ const encounterDetails = {
76
+ onOrAfter,
77
+ atLeastCount,
78
+ atMostCount,
79
+ encounterForms,
80
+ encounterLocations,
81
+ onOrBefore,
82
+ selectedEncounterTypes,
83
+ };
84
+ await onSubmit(
85
+ getQueryDetails(encounterDetails),
86
+ getDescription(encounterDetails)
87
+ );
88
+ setIsLoading(false);
89
+ };
90
+
91
+ return (
92
+ <>
93
+ <Column>
94
+ <div>
95
+ <MultiSelect
96
+ id="encounters"
97
+ data-testid="encounters"
98
+ onChange={(data) => setSelectedEncounterTypes(data.selectedItems)}
99
+ items={encounterTypes}
100
+ label={t("selectEncounterTypes", "Select encounter types")}
101
+ />
102
+ </div>
103
+ </Column>
104
+ <MultiSelect
105
+ id="forms"
106
+ data-testid="forms"
107
+ onChange={(data) => setEncounterForms(data.selectedItems)}
108
+ items={forms}
109
+ label={t("selectForms", "Select forms")}
110
+ />
111
+
112
+ <MultiSelect
113
+ id="locations"
114
+ data-testid="locations"
115
+ onChange={(data) => setEncounterLocations(data.selectedItems)}
116
+ items={locations}
117
+ label={t("selectLocations", "Select locations")}
118
+ />
119
+ <div className={styles.column}>
120
+ <Column className={styles.encounterRange}>
121
+ <div className={styles.multipleInputs}>
122
+ <NumberInput
123
+ hideSteppers={true}
124
+ id="atLeastCount"
125
+ data-testid="atLeastCount"
126
+ label={t("atLeast", "at least")}
127
+ min={0}
128
+ size="sm"
129
+ value={atLeastCount}
130
+ onChange={(event, { value }) => setAtLeastCount(value)}
131
+ />
132
+ </div>
133
+ <div className={styles.multipleInputs}>
134
+ <NumberInput
135
+ hideSteppers={true}
136
+ id="atMostCount"
137
+ data-testid="atMostCount"
138
+ label={t("upto", "upto this many")}
139
+ min={0}
140
+ size="sm"
141
+ value={atMostCount}
142
+ onChange={(event, { value }) => setAtMostCount(value)}
143
+ />
144
+ </div>
145
+ </Column>
146
+ </div>
147
+ <div className={styles.column}>
148
+ <Column>
149
+ <DatePicker
150
+ datePickerType="single"
151
+ allowInput={false}
152
+ onChange={(date) => setOnOrAfter(dayjs(date[0]).format())}
153
+ >
154
+ <DatePickerInput
155
+ id="onOrAfter"
156
+ labelText={t("from", "From")}
157
+ value={onOrAfter && dayjs(onOrAfter).format("DD-MM-YYYY")}
158
+ placeholder="DD-MM-YYYY"
159
+ size="md"
160
+ />
161
+ </DatePicker>
162
+ </Column>
163
+ <Column>
164
+ <DatePicker
165
+ datePickerType="single"
166
+ allowInput={false}
167
+ onChange={(date) => setOnOrBefore(dayjs(date[0]).format())}
168
+ >
169
+ <DatePickerInput
170
+ id="onOrBefore"
171
+ value={onOrBefore && dayjs(onOrBefore).format("DD-MM-YYYY")}
172
+ labelText={t("to", "to")}
173
+ placeholder="DD-MM-YYYY"
174
+ size="md"
175
+ />
176
+ </DatePicker>
177
+ </Column>
178
+ </div>
179
+ <SearchButtonSet
180
+ isLoading={isLoading}
181
+ onHandleSubmit={submit}
182
+ onHandleReset={reset}
183
+ />
184
+ </>
185
+ );
186
+ };
187
+
188
+ export default SearchByEncounters;
@@ -0,0 +1,51 @@
1
+ import { openmrsFetch } from "@openmrs/esm-framework";
2
+ import useSWRImmutable from "swr/immutable";
3
+
4
+ import { DropdownValue, Response } from "../../types";
5
+
6
+ /**
7
+ * @returns Locations
8
+ */
9
+ export const useForms = () => {
10
+ const { data, error } = useSWRImmutable<{
11
+ data: { results: Response[] };
12
+ }>("/ws/rest/v1/form", openmrsFetch);
13
+
14
+ const forms: DropdownValue[] = [];
15
+ data?.data.results.map((form: Response, index: number) => {
16
+ forms.push({
17
+ id: index,
18
+ label: form.display,
19
+ value: form.uuid,
20
+ });
21
+ });
22
+
23
+ return {
24
+ isLoading: !data && !error,
25
+ forms,
26
+ formsError: error,
27
+ };
28
+ };
29
+
30
+ /**
31
+ * @returns EncounterTypes
32
+ */
33
+ export const useEncounterTypes = () => {
34
+ const { data, error } = useSWRImmutable<{
35
+ data: { results: Response[] };
36
+ }>("/ws/rest/v1/encountertype", openmrsFetch);
37
+
38
+ const encounterTypes: DropdownValue[] = [];
39
+ data?.data.results.map((encounterType: Response, index: number) => {
40
+ encounterTypes.push({
41
+ id: index,
42
+ label: encounterType.display,
43
+ value: encounterType.uuid,
44
+ });
45
+ });
46
+ return {
47
+ isLoading: !data && !error,
48
+ encounterTypes,
49
+ encounterTypesError: error,
50
+ };
51
+ };
@@ -0,0 +1,12 @@
1
+ .column {
2
+ padding-top: 1.5rem;
3
+ display: flex;
4
+ }
5
+
6
+ .multipleInputs {
7
+ margin-right: 0.2rem;
8
+ }
9
+
10
+ .encounterRange {
11
+ display: flex;
12
+ }
@@ -0,0 +1,202 @@
1
+ import React from "react";
2
+
3
+ import { openmrsFetch } from "@openmrs/esm-framework";
4
+ import { render, cleanup, fireEvent, waitFor } from "@testing-library/react";
5
+ import userEvent from "@testing-library/user-event";
6
+
7
+ import translations from "../../../translations/en.json";
8
+ import { useLocations } from "../../cohort-builder.resources";
9
+ import SearchByEncounters from "./search-by-encounters.component";
10
+ import { useForms, useEncounterTypes } from "./search-by-encounters.resources";
11
+
12
+ const mockLocations = [
13
+ {
14
+ id: 0,
15
+ label: "Isolation Ward",
16
+ value: "ac7d7773-fe9f-11ec-8b9b-0242ac1b0002",
17
+ },
18
+ {
19
+ id: 1,
20
+ label: "Armani Hospital",
21
+ value: "8d8718c2-c2cc-11de-8d13-0010c6dffd0f",
22
+ },
23
+ {
24
+ id: 2,
25
+ label: "Pharmacy",
26
+ value: "8d871afc-c2cc-11de-8d13-0010c6dffd0f",
27
+ },
28
+ ];
29
+
30
+ const mockEncounterTypes = [
31
+ {
32
+ id: 0,
33
+ value: "0cd5d4cb-204e-419a-9dd7-1e18e939ce4c",
34
+ label: "Patient Tracing Form",
35
+ },
36
+ {
37
+ id: 1,
38
+ value: "3044916a-7e5f-478b-9091-803233f27f91",
39
+ label: "Transfer Out",
40
+ },
41
+ {
42
+ id: 2,
43
+ value: "41af1931-184e-45f8-86ca-d42e0db0b8a1",
44
+ label: "Viral Load results",
45
+ },
46
+ {
47
+ id: 3,
48
+ value: "d7151f82-c1f3-4152-a605-2f9ea7414a79",
49
+ label: "Visit Note",
50
+ },
51
+ {
52
+ id: 4,
53
+ value: "67a71486-1a54-468f-ac3e-7091a9a79584",
54
+ label: "Vitals",
55
+ },
56
+ ];
57
+
58
+ const mockForms = [
59
+ {
60
+ id: 0,
61
+ value: "bb826dc9-8c1a-4b19-83c9-b59e5e128a7b",
62
+ label: "POC Patient Consent",
63
+ },
64
+ {
65
+ id: 1,
66
+ value: "9326eb32-d0fd-40c3-8c30-69d5774af06d",
67
+ label: "POC Patient Consent v1.2",
68
+ },
69
+ {
70
+ id: 2,
71
+ value: "7f5ce1d4-a42e-4b59-840e-f239d844cf9b",
72
+ label: "POC Test Form",
73
+ },
74
+ ];
75
+
76
+ const expectedQuery = {
77
+ query: {
78
+ columns: [
79
+ {
80
+ key: "reporting.library.patientDataDefinition.builtIn.preferredName.givenName",
81
+ name: "firstname",
82
+ type: "org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition",
83
+ },
84
+ {
85
+ key: "reporting.library.patientDataDefinition.builtIn.preferredName.familyName",
86
+ name: "lastname",
87
+ type: "org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition",
88
+ },
89
+ {
90
+ key: "reporting.library.patientDataDefinition.builtIn.gender",
91
+ name: "gender",
92
+ type: "org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition",
93
+ },
94
+ {
95
+ key: "reporting.library.patientDataDefinition.builtIn.ageOnDate.fullYears",
96
+ name: "age",
97
+ type: "org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition",
98
+ },
99
+ {
100
+ key: "reporting.library.patientDataDefinition.builtIn.patientId",
101
+ name: "patientId",
102
+ type: "org.openmrs.module.reporting.data.patient.definition.PatientDataDefinition",
103
+ },
104
+ ],
105
+ customRowFilterCombination: "1",
106
+ rowFilters: [
107
+ {
108
+ key: "reporting.library.cohortDefinition.builtIn.encounterSearchAdvanced",
109
+ parameterValues: {
110
+ atLeastCount: "10",
111
+ atMostCount: "20",
112
+ encounterTypeList: [mockEncounterTypes[4].value],
113
+ formList: [mockForms[1].value],
114
+ locationList: [mockLocations[2].value],
115
+ },
116
+ type: "org.openmrs.module.reporting.dataset.definition.PatientDataSetDefinition",
117
+ },
118
+ ],
119
+ type: "org.openmrs.module.reporting.dataset.definition.PatientDataSetDefinition",
120
+ },
121
+ };
122
+
123
+ const mockOpenmrsFetch = openmrsFetch as jest.Mock;
124
+
125
+ jest.mock("./search-by-encounters.resources", () => {
126
+ const original = jest.requireActual("./search-by-encounters.resources");
127
+ return {
128
+ ...original,
129
+ useForms: jest.fn(),
130
+ useEncounterTypes: jest.fn(),
131
+ };
132
+ });
133
+
134
+ jest.mock("../../cohort-builder.resources", () => {
135
+ const original = jest.requireActual("../../cohort-builder.resources");
136
+ return {
137
+ ...original,
138
+ useLocations: jest.fn(),
139
+ };
140
+ });
141
+
142
+ describe("Test the search by encounters component", () => {
143
+ afterEach(cleanup);
144
+ it("should be able to select input values", async () => {
145
+ // @ts-ignore
146
+ useForms.mockImplementation(() => ({
147
+ forms: mockForms,
148
+ isLoading: false,
149
+ formsError: undefined,
150
+ }));
151
+ mockOpenmrsFetch.mockReturnValueOnce({
152
+ data: { results: mockForms },
153
+ });
154
+
155
+ // @ts-ignore
156
+ useEncounterTypes.mockImplementation(() => ({
157
+ encounterTypes: mockEncounterTypes,
158
+ isLoading: false,
159
+ encounterTypesError: undefined,
160
+ }));
161
+ mockOpenmrsFetch.mockReturnValueOnce({
162
+ data: { results: mockEncounterTypes },
163
+ });
164
+
165
+ // @ts-ignore
166
+ useLocations.mockImplementation(() => ({
167
+ locations: mockLocations,
168
+ isLoading: false,
169
+ locationsError: undefined,
170
+ }));
171
+ mockOpenmrsFetch.mockReturnValueOnce({
172
+ data: { results: mockLocations },
173
+ });
174
+
175
+ const submit = jest.fn();
176
+ const { getByTestId, getByText } = render(
177
+ <SearchByEncounters onSubmit={submit} />
178
+ );
179
+
180
+ fireEvent.click(getByText(translations.selectEncounterTypes));
181
+ fireEvent.click(getByText(mockEncounterTypes[4].label));
182
+ fireEvent.click(getByText(translations.selectForms));
183
+ fireEvent.click(getByText(mockForms[1].label));
184
+ fireEvent.click(getByText(translations.selectLocations));
185
+ fireEvent.click(getByText(mockLocations[2].label));
186
+
187
+ const atLeastCountInput = getByTestId("atLeastCount");
188
+ const atMostCountInput = getByTestId("atMostCount");
189
+ fireEvent.click(atLeastCountInput);
190
+ await userEvent.type(atLeastCountInput, "10");
191
+ fireEvent.click(atMostCountInput);
192
+ await userEvent.type(atMostCountInput, "20");
193
+
194
+ fireEvent.click(getByTestId("search-btn"));
195
+ await waitFor(async () => {
196
+ expect(submit).toBeCalledWith(
197
+ expectedQuery,
198
+ `Patients with Encounter of Types ${mockEncounterTypes[4].label} at ${mockLocations[2].label} from ${mockForms[1].label} at least 10 times and at most 20 times`
199
+ );
200
+ });
201
+ });
202
+ });
@@ -0,0 +1,113 @@
1
+ import { composeJson } from "../../cohort-builder.utils";
2
+ import { EncounterDetails } from "../../types";
3
+
4
+ export const getDescription = ({
5
+ selectedEncounterTypes,
6
+ encounterLocations,
7
+ encounterForms,
8
+ atLeastCount,
9
+ atMostCount,
10
+ onOrAfter,
11
+ onOrBefore,
12
+ }: EncounterDetails) => {
13
+ let description = "Patients with Encounter of";
14
+ const selectedEncounters = selectedEncounterTypes
15
+ .map((encounterType) => encounterType.label)
16
+ .join(", ")
17
+ .replace(/,(?=[^,]*$)/, " and ");
18
+
19
+ description += selectedEncounters
20
+ ? ` Type${selectedEncounters.length > 1 ? "s" : ""} ${selectedEncounters}`
21
+ : " any Type";
22
+ if (encounterLocations.length) {
23
+ description += ` at ${encounterLocations
24
+ .map((location) => location.label)
25
+ .join(", ")}`;
26
+ }
27
+ if (encounterForms.length) {
28
+ description += ` from ${encounterForms
29
+ .map((form) => form.label)
30
+ .join(", ")}`;
31
+ }
32
+ if (atLeastCount) {
33
+ description += ` at least ${atLeastCount} ${
34
+ atLeastCount > 1 ? "times" : "time"
35
+ }`;
36
+ }
37
+ if (atMostCount) {
38
+ description += ` ${atLeastCount ? " and" : ""} at most ${atMostCount} ${
39
+ atMostCount > 1 ? "times" : "time"
40
+ }`;
41
+ }
42
+ if (onOrAfter) {
43
+ if (onOrBefore) {
44
+ description += ` from ${onOrAfter}`;
45
+ } else {
46
+ description += ` on or after ${onOrAfter}`;
47
+ }
48
+ }
49
+ if (onOrBefore) {
50
+ if (onOrAfter) {
51
+ description += ` to ${onOrBefore}`;
52
+ } else {
53
+ description += ` on or before ${onOrBefore}`;
54
+ }
55
+ }
56
+ return description;
57
+ };
58
+
59
+ export const getQueryDetails = ({
60
+ onOrAfter,
61
+ atLeastCount,
62
+ atMostCount,
63
+ encounterForms,
64
+ encounterLocations,
65
+ onOrBefore,
66
+ selectedEncounterTypes,
67
+ }: EncounterDetails) => {
68
+ const searchParams = { encounterSearchAdvanced: [] };
69
+ if (onOrAfter) {
70
+ searchParams.encounterSearchAdvanced.push({
71
+ name: "onOrAfter",
72
+ value: onOrAfter,
73
+ });
74
+ }
75
+ if (atLeastCount) {
76
+ searchParams.encounterSearchAdvanced.push({
77
+ name: "atLeastCount",
78
+ value: atLeastCount,
79
+ });
80
+ }
81
+ if (atMostCount) {
82
+ searchParams.encounterSearchAdvanced.push({
83
+ name: "atMostCount",
84
+ value: atMostCount,
85
+ });
86
+ }
87
+ if (encounterForms.length) {
88
+ searchParams.encounterSearchAdvanced.push({
89
+ name: "formList",
90
+ value: encounterForms.map((form) => form.value),
91
+ });
92
+ }
93
+ if (encounterLocations.length) {
94
+ searchParams.encounterSearchAdvanced.push({
95
+ name: "locationList",
96
+ value: encounterLocations.map((location) => location.value),
97
+ });
98
+ }
99
+ if (onOrBefore) {
100
+ searchParams.encounterSearchAdvanced.push({
101
+ name: "onOrBefore",
102
+ value: onOrBefore,
103
+ });
104
+ }
105
+ if (selectedEncounterTypes.length > 0) {
106
+ searchParams.encounterSearchAdvanced.push({
107
+ name: "encounterTypeList",
108
+ value: selectedEncounterTypes.map((encounterType) => encounterType.value),
109
+ });
110
+ }
111
+
112
+ return composeJson(searchParams);
113
+ };