@openmrs/esm-patient-documents-admin-app 4.4.1-pre.635

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 (57) hide show
  1. package/.turbo/turbo-build.log +6 -0
  2. package/README.md +10 -0
  3. package/dist/105.js +1 -0
  4. package/dist/105.js.map +1 -0
  5. package/dist/117.js +1 -0
  6. package/dist/117.js.map +1 -0
  7. package/dist/121.js +1 -0
  8. package/dist/121.js.map +1 -0
  9. package/dist/335.js +1 -0
  10. package/dist/335.js.map +1 -0
  11. package/dist/361.js +6 -0
  12. package/dist/361.js.map +1 -0
  13. package/dist/366.js +1 -0
  14. package/dist/366.js.map +1 -0
  15. package/dist/466.js +1 -0
  16. package/dist/466.js.map +1 -0
  17. package/dist/483.js +32 -0
  18. package/dist/483.js.map +1 -0
  19. package/dist/61.js +1 -0
  20. package/dist/61.js.map +1 -0
  21. package/dist/689.js +1 -0
  22. package/dist/689.js.map +1 -0
  23. package/dist/697.js +1 -0
  24. package/dist/697.js.map +1 -0
  25. package/dist/712.js +1 -0
  26. package/dist/712.js.map +1 -0
  27. package/dist/720.js +1 -0
  28. package/dist/720.js.map +1 -0
  29. package/dist/771.js +1 -0
  30. package/dist/771.js.map +1 -0
  31. package/dist/789.js +1 -0
  32. package/dist/789.js.map +1 -0
  33. package/dist/989.js +1 -0
  34. package/dist/989.js.map +1 -0
  35. package/dist/main.js +6 -0
  36. package/dist/main.js.map +1 -0
  37. package/dist/openmrs-esm-patient-documents-admin-app.js +6 -0
  38. package/dist/openmrs-esm-patient-documents-admin-app.js.buildmanifest.json +597 -0
  39. package/dist/openmrs-esm-patient-documents-admin-app.js.map +1 -0
  40. package/dist/routes.json +1 -0
  41. package/package.json +57 -0
  42. package/rspack.config.js +1 -0
  43. package/src/admin-card-link.component.tsx +34 -0
  44. package/src/config/config.resource.ts +137 -0
  45. package/src/config/visit-summary-config.component.tsx +447 -0
  46. package/src/config/visit-summary-config.scss +106 -0
  47. package/src/config/visit-summary-config.test.tsx +320 -0
  48. package/src/config-schema.ts +3 -0
  49. package/src/declarations.d.ts +2 -0
  50. package/src/index.ts +19 -0
  51. package/src/root.component.tsx +18 -0
  52. package/src/root.scss +5 -0
  53. package/src/routes.json +21 -0
  54. package/src/types.ts +12 -0
  55. package/translations/en.json +40 -0
  56. package/tsconfig.json +4 -0
  57. package/vitest.config.ts +4 -0
@@ -0,0 +1,320 @@
1
+ import React from 'react';
2
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { screen } from '@testing-library/react';
5
+ import { type FetchResponse, openmrsFetch } from '@openmrs/esm-framework';
6
+ import { renderWithSwr } from '@tools/test-helpers';
7
+ import { saveSectionSettings } from './config.resource';
8
+ import VisitSummaryConfig from './visit-summary-config.component';
9
+ import type { VisitSummarySection } from '../types';
10
+
11
+ const mockOpenmrsFetch = vi.mocked(openmrsFetch);
12
+ const mockSaveSectionSettings = vi.mocked(saveSectionSettings);
13
+
14
+ vi.mock('./config.resource', async () => {
15
+ const originalModule = (await vi.importActual('./config.resource')) as object;
16
+
17
+ return {
18
+ ...originalModule,
19
+ saveSectionSettings: vi.fn(),
20
+ };
21
+ });
22
+
23
+ const mockSections: Array<VisitSummarySection> = [
24
+ { sectionKey: 'facilityHeader', label: 'Facility header', enabled: true, order: 10, toggleable: false },
25
+ { sectionKey: 'vitals', label: 'Vitals', enabled: true, order: 20, toggleable: true },
26
+ { sectionKey: 'allergies', label: 'Allergies', enabled: false, order: 30, toggleable: true },
27
+ ];
28
+
29
+ const mockPdfBlob = new Blob(['%PDF-1.4'], { type: 'application/pdf' });
30
+ const mockObjectUrl = 'blob:mock-visit-summary-preview-pdf';
31
+
32
+ const forbiddenText = 'Your account lacks the Get Global Properties privilege required to view this page.';
33
+ const endpointMissingText = /module running on this server is missing or too old/i;
34
+ const generationFailedText = /the server could not generate the sample preview/i;
35
+ const networkErrorText = /check your network connection/i;
36
+
37
+ const isPreviewCall = (url: unknown) => String(url).includes('/visitSummary/preview');
38
+
39
+ const rejectionWithStatus = (status: number) =>
40
+ Object.assign(new Error(`Server responded with ${status}`), { response: { status } });
41
+
42
+ /**
43
+ * The sections list and the preview both go through openmrsFetch, so the mock has
44
+ * to route on the URL rather than on call order.
45
+ */
46
+ function mockFetch({
47
+ sections = mockSections,
48
+ preview = () => Promise.resolve({ blob: () => Promise.resolve(mockPdfBlob) } as unknown as FetchResponse),
49
+ }: {
50
+ sections?: Array<VisitSummarySection>;
51
+ preview?: () => Promise<FetchResponse>;
52
+ } = {}) {
53
+ mockOpenmrsFetch.mockImplementation((url: string) =>
54
+ isPreviewCall(url) ? preview() : Promise.resolve({ data: { results: sections } } as unknown as FetchResponse),
55
+ );
56
+ }
57
+
58
+ const previewCall = () => mockOpenmrsFetch.mock.calls.find(([url]) => isPreviewCall(url));
59
+
60
+ describe('VisitSummaryConfig', () => {
61
+ beforeEach(() => {
62
+ mockSaveSectionSettings.mockResolvedValue([]);
63
+ window.URL.createObjectURL = vi.fn(() => mockObjectUrl);
64
+ window.URL.revokeObjectURL = vi.fn();
65
+ });
66
+
67
+ it('renders the sections sorted by order, with locked sections disabled', async () => {
68
+ mockFetch();
69
+ renderWithSwr(<VisitSummaryConfig />);
70
+
71
+ const rows = await screen.findAllByRole('listitem');
72
+ expect(rows[0]).toHaveTextContent('Facility header');
73
+ expect(rows[1]).toHaveTextContent('Vitals');
74
+ expect(rows[2]).toHaveTextContent('Allergies');
75
+
76
+ expect(screen.getByRole('switch', { name: 'Facility header is always included' })).toBeDisabled();
77
+ expect(screen.getByRole('switch', { name: 'Include Vitals' })).toBeEnabled();
78
+ expect(screen.getByRole('button', { name: 'Save' })).toBeDisabled();
79
+ });
80
+
81
+ it('shows the error state with a retry action when the fetch fails', async () => {
82
+ mockOpenmrsFetch.mockRejectedValue(new Error('Internal server error'));
83
+ renderWithSwr(<VisitSummaryConfig />);
84
+
85
+ expect(await screen.findByText("Couldn't load the visit summary sections")).toBeVisible();
86
+ expect(screen.getByRole('button', { name: 'Retry' })).toBeEnabled();
87
+ });
88
+
89
+ it('shows a permission message without retry when the server returns 403', async () => {
90
+ mockOpenmrsFetch.mockRejectedValue(rejectionWithStatus(403));
91
+ renderWithSwr(<VisitSummaryConfig />);
92
+
93
+ expect(await screen.findByText(forbiddenText)).toBeVisible();
94
+ expect(screen.queryByRole('button', { name: 'Retry' })).not.toBeInTheDocument();
95
+ });
96
+
97
+ it('shows a distinct empty state when the server returns no sections', async () => {
98
+ mockFetch({ sections: [] });
99
+ renderWithSwr(<VisitSummaryConfig />);
100
+
101
+ expect(await screen.findByText('No sections registered')).toBeVisible();
102
+ expect(screen.queryByText("Couldn't load the visit summary sections")).not.toBeInTheDocument();
103
+ });
104
+
105
+ it('saves an enabled change as the matching global property write', async () => {
106
+ const user = userEvent.setup();
107
+ mockFetch();
108
+ renderWithSwr(<VisitSummaryConfig />);
109
+
110
+ await user.click(await screen.findByRole('switch', { name: 'Include Vitals' }));
111
+ await user.click(screen.getByRole('button', { name: 'Save' }));
112
+
113
+ expect(mockSaveSectionSettings).toHaveBeenCalledWith([
114
+ { sectionKey: 'vitals', property: 'report.visitSummary.section.vitals.enabled', value: 'false' },
115
+ ]);
116
+ });
117
+
118
+ it('pins the footer to the bottom with its reorder arrows disabled', async () => {
119
+ const sectionsWithFooter: Array<VisitSummarySection> = [
120
+ // Deliberately gives the footer a low order value: pinning must win over sorting.
121
+ { sectionKey: 'footer', label: 'Footer', enabled: true, order: 5, toggleable: false },
122
+ { sectionKey: 'vitals', label: 'Vitals', enabled: true, order: 20, toggleable: true },
123
+ { sectionKey: 'allergies', label: 'Allergies', enabled: true, order: 30, toggleable: true },
124
+ ];
125
+ mockFetch({ sections: sectionsWithFooter });
126
+ renderWithSwr(<VisitSummaryConfig />);
127
+
128
+ const rows = await screen.findAllByRole('listitem');
129
+ expect(rows[0]).toHaveTextContent('Vitals');
130
+ expect(rows[1]).toHaveTextContent('Allergies');
131
+ expect(rows[2]).toHaveTextContent('Footer');
132
+
133
+ expect(screen.getByRole('button', { name: 'Move Footer up' })).toBeDisabled();
134
+ expect(screen.getByRole('button', { name: 'Move Footer down' })).toBeDisabled();
135
+ expect(screen.getByRole('button', { name: 'Move Allergies down' })).toBeDisabled();
136
+ expect(screen.getByRole('button', { name: 'Move Allergies up' })).toBeEnabled();
137
+ });
138
+
139
+ it('saves a reorder as renumbered order global properties', async () => {
140
+ const user = userEvent.setup();
141
+ mockFetch();
142
+ renderWithSwr(<VisitSummaryConfig />);
143
+
144
+ await user.click(await screen.findByRole('button', { name: 'Move Vitals down' }));
145
+ await user.click(screen.getByRole('button', { name: 'Save' }));
146
+
147
+ expect(mockSaveSectionSettings).toHaveBeenCalledWith([
148
+ { sectionKey: 'facilityHeader', property: 'report.visitSummary.section.facilityHeader.order', value: '10' },
149
+ { sectionKey: 'allergies', property: 'report.visitSummary.section.allergies.order', value: '20' },
150
+ { sectionKey: 'vitals', property: 'report.visitSummary.section.vitals.order', value: '30' },
151
+ ]);
152
+ });
153
+
154
+ it('offers the preview without asking for a visit uuid', async () => {
155
+ mockFetch();
156
+ renderWithSwr(<VisitSummaryConfig />);
157
+
158
+ expect(await screen.findByRole('button', { name: 'Save & preview' })).toBeEnabled();
159
+ expect(screen.queryByLabelText(/visit uuid/i)).not.toBeInTheDocument();
160
+ expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
161
+ });
162
+
163
+ it('requests the sample preview on a relative path with no visit uuid and renders the returned blob', async () => {
164
+ const user = userEvent.setup();
165
+ mockFetch();
166
+ renderWithSwr(<VisitSummaryConfig />);
167
+
168
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
169
+
170
+ const previewFrame = await screen.findByLabelText('Visit summary PDF preview');
171
+ expect(previewFrame).toHaveAttribute('data', mockObjectUrl);
172
+
173
+ const [requestUrl, requestInit] = previewCall();
174
+ expect(requestUrl).toBe('/ws/rest/v1/patientdocuments/visitSummary/preview');
175
+ expect(requestUrl).not.toContain('visitUuid');
176
+ // openmrsFetch prepends the OpenMRS base itself; an absolute path would double it.
177
+ expect(requestUrl).not.toContain(window.openmrsBase);
178
+ // No Accept header: it makes an old server reformat its 404 into a 500 HTML page,
179
+ // which would misreport a too-old module as a generation failure.
180
+ expect((requestInit as RequestInit).headers).toBeUndefined();
181
+ expect((requestInit as RequestInit).signal).toBeInstanceOf(AbortSignal);
182
+ expect(window.URL.createObjectURL).toHaveBeenCalledWith(mockPdfBlob);
183
+ });
184
+
185
+ it('saves the pending changes before requesting the preview', async () => {
186
+ const user = userEvent.setup();
187
+ mockFetch();
188
+ renderWithSwr(<VisitSummaryConfig />);
189
+
190
+ await user.click(await screen.findByRole('switch', { name: 'Include Vitals' }));
191
+ await user.click(screen.getByRole('button', { name: 'Save & preview' }));
192
+
193
+ await screen.findByLabelText('Visit summary PDF preview');
194
+
195
+ expect(mockSaveSectionSettings).toHaveBeenCalledWith([
196
+ { sectionKey: 'vitals', property: 'report.visitSummary.section.vitals.enabled', value: 'false' },
197
+ ]);
198
+ const previewCallIndex = mockOpenmrsFetch.mock.calls.findIndex(([url]) => isPreviewCall(url));
199
+ expect(mockSaveSectionSettings.mock.invocationCallOrder[0]).toBeLessThan(
200
+ mockOpenmrsFetch.mock.invocationCallOrder[previewCallIndex],
201
+ );
202
+ });
203
+
204
+ it('does not request the preview when the save fails', async () => {
205
+ const user = userEvent.setup();
206
+ mockFetch();
207
+ mockSaveSectionSettings.mockResolvedValue([
208
+ { sectionKey: 'vitals', property: 'report.visitSummary.section.vitals.enabled', value: 'false' },
209
+ ]);
210
+ renderWithSwr(<VisitSummaryConfig />);
211
+
212
+ await user.click(await screen.findByRole('switch', { name: 'Include Vitals' }));
213
+ await user.click(screen.getByRole('button', { name: 'Save & preview' }));
214
+
215
+ expect(previewCall()).toBeUndefined();
216
+ expect(screen.queryByLabelText('Visit summary PDF preview')).not.toBeInTheDocument();
217
+ });
218
+
219
+ it('shows the permission message without a retry when the preview returns 403', async () => {
220
+ const user = userEvent.setup();
221
+ mockFetch({ preview: () => Promise.reject(rejectionWithStatus(403)) });
222
+ renderWithSwr(<VisitSummaryConfig />);
223
+
224
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
225
+
226
+ expect(await screen.findByText('Not authorized')).toBeVisible();
227
+ expect(screen.getByText(forbiddenText)).toBeVisible();
228
+ expect(screen.queryByRole('button', { name: 'Retry' })).not.toBeInTheDocument();
229
+ expect(screen.queryByLabelText('Visit summary PDF preview')).not.toBeInTheDocument();
230
+ });
231
+
232
+ it('reports an outdated backend, not a generic failure, when the preview returns 404', async () => {
233
+ const user = userEvent.setup();
234
+ mockFetch({ preview: () => Promise.reject(rejectionWithStatus(404)) });
235
+ renderWithSwr(<VisitSummaryConfig />);
236
+
237
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
238
+
239
+ expect(await screen.findByText(endpointMissingText)).toBeVisible();
240
+ expect(screen.getByText('Preview not available on this server')).toBeVisible();
241
+ expect(screen.queryByText(generationFailedText)).not.toBeInTheDocument();
242
+ expect(screen.queryByText(forbiddenText)).not.toBeInTheDocument();
243
+ expect(screen.getByRole('button', { name: 'Retry' })).toBeEnabled();
244
+ });
245
+
246
+ it('shows a distinct retryable failure when the preview returns 500', async () => {
247
+ const user = userEvent.setup();
248
+ mockFetch({ preview: () => Promise.reject(rejectionWithStatus(500)) });
249
+ renderWithSwr(<VisitSummaryConfig />);
250
+
251
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
252
+
253
+ expect(await screen.findByText(generationFailedText)).toBeVisible();
254
+ expect(screen.getByText('PDF generation failed')).toBeVisible();
255
+ expect(screen.queryByText(endpointMissingText)).not.toBeInTheDocument();
256
+ expect(screen.queryByText(networkErrorText)).not.toBeInTheDocument();
257
+ expect(screen.getByRole('button', { name: 'Retry' })).toBeEnabled();
258
+ });
259
+
260
+ it('reports a generation failure, not a network error, when the response body cannot be read', async () => {
261
+ const user = userEvent.setup();
262
+ mockFetch({
263
+ preview: () =>
264
+ Promise.resolve({
265
+ blob: () => Promise.reject(new TypeError('Failed to read response body')),
266
+ } as unknown as FetchResponse),
267
+ });
268
+ renderWithSwr(<VisitSummaryConfig />);
269
+
270
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
271
+
272
+ expect(await screen.findByText(generationFailedText)).toBeVisible();
273
+ expect(screen.queryByText(networkErrorText)).not.toBeInTheDocument();
274
+ expect(screen.queryByLabelText('Visit summary PDF preview')).not.toBeInTheDocument();
275
+ });
276
+
277
+ it('shows the network error when the preview request never reaches the server', async () => {
278
+ const user = userEvent.setup();
279
+ mockFetch({ preview: () => Promise.reject(new TypeError('Failed to fetch')) });
280
+ renderWithSwr(<VisitSummaryConfig />);
281
+
282
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
283
+
284
+ expect(await screen.findByText(networkErrorText)).toBeVisible();
285
+ expect(screen.getByText('Network error')).toBeVisible();
286
+ expect(screen.queryByText(generationFailedText)).not.toBeInTheDocument();
287
+ expect(screen.queryByText(endpointMissingText)).not.toBeInTheDocument();
288
+ });
289
+
290
+ it('retries the preview after a failure and renders the PDF on success', async () => {
291
+ const user = userEvent.setup();
292
+ const preview = vi
293
+ .fn()
294
+ .mockRejectedValueOnce(rejectionWithStatus(500))
295
+ .mockResolvedValueOnce({ blob: () => Promise.resolve(mockPdfBlob) } as unknown as FetchResponse);
296
+ mockFetch({ preview });
297
+ renderWithSwr(<VisitSummaryConfig />);
298
+
299
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
300
+ await screen.findByText(generationFailedText);
301
+
302
+ await user.click(screen.getByRole('button', { name: 'Retry' }));
303
+
304
+ expect(await screen.findByLabelText('Visit summary PDF preview')).toHaveAttribute('data', mockObjectUrl);
305
+ expect(screen.queryByText(generationFailedText)).not.toBeInTheDocument();
306
+ });
307
+
308
+ it('revokes the preview object URL on unmount', async () => {
309
+ const user = userEvent.setup();
310
+ mockFetch();
311
+ const { unmount } = renderWithSwr(<VisitSummaryConfig />);
312
+
313
+ await user.click(await screen.findByRole('button', { name: 'Save & preview' }));
314
+ await screen.findByLabelText('Visit summary PDF preview');
315
+
316
+ unmount();
317
+
318
+ expect(window.URL.revokeObjectURL).toHaveBeenCalledWith(mockObjectUrl);
319
+ });
320
+ });
@@ -0,0 +1,3 @@
1
+ export const configSchema = {};
2
+
3
+ export type Config = {};
@@ -0,0 +1,2 @@
1
+ declare module '*.css';
2
+ declare module '*.scss';
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { getAsyncLifecycle, defineConfigSchema } from '@openmrs/esm-framework';
2
+ import { configSchema } from './config-schema';
3
+
4
+ const moduleName = '@openmrs/esm-patient-documents-admin-app';
5
+
6
+ const options = {
7
+ featureName: 'patient-documents-admin',
8
+ moduleName,
9
+ };
10
+
11
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
12
+
13
+ export function startupApp() {
14
+ defineConfigSchema(moduleName, configSchema);
15
+ }
16
+
17
+ export const root = getAsyncLifecycle(() => import('./root.component'), options);
18
+
19
+ export const visitSummaryConfigCardLink = getAsyncLifecycle(() => import('./admin-card-link.component'), options);
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { PageHeader } from '@openmrs/esm-framework';
4
+ import VisitSummaryConfig from './config/visit-summary-config.component';
5
+ import styles from './root.scss';
6
+
7
+ const Root: React.FC = () => {
8
+ const { t } = useTranslation();
9
+ return (
10
+ <main className={`omrs-main-content ${styles.main}`}>
11
+ {/* illustration is required by the type; no styleguide pictogram fits this page yet. */}
12
+ <PageHeader illustration={<></>} title={t('moduleTitle', 'Visit Summary Configuration')} />
13
+ <VisitSummaryConfig />
14
+ </main>
15
+ );
16
+ };
17
+
18
+ export default Root;
package/src/root.scss ADDED
@@ -0,0 +1,5 @@
1
+ @use '@carbon/colors';
2
+
3
+ .main {
4
+ background-color: colors.$white-0;
5
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json.openmrs.org/routes.schema.json",
3
+ "backendDependencies": {
4
+ "patientdocuments": ">=1.2.0",
5
+ "webservices.rest": ">=2.2.0"
6
+ },
7
+ "extensions": [
8
+ {
9
+ "name": "visit-summary-config-card-link",
10
+ "slot": "system-admin-page-card-link-slot",
11
+ "component": "visitSummaryConfigCardLink",
12
+ "privileges": ["Manage Global Properties"]
13
+ }
14
+ ],
15
+ "pages": [
16
+ {
17
+ "component": "root",
18
+ "route": "visit-summary-config"
19
+ }
20
+ ]
21
+ }
package/src/types.ts ADDED
@@ -0,0 +1,12 @@
1
+ export interface VisitSummarySection {
2
+ sectionKey: string;
3
+ label: string;
4
+ enabled: boolean;
5
+ order: number;
6
+ toggleable: boolean;
7
+ }
8
+
9
+ export interface SystemSetting {
10
+ uuid: string;
11
+ value: string;
12
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "cardLinkContent": "Visit Summary Sections",
3
+ "cardLinkHeading": "Manage Visit Summary",
4
+ "generatingPreview": "Generating preview...",
5
+ "instructions": "Choose which sections appear in the visit summary PDF and the order they appear in. Changes apply after saving.",
6
+ "lockedSection": "{{section}} is always included",
7
+ "lockedSectionExplanation": "This section is always included",
8
+ "moduleTitle": "Visit Summary Configuration",
9
+ "moveDown": "Move {{section}} down",
10
+ "moveUp": "Move {{section}} up",
11
+ "noSectionsBody": "The server returned no visit summary sections. Sections are registered by the patientdocuments module and by modules that extend it.",
12
+ "noSectionsTitle": "No sections registered",
13
+ "pinnedSectionExplanation": "This section is always included and always prints at the bottom of every page",
14
+ "previewEndpointMissing": "The patientdocuments module running on this server is missing or too old to provide the sample preview. Update it and try again.",
15
+ "previewEndpointMissingTitle": "Preview not available on this server",
16
+ "previewGenerationFailed": "The server could not generate the sample preview. Try again — if the problem persists, check the server logs.",
17
+ "previewGenerationFailedTitle": "PDF generation failed",
18
+ "previewHelper": "The preview is rendered from sample data with the saved settings. No patient record is used.",
19
+ "previewNetworkError": "The preview could not be retrieved. Check your network connection.",
20
+ "previewNetworkErrorTitle": "Network error",
21
+ "previewNotAuthorizedTitle": "Not authorized",
22
+ "previewOpenInNewTab": "Open the preview in a new tab",
23
+ "previewPaneLabel": "Visit summary PDF preview",
24
+ "previewPlaceholder": "The PDF preview will appear here after you select Save & preview.",
25
+ "previewUnsupported": "This browser can't display PDFs inline.",
26
+ "retry": "Retry",
27
+ "saveAndPreviewButton": "Save & preview",
28
+ "saveButton": "Save",
29
+ "saveFailedSubtitle": "Failed to save: {{properties}}. The list has been reloaded.",
30
+ "saveFailedTitle": "Some settings were not saved",
31
+ "saveSuccess": "Visit summary settings saved",
32
+ "saving": "Saving...",
33
+ "sectionListLabel": "Visit summary sections",
34
+ "sectionsFetchError": "Couldn't load the visit summary sections",
35
+ "sectionsFetchErrorSubtitle": "Check that the patientdocuments module is installed and up to date.",
36
+ "sectionsFetchForbiddenSubtitle": "Your account lacks the Get Global Properties privilege required to view this page.",
37
+ "toggleOff": "Off",
38
+ "toggleOn": "On",
39
+ "toggleSection": "Include {{section}}"
40
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["src/**/*", "../../tools/setup-tests.ts"],
4
+ }
@@ -0,0 +1,4 @@
1
+ import { mergeConfig } from 'vitest/config';
2
+ import sharedConfig from '../../tools/vitest.shared';
3
+
4
+ export default mergeConfig(sharedConfig, {});