@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.1-pre.101

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 (186) hide show
  1. package/README.md +58 -12
  2. package/dist/153.js +1 -0
  3. package/dist/153.js.map +1 -0
  4. package/dist/233.js +2 -0
  5. package/dist/{382.js.LICENSE.txt → 233.js.LICENSE.txt} +3 -2
  6. package/dist/233.js.map +1 -0
  7. package/dist/262.js +1 -0
  8. package/dist/262.js.map +1 -0
  9. package/dist/279.js +1 -0
  10. package/dist/279.js.map +1 -0
  11. package/dist/294.js +1 -2
  12. package/dist/294.js.LICENSE.txt +2 -7
  13. package/dist/294.js.map +1 -1
  14. package/dist/327.js +1 -0
  15. package/dist/327.js.map +1 -0
  16. package/dist/409.js +2 -0
  17. package/dist/409.js.LICENSE.txt +27 -0
  18. package/dist/409.js.map +1 -0
  19. package/dist/415.js +1 -2
  20. package/dist/415.js.map +1 -1
  21. package/dist/559.js +1 -0
  22. package/dist/559.js.map +1 -0
  23. package/dist/574.js +1 -1
  24. package/dist/651.js +1 -0
  25. package/dist/651.js.map +1 -0
  26. package/dist/706.js +1 -0
  27. package/dist/706.js.map +1 -0
  28. package/dist/757.js +1 -0
  29. package/dist/800.js +2 -0
  30. package/dist/800.js.LICENSE.txt +5 -0
  31. package/dist/800.js.map +1 -0
  32. package/dist/820.js +1 -0
  33. package/dist/820.js.map +1 -0
  34. package/dist/883.js +1 -0
  35. package/dist/883.js.map +1 -0
  36. package/dist/889.js +1 -0
  37. package/dist/889.js.map +1 -0
  38. package/dist/897.js +2 -0
  39. package/dist/897.js.LICENSE.txt +21 -0
  40. package/dist/897.js.map +1 -0
  41. package/dist/92.js +1 -0
  42. package/dist/92.js.map +1 -0
  43. package/dist/935.js +2 -0
  44. package/dist/{735.js.LICENSE.txt → 935.js.LICENSE.txt} +6 -16
  45. package/dist/935.js.map +1 -0
  46. package/dist/959.js +1 -0
  47. package/dist/959.js.map +1 -0
  48. package/dist/main.js +1 -0
  49. package/dist/main.js.map +1 -0
  50. package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
  51. package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +374 -89
  52. package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -1
  53. package/dist/routes.json +1 -0
  54. package/docs/config-icrc-forms.png +0 -0
  55. package/docs/config-other-forms.png +0 -0
  56. package/docs/configuring-form-categories.md +77 -0
  57. package/docs/fde-workflow.mov +0 -0
  58. package/docs/form-workflow-state-diagram.png +0 -0
  59. package/jest.config.json +21 -18
  60. package/package.json +100 -106
  61. package/src/CancelModal.tsx +48 -0
  62. package/src/CompleteModal.tsx +46 -0
  63. package/src/FormBootstrap.tsx +166 -0
  64. package/src/Root.tsx +14 -3
  65. package/src/add-group-modal/AddGroupModal.tsx +288 -0
  66. package/src/add-group-modal/styles.scss +45 -0
  67. package/src/config-schema.ts +85 -31
  68. package/src/context/FormWorkflowContext.tsx +126 -0
  69. package/src/context/FormWorkflowReducer.ts +287 -0
  70. package/src/context/GroupFormWorkflowContext.tsx +176 -0
  71. package/src/context/GroupFormWorkflowReducer.ts +430 -0
  72. package/src/empty-state/EmptyDataIllustration.tsx +51 -0
  73. package/src/empty-state/EmptyState.tsx +33 -0
  74. package/src/empty-state/styles.scss +55 -0
  75. package/src/form-entry-workflow/FormEntryWorkflow.tsx +196 -0
  76. package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +50 -0
  77. package/src/form-entry-workflow/form-review-card/index.ts +3 -0
  78. package/src/form-entry-workflow/form-review-card/styles.scss +39 -0
  79. package/src/form-entry-workflow/index.ts +3 -0
  80. package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
  81. package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +86 -0
  82. package/src/form-entry-workflow/patient-banner/index.ts +3 -0
  83. package/src/form-entry-workflow/patient-banner/styles.scss +45 -0
  84. package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +63 -0
  85. package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
  86. package/src/form-entry-workflow/patient-search-header/styles.scss +22 -0
  87. package/src/form-entry-workflow/styles.scss +65 -0
  88. package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +35 -0
  89. package/src/form-entry-workflow/workflow-review/index.ts +3 -0
  90. package/src/form-entry-workflow/workflow-review/styles.scss +34 -0
  91. package/src/forms-app-menu-link.tsx +3 -2
  92. package/src/forms-page/FormsPage.tsx +134 -0
  93. package/src/forms-page/forms-table/FormsTable.tsx +137 -0
  94. package/src/forms-page/forms-table/index.ts +3 -0
  95. package/src/forms-page/forms-table/styles.scss +20 -0
  96. package/src/forms-page/index.ts +3 -0
  97. package/src/forms-page/styles.scss +11 -0
  98. package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
  99. package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
  100. package/src/group-form-entry-workflow/SessionDetailsForm.tsx +131 -0
  101. package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
  102. package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +144 -0
  103. package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
  104. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.test.tsx +9 -0
  105. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +63 -0
  106. package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
  107. package/src/group-form-entry-workflow/group-display-header/styles.scss +60 -0
  108. package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +139 -0
  109. package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +68 -0
  110. package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +150 -0
  111. package/src/group-form-entry-workflow/group-search/compact-group-result.scss +64 -0
  112. package/src/group-form-entry-workflow/group-search/compact-group-search.scss +35 -0
  113. package/src/group-form-entry-workflow/group-search/group-search.scss +96 -0
  114. package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +73 -0
  115. package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
  116. package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
  117. package/src/group-form-entry-workflow/index.ts +3 -0
  118. package/src/group-form-entry-workflow/styles.scss +97 -0
  119. package/src/hooks/index.ts +7 -0
  120. package/src/hooks/useFormState.ts +23 -0
  121. package/src/hooks/useGetAllForms.ts +45 -0
  122. package/src/hooks/useGetEncounter.ts +21 -0
  123. package/src/hooks/useGetPatient.ts +23 -0
  124. package/src/hooks/useGetPatients.ts +34 -0
  125. package/src/hooks/useGetSystemSetting.ts +38 -0
  126. package/src/hooks/useKeyPress.ts +31 -0
  127. package/src/hooks/usePostEndpoint.ts +76 -0
  128. package/src/hooks/useSearchEndpoint.ts +120 -0
  129. package/src/hooks/useStartVisit.ts +92 -0
  130. package/src/index.ts +26 -62
  131. package/src/patient-card/PatientCard.tsx +67 -0
  132. package/src/patient-card/index.ts +3 -0
  133. package/src/patient-card/styles.scss +46 -0
  134. package/src/routes.json +24 -0
  135. package/tools/i18next-parser.config.js +93 -0
  136. package/translations/en.json +69 -4
  137. package/translations/fr.json +50 -0
  138. package/tsconfig.json +26 -23
  139. package/.editorconfig +0 -12
  140. package/.eslintignore +0 -2
  141. package/.eslintrc +0 -4
  142. package/.github/workflows/node.js.yml +0 -79
  143. package/.husky/pre-commit +0 -6
  144. package/.husky/pre-push +0 -6
  145. package/.prettierignore +0 -14
  146. package/dist/24.js +0 -3
  147. package/dist/24.js.LICENSE.txt +0 -16
  148. package/dist/24.js.map +0 -1
  149. package/dist/296.js +0 -2
  150. package/dist/296.js.map +0 -1
  151. package/dist/299.js +0 -2
  152. package/dist/299.js.map +0 -1
  153. package/dist/382.js +0 -3
  154. package/dist/382.js.map +0 -1
  155. package/dist/595.js +0 -3
  156. package/dist/595.js.LICENSE.txt +0 -1
  157. package/dist/595.js.map +0 -1
  158. package/dist/69.js +0 -2
  159. package/dist/69.js.map +0 -1
  160. package/dist/735.js +0 -3
  161. package/dist/735.js.map +0 -1
  162. package/dist/777.js +0 -2
  163. package/dist/777.js.map +0 -1
  164. package/dist/860.js +0 -2
  165. package/dist/860.js.map +0 -1
  166. package/dist/906.js +0 -2
  167. package/dist/906.js.map +0 -1
  168. package/dist/openmrs-esm-fast-data-entry-app.old +0 -2
  169. package/src/boxes/extensions/blue-box.tsx +0 -15
  170. package/src/boxes/extensions/box.scss +0 -23
  171. package/src/boxes/extensions/brand-box.tsx +0 -15
  172. package/src/boxes/extensions/red-box.tsx +0 -15
  173. package/src/boxes/slot/boxes.css +0 -23
  174. package/src/boxes/slot/boxes.tsx +0 -19
  175. package/src/forms/FormsRoot.tsx +0 -32
  176. package/src/forms/FormsTable.tsx +0 -64
  177. package/src/forms/mockData.ts +0 -43
  178. package/src/greeter/greeter.css +0 -4
  179. package/src/greeter/greeter.test.tsx +0 -29
  180. package/src/greeter/greeter.tsx +0 -25
  181. package/src/hello.css +0 -3
  182. package/src/hello.test.tsx +0 -45
  183. package/src/hello.tsx +0 -30
  184. package/src/patient-getter/patient-getter.resource.ts +0 -31
  185. package/src/patient-getter/patient-getter.test.tsx +0 -28
  186. package/src/patient-getter/patient-getter.tsx +0 -28
@@ -0,0 +1,68 @@
1
+ import React, { useState } from "react";
2
+ import { GroupType } from "../../context/GroupFormWorkflowContext";
3
+ import styles from "./compact-group-search.scss";
4
+ import GroupSearch from "./GroupSearch";
5
+ import { Button, Search } from "@carbon/react";
6
+ import { useTranslation } from "react-i18next";
7
+ import debounce from "lodash-es/debounce";
8
+
9
+ interface CompactGroupSearchProps {
10
+ selectGroupAction?: (group: GroupType) => void;
11
+ }
12
+
13
+ const CompactGroupSearch: React.FC<CompactGroupSearchProps> = ({
14
+ selectGroupAction,
15
+ }) => {
16
+ const { t } = useTranslation();
17
+ const [query, setQuery] = useState("");
18
+ const [dropdownShown, setDropdownShown] = useState(false);
19
+
20
+ const onGroupSelect = (group) => {
21
+ selectGroupAction(group);
22
+ setDropdownShown(false);
23
+ setQuery("");
24
+ };
25
+
26
+ const handleSearchChange = (e) => {
27
+ debounce((q) => {
28
+ setDropdownShown(!!e.length);
29
+ setQuery(q);
30
+ }, 300);
31
+ setQuery(e);
32
+ if (e.length) {
33
+ setDropdownShown(true);
34
+ } else {
35
+ setDropdownShown(false);
36
+ }
37
+ };
38
+
39
+ return (
40
+ <div className={styles.patientSearchBar}>
41
+ <div className={styles.searchArea}>
42
+ <Search
43
+ autoFocus
44
+ className={styles.patientSearchInput}
45
+ closeButtonLabelText={t("clearSearch", "Clear")}
46
+ labelText=""
47
+ onChange={(event) => {
48
+ handleSearchChange(event.target.value);
49
+ }}
50
+ onClear={() => undefined}
51
+ placeholder={t("searchForGroup", "Search for a group by name")}
52
+ size="sm"
53
+ value={query}
54
+ />
55
+ <Button kind="secondary" size="sm">
56
+ {t("search", "Search")}
57
+ </Button>
58
+ </div>
59
+ {dropdownShown && (
60
+ <div className={styles.floatingSearchResultsContainer}>
61
+ <GroupSearch query={query} selectGroupAction={onGroupSelect} />
62
+ </div>
63
+ )}
64
+ </div>
65
+ );
66
+ };
67
+
68
+ export default CompactGroupSearch;
@@ -0,0 +1,150 @@
1
+ import React, { useCallback, useRef } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Layer, Tile, Loading } from "@carbon/react";
4
+ import styles from "./group-search.scss";
5
+ import { EmptyDataIllustration } from "../../empty-state/EmptyDataIllustration";
6
+ import CompactGroupResults, {
7
+ SearchResultSkeleton,
8
+ } from "./CompactGroupResults";
9
+ import { GroupType } from "../../context/GroupFormWorkflowContext";
10
+ import { useSearchCohortInfinite } from "../../hooks/useSearchEndpoint";
11
+
12
+ interface GroupSearchProps {
13
+ query: string;
14
+ selectGroupAction?: (group: GroupType) => void;
15
+ }
16
+
17
+ const GroupSearch: React.FC<GroupSearchProps> = ({
18
+ query = "",
19
+ selectGroupAction,
20
+ }) => {
21
+ const { t } = useTranslation();
22
+ const {
23
+ isLoading,
24
+ data: results,
25
+ error,
26
+ loadingNewData,
27
+ setPage,
28
+ hasMore,
29
+ totalResults,
30
+ } = useSearchCohortInfinite({
31
+ searchTerm: query,
32
+ searching: !!query,
33
+ parameters: {
34
+ v: "full",
35
+ },
36
+ });
37
+
38
+ const lastItem = useRef(null);
39
+ const observer = useRef(null);
40
+ const loadingRef = useCallback(
41
+ (node) => {
42
+ if (loadingNewData) {
43
+ return;
44
+ }
45
+ if (observer.current) {
46
+ observer.current.disconnect();
47
+ }
48
+ observer.current = new IntersectionObserver(
49
+ (entries) => {
50
+ if (entries[0].isIntersecting && hasMore) {
51
+ setPage((page) => page + 1);
52
+ }
53
+ },
54
+ {
55
+ threshold: 0.75,
56
+ }
57
+ );
58
+ if (node) {
59
+ observer.current.observe(node);
60
+ }
61
+ },
62
+ [loadingNewData, hasMore, setPage]
63
+ );
64
+
65
+ if (error) {
66
+ return (
67
+ <div className={styles.searchResults}>
68
+ <Layer>
69
+ <Tile className={styles.emptySearchResultsTile}>
70
+ <EmptyDataIllustration />
71
+ <div>
72
+ <p className={styles.errorMessage}>{t("error", "Error")}</p>
73
+ <p className={styles.errorCopy}>
74
+ {t(
75
+ "errorCopy",
76
+ "Sorry, there was an error. You can try to reload this page, or contact the site administrator and quote the error code above."
77
+ )}
78
+ </p>
79
+ </div>
80
+ </Tile>
81
+ </Layer>
82
+ </div>
83
+ );
84
+ }
85
+
86
+ if (isLoading) {
87
+ return (
88
+ <div className={styles.searchResultsContainer}>
89
+ <SearchResultSkeleton />
90
+ <SearchResultSkeleton />
91
+ <SearchResultSkeleton />
92
+ <SearchResultSkeleton />
93
+ <SearchResultSkeleton />
94
+ </div>
95
+ );
96
+ }
97
+
98
+ if (results?.length === 0) {
99
+ return (
100
+ <div className={styles.searchResults}>
101
+ <Layer>
102
+ <Tile className={styles.emptySearchResultsTile}>
103
+ <EmptyDataIllustration />
104
+ <p className={styles.emptyResultText}>
105
+ {t("noGroupsFoundMessage", "Sorry, no groups have been found")}
106
+ </p>
107
+ <p className={styles.actionText}>
108
+ <span>
109
+ {t(
110
+ "trySearchWithPatientUniqueID",
111
+ "Try searching with the cohort's description"
112
+ )}
113
+ </span>
114
+ <br />
115
+ <span>{t("orLabelName", "OR label name")}</span>
116
+ </p>
117
+ </Tile>
118
+ </Layer>
119
+ </div>
120
+ );
121
+ }
122
+
123
+ return (
124
+ <div className={styles.searchResultsContainer}>
125
+ <div
126
+ className={styles.searchResults}
127
+ style={{
128
+ maxHeight: "22rem",
129
+ }}
130
+ >
131
+ <p className={styles.resultsText}>
132
+ {totalResults} {t("searchResultsText", "search result(s)")}
133
+ </p>
134
+ <CompactGroupResults
135
+ groups={results}
136
+ selectGroupAction={selectGroupAction}
137
+ lastRef={lastItem}
138
+ />
139
+ <div ref={lastItem}>
140
+ <div className={styles.lastItem} ref={loadingRef}>
141
+ {hasMore && <Loading withOverlay={false} small />}
142
+ {!hasMore && <p>{t("noMoreResults", "End of search results")}</p>}
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ );
148
+ };
149
+
150
+ export default GroupSearch;
@@ -0,0 +1,64 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @use '@carbon/styles/scss/colors';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .patientSearchResult {
7
+ text-decoration: none;
8
+ display: flex;
9
+ align-items: center;
10
+ border-bottom: 1px solid $ui-03;
11
+ padding: 0 spacing.$spacing-04;
12
+
13
+ &:hover,
14
+ &:focus {
15
+ background-color: $ui-01;
16
+ }
17
+ }
18
+
19
+ .patientSearchResultSelected {
20
+ background-color: colors.$gray-20;
21
+ }
22
+
23
+ .patientBanner {
24
+ display: flex;
25
+ }
26
+
27
+ .patientName {
28
+ @include type.type-style('heading-02');
29
+ }
30
+
31
+ .patientAvatar {
32
+ width: spacing.$spacing-09;
33
+ height: spacing.$spacing-09;
34
+ margin: spacing.$spacing-03 spacing.$spacing-05 spacing.$spacing-03 0;
35
+ border-radius: 1px;
36
+ }
37
+
38
+ .patientInfo {
39
+ width: 100%;
40
+ display: flex;
41
+ flex-flow: column wrap;
42
+ margin: spacing.$spacing-05;
43
+ cursor: pointer;
44
+ }
45
+
46
+ .demographics {
47
+ margin-top: spacing.$spacing-03;
48
+ @include type.type-style('body-compact-02');
49
+ color: $text-02;
50
+ }
51
+
52
+ .identifiers {
53
+ @include type.type-style('body-compact-02');
54
+ color: $ui-04;
55
+ }
56
+
57
+ .actionsContainer {
58
+ padding-top: spacing.$spacing-03;
59
+ margin-top: spacing.$spacing-05;
60
+ }
61
+
62
+ .middot {
63
+ margin: 0 spacing.$spacing-03;
64
+ }
@@ -0,0 +1,35 @@
1
+
2
+ @use '@carbon/styles/scss/spacing';
3
+ @use '@carbon/styles/scss/colors';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .patientSearchBar {
7
+ width: 50vw;
8
+ position: relative;
9
+ }
10
+
11
+ .floatingSearchResultsContainer {
12
+ position: absolute;
13
+ overflow-y: auto;
14
+ box-shadow: 0 spacing.$spacing-03 spacing.$spacing-05 $ui-03;
15
+ z-index: 99;
16
+ border: 0 1px 1px 1px solid $ui-03;
17
+ width: 100%;
18
+ background-color: $ui-02;
19
+ }
20
+
21
+ .searchArea {
22
+ width: inherit;
23
+ display: flex;
24
+ justify-content: center;
25
+ align-items: center;
26
+ border: 1px solid $ui-04;
27
+ }
28
+
29
+ .patientSearchInput {
30
+ border: none;
31
+ }
32
+
33
+ .patientSearchInput input:focus {
34
+ outline: 1px solid 1px solid colors.$orange-40;
35
+ }
@@ -0,0 +1,96 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/styles/scss/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+
6
+
7
+ .searchResultsContainer {
8
+ width: 100%;
9
+ background-color: $ui-02;
10
+
11
+ a {
12
+ text-decoration: none;
13
+ @include type.type-style('heading-02');
14
+ color: $text-02;
15
+ margin: 0rem;
16
+ }
17
+ }
18
+
19
+ :global(.omrs-breakpoint-lt-desktop) .searchResultsContainer {
20
+ top: 6.25rem;
21
+ }
22
+
23
+ :global(.omrs-breakpoint-gt-tablet) .searchResultsContainer {
24
+ padding: 0;
25
+ top: spacing.$spacing-09;
26
+ }
27
+
28
+ .searchResults {
29
+ width: 100%;
30
+ }
31
+
32
+
33
+
34
+ .searchTerm {
35
+ @include type.type-style('heading-03');
36
+ margin-top: 0.375rem;
37
+ }
38
+
39
+ .resultsText {
40
+ @include type.type-style('label-01');
41
+ color: $text-02;
42
+ line-height: spacing.$spacing-05;
43
+ margin: spacing.$spacing-03 spacing.$spacing-05;
44
+ }
45
+
46
+ .helperText {
47
+ color: var(--omrs-color-ink-medium-contrast);
48
+ margin-left: 2.375rem;
49
+ }
50
+
51
+ .emptyResultText {
52
+ @include type.type-style('heading-compact-01');
53
+ color: $text-02;
54
+ margin-top: spacing.$spacing-05;
55
+ margin-bottom: 0.313rem;
56
+ }
57
+
58
+ .actionText {
59
+ @include type.type-style('body-01');
60
+ color: $text-02;
61
+ }
62
+
63
+ .pagination {
64
+ display: flex;
65
+ justify-content: space-evenly;
66
+ padding: 4.688rem 0 spacing.$spacing-06;
67
+ }
68
+
69
+ .emptySearchResultsTile {
70
+ text-align: center;
71
+ margin-top: spacing.$spacing-05;
72
+ padding: spacing.$spacing-09 0rem;
73
+ }
74
+
75
+ :global(.omrs-breakpoint-gt-tablet) .emptySearchResultsTile {
76
+ margin: spacing.$spacing-05;
77
+ }
78
+
79
+ .errorMessage {
80
+ @include type.type-style('heading-compact-02');
81
+ margin-top: 2.25rem;
82
+ margin-bottom: spacing.$spacing-03;
83
+ }
84
+
85
+ .errorCopy {
86
+ margin-bottom: spacing.$spacing-03;
87
+ @include type.type-style('body-01');
88
+ color: $text-02;
89
+ }
90
+
91
+ .lastItem {
92
+ padding: spacing.$spacing-05;
93
+ display: flex;
94
+ justify-content: center;
95
+ align-items: center;
96
+ }
@@ -0,0 +1,73 @@
1
+ import { Close, Add } from "@carbon/react/icons";
2
+ import { Button } from "@carbon/react";
3
+ import React, { useCallback, useContext, useState } from "react";
4
+ import GroupFormWorkflowContext from "../../context/GroupFormWorkflowContext";
5
+ import styles from "./styles.scss";
6
+ import { useTranslation } from "react-i18next";
7
+ import CompactGroupSearch from "../group-search/CompactGroupSearch";
8
+ import AddGroupModal from "../../add-group-modal/AddGroupModal";
9
+
10
+ const GroupSearchHeader = () => {
11
+ const { t } = useTranslation();
12
+ const { activeGroupUuid, setGroup, destroySession } = useContext(
13
+ GroupFormWorkflowContext
14
+ );
15
+ const [isOpen, setOpen] = useState(false);
16
+ const handleSelectGroup = (group) => {
17
+ setGroup(group);
18
+ };
19
+
20
+ const handleCancel = useCallback(() => {
21
+ setOpen(false);
22
+ }, []);
23
+
24
+ const onPostSubmit = useCallback(() => {
25
+ setOpen(false);
26
+ }, []);
27
+
28
+ const handleOpenClick = useCallback(() => {
29
+ setOpen(true);
30
+ }, []);
31
+
32
+ if (activeGroupUuid) return null;
33
+
34
+ return (
35
+ <div className={styles.searchHeaderContainer}>
36
+ <span className={styles.padded}>{t("findGroup", "Find group")}:</span>
37
+ <span className={styles.searchBarWrapper}>
38
+ <CompactGroupSearch selectGroupAction={handleSelectGroup} />
39
+ </span>
40
+ <span className={styles.padded}>{t("or", "or")}</span>
41
+ <span>
42
+ <Button
43
+ onClick={handleOpenClick}
44
+ renderIcon={Add}
45
+ iconDescription="Add"
46
+ >
47
+ {t("createNewGroup", "Create New Group")}
48
+ </Button>
49
+ <AddGroupModal
50
+ {...{
51
+ isCreate: true,
52
+ isOpen: isOpen,
53
+ handleCancel: handleCancel,
54
+ onPostSubmit: onPostSubmit,
55
+ }}
56
+ />
57
+ </span>
58
+ <span style={{ flexGrow: 1 }} />
59
+ <span>
60
+ <Button
61
+ kind="ghost"
62
+ onClick={() => {
63
+ destroySession();
64
+ }}
65
+ >
66
+ {t("cancel", "Cancel")} <Close size={20} />
67
+ </Button>
68
+ </span>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default GroupSearchHeader;
@@ -0,0 +1,3 @@
1
+ import GroupSearchHeader from "./GroupSearchHeader";
2
+
3
+ export default GroupSearchHeader;
@@ -0,0 +1,20 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @import '~@openmrs/esm-styleguide/src/vars';
3
+
4
+ .searchHeaderContainer {
5
+ height: spacing.$spacing-11;
6
+ display: flex;
7
+ align-items: center;
8
+ background-color: $ui-02;
9
+ border-top: 0.0125rem solid $ui-03;
10
+ border-bottom: 0.0125rem solid $ui-03;
11
+ padding: 0 spacing.$spacing-05;
12
+ }
13
+
14
+ .searchBarWrapper {
15
+ min-width: 35rem;
16
+ }
17
+
18
+ .padded {
19
+ padding: spacing.$spacing-05;
20
+ }
@@ -0,0 +1,3 @@
1
+ import GroupFormEntryWorkflow from "./GroupFormEntryWorkflow";
2
+
3
+ export default GroupFormEntryWorkflow;
@@ -0,0 +1,97 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/colors';
3
+ @use '@carbon/styles/scss/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+
7
+ .breadcrumbsContainer > div > div > nav {
8
+ background-color: $ui-02;
9
+ padding: spacing.$spacing-04 spacing.$spacing-05;
10
+ height: spacing.$spacing-08;
11
+ }
12
+
13
+ .workspaceWrapper {
14
+ display: flex;
15
+ justify-content: center;
16
+ }
17
+
18
+ .workspace {
19
+ width: 1100px;
20
+ }
21
+
22
+ :global(.omrs-breakpoint-lt-large-desktop) .workspace {
23
+ width: 1000px;
24
+ }
25
+
26
+ :global(.omrs-breakpoint-lt-small-desktop) .workspace {
27
+ // there's only so much we can do here. Currenlty the design does not support tablet
28
+ width: 100vw;
29
+ padding: 0 spacing.$spacing-04;
30
+ }
31
+
32
+ .selectPatientMessage {
33
+ @include type.type-style('productive-heading-03');
34
+ margin: spacing.$spacing-07;
35
+ text-align: center;
36
+ }
37
+
38
+ .formMainContent {
39
+ display: flex;
40
+ text-align: center;
41
+ margin-top: spacing.$spacing-05;
42
+ column-gap: spacing.$spacing-05;
43
+ }
44
+
45
+ .formContainer {
46
+ flex-grow: 1;
47
+ max-height: calc(100vh - 14rem);
48
+ overflow-y: scroll;
49
+ text-align: left;
50
+ }
51
+
52
+ .formContainer :global(.cds--form-item) :global(.question-area) {
53
+ max-width: 100%;
54
+ }
55
+
56
+ .rightPanel {
57
+ min-width: 13rem;
58
+ text-align: left;
59
+ overflow-y: scroll;
60
+ display: flex;
61
+ flex-direction: column;
62
+ row-gap: spacing.$spacing-05;
63
+ }
64
+
65
+ .patientCardsSection {
66
+ margin: spacing.$spacing-05 0;
67
+ border-bottom: 1px solid colors.$gray-10;
68
+ }
69
+
70
+ .rightPanelActionButtons {
71
+ display: flex;
72
+ flex-direction: column;
73
+ row-gap: spacing.$spacing-03;
74
+ & button {
75
+ width: 100%;
76
+ text-decoration: "none";
77
+ }
78
+ }
79
+
80
+ .formSection {
81
+ display: flex;
82
+ flex-direction: column;
83
+ row-gap: 1rem;
84
+ justify-content: flex-start;
85
+ align-items: flex-start;
86
+ text-align: left;
87
+ }
88
+
89
+ .formSectionTile {
90
+ width: 500px;
91
+ }
92
+
93
+
94
+ .formError {
95
+ @include type.type-style("helper-text-02");
96
+ color: colors.$red-60;
97
+ }
@@ -0,0 +1,7 @@
1
+ import useGetAllForms from "./useGetAllForms";
2
+ import useGetPatient from "./useGetPatient";
3
+ import useFormState from "./useFormState";
4
+ import useGetEncounter from "./useGetEncounter";
5
+
6
+ export { useGetAllForms, useGetPatient, useFormState, useGetEncounter };
7
+ export * from "./usePostEndpoint";
@@ -0,0 +1,23 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ const useFormState = (formUuid) => {
4
+ const [state, setState] = useState(null);
5
+
6
+ useEffect(() => {
7
+ const handler = (e) => {
8
+ if (e.detail?.formUuid === formUuid) {
9
+ setState(e.detail?.state);
10
+ }
11
+ };
12
+
13
+ window.addEventListener("ampath-form-state", handler);
14
+
15
+ return () => {
16
+ window.removeEventListener("ampath-form-state", handler);
17
+ };
18
+ }, [formUuid]);
19
+
20
+ return state;
21
+ };
22
+
23
+ export default useFormState;
@@ -0,0 +1,45 @@
1
+ import {
2
+ openmrsFetch,
3
+ userHasAccess,
4
+ useSession,
5
+ } from "@openmrs/esm-framework";
6
+ import useSWR from "swr";
7
+
8
+ const customFormRepresentation =
9
+ "(uuid,name,display,encounterType:(uuid,name,viewPrivilege,editPrivilege),version,published,retired,resources:(uuid,name,dataType,valueReference))";
10
+
11
+ const formEncounterUrl = `/ws/rest/v1/form?v=custom:${customFormRepresentation}`;
12
+ const formEncounterUrlPoc = `/ws/rest/v1/form?v=custom:${customFormRepresentation}&q=poc`;
13
+
14
+ export function useGetAllForms(cachedOfflineFormsOnly = false) {
15
+ const session = useSession();
16
+ const showHtmlFormEntryForms = true;
17
+ const url = showHtmlFormEntryForms ? formEncounterUrl : formEncounterUrlPoc;
18
+ const { data, error } = useSWR([url, cachedOfflineFormsOnly], async () => {
19
+ const res = await openmrsFetch(url);
20
+ // show published forms, and hide component forms, and filter based on privileges
21
+ const forms =
22
+ res.data?.results?.filter(
23
+ (form) =>
24
+ // forms should be published
25
+ form.published &&
26
+ // forms should not be component forms
27
+ !/component/i.test(form.name)
28
+ // user should have privileges to edit forms
29
+ ) ?? [];
30
+
31
+ return forms;
32
+ });
33
+
34
+ return {
35
+ forms: data?.filter((form) =>
36
+ Boolean(
37
+ userHasAccess(form.encounterType?.editPrivilege?.display, session?.user)
38
+ )
39
+ ),
40
+ isLoading: !error && !data,
41
+ error,
42
+ };
43
+ }
44
+
45
+ export default useGetAllForms;