@centreon/ui 24.4.27 → 24.4.29

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.
Files changed (97) hide show
  1. package/package.json +19 -14
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Icon/index.tsx +1 -1
  4. package/src/Button/Save/StartIcon.tsx +3 -3
  5. package/src/Button/Save/index.tsx +9 -5
  6. package/src/Checkbox/Checkbox.tsx +2 -2
  7. package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
  8. package/src/Dashboard/Item.tsx +1 -1
  9. package/src/Dashboard/Layout.tsx +2 -2
  10. package/src/Dialog/index.tsx +1 -1
  11. package/src/FallbackPage/FallbackPage.tsx +3 -3
  12. package/src/FileDropZone/index.tsx +3 -1
  13. package/src/Form/Form.cypress.spec.tsx +133 -0
  14. package/src/Form/Inputs/CheckboxGroup.tsx +1 -4
  15. package/src/Form/Inputs/List/Content.tsx +62 -0
  16. package/src/Form/Inputs/List/List.styles.ts +29 -0
  17. package/src/Form/Inputs/List/List.tsx +58 -0
  18. package/src/Form/Inputs/List/useList.ts +81 -0
  19. package/src/Form/Inputs/index.tsx +3 -1
  20. package/src/Form/Inputs/models.ts +9 -1
  21. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  22. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +5 -4
  23. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  24. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  25. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  26. package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +2 -3
  27. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  28. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  29. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  30. package/src/Graph/LineChart/helpers/index.ts +1 -1
  31. package/src/Graph/LineChart/index.stories.tsx +4 -2
  32. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  33. package/src/Graph/Text/Text.stories.tsx +60 -4
  34. package/src/Graph/common/timeSeries/index.ts +3 -3
  35. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  36. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  37. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  38. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  39. package/src/InputField/Select/Autocomplete/index.tsx +121 -115
  40. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  41. package/src/InputField/Select/index.tsx +1 -1
  42. package/src/InputField/Text/index.tsx +2 -2
  43. package/src/Listing/Cell/DataCell.tsx +15 -1
  44. package/src/Listing/Header/ListingHeader.tsx +1 -1
  45. package/src/Listing/Listing.cypress.spec.tsx +2 -2
  46. package/src/Listing/Listing.styles.ts +2 -3
  47. package/src/Listing/index.stories.tsx +14 -3
  48. package/src/Listing/index.tsx +8 -9
  49. package/src/Module/Module.cypress.spec.tsx +129 -0
  50. package/src/Module/index.tsx +2 -4
  51. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  52. package/src/SortableItems/index.tsx +2 -7
  53. package/src/ThemeProvider/index.tsx +24 -0
  54. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  55. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  56. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  57. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  58. package/src/TimePeriods/helpers/index.ts +1 -1
  59. package/src/TimePeriods/index.stories.tsx +12 -4
  60. package/src/TimePeriods/index.tsx +2 -2
  61. package/src/api/QueryProvider.tsx +1 -1
  62. package/src/api/TestQueryProvider.tsx +1 -1
  63. package/src/api/useFetchQuery/index.ts +27 -23
  64. package/src/api/useMutationQuery/index.ts +45 -21
  65. package/src/components/Button/Icon/IconButton.tsx +6 -2
  66. package/src/components/DataTable/DataListing.tsx +6 -0
  67. package/src/components/DataTable/DataTable.cypress.spec.tsx +193 -0
  68. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  69. package/src/components/DataTable/DataTable.styles.ts +3 -0
  70. package/src/components/DataTable/DataTable.tsx +3 -3
  71. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  72. package/src/components/DataTable/Item/DataTableItem.tsx +4 -4
  73. package/src/components/DataTable/index.ts +3 -1
  74. package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +2 -0
  75. package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +1 -1
  76. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  77. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  78. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  79. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  80. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  81. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  82. package/src/components/Layout/PageLayout/index.ts +3 -1
  83. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  84. package/src/components/Modal/Modal.styles.ts +4 -3
  85. package/src/components/Modal/ModalActions.tsx +4 -2
  86. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  87. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  88. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  89. package/src/index.ts +2 -2
  90. package/src/queryParameters/url/index.ts +5 -1
  91. package/src/utils/index.ts +1 -1
  92. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  93. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  94. package/src/utils/usePluralizedTranslation.ts +21 -0
  95. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  96. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  97. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -73,14 +73,14 @@ const IconPopoverMultiAutocomplete = ({
73
73
  };
74
74
 
75
75
  const isSelected = (id: number | string): boolean => {
76
- return pipe(find(propEq('id', id)), Boolean)(value);
76
+ return pipe(find(propEq(id, 'id')), Boolean)(value);
77
77
  };
78
78
 
79
79
  const unSelect = (option: SelectEntry): void => {
80
80
  const { id } = option;
81
81
 
82
82
  const updatedValue = isSelected(id)
83
- ? reject(propEq('id', id), value)
83
+ ? reject(propEq(id, 'id'), value)
84
84
  : [...value, option];
85
85
 
86
86
  onChange(updatedValue);
@@ -69,7 +69,7 @@ const SelectField = ({
69
69
  const { classes, cx } = useStyles();
70
70
 
71
71
  const getOption = (id): SelectEntry => {
72
- return options.find(propEq('id', id)) as SelectEntry;
72
+ return options.find(propEq(id, 'id')) as SelectEntry;
73
73
  };
74
74
 
75
75
  const changeOption = (event): void => {
@@ -155,9 +155,9 @@ const TextField = forwardRef(
155
155
  helperText={displayErrorInTooltip ? undefined : error}
156
156
  id={getNormalizedId(dataTestId || '')}
157
157
  inputProps={{
158
- ...rest.inputProps,
159
158
  'aria-label': ariaLabel,
160
- 'data-testid': dataTestId
159
+ 'data-testid': dataTestId,
160
+ ...rest.inputProps
161
161
  }}
162
162
  label={label}
163
163
  ref={ref}
@@ -107,7 +107,21 @@ const DataCell = ({
107
107
  const isCellHidden = getHiddenCondition?.(isRowSelected);
108
108
 
109
109
  if (isCellHidden) {
110
- return null;
110
+ return (
111
+ <Cell
112
+ className={classes.cell}
113
+ isRowHighlighted={isRowHighlighted}
114
+ listingVariant={listingVariant}
115
+ onClick={(e): void => {
116
+ if (!clickable) {
117
+ return;
118
+ }
119
+ e.preventDefault();
120
+ e.stopPropagation();
121
+ }}
122
+ {...commonCellProps}
123
+ />
124
+ );
111
125
  }
112
126
 
113
127
  return (
@@ -71,7 +71,7 @@ const ListingHeader = ({
71
71
  });
72
72
 
73
73
  const getColumnById = (id: string): Column => {
74
- return find(propEq('id', id), columns) as Column;
74
+ return find(propEq(id, 'id'), columns) as Column;
75
75
  };
76
76
 
77
77
  const Content = React.useCallback(
@@ -77,9 +77,9 @@ const mountListing = (): void => {
77
77
  subItems={{
78
78
  canCheckSubItems: false,
79
79
  enable: true,
80
+ getRowProperty: () => 'subItems',
80
81
  labelCollapse: 'Collapse',
81
- labelExpand: 'Expand',
82
- rowProperty: 'subItems'
82
+ labelExpand: 'Expand'
83
83
  }}
84
84
  totalRows={10}
85
85
  />
@@ -9,13 +9,12 @@ const loadingIndicatorHeight = 3;
9
9
  interface StylesProps {
10
10
  dataStyle: TableStyle;
11
11
  getGridTemplateColumn: string;
12
- limit: number;
13
12
  listingVariant: ListingVariant;
14
13
  rows: Array<unknown>;
15
14
  }
16
15
 
17
16
  const useListingStyles = makeStyles<StylesProps>()(
18
- (theme, { dataStyle, getGridTemplateColumn, rows, limit }) => ({
17
+ (theme, { dataStyle, getGridTemplateColumn, rows }) => ({
19
18
  actionBar: {
20
19
  alignItems: 'center',
21
20
  display: 'flex'
@@ -56,7 +55,7 @@ const useListingStyles = makeStyles<StylesProps>()(
56
55
  display: 'grid',
57
56
  gridTemplateColumns: getGridTemplateColumn,
58
57
  gridTemplateRows: `${theme.spacing(dataStyle.header.height / 8)} repeat(${
59
- rows?.length || limit
58
+ rows?.length || 1
60
59
  }, ${dataStyle.body.height}px)`,
61
60
  position: 'relative'
62
61
  },
@@ -366,6 +366,16 @@ const columnsWithSubItems = [
366
366
  }
367
367
  ];
368
368
 
369
+ const TemplateSubItems = (args): JSX.Element => {
370
+ const { classes } = useStyles();
371
+
372
+ return (
373
+ <div className={classes.listing}>
374
+ <Listing {...args} />
375
+ </div>
376
+ );
377
+ };
378
+
369
379
  export const ListingWithSubItems = {
370
380
  args: {
371
381
  checkable: true,
@@ -377,10 +387,11 @@ export const ListingWithSubItems = {
377
387
  subItems: {
378
388
  canCheckSubItems: false,
379
389
  enable: true,
390
+ getRowProperty: () => 'subItems',
380
391
  labelCollapse: 'Collapse',
381
- labelExpand: 'Expand',
382
- rowProperty: 'subItems'
392
+ labelExpand: 'Expand'
383
393
  },
384
394
  totalRows: 10
385
- }
395
+ },
396
+ render: TemplateSubItems
386
397
  };
@@ -69,7 +69,7 @@ const getVisibleColumns = ({
69
69
  }
70
70
 
71
71
  return selectedColumnIds.map((id) =>
72
- columns.find(propEq('id', id))
72
+ columns.find(propEq(id, 'id'))
73
73
  ) as Array<Column>;
74
74
  };
75
75
 
@@ -121,9 +121,9 @@ export interface Props<TRow> {
121
121
  subItems?: {
122
122
  canCheckSubItems: boolean;
123
123
  enable: boolean;
124
+ getRowProperty: (row?) => string;
124
125
  labelCollapse: string;
125
126
  labelExpand: string;
126
- rowProperty: string;
127
127
  };
128
128
  totalRows?: number;
129
129
  viewerModeConfiguration?: ViewerModeConfiguration;
@@ -176,9 +176,9 @@ const Listing = <TRow extends { id: RowId }>({
176
176
  subItems = {
177
177
  canCheckSubItems: false,
178
178
  enable: false,
179
+ getRowProperty: () => '',
179
180
  labelCollapse: 'Collapse',
180
- labelExpand: 'Expand',
181
- rowProperty: ''
181
+ labelExpand: 'Expand'
182
182
  }
183
183
  }: Props<TRow>): JSX.Element => {
184
184
  const currentVisibleColumns = getVisibleColumns({
@@ -211,10 +211,10 @@ const Listing = <TRow extends { id: RowId }>({
211
211
  ? reduce<TRow, Array<TRow>>(
212
212
  (acc, row): Array<TRow> => {
213
213
  if (
214
- row[subItems.rowProperty] &&
214
+ row[subItems.getRowProperty()] &&
215
215
  subItemsPivots.includes(row.id)
216
216
  ) {
217
- return [...acc, row, ...row[subItems.rowProperty]];
217
+ return [...acc, row, ...row[subItems.getRowProperty()]];
218
218
  }
219
219
 
220
220
  return [...acc, row];
@@ -229,7 +229,6 @@ const Listing = <TRow extends { id: RowId }>({
229
229
  const { classes } = useListingStyles({
230
230
  dataStyle,
231
231
  getGridTemplateColumn,
232
- limit,
233
232
  listingVariant,
234
233
  rows: rowsToDisplay
235
234
  });
@@ -450,7 +449,7 @@ const Listing = <TRow extends { id: RowId }>({
450
449
  reduce<TRow | number, Array<string | number>>(
451
450
  (acc, row) => [
452
451
  ...acc,
453
- ...pluck('id', row[subItems?.rowProperty || ''] || [])
452
+ ...pluck('id', row[subItems?.getRowProperty() || ''] || [])
454
453
  ],
455
454
  [],
456
455
  rows
@@ -621,7 +620,7 @@ const Listing = <TRow extends { id: RowId }>({
621
620
  listingVariant={listingVariant}
622
621
  row={row}
623
622
  rowColorConditions={rowColorConditions}
624
- subItemsRowProperty={subItems?.rowProperty}
623
+ subItemsRowProperty={subItems?.getRowProperty(row)}
625
624
  />
626
625
  ))}
627
626
  </ListingRow>
@@ -0,0 +1,129 @@
1
+ import { createStore } from 'jotai';
2
+
3
+ import { Method } from '..';
4
+
5
+ import LicensedModule from './LicensedModule';
6
+
7
+ import Module from '.';
8
+
9
+ const initializeModule = (): void => {
10
+ cy.mount({
11
+ Component: (
12
+ <Module seedName="seed" store={createStore()}>
13
+ <p>Module</p>
14
+ </Module>
15
+ )
16
+ });
17
+ };
18
+
19
+ const initializeModuleWithValidLicense = (
20
+ isFederatedComponent = false
21
+ ): void => {
22
+ cy.interceptAPIRequest({
23
+ alias: 'getValidLicense',
24
+ method: Method.GET,
25
+ path: './api/internal.php?object=centreon_license_manager&action=licenseValid&productName=valid',
26
+ response: {
27
+ success: true
28
+ }
29
+ });
30
+
31
+ cy.mount({
32
+ Component: (
33
+ <div style={{ height: '100vh' }}>
34
+ <LicensedModule
35
+ isFederatedComponent={isFederatedComponent}
36
+ moduleName="valid"
37
+ seedName="seed"
38
+ store={createStore()}
39
+ >
40
+ <p>Module</p>
41
+ </LicensedModule>
42
+ </div>
43
+ )
44
+ });
45
+ };
46
+
47
+ const initializeModuleWithInvalidLicense = (
48
+ isFederatedComponent = false
49
+ ): void => {
50
+ cy.interceptAPIRequest({
51
+ alias: 'getInvalidLicense',
52
+ method: Method.GET,
53
+ path: './api/internal.php?object=centreon_license_manager&action=licenseValid&productName=invalid',
54
+ response: {
55
+ success: false
56
+ }
57
+ });
58
+
59
+ cy.mount({
60
+ Component: (
61
+ <div style={{ height: '100vh' }}>
62
+ <LicensedModule
63
+ isFederatedComponent={isFederatedComponent}
64
+ moduleName="invalid"
65
+ seedName="seed"
66
+ store={createStore()}
67
+ >
68
+ <p>Module</p>
69
+ </LicensedModule>
70
+ </div>
71
+ )
72
+ });
73
+ };
74
+
75
+ describe('Module', () => {
76
+ beforeEach(() => {
77
+ initializeModule();
78
+ });
79
+
80
+ it('displays the content of the module', () => {
81
+ cy.contains('Module').should('be.visible');
82
+
83
+ cy.makeSnapshot();
84
+ });
85
+ });
86
+
87
+ describe('Valid license module', () => {
88
+ it('displays the content of the page when the license is valid license', () => {
89
+ initializeModuleWithValidLicense();
90
+ cy.waitForRequest('@getValidLicense');
91
+
92
+ cy.contains('Module').should('be.visible');
93
+
94
+ cy.makeSnapshot();
95
+ });
96
+
97
+ it('displays the content of the component when the license is valid license', () => {
98
+ initializeModuleWithValidLicense(true);
99
+
100
+ cy.contains('Module').should('be.visible');
101
+
102
+ cy.makeSnapshot();
103
+ });
104
+ });
105
+
106
+ describe('Invalid license module', () => {
107
+ it('displays the content of the page when the license is invalid license', () => {
108
+ initializeModuleWithInvalidLicense();
109
+ cy.waitForRequest('@getInvalidLicense');
110
+
111
+ cy.contains('Module').should('not.exist');
112
+
113
+ cy.contains('Oops').should('be.visible');
114
+ cy.contains('License invalid or expired').should('be.visible');
115
+ cy.contains('Please contact your administrator.').should('be.visible');
116
+ cy.get('img[alt="License invalid or expired !"]').should('be.visible');
117
+ cy.get('img[alt="Centreon Logo"]').should('be.visible');
118
+
119
+ cy.makeSnapshot();
120
+ });
121
+
122
+ it('displays the content of the module when the license is invalid license', () => {
123
+ initializeModuleWithInvalidLicense(true);
124
+
125
+ cy.contains('Module').should('not.exist');
126
+
127
+ cy.makeSnapshot();
128
+ });
129
+ });
@@ -1,5 +1,3 @@
1
- import * as React from 'react';
2
-
3
1
  import { Provider as JotaiProvider, createStore } from 'jotai';
4
2
 
5
3
  import { createGenerateClassName, StylesProvider } from '@mui/styles';
@@ -9,7 +7,7 @@ import SnackbarProvider from '../Snackbar/SnackbarProvider';
9
7
 
10
8
  export interface ModuleProps {
11
9
  children: React.ReactElement;
12
- maxSnackbars: number;
10
+ maxSnackbars?: number;
13
11
  seedName: string;
14
12
  store: ReturnType<typeof createStore>;
15
13
  }
@@ -17,7 +15,7 @@ export interface ModuleProps {
17
15
  const Module = ({
18
16
  children,
19
17
  seedName,
20
- maxSnackbars,
18
+ maxSnackbars = 3,
21
19
  store
22
20
  }: ModuleProps): JSX.Element => {
23
21
  const generateClassName = createGenerateClassName({
@@ -12,6 +12,7 @@ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
12
12
  import { equals } from 'ramda';
13
13
  import { ListPlugin } from '@lexical/react/LexicalListPlugin';
14
14
  import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
15
+ import { $generateHtmlFromNodes } from '@lexical/html';
15
16
 
16
17
  import { Typography } from '@mui/material';
17
18
 
@@ -38,6 +39,7 @@ export interface RichTextEditorProps {
38
39
  openLinkInNewTab?: boolean;
39
40
  placeholder?: string;
40
41
  resetEditorToInitialStateCondition?: () => boolean;
42
+ setHtmlString?: (htmlString: string) => void;
41
43
  toolbarClassName?: string;
42
44
  toolbarPositions?: 'start' | 'end';
43
45
  }
@@ -143,6 +145,7 @@ const RichTextEditor = ({
143
145
  openLinkInNewTab = true,
144
146
  initialize,
145
147
  displayBlockButtons = true,
148
+ setHtmlString,
146
149
  toolbarClassName
147
150
  }: RichTextEditorProps): JSX.Element => {
148
151
  const { classes } = useStyles({ toolbarPositions });
@@ -178,6 +181,13 @@ const RichTextEditor = ({
178
181
  }
179
182
  };
180
183
 
184
+ const change = (state: EditorState, editor: LexicalEditor): void => {
185
+ editor.update(() => {
186
+ setHtmlString?.($generateHtmlFromNodes(editor, null));
187
+ });
188
+ getEditorState?.(state, editor);
189
+ };
190
+
181
191
  return (
182
192
  <LexicalComposer initialConfig={initialConfig}>
183
193
  <div className={classes.container}>
@@ -210,6 +220,7 @@ const RichTextEditor = ({
210
220
  resetEditorToInitialStateCondition={
211
221
  resetEditorToInitialStateCondition
212
222
  }
223
+ setHtmlString={setHtmlString}
213
224
  onBlur={onBlur}
214
225
  />
215
226
  }
@@ -218,7 +229,7 @@ const RichTextEditor = ({
218
229
  <HistoryPlugin />
219
230
  <LinkPlugin />
220
231
  <ListPlugin />
221
- <OnChangePlugin onChange={getEditorState} />
232
+ <OnChangePlugin onChange={change} />
222
233
  <AutoCompleteLinkPlugin openLinkInNewTab={openLinkInNewTab} />
223
234
  <FloatingLinkEditorPlugin
224
235
  editable={editable}
@@ -15,11 +15,7 @@ import {
15
15
  DragEndEvent,
16
16
  Over
17
17
  } from '@dnd-kit/core';
18
- import {
19
- SortableContext,
20
- sortableKeyboardCoordinates,
21
- SortingStrategy
22
- } from '@dnd-kit/sortable';
18
+ import { SortableContext, SortingStrategy } from '@dnd-kit/sortable';
23
19
  import {
24
20
  equals,
25
21
  find,
@@ -117,7 +113,6 @@ const SortableItems = <T extends { [propertyToFilterItemsOn]: string }>({
117
113
  useSensor(MouseSensor),
118
114
  useSensor(PointerSensor),
119
115
  useSensor(KeyboardSensor, {
120
- coordinateGetter: sortableKeyboardCoordinates,
121
116
  keyboardCodes: {
122
117
  cancel: ['Escape'],
123
118
  end: ['Space', 'Enter'],
@@ -169,7 +164,7 @@ const SortableItems = <T extends { [propertyToFilterItemsOn]: string }>({
169
164
  };
170
165
 
171
166
  const getItemById = (id): T | undefined =>
172
- find(propEq(propertyToFilterItemsOn, id), items);
167
+ find(propEq(id, propertyToFilterItemsOn), items);
173
168
 
174
169
  const activeItem = getItemById(activeId) as Record<string, unknown>;
175
170
 
@@ -147,6 +147,30 @@ export const getTheme = (mode: ThemeMode): ThemeOptions => ({
147
147
  },
148
148
  MuiCssBaseline: {
149
149
  styleOverrides: (theme) => `
150
+ ::-webkit-scrollbar {
151
+ height: ${theme.spacing(1)};
152
+ width: ${theme.spacing(1)};
153
+ background-color: transparent;
154
+ }
155
+ ::-webkit-scrollbar-thumb {
156
+ background-color: ${
157
+ equals(mode, 'dark')
158
+ ? theme.palette.divider
159
+ : theme.palette.text.disabled
160
+ };
161
+ border-radius: ${theme.spacing(0.5)};
162
+ }
163
+ ::-webkit-scrollbar-thumb:hover {
164
+ background-color: ${theme.palette.primary.main};
165
+ }
166
+ * {
167
+ scrollbar-color: ${
168
+ equals(mode, 'dark')
169
+ ? theme.palette.divider
170
+ : theme.palette.text.disabled
171
+ } ${theme.palette.background.default};
172
+ scrollbar-width: thin;
173
+ }
150
174
  html {
151
175
  margin: 0;
152
176
  padding: 0;
@@ -2,12 +2,15 @@ import { useAtomValue } from 'jotai';
2
2
  import { makeStyles } from 'tss-react/mui';
3
3
  import { equals } from 'ramda';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import dayjs from 'dayjs';
6
+ import utc from 'dayjs/plugin/utc';
7
+ import timezone from 'dayjs/plugin/timezone';
5
8
 
9
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
6
10
  import { Typography } from '@mui/material';
7
11
  import { LocalizationProvider } from '@mui/x-date-pickers';
8
12
 
9
13
  import { userAtom } from '@centreon/ui-context';
10
- import { useDateTimePickerAdapter } from '@centreon/ui';
11
14
 
12
15
  import DateTimePickerInput from '../../DateTimePickerInput';
13
16
  import {
@@ -20,6 +23,9 @@ import ErrorText from './ErrorText';
20
23
  import { PickersData, PickersStartEndDateDirection } from './models';
21
24
  import { PickersStartEndDateModel } from './usePickersStartEndDate';
22
25
 
26
+ dayjs.extend(utc);
27
+ dayjs.extend(timezone);
28
+
23
29
  const useStyles = makeStyles()((theme) => ({
24
30
  error: {
25
31
  textAlign: 'center'
@@ -104,7 +110,6 @@ const PickersStartEndDate = ({
104
110
  direction = PickersStartEndDateDirection.column
105
111
  }: Props): JSX.Element => {
106
112
  const { classes, cx } = useStyles();
107
- const { Adapter } = useDateTimePickerAdapter();
108
113
 
109
114
  const { locale } = useAtomValue(userAtom);
110
115
  const error = useAtomValue(errorTimePeriodAtom);
@@ -126,7 +131,7 @@ const PickersStartEndDate = ({
126
131
  return (
127
132
  <LocalizationProvider
128
133
  adapterLocale={locale.substring(0, 2)}
129
- dateAdapter={Adapter}
134
+ dateAdapter={AdapterDayjs}
130
135
  >
131
136
  <div className={styleContainer}>
132
137
  <PickerDateWithLabel
@@ -18,8 +18,6 @@ export interface PickersData {
18
18
  getError?: (value: boolean) => void;
19
19
  isDisabledEndPicker?: boolean;
20
20
  isDisabledStartPicker?: boolean;
21
- onCloseEndPicker?: (isClosed: boolean) => void;
22
- onCloseStartPicker?: (isClosed: boolean) => void;
23
21
  rangeEndDate?: RangeDate;
24
22
  rangeStartDate?: RangeDate;
25
23
  }
@@ -1,48 +1,85 @@
1
- import dayjs from 'dayjs';
1
+ import { useCallback } from 'react';
2
2
 
3
- import { DateTimePicker } from '@mui/x-date-pickers';
3
+ import dayjs, { Dayjs } from 'dayjs';
4
+ import { useAtomValue } from 'jotai';
5
+ import { equals } from 'ramda';
4
6
 
5
- import { useDateTimePickerAdapter } from '@centreon/ui';
7
+ import {
8
+ DateTimePicker,
9
+ DateTimePickerProps,
10
+ LocalizationProvider
11
+ } from '@mui/x-date-pickers';
12
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
13
+
14
+ import { userAtom } from '@centreon/ui-context';
6
15
 
7
16
  import { CustomTimePeriodProperty } from './models';
8
17
 
18
+ interface ChangeDateProps {
19
+ date: Date;
20
+ property: CustomTimePeriodProperty | string;
21
+ }
22
+
9
23
  interface Props {
10
- changeDate: (props) => void;
24
+ changeDate: (props: ChangeDateProps) => void;
11
25
  date: Date | null;
12
26
  desktopMediaQuery?: string;
13
27
  disabled?: boolean;
14
28
  maxDate?: Date;
15
29
  minDate?: Date;
16
- property: CustomTimePeriodProperty;
30
+ minDateTime?: Date;
31
+ property: CustomTimePeriodProperty | string;
17
32
  }
18
33
 
19
34
  const DateTimePickerInput = ({
20
35
  date,
21
36
  maxDate,
22
37
  minDate,
38
+ minDateTime,
23
39
  property,
24
40
  changeDate,
25
41
  disabled = false,
26
- desktopMediaQuery
27
- }: Props): JSX.Element => {
28
- const { desktopPickerMediaQuery } = useDateTimePickerAdapter();
42
+ desktopMediaQuery,
43
+ ...rest
44
+ }: Props & DateTimePickerProps<dayjs.Dayjs>): JSX.Element => {
45
+ const desktopPickerMediaQuery =
46
+ '@media (min-width: 1024px) or (pointer: fine)';
47
+
48
+ const { timezone, locale } = useAtomValue(userAtom);
49
+
50
+ const isUTC = equals(timezone, 'UTC');
29
51
 
30
52
  const changeTime = (newValue: dayjs.Dayjs | null): void => {
31
53
  changeDate({ date: dayjs(newValue).toDate(), property });
32
54
  };
33
55
 
56
+ const formatDate = useCallback(
57
+ (currentDate: Date | null): Dayjs => {
58
+ return isUTC ? dayjs.utc(currentDate) : dayjs.tz(currentDate, timezone);
59
+ },
60
+ [isUTC, timezone]
61
+ );
62
+
34
63
  return (
35
- <DateTimePicker<dayjs.Dayjs>
36
- dayOfWeekFormatter={(day: string): string =>
37
- day.substring(0, 2).toUpperCase()
38
- }
39
- desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
40
- disabled={disabled}
41
- maxDate={maxDate && dayjs(maxDate)}
42
- minDate={minDate && dayjs(minDate)}
43
- value={dayjs(date)}
44
- onChange={changeTime}
45
- />
64
+ <LocalizationProvider
65
+ adapterLocale={locale.substring(0, 2)}
66
+ dateAdapter={AdapterDayjs}
67
+ dateLibInstance={dayjs}
68
+ >
69
+ <DateTimePicker<dayjs.Dayjs>
70
+ dayOfWeekFormatter={(dayOfWeek) =>
71
+ dayOfWeek.substring(0, 2).toLocaleUpperCase()
72
+ }
73
+ desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
74
+ disabled={disabled}
75
+ maxDate={maxDate && formatDate(maxDate)}
76
+ minDate={minDate && formatDate(minDate)}
77
+ minDateTime={minDateTime && formatDate(minDateTime)}
78
+ value={formatDate(date)}
79
+ onChange={changeTime}
80
+ {...rest}
81
+ />
82
+ </LocalizationProvider>
46
83
  );
47
84
  };
48
85