@openmrs/esm-metadataexport-app 4.4.1-pre.877 → 4.4.1-pre.879

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 (29) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/2173.js +1 -1
  3. package/dist/2173.js.map +1 -1
  4. package/dist/5065.js +1 -1
  5. package/dist/5065.js.map +1 -1
  6. package/dist/5117.js +1 -1
  7. package/dist/5117.js.map +1 -1
  8. package/dist/7121.js +1 -1
  9. package/dist/7121.js.map +1 -1
  10. package/dist/9503.js +1 -1
  11. package/dist/9503.js.map +1 -1
  12. package/dist/openmrs-esm-metadataexport-app.js +1 -1
  13. package/dist/openmrs-esm-metadataexport-app.js.buildmanifest.json +16 -16
  14. package/dist/routes.json +1 -1
  15. package/package.json +1 -1
  16. package/src/admin-card-link.component.tsx +1 -1
  17. package/src/components/dashboard/home-dashboard.component.tsx +1 -2
  18. package/src/components/metadata-package-form/add-package-action-button/add-package-action-button.component.tsx +3 -3
  19. package/src/components/metadata-package-form/add-package-action-button/add-package-action-button.test.tsx +5 -5
  20. package/src/components/metadata-package-form/metadata-package-form-utils.ts +3 -7
  21. package/src/components/metadata-package-form/metadata-package-form.workspace.test.tsx +58 -30
  22. package/src/components/metadata-package-form/metadata-package-form.workspace.tsx +104 -106
  23. package/src/components/packages-table/packages-table.component.tsx +1 -1
  24. package/src/components/view-metadata-package/view-metadata-package-utils.ts +2 -5
  25. package/src/components/view-metadata-package/view-metadata-package.workspace.test.tsx +31 -6
  26. package/src/components/view-metadata-package/view-metadata-package.workspace.tsx +103 -93
  27. package/src/root.component.tsx +1 -1
  28. package/src/routes.json +25 -10
  29. package/translations/en.json +2 -1
@@ -1,9 +1,6 @@
1
- import { launchWorkspace } from '@openmrs/esm-framework';
1
+ import { launchWorkspace2 } from '@openmrs/esm-framework';
2
2
  import { type ExportPackage } from '../../types/index';
3
3
 
4
4
  export const launchViewMetadataPackageWorkspace = (exportPackage: ExportPackage) => {
5
- launchWorkspace('view-metadata-package-workspace', {
6
- workspaceTitle: exportPackage.name,
7
- exportPackage,
8
- });
5
+ launchWorkspace2('view-metadata-package-workspace', { exportPackage });
9
6
  };
@@ -34,8 +34,7 @@ const mockUserHasAccess = vi.mocked(esmFramework.userHasAccess);
34
34
  const sessionWithUser = () =>
35
35
  ({ user: { uuid: 'cc8507b8-7c9a-486b-85dc-b8f25ad1e4cc' } }) as unknown as esmFramework.Session;
36
36
  const mockCloseWorkspace = vi.fn();
37
- const mockCloseWorkspaceWithSavedChanges = vi.fn();
38
- const mockPromptBeforeClosing = vi.fn();
37
+ const mockLaunchWorkspace2 = vi.mocked(esmFramework.launchWorkspace2);
39
38
 
40
39
  const build = (overrides: Partial<ExportPackageBuild> = {}): ExportPackageBuild => ({
41
40
  uuid: '23dbfd25-eec7-4f4e-b26c-d95324346ce3',
@@ -78,11 +77,15 @@ function mockBuilds(overrides: Partial<ReturnType<typeof usePackageBuilds>> = {}
78
77
  function renderWorkspace(pkg: ExportPackage = exportPackage) {
79
78
  render(
80
79
  <ViewMetadataPackageWorkspace
81
- exportPackage={pkg}
80
+ workspaceProps={{ exportPackage: pkg }}
81
+ windowProps={null}
82
+ groupProps={null}
82
83
  closeWorkspace={mockCloseWorkspace}
83
- closeWorkspaceWithSavedChanges={mockCloseWorkspaceWithSavedChanges}
84
- promptBeforeClosing={mockPromptBeforeClosing}
85
- setTitle={vi.fn()}
84
+ launchChildWorkspace={vi.fn()}
85
+ workspaceName="view-metadata-package-workspace"
86
+ windowName="view-metadata-package-window"
87
+ isRootWorkspace
88
+ showActionMenu={false}
86
89
  />,
87
90
  );
88
91
  }
@@ -211,6 +214,28 @@ describe('ViewPackageWorkspace', () => {
211
214
  );
212
215
  });
213
216
 
217
+ it('replaces the workspace with the edit form when Edit is clicked', async () => {
218
+ const user = userEvent.setup();
219
+ mockCloseWorkspace.mockResolvedValue(true);
220
+ renderWorkspace();
221
+
222
+ await user.click(screen.getByRole('button', { name: 'Edit' }));
223
+
224
+ expect(mockCloseWorkspace).toHaveBeenCalled();
225
+ expect(mockLaunchWorkspace2).toHaveBeenCalledWith('metadata-package-form-workspace', { uuid: exportPackage.uuid });
226
+ });
227
+
228
+ it('does not launch the edit form if the workspace stays open', async () => {
229
+ const user = userEvent.setup();
230
+ mockCloseWorkspace.mockResolvedValue(false);
231
+ renderWorkspace();
232
+
233
+ await user.click(screen.getByRole('button', { name: 'Edit' }));
234
+
235
+ expect(mockCloseWorkspace).toHaveBeenCalled();
236
+ expect(mockLaunchWorkspace2).not.toHaveBeenCalled();
237
+ });
238
+
214
239
  it('launches the delete confirmation modal wired to close the workspace on success', async () => {
215
240
  const user = userEvent.setup();
216
241
  renderWorkspace();
@@ -4,7 +4,6 @@ import { Download } from '@carbon/react/icons';
4
4
  import { Button, InlineLoading, Link, Tag } from '@carbon/react';
5
5
  import { useSWRConfig } from 'swr';
6
6
  import {
7
- type DefaultWorkspaceProps,
8
7
  ErrorState,
9
8
  OpenmrsFetchError,
10
9
  formatDurationBetween,
@@ -14,6 +13,8 @@ import {
14
13
  showSnackbar,
15
14
  useSession,
16
15
  userHasAccess,
16
+ Workspace2,
17
+ type Workspace2DefinitionProps,
17
18
  } from '@openmrs/esm-framework';
18
19
  import { formatDomainLabel } from '../../domain-lookups/domain-lookups.resource';
19
20
  import { isPackagesCacheKey, triggerBuild, usePackageBuilds } from '../../packages/packages.resource';
@@ -21,7 +22,7 @@ import type { ExportBuildStatus, ExportPackage } from '../../types';
21
22
  import styles from './view-metadata-package.workspace.scss';
22
23
  import { launchPackageFormWorkspace } from '../metadata-package-form/metadata-package-form-utils';
23
24
 
24
- interface ViewPackageWorkspaceProps extends DefaultWorkspaceProps {
25
+ interface ViewPackageWorkspaceProps {
25
26
  exportPackage: ExportPackage;
26
27
  }
27
28
 
@@ -32,7 +33,11 @@ const statusTagType: Record<ExportBuildStatus, 'gray' | 'blue' | 'green' | 'red'
32
33
  FAILED: 'red',
33
34
  };
34
35
 
35
- const ViewMetadataPackageWorkspace: React.FC<ViewPackageWorkspaceProps> = ({ exportPackage, closeWorkspace }) => {
36
+ const ViewMetadataPackageWorkspace: React.FC<Workspace2DefinitionProps<ViewPackageWorkspaceProps>> = ({
37
+ workspaceProps,
38
+ closeWorkspace,
39
+ }) => {
40
+ const { exportPackage } = workspaceProps;
36
41
  const { t } = useTranslation();
37
42
  const session = useSession();
38
43
  const { mutate: globalMutate } = useSWRConfig();
@@ -100,108 +105,113 @@ const ViewMetadataPackageWorkspace: React.FC<ViewPackageWorkspaceProps> = ({ exp
100
105
  return exportPackage.entries.map((entry) => formatDomainLabel(entry.domain)).join(', ');
101
106
  }, [exportPackage.entries, t]);
102
107
 
103
- const editPackage = useCallback(() => {
104
- launchPackageFormWorkspace(t, exportPackage.uuid);
105
- }, [exportPackage.uuid, t]);
108
+ // Replace this workspace with the edit form so it doesn't reappear with stale package details
109
+ const editPackage = useCallback(async () => {
110
+ if (await closeWorkspace()) {
111
+ launchPackageFormWorkspace(exportPackage.uuid);
112
+ }
113
+ }, [closeWorkspace, exportPackage.uuid]);
106
114
 
107
115
  return (
108
- <div className={styles.container}>
109
- <div className={styles.body}>
110
- {canManage && (
111
- <section className={styles.actionRow}>
112
- <Button
113
- className={styles.triggerButton}
114
- kind="primary"
115
- onClick={handleTriggerBuild}
116
- disabled={isTriggeringBuild}
117
- >
118
- {isTriggeringBuild ? (
119
- <InlineLoading description={t('triggeringBuild', 'Triggering build') + '…'} />
120
- ) : (
121
- t('triggerNewBuild', 'Trigger new build')
122
- )}
123
- </Button>
124
- <div className={styles.secondaryActions}>
125
- <Button
126
- className={styles.secondaryButton}
127
- kind="secondary"
128
- onClick={editPackage}
129
- disabled={isTriggeringBuild}
130
- >
131
- {t('edit', 'Edit')}
132
- </Button>
116
+ <Workspace2 title={exportPackage.name}>
117
+ <div className={styles.container}>
118
+ <div className={styles.body}>
119
+ {canManage && (
120
+ <section className={styles.actionRow}>
133
121
  <Button
134
- className={styles.secondaryButton}
135
- kind="danger--tertiary"
136
- onClick={launchDeleteModal}
122
+ className={styles.triggerButton}
123
+ kind="primary"
124
+ onClick={handleTriggerBuild}
137
125
  disabled={isTriggeringBuild}
138
126
  >
139
- {t('delete', 'Delete')}
127
+ {isTriggeringBuild ? (
128
+ <InlineLoading description={t('triggeringBuild', 'Triggering build') + '…'} />
129
+ ) : (
130
+ t('triggerNewBuild', 'Trigger new build')
131
+ )}
140
132
  </Button>
141
- </div>
142
- </section>
143
- )}
133
+ <div className={styles.secondaryActions}>
134
+ <Button
135
+ className={styles.secondaryButton}
136
+ kind="secondary"
137
+ onClick={editPackage}
138
+ disabled={isTriggeringBuild}
139
+ >
140
+ {t('edit', 'Edit')}
141
+ </Button>
142
+ <Button
143
+ className={styles.secondaryButton}
144
+ kind="danger--tertiary"
145
+ onClick={launchDeleteModal}
146
+ disabled={isTriggeringBuild}
147
+ >
148
+ {t('delete', 'Delete')}
149
+ </Button>
150
+ </div>
151
+ </section>
152
+ )}
144
153
 
145
- <section className={styles.section}>
146
- <span className={styles.sectionLabel}>{t('details', 'Details')}</span>
147
- <dl className={styles.detailList}>
148
- <dt className={styles.detailKey}>{t('domains', 'Domains')}</dt>
149
- <dd className={styles.detailValue}>{domainsLabel}</dd>
150
- </dl>
151
- </section>
154
+ <section className={styles.section}>
155
+ <span className={styles.sectionLabel}>{t('details', 'Details')}</span>
156
+ <dl className={styles.detailList}>
157
+ <dt className={styles.detailKey}>{t('domains', 'Domains')}</dt>
158
+ <dd className={styles.detailValue}>{domainsLabel}</dd>
159
+ </dl>
160
+ </section>
152
161
 
153
- <section className={styles.section}>
154
- <span className={styles.sectionLabel}>{t('builds', 'Builds')}</span>
155
- {isLoading ? (
156
- <InlineLoading description={t('loadingBuilds', 'Loading builds…')} />
157
- ) : error ? (
158
- <ErrorState error={error} headerTitle={t('builds', 'Builds')} />
159
- ) : builds.length === 0 ? (
160
- <p className={styles.emptyBuilds}>{t('noBuildsYet', 'No builds yet')}</p>
161
- ) : (
162
- <ul className={styles.buildList}>
163
- {builds.map((build) => (
164
- <li key={build.uuid} className={styles.buildItem}>
165
- <div className={styles.buildRow}>
166
- <span className={styles.buildVersion}>
167
- {t('buildVersion', 'Build {{version}}', { version: build.version })}
168
- </span>
169
- <Tag type={statusTagType[build.status]} size="sm">
170
- {t(build.status)}
171
- </Tag>
172
- </div>
173
- <div className={styles.buildRow}>
174
- <span className={styles.buildMeta}>
175
- {build.dateStarted
176
- ? t('startedAgo', 'Started {{time}} ago', { time: formatDurationBetween(build.dateStarted) })
177
- : t('notStarted', 'Not started')}
178
- </span>
179
- {build.dateCompleted && (
162
+ <section className={styles.section}>
163
+ <span className={styles.sectionLabel}>{t('builds', 'Builds')}</span>
164
+ {isLoading ? (
165
+ <InlineLoading description={t('loadingBuilds', 'Loading builds…')} />
166
+ ) : error ? (
167
+ <ErrorState error={error} headerTitle={t('builds', 'Builds')} />
168
+ ) : builds.length === 0 ? (
169
+ <p className={styles.emptyBuilds}>{t('noBuildsYet', 'No builds yet')}</p>
170
+ ) : (
171
+ <ul className={styles.buildList}>
172
+ {builds.map((build) => (
173
+ <li key={build.uuid} className={styles.buildItem}>
174
+ <div className={styles.buildRow}>
175
+ <span className={styles.buildVersion}>
176
+ {t('buildVersion', 'Build {{version}}', { version: build.version })}
177
+ </span>
178
+ <Tag type={statusTagType[build.status]} size="sm">
179
+ {t(build.status)}
180
+ </Tag>
181
+ </div>
182
+ <div className={styles.buildRow}>
180
183
  <span className={styles.buildMeta}>
181
- {t('completedAgo', 'Completed {{time}} ago', {
182
- time: formatDurationBetween(build.dateCompleted),
183
- })}
184
+ {build.dateStarted
185
+ ? t('startedAgo', 'Started {{time}} ago', { time: formatDurationBetween(build.dateStarted) })
186
+ : t('notStarted', 'Not started')}
184
187
  </span>
188
+ {build.dateCompleted && (
189
+ <span className={styles.buildMeta}>
190
+ {t('completedAgo', 'Completed {{time}} ago', {
191
+ time: formatDurationBetween(build.dateCompleted),
192
+ })}
193
+ </span>
194
+ )}
195
+ </div>
196
+ {build.status === 'FAILED' && build.errorMessage && (
197
+ <p className={styles.buildError}>{build.errorMessage}</p>
185
198
  )}
186
- </div>
187
- {build.status === 'FAILED' && build.errorMessage && (
188
- <p className={styles.buildError}>{build.errorMessage}</p>
189
- )}
190
- {canManage && build.downloadUrl && (
191
- <Link
192
- href={makeUrl(`${restBaseUrl}/metadataexport/builds/${build.uuid}/download`)}
193
- renderIcon={() => <Download size={16} />}
194
- >
195
- {t('download', 'Download')}
196
- </Link>
197
- )}
198
- </li>
199
- ))}
200
- </ul>
201
- )}
202
- </section>
199
+ {canManage && build.downloadUrl && (
200
+ <Link
201
+ href={makeUrl(`${restBaseUrl}/metadataexport/builds/${build.uuid}/download`)}
202
+ renderIcon={() => <Download size={16} />}
203
+ >
204
+ {t('download', 'Download')}
205
+ </Link>
206
+ )}
207
+ </li>
208
+ ))}
209
+ </ul>
210
+ )}
211
+ </section>
212
+ </div>
203
213
  </div>
204
- </div>
214
+ </Workspace2>
205
215
  );
206
216
  };
207
217
 
@@ -5,7 +5,7 @@ import Dashboard from './components/dashboard/home-dashboard.component';
5
5
  const Root: React.FC = () => (
6
6
  <BrowserRouter basename={window.getOpenmrsSpaBase()}>
7
7
  <Routes>
8
- <Route path="metadataexport" element={<Dashboard />} />
8
+ <Route path="metadata-export" element={<Dashboard />} />
9
9
  </Routes>
10
10
  </BrowserRouter>
11
11
  );
package/src/routes.json CHANGED
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "pages": [
7
7
  {
8
- "route": "metadataexport",
8
+ "route": "metadata-export",
9
9
  "component": "root",
10
10
  "online": true
11
11
  }
@@ -24,22 +24,37 @@
24
24
  "component": "deleteMetadataPackageModal"
25
25
  }
26
26
  ],
27
- "workspaces": [
27
+ "workspaceGroups2": [
28
28
  {
29
- "name": "metadata-package-form-workspace",
30
- "component": "metadataPackageFormWorkspace",
31
- "title": "New package",
32
- "type": "form",
29
+ "name": "metadata-export-group",
30
+ "scopePattern": "^/metadata-export",
31
+ "overlay": true
32
+ }
33
+ ],
34
+ "workspaceWindows2": [
35
+ {
36
+ "name": "metadata-package-form-window",
37
+ "group": "metadata-export-group",
33
38
  "canMaximize": true,
34
39
  "width": "narrow"
35
40
  },
36
41
  {
37
- "name": "view-metadata-package-workspace",
38
- "component": "viewMetadataPackageWorkspace",
39
- "title": "View package",
40
- "type": "form",
42
+ "name": "view-metadata-package-window",
43
+ "group": "metadata-export-group",
41
44
  "canMaximize": true,
42
45
  "width": "narrow"
43
46
  }
47
+ ],
48
+ "workspaces2": [
49
+ {
50
+ "name": "metadata-package-form-workspace",
51
+ "component": "metadataPackageFormWorkspace",
52
+ "window": "metadata-package-form-window"
53
+ },
54
+ {
55
+ "name": "view-metadata-package-workspace",
56
+ "component": "viewMetadataPackageWorkspace",
57
+ "window": "view-metadata-package-window"
58
+ }
44
59
  ]
45
60
  }
@@ -20,6 +20,7 @@
20
20
  "domains": "Domains",
21
21
  "download": "Download",
22
22
  "edit": "Edit",
23
+ "editPackage": "Edit package",
23
24
  "errorLoadingDomains": "Error loading domains",
24
25
  "errorLoadingPackage": "Error loading package",
25
26
  "FAILED": "Failed",
@@ -27,7 +28,7 @@
27
28
  "loadingPackage": "Loading package…",
28
29
  "managePackages": "Manage packages",
29
30
  "metadataexport": "Metadata Export",
30
- "newpackage": "New Package",
31
+ "newPackage": "New package",
31
32
  "nextPage": "Next page",
32
33
  "noBuilds": "No builds",
33
34
  "noBuildsYet": "No builds yet",