@boarteam/boar-pack-common-frontend 2.4.1 → 2.6.0
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/package.json +2 -2
- package/src/components/Descriptions/Descriptions.tsx +151 -47
- package/src/components/Descriptions/DescriptionsCreateModal.tsx +22 -50
- package/src/components/Descriptions/descriptionTypes.ts +18 -8
- package/src/components/Descriptions/useContentViewMode.tsx +28 -0
- package/src/components/Descriptions/useDescriptionColumns.ts +6 -1
- package/src/components/Table/BulkDeleteButton.tsx +2 -2
- package/src/components/Table/BulkEditButton.tsx +2 -2
- package/src/components/Table/CreateEntityModal.tsx +85 -0
- package/src/components/Table/DeleteButton.tsx +44 -0
- package/src/components/Table/Table.tsx +88 -292
- package/src/components/Table/getTableDataQueryParams.ts +79 -0
- package/src/components/Table/index.ts +1 -0
- package/src/components/Table/tableTools.ts +19 -2
- package/src/components/Table/tableTypes.ts +14 -9
- package/src/components/Table/useBulkEditing.tsx +128 -0
- package/src/components/Table/useCreation.tsx +96 -0
- package/src/components/Table/useEditableTable.tsx +84 -0
- package/src/components/Table/ContentViewModeButton.tsx +0 -27
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Popconfirm, Tooltip } from "antd";
|
|
2
|
+
import { DeleteOutlined, LoadingOutlined } from "@ant-design/icons";
|
|
3
|
+
import { createStyles } from "antd-style";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
|
|
7
|
+
const useStyles = createStyles(() => {
|
|
8
|
+
return {
|
|
9
|
+
popconfirm: {
|
|
10
|
+
'.ant-popconfirm-description': {
|
|
11
|
+
marginTop: '0 !important',
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const DeleteButton = (
|
|
18
|
+
{
|
|
19
|
+
onDelete,
|
|
20
|
+
}: {
|
|
21
|
+
onDelete: () => Promise<void>
|
|
22
|
+
}) => {
|
|
23
|
+
const {styles} = useStyles();
|
|
24
|
+
const [loading, setLoading] = useState(false);
|
|
25
|
+
const intl = useIntl();
|
|
26
|
+
|
|
27
|
+
return (<a key="deleteButton">
|
|
28
|
+
{!loading && <Popconfirm
|
|
29
|
+
overlayClassName={styles.popconfirm}
|
|
30
|
+
title={false}
|
|
31
|
+
description={intl.formatMessage({id: "table.deletePopconfirmMessage"})}
|
|
32
|
+
onConfirm={() => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
onDelete().finally(() => setLoading(false));
|
|
35
|
+
}}>
|
|
36
|
+
<Tooltip title={intl.formatMessage({id: "table.deleteText"})}>
|
|
37
|
+
<DeleteOutlined/>
|
|
38
|
+
</Tooltip>
|
|
39
|
+
</Popconfirm>}
|
|
40
|
+
{loading && <LoadingOutlined/>}
|
|
41
|
+
</a>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
export default DeleteButton;
|
|
@@ -1,37 +1,14 @@
|
|
|
1
1
|
import ProTable, { ActionType } from "@ant-design/pro-table";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
DeleteOutlined,
|
|
6
|
-
PlusOutlined,
|
|
7
|
-
QuestionCircleTwoTone,
|
|
8
|
-
StopOutlined,
|
|
9
|
-
} from "@ant-design/icons";
|
|
10
|
-
import { FormattedMessage, useIntl } from "react-intl";
|
|
11
|
-
import { flushSync } from "react-dom";
|
|
12
|
-
import { applyKeywordToSearch, buildJoinFields, collectFieldsFromColumns, getFiltersSearch } from "./tableTools";
|
|
13
|
-
import { TFilterParams, TFilters, TGetAllParams, TSort, TTableProps } from "./tableTypes";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Modal } from "antd";
|
|
4
|
+
import { TFilterParams, TFilters, TSort, TTableProps } from "./tableTypes";
|
|
14
5
|
import useColumnsSets from "./useColumnsSets";
|
|
15
|
-
import BulkEditButton from "./BulkEditButton";
|
|
16
|
-
import _ from "lodash";
|
|
17
|
-
import BulkDeleteButton from "./BulkDeleteButton";
|
|
18
6
|
import { createStyles } from "antd-style";
|
|
19
|
-
import ContentViewModeButton, { VIEW_MODE_TYPE } from "./ContentViewModeButton";
|
|
20
7
|
import { Descriptions } from "../Descriptions";
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export const KEY_SYMBOL = Symbol('key');
|
|
26
|
-
const NEW_RECORD = 'NEW_RECORD';
|
|
27
|
-
|
|
28
|
-
export function getNewId(): string {
|
|
29
|
-
return NEW_RECORD + creatingRecordsCount++;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function isRecordNew(record: Record<string | symbol, any>): boolean {
|
|
33
|
-
return record[KEY_SYMBOL]?.startsWith?.(NEW_RECORD) || record.id?.startsWith?.(NEW_RECORD) || false;
|
|
34
|
-
}
|
|
8
|
+
import { KEY_SYMBOL, useCreation } from "./useCreation";
|
|
9
|
+
import { getTableDataQueryParams } from "./getTableDataQueryParams";
|
|
10
|
+
import { useEditableTable } from "./useEditableTable";
|
|
11
|
+
import { useBulkEditing } from "./useBulkEditing";
|
|
35
12
|
|
|
36
13
|
const useStyles = createStyles(() => {
|
|
37
14
|
return {
|
|
@@ -49,7 +26,7 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
49
26
|
TEntityParams = {},
|
|
50
27
|
TPathParams extends Record<string, string | number> = {},
|
|
51
28
|
TKey = string,
|
|
52
|
-
|
|
29
|
+
>(
|
|
53
30
|
{
|
|
54
31
|
getAll,
|
|
55
32
|
onCreate,
|
|
@@ -76,8 +53,9 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
76
53
|
popupCreation = false,
|
|
77
54
|
toolBarRender,
|
|
78
55
|
params,
|
|
79
|
-
popupDataState,
|
|
80
56
|
editPopupTitle,
|
|
57
|
+
createPopupTitle,
|
|
58
|
+
descriptionsMainTitle,
|
|
81
59
|
...rest
|
|
82
60
|
}: TTableProps<Entity,
|
|
83
61
|
CreateDto,
|
|
@@ -87,17 +65,58 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
87
65
|
) => {
|
|
88
66
|
const actionRefComponent = useRef<ActionType>();
|
|
89
67
|
const actionRef = actionRefProp || actionRefComponent;
|
|
90
|
-
const [createPopupData, setCreatePopupData] = popupDataState ?? useState<Partial<Entity> | undefined>();
|
|
91
68
|
const [updatePopupData, setUpdatePopupData] = useState<Partial<Entity> | undefined>();
|
|
92
|
-
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
|
|
93
|
-
const [selectedRecords, setSelectedRecords] = useState<Entity[]>([]);
|
|
94
|
-
const [lastRequest, setLastRequest] = useState<[TGetAllParams & TPathParams, any] | []>([]);
|
|
95
|
-
const [allSelected, setAllSelected] = useState(false);
|
|
96
69
|
const { styles } = useStyles();
|
|
97
|
-
const [messageApi, contextHolder] = message.useMessage();
|
|
98
|
-
const [descriptionsModalViewMode, setDescriptionsModalViewMode] = useState<VIEW_MODE_TYPE>(VIEW_MODE_TYPE.TABS);
|
|
99
70
|
|
|
100
|
-
const
|
|
71
|
+
const {
|
|
72
|
+
editableConfig,
|
|
73
|
+
} = useEditableTable<Entity, CreateDto, UpdateDto, TPathParams>({
|
|
74
|
+
actionRef,
|
|
75
|
+
pathParams,
|
|
76
|
+
onCreate,
|
|
77
|
+
onUpdate,
|
|
78
|
+
onDelete,
|
|
79
|
+
entityToCreateDto,
|
|
80
|
+
entityToUpdateDto,
|
|
81
|
+
afterSave,
|
|
82
|
+
editable,
|
|
83
|
+
onDeleteMany,
|
|
84
|
+
onUpdateMany,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const {
|
|
88
|
+
rowSelection,
|
|
89
|
+
setSelectedRecords,
|
|
90
|
+
setLastRequest,
|
|
91
|
+
bulkEditButton,
|
|
92
|
+
bulkDeleteButton,
|
|
93
|
+
messagesContext,
|
|
94
|
+
} = useBulkEditing<Entity, TPathParams, UpdateDto, TEntityParams & TFilterParams>({
|
|
95
|
+
actionRef,
|
|
96
|
+
columns,
|
|
97
|
+
idColumnName,
|
|
98
|
+
onDeleteMany,
|
|
99
|
+
onUpdateMany,
|
|
100
|
+
entityToUpdateDto,
|
|
101
|
+
pathParams,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
creationModal,
|
|
106
|
+
createButton,
|
|
107
|
+
} = useCreation<Entity, CreateDto, TPathParams>({
|
|
108
|
+
title: createPopupTitle,
|
|
109
|
+
mainTitle: descriptionsMainTitle,
|
|
110
|
+
columns: columns,
|
|
111
|
+
idColumnName: idColumnName,
|
|
112
|
+
onCreate,
|
|
113
|
+
pathParams,
|
|
114
|
+
entityToCreateDto,
|
|
115
|
+
actionRef,
|
|
116
|
+
createButtonSize: rest.size,
|
|
117
|
+
popupCreation,
|
|
118
|
+
createNewDefaultParams,
|
|
119
|
+
});
|
|
101
120
|
|
|
102
121
|
useEffect(() => {
|
|
103
122
|
setUpdatePopupData(editableRecord);
|
|
@@ -120,55 +139,17 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
120
139
|
sort: TSort = {},
|
|
121
140
|
filters: TFilters = {},
|
|
122
141
|
) => {
|
|
123
|
-
const {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
sortMap,
|
|
130
|
-
...filtersFromSearchForm
|
|
131
|
-
} = params;
|
|
132
|
-
|
|
133
|
-
const queryParams: TGetAllParams & TPathParams = {
|
|
134
|
-
...pathParams,
|
|
135
|
-
page: current,
|
|
136
|
-
limit: pageSize,
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const sortBy = Object
|
|
140
|
-
.entries(sort)
|
|
141
|
-
.reduce<string[]>(
|
|
142
|
-
(data: string[], [key, direction]) => {
|
|
143
|
-
data.push(`${sortMap?.[key] || key},${direction === 'ascend' ? 'ASC' : 'DESC'}`);
|
|
144
|
-
return data;
|
|
145
|
-
},
|
|
146
|
-
[]
|
|
147
|
-
);
|
|
148
|
-
if (!sortBy.length && defaultSort) {
|
|
149
|
-
sortBy.push(defaultSort.join(','));
|
|
150
|
-
}
|
|
151
|
-
queryParams.sort = sortBy;
|
|
152
|
-
|
|
153
|
-
let search = getFiltersSearch({
|
|
154
|
-
baseFilters,
|
|
155
|
-
filters: {
|
|
156
|
-
...filters,
|
|
157
|
-
...filtersFromSearchForm,
|
|
158
|
-
},
|
|
142
|
+
const queryParams = getTableDataQueryParams({
|
|
143
|
+
params,
|
|
144
|
+
sort,
|
|
145
|
+
filters,
|
|
146
|
+
pathParams,
|
|
147
|
+
defaultSort,
|
|
159
148
|
searchableColumns,
|
|
160
|
-
});
|
|
161
|
-
search = applyKeywordToSearch(search, searchableColumns!, columnsState.value!, keyword);
|
|
162
|
-
queryParams.s = JSON.stringify(search);
|
|
163
|
-
|
|
164
|
-
const { joinSelect, joinFields } = buildJoinFields(join);
|
|
165
|
-
queryParams.join = joinSelect;
|
|
166
|
-
|
|
167
|
-
queryParams.fields = columns && collectFieldsFromColumns(
|
|
168
149
|
columns,
|
|
169
150
|
idColumnName,
|
|
170
|
-
|
|
171
|
-
)
|
|
151
|
+
columnsState,
|
|
152
|
+
});
|
|
172
153
|
|
|
173
154
|
const result = await getAll(queryParams);
|
|
174
155
|
|
|
@@ -180,26 +161,6 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
180
161
|
return result;
|
|
181
162
|
}
|
|
182
163
|
|
|
183
|
-
const createButton = <Button
|
|
184
|
-
size={rest.size}
|
|
185
|
-
type="primary"
|
|
186
|
-
key="create"
|
|
187
|
-
onClick={() => {
|
|
188
|
-
if (popupCreation) {
|
|
189
|
-
setCreatePopupData(createNewDefaultParams);
|
|
190
|
-
} else {
|
|
191
|
-
actionRef?.current?.addEditRecord({
|
|
192
|
-
[KEY_SYMBOL]: getNewId(),
|
|
193
|
-
...createNewDefaultParams,
|
|
194
|
-
}, {
|
|
195
|
-
position: 'top',
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}}
|
|
199
|
-
>
|
|
200
|
-
<PlusOutlined /> <FormattedMessage id={'table.newButton'} />
|
|
201
|
-
</Button>;
|
|
202
|
-
|
|
203
164
|
return (<>
|
|
204
165
|
<ProTable<Entity, TEntityParams & TFilterParams>
|
|
205
166
|
actionRef={actionRef}
|
|
@@ -222,117 +183,15 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
222
183
|
}}
|
|
223
184
|
bordered
|
|
224
185
|
search={false}
|
|
225
|
-
editable={
|
|
226
|
-
type: 'multiple',
|
|
227
|
-
editableKeys,
|
|
228
|
-
onChange: setEditableRowKeys,
|
|
229
|
-
async onSave(
|
|
230
|
-
id,
|
|
231
|
-
record,
|
|
232
|
-
origin,
|
|
233
|
-
newLine,
|
|
234
|
-
) {
|
|
235
|
-
if (newLine) {
|
|
236
|
-
await onCreate?.({
|
|
237
|
-
...pathParams,
|
|
238
|
-
requestBody: entityToCreateDto(record),
|
|
239
|
-
});
|
|
240
|
-
} else {
|
|
241
|
-
await onUpdate({
|
|
242
|
-
...pathParams,
|
|
243
|
-
...record,
|
|
244
|
-
requestBody: entityToUpdateDto({
|
|
245
|
-
...pathParams,
|
|
246
|
-
...record,
|
|
247
|
-
}),
|
|
248
|
-
})
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (typeof afterSave === 'function') {
|
|
252
|
-
await afterSave(record);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
flushSync(() => {
|
|
256
|
-
actionRef?.current?.reload();
|
|
257
|
-
});
|
|
258
|
-
},
|
|
259
|
-
async onCancel(
|
|
260
|
-
id,
|
|
261
|
-
record,
|
|
262
|
-
origin,
|
|
263
|
-
) {
|
|
264
|
-
if (record) {
|
|
265
|
-
Object.assign(record, origin);
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
async onDelete(id, row) {
|
|
269
|
-
await onDelete({ ...row, ...pathParams });
|
|
270
|
-
},
|
|
271
|
-
deletePopconfirmMessage: intl.formatMessage({ id: 'table.deletePopconfirmMessage' }),
|
|
272
|
-
onlyAddOneLineAlertMessage: intl.formatMessage({ id: 'table.onlyAddOneLineAlertMessage' }),
|
|
273
|
-
cancelText: <Tooltip title={intl.formatMessage({ id: 'table.cancelText' })}><StopOutlined /></Tooltip>,
|
|
274
|
-
deleteText: <Tooltip title={intl.formatMessage({ id: 'table.deleteText' })}><DeleteOutlined /></Tooltip>,
|
|
275
|
-
saveText: <Button size={"small"} type={"primary"}><FormattedMessage id={'table.saveText'} /></Button>,
|
|
276
|
-
...editable,
|
|
277
|
-
}}
|
|
186
|
+
editable={editableConfig}
|
|
278
187
|
toolBarRender={(...args) => [
|
|
279
188
|
columnsSetSelect?.() || null,
|
|
280
189
|
!viewOnly && onUpdateMany
|
|
281
|
-
?
|
|
282
|
-
|
|
283
|
-
selectedRecords={selectedRecords}
|
|
284
|
-
lastRequest={lastRequest}
|
|
285
|
-
allSelected={allSelected}
|
|
286
|
-
columns={columns}
|
|
287
|
-
idColumnName={idColumnName}
|
|
288
|
-
// @ts-ignore
|
|
289
|
-
onSubmit={values => onUpdateMany({
|
|
290
|
-
...pathParams,
|
|
291
|
-
...lastRequest[0],
|
|
292
|
-
requestBody: {
|
|
293
|
-
updateValues: _.pickBy(
|
|
294
|
-
// @ts-ignore
|
|
295
|
-
entityToUpdateDto({
|
|
296
|
-
...pathParams,
|
|
297
|
-
...values,
|
|
298
|
-
}),
|
|
299
|
-
(value, key) => _.has(values, key),
|
|
300
|
-
),
|
|
301
|
-
records: allSelected ? [] : selectedRecords,
|
|
302
|
-
},
|
|
303
|
-
}).then(() => {
|
|
304
|
-
messageApi.open({
|
|
305
|
-
type: 'success',
|
|
306
|
-
content: 'Operation Successful',
|
|
307
|
-
});
|
|
308
|
-
actionRef?.current?.reload();
|
|
309
|
-
})}
|
|
310
|
-
/>
|
|
311
|
-
)
|
|
312
|
-
: <></>,
|
|
190
|
+
? bulkEditButton
|
|
191
|
+
: null,
|
|
313
192
|
!viewOnly && onDeleteMany
|
|
314
|
-
?
|
|
315
|
-
|
|
316
|
-
selectedRecords={selectedRecords}
|
|
317
|
-
lastRequest={lastRequest}
|
|
318
|
-
allSelected={allSelected}
|
|
319
|
-
// @ts-ignore
|
|
320
|
-
onDelete={() => onDeleteMany({
|
|
321
|
-
...pathParams,
|
|
322
|
-
...lastRequest[0],
|
|
323
|
-
requestBody: {
|
|
324
|
-
records: allSelected ? [] : selectedRecords,
|
|
325
|
-
},
|
|
326
|
-
}).then(() => {
|
|
327
|
-
messageApi.open({
|
|
328
|
-
type: 'success',
|
|
329
|
-
content: 'Operation Successful',
|
|
330
|
-
});
|
|
331
|
-
actionRef?.current?.reload();
|
|
332
|
-
})}
|
|
333
|
-
/>
|
|
334
|
-
)
|
|
335
|
-
: <></>,
|
|
193
|
+
? bulkDeleteButton
|
|
194
|
+
: null,
|
|
336
195
|
!viewOnly && createButton || null,
|
|
337
196
|
...toolBarRender && toolBarRender(...args) || [],
|
|
338
197
|
]}
|
|
@@ -343,100 +202,37 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
343
202
|
{
|
|
344
203
|
...(
|
|
345
204
|
!viewOnly && (onUpdateMany || onDeleteMany)
|
|
346
|
-
? {
|
|
347
|
-
rowSelection: {
|
|
348
|
-
selectedRowKeys: selectedRecords.map(record => Array.isArray(idColumnName) ? idColumnName.map(colName => record[colName]).join('-') : record[idColumnName]),
|
|
349
|
-
selections: [
|
|
350
|
-
{
|
|
351
|
-
key: 'all',
|
|
352
|
-
text: (
|
|
353
|
-
<Space>
|
|
354
|
-
Select ALL
|
|
355
|
-
<Popover
|
|
356
|
-
content={(
|
|
357
|
-
<div style={{ width: '100%' }}>
|
|
358
|
-
This includes records from ALL pages of the table.
|
|
359
|
-
</div>
|
|
360
|
-
)}
|
|
361
|
-
title={'Select All'}
|
|
362
|
-
trigger={['hover', 'click']}
|
|
363
|
-
zIndex={1080}
|
|
364
|
-
>
|
|
365
|
-
<QuestionCircleTwoTone />
|
|
366
|
-
</Popover>
|
|
367
|
-
</Space>
|
|
368
|
-
),
|
|
369
|
-
onSelect: () => {
|
|
370
|
-
setSelectedRecords(lastRequest[1].data);
|
|
371
|
-
setAllSelected(true);
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
onChange: (rowKeys, records) => {
|
|
376
|
-
setSelectedRecords(records);
|
|
377
|
-
allSelected && setAllSelected(false);
|
|
378
|
-
},
|
|
379
|
-
}
|
|
380
|
-
}
|
|
205
|
+
? { rowSelection }
|
|
381
206
|
: {}
|
|
382
207
|
)
|
|
383
208
|
}
|
|
384
209
|
{...rest}
|
|
385
210
|
/>
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
onSubmit={async (data) => {
|
|
390
|
-
try {
|
|
391
|
-
await onCreate?.({
|
|
392
|
-
...pathParams,
|
|
393
|
-
requestBody: entityToCreateDto({
|
|
394
|
-
...pathParams,
|
|
395
|
-
...data,
|
|
396
|
-
})
|
|
397
|
-
});
|
|
398
|
-
actionRef?.current?.reload();
|
|
399
|
-
setCreatePopupData(undefined);
|
|
400
|
-
}
|
|
401
|
-
catch (e) {
|
|
402
|
-
console.error(e);
|
|
403
|
-
}
|
|
404
|
-
}}
|
|
405
|
-
idColumnName={idColumnName}
|
|
406
|
-
columns={columns ?? []}
|
|
407
|
-
viewMode={descriptionsModalViewMode}
|
|
408
|
-
extra={
|
|
409
|
-
<ContentViewModeButton
|
|
410
|
-
contentViewMode={descriptionsModalViewMode}
|
|
411
|
-
setContentViewMode={setDescriptionsModalViewMode}
|
|
412
|
-
/>
|
|
413
|
-
}
|
|
414
|
-
/>
|
|
211
|
+
|
|
212
|
+
{creationModal}
|
|
213
|
+
|
|
415
214
|
<Modal
|
|
416
215
|
title={editPopupTitle}
|
|
417
216
|
open={updatePopupData !== undefined}
|
|
418
217
|
width='80%'
|
|
218
|
+
closeIcon={true}
|
|
419
219
|
footer={null}
|
|
420
|
-
|
|
421
|
-
|
|
220
|
+
onCancel={() => {
|
|
221
|
+
actionRef?.current?.reload();
|
|
222
|
+
setUpdatePopupData(undefined);
|
|
223
|
+
}}
|
|
422
224
|
>
|
|
423
|
-
<Descriptions<Entity>
|
|
424
|
-
mainTitle=
|
|
425
|
-
columns={columns}
|
|
426
|
-
canEdit={true}
|
|
225
|
+
<Descriptions<Entity, CreateDto, UpdateDto, TPathParams>
|
|
226
|
+
mainTitle={descriptionsMainTitle}
|
|
227
|
+
columns={columns ?? []}
|
|
427
228
|
entity={updatePopupData}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
contentViewMode={descriptionsModalViewMode}
|
|
432
|
-
setContentViewMode={setDescriptionsModalViewMode}
|
|
433
|
-
/>
|
|
434
|
-
}
|
|
229
|
+
canEdit={true}
|
|
230
|
+
onUpdate={onUpdate}
|
|
231
|
+
entityToUpdateDto={entityToUpdateDto}
|
|
435
232
|
/>
|
|
436
233
|
</Modal>
|
|
437
|
-
{
|
|
234
|
+
{messagesContext}
|
|
438
235
|
</>);
|
|
439
236
|
};
|
|
440
237
|
|
|
441
238
|
export default Table;
|
|
442
|
-
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { TFilterParams, TFilters, TGetAllParams, TSearchableColumn, TSort } from "./tableTypes";
|
|
2
|
+
import { applyKeywordToSearch, buildJoinFields, collectFieldsFromColumns, getFiltersSearch } from "./tableTools";
|
|
3
|
+
import { QuerySortArr } from "@nestjsx/crud-request";
|
|
4
|
+
import { ProColumns } from "@ant-design/pro-components";
|
|
5
|
+
import { ColumnStateType } from "@ant-design/pro-table/es/typing";
|
|
6
|
+
|
|
7
|
+
export function getTableDataQueryParams<Entity, TPathParams extends Record<string, string | number> = {}>({
|
|
8
|
+
params,
|
|
9
|
+
sort = {},
|
|
10
|
+
filters = {},
|
|
11
|
+
pathParams,
|
|
12
|
+
defaultSort,
|
|
13
|
+
searchableColumns,
|
|
14
|
+
columns = [],
|
|
15
|
+
idColumnName = 'id',
|
|
16
|
+
columnsState,
|
|
17
|
+
}: {
|
|
18
|
+
params: TFilterParams,
|
|
19
|
+
sort?: TSort,
|
|
20
|
+
filters?: TFilters,
|
|
21
|
+
pathParams: TPathParams,
|
|
22
|
+
defaultSort?: QuerySortArr,
|
|
23
|
+
searchableColumns?: TSearchableColumn[],
|
|
24
|
+
columns?: ProColumns<Entity>[],
|
|
25
|
+
idColumnName?: string | string[];
|
|
26
|
+
columnsState?: ColumnStateType;
|
|
27
|
+
}): TGetAllParams & TPathParams {
|
|
28
|
+
const {
|
|
29
|
+
current,
|
|
30
|
+
pageSize,
|
|
31
|
+
keyword,
|
|
32
|
+
baseFilters,
|
|
33
|
+
join,
|
|
34
|
+
sortMap,
|
|
35
|
+
...filtersFromSearchForm
|
|
36
|
+
} = params;
|
|
37
|
+
|
|
38
|
+
const queryParams: TGetAllParams & TPathParams = {
|
|
39
|
+
...pathParams,
|
|
40
|
+
page: current,
|
|
41
|
+
limit: pageSize,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const sortBy = Object
|
|
45
|
+
.entries(sort)
|
|
46
|
+
.reduce<string[]>(
|
|
47
|
+
(data: string[], [key, direction]) => {
|
|
48
|
+
data.push(`${sortMap?.[key] || key},${direction === 'ascend' ? 'ASC' : 'DESC'}`);
|
|
49
|
+
return data;
|
|
50
|
+
},
|
|
51
|
+
[]
|
|
52
|
+
);
|
|
53
|
+
if (!sortBy.length && defaultSort) {
|
|
54
|
+
sortBy.push(defaultSort.join(','));
|
|
55
|
+
}
|
|
56
|
+
queryParams.sort = sortBy;
|
|
57
|
+
|
|
58
|
+
let search = getFiltersSearch({
|
|
59
|
+
baseFilters,
|
|
60
|
+
filters: {
|
|
61
|
+
...filters,
|
|
62
|
+
...filtersFromSearchForm,
|
|
63
|
+
},
|
|
64
|
+
searchableColumns,
|
|
65
|
+
});
|
|
66
|
+
search = applyKeywordToSearch(search, searchableColumns!, columnsState.value!, keyword);
|
|
67
|
+
queryParams.s = JSON.stringify(search);
|
|
68
|
+
|
|
69
|
+
const { joinSelect, joinFields } = buildJoinFields(join);
|
|
70
|
+
queryParams.join = joinSelect;
|
|
71
|
+
|
|
72
|
+
queryParams.fields = columns && collectFieldsFromColumns(
|
|
73
|
+
columns,
|
|
74
|
+
idColumnName,
|
|
75
|
+
joinFields,
|
|
76
|
+
) || [];
|
|
77
|
+
|
|
78
|
+
return queryParams;
|
|
79
|
+
}
|
|
@@ -58,6 +58,22 @@ export function getFiltersSearch({
|
|
|
58
58
|
value = true;
|
|
59
59
|
}
|
|
60
60
|
break;
|
|
61
|
+
|
|
62
|
+
case Operators.in:
|
|
63
|
+
case Operators.inLow:
|
|
64
|
+
if (Array.isArray(value) && value.length === 1 && value[0] === null) {
|
|
65
|
+
operator = Operators.isNull;
|
|
66
|
+
value = true;
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case Operators.equals:
|
|
71
|
+
if (Array.isArray(value) && value.length === 1 && value[0] === null || value === null) {
|
|
72
|
+
operator = Operators.isNull;
|
|
73
|
+
value = true;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
search.$and?.push({ [field]: { [operator]: value } });
|
|
@@ -169,11 +185,12 @@ export function buildFieldsFromColumns<T>(
|
|
|
169
185
|
// skip id column because it is always included by backend
|
|
170
186
|
// and join fields because they are included by join
|
|
171
187
|
|
|
172
|
-
|
|
188
|
+
const dataIndex = String(Array.isArray(col.dataIndex) ? col.dataIndex[0] : col.dataIndex);
|
|
189
|
+
if (!dataIndex || (Array.isArray(idColumnName) ? idColumnName.includes(dataIndex) : dataIndex === idColumnName) || joinFields.has(dataIndex)) {
|
|
173
190
|
return;
|
|
174
191
|
}
|
|
175
192
|
|
|
176
|
-
fields.add(
|
|
193
|
+
fields.add(dataIndex);
|
|
177
194
|
});
|
|
178
195
|
|
|
179
196
|
return fields;
|
|
@@ -5,6 +5,7 @@ import { Operators } from "./tableTools";
|
|
|
5
5
|
import { TColumnsSet } from "./useColumnsSets";
|
|
6
6
|
import { ColumnStateType } from "@ant-design/pro-table/es/typing";
|
|
7
7
|
import { RowEditableConfig } from "@ant-design/pro-utils";
|
|
8
|
+
import { ProColumns } from "@ant-design/pro-components";
|
|
8
9
|
|
|
9
10
|
export type IWithId = {
|
|
10
11
|
id: string | number,
|
|
@@ -89,9 +90,6 @@ interface BaseProps<Entity,
|
|
|
89
90
|
idColumnName?: string & keyof Entity | (string & keyof Entity)[];
|
|
90
91
|
createNewDefaultParams?: Partial<Entity>;
|
|
91
92
|
editableRecord?: Partial<Entity>;
|
|
92
|
-
afterSave?: (record: Entity) => Promise<void>;
|
|
93
|
-
actionRef?: MutableRefObject<ActionType | undefined>;
|
|
94
|
-
editable?: RowEditableConfig<Entity>;
|
|
95
93
|
defaultSort?: QuerySortArr;
|
|
96
94
|
searchableColumns?: TSearchableColumn[];
|
|
97
95
|
viewOnly?: boolean;
|
|
@@ -99,18 +97,25 @@ interface BaseProps<Entity,
|
|
|
99
97
|
popupCreation?: boolean;
|
|
100
98
|
columnsState?: ColumnStateType;
|
|
101
99
|
columnsSetSelect?: () => React.ReactNode;
|
|
102
|
-
popupDataState?: [Partial<Entity>, React.Dispatch<React.SetStateAction<Partial<Entity>>>]
|
|
103
100
|
editPopupTitle?: string;
|
|
101
|
+
createPopupTitle?: string;
|
|
102
|
+
descriptionsMainTitle?: ProColumns<Entity>['title'] | null;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
interface EditableProps<Entity, CreateDto, UpdateDto, TPathParams = {}> {
|
|
105
|
+
export interface EditableProps<Entity, CreateDto, UpdateDto, TPathParams = {}> {
|
|
106
|
+
actionRef?: MutableRefObject<ActionType | undefined>;
|
|
107
|
+
editable?: RowEditableConfig<Entity>;
|
|
108
|
+
afterSave?: (record: Entity) => Promise<void>;
|
|
107
109
|
onCreate?: ({}: { requestBody: CreateDto } & TPathParams) => Promise<Entity>;
|
|
108
|
-
onUpdate: ({}:
|
|
109
|
-
|
|
110
|
+
onUpdate: ({}: Partial<Entity> & {
|
|
111
|
+
requestBody: UpdateDto,
|
|
112
|
+
index?: number,
|
|
113
|
+
} & TPathParams) => Promise<Entity>;
|
|
114
|
+
onDelete: ({}: Partial<Entity> & TPathParams) => Promise<void>;
|
|
110
115
|
entityToCreateDto: (entity: Entity) => CreateDto;
|
|
111
116
|
entityToUpdateDto: (entity: Entity) => UpdateDto;
|
|
112
|
-
onUpdateMany: ({}:
|
|
113
|
-
onDeleteMany: ({}:
|
|
117
|
+
onUpdateMany: ({}: Partial<Entity> & { requestBody: { updateValues: Partial<UpdateDto>[], records: Entity[] } } & TPathParams) => Promise<void>,
|
|
118
|
+
onDeleteMany: ({}: Partial<Entity> & { requestBody: { records: Entity[] } } & TPathParams) => Promise<void>,
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
// Conditional type to merge base and editable props conditionally
|