@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "24.4.6",
3
+ "version": "24.4.7",
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;
@@ -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
- 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}`;