@boarteam/boar-pack-common-frontend 3.4.0 → 4.1.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.
Files changed (216) hide show
  1. package/dist/components/ChangesModal/ChangesModal.d.ts +37 -0
  2. package/dist/components/ChangesModal/ChangesModal.js +143 -0
  3. package/dist/components/ChangesModal/ChangesModal.js.map +1 -0
  4. package/dist/components/ChangesModal/ChangesTab.d.ts +7 -0
  5. package/dist/components/ChangesModal/ChangesTab.js +29 -0
  6. package/dist/components/ChangesModal/ChangesTab.js.map +1 -0
  7. package/dist/components/ChangesModal/ConflictsTab.d.ts +10 -0
  8. package/dist/components/ChangesModal/ConflictsTab.js +105 -0
  9. package/dist/components/ChangesModal/ConflictsTab.js.map +1 -0
  10. package/dist/components/ChangesModal/ErrorsTab.d.ts +5 -0
  11. package/dist/components/ChangesModal/ErrorsTab.js +27 -0
  12. package/dist/components/ChangesModal/ErrorsTab.js.map +1 -0
  13. package/dist/components/ChangesModal/NewRecordsTab.d.ts +12 -0
  14. package/dist/components/ChangesModal/NewRecordsTab.js +24 -0
  15. package/dist/components/ChangesModal/NewRecordsTab.js.map +1 -0
  16. package/dist/components/ChangesModal/ResultsTab.d.ts +8 -0
  17. package/dist/components/ChangesModal/ResultsTab.js +12 -0
  18. package/dist/components/ChangesModal/ResultsTab.js.map +1 -0
  19. package/dist/components/ChangesModal/index.d.ts +1 -0
  20. package/dist/components/ChangesModal/index.js +5 -0
  21. package/dist/components/ChangesModal/index.js.map +1 -0
  22. package/dist/components/Comment/Comment.d.ts +12 -0
  23. package/dist/components/Comment/Comment.js +25 -0
  24. package/dist/components/Comment/Comment.js.map +1 -0
  25. package/dist/components/Comment/CommentAvatar.d.ts +7 -0
  26. package/dist/components/Comment/CommentAvatar.js +22 -0
  27. package/dist/components/Comment/CommentAvatar.js.map +1 -0
  28. package/dist/components/Comment/CommentForm.d.ts +6 -0
  29. package/dist/components/Comment/CommentForm.js +17 -0
  30. package/dist/components/Comment/CommentForm.js.map +1 -0
  31. package/dist/components/Comment/CommentFormModal.d.ts +10 -0
  32. package/dist/components/Comment/CommentFormModal.js +13 -0
  33. package/dist/components/Comment/CommentFormModal.js.map +1 -0
  34. package/dist/components/Comment/index.js +11 -0
  35. package/dist/components/Comment/index.js.map +1 -0
  36. package/dist/components/Descriptions/Descriptions.d.ts +4 -0
  37. package/dist/components/Descriptions/Descriptions.js +218 -0
  38. package/dist/components/Descriptions/Descriptions.js.map +1 -0
  39. package/dist/components/Descriptions/DescriptionsCreateModal.d.ts +3 -0
  40. package/dist/components/Descriptions/DescriptionsCreateModal.js +30 -0
  41. package/dist/components/Descriptions/DescriptionsCreateModal.js.map +1 -0
  42. package/dist/components/Descriptions/descriptionTypes.d.ts +68 -0
  43. package/dist/components/Descriptions/descriptionTypes.js +9 -0
  44. package/dist/components/Descriptions/descriptionTypes.js.map +1 -0
  45. package/dist/components/Descriptions/index.js +11 -0
  46. package/dist/components/Descriptions/index.js.map +1 -0
  47. package/dist/components/Descriptions/useContentViewMode.d.ts +10 -0
  48. package/dist/components/Descriptions/useContentViewMode.js +19 -0
  49. package/dist/components/Descriptions/useContentViewMode.js.map +1 -0
  50. package/dist/components/Descriptions/useDescriptionColumns.d.ts +8 -0
  51. package/dist/components/Descriptions/useDescriptionColumns.js +27 -0
  52. package/dist/components/Descriptions/useDescriptionColumns.js.map +1 -0
  53. package/dist/components/Inputs/DateRange.d.ts +8 -0
  54. package/dist/components/Inputs/DateRange.js +61 -0
  55. package/dist/components/Inputs/DateRange.js.map +1 -0
  56. package/dist/components/Inputs/MultiStringSelect.d.ts +5 -0
  57. package/dist/components/Inputs/MultiStringSelect.js +10 -0
  58. package/dist/components/Inputs/MultiStringSelect.js.map +1 -0
  59. package/dist/components/Inputs/NumberInputHandlingNewRecord.d.ts +3 -0
  60. package/dist/components/Inputs/NumberInputHandlingNewRecord.js +11 -0
  61. package/dist/components/Inputs/NumberInputHandlingNewRecord.js.map +1 -0
  62. package/dist/components/Inputs/NumberSwitcher.d.ts +8 -0
  63. package/dist/components/Inputs/NumberSwitcher.js +13 -0
  64. package/dist/components/Inputs/NumberSwitcher.js.map +1 -0
  65. package/dist/components/Inputs/Password.d.ts +3 -0
  66. package/dist/components/Inputs/Password.js +18 -0
  67. package/dist/components/Inputs/Password.js.map +1 -0
  68. package/dist/components/Inputs/RelationSelect.d.ts +21 -0
  69. package/dist/components/Inputs/RelationSelect.js +58 -0
  70. package/dist/components/Inputs/RelationSelect.js.map +1 -0
  71. package/dist/components/Inputs/SearchSelect.d.ts +12 -0
  72. package/dist/components/Inputs/SearchSelect.js +40 -0
  73. package/dist/components/Inputs/SearchSelect.js.map +1 -0
  74. package/dist/components/Inputs/filterDropdowns.d.ts +11 -0
  75. package/dist/components/Inputs/filterDropdowns.js +56 -0
  76. package/dist/components/Inputs/filterDropdowns.js.map +1 -0
  77. package/dist/components/Inputs/index.js +13 -0
  78. package/dist/components/Inputs/index.js.map +1 -0
  79. package/dist/components/Inputs/useCheckConnection.d.ts +13 -0
  80. package/dist/components/Inputs/useCheckConnection.js +53 -0
  81. package/dist/components/Inputs/useCheckConnection.js.map +1 -0
  82. package/dist/components/List/List.d.ts +3 -0
  83. package/dist/components/List/List.js +144 -0
  84. package/dist/components/List/List.js.map +1 -0
  85. package/dist/components/List/index.js +9 -0
  86. package/dist/components/List/index.js.map +1 -0
  87. package/dist/components/List/listTypes.d.ts +30 -0
  88. package/dist/components/List/listTypes.js +3 -0
  89. package/dist/components/List/listTypes.js.map +1 -0
  90. package/dist/components/QuestionMarkHint/QuestionMarkHint.d.ts +7 -0
  91. package/dist/components/QuestionMarkHint/QuestionMarkHint.js +16 -0
  92. package/dist/components/QuestionMarkHint/QuestionMarkHint.js.map +1 -0
  93. package/dist/components/QuestionMarkHint/index.js +5 -0
  94. package/dist/components/QuestionMarkHint/index.js.map +1 -0
  95. package/dist/components/Table/BulkDeleteButton.d.ts +8 -0
  96. package/dist/components/Table/BulkDeleteButton.js +27 -0
  97. package/dist/components/Table/BulkDeleteButton.js.map +1 -0
  98. package/dist/components/Table/BulkEditButton.d.ts +11 -0
  99. package/dist/components/Table/BulkEditButton.js +66 -0
  100. package/dist/components/Table/BulkEditButton.js.map +1 -0
  101. package/dist/components/Table/CreateEntityModal.d.ts +25 -0
  102. package/dist/components/Table/CreateEntityModal.js +23 -0
  103. package/dist/components/Table/CreateEntityModal.js.map +1 -0
  104. package/dist/components/Table/DeleteButton.d.ts +4 -0
  105. package/dist/components/Table/DeleteButton.js +28 -0
  106. package/dist/components/Table/DeleteButton.js.map +1 -0
  107. package/dist/components/Table/Table.d.ts +6 -0
  108. package/dist/components/Table/Table.js +154 -0
  109. package/dist/components/Table/Table.js.map +1 -0
  110. package/dist/components/Table/getTableDataQueryParams.d.ts +15 -0
  111. package/dist/components/Table/getTableDataQueryParams.js +37 -0
  112. package/dist/components/Table/getTableDataQueryParams.js.map +1 -0
  113. package/{src/components/Table/index.ts → dist/components/Table/index.d.ts} +1 -1
  114. package/dist/components/Table/index.js +14 -0
  115. package/dist/components/Table/index.js.map +1 -0
  116. package/dist/components/Table/tableTools.d.ts +38 -0
  117. package/dist/components/Table/tableTools.js +185 -0
  118. package/dist/components/Table/tableTools.js.map +1 -0
  119. package/dist/components/Table/tableTypes.d.ts +145 -0
  120. package/dist/components/Table/tableTypes.js +3 -0
  121. package/dist/components/Table/tableTypes.js.map +1 -0
  122. package/dist/components/Table/useBulkEditing.d.ts +30 -0
  123. package/dist/components/Table/useBulkEditing.js +79 -0
  124. package/dist/components/Table/useBulkEditing.js.map +1 -0
  125. package/dist/components/Table/useColumnsSets.d.ts +22 -0
  126. package/dist/components/Table/useColumnsSets.js +82 -0
  127. package/dist/components/Table/useColumnsSets.js.map +1 -0
  128. package/dist/components/Table/useCreation.d.ts +21 -0
  129. package/dist/components/Table/useCreation.js +75 -0
  130. package/dist/components/Table/useCreation.js.map +1 -0
  131. package/dist/components/Table/useEditableTable.d.ts +7 -0
  132. package/dist/components/Table/useEditableTable.js +63 -0
  133. package/dist/components/Table/useEditableTable.js.map +1 -0
  134. package/dist/components/Table/useImportExport.d.ts +35 -0
  135. package/dist/components/Table/useImportExport.js +207 -0
  136. package/dist/components/Table/useImportExport.js.map +1 -0
  137. package/dist/components/index.js +10 -0
  138. package/dist/components/index.js.map +1 -0
  139. package/dist/index.js +6 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/tools/ApiError.d.ts +16 -0
  142. package/dist/tools/ApiError.js +13 -0
  143. package/dist/tools/ApiError.js.map +1 -0
  144. package/dist/tools/WebsocketClient.d.ts +38 -0
  145. package/dist/tools/WebsocketClient.js +114 -0
  146. package/dist/tools/WebsocketClient.js.map +1 -0
  147. package/{src/tools/index.ts → dist/tools/index.d.ts} +1 -1
  148. package/dist/tools/index.js +10 -0
  149. package/dist/tools/index.js.map +1 -0
  150. package/dist/tools/numberTools.d.ts +1 -0
  151. package/dist/tools/numberTools.js +10 -0
  152. package/dist/tools/numberTools.js.map +1 -0
  153. package/dist/tools/safetyRun.d.ts +2 -0
  154. package/dist/tools/safetyRun.js +10 -0
  155. package/dist/tools/safetyRun.js.map +1 -0
  156. package/dist/tools/useFullscreen.d.ts +6 -0
  157. package/dist/tools/useFullscreen.js +56 -0
  158. package/dist/tools/useFullscreen.js.map +1 -0
  159. package/dist/tools/useTabs.d.ts +2 -0
  160. package/dist/tools/useTabs.js +18 -0
  161. package/dist/tools/useTabs.js.map +1 -0
  162. package/package.json +6 -4
  163. package/src/components/ChangesModal/ChangesModal.tsx +0 -262
  164. package/src/components/ChangesModal/ChangesTab.tsx +0 -51
  165. package/src/components/ChangesModal/ConflictsTab.tsx +0 -176
  166. package/src/components/ChangesModal/ErrorsTab.tsx +0 -49
  167. package/src/components/ChangesModal/NewRecordsTab.tsx +0 -48
  168. package/src/components/ChangesModal/ResultsTab.tsx +0 -28
  169. package/src/components/ChangesModal/index.ts +0 -1
  170. package/src/components/Comment/Comment.tsx +0 -53
  171. package/src/components/Comment/CommentAvatar.tsx +0 -34
  172. package/src/components/Comment/CommentForm.tsx +0 -36
  173. package/src/components/Comment/CommentFormModal.tsx +0 -31
  174. package/src/components/Descriptions/Descriptions.tsx +0 -351
  175. package/src/components/Descriptions/DescriptionsCreateModal.tsx +0 -65
  176. package/src/components/Descriptions/descriptionTypes.ts +0 -74
  177. package/src/components/Descriptions/useContentViewMode.tsx +0 -28
  178. package/src/components/Descriptions/useDescriptionColumns.ts +0 -42
  179. package/src/components/Inputs/DateRange.tsx +0 -75
  180. package/src/components/Inputs/MultiStringSelect.tsx +0 -20
  181. package/src/components/Inputs/NumberInputHandlingNewRecord.tsx +0 -11
  182. package/src/components/Inputs/NumberSwitcher.tsx +0 -27
  183. package/src/components/Inputs/Password.tsx +0 -33
  184. package/src/components/Inputs/RelationSelect.tsx +0 -108
  185. package/src/components/Inputs/SearchSelect.tsx +0 -93
  186. package/src/components/Inputs/filterDropdowns.tsx +0 -141
  187. package/src/components/Inputs/useCheckConnection.tsx +0 -79
  188. package/src/components/List/List.tsx +0 -266
  189. package/src/components/List/listTypes.ts +0 -31
  190. package/src/components/QuestionMarkHint/QuestionMarkHint.tsx +0 -33
  191. package/src/components/Table/BulkDeleteButton.tsx +0 -55
  192. package/src/components/Table/BulkEditButton.tsx +0 -160
  193. package/src/components/Table/CreateEntityModal.tsx +0 -85
  194. package/src/components/Table/DeleteButton.tsx +0 -44
  195. package/src/components/Table/Table.tsx +0 -316
  196. package/src/components/Table/getTableDataQueryParams.ts +0 -79
  197. package/src/components/Table/tableTools.ts +0 -244
  198. package/src/components/Table/tableTypes.ts +0 -152
  199. package/src/components/Table/useBulkEditing.tsx +0 -128
  200. package/src/components/Table/useColumnsSets.tsx +0 -142
  201. package/src/components/Table/useCreation.tsx +0 -114
  202. package/src/components/Table/useEditableTable.tsx +0 -86
  203. package/src/components/Table/useImportExport.tsx +0 -312
  204. package/src/tools/ApiError.ts +0 -18
  205. package/src/tools/WebsocketClient.ts +0 -138
  206. package/src/tools/numberTools.ts +0 -6
  207. package/src/tools/safetyRun.ts +0 -5
  208. package/src/tools/useFullscreen.tsx +0 -62
  209. package/src/tools/useTabs.ts +0 -17
  210. /package/{src/components/Comment/index.ts → dist/components/Comment/index.d.ts} +0 -0
  211. /package/{src/components/Descriptions/index.ts → dist/components/Descriptions/index.d.ts} +0 -0
  212. /package/{src/components/Inputs/index.ts → dist/components/Inputs/index.d.ts} +0 -0
  213. /package/{src/components/List/index.ts → dist/components/List/index.d.ts} +0 -0
  214. /package/{src/components/QuestionMarkHint/index.ts → dist/components/QuestionMarkHint/index.d.ts} +0 -0
  215. /package/{src/components/index.ts → dist/components/index.d.ts} +0 -0
  216. /package/{src/index.ts → dist/index.d.ts} +0 -0
@@ -1,49 +0,0 @@
1
- import { List, Tag } from "antd";
2
- import { TServerErrorItem } from "./ChangesModal";
3
-
4
- const formatField = (field: string) => {
5
- const parts = field.split(".");
6
- if (parts[0] === "new" && parts.length >= 3) {
7
- const idx = Number(parts[1]);
8
- return `New #${isNaN(idx) ? parts[1] : idx + 1}: ${parts.slice(2).join(".")}`;
9
- }
10
- if (parts[0] === "updated" && parts.length >= 3) {
11
- const id = parts[1];
12
- return `Updated ${id}: ${parts.slice(2).join(".")}`;
13
- }
14
- return field;
15
- };
16
-
17
- const ErrorsTab = ({
18
- serverErrors,
19
- }: {
20
- serverErrors: TServerErrorItem[],
21
- }) => {
22
- if (serverErrors.length === 0) {
23
- return <div>No errors</div>;
24
- }
25
-
26
- return (
27
- [
28
- <p key='errors-header'>Please fix the following errors and repeat import</p>,
29
- <List
30
- key='errors-list'
31
- size="small"
32
- dataSource={serverErrors}
33
- renderItem={(err, idx) => (
34
- <List.Item key={idx}>
35
- <div style={{ display: "flex", flexDirection: "column", gap: 4, width: "100%" }}>
36
- {err.field && <div>
37
- <Tag color="error">{formatField(err.field)}</Tag>
38
- </div>}
39
- <div>{err.message}</div>
40
- {err.field && <div style={{ color: "#999" }}>({err.field})</div>}
41
- </div>
42
- </List.Item>
43
- )}
44
- />,
45
- ]
46
- );
47
- };
48
-
49
- export default ErrorsTab;
@@ -1,48 +0,0 @@
1
- import { ProColumns } from "@ant-design/pro-components";
2
- import { TDiffResult } from "../Table/useImportExport";
3
- import ProTable from "@ant-design/pro-table";
4
- import useColumnsSets, { TColumnsSet } from "../Table/useColumnsSets";
5
-
6
- export type TCreatedRecordsColumnsConfig<Entity> = {
7
- columnsSets?: TColumnsSet<Entity>[],
8
- columns: ProColumns<Entity>[],
9
- }
10
-
11
- function NewRecordsTab<Entity>({
12
- created,
13
- createdRecordsColumnsConfig,
14
- }: {
15
- created: TDiffResult<Entity>['created'],
16
- createdRecordsColumnsConfig: TCreatedRecordsColumnsConfig<Entity>,
17
- }) {
18
- const { columns, columnsSets } = createdRecordsColumnsConfig;
19
-
20
- const {
21
- columnsSetSelect,
22
- columnsState,
23
- } = useColumnsSets<Entity>({
24
- columns,
25
- columnsSets,
26
- });
27
-
28
- if (!created.length) {
29
- return <p>No new records found.</p>
30
- }
31
-
32
- return [
33
- <h3 key='new-records-header'>New Records (Local Comparing)</h3>,
34
- <ProTable<Entity>
35
- key='new-records-data'
36
- dataSource={created}
37
- columns={columns}
38
- columnsState={columnsState}
39
- toolBarRender={(...args) => [
40
- columnsSetSelect?.() || null,
41
- ]}
42
- rowKey={(record, index) => index}
43
- search={false}
44
- />,
45
- ];
46
- }
47
-
48
- export default NewRecordsTab;
@@ -1,28 +0,0 @@
1
- import { CheckCircleOutlined, SyncOutlined } from "@ant-design/icons";
2
-
3
- type TImportStatistic = {
4
- created: number;
5
- updated: number;
6
- }
7
-
8
- const ResultsTab = ({
9
- importStatistic
10
- }: {
11
- importStatistic: TImportStatistic
12
- }) => {
13
- return [
14
- <h3 key='results-header'>Import Results</h3>,
15
- <div key='results-data' style={{ display: "flex", gap: "16px", alignItems: "center" }}>
16
- <div style={{ display: "flex", alignItems: "center" }}>
17
- <CheckCircleOutlined style={{ color: "#52c41a", marginRight: "8px" }} />
18
- <span>Created: {importStatistic.created}</span>
19
- </div>
20
- <div style={{ display: "flex", alignItems: "center" }}>
21
- <SyncOutlined style={{ color: "#1890ff", marginRight: "8px" }} />
22
- <span>Updated: {importStatistic.updated}</span>
23
- </div>
24
- </div>
25
- ]
26
- }
27
-
28
- export default ResultsTab;
@@ -1 +0,0 @@
1
- export * from './ChangesModal';
@@ -1,53 +0,0 @@
1
- import dayjs from "dayjs";
2
- import { createStyles } from "antd-style";
3
- import CommentAvatar from "./CommentAvatar";
4
-
5
- export interface AuthorProps {
6
- id: string;
7
- name: string;
8
- }
9
-
10
- interface CommentProps {
11
- key: string;
12
- content: string;
13
- author: AuthorProps;
14
- date: string;
15
- }
16
-
17
- const useStyles = createStyles(() => {
18
- return {
19
- /**
20
- * Styles for the ant-descriptions component to show edit icon on hover
21
- */
22
- commentStyles: {
23
- display: 'flex',
24
- alignItems: 'flex-start',
25
- gap: '10px'
26
- }
27
- }
28
- })
29
-
30
- const Comment: React.FC<CommentProps> = ({
31
- content,
32
- author,
33
- date,
34
- ...rest
35
- }) => {
36
- const { styles } = useStyles();
37
-
38
- return (
39
- <div
40
- className={styles.commentStyles}
41
- {...rest}
42
- >
43
- <CommentAvatar author={author} />
44
- <div>
45
- <strong>{author.name}</strong>
46
- <p>{content}</p>
47
- <small style={{ color: '#888' }}>{dayjs(date).format('DD.MM.YYYY HH:mm')}</small>
48
- </div>
49
- </div>
50
- );
51
- };
52
-
53
- export default Comment;
@@ -1,34 +0,0 @@
1
- import { Avatar } from "antd";
2
- import { AuthorProps } from "./Comment";
3
- import React from "react";
4
-
5
- interface CommentProps {
6
- author: AuthorProps;
7
- }
8
-
9
- const ColorList = [
10
- '#f56a00',
11
- '#7265e6',
12
- '#ffbf00',
13
- '#00a2ae',
14
- '#b45d7e',
15
- '#ace665',
16
- '#6e3aaf',
17
- '#54ae00'
18
- ];
19
-
20
- const getColorByAuthor = (authorId: string) => {
21
- return ColorList[parseInt(authorId, 36) % ColorList.length]
22
- }
23
-
24
- const CommentAvatar: React.FC<CommentProps> = ({
25
- author,
26
- }) => {
27
- return (
28
- <Avatar style={{ backgroundColor: getColorByAuthor(author.id), verticalAlign: 'middle', flexShrink: 0 }} size="large">
29
- {author.name.slice(0, 1)}
30
- </Avatar>
31
- );
32
- };
33
-
34
- export default CommentAvatar;
@@ -1,36 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Form, Input, Button } from 'antd';
3
-
4
- interface CommentFormProps {
5
- onSubmit: (content: string) => void;
6
- }
7
-
8
- const CommentForm: React.FC<CommentFormProps> = ({ onSubmit }) => {
9
- const [content, setContent] = useState('');
10
-
11
- const handleSubmit = () => {
12
- if (content.trim()) {
13
- onSubmit(content);
14
- setContent('');
15
- }
16
- };
17
-
18
- return (
19
- <Form layout="vertical" onFinish={handleSubmit}>
20
- <div>
21
- <Form.Item label="Leave a comment:">
22
- <Input.TextArea
23
- value={content}
24
- onChange={(e) => setContent(e.target.value)}
25
- autoSize={{ minRows: 3, maxRows: 6 }}
26
- />
27
- </Form.Item>
28
- </div>
29
- <Button type="primary" htmlType="submit" style={{ width: 100 }}>
30
- Send
31
- </Button>
32
- </Form>
33
- );
34
- };
35
-
36
- export default CommentForm;
@@ -1,31 +0,0 @@
1
- import React from 'react';
2
- import { Modal } from 'antd';
3
- import CommentForm from "./CommentForm";
4
-
5
- interface CommentFormProps {
6
- isOpen: boolean;
7
- setIsOpen: (open: boolean) => void;
8
- onSubmit: (content: string) => void;
9
- children?: React.ReactNode;
10
- title?: string;
11
- }
12
-
13
- const CommentFormModal: React.FC<CommentFormProps> = ({ setIsOpen, isOpen, onSubmit, children, title = 'Add comment' }) => {
14
- return (
15
- <Modal
16
- title={title}
17
- open={isOpen}
18
- width={800}
19
- closeIcon={true}
20
- footer={null}
21
- onCancel={() => {
22
- setIsOpen(false);
23
- }}
24
- >
25
- {children}
26
- <CommentForm onSubmit={onSubmit} />
27
- </Modal>
28
- );
29
- };
30
-
31
- export default CommentFormModal;
@@ -1,351 +0,0 @@
1
- import { ActionType } from "@ant-design/pro-table";
2
- import React, { Key, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
3
- import { Badge, Button, Result, Tabs, TabsProps, Tooltip } from "antd";
4
- import { DeleteOutlined, StopOutlined } from "@ant-design/icons";
5
- import { FormattedMessage, useIntl } from "react-intl";
6
- import { DescriptionsRefType, FieldsEdit, TDescriptionsProps, TGetOneParams } from "./descriptionTypes";
7
- import { PageLoading, ProDescriptions } from "@ant-design/pro-components";
8
- import { columnsToDescriptionItemProps, TDescriptionSection } from "./useDescriptionColumns";
9
- import pick from "lodash/pick";
10
- import safetyRun from "../../tools/safetyRun";
11
- import {
12
- buildFieldsFromColumnsForDescriptionsDisplay,
13
- buildJoinFields,
14
- collectFieldsFromColumns,
15
- } from "../Table";
16
- import { RowEditableConfig } from "@ant-design/pro-utils";
17
- import { useForm } from "antd/es/form/Form";
18
- import useContentViewMode, { VIEW_MODE_TYPE } from "./useContentViewMode";
19
- import { createStyles } from "antd-style";
20
- import { debounce } from "lodash";
21
- import { NamePath } from "antd/lib/form/interface";
22
- import { FieldData } from "rc-field-form/lib/interface";
23
-
24
- const useStyles = createStyles(({css}) => {
25
- return {
26
- antDescriptionsStyles: css`
27
- .ant-descriptions-item-content {
28
- .anticon-edit {
29
- opacity: 0;
30
- transition: opacity 200ms;
31
- }
32
-
33
- &:hover {
34
- .anticon-edit {
35
- opacity: 1;
36
- }
37
- }
38
- }
39
- `
40
- }
41
- })
42
-
43
- const DescriptionsComponent = <Entity extends Record<string | symbol, any>,
44
- CreateDto = Entity,
45
- UpdateDto = Entity,
46
- TPathParams = object,
47
- >(
48
- {
49
- mainTitle = 'General',
50
- entity,
51
- getOne,
52
- onUpdate,
53
- onCreate,
54
- pathParams,
55
- idColumnName = 'id',
56
- entityToUpdateDto,
57
- afterSave,
58
- actionRef: actionRefProp,
59
- editable,
60
- canEdit = false,
61
- fieldsEditType = FieldsEdit.Single,
62
- columns,
63
- params,
64
- onEntityChange,
65
- contentViewMode: contentViewModeProp,
66
- ...rest
67
- }: TDescriptionsProps<Entity,
68
- CreateDto,
69
- UpdateDto,
70
- TPathParams>,
71
- ref: React.Ref<DescriptionsRefType<Entity>>,
72
- ) => {
73
- const { styles } = useStyles();
74
-
75
- let [form] = useForm<Entity>();
76
- if (!editable?.form) {
77
- editable = {
78
- ...editable,
79
- form,
80
- }
81
- }
82
- form = editable.form;
83
-
84
- const actionRefComponent = useRef<ActionType>();
85
- const actionRef = actionRefProp || actionRefComponent;
86
- const intl = useIntl();
87
- const [data, setData] = useState<Partial<Entity> | undefined>(entity);
88
- const [loading, setLoading] = useState(false);
89
-
90
- const sections = columnsToDescriptionItemProps(columns, mainTitle);
91
-
92
- const recalculateErrors = () => {
93
- // Recalculate the error count per section (tab) after validation
94
- const newErrorsPerSection = new Map<string, number>();
95
- sections.forEach((section) => {
96
- let errorCount = 0;
97
- section.columns.forEach((column) => {
98
- if (form.getFieldError(column.dataIndex as NamePath<Entity>)?.length > 0) {
99
- errorCount++;
100
- }
101
- });
102
- newErrorsPerSection.set(section.key, errorCount);
103
- });
104
- setErrorsPerSection(newErrorsPerSection);
105
- }
106
-
107
- const columnDataIndexToSection = sections.reduce((acc, section) => {
108
- section.columns.forEach(column => {
109
- if (Array.isArray(column.dataIndex)) {
110
- throw new Error('We only support simple dataIndex for now');
111
- }
112
-
113
- acc.set(column.dataIndex, section);
114
- })
115
- return acc;
116
- }, new Map<Key, TDescriptionSection<Entity>>());
117
-
118
- const {
119
- contentViewModeButton,
120
- contentViewMode
121
- } = useContentViewMode({
122
- mode: contentViewModeProp || (sections.length > 1 ? VIEW_MODE_TYPE.TABS : VIEW_MODE_TYPE.GENERAL)
123
- });
124
- const [errorsPerSection, setErrorsPerSection] = useState<Map<TDescriptionSection<Entity>['key'], number>>(
125
- new Map(sections.map(section => [section.key, 0]))
126
- );
127
-
128
- const handleSubmit = async () => {
129
- try {
130
- // Validate all fields in the form
131
- const data = await form.validateFields();
132
- // Let the parent component handle the submit logic
133
- await onCreate(data);
134
- } catch (error) {
135
- console.error('Validation or submission failed:', error);
136
- } finally {
137
- recalculateErrors();
138
- }
139
- };
140
-
141
- const handleUpdate = async () => {
142
- try {
143
- // Validate all fields in the form
144
- const data = await form.validateFields();
145
- // Let the parent component handle the update logic
146
- return onSave(null, data, null);
147
- } catch (error) {
148
- console.error('Validation or submission failed:', error);
149
- throw error;
150
- } finally {
151
- recalculateErrors();
152
- }
153
- };
154
-
155
- useImperativeHandle(ref, () => ({
156
- reset: () => {
157
- setErrorsPerSection(new Map(sections.map(section => [section.key, 0])));
158
- form.resetFields();
159
- },
160
- submit: () => handleSubmit(),
161
- update: () => handleUpdate(),
162
- setFieldErrors: (fields: FieldData<Entity>[]) => {
163
- form.setFields(fields)
164
- }
165
- }));
166
-
167
- const onValuesChange = debounce((changedValues, allValues) => {
168
- let key = Object.keys(changedValues)[0];
169
-
170
- // changedValues = {} if we clear select value
171
- if (!key) {
172
- const previousValues = form.getFieldsValue(true);
173
- key = Object.keys(previousValues).find((field) => !(field in allValues));
174
- }
175
-
176
- form.validateFields([key])
177
- .finally(() => {
178
- const section = columnDataIndexToSection.get(key);
179
- const dataIndexes = section.columns.map(column => {
180
- return Array.isArray(column.dataIndex) ? column.dataIndex.join('.') : column.dataIndex;
181
- });
182
-
183
- const errorsNumber = form.getFieldsError(dataIndexes as NamePath<Entity>[]).reduce((acc, field) => acc + field.errors.length, 0);
184
- setErrorsPerSection((prev) => {
185
- const updated = new Map(prev);
186
- updated.set(section.key, errorsNumber);
187
- return updated;
188
- });
189
- });
190
- }, 500);
191
-
192
- const queryParams = useMemo(() => {
193
- const join = params?.join;
194
- const queryParams: TGetOneParams & TPathParams = {
195
- ...(pathParams ?? {} as TPathParams),
196
- };
197
-
198
- const { joinSelect, joinFields } = buildJoinFields(join);
199
- queryParams.join = joinSelect;
200
- queryParams.fields = collectFieldsFromColumns(
201
- columns,
202
- idColumnName,
203
- joinFields,
204
- );
205
-
206
- return queryParams;
207
- }, [params, pathParams]);
208
-
209
- const getKey = (index: number) =>
210
- index + String(pathParams?.[idColumnName as keyof TPathParams])
211
-
212
- const requestData = async () => {
213
- if (!getOne) {
214
- return;
215
- }
216
-
217
- setLoading(true);
218
-
219
- try {
220
- const record = await getOne(queryParams);
221
- onEntityChange?.(record);
222
- setData(record ?? undefined);
223
- } catch (e) {
224
- console.error(e);
225
- setData(undefined);
226
- } finally {
227
- setLoading(false);
228
- }
229
- }
230
-
231
- const onSave: RowEditableConfig<Entity>['onSave'] = async (propName, record) => {
232
- try {
233
- await form.validateFields();
234
- if (onUpdate && entityToUpdateDto) {
235
- await onUpdate({
236
- ...queryParams,
237
- ...{} as Partial<Entity>,
238
- requestBody: entityToUpdateDto(
239
- propName === null
240
- ? record
241
- : pick(record, [propName as keyof Entity])
242
- ),
243
- });
244
- }
245
-
246
- setData(record);
247
- if (typeof afterSave === 'function') {
248
- await afterSave(record);
249
- }
250
- } catch (e) {
251
- console.error(e);
252
- }
253
- }
254
-
255
- useEffect(() => {
256
- safetyRun(requestData());
257
- }, [])
258
-
259
- useEffect(() => {
260
- setData(entity);
261
- form.setFieldsValue(entity);
262
- }, [entity])
263
-
264
- if (loading) {
265
- return <PageLoading />;
266
- }
267
-
268
- if (!data) {
269
- return (
270
- <Result
271
- status="404"
272
- title="404"
273
- subTitle="The entity is not found."
274
- />
275
- );
276
- }
277
-
278
- const formProps = contentViewMode === VIEW_MODE_TYPE.TABS ? {
279
- onValuesChange,
280
- } : undefined;
281
-
282
- const contentViewSwitcher = sections.length > 1 ? contentViewModeButton : undefined;
283
- const descriptions = sections.map((section, index) => {
284
- return <ProDescriptions<Entity>
285
- key={getKey(index)}
286
- title={section.title as React.ReactNode}
287
- actionRef={actionRef}
288
- size={"small"}
289
- bordered
290
- loading={loading}
291
- style={{ marginBottom: 20 }}
292
- labelStyle={{ width: '15%' }}
293
- dataSource={data as Entity}
294
- className={styles.antDescriptionsStyles}
295
- editable={canEdit ? {
296
- type: 'multiple',
297
- onSave,
298
- deletePopconfirmMessage: intl.formatMessage({ id: 'table.deletePopconfirmMessage' }),
299
- onlyAddOneLineAlertMessage: intl.formatMessage({ id: 'table.onlyAddOneLineAlertMessage' }),
300
- cancelText: <Tooltip title={intl.formatMessage({ id: 'table.cancelText' })}><StopOutlined /></Tooltip>,
301
- deleteText: <Tooltip title={intl.formatMessage({ id: 'table.deleteText' })}><DeleteOutlined /></Tooltip>,
302
- saveText: <Button size={"small"} type={"primary"}><FormattedMessage id={'table.saveText'} /></Button>,
303
- ...(fieldsEditType === FieldsEdit.All && {
304
- editableKeys: [...buildFieldsFromColumnsForDescriptionsDisplay(columns, idColumnName)],
305
- actionRender: () => [],
306
- }),
307
- ...editable,
308
- } : undefined}
309
- columns={section.columns}
310
- extra={contentViewMode === VIEW_MODE_TYPE.GENERAL && index === 0 ? contentViewSwitcher : undefined}
311
- formProps={formProps}
312
- {...rest}
313
- />;
314
- });
315
-
316
- if (contentViewMode === VIEW_MODE_TYPE.GENERAL) {
317
- return descriptions;
318
- }
319
-
320
- const tabsItems: TabsProps['items'] = sections.map((section, index) => {
321
- return {
322
- key: getKey(index),
323
- label: (
324
- <Badge
325
- size='small'
326
- overflowCount={5}
327
- count={errorsPerSection.get(section.key)}
328
- >
329
- {section.title as React.ReactNode}
330
- </Badge>
331
- ),
332
- forceRender: true,
333
- children: descriptions[index],
334
- }
335
- });
336
-
337
- return <Tabs
338
- defaultActiveKey="0"
339
- items={tabsItems}
340
- tabBarExtraContent={contentViewModeButton}
341
- />;
342
- };
343
-
344
- const Descriptions = React.forwardRef(DescriptionsComponent) as <Entity extends Record<string | symbol, any>,
345
- CreateDto = Entity,
346
- UpdateDto = Entity,
347
- TPathParams = object>(
348
- props: TDescriptionsProps<Entity, CreateDto, UpdateDto, TPathParams>
349
- ) => React.ReactElement;
350
-
351
- export default Descriptions;
@@ -1,65 +0,0 @@
1
- import { useEffect, useMemo } from "react";
2
- import { Button, Modal } from "antd";
3
- import { TDescriptionsCreateModalProps } from "./descriptionTypes";
4
- import { ProDescriptions } from "@ant-design/pro-components";
5
- import { columnsToDescriptionItemProps } from "./useDescriptionColumns";
6
- import { useForm } from "antd/es/form/Form";
7
- import { buildFieldsFromColumnsForDescriptionsDisplay } from "../Table";
8
-
9
- const DescriptionsCreateModal = <Entity extends Record<string | symbol, any>>({
10
- idColumnName,
11
- columns,
12
- data,
13
- onClose,
14
- onSubmit,
15
- ...rest
16
- }: TDescriptionsCreateModalProps<Entity>) => {
17
- const sections = columnsToDescriptionItemProps(columns, 'General');
18
- const [form] = useForm();
19
-
20
- const editableKeys = useMemo(() => {
21
- return [
22
- ...buildFieldsFromColumnsForDescriptionsDisplay(
23
- columns,
24
- idColumnName,
25
- ),
26
- ];
27
- }, [columns, idColumnName]);
28
-
29
- useEffect(() => {
30
- data ? form.setFieldsValue(data) : form.resetFields();
31
- }, [data]);
32
-
33
- return (
34
- <Modal
35
- open={data !== undefined}
36
- onCancel={onClose}
37
- width='80%'
38
- footer={[
39
- <Button key='submit' type="primary" onClick={async () => form.validateFields().then(onSubmit)}>Create</Button>
40
- ]}
41
- >
42
- {sections.map((section, index) => (
43
- <ProDescriptions<Entity>
44
- key={index + (Array.isArray(idColumnName) ? idColumnName.join('-') : idColumnName)}
45
- title={section.title as React.ReactNode}
46
- size={"small"}
47
- bordered
48
- column={2}
49
- style={{ marginBottom: 20 }}
50
- labelStyle={{ width: '15%' }}
51
- contentStyle={{ width: '25%' }}
52
- editable={{
53
- form,
54
- editableKeys,
55
- actionRender: () => [],
56
- }}
57
- columns={section.columns}
58
- {...rest}
59
- />
60
- ))}
61
- </Modal>
62
- );
63
- };
64
-
65
- export default DescriptionsCreateModal;