@centreon/ui 24.4.6 → 24.4.7
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 +1 -1
- package/src/Graph/HeatMap/HeatMap.styles.tsx +1 -0
- package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +12 -8
- package/src/Graph/HeatMap/model.ts +1 -0
- package/src/InputField/Select/Autocomplete/Connected/Multi/index.test.tsx +1 -0
- package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +1 -0
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +3 -0
- package/src/InputField/Text/index.tsx +6 -1
- package/src/ThemeProvider/palettes.ts +4 -4
- package/src/api/buildListingEndpoint/models.ts +2 -1
- package/src/api/customFetch.ts +12 -3
- package/src/api/useFetchQuery/index.ts +16 -5
- package/src/api/useMutationQuery/index.ts +4 -1
- package/src/utils/useInfiniteScrollListing.ts +22 -7
- package/src/utils/useLocaleDateTimeFormat/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import { scaleLinear } from '@visx/scale';
|
|
4
|
-
import { equals, gt, lt } from 'ramda';
|
|
4
|
+
import { T, equals, gt, lt } from 'ramda';
|
|
5
5
|
|
|
6
6
|
import { Box } from '@mui/material';
|
|
7
7
|
|
|
@@ -20,7 +20,8 @@ const ResponsiveHeatMap = <TData,>({
|
|
|
20
20
|
tiles,
|
|
21
21
|
arrowClassName,
|
|
22
22
|
tooltipContent,
|
|
23
|
-
tileSizeFixed
|
|
23
|
+
tileSizeFixed,
|
|
24
|
+
displayTooltipCondition = T
|
|
24
25
|
}: HeatMapProps<TData> & { width: number }): JSX.Element | null => {
|
|
25
26
|
const { classes, cx } = useHeatMapStyles();
|
|
26
27
|
|
|
@@ -73,12 +74,15 @@ const ResponsiveHeatMap = <TData,>({
|
|
|
73
74
|
tooltip: classes.heatMapTooltip
|
|
74
75
|
}}
|
|
75
76
|
followCursor={false}
|
|
76
|
-
label={
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
label={
|
|
78
|
+
displayTooltipCondition?.(data) &&
|
|
79
|
+
tooltipContent?.({
|
|
80
|
+
backgroundColor,
|
|
81
|
+
data,
|
|
82
|
+
id,
|
|
83
|
+
isSmallestSize
|
|
84
|
+
})
|
|
85
|
+
}
|
|
82
86
|
position="right-start"
|
|
83
87
|
>
|
|
84
88
|
<div className={classes.heatMapTileContent}>
|
|
@@ -21,6 +21,7 @@ export interface HeatMapProps<TData> {
|
|
|
21
21
|
data,
|
|
22
22
|
isSmallestSize
|
|
23
23
|
}: ChildrenProps<TData>) => ReactElement | boolean | null;
|
|
24
|
+
displayTooltipCondition?: (data: TData) => boolean;
|
|
24
25
|
tileSizeFixed?: boolean;
|
|
25
26
|
tiles: Array<Tile<TData>>;
|
|
26
27
|
tooltipContent?: ({
|
|
@@ -32,6 +32,7 @@ import useFetchQuery from '../../../../api/useFetchQuery';
|
|
|
32
32
|
|
|
33
33
|
export interface ConnectedAutoCompleteFieldProps<TData> {
|
|
34
34
|
allowUniqOption?: boolean;
|
|
35
|
+
baseEndpoint?: string;
|
|
35
36
|
conditionField?: keyof SelectEntry;
|
|
36
37
|
field: string;
|
|
37
38
|
getEndpoint: ({ search, page }) => string;
|
|
@@ -60,6 +61,7 @@ const ConnectedAutocompleteField = (
|
|
|
60
61
|
displayOptionThumbnail,
|
|
61
62
|
queryKey,
|
|
62
63
|
allowUniqOption,
|
|
64
|
+
baseEndpoint,
|
|
63
65
|
...props
|
|
64
66
|
}: ConnectedAutoCompleteFieldProps<TData> &
|
|
65
67
|
Omit<AutocompleteFieldProps, 'options'>): JSX.Element => {
|
|
@@ -87,6 +89,7 @@ const ConnectedAutocompleteField = (
|
|
|
87
89
|
const { fetchQuery, isFetching, prefetchNextPage } = useFetchQuery<
|
|
88
90
|
ListingModel<TData>
|
|
89
91
|
>({
|
|
92
|
+
baseEndpoint,
|
|
90
93
|
fetchHeaders: getRequestHeaders,
|
|
91
94
|
getEndpoint: (params) => {
|
|
92
95
|
return getEndpoint({
|
|
@@ -80,6 +80,7 @@ export type Props = {
|
|
|
80
80
|
autoSizeCustomPadding?: number;
|
|
81
81
|
autoSizeDefaultWidth?: number;
|
|
82
82
|
className?: string;
|
|
83
|
+
containerClassName?: string;
|
|
83
84
|
dataTestId: string;
|
|
84
85
|
debounced?: boolean;
|
|
85
86
|
displayErrorInTooltip?: boolean;
|
|
@@ -112,6 +113,7 @@ const TextField = forwardRef(
|
|
|
112
113
|
autoSizeCustomPadding,
|
|
113
114
|
defaultValue,
|
|
114
115
|
required = false,
|
|
116
|
+
containerClassName,
|
|
115
117
|
...rest
|
|
116
118
|
}: Props,
|
|
117
119
|
ref: React.ForwardedRef<HTMLDivElement>
|
|
@@ -142,7 +144,10 @@ const TextField = forwardRef(
|
|
|
142
144
|
}, [innerValue, debounced, defaultValue]);
|
|
143
145
|
|
|
144
146
|
return (
|
|
145
|
-
<Box
|
|
147
|
+
<Box
|
|
148
|
+
className={containerClassName}
|
|
149
|
+
sx={{ width: autoSize ? 'auto' : '100%' }}
|
|
150
|
+
>
|
|
146
151
|
<Tooltip placement="top" title={tooltipTitle}>
|
|
147
152
|
<MuiTextField
|
|
148
153
|
data-testid={dataTestId}
|
|
@@ -143,7 +143,7 @@ declare module '@mui/material/Badge' {
|
|
|
143
143
|
export const lightPalette: PaletteOptions = {
|
|
144
144
|
action: {
|
|
145
145
|
acknowledged: '#67532C',
|
|
146
|
-
acknowledgedBackground: '#
|
|
146
|
+
acknowledgedBackground: '#DFD2B9',
|
|
147
147
|
activatedOpacity: 0.12,
|
|
148
148
|
active: '#666666',
|
|
149
149
|
disabled: '#999999',
|
|
@@ -153,7 +153,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
153
153
|
hover: 'rgba(0, 0, 0, 0.06)',
|
|
154
154
|
hoverOpacity: 0.06,
|
|
155
155
|
inDowntime: '#4B2352',
|
|
156
|
-
inDowntimeBackground: '#
|
|
156
|
+
inDowntimeBackground: '#E5D8F3',
|
|
157
157
|
selected: 'rgba(102, 102, 102, 0.3)',
|
|
158
158
|
selectedOpacity: 0.3
|
|
159
159
|
},
|
|
@@ -283,7 +283,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
283
283
|
export const darkPalette: PaletteOptions = {
|
|
284
284
|
action: {
|
|
285
285
|
acknowledged: '#67532C',
|
|
286
|
-
acknowledgedBackground: '#
|
|
286
|
+
acknowledgedBackground: '#745F35',
|
|
287
287
|
activatedOpacity: 0.3,
|
|
288
288
|
active: '#B5B5B5',
|
|
289
289
|
disabled: '#999999',
|
|
@@ -293,7 +293,7 @@ export const darkPalette: PaletteOptions = {
|
|
|
293
293
|
hover: 'rgba(255, 255, 255, 0.16)',
|
|
294
294
|
hoverOpacity: 0.16,
|
|
295
295
|
inDowntime: '#4B2352',
|
|
296
|
-
inDowntimeBackground: '#
|
|
296
|
+
inDowntimeBackground: '#512980',
|
|
297
297
|
selected: 'rgba(255, 255, 255, 0.5)',
|
|
298
298
|
selectedOpacity: 0.5
|
|
299
299
|
},
|
package/src/api/customFetch.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { equals } from 'ramda';
|
|
1
|
+
import { equals, isNil, startsWith } from 'ramda';
|
|
2
2
|
import { JsonDecoder } from 'ts.data.json';
|
|
3
3
|
|
|
4
4
|
import { Method } from './useMutationQuery';
|
|
@@ -22,6 +22,7 @@ export interface CatchErrorProps {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface CustomFetchProps<T> {
|
|
25
|
+
baseEndpoint?: string;
|
|
25
26
|
catchError?: (props: CatchErrorProps) => void;
|
|
26
27
|
decoder?: JsonDecoder.Decoder<T>;
|
|
27
28
|
defaultFailureMessage?: string;
|
|
@@ -42,10 +43,18 @@ export const customFetch = <T>({
|
|
|
42
43
|
defaultFailureMessage = 'Something went wrong',
|
|
43
44
|
isMutation = false,
|
|
44
45
|
payload,
|
|
45
|
-
method = 'GET'
|
|
46
|
+
method = 'GET',
|
|
47
|
+
baseEndpoint = './api/latest'
|
|
46
48
|
}: CustomFetchProps<T>): Promise<T | ResponseError> => {
|
|
47
49
|
const defaultOptions = { headers, method, signal };
|
|
48
50
|
|
|
51
|
+
const formattedEndpoint =
|
|
52
|
+
!isNil(baseEndpoint) &&
|
|
53
|
+
!startsWith(baseEndpoint, endpoint) &&
|
|
54
|
+
!startsWith('./api/internal.php', endpoint)
|
|
55
|
+
? `${baseEndpoint}${endpoint}`
|
|
56
|
+
: endpoint;
|
|
57
|
+
|
|
49
58
|
const options = isMutation
|
|
50
59
|
? {
|
|
51
60
|
...defaultOptions,
|
|
@@ -53,7 +62,7 @@ export const customFetch = <T>({
|
|
|
53
62
|
}
|
|
54
63
|
: defaultOptions;
|
|
55
64
|
|
|
56
|
-
return fetch(
|
|
65
|
+
return fetch(formattedEndpoint, options)
|
|
57
66
|
.then((response) => {
|
|
58
67
|
if (equals(response.status, 204)) {
|
|
59
68
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import 'ulog';
|
|
4
4
|
import {
|
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
} from '@tanstack/react-query';
|
|
11
11
|
import { JsonDecoder } from 'ts.data.json';
|
|
12
12
|
import anylogger from 'anylogger';
|
|
13
|
-
import { has, includes, not, omit } from 'ramda';
|
|
13
|
+
import { has, includes, isNil, not, omit } from 'ramda';
|
|
14
14
|
|
|
15
15
|
import { CatchErrorProps, customFetch, ResponseError } from '../customFetch';
|
|
16
16
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
17
17
|
import { useDeepCompare } from '../../utils';
|
|
18
18
|
|
|
19
19
|
export interface UseFetchQueryProps<T> {
|
|
20
|
+
baseEndpoint?: string;
|
|
20
21
|
catchError?: (props: CatchErrorProps) => void;
|
|
21
22
|
decoder?: JsonDecoder.Decoder<T>;
|
|
22
23
|
defaultFailureMessage?: string;
|
|
@@ -54,14 +55,18 @@ const useFetchQuery = <T extends object>({
|
|
|
54
55
|
fetchHeaders,
|
|
55
56
|
isPaginated,
|
|
56
57
|
queryOptions,
|
|
57
|
-
httpCodesBypassErrorSnackbar = []
|
|
58
|
+
httpCodesBypassErrorSnackbar = [],
|
|
59
|
+
baseEndpoint
|
|
58
60
|
}: UseFetchQueryProps<T>): UseFetchQueryState<T> => {
|
|
61
|
+
const dataRef = useRef<T | undefined>(undefined);
|
|
62
|
+
|
|
59
63
|
const { showErrorMessage } = useSnackbar();
|
|
60
64
|
|
|
61
65
|
const queryData = useQuery<T | ResponseError, Error>(
|
|
62
66
|
getQueryKey(),
|
|
63
67
|
({ signal }): Promise<T | ResponseError> =>
|
|
64
68
|
customFetch<T>({
|
|
69
|
+
baseEndpoint,
|
|
65
70
|
catchError,
|
|
66
71
|
decoder,
|
|
67
72
|
defaultFailureMessage,
|
|
@@ -96,6 +101,7 @@ const useFetchQuery = <T extends object>({
|
|
|
96
101
|
queryKey,
|
|
97
102
|
({ signal }): Promise<T | ResponseError> =>
|
|
98
103
|
customFetch<T>({
|
|
104
|
+
baseEndpoint,
|
|
99
105
|
catchError,
|
|
100
106
|
decoder,
|
|
101
107
|
defaultFailureMessage,
|
|
@@ -137,6 +143,7 @@ const useFetchQuery = <T extends object>({
|
|
|
137
143
|
getQueryKey(),
|
|
138
144
|
({ signal }): Promise<T | ResponseError> =>
|
|
139
145
|
customFetch<T>({
|
|
146
|
+
baseEndpoint,
|
|
140
147
|
catchError,
|
|
141
148
|
decoder,
|
|
142
149
|
defaultFailureMessage,
|
|
@@ -153,6 +160,10 @@ const useFetchQuery = <T extends object>({
|
|
|
153
160
|
[queryData.data]
|
|
154
161
|
);
|
|
155
162
|
|
|
163
|
+
if (!isNil(data)) {
|
|
164
|
+
dataRef.current = data;
|
|
165
|
+
}
|
|
166
|
+
|
|
156
167
|
const errorData = queryData.data as ResponseError | undefined;
|
|
157
168
|
|
|
158
169
|
useEffect(() => {
|
|
@@ -166,8 +177,8 @@ const useFetchQuery = <T extends object>({
|
|
|
166
177
|
}, useDeepCompare([queryData.data]));
|
|
167
178
|
|
|
168
179
|
return {
|
|
169
|
-
...omit(['data'], queryData),
|
|
170
|
-
data,
|
|
180
|
+
...omit(['data', 'error'], queryData),
|
|
181
|
+
data: dataRef.current,
|
|
171
182
|
error: errorData?.isError ? omit(['isError'], errorData) : null,
|
|
172
183
|
fetchQuery,
|
|
173
184
|
prefetchNextPage,
|
|
@@ -23,6 +23,7 @@ export enum Method {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export type UseMutationQueryProps<T, TMeta> = {
|
|
26
|
+
baseEndpoint?: string;
|
|
26
27
|
catchError?: (props: CatchErrorProps) => void;
|
|
27
28
|
decoder?: JsonDecoder.Decoder<T>;
|
|
28
29
|
defaultFailureMessage?: string;
|
|
@@ -49,7 +50,8 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
49
50
|
method,
|
|
50
51
|
onMutate,
|
|
51
52
|
onError,
|
|
52
|
-
onSuccess
|
|
53
|
+
onSuccess,
|
|
54
|
+
baseEndpoint
|
|
53
55
|
}: UseMutationQueryProps<T, TMeta>): UseMutationQueryState<T> => {
|
|
54
56
|
const { showErrorMessage } = useSnackbar();
|
|
55
57
|
|
|
@@ -62,6 +64,7 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
62
64
|
const { _meta, ...payload } = _payload || {};
|
|
63
65
|
|
|
64
66
|
return customFetch<T>({
|
|
67
|
+
baseEndpoint,
|
|
65
68
|
catchError,
|
|
66
69
|
decoder,
|
|
67
70
|
defaultFailureMessage,
|
|
@@ -5,33 +5,42 @@ import { PrimitiveAtom, useAtom } from 'jotai';
|
|
|
5
5
|
import { JsonDecoder } from 'ts.data.json';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
QueryParameter,
|
|
8
9
|
buildListingEndpoint,
|
|
9
10
|
useFetchQuery,
|
|
10
11
|
useIntersectionObserver
|
|
11
12
|
} from '@centreon/ui';
|
|
12
13
|
|
|
13
14
|
import type { Listing } from '../api/models';
|
|
14
|
-
|
|
15
|
-
const limit = 100;
|
|
15
|
+
import { Parameters } from '../api/buildListingEndpoint/models';
|
|
16
16
|
|
|
17
17
|
interface UseInfiniteScrollListing<T> {
|
|
18
18
|
elementRef: (node) => void;
|
|
19
19
|
elements: Array<T>;
|
|
20
20
|
isLoading: boolean;
|
|
21
|
+
total?: number;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
interface UseInfiniteScrollListingProps<T> {
|
|
24
|
-
|
|
25
|
+
customQueryParameters?: Array<QueryParameter>;
|
|
26
|
+
decoder?: JsonDecoder.Decoder<Listing<T>>;
|
|
25
27
|
endpoint: string;
|
|
28
|
+
limit?: number;
|
|
26
29
|
pageAtom: PrimitiveAtom<number>;
|
|
30
|
+
parameters?: Parameters;
|
|
27
31
|
queryKeyName: string;
|
|
32
|
+
suspense?: boolean;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export const useInfiniteScrollListing = <T>({
|
|
31
36
|
queryKeyName,
|
|
32
37
|
endpoint,
|
|
33
38
|
decoder,
|
|
34
|
-
pageAtom
|
|
39
|
+
pageAtom,
|
|
40
|
+
suspense = true,
|
|
41
|
+
parameters,
|
|
42
|
+
customQueryParameters,
|
|
43
|
+
limit = 100
|
|
35
44
|
}: UseInfiniteScrollListingProps<T>): UseInfiniteScrollListing<T> => {
|
|
36
45
|
const [maxPage, setMaxPage] = useState(1);
|
|
37
46
|
|
|
@@ -46,14 +55,15 @@ export const useInfiniteScrollListing = <T>({
|
|
|
46
55
|
getEndpoint: (params) =>
|
|
47
56
|
buildListingEndpoint({
|
|
48
57
|
baseEndpoint: endpoint,
|
|
49
|
-
|
|
58
|
+
customQueryParameters,
|
|
59
|
+
parameters: { limit, page: params?.page || page, ...parameters }
|
|
50
60
|
}),
|
|
51
61
|
getQueryKey: () => [queryKeyName, page],
|
|
52
62
|
isPaginated: true,
|
|
53
63
|
queryOptions: {
|
|
54
64
|
refetchOnMount: false,
|
|
55
65
|
refetchOnWindowFocus: false,
|
|
56
|
-
suspense: equals(page, 1)
|
|
66
|
+
suspense: suspense && equals(page, 1)
|
|
57
67
|
}
|
|
58
68
|
});
|
|
59
69
|
|
|
@@ -98,9 +108,14 @@ export const useInfiniteScrollListing = <T>({
|
|
|
98
108
|
});
|
|
99
109
|
}, [data]);
|
|
100
110
|
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
return () => setPage(1);
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
101
115
|
return {
|
|
102
116
|
elementRef,
|
|
103
117
|
elements: elements.current || [],
|
|
104
|
-
isLoading
|
|
118
|
+
isLoading,
|
|
119
|
+
total: data?.meta.total
|
|
105
120
|
};
|
|
106
121
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import humanizeDuration from 'humanize-duration';
|
|
3
3
|
import { useAtomValue } from 'jotai';
|
|
4
|
+
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
4
5
|
|
|
5
6
|
import { userAtom } from '@centreon/ui-context';
|
|
6
7
|
|
|
@@ -20,6 +21,8 @@ export interface LocaleDateTimeFormat {
|
|
|
20
21
|
toTime: (date: Date | string) => string;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
dayjs.extend(localizedFormat);
|
|
25
|
+
|
|
23
26
|
const dateFormat = 'L';
|
|
24
27
|
const timeFormat = 'LT';
|
|
25
28
|
const dateTimeFormat = `${dateFormat} ${timeFormat}`;
|