@kenyaemr/esm-admin-app 5.4.4-pre.424 → 5.4.4-pre.426
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/1462.js +1 -1
- package/dist/1972.js +1 -1
- package/dist/2016.js +1 -1
- package/dist/2270.js +1 -1
- package/dist/2270.js.map +1 -1
- package/dist/2294.js +1 -1
- package/dist/2294.js.map +1 -1
- package/dist/235.js +1 -0
- package/dist/235.js.map +1 -0
- package/dist/2467.js +1 -1
- package/dist/2467.js.map +1 -1
- package/dist/2763.js +1 -0
- package/dist/2763.js.map +1 -0
- package/dist/2948.js +1 -1
- package/dist/3380.js +1 -1
- package/dist/3380.js.map +1 -1
- package/dist/3548.js +1 -1
- package/dist/3816.js +1 -1
- package/dist/3852.js +1 -0
- package/dist/3852.js.map +1 -0
- package/dist/4157.js +1 -0
- package/dist/4157.js.map +1 -0
- package/dist/4337.js +1 -1
- package/dist/4858.js +1 -1
- package/dist/487.js +1 -1
- package/dist/552.js +1 -0
- package/dist/552.js.map +1 -0
- package/dist/6092.js +1 -0
- package/dist/6092.js.map +1 -0
- package/dist/6456.js +1 -1
- package/dist/7607.js +1 -1
- package/dist/7737.js +1 -1
- package/dist/7739.js +1 -1
- package/dist/8376.js +1 -1
- package/dist/8727.js +1 -1
- package/dist/9182.js +1 -1
- package/dist/9406.js +1 -0
- package/dist/9406.js.map +1 -0
- package/dist/9446.js +1 -1
- package/dist/9647.js +1 -1
- package/dist/kenyaemr-esm-admin-app.js +3 -3
- package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +208 -157
- package/dist/kenyaemr-esm-admin-app.js.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/components/global-property/hooks/useLogoUpload.ts +28 -0
- package/src/components/global-property/table/global-property-table.component.tsx +117 -125
- package/src/components/global-property/table/global-property-table.scss +10 -0
- package/src/components/global-property/table/global-property-table.test.tsx +30 -26
- package/src/components/global-property/workspace/upload-logo.workspace.scss +61 -0
- package/src/components/global-property/workspace/upload-logo.workspace.test.tsx +180 -0
- package/src/components/global-property/workspace/upload-logo.workspace.tsx +191 -0
- package/src/index.ts +4 -0
- package/src/routes.json +5 -0
- package/translations/am.json +22 -5
- package/translations/en.json +25 -8
- package/translations/fr.json +22 -5
- package/translations/sw.json +22 -5
- package/dist/1442.js +0 -1
- package/dist/1442.js.map +0 -1
- package/dist/5910.js +0 -1
- package/dist/5910.js.map +0 -1
- package/dist/6253.js +0 -1
- package/dist/6253.js.map +0 -1
- package/dist/6455.js +0 -1
- package/dist/6455.js.map +0 -1
- package/dist/6925.js +0 -1
- package/dist/6925.js.map +0 -1
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"adminLeftPanelLink","name":"admin-left-panel-link","slot":"admin-left-panel-slot"},{"component":"userManagementLeftPannelLink","name":"user-management-left-panel-link","slot":"admin-left-panel-slot"},{"component":"etlAdministrationLeftPannelLink","name":"etl-administration-left-panel-link","slot":"admin-left-panel-slot"},{"component":"locationsLeftPanelLink","name":"locations-left-panel-link","slot":"admin-left-panel-slot"},{"component":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"},{"name":"global-property-link","component":"globalPropertyLink","slot":"admin-left-panel-slot"},{"component":"providerBanner","name":"provider-banner","slot":"provider-banner-info-slot","order":1},{"component":"globalPropertyDashboard","name":"global-property-dashboard"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"},{"component":"hwrConfirmationModal","name":"hwr-confirmation-modal"},{"component":"hwrEmptyModal","name":"hwr-empty-modal"},{"component":"hwrSyncModal","name":"hwr-syncing-modal"},{"component":"deleteGlobalPropertyModal","name":"delete-global-property-modal"}],"workspaces2":[{"name":"manage-user-workspace","component":"manageUserWorkspace","window":"esm-admin-extra-wide-workspace-window"},{"name":"user-role-scope-workspace","component":"userRoleScopeWorkspace","window":"esm-admin-workspace-window"},{"name":"add-location-workspace","component":"addLocation","window":"esm-admin-workspace-window"},{"name":"search-location-workspace","component":"searchLocationWorkspace","window":"esm-admin-workspace-window"},{"name":"global-property-workspace","component":"globalPropertyWorkspace","window":"esm-admin-workspace-window"}],"workspaceWindows2":[{"name":"esm-admin-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"wider"},{"name":"esm-admin-extra-wide-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"extra-wide"}],"workspaceGroups2":[{"name":"esm-admin-workspace-group","scopePattern":"/admin","closeable":true}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.4-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"adminLeftPanelLink","name":"admin-left-panel-link","slot":"admin-left-panel-slot"},{"component":"userManagementLeftPannelLink","name":"user-management-left-panel-link","slot":"admin-left-panel-slot"},{"component":"etlAdministrationLeftPannelLink","name":"etl-administration-left-panel-link","slot":"admin-left-panel-slot"},{"component":"locationsLeftPanelLink","name":"locations-left-panel-link","slot":"admin-left-panel-slot"},{"component":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"},{"name":"global-property-link","component":"globalPropertyLink","slot":"admin-left-panel-slot"},{"component":"providerBanner","name":"provider-banner","slot":"provider-banner-info-slot","order":1},{"component":"globalPropertyDashboard","name":"global-property-dashboard"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"},{"component":"hwrConfirmationModal","name":"hwr-confirmation-modal"},{"component":"hwrEmptyModal","name":"hwr-empty-modal"},{"component":"hwrSyncModal","name":"hwr-syncing-modal"},{"component":"deleteGlobalPropertyModal","name":"delete-global-property-modal"}],"workspaces2":[{"name":"manage-user-workspace","component":"manageUserWorkspace","window":"esm-admin-extra-wide-workspace-window"},{"name":"user-role-scope-workspace","component":"userRoleScopeWorkspace","window":"esm-admin-workspace-window"},{"name":"add-location-workspace","component":"addLocation","window":"esm-admin-workspace-window"},{"name":"search-location-workspace","component":"searchLocationWorkspace","window":"esm-admin-workspace-window"},{"name":"global-property-workspace","component":"globalPropertyWorkspace","window":"esm-admin-workspace-window"},{"name":"upload-logo-workspace","component":"uploadLogoWorkspace","window":"esm-admin-workspace-window"}],"workspaceWindows2":[{"name":"esm-admin-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"wider"},{"name":"esm-admin-extra-wide-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"extra-wide"}],"workspaceGroups2":[{"name":"esm-admin-workspace-group","scopePattern":"/admin","closeable":true}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.4-pre.426"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export const ALLOWED_LOGO_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
|
|
4
|
+
export const MAX_LOGO_SIZE_BYTES = 2 * 1024 * 1024; // 2 MB
|
|
5
|
+
|
|
6
|
+
export type LogoTarget = 'prescription' | 'receipt';
|
|
7
|
+
|
|
8
|
+
export type UploadLogoResponse = {
|
|
9
|
+
savedPath: string;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function uploadLogo(file: File, target: LogoTarget = 'prescription'): Promise<UploadLogoResponse> {
|
|
14
|
+
const formData = new FormData();
|
|
15
|
+
formData.append('file', file);
|
|
16
|
+
|
|
17
|
+
const response = await openmrsFetch(`${restBaseUrl}/palladiumemr/logo?target=${target}`, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
body: formData,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
const errorData = await response.json().catch(() => ({}));
|
|
24
|
+
throw new Error(errorData?.error ?? errorData?.error?.message ?? 'Failed to upload image');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return response.json();
|
|
28
|
+
}
|
|
@@ -2,28 +2,25 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
DataTable,
|
|
5
|
-
|
|
5
|
+
OverflowMenu,
|
|
6
|
+
OverflowMenuItem,
|
|
6
7
|
Pagination,
|
|
7
|
-
Search,
|
|
8
8
|
Table,
|
|
9
9
|
TableBody,
|
|
10
10
|
TableCell,
|
|
11
|
+
TableContainer,
|
|
11
12
|
TableHead,
|
|
12
13
|
TableHeader,
|
|
13
14
|
TableRow,
|
|
15
|
+
TableToolbar,
|
|
16
|
+
TableToolbarContent,
|
|
17
|
+
TableToolbarSearch,
|
|
18
|
+
Tag,
|
|
14
19
|
type DataTableHeader,
|
|
15
20
|
} from '@carbon/react';
|
|
16
|
-
import { Add,
|
|
21
|
+
import { Add, Upload } from '@carbon/react/icons';
|
|
17
22
|
import { useTranslation } from 'react-i18next';
|
|
18
|
-
import {
|
|
19
|
-
ErrorCard,
|
|
20
|
-
isDesktop,
|
|
21
|
-
launchWorkspace2,
|
|
22
|
-
showModal,
|
|
23
|
-
useDebounce,
|
|
24
|
-
useLayoutType,
|
|
25
|
-
usePaginationInfo,
|
|
26
|
-
} from '@openmrs/esm-framework';
|
|
23
|
+
import { ErrorCard, launchWorkspace2, showModal, useDebounce, usePaginationInfo } from '@openmrs/esm-framework';
|
|
27
24
|
|
|
28
25
|
import { useGlobalProperties } from '../hooks/useGlobalProperty';
|
|
29
26
|
import styles from './global-property-table.scss';
|
|
@@ -39,15 +36,12 @@ type GlobalPropertyRow = {
|
|
|
39
36
|
|
|
40
37
|
const GlobalPropertyTable: React.FC = () => {
|
|
41
38
|
const { t } = useTranslation();
|
|
42
|
-
const layoutType = useLayoutType();
|
|
43
|
-
const desktop = isDesktop(layoutType);
|
|
44
39
|
|
|
45
40
|
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
|
|
46
41
|
const [searchTerm, setSearchTerm] = useState('');
|
|
47
42
|
const debouncedSearchTerm = useDebounce(searchTerm, 300);
|
|
48
43
|
|
|
49
44
|
const {
|
|
50
|
-
isLoading,
|
|
51
45
|
data: globalProperties = [],
|
|
52
46
|
error,
|
|
53
47
|
goTo,
|
|
@@ -66,7 +60,7 @@ const GlobalPropertyTable: React.FC = () => {
|
|
|
66
60
|
[t],
|
|
67
61
|
);
|
|
68
62
|
|
|
69
|
-
const
|
|
63
|
+
const tableRows: Array<GlobalPropertyRow> = useMemo(
|
|
70
64
|
() =>
|
|
71
65
|
globalProperties.map((gp, idx) => ({
|
|
72
66
|
id: gp?.uuid ?? `gp-${idx}`,
|
|
@@ -77,8 +71,9 @@ const GlobalPropertyTable: React.FC = () => {
|
|
|
77
71
|
);
|
|
78
72
|
|
|
79
73
|
const handleSearchChange = useCallback(
|
|
80
|
-
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
-
|
|
74
|
+
(event: React.ChangeEvent<HTMLInputElement> | '', value?: string) => {
|
|
75
|
+
const searchValue = typeof event === 'string' ? value ?? '' : event.target.value;
|
|
76
|
+
setSearchTerm(searchValue);
|
|
82
77
|
if (currentPage !== 1) {
|
|
83
78
|
goTo(1);
|
|
84
79
|
}
|
|
@@ -106,6 +101,12 @@ const GlobalPropertyTable: React.FC = () => {
|
|
|
106
101
|
[globalProperties, mutate],
|
|
107
102
|
);
|
|
108
103
|
|
|
104
|
+
const openUploadImageWorkspace = useCallback(() => {
|
|
105
|
+
launchWorkspace2('upload-logo-workspace', {
|
|
106
|
+
mutateGlobalProperty: mutate,
|
|
107
|
+
});
|
|
108
|
+
}, [mutate]);
|
|
109
|
+
|
|
109
110
|
const handleEdit = useCallback(
|
|
110
111
|
(row: GlobalPropertyRow) => {
|
|
111
112
|
const systemSetting = globalProperties.find((gp) => gp.uuid === row.id);
|
|
@@ -126,95 +127,6 @@ const GlobalPropertyTable: React.FC = () => {
|
|
|
126
127
|
[mutate],
|
|
127
128
|
);
|
|
128
129
|
|
|
129
|
-
const renderContent = () => {
|
|
130
|
-
if (isLoading) {
|
|
131
|
-
return (
|
|
132
|
-
<DataTableSkeleton
|
|
133
|
-
aria-label={t('globalProperties', 'Global properties')}
|
|
134
|
-
headers={headers}
|
|
135
|
-
showHeader
|
|
136
|
-
showToolbar
|
|
137
|
-
/>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (rows.length === 0) {
|
|
142
|
-
return (
|
|
143
|
-
<p className={styles.emptyState}>
|
|
144
|
-
{debouncedSearchTerm
|
|
145
|
-
? t('noMatchingGlobalProperties', 'No global properties match your search')
|
|
146
|
-
: t('noGlobalProperties', 'No global properties to display')}
|
|
147
|
-
</p>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<>
|
|
153
|
-
<DataTable useZebraStyles size={desktop ? 'sm' : 'md'} rows={rows} headers={headers}>
|
|
154
|
-
{({ rows: renderRows, headers: renderHeaders, getTableProps, getHeaderProps, getRowProps, getCellProps }) => (
|
|
155
|
-
<Table {...getTableProps()}>
|
|
156
|
-
<TableHead>
|
|
157
|
-
<TableRow>
|
|
158
|
-
{renderHeaders.map((header) => (
|
|
159
|
-
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
|
|
160
|
-
))}
|
|
161
|
-
<TableHeader>
|
|
162
|
-
<span className={styles.visuallyHidden}>{t('actions', 'Actions')}</span>
|
|
163
|
-
</TableHeader>
|
|
164
|
-
</TableRow>
|
|
165
|
-
</TableHead>
|
|
166
|
-
<TableBody>
|
|
167
|
-
{renderRows.map((row) => {
|
|
168
|
-
const sourceRow = rows.find((r) => r.id === row.id)!;
|
|
169
|
-
return (
|
|
170
|
-
<TableRow {...getRowProps({ row })}>
|
|
171
|
-
{row.cells.map((cell) => (
|
|
172
|
-
<TableCell {...getCellProps({ cell })}>{cell.value}</TableCell>
|
|
173
|
-
))}
|
|
174
|
-
<TableCell className={styles.actionsCell}>
|
|
175
|
-
<Button
|
|
176
|
-
kind="ghost"
|
|
177
|
-
size="sm"
|
|
178
|
-
renderIcon={Edit}
|
|
179
|
-
iconDescription={t('edit', 'Edit')}
|
|
180
|
-
hasIconOnly
|
|
181
|
-
onClick={() => handleEdit(sourceRow)}
|
|
182
|
-
/>
|
|
183
|
-
<Button
|
|
184
|
-
kind="danger--ghost"
|
|
185
|
-
size="sm"
|
|
186
|
-
renderIcon={TrashCan}
|
|
187
|
-
iconDescription={t('delete', 'Delete')}
|
|
188
|
-
hasIconOnly
|
|
189
|
-
onClick={() => handleDelete(sourceRow)}
|
|
190
|
-
/>
|
|
191
|
-
</TableCell>
|
|
192
|
-
</TableRow>
|
|
193
|
-
);
|
|
194
|
-
})}
|
|
195
|
-
</TableBody>
|
|
196
|
-
</Table>
|
|
197
|
-
)}
|
|
198
|
-
</DataTable>
|
|
199
|
-
|
|
200
|
-
<Pagination
|
|
201
|
-
itemsPerPageText={t('itemsPerPage', 'Items per page:')}
|
|
202
|
-
forwardText={t('nextPage', 'Next page')}
|
|
203
|
-
backwardText={t('previousPage', 'Previous page')}
|
|
204
|
-
itemRangeText={(min, max, total) =>
|
|
205
|
-
t('minMaxItems', '{{min}}-{{max}} of {{total}} items', { min, max, total })
|
|
206
|
-
}
|
|
207
|
-
pageRangeText={(_current, total) => t('pageRangeText', 'of {{count}} pages', { count: total })}
|
|
208
|
-
page={currentPage}
|
|
209
|
-
pageSize={pageSize}
|
|
210
|
-
pageSizes={pageSizes?.length > 0 ? pageSizes : PAGE_SIZE_OPTIONS}
|
|
211
|
-
totalItems={totalCount ?? 0}
|
|
212
|
-
onChange={handlePaginationChange}
|
|
213
|
-
/>
|
|
214
|
-
</>
|
|
215
|
-
);
|
|
216
|
-
};
|
|
217
|
-
|
|
218
130
|
if (error) {
|
|
219
131
|
return (
|
|
220
132
|
<div className={styles.dataTableContainer}>
|
|
@@ -224,24 +136,104 @@ const GlobalPropertyTable: React.FC = () => {
|
|
|
224
136
|
}
|
|
225
137
|
|
|
226
138
|
return (
|
|
227
|
-
<div className={styles.
|
|
228
|
-
<
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
139
|
+
<div className={styles.container}>
|
|
140
|
+
<DataTable size="sm" rows={tableRows} headers={headers} isSortable useZebraStyles>
|
|
141
|
+
{({
|
|
142
|
+
rows,
|
|
143
|
+
headers,
|
|
144
|
+
getHeaderProps,
|
|
145
|
+
getRowProps,
|
|
146
|
+
getBatchActionProps,
|
|
147
|
+
getToolbarProps,
|
|
148
|
+
getTableProps,
|
|
149
|
+
getTableContainerProps,
|
|
150
|
+
}) => {
|
|
151
|
+
const batchActionProps = getBatchActionProps();
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<TableContainer
|
|
155
|
+
title={t('globalProperty', 'Global Property')}
|
|
156
|
+
description={t(
|
|
157
|
+
'globalPropertyDescription',
|
|
158
|
+
'A list of all global properties for the system. Filter by search term to find specific properties.',
|
|
159
|
+
)}
|
|
160
|
+
{...getTableContainerProps()}>
|
|
161
|
+
<TableToolbar {...getToolbarProps()}>
|
|
162
|
+
<TableToolbarContent aria-hidden={batchActionProps.shouldShowBatchActions}>
|
|
163
|
+
<TableToolbarSearch
|
|
164
|
+
persistent
|
|
165
|
+
onChange={handleSearchChange}
|
|
166
|
+
placeholder={t('searchForGlobalProperties', 'Search for global properties')}
|
|
167
|
+
/>
|
|
168
|
+
<Button renderIcon={Add} onClick={() => openWorkspace()} size="sm" kind="primary">
|
|
169
|
+
{t('addGlobalProperty', 'Add global property')}
|
|
170
|
+
</Button>
|
|
171
|
+
<Button renderIcon={Upload} onClick={openUploadImageWorkspace} size="sm" kind="tertiary">
|
|
172
|
+
{t('uploadImage', 'Upload image')}
|
|
173
|
+
</Button>
|
|
174
|
+
</TableToolbarContent>
|
|
175
|
+
</TableToolbar>
|
|
176
|
+
|
|
177
|
+
<Table {...getTableProps()} aria-label="Global properties">
|
|
178
|
+
<TableHead>
|
|
179
|
+
<TableRow>
|
|
180
|
+
{headers.map((header) => (
|
|
181
|
+
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
|
|
182
|
+
))}
|
|
183
|
+
<TableHeader aria-label={t('rowActions', 'Row actions')} />
|
|
184
|
+
</TableRow>
|
|
185
|
+
</TableHead>
|
|
186
|
+
|
|
187
|
+
<TableBody>
|
|
188
|
+
{rows.map((row, index) => {
|
|
189
|
+
return (
|
|
190
|
+
<React.Fragment key={row.id}>
|
|
191
|
+
<TableRow {...getRowProps({ row })}>
|
|
192
|
+
{row.cells.map((cell) => {
|
|
193
|
+
if (cell.info.header === 'status') {
|
|
194
|
+
return (
|
|
195
|
+
<TableCell key={cell.id}>
|
|
196
|
+
<Tag size="sm">{cell.value}</Tag>
|
|
197
|
+
</TableCell>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return <TableCell key={cell.id}>{cell.value}</TableCell>;
|
|
201
|
+
})}
|
|
202
|
+
<TableCell className="cds--table-column-menu">
|
|
203
|
+
<OverflowMenu flipped aria-label="overflow-menu">
|
|
204
|
+
<OverflowMenuItem
|
|
205
|
+
onClick={() => handleEdit(tableRows[index])}
|
|
206
|
+
itemText={t('edit', 'Edit')}
|
|
207
|
+
/>
|
|
208
|
+
<OverflowMenuItem
|
|
209
|
+
isDelete
|
|
210
|
+
onClick={() => handleDelete(tableRows[index])}
|
|
211
|
+
itemText={t('delete', 'Delete')}
|
|
212
|
+
/>
|
|
213
|
+
</OverflowMenu>
|
|
214
|
+
</TableCell>
|
|
215
|
+
</TableRow>
|
|
216
|
+
</React.Fragment>
|
|
217
|
+
);
|
|
218
|
+
})}
|
|
219
|
+
</TableBody>
|
|
220
|
+
</Table>
|
|
221
|
+
</TableContainer>
|
|
222
|
+
);
|
|
223
|
+
}}
|
|
224
|
+
</DataTable>
|
|
225
|
+
<Pagination
|
|
226
|
+
itemsPerPageText={t('itemsPerPage', 'Items per page:')}
|
|
227
|
+
forwardText={t('nextPage', 'Next page')}
|
|
228
|
+
backwardText={t('previousPage', 'Previous page')}
|
|
229
|
+
itemRangeText={(min, max, total) => t('minMaxItems', '{{min}}-{{max}} of {{total}} items', { min, max, total })}
|
|
230
|
+
pageRangeText={(_current, total) => t('pageRangeText', 'of {{count}} pages', { count: total })}
|
|
231
|
+
page={currentPage}
|
|
232
|
+
pageSize={pageSize}
|
|
233
|
+
pageSizes={pageSizes?.length > 0 ? pageSizes : PAGE_SIZE_OPTIONS}
|
|
234
|
+
totalItems={totalCount ?? 0}
|
|
235
|
+
onChange={handlePaginationChange}
|
|
236
|
+
/>
|
|
245
237
|
</div>
|
|
246
238
|
);
|
|
247
239
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/colors';
|
|
2
3
|
|
|
3
4
|
.dataTableContainer {
|
|
4
5
|
height: 100%;
|
|
@@ -32,3 +33,12 @@
|
|
|
32
33
|
text-align: center;
|
|
33
34
|
color: var(--cds-text-secondary);
|
|
34
35
|
}
|
|
36
|
+
|
|
37
|
+
.container {
|
|
38
|
+
margin: layout.$layout-01;
|
|
39
|
+
border: 1px solid colors.$gray-20;
|
|
40
|
+
|
|
41
|
+
:global(.cds--table-toolbar) {
|
|
42
|
+
position: relative !important;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -52,6 +52,9 @@ vi.mock('@carbon/react', async (importOriginal) => {
|
|
|
52
52
|
getHeaderProps: ({ header }: any) => ({ key: header.key }),
|
|
53
53
|
getRowProps: ({ row }: any) => ({ key: row.id }),
|
|
54
54
|
getCellProps: ({ cell }: any) => ({ key: cell.id }),
|
|
55
|
+
getToolbarProps: () => ({}),
|
|
56
|
+
getTableContainerProps: () => ({}),
|
|
57
|
+
getBatchActionProps: () => ({ shouldShowBatchActions: false }),
|
|
55
58
|
});
|
|
56
59
|
}
|
|
57
60
|
return children;
|
|
@@ -65,6 +68,8 @@ vi.mock('@carbon/react', async (importOriginal) => {
|
|
|
65
68
|
{hasIconOnly ? null : children}
|
|
66
69
|
</button>
|
|
67
70
|
),
|
|
71
|
+
OverflowMenu: ({ children }: any) => <div>{children}</div>,
|
|
72
|
+
OverflowMenuItem: ({ itemText, onClick }: any) => <button onClick={onClick}>{itemText}</button>,
|
|
68
73
|
};
|
|
69
74
|
});
|
|
70
75
|
|
|
@@ -97,18 +102,6 @@ describe('GlobalPropertyTable', () => {
|
|
|
97
102
|
vi.clearAllMocks();
|
|
98
103
|
});
|
|
99
104
|
|
|
100
|
-
it('shows a loading skeleton while data is loading', () => {
|
|
101
|
-
setupMock({ isLoading: true, data: [] });
|
|
102
|
-
render(<GlobalPropertyTable />);
|
|
103
|
-
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('shows empty state message when no properties exist', () => {
|
|
107
|
-
setupMock({ data: [] });
|
|
108
|
-
render(<GlobalPropertyTable />);
|
|
109
|
-
expect(screen.getByText('No global properties to display')).toBeInTheDocument();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
105
|
it('renders the table with property and value columns', () => {
|
|
113
106
|
setupMock();
|
|
114
107
|
render(<GlobalPropertyTable />);
|
|
@@ -125,10 +118,17 @@ describe('GlobalPropertyTable', () => {
|
|
|
125
118
|
expect(screen.getByText('value2')).toBeInTheDocument();
|
|
126
119
|
});
|
|
127
120
|
|
|
128
|
-
it('
|
|
121
|
+
it('renders the table without data rows when no properties exist', () => {
|
|
122
|
+
setupMock({ data: [], totalCount: 0 });
|
|
123
|
+
render(<GlobalPropertyTable />);
|
|
124
|
+
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
125
|
+
expect(screen.queryByText('setting.one')).not.toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('opens the add workspace when "Add global property" button is clicked', () => {
|
|
129
129
|
setupMock();
|
|
130
130
|
render(<GlobalPropertyTable />);
|
|
131
|
-
fireEvent.click(screen.getByText('Add
|
|
131
|
+
fireEvent.click(screen.getByText('Add global property'));
|
|
132
132
|
expect(mockLaunchWorkspace2).toHaveBeenCalledWith(
|
|
133
133
|
'global-property-workspace',
|
|
134
134
|
expect.objectContaining({
|
|
@@ -138,6 +138,18 @@ describe('GlobalPropertyTable', () => {
|
|
|
138
138
|
);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
+
it('opens the upload image workspace when "Upload image" button is clicked', () => {
|
|
142
|
+
setupMock();
|
|
143
|
+
render(<GlobalPropertyTable />);
|
|
144
|
+
fireEvent.click(screen.getByText('Upload image'));
|
|
145
|
+
expect(mockLaunchWorkspace2).toHaveBeenCalledWith(
|
|
146
|
+
'upload-logo-workspace',
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
mutateGlobalProperty: mockMutate,
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
141
153
|
it('opens the edit workspace with the correct property when edit is clicked', () => {
|
|
142
154
|
setupMock();
|
|
143
155
|
render(<GlobalPropertyTable />);
|
|
@@ -168,19 +180,17 @@ describe('GlobalPropertyTable', () => {
|
|
|
168
180
|
it('filters results and resets to page 1 when user types in the search box', async () => {
|
|
169
181
|
setupMock({ currentPage: 2 });
|
|
170
182
|
render(<GlobalPropertyTable />);
|
|
171
|
-
const searchInput = screen.getByPlaceholderText('Search global
|
|
183
|
+
const searchInput = screen.getByPlaceholderText('Search for global properties');
|
|
172
184
|
fireEvent.change(searchInput, { target: { value: 'setting' } });
|
|
173
185
|
await waitFor(() => {
|
|
174
186
|
expect(mockGoTo).toHaveBeenCalledWith(1);
|
|
175
187
|
});
|
|
176
188
|
});
|
|
177
189
|
|
|
178
|
-
it('
|
|
179
|
-
setupMock(
|
|
190
|
+
it('renders a search input for filtering properties', () => {
|
|
191
|
+
setupMock();
|
|
180
192
|
render(<GlobalPropertyTable />);
|
|
181
|
-
|
|
182
|
-
fireEvent.change(searchInput, { target: { value: 'nonexistent' } });
|
|
183
|
-
expect(screen.getByText('No global properties match your search')).toBeInTheDocument();
|
|
193
|
+
expect(screen.getByPlaceholderText('Search for global properties')).toBeInTheDocument();
|
|
184
194
|
});
|
|
185
195
|
|
|
186
196
|
it('renders an error card when the hook returns an error', () => {
|
|
@@ -189,10 +199,4 @@ describe('GlobalPropertyTable', () => {
|
|
|
189
199
|
expect(screen.getByTestId('error-card')).toBeInTheDocument();
|
|
190
200
|
expect(screen.getByText('Global property')).toBeInTheDocument();
|
|
191
201
|
});
|
|
192
|
-
|
|
193
|
-
it('renders a search input for filtering properties', () => {
|
|
194
|
-
setupMock();
|
|
195
|
-
render(<GlobalPropertyTable />);
|
|
196
|
-
expect(screen.getByPlaceholderText('Search global property by name')).toBeInTheDocument();
|
|
197
|
-
});
|
|
198
202
|
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
|
+
|
|
5
|
+
.form {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
height: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.formContainer {
|
|
13
|
+
margin: layout.$spacing-05;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: layout.$spacing-05;
|
|
17
|
+
overflow-y: auto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.previewContainer {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: layout.$spacing-03;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.previewLabel {
|
|
27
|
+
@include type.type-style('label-01');
|
|
28
|
+
color: colors.$gray-70;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.preview {
|
|
32
|
+
max-width: 100%;
|
|
33
|
+
max-height: 240px;
|
|
34
|
+
object-fit: contain;
|
|
35
|
+
border: 1px solid colors.$gray-20;
|
|
36
|
+
padding: layout.$spacing-03;
|
|
37
|
+
background-color: colors.$white;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.tablet {
|
|
41
|
+
padding: layout.$spacing-06 layout.$spacing-05;
|
|
42
|
+
background-color: colors.$white;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.desktop {
|
|
46
|
+
padding: 0;
|
|
47
|
+
|
|
48
|
+
& button {
|
|
49
|
+
max-width: 50% !important;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.buttonContainer {
|
|
54
|
+
max-width: 50%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.inlineLoading {
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: layout.$spacing-03;
|
|
61
|
+
}
|