@boarteam/boar-pack-common-frontend 2.7.0 → 2.8.1
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 +6 -3
- package/src/components/Descriptions/descriptionTypes.ts +4 -2
- package/src/components/Inputs/RelationSelect.tsx +2 -5
- package/src/components/Table/CreateEntityModal.tsx +4 -4
- package/src/components/Table/Table.tsx +2 -2
- package/src/components/Table/tableTools.ts +1 -0
- package/src/components/Table/useCreation.tsx +19 -1
- package/src/components/Table/useEditableTable.tsx +2 -0
- package/src/tools/ApiError.ts +18 -0
- package/src/tools/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boarteam/boar-pack-common-frontend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"description": "Common frontend package for Boar Pack",
|
|
5
5
|
"repository": "git@github.com:boarteam/boar-pack.git",
|
|
6
6
|
"author": "Andrew Balakirev <balakirev.andrey@gmail.com>",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"scripts": {
|
|
47
47
|
"yalc:push": "yalc push"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "da7dfee327d3713dbc9419e69ae61bf725aba89a"
|
|
50
50
|
}
|
|
@@ -15,6 +15,7 @@ import useContentViewMode, { VIEW_MODE_TYPE } from "./useContentViewMode";
|
|
|
15
15
|
import { createStyles } from "antd-style";
|
|
16
16
|
import { debounce } from "lodash";
|
|
17
17
|
import { NamePath } from "antd/lib/form/interface";
|
|
18
|
+
import { FieldData } from "rc-field-form/lib/interface";
|
|
18
19
|
|
|
19
20
|
const useStyles = createStyles(({css}) => {
|
|
20
21
|
return {
|
|
@@ -61,7 +62,7 @@ const DescriptionsComponent = <Entity extends Record<string | symbol, any>,
|
|
|
61
62
|
CreateDto,
|
|
62
63
|
UpdateDto,
|
|
63
64
|
TPathParams>,
|
|
64
|
-
ref: React.Ref<DescriptionsRefType
|
|
65
|
+
ref: React.Ref<DescriptionsRefType<Entity>>,
|
|
65
66
|
) => {
|
|
66
67
|
const { styles } = useStyles();
|
|
67
68
|
|
|
@@ -133,6 +134,9 @@ const DescriptionsComponent = <Entity extends Record<string | symbol, any>,
|
|
|
133
134
|
form.resetFields();
|
|
134
135
|
},
|
|
135
136
|
submit: () => handleSubmit(),
|
|
137
|
+
setFieldErrors: (fields: FieldData<Entity>[]) => {
|
|
138
|
+
form.setFields(fields)
|
|
139
|
+
}
|
|
136
140
|
}));
|
|
137
141
|
|
|
138
142
|
const onValuesChange = debounce((changedValues, allValues) => {
|
|
@@ -237,8 +241,7 @@ const DescriptionsComponent = <Entity extends Record<string | symbol, any>,
|
|
|
237
241
|
<Result
|
|
238
242
|
status="404"
|
|
239
243
|
title="404"
|
|
240
|
-
subTitle="The
|
|
241
|
-
extra={<Button type="primary" href={'/liquidity/ecn-instruments'}>See list of instruments</Button>}
|
|
244
|
+
subTitle="The entity is not found."
|
|
242
245
|
/>
|
|
243
246
|
);
|
|
244
247
|
}
|
|
@@ -4,6 +4,7 @@ import { RowEditableConfig } from "@ant-design/pro-utils";
|
|
|
4
4
|
import { QueryJoin } from "@nestjsx/crud-request";
|
|
5
5
|
import { ProColumns } from "@ant-design/pro-components";
|
|
6
6
|
import { ProDescriptionsProps } from "@ant-design/pro-descriptions";
|
|
7
|
+
import { FieldData } from "rc-field-form/lib/interface";
|
|
7
8
|
|
|
8
9
|
export type TGetOneParams = {
|
|
9
10
|
/**
|
|
@@ -23,9 +24,10 @@ export type TDescriptionGetRequestParams = {
|
|
|
23
24
|
join?: QueryJoin | QueryJoin[];
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
export type DescriptionsRefType = {
|
|
27
|
+
export type DescriptionsRefType<Entity> = {
|
|
27
28
|
reset: () => void;
|
|
28
29
|
submit: () => void;
|
|
30
|
+
setFieldErrors: (fields: FieldData<Entity>[]) => void;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export type TDescriptionsProps<Entity, CreateDto, UpdateDto, TPathParams = object> = {
|
|
@@ -49,7 +51,7 @@ export type TDescriptionsProps<Entity, CreateDto, UpdateDto, TPathParams = objec
|
|
|
49
51
|
params?: TDescriptionGetRequestParams,
|
|
50
52
|
columns: ProColumns<Entity>[],
|
|
51
53
|
onEntityChange?: (entity: Entity | null) => void;
|
|
52
|
-
ref?: React.Ref<DescriptionsRefType
|
|
54
|
+
ref?: React.Ref<DescriptionsRefType<Entity>>,
|
|
53
55
|
} & Omit<ProDescriptionsProps<Entity>, 'columns'>;
|
|
54
56
|
|
|
55
57
|
export type TDescriptionsCreateModalProps<Entity> = Omit<ProDescriptionsProps<Entity>, 'columns'> & {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ProFormSelectProps } from "@ant-design/pro-
|
|
1
|
+
import { ProFormSelect, ProFormSelectProps } from "@ant-design/pro-components";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import { ProFormSelect } from "@ant-design/pro-form";
|
|
4
3
|
|
|
5
4
|
type RelationSelectProps<T> = ProFormSelectProps & {
|
|
6
5
|
selectedItem: T | null | undefined,
|
|
@@ -44,10 +43,8 @@ export const RelationSelect = function<T>({
|
|
|
44
43
|
showSearch
|
|
45
44
|
mode={'single'}
|
|
46
45
|
request={request}
|
|
46
|
+
className='relational-select'
|
|
47
47
|
formItemProps={{
|
|
48
|
-
// correct color for invalid relational fields (#64)
|
|
49
|
-
// @ts-ignore-next-line
|
|
50
|
-
validateStatus: rest['aria-invalid'] === 'true' ? 'error' : 'success',
|
|
51
48
|
style: {
|
|
52
49
|
margin: 0,
|
|
53
50
|
display: 'inline-block',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ProColumns } from "@ant-design/pro-components";
|
|
2
2
|
import { Button, Modal } from "antd";
|
|
3
|
-
import { useRef } from "react";
|
|
3
|
+
import { MutableRefObject, useRef } from "react";
|
|
4
4
|
import { Descriptions, DescriptionsRefType } from "../Descriptions";
|
|
5
5
|
import { buildFieldsFromColumnsForDescriptionsDisplay } from "./tableTools";
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ export interface CreateEntityModalProps<Entity> {
|
|
|
23
23
|
* Called when the form is submitted.
|
|
24
24
|
* Receives the validated form data.
|
|
25
25
|
*/
|
|
26
|
-
onSubmit: (data: any) => Promise<void>;
|
|
26
|
+
onSubmit: (data: any, descriptionsRef: MutableRefObject<DescriptionsRefType<Entity>>) => Promise<void>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function CreateEntityModal<
|
|
@@ -41,7 +41,7 @@ export function CreateEntityModal<
|
|
|
41
41
|
onCancel,
|
|
42
42
|
onSubmit,
|
|
43
43
|
}: CreateEntityModalProps<Entity>) {
|
|
44
|
-
const descriptionsRef = useRef<DescriptionsRefType
|
|
44
|
+
const descriptionsRef = useRef<DescriptionsRefType<Entity>>(null);
|
|
45
45
|
|
|
46
46
|
// Calculate the editable keys from the columns and idColumnName
|
|
47
47
|
const editableKeys = [...buildFieldsFromColumnsForDescriptionsDisplay(columns, idColumnName)];
|
|
@@ -74,7 +74,7 @@ export function CreateEntityModal<
|
|
|
74
74
|
labelStyle={{ width: '15%' }}
|
|
75
75
|
contentStyle={{ width: '25%' }}
|
|
76
76
|
canEdit={true}
|
|
77
|
-
onCreate={onSubmit}
|
|
77
|
+
onCreate={(data) => onSubmit(data, descriptionsRef)}
|
|
78
78
|
editable={{
|
|
79
79
|
editableKeys,
|
|
80
80
|
actionRender: () => [],
|
|
@@ -196,7 +196,7 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
196
196
|
bordered
|
|
197
197
|
search={false}
|
|
198
198
|
editable={editableConfig}
|
|
199
|
-
toolBarRender={(...args) => [
|
|
199
|
+
toolBarRender={toolBarRender === false ? false : (...args) => [
|
|
200
200
|
columnsSetSelect?.() || null,
|
|
201
201
|
!viewOnly && onUpdateMany
|
|
202
202
|
? bulkEditButton
|
|
@@ -205,7 +205,7 @@ const Table = <Entity extends Record<string | symbol, any>,
|
|
|
205
205
|
? bulkDeleteButton
|
|
206
206
|
: null,
|
|
207
207
|
!viewOnly && createButton || null,
|
|
208
|
-
!viewOnly && importButton || null,
|
|
208
|
+
!viewOnly && onImport && importButton || null,
|
|
209
209
|
exportUrl && exportButton || null,
|
|
210
210
|
...toolBarRender && toolBarRender(...args) || [],
|
|
211
211
|
]}
|
|
@@ -5,6 +5,8 @@ import { PlusOutlined } from "@ant-design/icons";
|
|
|
5
5
|
import { FormattedMessage } from "react-intl";
|
|
6
6
|
import type { SizeType } from "antd/es/config-provider/SizeContext";
|
|
7
7
|
import { CreateEntityModal, CreateEntityModalProps } from "./CreateEntityModal";
|
|
8
|
+
import { DescriptionsRefType } from "../Descriptions";
|
|
9
|
+
import { ApiError } from '../../tools'
|
|
8
10
|
|
|
9
11
|
let creatingRecordsCount = 0;
|
|
10
12
|
export const KEY_SYMBOL = Symbol('key');
|
|
@@ -41,7 +43,7 @@ export function useCreation<Entity, CreateDto, TPathParams = {}>({
|
|
|
41
43
|
} & Omit<CreateEntityModalProps<Entity>, 'onSubmit' | 'onCancel' | 'entity'>) {
|
|
42
44
|
const [createPopupData, setCreatePopupData] = useState<Partial<Entity> | undefined>();
|
|
43
45
|
|
|
44
|
-
const onCreateSubmit = async (data: Partial<Entity
|
|
46
|
+
const onCreateSubmit = async (data: Partial<Entity>, descriptionsRef: MutableRefObject<DescriptionsRefType<Entity>>) => {
|
|
45
47
|
try {
|
|
46
48
|
await onCreate?.({
|
|
47
49
|
...pathParams,
|
|
@@ -54,6 +56,22 @@ export function useCreation<Entity, CreateDto, TPathParams = {}>({
|
|
|
54
56
|
await actionRef?.current?.reload();
|
|
55
57
|
} catch (e) {
|
|
56
58
|
console.error(e);
|
|
59
|
+
|
|
60
|
+
// Handle common error
|
|
61
|
+
if (e.body && e.body.statusCode && e.body.errors) {
|
|
62
|
+
const error = e as ApiError;
|
|
63
|
+
const { statusCode, errors } = error.body;
|
|
64
|
+
// Validation error. Highlight corresponding form fields
|
|
65
|
+
if (statusCode === 400) {
|
|
66
|
+
const formErrors = errors.map(error => ({
|
|
67
|
+
name: error.field,
|
|
68
|
+
errors: [error.message],
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
descriptionsRef.current.setFieldErrors(formErrors);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
}
|
|
58
76
|
};
|
|
59
77
|
|
|
@@ -5,6 +5,7 @@ import { Button, Tooltip } from "antd";
|
|
|
5
5
|
import { DeleteOutlined, StopOutlined } from "@ant-design/icons";
|
|
6
6
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
7
7
|
import React, { useState } from "react";
|
|
8
|
+
import { isRecordNew } from "./useCreation";
|
|
8
9
|
|
|
9
10
|
export function useEditableTable<Entity, CreateDto, UpdateDto, TPathParams = {}>(
|
|
10
11
|
{
|
|
@@ -68,6 +69,7 @@ export function useEditableTable<Entity, CreateDto, UpdateDto, TPathParams = {}>
|
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
async onDelete(id, row) {
|
|
72
|
+
if (isRecordNew(row)) return;
|
|
71
73
|
await onDelete({ ...row, ...pathParams });
|
|
72
74
|
},
|
|
73
75
|
deletePopconfirmMessage: intl.formatMessage({ id: 'table.deletePopconfirmMessage' }),
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Should be synced with common-backend/src/tools/ApiError.ts
|
|
2
|
+
type TApiErrorBodyType = {
|
|
3
|
+
statusCode: number
|
|
4
|
+
message: string
|
|
5
|
+
errors: {
|
|
6
|
+
field: string,
|
|
7
|
+
message: string
|
|
8
|
+
}[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Copied from api-client/generated/core/ApiError.ts
|
|
12
|
+
export class ApiError extends Error {
|
|
13
|
+
public readonly url: string;
|
|
14
|
+
public readonly status: number;
|
|
15
|
+
public readonly statusText: string;
|
|
16
|
+
public readonly body: TApiErrorBodyType;
|
|
17
|
+
public readonly request: any;
|
|
18
|
+
}
|
package/src/tools/index.ts
CHANGED