@mbehenri/openmrs-esm-opentms-meet-app 1.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 (81) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc +57 -0
  4. package/.husky/pre-commit +7 -0
  5. package/.husky/pre-push +6 -0
  6. package/.prettierignore +14 -0
  7. package/.turbo.json +18 -0
  8. package/.yarn/install-state.gz +0 -0
  9. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
  10. package/.yarn/plugins/@yarnpkg/plugin-outdated.cjs +35 -0
  11. package/.yarn/releases/yarn-3.6.1.cjs +874 -0
  12. package/.yarnrc.yml +9 -0
  13. package/LICENSE +401 -0
  14. package/README.md +26 -0
  15. package/__mocks__/react-i18next.js +55 -0
  16. package/e2e/README.md +115 -0
  17. package/e2e/core/global-setup.ts +32 -0
  18. package/e2e/core/index.ts +1 -0
  19. package/e2e/core/test.ts +20 -0
  20. package/e2e/fixtures/api.ts +26 -0
  21. package/e2e/fixtures/index.ts +1 -0
  22. package/e2e/pages/home-page.ts +9 -0
  23. package/e2e/pages/index.ts +1 -0
  24. package/e2e/specs/sample-test.spec.ts +11 -0
  25. package/e2e/support/github/Dockerfile +34 -0
  26. package/e2e/support/github/docker-compose.yml +24 -0
  27. package/e2e/support/github/run-e2e-docker-env.sh +49 -0
  28. package/example.env +6 -0
  29. package/i18next-parser.config.js +89 -0
  30. package/jest.config.js +31 -0
  31. package/package.json +108 -0
  32. package/playwright.config.ts +32 -0
  33. package/src/Extensions/AppointmentTabExt.tsx +23 -0
  34. package/src/Extensions/DemandTabExt.tsx +14 -0
  35. package/src/Extensions/MeetIframeExt.tsx +14 -0
  36. package/src/Extensions/ValidateDemandFormExt.tsx +14 -0
  37. package/src/assets/img/Logo-texte.png +0 -0
  38. package/src/assets/img/Logo-texte.sim.white.png +0 -0
  39. package/src/assets/img/Logo-texte.white.png +0 -0
  40. package/src/assets/img/Logo.png +0 -0
  41. package/src/assets/img/favicon.ico +0 -0
  42. package/src/components/Appointment/index.scss +91 -0
  43. package/src/components/Appointment/index.tsx +207 -0
  44. package/src/components/Appointment/menu.scss +7 -0
  45. package/src/components/Appointment/menu.tsx +48 -0
  46. package/src/components/Appointment/tab.tsx +162 -0
  47. package/src/components/Demand/form.scss +19 -0
  48. package/src/components/Demand/form.tsx +236 -0
  49. package/src/components/Demand/index.tsx +0 -0
  50. package/src/components/Demand/tab.scss +145 -0
  51. package/src/components/Demand/tab.tsx +315 -0
  52. package/src/components/EmptyLayout/index.scss +69 -0
  53. package/src/components/EmptyLayout/index.tsx +32 -0
  54. package/src/components/MeetIframe/index.scss +56 -0
  55. package/src/components/MeetIframe/index.tsx +117 -0
  56. package/src/config-schema.ts +45 -0
  57. package/src/dashboard.meta.ts +12 -0
  58. package/src/declarations.d.ts +6 -0
  59. package/src/index.ts +75 -0
  60. package/src/pages/home/home.component.tsx +8 -0
  61. package/src/privileges/doctor.ts +213 -0
  62. package/src/repositories/Opencare/index.ts +12 -0
  63. package/src/repositories/Opencare/prodRepository.ts +176 -0
  64. package/src/repositories/Opencare/repository.ts +34 -0
  65. package/src/repositories/TypeRepository.ts +1 -0
  66. package/src/repositories/env.ts +7 -0
  67. package/src/repositories/errors.ts +13 -0
  68. package/src/root.component.tsx +46 -0
  69. package/src/root.scss +15 -0
  70. package/src/root.test.tsx +54 -0
  71. package/src/routes.json +44 -0
  72. package/src/services/doctor.ts +165 -0
  73. package/src/setup-tests.ts +1 -0
  74. package/src/utils.ts +41 -0
  75. package/translations/en.json +1 -0
  76. package/translations/es.json +24 -0
  77. package/translations/fr.json +24 -0
  78. package/translations/he.json +24 -0
  79. package/translations/km.json +24 -0
  80. package/tsconfig.json +24 -0
  81. package/webpack.config.js +1 -0
@@ -0,0 +1,145 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .totalDemand {
7
+ @include type.type-style('heading-compact-02');
8
+ color: colors.$gray-70;
9
+ display: flex;
10
+ }
11
+
12
+ .totalDemandLength {
13
+ color: var(--brand-03);
14
+ display: block;
15
+ margin-left: 6px;
16
+ }
17
+
18
+ .expandedRow>td>div {
19
+ max-height: max-content !important;
20
+ }
21
+
22
+ .expandedRow td {
23
+ padding: 0 2rem;
24
+ }
25
+
26
+ .expandedRow th[colspan] td[colspan]>div:first-child {
27
+ padding: 0 1rem;
28
+ }
29
+
30
+ .hiddenRow {
31
+ display: none;
32
+ }
33
+
34
+ .container {
35
+ border: 1px solid colors.$gray-20;
36
+ }
37
+
38
+ .headerContainer {
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ padding: layout.$spacing-04 layout.$spacing-05 layout.$spacing-04 layout.$spacing-05;
43
+ background-color: colors.$white-0;
44
+ }
45
+
46
+ .tabletHeading {
47
+ h4 {
48
+ @include type.type-style('heading-03');
49
+ color: colors.$gray-70;
50
+ }
51
+ }
52
+
53
+ .desktopHeading,
54
+ .tabletHeading {
55
+ display: flex;
56
+ justify-content: space-between;
57
+ text-align: left;
58
+ text-transform: capitalize;
59
+
60
+ h4 {
61
+ @include type.type-style('heading-compact-02');
62
+ color: colors.$gray-70;
63
+
64
+ &:after {
65
+ content: '';
66
+ display: block;
67
+ width: 2rem;
68
+ padding-top: 3px;
69
+ border-bottom: 0.375rem solid var(--brand-03);
70
+ }
71
+ }
72
+ }
73
+
74
+ .emptyStateContent {
75
+ @include type.type-style('heading-compact-01');
76
+ color: $text-02;
77
+ margin-top: layout.$spacing-05;
78
+ margin-bottom: layout.$spacing-03;
79
+ }
80
+
81
+ .searchbar {
82
+ input {
83
+ background-color: colors.$gray-10;
84
+ }
85
+ }
86
+
87
+ .link {
88
+ text-decoration: none;
89
+ max-width: 50%;
90
+ }
91
+
92
+ .toolbar {
93
+ display: flex;
94
+ justify-content: space-between;
95
+ align-items: center;
96
+ }
97
+
98
+ .menuItem {
99
+ max-width: none;
100
+ }
101
+
102
+ .content {
103
+ @include type.type-style('heading-compact-02');
104
+ color: $text-02;
105
+ margin-bottom: 0.5rem;
106
+ }
107
+
108
+ .tileContainer {
109
+ background-color: $ui-02;
110
+ border-top: 1px solid $ui-03;
111
+ padding: 5rem 0;
112
+ }
113
+
114
+ .tile {
115
+ margin: auto;
116
+ width: fit-content;
117
+ }
118
+
119
+ .tileContent {
120
+ display: flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ }
124
+
125
+ .layer {
126
+ height: 100%;
127
+
128
+ :global(.cds--btn--primary) {
129
+ background-color: unset;
130
+ }
131
+ }
132
+
133
+ // Overriding styles for RTL support
134
+ html[dir='rtl'] {
135
+ .headerContainer {
136
+ padding: layout.$spacing-04 layout.$spacing-05 layout.$spacing-04 0;
137
+ svg {
138
+ margin-left: 0;
139
+ margin-right: layout.$spacing-03;
140
+ }
141
+ h4 {
142
+ text-align: right;
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,315 @@
1
+ import React, { useCallback, useEffect, useMemo } from "react";
2
+ import { useState } from "react";
3
+ import {
4
+ Button,
5
+ DataTable,
6
+ DataTableSkeleton,
7
+ type DataTableHeader,
8
+ Layer,
9
+ Table,
10
+ TableBody,
11
+ TableCell,
12
+ TableContainer,
13
+ TableHead,
14
+ TableHeader,
15
+ TableRow,
16
+ Tile,
17
+ Pagination,
18
+ } from "@carbon/react";
19
+
20
+ import {
21
+ type ToastType,
22
+ isDesktop,
23
+ showModal,
24
+ showToast,
25
+ useConfig,
26
+ useLayoutType,
27
+ formatDatetime,
28
+ parseDate,
29
+ usePagination,
30
+ } from "@openmrs/esm-framework";
31
+ import { EmptyLayout } from "../EmptyLayout";
32
+ import styles from "./tab.scss";
33
+ import DoctorService from "../../services/doctor";
34
+ import env from "../../repositories/env";
35
+ import { Search } from "@carbon/react";
36
+ import { useTranslation } from "react-i18next";
37
+ import { getPageSizes } from "../../utils";
38
+
39
+ const DemandTab: React.FC = (/* {} */) => {
40
+ //I. hooks
41
+ const [loading, setLoading] = useState(true);
42
+ const { t } = useTranslation();
43
+ const [error, setError] = useState(false);
44
+ const layout = useLayoutType();
45
+ const responsiveSize = isDesktop(layout) ? "sm" : "lg";
46
+ const [demands, setDemands] = useState([]);
47
+ const [searchString, setSearchString] = useState("");
48
+ const [processing, setProcessing] = useState(false);
49
+ const [pageSize, setPageSize] = useState(5);
50
+
51
+ //recupération de la configuration
52
+ const conf = useConfig();
53
+
54
+ // update env variable
55
+
56
+ env.API_HOST = conf["API_HOST"];
57
+ env.API_PASSWORD = conf["API_PASSWORD"];
58
+ env.API_PORT = conf["API_PORT"];
59
+ env.API_USER = conf["API_USER"];
60
+ env.API_SECURE = conf["API_SECURE"];
61
+ const doctorService = useMemo(() => DoctorService.getInstance(), []);
62
+
63
+ //const [reload, setReload] = useState("");
64
+ // colonnes du tableau
65
+ const headers: Array<typeof DataTableHeader> = useMemo(
66
+ () => [
67
+ { key: "numero", header: "N°" },
68
+ { key: "patient", header: "Patient" },
69
+ { key: "service", header: "Service" },
70
+ { key: "date", header: "Date" },
71
+ { key: "action", header: "Action" },
72
+ ],
73
+ []
74
+ );
75
+
76
+ // chargement des démandes
77
+ useEffect(() => {
78
+ const fun = async () => {
79
+ setLoading(true);
80
+ setError(false);
81
+ await doctorService
82
+ .getDemands()
83
+ .then((demands) => {
84
+ if (demands) {
85
+ setDemands(demands);
86
+ } else {
87
+ setError(true);
88
+ }
89
+ })
90
+ .finally(() => {
91
+ setLoading(false);
92
+ });
93
+ };
94
+ fun();
95
+
96
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
97
+ return () => {};
98
+ }, [doctorService]);
99
+
100
+ // filtrage et formatage des demandes
101
+ const mapDemands = useMemo(() => {
102
+ const map = new Map();
103
+ demands.forEach((demand) => {
104
+ map.set(demand.id, demand);
105
+ });
106
+ return map;
107
+ }, [demands]);
108
+
109
+ const tableRows = useMemo(
110
+ () =>
111
+ demands
112
+ .map((demand, i) => {
113
+ return {
114
+ ...demand,
115
+ date: formatDatetime(parseDate(demand.date), {
116
+ mode: "wide",
117
+ }),
118
+ numero: i + 1,
119
+ };
120
+ })
121
+ .filter((demand) => {
122
+ if (searchString === "") {
123
+ return true;
124
+ }
125
+ if (demand.date && `${demand.date}`.includes(searchString)) {
126
+ return true;
127
+ }
128
+ if (demand.service && `${demand.service}`.includes(searchString)) {
129
+ return true;
130
+ }
131
+ return false;
132
+ }),
133
+ [demands, searchString]
134
+ );
135
+
136
+ // pagination des demandes
137
+ const { results, goTo, currentPage } = usePagination(tableRows, pageSize);
138
+
139
+ // fonction de rejet
140
+ const handleReject = useCallback(
141
+ async (demand) => {
142
+ if (!processing) {
143
+ setProcessing(true);
144
+ const res = await doctorService.rejectDemand(demand.id);
145
+ let message = "";
146
+ let type: ToastType = "error";
147
+ if (res) {
148
+ message = `the demand initiated by ${demand.patient} have been rejected`;
149
+ await doctorService.getDemands().then((demands) => {
150
+ if (demands) {
151
+ setDemands(demands);
152
+ type = "success";
153
+ } else {
154
+ setError(true);
155
+ }
156
+ });
157
+ } else {
158
+ message = `Erreur de rejet`;
159
+ }
160
+ setProcessing(false);
161
+ showToast({ description: message, kind: type });
162
+ }
163
+ },
164
+ [doctorService, processing]
165
+ );
166
+
167
+ // fonction de validation
168
+ const handleValidate = useCallback(
169
+ async (demand) => {
170
+ if (!processing) {
171
+ setProcessing(true);
172
+ const dispose = showModal(
173
+ "opencare-validate-demand-form",
174
+ {
175
+ demand,
176
+ onClose: () => {
177
+ dispose();
178
+ },
179
+ },
180
+ async () => {
181
+ await doctorService.getDemands().then((demands) => {
182
+ if (demands) {
183
+ setDemands(demands);
184
+ } else {
185
+ setError(true);
186
+ }
187
+ });
188
+ setProcessing(false);
189
+ }
190
+ );
191
+ }
192
+ },
193
+ [doctorService, processing]
194
+ );
195
+
196
+ // II. returns
197
+ if (loading) {
198
+ return <DataTableSkeleton role="progressbar" row={5} />;
199
+ }
200
+
201
+ if (error) {
202
+ return <span>Error</span>;
203
+ }
204
+
205
+ if (demands.length == 0) {
206
+ return <EmptyLayout headerTitle={"Demands"} displayText={"demands"} />;
207
+ }
208
+
209
+ return (
210
+ <Layer className={styles.container}>
211
+ <Tile className={styles.headerContainer}>
212
+ <div
213
+ className={
214
+ isDesktop(layout) ? styles.desktopHeading : styles.tabletHeading
215
+ }
216
+ >
217
+ <h4>Demands</h4>
218
+ </div>
219
+ <span className={styles.totalDemand}>
220
+ Total :
221
+ <span className={styles.totalDemandLength}>{demands.length}</span>
222
+ </span>
223
+ </Tile>
224
+ <div className={styles.toolbar}>
225
+ <Search
226
+ className={styles.searchbar}
227
+ labelText=""
228
+ placeholder={t("filterTable", "Filter table")}
229
+ onChange={(event) => setSearchString(event.target.value)}
230
+ size={responsiveSize}
231
+ />
232
+ </div>
233
+ <DataTable
234
+ rows={results}
235
+ headers={headers}
236
+ isSortable
237
+ size={responsiveSize}
238
+ useZebraStyles
239
+ >
240
+ {({
241
+ rows,
242
+ headers,
243
+ getHeaderProps,
244
+ getRowProps,
245
+ getTableProps,
246
+ getTableContainerProps,
247
+ }) => (
248
+ <>
249
+ <TableContainer {...getTableContainerProps()}>
250
+ <Table {...getTableProps()}>
251
+ <TableHead>
252
+ <TableRow>
253
+ {headers.map((header) => (
254
+ <TableHeader {...getHeaderProps({ header })}>
255
+ {header.header}
256
+ </TableHeader>
257
+ ))}
258
+ </TableRow>
259
+ </TableHead>
260
+ <TableBody>
261
+ {rows.map((row) => (
262
+ <TableRow {...getRowProps({ row })}>
263
+ {row.cells.map((cell) => (
264
+ <TableCell key={cell.id}>
265
+ {cell.value ? (
266
+ cell.value
267
+ ) : (
268
+ <TableCell>
269
+ <Button
270
+ kind="danger"
271
+ size="small"
272
+ disable={processing}
273
+ onClick={() => {
274
+ handleReject(mapDemands.get(row.id));
275
+ }}
276
+ >
277
+ Reject
278
+ </Button>
279
+ <Button
280
+ kind="primary"
281
+ size="small"
282
+ disable={processing}
283
+ onClick={() => {
284
+ handleValidate(mapDemands.get(row.id));
285
+ }}
286
+ >
287
+ Validation
288
+ </Button>
289
+ </TableCell>
290
+ )}
291
+ </TableCell>
292
+ ))}
293
+ </TableRow>
294
+ ))}
295
+ </TableBody>
296
+ </Table>
297
+ </TableContainer>
298
+ </>
299
+ )}
300
+ </DataTable>
301
+ <Pagination
302
+ page={currentPage}
303
+ pageSize={pageSize}
304
+ pageSizes={getPageSizes(tableRows, 5) ?? []}
305
+ onChange={({ page, pageSize }) => {
306
+ goTo(page);
307
+ setPageSize(pageSize);
308
+ }}
309
+ totalItems={tableRows.length}
310
+ />
311
+ </Layer>
312
+ );
313
+ };
314
+
315
+ export default DemandTab;
@@ -0,0 +1,69 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .content {
6
+ @include type.type-style('heading-compact-01');
7
+ color: $text-02;
8
+ margin-top: layout.$spacing-05;
9
+ margin-bottom: layout.$spacing-03;
10
+ }
11
+
12
+ .displayText {
13
+ text-transform: lowercase;
14
+ }
15
+
16
+ .desktopHeading {
17
+ h4 {
18
+ @include type.type-style('heading-compact-02');
19
+ color: $text-02;
20
+ }
21
+ }
22
+
23
+ .tabletHeading {
24
+ h4 {
25
+ @include type.type-style('heading-03');
26
+ color: $text-02;
27
+ }
28
+ }
29
+
30
+ .desktopHeading,
31
+ .tabletHeading {
32
+ text-align: left;
33
+ text-transform: capitalize;
34
+ margin-bottom: layout.$spacing-05;
35
+
36
+ h4:after {
37
+ content: '';
38
+ display: block;
39
+ width: 2rem;
40
+ padding-top: 0.188rem;
41
+ border-bottom: 0.375rem solid var(--brand-03);
42
+ }
43
+ }
44
+
45
+ .heading:after {
46
+ content: '';
47
+ display: block;
48
+ width: 2rem;
49
+ padding-top: 0.188rem;
50
+ border-bottom: 0.375rem solid var(--brand-03);
51
+ }
52
+
53
+ .tile {
54
+ text-align: center;
55
+ border: 1px solid $ui-03;
56
+ }
57
+
58
+ .header4 {
59
+ @include type.type-style('heading-compact-01');
60
+ margin-bottom: 1rem;
61
+ }
62
+
63
+ // Overriding styles for RTL support
64
+ html[dir='rtl'] {
65
+ .desktopHeading,
66
+ .tabletHeading {
67
+ text-align: right;
68
+ }
69
+ }
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { Layer, Tile } from "@carbon/react";
3
+ import { useLayoutType } from "@openmrs/esm-framework";
4
+ import styles from "./index.scss";
5
+
6
+ export interface EmptyLayoutProps {
7
+ displayText: string;
8
+ headerTitle: string;
9
+ }
10
+
11
+ export const EmptyLayout: React.FC<EmptyLayoutProps> = ({
12
+ headerTitle,
13
+ displayText,
14
+ }) => {
15
+ const isTablet = useLayoutType() === "tablet";
16
+
17
+ return (
18
+ <Layer>
19
+ <Tile className={styles.tile}>
20
+ <div
21
+ className={isTablet ? styles.tabletHeading : styles.desktopHeading}
22
+ >
23
+ <h4>{headerTitle}</h4>
24
+ </div>
25
+ <p className={styles.content}>
26
+ There are no <span className={styles.displayText}>{displayText}</span>{" "}
27
+ for this location
28
+ </p>
29
+ </Tile>
30
+ </Layer>
31
+ );
32
+ };
@@ -0,0 +1,56 @@
1
+ @use '@carbon/colors';
2
+
3
+ $color-blue-10: #edf5ff;
4
+
5
+ $color-spinner: #2a73cd;
6
+ /*$size-spinner: 56px;*/
7
+
8
+ .contentViewWrapper {
9
+ background-color: $color-blue-10;
10
+ position: relative;
11
+ width: 100%;
12
+ height: 550px;
13
+ }
14
+
15
+ @keyframes spinner-annim {
16
+ to {
17
+ transform: rotate(1turn);
18
+ }
19
+ }
20
+
21
+ .preloader {
22
+ background-color: $color-blue-10;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ position: absolute;
27
+ width: 100%;
28
+ height: 100%;
29
+ top: 0;
30
+ left: 0;
31
+ z-index: 100000;
32
+ }
33
+
34
+ .preloader .logo{
35
+ width: 50px;
36
+ height: 50px;
37
+ }
38
+
39
+ .spinner {
40
+ display: block;
41
+ position: absolute;
42
+ top: 0;
43
+ left: 0;
44
+ width: 56px;
45
+ height: 56px;
46
+ border-radius: 50%;
47
+ background: conic-gradient(#0000 10%, $color-spinner);
48
+ animation: spinner-annim 1s infinite linear;
49
+ }
50
+
51
+
52
+
53
+ .viewer {
54
+ width: 100%;
55
+ height: 100%;
56
+ }