@centreon/ui 24.4.3 → 24.4.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "24.4.3",
3
+ "version": "24.4.4",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -7,6 +7,7 @@ export const useHeatMapStyles = makeStyles()((theme) => ({
7
7
  heatMapTile: {
8
8
  alignItems: 'center',
9
9
  aspectRatio: '1 / 1',
10
+ borderRadius: theme.shape.borderRadius,
10
11
  display: 'flex',
11
12
  justifyContent: 'center',
12
13
  width: '100%'
@@ -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={tooltipContent?.({
77
- backgroundColor,
78
- data,
79
- id,
80
- isSmallestSize
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?: ({
@@ -17,6 +17,7 @@ const renderMultiAutocompleteField = (): RenderResult =>
17
17
  render(
18
18
  <TestQueryProvider>
19
19
  <MultiConnectedAutocompleteField
20
+ baseEndpoint=""
20
21
  field="host.name"
21
22
  getEndpoint={getEndpoint}
22
23
  label={label}
@@ -45,6 +45,7 @@ const renderSingleConnectedAutocompleteField = (
45
45
  render(
46
46
  <TestQueryProvider>
47
47
  <SingleConnectedAutocompleteField
48
+ baseEndpoint=""
48
49
  field="host.name"
49
50
  getEndpoint={getEndpoint}
50
51
  label={label}
@@ -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 sx={{ width: autoSize ? 'auto' : '100%' }}>
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: '#F5F1E9',
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: '#F0E9F8',
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: '#67532C',
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: '#4B2352',
296
+ inDowntimeBackground: '#512980',
297
297
  selected: 'rgba(255, 255, 255, 0.5)',
298
298
  selectedOpacity: 0.5
299
299
  },
@@ -52,7 +52,8 @@ export type Operator =
52
52
  | '$lk'
53
53
  | '$nk'
54
54
  | '$in'
55
- | '$ni';
55
+ | '$ni'
56
+ | '$rg';
56
57
 
57
58
  export type ConditionValue = {
58
59
  [value in Operator]?: string | Array<string>;
@@ -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(endpoint, options)
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;
@@ -57,13 +58,17 @@ const useFetchQuery = <T extends object>({
57
58
  fetchHeaders,
58
59
  isPaginated,
59
60
  queryOptions,
60
- httpCodesBypassErrorSnackbar = []
61
+ httpCodesBypassErrorSnackbar = [],
62
+ baseEndpoint
61
63
  }: UseFetchQueryProps<T>): UseFetchQueryState<T> => {
64
+ const dataRef = useRef<T | undefined>(undefined);
65
+
62
66
  const { showErrorMessage } = useSnackbar();
63
67
 
64
68
  const queryData = useQuery<T | ResponseError, Error>({
65
69
  queryFn: ({ signal }): Promise<T | ResponseError> =>
66
70
  customFetch<T>({
71
+ baseEndpoint,
67
72
  catchError,
68
73
  decoder,
69
74
  defaultFailureMessage,
@@ -96,6 +101,7 @@ const useFetchQuery = <T extends object>({
96
101
  queryClient.prefetchQuery({
97
102
  queryFn: ({ 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
  return queryClient.fetchQuery({
138
144
  queryFn: ({ signal }): Promise<T | ResponseError> =>
139
145
  customFetch<T>({
146
+ baseEndpoint,
140
147
  catchError,
141
148
  decoder,
142
149
  defaultFailureMessage,
@@ -154,6 +161,10 @@ const useFetchQuery = <T extends object>({
154
161
  [queryData.data]
155
162
  );
156
163
 
164
+ if (!isNil(data)) {
165
+ dataRef.current = data;
166
+ }
167
+
157
168
  const errorData = queryData.data as ResponseError | undefined;
158
169
 
159
170
  useEffect(() => {
@@ -171,7 +182,7 @@ const useFetchQuery = <T extends object>({
171
182
 
172
183
  return {
173
184
  ...omit(['data', 'error'], queryData),
174
- data,
185
+ data: dataRef.current,
175
186
  error: errorData?.isError ? omit(['isError'], errorData) : null,
176
187
  fetchQuery,
177
188
  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;
@@ -68,7 +69,8 @@ const useMutationQuery = <T extends object, TMeta>({
68
69
  method,
69
70
  onMutate,
70
71
  onError,
71
- onSuccess
72
+ onSuccess,
73
+ baseEndpoint
72
74
  }: UseMutationQueryProps<T, TMeta>): UseMutationQueryState<T> => {
73
75
  const { showErrorMessage } = useSnackbar();
74
76
 
@@ -83,6 +85,7 @@ const useMutationQuery = <T extends object, TMeta>({
83
85
  const { _meta, ...payload } = _payload || {};
84
86
 
85
87
  return customFetch<T>({
88
+ baseEndpoint,
86
89
  catchError,
87
90
  decoder,
88
91
  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
- decoder: JsonDecoder.Decoder<Listing<T>>;
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
- parameters: { limit, page: params?.page || page }
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}`;