@boarteam/boar-pack-common-frontend 2.4.1 → 2.5.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 +3 -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,128 @@
|
|
|
1
|
+
import { message, Popover, Space } from "antd";
|
|
2
|
+
import { QuestionCircleTwoTone } from "@ant-design/icons";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import BulkEditButton from "./BulkEditButton";
|
|
5
|
+
import { ActionType, ProTableProps } from "@ant-design/pro-table";
|
|
6
|
+
import BulkDeleteButton from "./BulkDeleteButton";
|
|
7
|
+
import { MutableRefObject, useState } from "react";
|
|
8
|
+
import { TGetAllParams } from "./tableTypes";
|
|
9
|
+
|
|
10
|
+
export function useBulkEditing<Entity, TPathParams, UpdateDto, U>({
|
|
11
|
+
actionRef,
|
|
12
|
+
columns,
|
|
13
|
+
idColumnName,
|
|
14
|
+
onDeleteMany,
|
|
15
|
+
onUpdateMany,
|
|
16
|
+
entityToUpdateDto,
|
|
17
|
+
pathParams,
|
|
18
|
+
}: {
|
|
19
|
+
actionRef?: MutableRefObject<ActionType | undefined>;
|
|
20
|
+
columns: ProTableProps<Entity, U>['columns'],
|
|
21
|
+
idColumnName?: string & keyof Entity | (string & keyof Entity)[];
|
|
22
|
+
onDeleteMany: ({}: Partial<Entity> & {
|
|
23
|
+
requestBody: { records: Entity[] }
|
|
24
|
+
} & TPathParams) => Promise<void>,
|
|
25
|
+
onUpdateMany: ({}: Partial<Entity> & {
|
|
26
|
+
requestBody: { updateValues: Partial<UpdateDto>[], records: Entity[] }
|
|
27
|
+
} & TPathParams) => Promise<void>,
|
|
28
|
+
entityToUpdateDto: (entity: Entity) => UpdateDto;
|
|
29
|
+
pathParams: TPathParams,
|
|
30
|
+
}) {
|
|
31
|
+
const [allSelected, setAllSelected] = useState(false);
|
|
32
|
+
const [selectedRecords, setSelectedRecords] = useState<Entity[]>([]);
|
|
33
|
+
const [lastRequest, setLastRequest] = useState<[TGetAllParams & TPathParams, any] | []>([]);
|
|
34
|
+
const [messageApi, contextHolder] = message.useMessage();
|
|
35
|
+
|
|
36
|
+
const bulkEditButton = <BulkEditButton<Entity, TPathParams>
|
|
37
|
+
selectedRecords={selectedRecords}
|
|
38
|
+
lastRequest={lastRequest}
|
|
39
|
+
allSelected={allSelected}
|
|
40
|
+
columns={columns}
|
|
41
|
+
idColumnName={idColumnName}
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
onSubmit={values => onUpdateMany({
|
|
44
|
+
...pathParams,
|
|
45
|
+
...lastRequest[0],
|
|
46
|
+
requestBody: {
|
|
47
|
+
updateValues: _.pickBy(
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
entityToUpdateDto({
|
|
50
|
+
...pathParams,
|
|
51
|
+
...values,
|
|
52
|
+
}),
|
|
53
|
+
(value, key) => _.has(values, key),
|
|
54
|
+
),
|
|
55
|
+
records: allSelected ? [] : selectedRecords,
|
|
56
|
+
},
|
|
57
|
+
}).then(() => {
|
|
58
|
+
messageApi.open({
|
|
59
|
+
type: 'success',
|
|
60
|
+
content: 'Operation Successful',
|
|
61
|
+
});
|
|
62
|
+
actionRef?.current?.reload();
|
|
63
|
+
})}
|
|
64
|
+
/>;
|
|
65
|
+
|
|
66
|
+
const bulkDeleteButton = <BulkDeleteButton<Entity, TPathParams>
|
|
67
|
+
selectedRecords={selectedRecords}
|
|
68
|
+
lastRequest={lastRequest}
|
|
69
|
+
allSelected={allSelected}
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
onDelete={() => onDeleteMany({
|
|
72
|
+
...pathParams,
|
|
73
|
+
...lastRequest[0],
|
|
74
|
+
requestBody: {
|
|
75
|
+
records: allSelected ? [] : selectedRecords,
|
|
76
|
+
},
|
|
77
|
+
}).then(() => {
|
|
78
|
+
messageApi.open({
|
|
79
|
+
type: 'success',
|
|
80
|
+
content: 'Operation Successful',
|
|
81
|
+
});
|
|
82
|
+
actionRef?.current?.reload();
|
|
83
|
+
})}
|
|
84
|
+
/>;
|
|
85
|
+
|
|
86
|
+
const rowSelection: ProTableProps<Entity, U>['rowSelection'] = {
|
|
87
|
+
selectedRowKeys: selectedRecords.map(record => Array.isArray(idColumnName) ? idColumnName.map(colName => record[colName]).join('-') : record[idColumnName]),
|
|
88
|
+
selections: [
|
|
89
|
+
{
|
|
90
|
+
key: 'all',
|
|
91
|
+
text: (
|
|
92
|
+
<Space>
|
|
93
|
+
Select ALL
|
|
94
|
+
<Popover
|
|
95
|
+
content={(
|
|
96
|
+
<div style={{ width: '100%' }}>
|
|
97
|
+
This includes records from ALL pages of the table.
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
title={'Select All'}
|
|
101
|
+
trigger={['hover', 'click']}
|
|
102
|
+
zIndex={1080}
|
|
103
|
+
>
|
|
104
|
+
<QuestionCircleTwoTone />
|
|
105
|
+
</Popover>
|
|
106
|
+
</Space>
|
|
107
|
+
),
|
|
108
|
+
onSelect: () => {
|
|
109
|
+
setSelectedRecords(lastRequest[1].data);
|
|
110
|
+
setAllSelected(true);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
onChange: (rowKeys, records) => {
|
|
115
|
+
setSelectedRecords(records);
|
|
116
|
+
allSelected && setAllSelected(false);
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
rowSelection,
|
|
122
|
+
setSelectedRecords,
|
|
123
|
+
setLastRequest,
|
|
124
|
+
messagesContext: contextHolder,
|
|
125
|
+
bulkEditButton,
|
|
126
|
+
bulkDeleteButton,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { MutableRefObject, useState } from 'react';
|
|
2
|
+
import { Button } from 'antd';
|
|
3
|
+
import { ActionType } from "@ant-design/pro-table";
|
|
4
|
+
import { PlusOutlined } from "@ant-design/icons";
|
|
5
|
+
import { FormattedMessage } from "react-intl";
|
|
6
|
+
import type { SizeType } from "antd/es/config-provider/SizeContext";
|
|
7
|
+
import { CreateEntityModal, CreateEntityModalProps } from "./CreateEntityModal";
|
|
8
|
+
|
|
9
|
+
let creatingRecordsCount = 0;
|
|
10
|
+
export const KEY_SYMBOL = Symbol('key');
|
|
11
|
+
const NEW_RECORD = 'NEW_RECORD';
|
|
12
|
+
|
|
13
|
+
export function getNewId(): string {
|
|
14
|
+
return NEW_RECORD + creatingRecordsCount++;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isRecordNew(record: Record<string | symbol, any>): boolean {
|
|
18
|
+
return record[KEY_SYMBOL]?.startsWith?.(NEW_RECORD) || record.id?.startsWith?.(NEW_RECORD) || false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useCreation<Entity, CreateDto, TPathParams = {}>({
|
|
22
|
+
title,
|
|
23
|
+
mainTitle,
|
|
24
|
+
columns,
|
|
25
|
+
idColumnName,
|
|
26
|
+
onCreate,
|
|
27
|
+
pathParams,
|
|
28
|
+
entityToCreateDto,
|
|
29
|
+
actionRef,
|
|
30
|
+
createButtonSize,
|
|
31
|
+
popupCreation,
|
|
32
|
+
createNewDefaultParams,
|
|
33
|
+
}: {
|
|
34
|
+
actionRef?: MutableRefObject<ActionType | undefined>;
|
|
35
|
+
pathParams: TPathParams;
|
|
36
|
+
entityToCreateDto: (entity: Partial<Entity>) => CreateDto;
|
|
37
|
+
onCreate?: ({}: { requestBody: CreateDto } & TPathParams) => Promise<Entity>;
|
|
38
|
+
createButtonSize: SizeType;
|
|
39
|
+
popupCreation?: boolean;
|
|
40
|
+
createNewDefaultParams?: Partial<Entity>;
|
|
41
|
+
} & Omit<CreateEntityModalProps<Entity>, 'onSubmit' | 'onCancel' | 'entity'>) {
|
|
42
|
+
const [createPopupData, setCreatePopupData] = useState<Partial<Entity> | undefined>();
|
|
43
|
+
|
|
44
|
+
const onCreateSubmit = async (data: Partial<Entity>) => {
|
|
45
|
+
try {
|
|
46
|
+
await onCreate?.({
|
|
47
|
+
...pathParams,
|
|
48
|
+
requestBody: entityToCreateDto({
|
|
49
|
+
...pathParams,
|
|
50
|
+
...data,
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
setCreatePopupData(undefined);
|
|
54
|
+
await actionRef?.current?.reload();
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const createButton = <Button
|
|
61
|
+
size={createButtonSize}
|
|
62
|
+
type="primary"
|
|
63
|
+
key="create"
|
|
64
|
+
onClick={() => {
|
|
65
|
+
if (popupCreation) {
|
|
66
|
+
setCreatePopupData(createNewDefaultParams);
|
|
67
|
+
} else {
|
|
68
|
+
actionRef?.current?.addEditRecord({
|
|
69
|
+
[KEY_SYMBOL]: getNewId(),
|
|
70
|
+
...createNewDefaultParams,
|
|
71
|
+
}, {
|
|
72
|
+
position: 'top',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<PlusOutlined /> <FormattedMessage id={'table.newButton'} />
|
|
78
|
+
</Button>;
|
|
79
|
+
|
|
80
|
+
const modal = <CreateEntityModal<Entity, CreateDto, Entity, TPathParams>
|
|
81
|
+
entity={createPopupData}
|
|
82
|
+
title={title}
|
|
83
|
+
mainTitle={mainTitle}
|
|
84
|
+
columns={columns}
|
|
85
|
+
idColumnName={idColumnName}
|
|
86
|
+
onCancel={() => {
|
|
87
|
+
setCreatePopupData(undefined);
|
|
88
|
+
}}
|
|
89
|
+
onSubmit={onCreateSubmit}
|
|
90
|
+
/>;
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
creationModal: modal,
|
|
94
|
+
createButton,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { EditableProps } from "./tableTypes";
|
|
2
|
+
import type { RowEditableConfig } from "@ant-design/pro-utils";
|
|
3
|
+
import { flushSync } from "react-dom";
|
|
4
|
+
import { Button, Tooltip } from "antd";
|
|
5
|
+
import { DeleteOutlined, StopOutlined } from "@ant-design/icons";
|
|
6
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
7
|
+
import React, { useState } from "react";
|
|
8
|
+
|
|
9
|
+
export function useEditableTable<Entity, CreateDto, UpdateDto, TPathParams = {}>(
|
|
10
|
+
{
|
|
11
|
+
actionRef,
|
|
12
|
+
pathParams,
|
|
13
|
+
onCreate,
|
|
14
|
+
onUpdate,
|
|
15
|
+
onDelete,
|
|
16
|
+
entityToCreateDto,
|
|
17
|
+
entityToUpdateDto,
|
|
18
|
+
afterSave,
|
|
19
|
+
editable,
|
|
20
|
+
}: {
|
|
21
|
+
pathParams: TPathParams,
|
|
22
|
+
} & EditableProps<Entity, CreateDto, UpdateDto, TPathParams>
|
|
23
|
+
) {
|
|
24
|
+
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
|
|
25
|
+
const intl = useIntl();
|
|
26
|
+
|
|
27
|
+
const editableConfig: RowEditableConfig<Entity> = {
|
|
28
|
+
type: 'multiple',
|
|
29
|
+
editableKeys,
|
|
30
|
+
onChange: setEditableRowKeys,
|
|
31
|
+
async onSave(
|
|
32
|
+
id,
|
|
33
|
+
record,
|
|
34
|
+
origin,
|
|
35
|
+
newLine,
|
|
36
|
+
) {
|
|
37
|
+
if (newLine) {
|
|
38
|
+
await onCreate?.({
|
|
39
|
+
...pathParams,
|
|
40
|
+
requestBody: entityToCreateDto(record),
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
await onUpdate({
|
|
44
|
+
...pathParams,
|
|
45
|
+
...record,
|
|
46
|
+
requestBody: entityToUpdateDto({
|
|
47
|
+
...pathParams,
|
|
48
|
+
...record,
|
|
49
|
+
}),
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof afterSave === 'function') {
|
|
54
|
+
await afterSave(record);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
flushSync(() => {
|
|
58
|
+
actionRef?.current?.reload();
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
async onCancel(
|
|
62
|
+
id,
|
|
63
|
+
record,
|
|
64
|
+
origin,
|
|
65
|
+
) {
|
|
66
|
+
if (record) {
|
|
67
|
+
Object.assign(record, origin);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
async onDelete(id, row) {
|
|
71
|
+
await onDelete({ ...row, ...pathParams });
|
|
72
|
+
},
|
|
73
|
+
deletePopconfirmMessage: intl.formatMessage({ id: 'table.deletePopconfirmMessage' }),
|
|
74
|
+
onlyAddOneLineAlertMessage: intl.formatMessage({ id: 'table.onlyAddOneLineAlertMessage' }),
|
|
75
|
+
cancelText: <Tooltip title={intl.formatMessage({ id: 'table.cancelText' })}><StopOutlined /></Tooltip>,
|
|
76
|
+
deleteText: <Tooltip title={intl.formatMessage({ id: 'table.deleteText' })}><DeleteOutlined /></Tooltip>,
|
|
77
|
+
saveText: <Button size={"small"} type={"primary"}><FormattedMessage id={'table.saveText'} /></Button>,
|
|
78
|
+
...editable,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
editableConfig,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Button, Tooltip } from "antd";
|
|
2
|
-
import { AppstoreOutlined, UnorderedListOutlined } from "@ant-design/icons";
|
|
3
|
-
export enum VIEW_MODE_TYPE {
|
|
4
|
-
TABS = 'tabs',
|
|
5
|
-
GENERAL = 'general'
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface ContentViewModeButtonProps {
|
|
9
|
-
contentViewMode: VIEW_MODE_TYPE;
|
|
10
|
-
setContentViewMode: (mode: VIEW_MODE_TYPE) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const ContentViewModeButton: React.FC<ContentViewModeButtonProps> = ({ contentViewMode, setContentViewMode }) => {
|
|
14
|
-
return (
|
|
15
|
-
<Tooltip
|
|
16
|
-
title={contentViewMode === VIEW_MODE_TYPE.TABS ? 'Switch to general view' : 'Switch to tabs view'}
|
|
17
|
-
key="viewModeToggle">
|
|
18
|
-
<Button
|
|
19
|
-
type="text"
|
|
20
|
-
icon={contentViewMode === VIEW_MODE_TYPE.TABS ? <UnorderedListOutlined/> :
|
|
21
|
-
<AppstoreOutlined/>}
|
|
22
|
-
onClick={() => setContentViewMode(contentViewMode === VIEW_MODE_TYPE.TABS ? VIEW_MODE_TYPE.GENERAL : VIEW_MODE_TYPE.TABS)}
|
|
23
|
-
/>
|
|
24
|
-
</Tooltip>);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default ContentViewModeButton;
|