@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.
@@ -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 React, { useEffect, useRef, useState } from "react";
3
- import {Button, Popover, Space, Tooltip, message, Modal } from "antd";
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 DescriptionsCreateModal from "../Descriptions/DescriptionsCreateModal";
22
-
23
- let creatingRecordsCount = 0;
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 intl = useIntl();
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
- current,
125
- pageSize,
126
- keyword,
127
- baseFilters,
128
- join,
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
- joinFields,
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
- <BulkEditButton
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
- <BulkDeleteButton
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
- <DescriptionsCreateModal<Entity>
387
- data={createPopupData}
388
- onClose={() => setCreatePopupData(undefined)}
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
- closeIcon={false}
421
- onCancel={() => setUpdatePopupData(undefined)}
220
+ onCancel={() => {
221
+ actionRef?.current?.reload();
222
+ setUpdatePopupData(undefined);
223
+ }}
422
224
  >
423
- <Descriptions<Entity>
424
- mainTitle='Main'
425
- columns={columns}
426
- canEdit={true}
225
+ <Descriptions<Entity, CreateDto, UpdateDto, TPathParams>
226
+ mainTitle={descriptionsMainTitle}
227
+ columns={columns ?? []}
427
228
  entity={updatePopupData}
428
- viewMode={descriptionsModalViewMode}
429
- extra={
430
- <ContentViewModeButton
431
- contentViewMode={descriptionsModalViewMode}
432
- setContentViewMode={setDescriptionsModalViewMode}
433
- />
434
- }
229
+ canEdit={true}
230
+ onUpdate={onUpdate}
231
+ entityToUpdateDto={entityToUpdateDto}
435
232
  />
436
233
  </Modal>
437
- {contextHolder}
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
+ }
@@ -4,3 +4,4 @@ export * from './tableTools';
4
4
  export * from './tableTypes';
5
5
  export * from './useColumnsSets';
6
6
  export { default as useColumnsSets } from './useColumnsSets';
7
+ export * from "./useCreation";
@@ -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
- if (!col.dataIndex || (Array.isArray(idColumnName) ? idColumnName.includes(col.dataIndex) : col.dataIndex === idColumnName) || joinFields.has(col.dataIndex as string)) {
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(String(Array.isArray(col.dataIndex) ? col.dataIndex[0] : col.dataIndex));
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: ({}: Record<keyof Entity, string> & { requestBody: UpdateDto } & TPathParams) => Promise<Entity>;
109
- onDelete: ({}: Record<keyof Entity, string> & TPathParams) => Promise<void>;
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: ({}: Record<keyof Entity, string> & { requestBody: { updateValues: Partial<UpdateDto>[], records: Entity[] } } & TPathParams) => Promise<void>,
113
- onDeleteMany: ({}: Record<keyof Entity, string> & { requestBody: { records: Entity[] } } & TPathParams) => Promise<void>,
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