@openmrs/esm-openconceptlab-app 3.0.1-pre.7 → 3.0.1-pre.70

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 (72) 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/553.js +1 -0
  10. package/dist/553.js.map +1 -0
  11. package/dist/574.js +1 -1
  12. package/dist/608.js +2 -0
  13. package/dist/608.js.LICENSE.txt +15 -0
  14. package/dist/608.js.map +1 -0
  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 +143 -54
  28. package/dist/openmrs-esm-openconceptlab-app.js.map +1 -0
  29. package/dist/routes.json +1 -0
  30. package/jest.config.js +3 -0
  31. package/package.json +7 -5
  32. package/src/admin-ocl-card-link.component.tsx +23 -0
  33. package/src/declarations.d.tsx +2 -0
  34. package/src/import/import.component.tsx +38 -21
  35. package/src/import/import.resource.ts +1 -1
  36. package/src/import/import.test.tsx +13 -15
  37. package/src/index.ts +9 -51
  38. package/src/previous-imports/import-overview/import-items.component.tsx +23 -20
  39. package/src/previous-imports/import-overview/import-items.resource.ts +1 -1
  40. package/src/previous-imports/import-overview/import-items.test.tsx +7 -14
  41. package/src/previous-imports/import-overview/import-overview.component.tsx +2 -2
  42. package/src/previous-imports/import-overview/import-overview.test.tsx +1 -2
  43. package/src/previous-imports/previous-imports.component.tsx +4 -4
  44. package/src/previous-imports/previous-imports.resource.ts +1 -1
  45. package/src/previous-imports/previous-imports.test.tsx +1 -2
  46. package/src/root.component.tsx +8 -8
  47. package/src/root.test.tsx +4 -4
  48. package/src/routes.json +20 -0
  49. package/src/subscription/subscription.component.tsx +58 -24
  50. package/src/subscription/subscription.resource.ts +1 -1
  51. package/src/subscription/subscription.test.tsx +22 -20
  52. package/src/utils.ts +27 -0
  53. package/translations/en.json +42 -40
  54. package/tsconfig.json +1 -19
  55. package/webpack.config.js +1 -1
  56. package/dist/452.js +0 -1
  57. package/dist/645.js +0 -2
  58. package/dist/645.js.LICENSE.txt +0 -20
  59. package/dist/709.js +0 -1
  60. package/dist/858.js +0 -2
  61. package/dist/858.js.LICENSE.txt +0 -1
  62. package/dist/862.js +0 -1
  63. package/dist/openmrs-esm-openconceptlab-app.old +0 -1
  64. package/locales/en/translation.json +0 -35
  65. package/locales/fr/translation.json +0 -35
  66. package/src/setup-tests.ts +0 -1
  67. /package/src/import/{import.component.scss → import.scss} +0 -0
  68. /package/src/previous-imports/import-overview/{import-items.component.scss → import-items.scss} +0 -0
  69. /package/src/previous-imports/import-overview/{import-overview.component.scss → import-overview.scss} +0 -0
  70. /package/src/previous-imports/{previous-imports.component.scss → previous-imports.scss} +0 -0
  71. /package/src/{root.component.scss → root.scss} +0 -0
  72. /package/src/subscription/{subscription.component.scss → subscription.scss} +0 -0
@@ -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);
@@ -14,8 +14,8 @@ import {
14
14
  import React, { Fragment, useCallback, useEffect, useState } from 'react';
15
15
  import { useTranslation } from 'react-i18next';
16
16
  import { getImportDetails } from './import-items.resource';
17
- import styles from './import-items.component.scss';
18
- import { ImportItem } from '../../types';
17
+ import type { ImportItem } from '../../types';
18
+ import styles from './import-items.scss';
19
19
 
20
20
  interface ImportItemsProps {
21
21
  importUuid: string;
@@ -29,29 +29,32 @@ const ImportItems: React.FC<ImportItemsProps> = ({ importUuid }) => {
29
29
  const [selectedImportItems, setSelectedImportItems] = useState<ImportItem[]>([]);
30
30
  const { results, currentPage, goTo } = usePagination(selectedImportItems, pageSize);
31
31
 
32
- const handleImportDetails = useCallback(async (uuid: string) => {
33
- setIsLoading(true);
34
- const abortController = new AbortController();
32
+ const handleImportDetails = useCallback(
33
+ async (uuid: string) => {
34
+ setIsLoading(true);
35
+ const abortController = new AbortController();
35
36
 
36
- const response = await getImportDetails(uuid, abortController);
37
+ const response = await getImportDetails(uuid, abortController);
37
38
 
38
- if (response.ok) {
39
- setSelectedImportItems(response.data.results);
40
- } else {
41
- showNotification({
42
- title: t('importItemsFetchError', 'Error occured while fetching the import items'),
43
- kind: 'error',
44
- critical: true,
45
- description: JSON.stringify(response.data),
46
- });
47
- }
48
- setIsLoading(false);
49
- return () => abortController.abort();
50
- }, []);
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
+ );
51
54
 
52
55
  useEffect(() => {
53
56
  handleImportDetails(importUuid);
54
- }, []);
57
+ }, [handleImportDetails, importUuid]);
55
58
 
56
59
  if (isLoading) {
57
60
  return (
@@ -1,5 +1,5 @@
1
1
  import { openmrsFetch } from '@openmrs/esm-framework';
2
- import { ImportItem } from '../../types';
2
+ import type { ImportItem } from '../../types';
3
3
 
4
4
  export async function getImportDetails(importUuid: string, abortController?: AbortController) {
5
5
  const url = `/ws/rest/v1/openconceptlab/import/${importUuid}/item?state=ERROR&v=full`;
@@ -3,9 +3,12 @@ import { screen, waitFor } from '@testing-library/react';
3
3
  import { usePagination } from '@openmrs/esm-framework';
4
4
  import { renderWithSwr } from '../../../../../tools/test-helpers';
5
5
  import { mockImportItems, mockPreviousImports } from '../../../../../__mocks__/openconceptlab.mock';
6
- import { ImportItem } from '../../types';
7
- import ImportItems from './import-items.component';
8
6
  import { getImportDetails } from './import-items.resource';
7
+ import ImportItems from './import-items.component';
8
+
9
+ const testProps = {
10
+ importUuid: mockPreviousImports[1].uuid,
11
+ };
9
12
 
10
13
  const mockGetImportDetails = getImportDetails as jest.Mock;
11
14
  const mockUsePagination = usePagination as jest.Mock;
@@ -34,16 +37,6 @@ describe(`Import Items component`, () => {
34
37
  mockUsePagination.mockReset();
35
38
  });
36
39
 
37
- it(`renders without dying`, () => {
38
- mockGetImportDetails.mockReturnValue({ status: 200, ok: true, data: [] });
39
- mockUsePagination.mockReturnValue({
40
- currentPage: 1,
41
- goTo: () => {},
42
- results: [],
43
- });
44
- renderImportItemsComponent();
45
- });
46
-
47
40
  it(`renders the table`, async () => {
48
41
  mockGetImportDetails.mockReturnValue({ status: 200, ok: true, data: mockImportItems });
49
42
  mockUsePagination.mockReturnValue({
@@ -71,7 +64,7 @@ describe(`Import Items component`, () => {
71
64
  expect(screen.getByText('Concept/Mapping')).toBeVisible();
72
65
  expect(screen.getByText('Message')).toBeVisible();
73
66
 
74
- mockImportItems.slice(5).forEach((importItem: ImportItem) => {
67
+ mockImportItems.slice(5).forEach((importItem) => {
75
68
  expect(screen.getByText(importItem.type + ' ' + importItem.uuid)).toBeVisible();
76
69
  expect(screen.getByText(importItem.errorMessage)).toBeVisible();
77
70
  });
@@ -79,7 +72,7 @@ describe(`Import Items component`, () => {
79
72
  });
80
73
 
81
74
  function renderImportItemsComponent() {
82
- renderWithSwr(<ImportItems importUuid={mockPreviousImports[1].uuid} />);
75
+ renderWithSwr(<ImportItems {...testProps} />);
83
76
  }
84
77
 
85
78
  function waitForLoadingToFinish() {
@@ -1,9 +1,9 @@
1
1
  import { formatDatetime } from '@openmrs/esm-framework';
2
2
  import React from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
- import styles from './import-overview.component.scss';
5
- import { Import } from '../../types';
4
+ import type { Import } from '../../types';
6
5
  import ImportItems from './import-items.component';
6
+ import styles from './import-overview.scss';
7
7
 
8
8
  interface ImportOverviewProps {
9
9
  selectedImportObject: Import;
@@ -3,7 +3,6 @@ import { screen, waitFor } from '@testing-library/react';
3
3
  import { formatDatetime } from '@openmrs/esm-framework';
4
4
  import { renderWithSwr } from '../../../../../tools/test-helpers';
5
5
  import { mockPreviousImports } from '../../../../../__mocks__/openconceptlab.mock';
6
- import { Import } from '../../types';
7
6
  import ImportOverview from './import-overview.component';
8
7
 
9
8
  describe(`Import Overview component`, () => {
@@ -72,7 +71,7 @@ describe(`Import Overview component`, () => {
72
71
  });
73
72
  });
74
73
 
75
- function renderImportOverviewComponent(importObject: Import) {
74
+ function renderImportOverviewComponent(importObject) {
76
75
  renderWithSwr(<ImportOverview selectedImportObject={importObject} />);
77
76
  }
78
77
 
@@ -1,4 +1,4 @@
1
- import { formatDatetime, showNotification, usePagination } from '@openmrs/esm-framework';
1
+ import React, { Fragment, useState } from 'react';
2
2
  import {
3
3
  Column,
4
4
  DataTable,
@@ -17,12 +17,12 @@ import {
17
17
  TableHeader,
18
18
  TableRow,
19
19
  } from '@carbon/react';
20
- import React, { Fragment, useState } from 'react';
21
20
  import { useTranslation } from 'react-i18next';
21
+ import { formatDatetime, showNotification, usePagination } from '@openmrs/esm-framework';
22
22
  import { usePreviousImports } from './previous-imports.resource';
23
- import styles from './previous-imports.component.scss';
24
- import { Import } from '../types';
23
+ import type { Import } from '../types';
25
24
  import ImportOverview from './import-overview/import-overview.component';
25
+ import styles from './previous-imports.scss';
26
26
 
27
27
  const PreviousImports: React.FC = () => {
28
28
  const { t } = useTranslation();
@@ -1,6 +1,6 @@
1
1
  import { openmrsFetch } from '@openmrs/esm-framework';
2
2
  import useSWR from 'swr';
3
- import { Import } from '../types';
3
+ import type { Import } from '../types';
4
4
 
5
5
  export function usePreviousImports() {
6
6
  const { data, error, isValidating } = useSWR<{ data: { results: Import[] } }, Error>(
@@ -3,7 +3,6 @@ import { screen, waitFor } from '@testing-library/react';
3
3
  import { formatDatetime, openmrsFetch, usePagination } from '@openmrs/esm-framework';
4
4
  import { renderWithSwr } from '../../../../tools/test-helpers';
5
5
  import { mockPreviousImports } from '../../../../__mocks__/openconceptlab.mock';
6
- import { Import } from '../types';
7
6
  import PreviousImports from './previous-imports.component';
8
7
 
9
8
  const mockOpenmrsFetch = openmrsFetch as jest.Mock;
@@ -58,7 +57,7 @@ describe(`Previous Imports component`, () => {
58
57
  renderPreviousImportsComponent();
59
58
  await waitForLoadingToFinish();
60
59
 
61
- mockPreviousImports.forEach((item: Import) => {
60
+ mockPreviousImports.forEach((item) => {
62
61
  expect(screen.getByText(formatDatetime(item.localDateStarted))).toBeVisible();
63
62
  expect(screen.getByText(item.importTime)).toBeVisible();
64
63
  expect(screen.getByText(item.status)).toBeVisible();
@@ -1,23 +1,23 @@
1
- import { Tab, Tabs, TabList, TabPanels, TabPanel } from '@carbon/react';
2
1
  import React from 'react';
2
+ import { Tab, Tabs, TabList, TabPanels, TabPanel } from '@carbon/react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { SWRConfig } from 'swr';
5
- import Subscription from './subscription/subscription.component';
6
- import styles from './root.component.scss';
7
5
  import Import from './import/import.component';
8
6
  import PreviousImports from './previous-imports/previous-imports.component';
7
+ import Subscription from './subscription/subscription.component';
8
+ import styles from './root.scss';
9
9
 
10
10
  const Root: React.FC = () => {
11
11
  const { t } = useTranslation();
12
12
  return (
13
13
  <SWRConfig>
14
14
  <main className={`omrs-main-content ${styles.main}`}>
15
- <h3 className={styles.moduleHeader}>{t('moduleTitle')}</h3>
15
+ <h3 className={styles.moduleHeader}>{t('moduleTitle', 'OCL Subscription Module')}</h3>
16
16
  <Tabs>
17
- <TabList className={styles.tabList} contained={true}>
18
- <Tab>{t('subscription')} </Tab>
19
- <Tab>{t('import')} </Tab>
20
- <Tab>{t('previousImports')} </Tab>
17
+ <TabList aria-label="OCL tabs" className={styles.tabList} contained={true}>
18
+ <Tab>{t('subscription', 'Subscription')} </Tab>
19
+ <Tab>{t('import', 'Import')} </Tab>
20
+ <Tab>{t('previousImports', 'Previous Imports')} </Tab>
21
21
  </TabList>
22
22
  <TabPanels>
23
23
  <TabPanel className={styles.tabPanel}>
package/src/root.test.tsx CHANGED
@@ -10,10 +10,10 @@ describe(`Root component`, () => {
10
10
 
11
11
  it(`renders the title and tab containers`, () => {
12
12
  renderOclSubscriptionModule();
13
- expect(screen.getByText('moduleTitle')).toBeInTheDocument();
14
- expect(screen.getByRole('tab', { name: 'subscription' })).toBeInTheDocument();
15
- expect(screen.getByRole('tab', { name: 'import' })).toBeInTheDocument();
16
- expect(screen.getByRole('tab', { name: 'previousImports' })).toBeInTheDocument();
13
+ expect(screen.getByText('OCL Subscription Module')).toBeInTheDocument();
14
+ expect(screen.getByRole('tab', { name: 'Subscription' })).toBeInTheDocument();
15
+ expect(screen.getByRole('tab', { name: 'Import' })).toBeInTheDocument();
16
+ expect(screen.getByRole('tab', { name: 'Previous Imports' })).toBeInTheDocument();
17
17
  });
18
18
  });
19
19
 
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json.openmrs.org/routes.schema.json",
3
+ "backendDependencies": {
4
+ "openconceptlab": ">=1.2.0",
5
+ "webservices.rest": "^2.2.0"
6
+ },
7
+ "extensions": [
8
+ {
9
+ "name": "admin-ocl-card-link",
10
+ "slot": "system-admin-page-card-link-slot",
11
+ "component": "adminOclCardLink"
12
+ }
13
+ ],
14
+ "pages": [
15
+ {
16
+ "component": "root",
17
+ "route": "ocl"
18
+ }
19
+ ]
20
+ }
@@ -1,4 +1,5 @@
1
- import { showNotification } from '@openmrs/esm-framework';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import { useSWRConfig } from 'swr';
2
3
  import {
3
4
  Button,
4
5
  ButtonSkeleton,
@@ -13,11 +14,11 @@ import {
13
14
  TextInput,
14
15
  TextInputSkeleton,
15
16
  } from '@carbon/react';
16
- import React, { useCallback, useEffect, useState } from 'react';
17
17
  import { useTranslation } from 'react-i18next';
18
+ import { showNotification } from '@openmrs/esm-framework';
18
19
  import { deleteSubscription, updateSubscription, useSubscription } from './subscription.resource';
19
- import styles from './subscription.component.scss';
20
- import { useSWRConfig } from 'swr';
20
+ import { isVersionDefinedInUrl } from '../utils';
21
+ import styles from './subscription.scss';
21
22
 
22
23
  const Subscription: React.FC = () => {
23
24
  const { t } = useTranslation();
@@ -26,6 +27,7 @@ const Subscription: React.FC = () => {
26
27
  const [token, setToken] = useState('');
27
28
  const [isSubscribedToSnapshot, setIsSubscribedToSnapshot] = useState(false);
28
29
  const [validationType, setValidationType] = useState<'NONE' | 'FULL'>('FULL');
30
+ const [isSnapshotOptionDisabled, setIsSnapshotOptionDisabled] = useState(false);
29
31
 
30
32
  const { data: subscription, isLoading, isError } = useSubscription();
31
33
 
@@ -35,22 +37,29 @@ const Subscription: React.FC = () => {
35
37
  setToken(subscription?.token || '');
36
38
  setIsSubscribedToSnapshot(subscription?.subscribedToSnapshot || false);
37
39
  setValidationType(subscription?.validationType || 'FULL');
40
+ setIsSnapshotOptionDisabled(subscription ? isVersionDefinedInUrl(subscription.url) : false);
38
41
  }
39
42
  }, [isLoading, isError, subscription]);
40
43
 
41
44
  const handleChangeSubscriptionUrl = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
42
45
  setSubscriptionUrl(event.target.value);
46
+ if (isVersionDefinedInUrl(event.target.value)) {
47
+ setIsSnapshotOptionDisabled(true);
48
+ setIsSubscribedToSnapshot(false);
49
+ } else {
50
+ setIsSnapshotOptionDisabled(false);
51
+ }
43
52
  }, []);
44
53
 
45
54
  const handleChangeToken = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
46
55
  setToken(event.target.value);
47
56
  }, []);
48
57
 
49
- const handleChangeValidationType = useCallback((checked: boolean) => {
58
+ const handleChangeValidationType = useCallback((event, { checked, id }) => {
50
59
  setValidationType(checked ? 'NONE' : 'FULL');
51
60
  }, []);
52
61
 
53
- const handleChangeSubscriptionType = useCallback((checked: boolean) => {
62
+ const handleChangeSubscriptionType = useCallback((event, { checked, id }) => {
54
63
  setIsSubscribedToSnapshot(checked);
55
64
  }, []);
56
65
 
@@ -59,6 +68,17 @@ const Subscription: React.FC = () => {
59
68
  evt.preventDefault();
60
69
  evt.stopPropagation();
61
70
 
71
+ if (isSnapshotOptionDisabled && isSubscribedToSnapshot) {
72
+ showNotification({
73
+ kind: 'error',
74
+ description: t(
75
+ 'snapshotDisabledError',
76
+ "You cannot subscribe to a SNAPSHOT if you've provided the collection version",
77
+ ),
78
+ });
79
+ return;
80
+ }
81
+
62
82
  const abortController = new AbortController();
63
83
 
64
84
  const updatedSubscription = {
@@ -76,11 +96,14 @@ const Subscription: React.FC = () => {
76
96
  if (response.ok) {
77
97
  showNotification({
78
98
  kind: 'success',
79
- description: t(response.status === 201 ? 'subscriptionCreated' : 'subscriptionUpdated'),
99
+ description: t(
100
+ response.status === 201 ? 'subscriptionCreated' : 'subscriptionUpdated',
101
+ response.status === 201 ? 'Subscription created successfully' : 'Subscription updated successfully',
102
+ ),
80
103
  });
81
104
  } else {
82
105
  showNotification({
83
- title: t('errorSavingSubscription'),
106
+ title: t('errorSavingSubscription', 'Error occured while saving the subscription'),
84
107
  kind: 'error',
85
108
  critical: true,
86
109
  description: JSON.stringify(response.data),
@@ -89,7 +112,7 @@ const Subscription: React.FC = () => {
89
112
 
90
113
  return () => abortController.abort();
91
114
  },
92
- [subscriptionUrl, token, validationType, isSubscribedToSnapshot, t, subscription, mutate],
115
+ [subscriptionUrl, token, validationType, isSubscribedToSnapshot, isSnapshotOptionDisabled, subscription, t, mutate],
93
116
  );
94
117
 
95
118
  const handleCancel = useCallback(() => {
@@ -97,10 +120,11 @@ const Subscription: React.FC = () => {
97
120
  setToken(subscription?.token || '');
98
121
  setIsSubscribedToSnapshot(subscription?.subscribedToSnapshot || false);
99
122
  setValidationType(subscription?.validationType || 'FULL');
123
+ setIsSnapshotOptionDisabled(subscription ? isVersionDefinedInUrl(subscription.url) : false);
100
124
 
101
125
  showNotification({
102
126
  kind: 'info',
103
- description: t('cancelledChanges'),
127
+ description: t('cancelledChanges', 'Cancelled changes successfully'),
104
128
  });
105
129
  }, [subscription, t]);
106
130
 
@@ -118,13 +142,14 @@ const Subscription: React.FC = () => {
118
142
  setToken('');
119
143
  setIsSubscribedToSnapshot(false);
120
144
  setValidationType('FULL');
145
+ setIsSnapshotOptionDisabled(false);
121
146
  showNotification({
122
147
  kind: 'success',
123
- description: t('subscriptionDeleted'),
148
+ description: t('subscriptionDeleted', 'Successfully unsubscribed'),
124
149
  });
125
150
  } else {
126
151
  showNotification({
127
- title: t('errorDeletingSubscription'),
152
+ title: t('errorDeletingSubscription', 'Error occured while deleting the subscription'),
128
153
  kind: 'error',
129
154
  critical: true,
130
155
  description: JSON.stringify(response.data),
@@ -166,7 +191,7 @@ const Subscription: React.FC = () => {
166
191
  if (isError) {
167
192
  showNotification({
168
193
  kind: 'error',
169
- description: t('subscriptionError'),
194
+ description: t('subscriptionError', 'Error occured while fetching the subscription'),
170
195
  });
171
196
  }
172
197
 
@@ -174,12 +199,12 @@ const Subscription: React.FC = () => {
174
199
  <Grid className={styles.grid}>
175
200
  <Column sm={4} md={8} lg={10}>
176
201
  <Form onSubmit={handleSubmit}>
177
- <h3 className={styles.productiveHeading03}>{t('setupSubscription')}</h3>
202
+ <h3 className={styles.productiveHeading03}>{t('setupSubscription', 'Setup Subscription')}</h3>
178
203
  <Stack gap={5}>
179
204
  <TextInput
180
205
  id="subscriptionUrl"
181
206
  type="url"
182
- labelText={t('subscriptionUrl')}
207
+ labelText={t('subscriptionUrl', 'Subscription URL')}
183
208
  placeholder="https://api.openconceptlab.org/orgs/organization-name/collections/dictionary-name"
184
209
  value={subscriptionUrl}
185
210
  onChange={handleChangeSubscriptionUrl}
@@ -190,39 +215,48 @@ const Subscription: React.FC = () => {
190
215
  id="apiToken"
191
216
  type="password"
192
217
  placeholder="••••••••••••••••••••••••••••••••••••••••••••••••"
193
- labelText={t('apiToken')}
218
+ labelText={t('apiToken', 'Token')}
194
219
  value={token}
195
220
  onChange={handleChangeToken}
196
221
  light={true}
197
222
  required
198
223
  />
199
- <FormGroup legendText={t('advancedOptions')} className={styles.formGroup}>
224
+ <FormGroup legendText={t('advancedOptions', 'Advanced Options')} className={styles.formGroup}>
200
225
  <Checkbox
201
226
  checked={isSubscribedToSnapshot}
202
227
  onChange={handleChangeSubscriptionType}
203
- labelText={t('subscribeToSnapshotText')}
228
+ labelText={t('subscribeToSnapshotText', 'Subscribe to SNAPSHOT versions (not recommended)')}
204
229
  id="isSubscribedToSnapshot"
230
+ disabled={isSnapshotOptionDisabled}
205
231
  />
206
232
  <Checkbox
207
233
  checked={validationType === 'NONE'}
208
234
  onChange={handleChangeValidationType}
209
- labelText={t('disableValidationText')}
235
+ labelText={t(
236
+ 'disableValidationText',
237
+ 'Disable validation (should be used with care for well curated collections or sources)',
238
+ )}
210
239
  id="isValidationDisabled"
211
240
  />
212
241
  </FormGroup>
213
242
  </Stack>
214
243
  <Button kind="secondary" onClick={handleCancel}>
215
- {t('cancelButton')}
244
+ {t('cancelButton', 'Cancel changes')}
216
245
  </Button>
217
246
  <Button kind="primary" type="submit">
218
- {t('subscribeButton')}
247
+ {t('subscribeButton', 'Save changes')}
219
248
  </Button>
220
249
  </Form>
221
250
  <Form onSubmit={handleUnsubscribe} className={styles.unsubscribeForm}>
222
- <h3 className={styles.productiveHeading03}>{t('unsubscribe')}</h3>
223
- <p className={styles.unsubscribeText}>{t('unsubscribeInfo')}</p>
251
+ <h3 className={styles.productiveHeading03}>{t('unsubscribe', 'Unsubscribe')}</h3>
252
+ <p className={styles.unsubscribeText}>
253
+ {t(
254
+ 'unsubscribeInfo',
255
+ 'If you unsubscribe, no concepts will be deleted nor changed. All information about subscription will be deleted from your system.',
256
+ )}
257
+ </p>
224
258
  <Button kind="danger" type="submit" disabled={!subscription}>
225
- {t('unsubscribeButton')}
259
+ {t('unsubscribeButton', 'Unsubscribe')}
226
260
  </Button>
227
261
  </Form>
228
262
  </Column>
@@ -1,7 +1,7 @@
1
1
  import { openmrsFetch } from '@openmrs/esm-framework';
2
2
  import useSWR from 'swr';
3
- import { Subscription } from '../types';
4
3
  import isNil from 'lodash-es/isNil';
4
+ import type { Subscription } from '../types';
5
5
 
6
6
  export function useSubscription() {
7
7
  const { data, error, isValidating } = useSWR<{ data: { results: Subscription[] } }, Error>(