@openmrs/esm-openconceptlab-app 3.0.1-pre.5 → 3.0.1-pre.55

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 (71) hide show
  1. package/.turbo/turbo-build.log +33 -36
  2. package/dist/130.js +2 -0
  3. package/dist/130.js.LICENSE.txt +5 -0
  4. package/dist/130.js.map +1 -0
  5. package/dist/316.js +1 -1
  6. package/dist/316.js.map +1 -0
  7. package/dist/392.js +1 -0
  8. package/dist/392.js.map +1 -0
  9. package/dist/442.js +2 -0
  10. package/dist/442.js.LICENSE.txt +5 -0
  11. package/dist/442.js.map +1 -0
  12. package/dist/553.js +1 -0
  13. package/dist/553.js.map +1 -0
  14. package/dist/574.js +1 -1
  15. package/dist/784.js +1 -1
  16. package/dist/784.js.map +1 -0
  17. package/dist/842.js +1 -0
  18. package/dist/842.js.map +1 -0
  19. package/dist/853.js +1 -0
  20. package/dist/853.js.map +1 -0
  21. package/dist/912.js +2 -0
  22. package/dist/912.js.LICENSE.txt +9 -0
  23. package/dist/912.js.map +1 -0
  24. package/dist/main.js +1 -1
  25. package/dist/main.js.map +1 -0
  26. package/dist/openmrs-esm-openconceptlab-app.js +1 -1
  27. package/dist/openmrs-esm-openconceptlab-app.js.buildmanifest.json +144 -55
  28. package/dist/openmrs-esm-openconceptlab-app.js.map +1 -0
  29. package/dist/routes.json +1 -0
  30. package/package.json +3 -3
  31. package/src/admin-ocl-card-link.component.tsx +23 -0
  32. package/src/declarations.d.tsx +2 -0
  33. package/src/import/import.component.tsx +38 -21
  34. package/src/import/import.resource.ts +1 -1
  35. package/src/import/import.test.tsx +13 -15
  36. package/src/index.ts +9 -51
  37. package/src/previous-imports/import-overview/import-items.component.tsx +124 -0
  38. package/src/previous-imports/import-overview/import-items.resource.ts +10 -0
  39. package/src/previous-imports/import-overview/import-items.scss +24 -0
  40. package/src/previous-imports/import-overview/import-items.test.tsx +82 -0
  41. package/src/previous-imports/import-overview/import-overview.component.tsx +65 -0
  42. package/src/previous-imports/import-overview/import-overview.scss +11 -0
  43. package/src/previous-imports/import-overview/import-overview.test.tsx +82 -0
  44. package/src/previous-imports/previous-imports.component.tsx +136 -0
  45. package/src/previous-imports/previous-imports.resource.ts +17 -0
  46. package/src/previous-imports/previous-imports.scss +39 -0
  47. package/src/previous-imports/previous-imports.test.tsx +76 -0
  48. package/src/root.component.tsx +12 -9
  49. package/src/root.test.tsx +4 -4
  50. package/src/routes.json +20 -0
  51. package/src/subscription/subscription.component.tsx +58 -24
  52. package/src/subscription/subscription.resource.ts +1 -1
  53. package/src/subscription/subscription.test.tsx +22 -20
  54. package/src/types.ts +10 -0
  55. package/src/utils.ts +27 -0
  56. package/translations/en.json +44 -26
  57. package/tsconfig.json +1 -19
  58. package/webpack.config.js +1 -1
  59. package/dist/15.js +0 -1
  60. package/dist/538.js +0 -2
  61. package/dist/538.js.LICENSE.txt +0 -20
  62. package/dist/709.js +0 -1
  63. package/dist/858.js +0 -2
  64. package/dist/858.js.LICENSE.txt +0 -1
  65. package/dist/862.js +0 -1
  66. package/dist/openmrs-esm-openconceptlab-app.old +0 -1
  67. package/locales/en/translation.json +0 -35
  68. package/locales/fr/translation.json +0 -35
  69. /package/src/import/{import.component.scss → import.scss} +0 -0
  70. /package/src/{root.component.scss → root.scss} +0 -0
  71. /package/src/subscription/{subscription.component.scss → subscription.scss} +0 -0
@@ -1,4 +1,4 @@
1
- import { showNotification } from '@openmrs/esm-framework';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import {
3
3
  Button,
4
4
  ButtonSkeleton,
@@ -8,17 +8,20 @@ import {
8
8
  FileUploaderSkeleton,
9
9
  Form,
10
10
  Grid,
11
+ InlineLoading,
12
+ Stack,
11
13
  SkeletonText,
12
14
  } from '@carbon/react';
13
- import React, { useCallback, useEffect, useState } from 'react';
14
15
  import { useTranslation } from 'react-i18next';
15
- import styles from './import.component.scss';
16
+ import { showNotification } from '@openmrs/esm-framework';
16
17
  import { startImportWithFile, startImportWithSubscription, useSubscription } from './import.resource';
18
+ import styles from './import.scss';
17
19
 
18
20
  const Import: React.FC = () => {
19
21
  const { t } = useTranslation();
20
22
  const [isSubscriptionAvailable, setIsSubscriptionAvailable] = useState(false);
21
23
  const [file, setFile] = useState<File>();
24
+ const [isFileUploading, setIsFileUploading] = useState(false);
22
25
 
23
26
  const { data: subscription, isLoading, isError } = useSubscription();
24
27
 
@@ -34,7 +37,7 @@ const Import: React.FC = () => {
34
37
  if (fileToUpload.type !== 'application/zip') {
35
38
  showNotification({
36
39
  kind: 'error',
37
- description: t('fileFormatError'),
40
+ description: t('fileFormatError', 'Only .zip files are allowed'),
38
41
  });
39
42
  } else {
40
43
  setFile(fileToUpload);
@@ -55,7 +58,7 @@ const Import: React.FC = () => {
55
58
  if (!isSubscriptionAvailable) {
56
59
  showNotification({
57
60
  kind: 'error',
58
- description: t('noSubscriptionError'),
61
+ description: t('noSubscriptionError', 'No saved subscription'),
59
62
  });
60
63
  return;
61
64
  }
@@ -66,12 +69,12 @@ const Import: React.FC = () => {
66
69
  if (response.status === 201) {
67
70
  showNotification({
68
71
  kind: 'success',
69
- description: t('importSuccess'),
72
+ description: t('importSuccess', 'Import started successfully'),
70
73
  });
71
74
  } else {
72
75
  showNotification({
73
76
  kind: 'error',
74
- description: t('importFailed'),
77
+ description: t('importFailed', 'Import failed'),
75
78
  });
76
79
  }
77
80
  },
@@ -80,13 +83,15 @@ const Import: React.FC = () => {
80
83
 
81
84
  const handleImportWithFile = useCallback(
82
85
  async (evt: React.FormEvent<HTMLFormElement>) => {
86
+ setIsFileUploading(true);
83
87
  evt.preventDefault();
84
88
  evt.stopPropagation();
85
89
 
86
90
  if (!file) {
91
+ setIsFileUploading(false);
87
92
  showNotification({
88
93
  kind: 'error',
89
- description: t('noFileSelected'),
94
+ description: t('noFileSelected', 'No file selected'),
90
95
  });
91
96
  return;
92
97
  }
@@ -96,14 +101,16 @@ const Import: React.FC = () => {
96
101
 
97
102
  if (response.status === 201) {
98
103
  setFile(null);
104
+ setIsFileUploading(false);
99
105
  showNotification({
100
106
  kind: 'success',
101
- description: t('importSuccess'),
107
+ description: t('importSuccess', 'Import started successfully'),
102
108
  });
103
109
  } else {
110
+ setIsFileUploading(false);
104
111
  showNotification({
105
112
  kind: 'error',
106
- description: t('importFailed'),
113
+ description: t('importFailed', 'Import failed'),
107
114
  });
108
115
  }
109
116
  },
@@ -134,7 +141,7 @@ const Import: React.FC = () => {
134
141
  if (isError) {
135
142
  showNotification({
136
143
  kind: 'error',
137
- description: t('subscriptionError'),
144
+ description: t('subscriptionError', 'Error occured while fetching the subscription'),
138
145
  });
139
146
  }
140
147
 
@@ -142,23 +149,30 @@ const Import: React.FC = () => {
142
149
  <Grid className={styles.grid}>
143
150
  <Column sm={4} md={8} lg={10}>
144
151
  <Form onSubmit={handleImportWithSubscription}>
145
- <h3 className={styles.productiveHeading03}>{t('importConcepts')}</h3>
146
- <p className={styles.formText}>{t('importInstructions')}</p>
152
+ <h3 className={styles.productiveHeading03}>{t('importConcepts', 'Import Concepts')}</h3>
153
+ <p className={styles.formText}>
154
+ {t(
155
+ 'importInstructions',
156
+ 'Avoid importing during data entry hours, because the operation may significantly slow down the system. You can either use the subscription or a zip file to import concepts.',
157
+ )}
158
+ </p>
147
159
  <Button kind="primary" type="submit" disabled={!isSubscriptionAvailable}>
148
- {t('importFromSubscription')}
160
+ {t('importFromSubscription', 'Import from Subscription')}
149
161
  </Button>
150
162
  </Form>
151
163
 
152
164
  <Form className={styles.form} onSubmit={handleImportWithFile}>
153
- <h3 className={styles.productiveHeading03}>{t('importFromFileHeading')}</h3>
154
- <p className={styles.formText}>{t('importFromFileInfo')}</p>
165
+ <h3 className={styles.productiveHeading03}>{t('importFromFileHeading', 'Import from file (Offline)')}</h3>
166
+ <p className={styles.formText}>
167
+ {t('importFromFileInfo', 'You can import data without setting up a subscription by using this option.')}
168
+ </p>
155
169
  {file ? (
156
170
  <FileUploaderItem
157
171
  key={file.name}
158
172
  name={file.name}
159
173
  size="default"
160
174
  status="edit"
161
- iconDescription={t('fileAdded')}
175
+ iconDescription={t('fileAdded', 'File Added')}
162
176
  onDelete={handleFileUploaderItemClick}
163
177
  style={{ backgroundColor: '#e0e0e0' }}
164
178
  />
@@ -166,14 +180,17 @@ const Import: React.FC = () => {
166
180
  <FileUploaderDropContainer
167
181
  accept={['application/zip']}
168
182
  multiple
169
- labelText={t('importFromFileDragInfo')}
183
+ labelText={t('importFromFileDragInfo', 'Drag and drop file here or click to upload')}
170
184
  onAddFiles={onAddFiles}
171
185
  style={{ marginBottom: '1.5rem' }}
172
186
  />
173
187
  )}
174
- <Button kind="primary" type="submit">
175
- {t('importFromFile')}
176
- </Button>
188
+ <Stack gap={6} orientation="horizontal">
189
+ <Button kind="primary" type="submit">
190
+ {t('importFromFile', 'Import from file')}
191
+ </Button>
192
+ {isFileUploading && <InlineLoading description={t('uploading', 'Uploading...')} />}
193
+ </Stack>
177
194
  </Form>
178
195
  </Column>
179
196
  </Grid>
@@ -1,6 +1,6 @@
1
1
  import { openmrsFetch } from '@openmrs/esm-framework';
2
2
  import useSWR from 'swr';
3
- import { Import, Subscription } from '../types';
3
+ import type { Import, Subscription } from '../types';
4
4
 
5
5
  export function useSubscription() {
6
6
  const { data, error, isValidating } = useSWR<{ data: { results: Subscription[] } }, Error>(
@@ -44,14 +44,12 @@ describe(`Import component`, () => {
44
44
  renderImportComponent();
45
45
  await waitForLoadingToFinish();
46
46
 
47
- expect(screen.getByText('importConcepts')).toBeVisible();
48
- expect(screen.getByText('importInstructions')).toBeVisible();
49
- expect(screen.getByText('importFromSubscription')).toBeVisible();
50
-
51
- expect(screen.getByText('importFromFileHeading')).toBeVisible();
52
- expect(screen.getByText('importFromFileInfo')).toBeVisible();
53
- expect(screen.getByText('importFromFile')).toBeEnabled();
54
- expect(screen.queryByText('fileAdded')).not.toBeInTheDocument();
47
+ expect(screen.getByText('Import Concepts')).toBeVisible();
48
+ expect(screen.getByText('Import from Subscription')).toBeVisible();
49
+
50
+ expect(screen.getByText('Import from file (Offline)')).toBeVisible();
51
+ expect(screen.getByText('Import from file')).toBeEnabled();
52
+ expect(screen.queryByText('File Added')).not.toBeInTheDocument();
55
53
  });
56
54
 
57
55
  it(`renders correctly when there is no subscription`, async () => {
@@ -59,8 +57,8 @@ describe(`Import component`, () => {
59
57
  renderImportComponent();
60
58
  await waitForLoadingToFinish();
61
59
 
62
- expect(screen.getByText('importFromSubscription')).toBeDisabled();
63
- expect(screen.getByText('importFromFile')).toBeEnabled();
60
+ expect(screen.getByText('Import from Subscription')).toBeDisabled();
61
+ expect(screen.getByText('Import from file')).toBeEnabled();
64
62
  });
65
63
 
66
64
  it(`renders correctly when when a subscription exists`, async () => {
@@ -68,8 +66,8 @@ describe(`Import component`, () => {
68
66
  renderImportComponent();
69
67
  await waitForLoadingToFinish();
70
68
 
71
- expect(screen.getByText('importFromSubscription')).toBeEnabled();
72
- expect(screen.getByText('importFromFile')).toBeEnabled();
69
+ await waitFor(() => expect(screen.getByText('Import from Subscription')).toBeEnabled(), { timeout: 2000 });
70
+ expect(screen.getByText('Import from file')).toBeEnabled();
73
71
  });
74
72
 
75
73
  it(`allows starting an import using the subscription`, async () => {
@@ -79,14 +77,14 @@ describe(`Import component`, () => {
79
77
 
80
78
  mockStartImportWithSubscription.mockReturnValueOnce({ status: 201 });
81
79
 
82
- await waitFor(() => userEvent.click(screen.getByText('importFromSubscription')));
80
+ await waitFor(() => userEvent.click(screen.getByText('Import from Subscription')));
83
81
 
84
82
  expect(mockStartImportWithSubscription).toHaveBeenCalledWith(new AbortController());
85
83
  expect(mockStartImportWithSubscription).toHaveBeenCalledTimes(1);
86
84
 
87
85
  expect(mockShowNotification).toHaveBeenCalledWith(
88
86
  expect.objectContaining({
89
- description: 'importSuccess',
87
+ description: 'Import started successfully',
90
88
  kind: 'success',
91
89
  }),
92
90
  );
@@ -100,6 +98,6 @@ function renderImportComponent() {
100
98
 
101
99
  function waitForLoadingToFinish() {
102
100
  return waitFor(() => {
103
- expect(screen.getByText('importConcepts')).toBeVisible, { timeout: 2000 };
101
+ expect(screen.getByText('Import Concepts')).toBeVisible, { timeout: 2000 };
104
102
  });
105
103
  }
package/src/index.ts CHANGED
@@ -1,61 +1,19 @@
1
- /**
2
- * This is the entrypoint file of the application. It communicates the
3
- * important features of this microfrontend to the app shell. It
4
- * connects the app shell to the React application(s) that make up this
5
- * microfrontend.
6
- */
7
-
8
1
  import { getAsyncLifecycle, defineConfigSchema } from '@openmrs/esm-framework';
9
2
  import { configSchema } from './config-schema';
10
3
 
11
- /**
12
- * This tells the app shell how to obtain translation files: that they
13
- * are JSON files in the directory `../translations` (which you should
14
- * see in the directory structure).
15
- */
16
- const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
4
+ const moduleName = '@openmrs/esm-openconceptlab-app';
17
5
 
18
- /**
19
- * This tells the app shell what versions of what OpenMRS backend modules
20
- * are expected. Warnings will appear if suitable modules are not
21
- * installed. The keys are the part of the module name after
22
- * `openmrs-module-`; e.g., `openmrs-module-fhir2` becomes `fhir2`.
23
- */
24
- const backendDependencies = {
25
- openconceptlab: '^1.2.0',
26
- 'webservices.rest': '^2.2.0',
6
+ const options = {
7
+ featureName: 'openconceptlab',
8
+ moduleName,
27
9
  };
28
10
 
29
- /**
30
- * This function performs any setup that should happen at microfrontend
31
- * load-time (such as defining the config schema) and then returns an
32
- * object which describes how the React application(s) should be
33
- * rendered.
34
- *
35
- * In this example, our return object contains a single page definition.
36
- * It tells the app shell that the default export of `greeter.tsx`
37
- * should be rendered when the route matches `hello`. The full route
38
- * will be `openmrsSpaBase() + 'hello'`, which is usually
39
- * `/openmrs/spa/hello`.
40
- */
41
- function setupOpenMRS() {
42
- const moduleName = '@openmrs/esm-openconceptlab-app';
43
-
44
- const options = {
45
- featureName: 'openconceptlab',
46
- moduleName,
47
- };
11
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
48
12
 
13
+ export function startupApp() {
49
14
  defineConfigSchema(moduleName, configSchema);
50
-
51
- return {
52
- pages: [
53
- {
54
- load: getAsyncLifecycle(() => import('./root.component'), options),
55
- route: 'ocl',
56
- },
57
- ],
58
- };
59
15
  }
60
16
 
61
- export { backendDependencies, importTranslation, setupOpenMRS };
17
+ export const root = getAsyncLifecycle(() => import('./root.component'), options);
18
+
19
+ export const adminOclCardLink = getAsyncLifecycle(() => import('./admin-ocl-card-link.component'), options);
@@ -0,0 +1,124 @@
1
+ import { showNotification, usePagination } from '@openmrs/esm-framework';
2
+ import {
3
+ DataTableSkeleton,
4
+ Link,
5
+ Pagination,
6
+ PaginationSkeleton,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@carbon/react';
14
+ import React, { Fragment, useCallback, useEffect, useState } from 'react';
15
+ import { useTranslation } from 'react-i18next';
16
+ import { getImportDetails } from './import-items.resource';
17
+ import type { ImportItem } from '../../types';
18
+ import styles from './import-items.scss';
19
+
20
+ interface ImportItemsProps {
21
+ importUuid: string;
22
+ }
23
+
24
+ const ImportItems: React.FC<ImportItemsProps> = ({ importUuid }) => {
25
+ const { t } = useTranslation();
26
+ const [isLoading, setIsLoading] = useState<Boolean>();
27
+ const [pageSize, setPageSize] = useState(5);
28
+
29
+ const [selectedImportItems, setSelectedImportItems] = useState<ImportItem[]>([]);
30
+ const { results, currentPage, goTo } = usePagination(selectedImportItems, pageSize);
31
+
32
+ const handleImportDetails = useCallback(
33
+ async (uuid: string) => {
34
+ setIsLoading(true);
35
+ const abortController = new AbortController();
36
+
37
+ const response = await getImportDetails(uuid, abortController);
38
+
39
+ if (response.ok) {
40
+ setSelectedImportItems(response.data.results);
41
+ } else {
42
+ showNotification({
43
+ title: t('importItemsFetchError', 'Error occured while fetching the import items'),
44
+ kind: 'error',
45
+ critical: true,
46
+ description: JSON.stringify(response.data),
47
+ });
48
+ }
49
+ setIsLoading(false);
50
+ return () => abortController.abort();
51
+ },
52
+ [t],
53
+ );
54
+
55
+ useEffect(() => {
56
+ handleImportDetails(importUuid);
57
+ }, [handleImportDetails, importUuid]);
58
+
59
+ if (isLoading) {
60
+ return (
61
+ <div>
62
+ <DataTableSkeleton showHeader={false} showToolbar={false} columnCount={2} />
63
+ <PaginationSkeleton />
64
+ </div>
65
+ );
66
+ }
67
+
68
+ const headerData = [
69
+ {
70
+ header: t('conceptOrMapping', 'Concept/Mapping'),
71
+ key: 'uuid',
72
+ },
73
+ {
74
+ header: t('message', 'Message'),
75
+ key: 'errorMessage',
76
+ },
77
+ ];
78
+
79
+ return (
80
+ !isLoading && (
81
+ <div>
82
+ <Table size="sm" className={styles.tableBordered}>
83
+ <TableHead>
84
+ <TableRow className={styles.tableHeaderRow}>
85
+ {headerData.map((header, i) => (
86
+ <TableHeader key={i} isSortable={true}>
87
+ {header.header}
88
+ </TableHeader>
89
+ ))}
90
+ </TableRow>
91
+ </TableHead>
92
+ <TableBody>
93
+ {results?.map((row) => (
94
+ <Fragment key={row.uuid}>
95
+ <TableRow className={styles.tableDataRow}>
96
+ <TableCell>
97
+ <Link href={row.versionUrl}>
98
+ {row.type} {row.uuid}
99
+ </Link>
100
+ </TableCell>
101
+ <TableCell>{row.errorMessage}</TableCell>
102
+ </TableRow>
103
+ </Fragment>
104
+ ))}
105
+ </TableBody>
106
+ </Table>
107
+ <Pagination
108
+ className={styles.pagination}
109
+ size="sm"
110
+ page={currentPage}
111
+ pageSize={pageSize}
112
+ pageSizes={[5, 10, 20, 50, 100]}
113
+ totalItems={selectedImportItems?.length}
114
+ onChange={({ page, pageSize }) => {
115
+ goTo(page);
116
+ setPageSize(pageSize);
117
+ }}
118
+ />
119
+ </div>
120
+ )
121
+ );
122
+ };
123
+
124
+ export default ImportItems;
@@ -0,0 +1,10 @@
1
+ import { openmrsFetch } from '@openmrs/esm-framework';
2
+ import type { ImportItem } from '../../types';
3
+
4
+ export async function getImportDetails(importUuid: string, abortController?: AbortController) {
5
+ const url = `/ws/rest/v1/openconceptlab/import/${importUuid}/item?state=ERROR&v=full`;
6
+ return openmrsFetch<{ results: ImportItem[] }>(url, {
7
+ method: 'GET',
8
+ signal: abortController?.signal,
9
+ });
10
+ }
@@ -0,0 +1,24 @@
1
+ @use "@carbon/styles/scss/spacing";
2
+ @use "@carbon/styles/scss/type";
3
+ @import "~@openmrs/esm-styleguide/src/vars";
4
+
5
+ .pagination {
6
+ @include type.type-style('body-short-01');
7
+ background-color: $ui-02;
8
+ color: $text-02;
9
+ border: 1px solid $ui-03;
10
+ display: flex;
11
+ }
12
+
13
+ .tableBordered {
14
+ border: 1px solid $ui-03;
15
+ }
16
+
17
+ .tableDataRow > td {
18
+ padding-left: 1rem !important;
19
+ background-color: $ui-02 !important;
20
+ }
21
+
22
+ .tableHeaderRow > th {
23
+ background-color: $ui-03 !important;
24
+ }
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { screen, waitFor } from '@testing-library/react';
3
+ import { usePagination } from '@openmrs/esm-framework';
4
+ import { renderWithSwr } from '../../../../../tools/test-helpers';
5
+ import { mockImportItems, mockPreviousImports } from '../../../../../__mocks__/openconceptlab.mock';
6
+ import { getImportDetails } from './import-items.resource';
7
+ import ImportItems from './import-items.component';
8
+
9
+ const testProps = {
10
+ importUuid: mockPreviousImports[1].uuid,
11
+ };
12
+
13
+ const mockGetImportDetails = getImportDetails as jest.Mock;
14
+ const mockUsePagination = usePagination as jest.Mock;
15
+
16
+ jest.mock('./import-items.resource', () => {
17
+ const originalModule = jest.requireActual('./import-items.resource');
18
+
19
+ return {
20
+ ...originalModule,
21
+ getImportDetails: jest.fn(),
22
+ };
23
+ });
24
+
25
+ jest.mock('@openmrs/esm-framework', () => {
26
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
27
+
28
+ return {
29
+ ...originalModule,
30
+ usePagination: jest.fn(),
31
+ };
32
+ });
33
+
34
+ describe(`Import Items component`, () => {
35
+ afterEach(() => {
36
+ mockGetImportDetails.mockReset();
37
+ mockUsePagination.mockReset();
38
+ });
39
+
40
+ it(`renders the table`, async () => {
41
+ mockGetImportDetails.mockReturnValue({ status: 200, ok: true, data: mockImportItems });
42
+ mockUsePagination.mockReturnValue({
43
+ currentPage: 1,
44
+ goTo: () => {},
45
+ results: mockImportItems,
46
+ });
47
+ renderImportItemsComponent();
48
+ await waitForLoadingToFinish();
49
+
50
+ expect(screen.getByText('Concept/Mapping')).toBeVisible();
51
+ expect(screen.getByText('Message')).toBeVisible();
52
+ });
53
+
54
+ it(`renders the import items correctly`, async () => {
55
+ mockGetImportDetails.mockReturnValue({ status: 200, ok: true, data: mockImportItems });
56
+ mockUsePagination.mockReturnValue({
57
+ currentPage: 1,
58
+ goTo: () => {},
59
+ results: mockImportItems,
60
+ });
61
+ renderImportItemsComponent();
62
+ await waitForLoadingToFinish();
63
+
64
+ expect(screen.getByText('Concept/Mapping')).toBeVisible();
65
+ expect(screen.getByText('Message')).toBeVisible();
66
+
67
+ mockImportItems.slice(5).forEach((importItem) => {
68
+ expect(screen.getByText(importItem.type + ' ' + importItem.uuid)).toBeVisible();
69
+ expect(screen.getByText(importItem.errorMessage)).toBeVisible();
70
+ });
71
+ });
72
+ });
73
+
74
+ function renderImportItemsComponent() {
75
+ renderWithSwr(<ImportItems {...testProps} />);
76
+ }
77
+
78
+ function waitForLoadingToFinish() {
79
+ return waitFor(() => {
80
+ expect(screen.getByText('Concept/Mapping')).toBeVisible(), { timeout: 2000 };
81
+ });
82
+ }
@@ -0,0 +1,65 @@
1
+ import { formatDatetime } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import type { Import } from '../../types';
5
+ import ImportItems from './import-items.component';
6
+ import styles from './import-overview.scss';
7
+
8
+ interface ImportOverviewProps {
9
+ selectedImportObject: Import;
10
+ }
11
+
12
+ const ImportOverview: React.FC<ImportOverviewProps> = ({ selectedImportObject }) => {
13
+ const { t } = useTranslation();
14
+
15
+ return (
16
+ <div>
17
+ <span className={styles.heading01}>{t('startedOn', 'Started on')} </span>
18
+ <span className="body">{formatDatetime(new Date(selectedImportObject.localDateStarted))}</span>
19
+ <br />
20
+
21
+ <span className={styles.heading01}>{t('completedOn', 'Completed on')} </span>
22
+ <span className="body">{formatDatetime(new Date(selectedImportObject.localDateStopped))}</span>
23
+ <br />
24
+
25
+ <span className={styles.heading01}>{t('duration', 'Duration')} </span>
26
+ <span className="body">{selectedImportObject.importTime}</span>
27
+ <br />
28
+ <br />
29
+
30
+ <span className={styles.heading01}>{t('result', 'Result:')} </span>
31
+ <br />
32
+
33
+ <div className={`body ${styles.indentedContent}`}>
34
+ <span>
35
+ {selectedImportObject.upToDateItemsCount} {t('conceptsUpToDate', 'concepts up to date')}
36
+ </span>
37
+ <br />
38
+ <span>
39
+ {selectedImportObject.addedItemsCount} {t('conceptsAdded', 'concepts added')}
40
+ </span>
41
+ <br />
42
+ <span>
43
+ {selectedImportObject.updatedItemsCount} {t('conceptsUpdated', 'concepts updated')}
44
+ </span>
45
+ <br />
46
+ <span>
47
+ {selectedImportObject.retiredItemsCount} {t('conceptsRetired', 'concepts retired')}
48
+ </span>
49
+ <br />
50
+ <span>
51
+ {selectedImportObject.errorItemsCount} {t('errorsFound', 'errors found')}
52
+ </span>
53
+ <br />
54
+ <span>
55
+ {selectedImportObject.ignoredErrorsCount} {t('errorsIgnored', 'errors ignored')}
56
+ </span>
57
+ <br />
58
+ </div>
59
+ <br />
60
+ {selectedImportObject.errorItemsCount != 0 && <ImportItems importUuid={selectedImportObject.uuid} />}
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export default ImportOverview;
@@ -0,0 +1,11 @@
1
+ @use "@carbon/styles/scss/spacing";
2
+ @use "@carbon/styles/scss/type";
3
+ @import "~@openmrs/esm-styleguide/src/vars";
4
+
5
+ .heading01 {
6
+ @include type.type-style('heading-01');
7
+ }
8
+
9
+ .indentedContent {
10
+ margin-left: spacing.$spacing-07;
11
+ }