@centreon/ui 24.4.72 → 24.4.74
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 +24 -23
- package/src/Checkbox/fonts/roboto-bold-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-bold-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-bold-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.woff2 +0 -0
- package/src/Dashboard/Item.tsx +2 -11
- package/src/Dashboard/Layout.tsx +2 -4
- package/src/Dashboard/utils.ts +1 -1
- package/src/Form/Inputs/Grid.tsx +8 -4
- package/src/Form/Inputs/models.ts +15 -14
- package/src/Graph/BarStack/BarStack.cypress.spec.tsx +87 -9
- package/src/Graph/BarStack/BarStack.stories.tsx +4 -2
- package/src/Graph/BarStack/BarStack.styles.ts +59 -30
- package/src/Graph/BarStack/Graph.tsx +176 -0
- package/src/Graph/BarStack/GraphAndLegend.tsx +119 -0
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +62 -157
- package/src/Graph/BarStack/constants.ts +5 -0
- package/src/Graph/BarStack/models.ts +1 -1
- package/src/Graph/BarStack/useGraphAndLegend.ts +86 -0
- package/src/Graph/BarStack/useResponsiveBarStack.ts +74 -99
- package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +19 -2
- package/src/Graph/HeatMap/model.ts +5 -1
- package/src/Graph/Legend/Legend.styles.ts +10 -0
- package/src/Graph/Legend/Legend.tsx +6 -1
- package/src/Graph/LineChart/BasicComponents/Lines/RegularLines/index.tsx +2 -1
- package/src/Graph/LineChart/BasicComponents/Lines/StackedLines/index.tsx +3 -4
- package/src/Graph/LineChart/Icons/Downtime.tsx +3 -3
- package/src/Graph/LineChart/InteractiveComponents/ZoomPreview/index.tsx +2 -1
- package/src/Graph/LineChart/Legend/Legend.styles.ts +16 -5
- package/src/Graph/LineChart/Legend/LegendHeader.tsx +4 -1
- package/src/Graph/LineChart/Legend/index.tsx +12 -5
- package/src/Graph/LineChart/LineChart.cypress.spec.tsx +53 -0
- package/src/Graph/LineChart/LineChart.tsx +10 -9
- package/src/Graph/LineChart/index.stories.tsx +13 -0
- package/src/Graph/LineChart/mockedData/curvesWithSameColor.json +252 -0
- package/src/Graph/LineChart/useLineChartData.ts +68 -18
- package/src/Graph/PieChart/ResponsivePie.tsx +3 -1
- package/src/Graph/PieChart/models.ts +1 -0
- package/src/Graph/Tree/DescendantNodes.tsx +0 -1
- package/src/Graph/Tree/Links.tsx +2 -15
- package/src/Graph/Tree/Tree.cypress.spec.tsx +0 -24
- package/src/Graph/Tree/Tree.stories.tsx +1 -17
- package/src/Graph/Tree/models.ts +0 -3
- package/src/Graph/common/utils.ts +51 -2
- package/src/Icon/BaseIcon.tsx +32 -0
- package/src/Icon/DowntimeIcon.tsx +14 -0
- package/src/InputField/Select/Autocomplete/Connected/Multi/index.test.tsx +21 -1
- package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +2 -2
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
- package/src/InputField/Select/Autocomplete/Multi/index.stories.tsx +19 -0
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +8 -5
- package/src/InputField/Text/index.tsx +7 -5
- package/src/Listing/ActionBar/index.tsx +1 -0
- package/src/PopoverMenu/index.tsx +4 -4
- package/src/RichTextEditor/ContentEditable.tsx +195 -195
- package/src/StoryBookThemeProvider/index.tsx +35 -36
- package/src/ThemeProvider/index.tsx +12 -12
- package/src/ThemeProvider/palettes.ts +11 -8
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +2 -3
- package/src/TimePeriods/DateTimePickerInput.tsx +4 -1
- package/src/TopCounterElements/TopCounterLayout.tsx +4 -3
- package/src/TopCounterElements/useCloseOnLegacyPage.tsx +9 -6
- package/src/api/buildListingEndpoint/getSearchQueryParameterValue.ts +7 -1
- package/src/api/buildListingEndpoint/models.ts +1 -0
- package/src/api/useGraphQuery/index.ts +1 -7
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -27
- package/src/components/Form/AccessRights/AccessRights.stories.tsx +19 -0
- package/src/components/Form/AccessRights/AccessRights.styles.ts +1 -1
- package/src/components/Form/AccessRights/AccessRights.tsx +5 -6
- package/src/components/Form/AccessRights/Actions/Actions.styles.ts +7 -3
- package/src/components/Form/AccessRights/Actions/Actions.tsx +32 -15
- package/src/components/Form/AccessRights/Actions/useActions.ts +37 -4
- package/src/components/Form/AccessRights/models.ts +3 -0
- package/src/components/Form/AccessRights/storiesData.ts +3 -0
- package/src/components/List/Item/ListItem.styles.ts +2 -2
- package/src/components/Zoom/Minimap.tsx +2 -4
- package/src/components/Zoom/Zoom.cypress.spec.tsx +13 -13
- package/src/components/Zoom/Zoom.tsx +1 -4
- package/src/components/Zoom/ZoomContent.tsx +2 -5
- package/src/components/index.ts +0 -1
- package/src/fonts/roboto-bold-webfont.ttf +0 -0
- package/src/fonts/roboto-bold-webfont.woff +0 -0
- package/src/fonts/roboto-bold-webfont.woff2 +0 -0
- package/src/fonts/roboto-light-webfont.ttf +0 -0
- package/src/fonts/roboto-light-webfont.woff +0 -0
- package/src/fonts/roboto-light-webfont.woff2 +0 -0
- package/src/fonts/roboto-medium-webfont.ttf +0 -0
- package/src/fonts/roboto-medium-webfont.woff +0 -0
- package/src/fonts/roboto-medium-webfont.woff2 +0 -0
- package/src/fonts/roboto-regular-webfont.ttf +0 -0
- package/src/fonts/roboto-regular-webfont.woff +0 -0
- package/src/fonts/roboto-regular-webfont.woff2 +0 -0
- package/src/index.ts +1 -0
- package/src/utils/index.ts +25 -25
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +3 -0
- package/src/utils/useInfiniteScrollListing.ts +6 -1
- package/src/utils/useLocale/index.ts +10 -0
- package/src/utils/useLocale/useLocale.cypress.spec.tsx +40 -0
- package/src/utils/useLocaleDateTimeFormat/index.ts +5 -2
- package/src/utils/usePluralizedTranslation.ts +4 -21
- package/src/@assets/icons/downtime.icon.svg +0 -1
- package/src/components/Form/AccessRights/useAccessRightsChange.ts +0 -30
- package/src/components/Form/AccessRights/utils.ts +0 -18
- package/src/components/Tabs/Tab.styles.ts +0 -25
- package/src/components/Tabs/TabPanel.tsx +0 -22
- package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -70
- package/src/components/Tabs/Tabs.stories.tsx +0 -55
- package/src/components/Tabs/Tabs.tsx +0 -55
- package/src/components/Tabs/index.ts +0 -6
- package/src/utils/resourcesStatusURL.ts +0 -166
- package/src/utils/usePluralizedTranslation.test.ts +0 -159
- /package/{public → src/Button}/fonts/roboto-bold-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-bold-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-bold-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.woff2 +0 -0
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactNode, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import { useAtomValue } from 'jotai';
|
|
4
|
-
import { CSSInterpolation } from 'tss-react';
|
|
5
4
|
import { equals } from 'ramda';
|
|
5
|
+
import { CSSInterpolation } from 'tss-react';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
ButtonProps,
|
|
9
|
-
createTheme,
|
|
10
9
|
InputBaseProps,
|
|
10
|
+
ThemeProvider as MuiThemeProvider,
|
|
11
11
|
StyledEngineProvider,
|
|
12
12
|
Theme,
|
|
13
|
-
|
|
13
|
+
createTheme
|
|
14
14
|
} from '@mui/material';
|
|
15
|
-
import CssBaseline from '@mui/material/CssBaseline';
|
|
16
15
|
import { autocompleteClasses } from '@mui/material/Autocomplete';
|
|
16
|
+
import CssBaseline from '@mui/material/CssBaseline';
|
|
17
17
|
import { ThemeOptions } from '@mui/material/styles/createTheme';
|
|
18
18
|
|
|
19
19
|
import { ThemeMode, userAtom } from '@centreon/ui-context';
|
|
20
|
-
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
20
|
+
|
|
21
|
+
import RobotoBoldWoff2 from '../fonts/roboto-bold-webfont.woff2';
|
|
22
|
+
import RobotoLightWoff2 from '../fonts/roboto-light-webfont.woff2';
|
|
23
|
+
import RobotoMediumWoff2 from '../fonts/roboto-medium-webfont.woff2';
|
|
24
|
+
import RobotoRegularWoff2 from '../fonts/roboto-regular-webfont.woff2';
|
|
24
25
|
|
|
25
26
|
import { getPalette } from './palettes';
|
|
26
27
|
|
|
27
28
|
declare module '@mui/styles/defaultTheme' {
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
29
29
|
interface DefaultTheme extends Theme {}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -293,13 +293,13 @@ export const getTheme = (mode: ThemeMode): ThemeOptions => ({
|
|
|
293
293
|
});
|
|
294
294
|
|
|
295
295
|
interface Props {
|
|
296
|
-
children:
|
|
296
|
+
children: ReactNode;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
const ThemeProvider = ({ children }: Props): JSX.Element => {
|
|
300
300
|
const { themeMode } = useAtomValue(userAtom);
|
|
301
301
|
|
|
302
|
-
const theme =
|
|
302
|
+
const theme = useMemo(
|
|
303
303
|
() => createTheme(getTheme(themeMode || ThemeMode.light)),
|
|
304
304
|
[themeMode]
|
|
305
305
|
);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { equals } from 'ramda';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type PaletteOptions as PaletteOptionsModel,
|
|
5
|
+
alpha
|
|
6
|
+
} from '@mui/material';
|
|
4
7
|
|
|
5
8
|
import { ThemeMode } from '@centreon/ui-context';
|
|
6
9
|
|
|
@@ -140,9 +143,9 @@ declare module '@mui/material/Badge' {
|
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
|
|
143
|
-
export const lightPalette:
|
|
146
|
+
export const lightPalette: PaletteOptionsModel = {
|
|
144
147
|
action: {
|
|
145
|
-
acknowledged: '#
|
|
148
|
+
acknowledged: '#745F35',
|
|
146
149
|
acknowledgedBackground: '#DFD2B9',
|
|
147
150
|
activatedOpacity: 0.12,
|
|
148
151
|
active: '#666666',
|
|
@@ -152,7 +155,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
152
155
|
focusOpacity: 0.12,
|
|
153
156
|
hover: 'rgba(0, 0, 0, 0.06)',
|
|
154
157
|
hoverOpacity: 0.06,
|
|
155
|
-
inDowntime: '#
|
|
158
|
+
inDowntime: '#512980',
|
|
156
159
|
inDowntimeBackground: '#E5D8F3',
|
|
157
160
|
selected: 'rgba(102, 102, 102, 0.3)',
|
|
158
161
|
selectedOpacity: 0.3
|
|
@@ -280,9 +283,9 @@ export const lightPalette: PaletteOptions = {
|
|
|
280
283
|
}
|
|
281
284
|
};
|
|
282
285
|
|
|
283
|
-
export const darkPalette:
|
|
286
|
+
export const darkPalette: PaletteOptionsModel = {
|
|
284
287
|
action: {
|
|
285
|
-
acknowledged: '#
|
|
288
|
+
acknowledged: '#DFD2B9',
|
|
286
289
|
acknowledgedBackground: '#745F35',
|
|
287
290
|
activatedOpacity: 0.3,
|
|
288
291
|
active: '#B5B5B5',
|
|
@@ -292,7 +295,7 @@ export const darkPalette: PaletteOptions = {
|
|
|
292
295
|
focusOpacity: 0.3,
|
|
293
296
|
hover: 'rgba(255, 255, 255, 0.16)',
|
|
294
297
|
hoverOpacity: 0.16,
|
|
295
|
-
inDowntime: '#
|
|
298
|
+
inDowntime: '#E5D8F3',
|
|
296
299
|
inDowntimeBackground: '#512980',
|
|
297
300
|
selected: 'rgba(255, 255, 255, 0.5)',
|
|
298
301
|
selectedOpacity: 0.5
|
|
@@ -418,5 +421,5 @@ export const darkPalette: PaletteOptions = {
|
|
|
418
421
|
}
|
|
419
422
|
};
|
|
420
423
|
|
|
421
|
-
export const getPalette = (mode: ThemeMode):
|
|
424
|
+
export const getPalette = (mode: ThemeMode): PaletteOptionsModel =>
|
|
422
425
|
equals(mode, ThemeMode.dark) ? darkPalette : lightPalette;
|
|
@@ -10,14 +10,13 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
|
10
10
|
import { Typography } from '@mui/material';
|
|
11
11
|
import { LocalizationProvider } from '@mui/x-date-pickers';
|
|
12
12
|
|
|
13
|
-
import { userAtom } from '@centreon/ui-context';
|
|
14
|
-
|
|
15
13
|
import DateTimePickerInput from '../../DateTimePickerInput';
|
|
16
14
|
import {
|
|
17
15
|
CustomTimePeriodProperty,
|
|
18
16
|
DateTimePickerInputModel
|
|
19
17
|
} from '../../models';
|
|
20
18
|
import { errorTimePeriodAtom } from '../../timePeriodsAtoms';
|
|
19
|
+
import { useLocale } from '../../../utils';
|
|
21
20
|
|
|
22
21
|
import ErrorText from './ErrorText';
|
|
23
22
|
import { PickersData, PickersStartEndDateDirection } from './models';
|
|
@@ -111,7 +110,7 @@ const PickersStartEndDate = ({
|
|
|
111
110
|
}: Props): JSX.Element => {
|
|
112
111
|
const { classes, cx } = useStyles();
|
|
113
112
|
|
|
114
|
-
const
|
|
113
|
+
const locale = useLocale();
|
|
115
114
|
const error = useAtomValue(errorTimePeriodAtom);
|
|
116
115
|
|
|
117
116
|
const maxStart = rangeStartDate?.max || endDate;
|
|
@@ -13,6 +13,8 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
|
13
13
|
|
|
14
14
|
import { userAtom } from '@centreon/ui-context';
|
|
15
15
|
|
|
16
|
+
import { useLocale } from '../utils';
|
|
17
|
+
|
|
16
18
|
import { CustomTimePeriodProperty } from './models';
|
|
17
19
|
|
|
18
20
|
interface ChangeDateProps {
|
|
@@ -46,6 +48,7 @@ const DateTimePickerInput = ({
|
|
|
46
48
|
'@media (min-width: 1024px) or (pointer: fine)';
|
|
47
49
|
|
|
48
50
|
const { timezone, locale } = useAtomValue(userAtom);
|
|
51
|
+
const localeToUse = useLocale();
|
|
49
52
|
|
|
50
53
|
const isUTC = equals(timezone, 'UTC');
|
|
51
54
|
|
|
@@ -62,7 +65,7 @@ const DateTimePickerInput = ({
|
|
|
62
65
|
|
|
63
66
|
return (
|
|
64
67
|
<LocalizationProvider
|
|
65
|
-
adapterLocale={locale.substring(0, 2)}
|
|
68
|
+
adapterLocale={(locale ?? localeToUse).substring(0, 2)}
|
|
66
69
|
dateAdapter={AdapterDayjs}
|
|
67
70
|
dateLibInstance={dayjs}
|
|
68
71
|
>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
|
|
5
|
-
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
|
6
5
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
7
|
-
import
|
|
6
|
+
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
|
8
7
|
import { Badge, ClickAwayListener } from '@mui/material';
|
|
8
|
+
import type { SvgIcon } from '@mui/material';
|
|
9
9
|
|
|
10
10
|
import useCloseOnLegacyPage from './useCloseOnLegacyPage';
|
|
11
11
|
|
|
@@ -115,6 +115,7 @@ const TopCounterLayout = ({
|
|
|
115
115
|
}: TopCounterLayoutProps): JSX.Element => {
|
|
116
116
|
const { classes, cx } = useStyles();
|
|
117
117
|
const [toggled, setToggled] = useState(false);
|
|
118
|
+
|
|
118
119
|
const subMenuId = title.replace(/[^A-Za-z]/, '-');
|
|
119
120
|
useCloseOnLegacyPage({ setToggled });
|
|
120
121
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
+
import { isNil } from 'ramda';
|
|
3
4
|
import { useLocation } from 'react-router-dom';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -14,21 +15,23 @@ const useCloseOnLegacyPage = ({ setToggled }: Props): void => {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
useEffect(() => {
|
|
17
|
-
const iframe = document.getElementById(
|
|
18
|
+
const iframe = document.getElementById(
|
|
19
|
+
'main-content'
|
|
20
|
+
) as HTMLIFrameElement | null;
|
|
18
21
|
|
|
19
|
-
if (!isLegacyRoute) {
|
|
22
|
+
if (!isLegacyRoute || isNil(iframe)) {
|
|
20
23
|
return () => undefined;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
const closeSubMenuOnLegacyPage = (): void => {
|
|
24
|
-
iframe
|
|
27
|
+
iframe?.contentWindow?.document?.addEventListener('click', closeSubMenu);
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
iframe
|
|
30
|
+
iframe?.addEventListener('load', closeSubMenuOnLegacyPage);
|
|
28
31
|
|
|
29
32
|
return () => {
|
|
30
|
-
iframe
|
|
31
|
-
iframe
|
|
33
|
+
iframe?.removeEventListener('load', closeSubMenuOnLegacyPage);
|
|
34
|
+
iframe?.contentWindow?.document?.removeEventListener(
|
|
32
35
|
'click',
|
|
33
36
|
closeSubMenu
|
|
34
37
|
);
|
|
@@ -93,8 +93,14 @@ const getConditionsSearchQueryParameterValue = (
|
|
|
93
93
|
equals(listField, field)
|
|
94
94
|
);
|
|
95
95
|
|
|
96
|
+
const globalOperator = filteredItems.every(({ operator }) =>
|
|
97
|
+
equals(operator, filteredItems[0].operator)
|
|
98
|
+
)
|
|
99
|
+
? filteredItems[0].operator || '$or'
|
|
100
|
+
: '$or';
|
|
101
|
+
|
|
96
102
|
return {
|
|
97
|
-
|
|
103
|
+
[globalOperator]: flatten(
|
|
98
104
|
filteredItems.map(({ value, values }) => {
|
|
99
105
|
if (!isNil(value)) {
|
|
100
106
|
return [
|
|
@@ -24,7 +24,6 @@ interface CustomTimePeriod {
|
|
|
24
24
|
interface UseMetricsQueryProps {
|
|
25
25
|
baseEndpoint: string;
|
|
26
26
|
bypassMetricsExclusion?: boolean;
|
|
27
|
-
bypassQueryParams?: boolean;
|
|
28
27
|
includeAllResources?: boolean;
|
|
29
28
|
metrics: Array<Metric>;
|
|
30
29
|
refreshCount?: number;
|
|
@@ -91,8 +90,7 @@ const useGraphQuery = ({
|
|
|
91
90
|
timePeriodType: 1
|
|
92
91
|
},
|
|
93
92
|
refreshInterval = false,
|
|
94
|
-
refreshCount
|
|
95
|
-
bypassQueryParams = false
|
|
93
|
+
refreshCount
|
|
96
94
|
}: UseMetricsQueryProps): UseMetricsQueryState => {
|
|
97
95
|
const timePeriodToUse = equals(timePeriod?.timePeriodType, -1)
|
|
98
96
|
? {
|
|
@@ -116,10 +114,6 @@ const useGraphQuery = ({
|
|
|
116
114
|
isLoading
|
|
117
115
|
} = useFetchQuery<PerformanceGraphData>({
|
|
118
116
|
getEndpoint: () => {
|
|
119
|
-
if (bypassQueryParams) {
|
|
120
|
-
return baseEndpoint;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
117
|
const endpoint = buildListingEndpoint({
|
|
124
118
|
baseEndpoint,
|
|
125
119
|
parameters: {
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
|
|
16
16
|
const initialize = ({
|
|
17
17
|
initialValues = simpleAccessRights,
|
|
18
|
-
loading = false
|
|
18
|
+
loading = false,
|
|
19
|
+
link = 'link'
|
|
19
20
|
}): unknown => {
|
|
20
21
|
const cancel = cy.stub();
|
|
21
22
|
const save = cy.stub();
|
|
22
|
-
const change = cy.stub();
|
|
23
23
|
|
|
24
24
|
cy.interceptAPIRequest({
|
|
25
25
|
alias: 'getContacts',
|
|
@@ -47,10 +47,10 @@ const initialize = ({
|
|
|
47
47
|
}}
|
|
48
48
|
initialValues={initialValues}
|
|
49
49
|
labels={labels}
|
|
50
|
+
link={link}
|
|
50
51
|
loading={loading}
|
|
51
52
|
roles={roles}
|
|
52
53
|
submit={save}
|
|
53
|
-
onChange={change}
|
|
54
54
|
/>
|
|
55
55
|
</Provider>
|
|
56
56
|
</TestQueryProvider>
|
|
@@ -60,7 +60,6 @@ const initialize = ({
|
|
|
60
60
|
|
|
61
61
|
return {
|
|
62
62
|
cancel,
|
|
63
|
-
change,
|
|
64
63
|
save
|
|
65
64
|
};
|
|
66
65
|
};
|
|
@@ -75,12 +74,21 @@ describe('Access rights', () => {
|
|
|
75
74
|
cy.findByLabelText('Add a contact').should('be.visible');
|
|
76
75
|
cy.findByTestId('add_role').should('be.disabled');
|
|
77
76
|
cy.findByTestId('add').should('be.disabled');
|
|
77
|
+
cy.findByLabelText('Copy link').should('be.visible');
|
|
78
78
|
cy.findByLabelText('Cancel').should('be.visible');
|
|
79
79
|
cy.findByLabelText('Save').should('be.visible');
|
|
80
80
|
|
|
81
81
|
cy.makeSnapshot();
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
it('displays the access rights without link', () => {
|
|
85
|
+
initialize({ link: null });
|
|
86
|
+
|
|
87
|
+
cy.findByLabelText('Copy link').should('not.exist');
|
|
88
|
+
|
|
89
|
+
cy.makeSnapshot();
|
|
90
|
+
});
|
|
91
|
+
|
|
84
92
|
it('displays the access rights with an empty list', () => {
|
|
85
93
|
initialize({ initialValues: emptyAccessRights });
|
|
86
94
|
|
|
@@ -89,7 +97,7 @@ describe('Access rights', () => {
|
|
|
89
97
|
cy.makeSnapshot();
|
|
90
98
|
});
|
|
91
99
|
|
|
92
|
-
it('displays the access rights list', () => {
|
|
100
|
+
it('displays the access rights with an empty list', () => {
|
|
93
101
|
initialize({});
|
|
94
102
|
|
|
95
103
|
simpleAccessRights.forEach(({ name, email, isContactGroup, role }) => {
|
|
@@ -362,26 +370,4 @@ describe('Access rights', () => {
|
|
|
362
370
|
|
|
363
371
|
cy.makeSnapshot();
|
|
364
372
|
});
|
|
365
|
-
|
|
366
|
-
it('calls the change function when the corresponding prop is set and the form is updated', () => {
|
|
367
|
-
const { change } = initialize({});
|
|
368
|
-
|
|
369
|
-
cy.contains(labels.add.contact).click();
|
|
370
|
-
cy.findByLabelText(labels.add.autocompleteContact).click();
|
|
371
|
-
|
|
372
|
-
cy.waitForRequest('@getContacts');
|
|
373
|
-
|
|
374
|
-
cy.contains('Entity 10').click();
|
|
375
|
-
|
|
376
|
-
cy.findByTestId('add').click();
|
|
377
|
-
|
|
378
|
-
cy.contains('Entity 10').should('be.visible');
|
|
379
|
-
|
|
380
|
-
cy.findByTestId('role-Entity 10').should('have.value', 'viewer');
|
|
381
|
-
cy.contains(labels.list.added)
|
|
382
|
-
.should('be.visible')
|
|
383
|
-
.then(() => {
|
|
384
|
-
expect(change).to.have.callCount(2);
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
373
|
});
|
|
@@ -47,6 +47,7 @@ export const Default: Story = {
|
|
|
47
47
|
},
|
|
48
48
|
initialValues: defaultAccessRights,
|
|
49
49
|
labels,
|
|
50
|
+
link: 'link',
|
|
50
51
|
roles,
|
|
51
52
|
submit: () => undefined
|
|
52
53
|
},
|
|
@@ -62,6 +63,7 @@ export const AccessRightsWithStates: Story = {
|
|
|
62
63
|
},
|
|
63
64
|
initialValues: accessRightsWithStates,
|
|
64
65
|
labels,
|
|
66
|
+
link: 'link',
|
|
65
67
|
roles,
|
|
66
68
|
submit: () => undefined
|
|
67
69
|
},
|
|
@@ -77,6 +79,22 @@ export const withEmptyState: Story = {
|
|
|
77
79
|
},
|
|
78
80
|
initialValues: emptyAccessRights,
|
|
79
81
|
labels,
|
|
82
|
+
link: 'link',
|
|
83
|
+
roles,
|
|
84
|
+
submit: () => undefined
|
|
85
|
+
},
|
|
86
|
+
render: Template
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const withoutLink: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
cancel: () => undefined,
|
|
92
|
+
endpoints: {
|
|
93
|
+
contact: '/contact',
|
|
94
|
+
contactGroup: '/contactGroup'
|
|
95
|
+
},
|
|
96
|
+
initialValues: defaultAccessRights,
|
|
97
|
+
labels,
|
|
80
98
|
roles,
|
|
81
99
|
submit: () => undefined
|
|
82
100
|
},
|
|
@@ -92,6 +110,7 @@ export const loading: Story = {
|
|
|
92
110
|
},
|
|
93
111
|
initialValues: emptyAccessRights,
|
|
94
112
|
labels,
|
|
113
|
+
link: 'link',
|
|
95
114
|
loading: true,
|
|
96
115
|
roles,
|
|
97
116
|
submit: () => undefined
|
|
@@ -8,17 +8,16 @@ import Provider from './Provider';
|
|
|
8
8
|
import ShareInput from './ShareInput/ShareInput';
|
|
9
9
|
import Stats from './Stats/Stats';
|
|
10
10
|
import { AccessRightInitialValues, Endpoints, Labels } from './models';
|
|
11
|
-
import { useAccessRightsChange } from './useAccessRightsChange';
|
|
12
11
|
import { useAccessRightsInitValues } from './useAccessRightsInitValues';
|
|
13
12
|
|
|
14
13
|
interface Props {
|
|
15
|
-
cancel
|
|
14
|
+
cancel: ({ dirty, values }) => void;
|
|
16
15
|
endpoints: Endpoints;
|
|
17
16
|
initialValues: Array<AccessRightInitialValues>;
|
|
18
17
|
isSubmitting?: boolean;
|
|
19
18
|
labels: Labels;
|
|
19
|
+
link?: string;
|
|
20
20
|
loading?: boolean;
|
|
21
|
-
onChange?: (values: Array<AccessRightInitialValues>) => void;
|
|
22
21
|
roles: Array<SelectEntry>;
|
|
23
22
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
24
23
|
}
|
|
@@ -29,14 +28,13 @@ export const AccessRights = ({
|
|
|
29
28
|
endpoints,
|
|
30
29
|
submit,
|
|
31
30
|
cancel,
|
|
31
|
+
link,
|
|
32
32
|
loading,
|
|
33
33
|
labels,
|
|
34
|
-
isSubmitting
|
|
35
|
-
onChange
|
|
34
|
+
isSubmitting
|
|
36
35
|
}: Props): JSX.Element => {
|
|
37
36
|
const { classes } = useAccessRightsStyles();
|
|
38
37
|
const clear = useAccessRightsInitValues({ initialValues });
|
|
39
|
-
useAccessRightsChange(onChange);
|
|
40
38
|
|
|
41
39
|
return (
|
|
42
40
|
<div className={classes.container}>
|
|
@@ -48,6 +46,7 @@ export const AccessRights = ({
|
|
|
48
46
|
clear={clear}
|
|
49
47
|
isSubmitting={isSubmitting}
|
|
50
48
|
labels={labels.actions}
|
|
49
|
+
link={link}
|
|
51
50
|
submit={submit}
|
|
52
51
|
/>
|
|
53
52
|
</div>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { makeStyles } from 'tss-react/mui';
|
|
2
2
|
|
|
3
3
|
export const useActionsStyles = makeStyles()((theme) => ({
|
|
4
|
+
actions: {
|
|
5
|
+
backgroundColor: theme.palette.background.paper,
|
|
6
|
+
display: 'flex',
|
|
7
|
+
justifyContent: 'space-between'
|
|
8
|
+
},
|
|
4
9
|
cancelAndSave: {
|
|
5
10
|
display: 'flex',
|
|
6
|
-
|
|
7
|
-
gap: theme.spacing(2)
|
|
8
|
-
justifyContent: 'flex-end'
|
|
11
|
+
flex: 'row',
|
|
12
|
+
gap: theme.spacing(2)
|
|
9
13
|
}
|
|
10
14
|
}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useTranslation } from 'react-i18next';
|
|
2
2
|
|
|
3
|
+
import LinkIcon from '@mui/icons-material/Link';
|
|
3
4
|
import { CircularProgress } from '@mui/material';
|
|
4
5
|
|
|
5
6
|
import { Button } from '../../..';
|
|
@@ -9,10 +10,11 @@ import { useActions } from './useActions';
|
|
|
9
10
|
import { useActionsStyles } from './Actions.styles';
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
|
-
cancel
|
|
13
|
+
cancel: ({ dirty, values }) => void;
|
|
13
14
|
clear: () => void;
|
|
14
15
|
isSubmitting?: boolean;
|
|
15
16
|
labels: Labels['actions'];
|
|
17
|
+
link?: string;
|
|
16
18
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -20,15 +22,17 @@ const Actions = ({
|
|
|
20
22
|
labels,
|
|
21
23
|
cancel,
|
|
22
24
|
submit,
|
|
25
|
+
link,
|
|
23
26
|
isSubmitting,
|
|
24
27
|
clear
|
|
25
28
|
}: Props): JSX.Element => {
|
|
26
29
|
const { t } = useTranslation();
|
|
27
30
|
const { classes } = useActionsStyles();
|
|
28
31
|
|
|
29
|
-
const { dirty, save, formattedValues } = useActions({
|
|
32
|
+
const { dirty, copyLink, save, formattedValues } = useActions({
|
|
30
33
|
clear,
|
|
31
34
|
labels,
|
|
35
|
+
link,
|
|
32
36
|
submit
|
|
33
37
|
});
|
|
34
38
|
|
|
@@ -37,8 +41,21 @@ const Actions = ({
|
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
return (
|
|
40
|
-
<div className={classes.
|
|
41
|
-
{
|
|
44
|
+
<div className={classes.actions}>
|
|
45
|
+
{link ? (
|
|
46
|
+
<Button
|
|
47
|
+
aria-label={t(labels.copyLink)}
|
|
48
|
+
icon={<LinkIcon />}
|
|
49
|
+
iconVariant="start"
|
|
50
|
+
variant="ghost"
|
|
51
|
+
onClick={copyLink}
|
|
52
|
+
>
|
|
53
|
+
{t(labels.copyLink)}
|
|
54
|
+
</Button>
|
|
55
|
+
) : (
|
|
56
|
+
<div />
|
|
57
|
+
)}
|
|
58
|
+
<div className={classes.cancelAndSave}>
|
|
42
59
|
<Button
|
|
43
60
|
aria-label={t(labels.cancel)}
|
|
44
61
|
variant="secondary"
|
|
@@ -46,17 +63,17 @@ const Actions = ({
|
|
|
46
63
|
>
|
|
47
64
|
{t(labels.cancel)}
|
|
48
65
|
</Button>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
</
|
|
66
|
+
<Button
|
|
67
|
+
aria-label={t(labels.save)}
|
|
68
|
+
disabled={isSubmitting || !dirty}
|
|
69
|
+
icon={isSubmitting ? <CircularProgress size={24} /> : null}
|
|
70
|
+
iconVariant={isSubmitting ? 'start' : 'none'}
|
|
71
|
+
variant="primary"
|
|
72
|
+
onClick={save}
|
|
73
|
+
>
|
|
74
|
+
{t(labels.save)}
|
|
75
|
+
</Button>
|
|
76
|
+
</div>
|
|
60
77
|
</div>
|
|
61
78
|
);
|
|
62
79
|
};
|
|
@@ -1,32 +1,64 @@
|
|
|
1
1
|
import { useAtomValue } from 'jotai';
|
|
2
|
-
import { equals } from 'ramda';
|
|
2
|
+
import { equals, omit } from 'ramda';
|
|
3
3
|
|
|
4
4
|
import { initialValuesAtom, valuesAtom } from '../atoms';
|
|
5
|
-
import { AccessRightInitialValues, Labels } from '../models';
|
|
6
|
-
import {
|
|
5
|
+
import { AccessRight, AccessRightInitialValues, Labels } from '../models';
|
|
6
|
+
import { useCopyToClipboard } from '../../../..';
|
|
7
|
+
|
|
8
|
+
const formatValue = (accessRight: AccessRight): AccessRightInitialValues => {
|
|
9
|
+
return omit(['isAdded', 'isUpdated', 'isRemoved'], accessRight);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const formatValueForSubmition = (
|
|
13
|
+
accessRight: AccessRight
|
|
14
|
+
): AccessRightInitialValues => {
|
|
15
|
+
return {
|
|
16
|
+
...formatValue(accessRight),
|
|
17
|
+
id: Number((accessRight.id as string).split('_')[1])
|
|
18
|
+
};
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
interface Props {
|
|
9
22
|
clear: () => void;
|
|
10
23
|
labels: Labels['actions'];
|
|
24
|
+
link?: string;
|
|
11
25
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
12
26
|
}
|
|
13
27
|
|
|
14
28
|
interface UseActionsState {
|
|
29
|
+
copyLink: () => void;
|
|
15
30
|
dirty: boolean;
|
|
16
31
|
formattedValues: Array<AccessRightInitialValues>;
|
|
17
32
|
save: () => void;
|
|
18
33
|
}
|
|
19
34
|
|
|
20
|
-
export const useActions = ({
|
|
35
|
+
export const useActions = ({
|
|
36
|
+
link,
|
|
37
|
+
labels,
|
|
38
|
+
submit,
|
|
39
|
+
clear
|
|
40
|
+
}: Props): UseActionsState => {
|
|
21
41
|
const values = useAtomValue(valuesAtom);
|
|
22
42
|
const initialValues = useAtomValue(initialValuesAtom);
|
|
23
43
|
|
|
44
|
+
const { copy } = useCopyToClipboard({
|
|
45
|
+
errorMessage: labels.copyError,
|
|
46
|
+
successMessage: labels.copySuccess
|
|
47
|
+
});
|
|
48
|
+
|
|
24
49
|
const formattedValues = values
|
|
25
50
|
.filter(({ isRemoved }) => !isRemoved)
|
|
26
51
|
.map(formatValue);
|
|
27
52
|
|
|
28
53
|
const dirty = !equals(initialValues, formattedValues);
|
|
29
54
|
|
|
55
|
+
const copyLink = (): void => {
|
|
56
|
+
if (!link) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
copy(link);
|
|
60
|
+
};
|
|
61
|
+
|
|
30
62
|
const save = (): void => {
|
|
31
63
|
submit(
|
|
32
64
|
values.filter(({ isRemoved }) => !isRemoved).map(formatValueForSubmition)
|
|
@@ -39,6 +71,7 @@ export const useActions = ({ submit, clear }: Props): UseActionsState => {
|
|
|
39
71
|
};
|
|
40
72
|
|
|
41
73
|
return {
|
|
74
|
+
copyLink,
|
|
42
75
|
dirty,
|
|
43
76
|
formattedValues,
|
|
44
77
|
save
|
|
@@ -105,6 +105,9 @@ export const buildResult = (isGroup): Listing<SelectEntry> => ({
|
|
|
105
105
|
export const labels: Labels = {
|
|
106
106
|
actions: {
|
|
107
107
|
cancel: 'Cancel',
|
|
108
|
+
copyError: 'Failed to copy',
|
|
109
|
+
copyLink: 'Copy link',
|
|
110
|
+
copySuccess: 'Copied',
|
|
108
111
|
save: 'Save'
|
|
109
112
|
},
|
|
110
113
|
add: {
|