@centreon/ui 26.3.19 → 26.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 +3 -3
- package/src/Form/Inputs/ConnectedAutocomplete.tsx +23 -14
- package/src/Form/Inputs/models.ts +1 -0
- package/src/InputField/Text/index.tsx +5 -4
- package/src/Listing/Row/Row.tsx +3 -42
- package/src/Listing/index.tsx +3 -10
- package/src/api/buildListingDecoder.ts +39 -6
- package/src/api/buildListingEndpoint/index.ts +24 -20
- package/src/api/buildListingEndpoint/models.ts +4 -2
- package/src/api/useMutationQuery/index.ts +3 -1
- package/src/queryParameters/index.ts +15 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "26.
|
|
3
|
+
"version": "26.5.0",
|
|
4
4
|
"description": "Centreon UI Components",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"update:deps": "pnpx npm-check-updates -i --format group",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"url": "git+https://github.com/centreon/centreon.git"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
|
-
"
|
|
32
|
-
"
|
|
31
|
+
"react",
|
|
32
|
+
"centreon"
|
|
33
33
|
],
|
|
34
34
|
"author": {
|
|
35
35
|
"name": "centreon@centreon.com"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FormikValues, useFormikContext } from 'formik';
|
|
2
2
|
import { equals, isEmpty, path, propEq, reject, split } from 'ramda';
|
|
3
3
|
import { useCallback, useMemo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useMemoComponent
|
|
10
10
|
} from '../..';
|
|
11
11
|
import MultiConnectedAutocompleteField from '../../InputField/Select/Autocomplete/Connected/Multi';
|
|
12
|
-
import {
|
|
12
|
+
import { InputPropsWithoutGroup, InputType } from './models';
|
|
13
13
|
|
|
14
14
|
const defaultFilterKey = 'name';
|
|
15
15
|
|
|
@@ -42,10 +42,26 @@ const ConnectedAutocomplete = ({
|
|
|
42
42
|
|
|
43
43
|
const isMultiple = equals(type, InputType.MultiConnectedAutocomplete);
|
|
44
44
|
|
|
45
|
-
const getEndpoint = (parameters): string =>
|
|
46
|
-
|
|
45
|
+
const getEndpoint = (parameters): string => {
|
|
46
|
+
const nameQueryParameters =
|
|
47
|
+
connectedAutocomplete?.useNewAPIFormat && parameters?.search
|
|
48
|
+
? [
|
|
49
|
+
{
|
|
50
|
+
name: 'name[lk]',
|
|
51
|
+
value: parameters.search.conditions[0].values.$lk.slice(1, -1)
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
: [];
|
|
55
|
+
|
|
56
|
+
return buildListingEndpoint({
|
|
57
|
+
apiFormat: connectedAutocomplete?.useNewAPIFormat
|
|
58
|
+
? 'JSON-LD'
|
|
59
|
+
: 'Standard',
|
|
47
60
|
baseEndpoint: connectedAutocomplete?.endpoint,
|
|
48
|
-
customQueryParameters:
|
|
61
|
+
customQueryParameters: [
|
|
62
|
+
...(connectedAutocomplete?.customQueryParameters || []),
|
|
63
|
+
...nameQueryParameters
|
|
64
|
+
],
|
|
49
65
|
parameters: {
|
|
50
66
|
...parameters,
|
|
51
67
|
search: {
|
|
@@ -58,6 +74,7 @@ const ConnectedAutocomplete = ({
|
|
|
58
74
|
sort: { [filterKey]: 'ASC' }
|
|
59
75
|
}
|
|
60
76
|
});
|
|
77
|
+
};
|
|
61
78
|
|
|
62
79
|
const fieldNamePath = split('.', fieldName);
|
|
63
80
|
|
|
@@ -79,15 +96,7 @@ const ConnectedAutocomplete = ({
|
|
|
79
96
|
setFieldTouched(fieldName, true, false);
|
|
80
97
|
setFieldValue(fieldName, value);
|
|
81
98
|
},
|
|
82
|
-
[
|
|
83
|
-
fieldName,
|
|
84
|
-
change,
|
|
85
|
-
setFieldTouched,
|
|
86
|
-
setFieldValue,
|
|
87
|
-
setTouched,
|
|
88
|
-
setValues,
|
|
89
|
-
values
|
|
90
|
-
]
|
|
99
|
+
[fieldName, touched, additionalMemoProps]
|
|
91
100
|
);
|
|
92
101
|
|
|
93
102
|
const blur = (): void => setFieldTouched(fieldName, true);
|
|
@@ -56,6 +56,7 @@ export interface InputProps {
|
|
|
56
56
|
options?: Array<string>;
|
|
57
57
|
};
|
|
58
58
|
connectedAutocomplete?: {
|
|
59
|
+
useNewAPIFormat?: boolean;
|
|
59
60
|
additionalConditionParameters: Array<ConditionsSearchParameter>;
|
|
60
61
|
customQueryParameters: Array<QueryParameter>;
|
|
61
62
|
chipColor?: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Box,
|
|
3
3
|
InputAdornment,
|
|
4
|
-
|
|
4
|
+
InputProps,
|
|
5
5
|
TextField as MuiTextField,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
TextFieldProps,
|
|
7
|
+
TextFieldSlotsAndSlotProps,
|
|
8
|
+
Theme,
|
|
9
9
|
Tooltip,
|
|
10
10
|
Typography
|
|
11
11
|
} from '@mui/material';
|
|
@@ -170,6 +170,7 @@ const TextField = forwardRef(
|
|
|
170
170
|
ref={ref}
|
|
171
171
|
size={size || 'small'}
|
|
172
172
|
{...getValueProps()}
|
|
173
|
+
autoComplete="off"
|
|
173
174
|
className={classes.textField}
|
|
174
175
|
required={required}
|
|
175
176
|
slotProps={{
|
package/src/Listing/Row/Row.tsx
CHANGED
|
@@ -2,12 +2,11 @@ import { TableRow, type TableRowProps, useTheme } from '@mui/material';
|
|
|
2
2
|
|
|
3
3
|
import type { ListingVariant } from '@centreon/ui-context';
|
|
4
4
|
|
|
5
|
-
import { equals,
|
|
5
|
+
import { equals, lt, not, pluck } from 'ramda';
|
|
6
6
|
import { memo, useCallback, useEffect, useRef } from 'react';
|
|
7
7
|
|
|
8
|
-
import LoadingSkeleton from '../../LoadingSkeleton';
|
|
9
8
|
import { useViewportIntersection } from '../../utils/useViewportIntersection';
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
import type { Column, ColumnConfiguration, RowColorCondition } from '../models';
|
|
12
11
|
|
|
13
12
|
type Props = {
|
|
@@ -39,33 +38,8 @@ const Row = memo<RowProps>(
|
|
|
39
38
|
tabIndex,
|
|
40
39
|
onMouseOver,
|
|
41
40
|
onFocus,
|
|
42
|
-
onClick
|
|
43
|
-
isInViewport,
|
|
44
|
-
visibleColumns,
|
|
45
|
-
checkable,
|
|
46
|
-
limit
|
|
41
|
+
onClick
|
|
47
42
|
}: RowProps): JSX.Element => {
|
|
48
|
-
if (not(isInViewport) && gte(limit, performanceRowsLimit)) {
|
|
49
|
-
return (
|
|
50
|
-
<div className="contents">
|
|
51
|
-
{checkable && (
|
|
52
|
-
<div className="p-1">
|
|
53
|
-
<div>
|
|
54
|
-
<LoadingSkeleton className="w-full" />
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
)}
|
|
58
|
-
{visibleColumns.map(({ id }) => (
|
|
59
|
-
<div className="p-1" key={`loading_${id}`}>
|
|
60
|
-
<div>
|
|
61
|
-
<LoadingSkeleton className="w-full" />
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
))}
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
43
|
return (
|
|
70
44
|
<TableRow
|
|
71
45
|
className="cursor-pointer contents w-full"
|
|
@@ -83,7 +57,6 @@ const Row = memo<RowProps>(
|
|
|
83
57
|
const {
|
|
84
58
|
row: previousRow,
|
|
85
59
|
rowColorConditions: previousRowColorConditions,
|
|
86
|
-
isInViewport: prevIsInViewport,
|
|
87
60
|
visibleColumns: previousVisibleColumns,
|
|
88
61
|
isShiftKeyDown: prevIsShiftKeyDown,
|
|
89
62
|
shiftKeyDownRowPivot: prevShiftKeyDownRowPivot,
|
|
@@ -117,18 +90,6 @@ const Row = memo<RowProps>(
|
|
|
117
90
|
return false;
|
|
118
91
|
}
|
|
119
92
|
|
|
120
|
-
const isNoLongerInViewport = not(prevIsInViewport) && not(nextIsInViewport);
|
|
121
|
-
|
|
122
|
-
if (isNoLongerInViewport && gte(nextLimit, performanceRowsLimit)) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const isBackInViewport = not(prevIsInViewport) && nextIsInViewport;
|
|
127
|
-
|
|
128
|
-
if (isBackInViewport && gte(nextLimit, performanceRowsLimit)) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
93
|
const previousRowConditions = previousRowColorConditions?.map(
|
|
133
94
|
({ condition }) => condition(previousRow)
|
|
134
95
|
);
|
package/src/Listing/index.tsx
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
filter,
|
|
11
11
|
findIndex,
|
|
12
12
|
gt,
|
|
13
|
-
gte,
|
|
14
13
|
identity,
|
|
15
14
|
includes,
|
|
16
15
|
isNil,
|
|
@@ -46,6 +45,8 @@ import DataCell from './Cell/DataCell';
|
|
|
46
45
|
import Checkbox from './Checkbox';
|
|
47
46
|
import { EmptyResult } from './EmptyResult/EmptyResult';
|
|
48
47
|
import { ListingHeader } from './Header';
|
|
48
|
+
import ListingRow from './Row/Row';
|
|
49
|
+
import { SkeletonLoader } from './Row/SkeletonLoaderRows';
|
|
49
50
|
import type {
|
|
50
51
|
Column,
|
|
51
52
|
ColumnConfiguration,
|
|
@@ -54,8 +55,6 @@ import type {
|
|
|
54
55
|
RowId,
|
|
55
56
|
SortOrder
|
|
56
57
|
} from './models';
|
|
57
|
-
import ListingRow from './Row/Row';
|
|
58
|
-
import { SkeletonLoader } from './Row/SkeletonLoaderRows';
|
|
59
58
|
import { subItemsPivotsAtom } from './tableAtoms';
|
|
60
59
|
import { labelNoResultFound as defaultLabelNoResultFound } from './translatedLabels';
|
|
61
60
|
import useStyleTable, { useColumnStyle } from './useStyleTable';
|
|
@@ -143,8 +142,6 @@ const defaultColumnConfiguration = {
|
|
|
143
142
|
sortable: false
|
|
144
143
|
};
|
|
145
144
|
|
|
146
|
-
export const performanceRowsLimit = 60;
|
|
147
|
-
|
|
148
145
|
const Listing = <
|
|
149
146
|
TRow extends {
|
|
150
147
|
id: RowId;
|
|
@@ -617,11 +614,7 @@ const Listing = <
|
|
|
617
614
|
isHovered={isRowHovered}
|
|
618
615
|
isSelected={isRowSelected}
|
|
619
616
|
isShiftKeyDown={isShiftKeyDown}
|
|
620
|
-
key={
|
|
621
|
-
gte(limit, performanceRowsLimit)
|
|
622
|
-
? `row_${index}`
|
|
623
|
-
: getRowId(row)
|
|
624
|
-
}
|
|
617
|
+
key={getRowId(row)}
|
|
625
618
|
lastSelectionIndex={lastSelectionIndex}
|
|
626
619
|
limit={limit}
|
|
627
620
|
listingVariant={listingVariant}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { equals } from 'ramda';
|
|
1
2
|
import { JsonDecoder } from 'ts.data.json';
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
+
import { Listing, ListingMeta } from './models';
|
|
4
5
|
|
|
5
6
|
const metaDecoder = JsonDecoder.object<ListingMeta>(
|
|
6
7
|
{
|
|
@@ -15,13 +16,34 @@ interface ListingDecoderOptions<TEntity> {
|
|
|
15
16
|
entityDecoder: JsonDecoder.Decoder<TEntity>;
|
|
16
17
|
entityDecoderName: string;
|
|
17
18
|
listingDecoderName: string;
|
|
19
|
+
apiFormat?: 'Standard' | 'JSON-LD';
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
entityDecoder
|
|
22
|
-
entityDecoderName,
|
|
23
|
-
listingDecoderName
|
|
24
|
-
|
|
22
|
+
const jsonLdListingDecoder = <TEntity>(
|
|
23
|
+
entityDecoder: JsonDecoder.Decoder<TEntity>,
|
|
24
|
+
entityDecoderName: string,
|
|
25
|
+
listingDecoderName: string
|
|
26
|
+
): JsonDecoder.Decoder<Listing<TEntity>> =>
|
|
27
|
+
JsonDecoder.object(
|
|
28
|
+
{
|
|
29
|
+
member: JsonDecoder.array(entityDecoder, entityDecoderName),
|
|
30
|
+
totalItems: JsonDecoder.number
|
|
31
|
+
},
|
|
32
|
+
listingDecoderName
|
|
33
|
+
).map((data) => ({
|
|
34
|
+
meta: {
|
|
35
|
+
limit: data.member.length,
|
|
36
|
+
page: 1,
|
|
37
|
+
total: data.totalItems
|
|
38
|
+
},
|
|
39
|
+
result: data.member
|
|
40
|
+
})) as JsonDecoder.Decoder<Listing<TEntity>>;
|
|
41
|
+
|
|
42
|
+
const standardListingDecoder = <TEntity>(
|
|
43
|
+
entityDecoder: JsonDecoder.Decoder<TEntity>,
|
|
44
|
+
entityDecoderName: string,
|
|
45
|
+
listingDecoderName: string
|
|
46
|
+
): JsonDecoder.Decoder<Listing<TEntity>> =>
|
|
25
47
|
JsonDecoder.object<Listing<TEntity>>(
|
|
26
48
|
{
|
|
27
49
|
meta: metaDecoder,
|
|
@@ -30,4 +52,15 @@ const buildListingDecoder = <TEntity>({
|
|
|
30
52
|
listingDecoderName
|
|
31
53
|
);
|
|
32
54
|
|
|
55
|
+
const buildListingDecoder = <TEntity>({
|
|
56
|
+
entityDecoder,
|
|
57
|
+
entityDecoderName,
|
|
58
|
+
listingDecoderName,
|
|
59
|
+
apiFormat = 'Standard'
|
|
60
|
+
}: ListingDecoderOptions<TEntity>): JsonDecoder.Decoder<Listing<TEntity>> => {
|
|
61
|
+
return (
|
|
62
|
+
equals(apiFormat, 'JSON-LD') ? jsonLdListingDecoder : standardListingDecoder
|
|
63
|
+
)(entityDecoder, entityDecoderName, listingDecoderName);
|
|
64
|
+
};
|
|
65
|
+
|
|
33
66
|
export default buildListingDecoder;
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
import { equals, keys, values } from 'ramda';
|
|
2
|
+
|
|
1
3
|
import toRawQueryParameters from '../../queryParameters';
|
|
2
|
-
import
|
|
4
|
+
import { QueryParameter } from '../../queryParameters/models';
|
|
3
5
|
import { getSearchQueryParameterValue } from './getSearchQueryParameterValue';
|
|
4
|
-
import
|
|
6
|
+
import { BuildListingEndpointParameters, Parameters } from './models';
|
|
5
7
|
|
|
6
8
|
const getQueryParameters = ({
|
|
7
9
|
sort,
|
|
8
10
|
page,
|
|
9
11
|
limit,
|
|
10
12
|
search,
|
|
11
|
-
customQueryParameters = []
|
|
13
|
+
customQueryParameters = [],
|
|
14
|
+
apiFormat
|
|
12
15
|
}: Parameters): Array<QueryParameter> => {
|
|
16
|
+
if (equals(apiFormat, 'JSON-LD')) {
|
|
17
|
+
return [
|
|
18
|
+
{ name: 'page', value: page },
|
|
19
|
+
{ name: 'itemsPerPage', value: limit },
|
|
20
|
+
{ name: `sort[${keys(sort || {})[0]}]`, value: values(sort || {})[0] },
|
|
21
|
+
...customQueryParameters
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
return [
|
|
14
26
|
{ name: 'page', value: page },
|
|
15
27
|
{ name: 'limit', value: limit },
|
|
@@ -22,33 +34,25 @@ const getQueryParameters = ({
|
|
|
22
34
|
];
|
|
23
35
|
};
|
|
24
36
|
|
|
25
|
-
const buildEndpoint = ({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return `${baseEndpoint}
|
|
37
|
+
const buildEndpoint = ({
|
|
38
|
+
baseEndpoint,
|
|
39
|
+
queryParameters,
|
|
40
|
+
apiFormat
|
|
41
|
+
}): string => {
|
|
42
|
+
return `${baseEndpoint}?${toRawQueryParameters({ apiFormat, queryParameters })}`;
|
|
31
43
|
};
|
|
32
44
|
|
|
33
45
|
const buildListingEndpoint = ({
|
|
34
46
|
baseEndpoint,
|
|
35
47
|
parameters,
|
|
36
48
|
customQueryParameters,
|
|
37
|
-
|
|
49
|
+
apiFormat = 'Standard'
|
|
38
50
|
}: BuildListingEndpointParameters): string => {
|
|
39
|
-
if (isCustomEndpoint) {
|
|
40
|
-
return buildCustomEndpoint({
|
|
41
|
-
baseEndpoint,
|
|
42
|
-
queryParameters: [
|
|
43
|
-
...getQueryParameters({ ...parameters, customQueryParameters })
|
|
44
|
-
]
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
51
|
return buildEndpoint({
|
|
52
|
+
apiFormat,
|
|
49
53
|
baseEndpoint,
|
|
50
54
|
queryParameters: [
|
|
51
|
-
...getQueryParameters({ ...parameters, customQueryParameters })
|
|
55
|
+
...getQueryParameters({ ...parameters, apiFormat, customQueryParameters })
|
|
52
56
|
]
|
|
53
57
|
});
|
|
54
58
|
};
|
|
@@ -3,8 +3,8 @@ import type { QueryParameter } from '../../queryParameters/models';
|
|
|
3
3
|
export interface BuildListingEndpointParameters {
|
|
4
4
|
baseEndpoint?: string;
|
|
5
5
|
customQueryParameters?: Array<QueryParameter>;
|
|
6
|
-
parameters: Parameters
|
|
7
|
-
|
|
6
|
+
parameters: Omit<Parameters, 'apiFormat' | 'customQueryParameters'>;
|
|
7
|
+
apiFormat?: 'Standard' | 'JSON-LD';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export interface SearchMatch {
|
|
@@ -18,7 +18,9 @@ export interface Parameters {
|
|
|
18
18
|
page?: number;
|
|
19
19
|
search?: SearchParameter;
|
|
20
20
|
sort?: SortQueryParameterValue;
|
|
21
|
+
apiFormat: 'Standard' | 'JSON-LD';
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
export interface SearchParameter {
|
|
23
25
|
conditions?: Array<ConditionsSearchParameter>;
|
|
24
26
|
lists?: Array<ListsSearchParameter>;
|
|
@@ -114,7 +114,9 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
114
114
|
defaultFailureMessage,
|
|
115
115
|
endpoint: getEndpoint(_meta as TMeta),
|
|
116
116
|
headers: new Headers({
|
|
117
|
-
'Content-Type':
|
|
117
|
+
'Content-Type': equals(method, Method.PATCH)
|
|
118
|
+
? 'application/merge-patch+json'
|
|
119
|
+
: 'application/json',
|
|
118
120
|
...fetchHeaders
|
|
119
121
|
}),
|
|
120
122
|
isMutation: true,
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { isEmpty, isNil } from 'ramda';
|
|
1
|
+
import { equals, isEmpty, isNil } from 'ramda';
|
|
2
2
|
|
|
3
3
|
import type { QueryParameter } from './models';
|
|
4
4
|
|
|
5
|
+
interface ToRawQueryParametersProps {
|
|
6
|
+
queryParameters: Array<QueryParameter>;
|
|
7
|
+
apiFormat: 'Standard' | 'JSON-LD';
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
const toRawQueryParameter = ({ name, value }): string => {
|
|
6
|
-
return `${name}=${encodeURIComponent(
|
|
11
|
+
return `${name}=${encodeURIComponent(value)}`;
|
|
7
12
|
};
|
|
8
13
|
|
|
9
|
-
const toRawQueryParameters = (
|
|
14
|
+
const toRawQueryParameters = ({
|
|
15
|
+
queryParameters,
|
|
16
|
+
apiFormat
|
|
17
|
+
}: ToRawQueryParametersProps): string =>
|
|
10
18
|
queryParameters
|
|
11
19
|
.filter(({ value }) => !isNil(value) && !isEmpty(value))
|
|
20
|
+
.map(({ name, value }) => ({
|
|
21
|
+
name,
|
|
22
|
+
value: equals(apiFormat, 'JSON-LD') ? value : JSON.stringify(value)
|
|
23
|
+
}))
|
|
12
24
|
.map(toRawQueryParameter)
|
|
13
25
|
.join('&');
|
|
14
26
|
|