@centreon/ui 23.10.79 → 23.10.81
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/Dashboard/Dashboard.styles.ts +2 -1
- package/src/Form/Inputs/Radio.tsx +15 -4
- package/src/Form/Inputs/models.ts +1 -1
- package/src/Graph/Gauge/ResponsiveGauge.tsx +1 -1
- package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +46 -39
- package/src/RichTextEditor/ContentEditable.tsx +11 -6
- package/src/RichTextEditor/index.stories.tsx +7 -0
- package/src/components/Form/Dashboard/Dashboard.resource.ts +5 -0
- package/src/components/Form/Dashboard/DashboardForm.styles.ts +10 -2
- package/src/components/Form/Dashboard/DashboardForm.tsx +34 -1
- package/src/components/Form/Dashboard/GlobalRefreshFieldOption.tsx +54 -0
- package/src/components/Form/Dashboard/translatedLabels.ts +3 -0
- package/src/utils/useRefreshInterval.ts +19 -4
package/package.json
CHANGED
|
@@ -70,6 +70,7 @@ export const useDashboardItemStyles = makeStyles<{ hasHeader: boolean }>()(
|
|
|
70
70
|
(theme, { hasHeader }) => ({
|
|
71
71
|
widgetContainer: {
|
|
72
72
|
height: '100%',
|
|
73
|
+
paddingBottom: theme.spacing(2),
|
|
73
74
|
width: '100%'
|
|
74
75
|
},
|
|
75
76
|
widgetContent: {
|
|
@@ -89,7 +90,7 @@ export const useDashboardItemStyles = makeStyles<{ hasHeader: boolean }>()(
|
|
|
89
90
|
},
|
|
90
91
|
widgetPadding: {
|
|
91
92
|
overflowX: 'auto',
|
|
92
|
-
padding: hasHeader ? theme.spacing(1, 2,
|
|
93
|
+
padding: hasHeader ? theme.spacing(1, 2, 0) : theme.spacing(1, 2)
|
|
93
94
|
}
|
|
94
95
|
})
|
|
95
96
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormikValues, useFormikContext } from 'formik';
|
|
2
|
-
import { equals, includes, path, split } from 'ramda';
|
|
2
|
+
import { equals, includes, path, split, type } from 'ramda';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
5
|
import {
|
|
@@ -73,12 +73,23 @@ const Radio = ({
|
|
|
73
73
|
disabled={disabled}
|
|
74
74
|
id={getNormalizedId(`${dataTestId}${optionLabel}`)}
|
|
75
75
|
inputProps={{
|
|
76
|
-
'aria-label':
|
|
76
|
+
'aria-label':
|
|
77
|
+
(equals(type(optionLabel), 'String') &&
|
|
78
|
+
t(optionLabel as string)) ||
|
|
79
|
+
''
|
|
77
80
|
}}
|
|
78
81
|
/>
|
|
79
82
|
}
|
|
80
|
-
key={
|
|
81
|
-
|
|
83
|
+
key={
|
|
84
|
+
equals(type(optionLabel), 'String')
|
|
85
|
+
? (optionLabel as string)
|
|
86
|
+
: `${optionValue}`
|
|
87
|
+
}
|
|
88
|
+
label={
|
|
89
|
+
equals(type(optionLabel), 'String')
|
|
90
|
+
? (t(optionLabel as string) as string)
|
|
91
|
+
: optionLabel
|
|
92
|
+
}
|
|
82
93
|
value={optionValue}
|
|
83
94
|
/>
|
|
84
95
|
))}
|
|
@@ -134,7 +134,7 @@ const ResponsiveGauge = ({
|
|
|
134
134
|
...baseStyles,
|
|
135
135
|
backgroundColor: theme.palette.background.paper,
|
|
136
136
|
color: theme.palette.text.primary,
|
|
137
|
-
transform: 'translate(-50%,
|
|
137
|
+
transform: 'translate(-50%, -24px)'
|
|
138
138
|
}}
|
|
139
139
|
top={(tooltipTop || 0) - svgTop + 20}
|
|
140
140
|
>
|
|
@@ -127,47 +127,54 @@ const ResponsiveSingleBar = ({
|
|
|
127
127
|
const springStyle = useSpring({ width: metricBarWidth });
|
|
128
128
|
|
|
129
129
|
return (
|
|
130
|
-
<
|
|
131
|
-
|
|
130
|
+
<div
|
|
131
|
+
style={{
|
|
132
132
|
height: '100%',
|
|
133
|
-
|
|
134
|
-
position: 'relative',
|
|
135
|
-
width: '100%'
|
|
133
|
+
position: 'relative'
|
|
136
134
|
}}
|
|
137
135
|
>
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
136
|
+
<Box
|
|
137
|
+
sx={{
|
|
138
|
+
height: '100%',
|
|
139
|
+
overflow: 'hidden',
|
|
140
|
+
position: 'relative',
|
|
141
|
+
width: '100%'
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<svg height={height} ref={svgRef} width={width}>
|
|
145
|
+
<Group.Group>
|
|
146
|
+
{text}
|
|
147
|
+
<animated.rect
|
|
148
|
+
data-testid={`${latestMetricData}-bar-${barColor}`}
|
|
149
|
+
fill={barColor}
|
|
150
|
+
height={barHeights[size]}
|
|
151
|
+
rx={4}
|
|
152
|
+
style={springStyle}
|
|
153
|
+
x={0}
|
|
154
|
+
y={groupMargin + (isSmall ? 0 : 2 * margins.top)}
|
|
155
|
+
/>
|
|
156
|
+
<Bar
|
|
157
|
+
fill="transparent"
|
|
158
|
+
height={barHeights[size]}
|
|
159
|
+
rx={4}
|
|
160
|
+
ry={4}
|
|
161
|
+
stroke={alpha(theme.palette.text.primary, 0.3)}
|
|
162
|
+
width={maxBarWidth}
|
|
163
|
+
x={0}
|
|
164
|
+
y={groupMargin + (isSmall ? 0 : 2 * margins.top)}
|
|
167
165
|
/>
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
{thresholds.enabled && (
|
|
167
|
+
<Thresholds
|
|
168
|
+
hideTooltip={hideTooltip}
|
|
169
|
+
showTooltip={showTooltip}
|
|
170
|
+
size={size}
|
|
171
|
+
thresholds={thresholds}
|
|
172
|
+
xScale={xScale}
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
</Group.Group>
|
|
176
|
+
</svg>
|
|
177
|
+
</Box>
|
|
171
178
|
<Fade in={tooltipOpen}>
|
|
172
179
|
<Tooltip.Tooltip
|
|
173
180
|
left={tooltipLeft}
|
|
@@ -175,7 +182,7 @@ const ResponsiveSingleBar = ({
|
|
|
175
182
|
...baseStyles,
|
|
176
183
|
backgroundColor: theme.palette.background.paper,
|
|
177
184
|
color: theme.palette.text.primary,
|
|
178
|
-
transform: `translate(-50%, ${isSmall ? -
|
|
185
|
+
transform: `translate(-50%, ${isSmall ? -100 : -120}px)`,
|
|
179
186
|
zIndex: theme.zIndex.tooltip
|
|
180
187
|
}}
|
|
181
188
|
top={tooltipTop}
|
|
@@ -183,7 +190,7 @@ const ResponsiveSingleBar = ({
|
|
|
183
190
|
{tooltipData}
|
|
184
191
|
</Tooltip.Tooltip>
|
|
185
192
|
</Fade>
|
|
186
|
-
</
|
|
193
|
+
</div>
|
|
187
194
|
);
|
|
188
195
|
};
|
|
189
196
|
|
|
@@ -16,9 +16,6 @@ interface StyleProps {
|
|
|
16
16
|
const useStyles = makeStyles<StyleProps>()(
|
|
17
17
|
(theme, { minInputHeight, editable, error }) => ({
|
|
18
18
|
container: {
|
|
19
|
-
'& p': {
|
|
20
|
-
margin: 0
|
|
21
|
-
},
|
|
22
19
|
backgroundColor: theme.palette.background.paper,
|
|
23
20
|
border: error
|
|
24
21
|
? `1px solid ${theme.palette.error.main}`
|
|
@@ -41,6 +38,11 @@ const useStyles = makeStyles<StyleProps>()(
|
|
|
41
38
|
placeholder: {
|
|
42
39
|
color: theme.palette.grey[500],
|
|
43
40
|
pointerEvents: 'none'
|
|
41
|
+
},
|
|
42
|
+
root: {
|
|
43
|
+
'& p,h1,h2,h3,h4,h5,h6,span': {
|
|
44
|
+
margin: 0
|
|
45
|
+
}
|
|
44
46
|
}
|
|
45
47
|
})
|
|
46
48
|
);
|
|
@@ -121,11 +123,13 @@ const ContentEditable = ({
|
|
|
121
123
|
const shouldResetEditorToInitialState =
|
|
122
124
|
resetEditorToInitialStateCondition?.();
|
|
123
125
|
|
|
124
|
-
if (!shouldResetEditorToInitialState
|
|
126
|
+
if (!shouldResetEditorToInitialState) {
|
|
125
127
|
return;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
const newEditorState = editor.parseEditorState(
|
|
130
|
+
const newEditorState = editor.parseEditorState(
|
|
131
|
+
initialEditorState || defaultState
|
|
132
|
+
);
|
|
129
133
|
|
|
130
134
|
editor.setEditorState(newEditorState);
|
|
131
135
|
}, [editorState]);
|
|
@@ -156,7 +160,8 @@ const ContentEditable = ({
|
|
|
156
160
|
return (
|
|
157
161
|
<div
|
|
158
162
|
className={cx(
|
|
159
|
-
classes.
|
|
163
|
+
classes.root,
|
|
164
|
+
editable && classes.container,
|
|
160
165
|
className,
|
|
161
166
|
isFocused && classes.inputFocused
|
|
162
167
|
)}
|
|
@@ -67,6 +67,13 @@ withEditableFalse.args = {
|
|
|
67
67
|
initialEditorState: JSON.stringify(initialEditorState)
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
export const Disabled = Template.bind({});
|
|
71
|
+
Disabled.args = {
|
|
72
|
+
disabled: true,
|
|
73
|
+
editable: false,
|
|
74
|
+
editorState: JSON.stringify(initialEditorState)
|
|
75
|
+
};
|
|
76
|
+
|
|
70
77
|
const StoryWithEditableFalseLikePreview = (): JSX.Element => {
|
|
71
78
|
const [editorState, setEditorState] =
|
|
72
79
|
useState<EditorState>(initialEditorState);
|
|
@@ -5,9 +5,17 @@ const useStyles = makeStyles()(() => ({
|
|
|
5
5
|
display: 'flex',
|
|
6
6
|
flexDirection: 'column',
|
|
7
7
|
maxWidth: '480px',
|
|
8
|
-
|
|
9
8
|
width: '100%'
|
|
10
9
|
}
|
|
11
10
|
}));
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
const useGlobalRefreshIntervalStyles = makeStyles()((theme) => ({
|
|
13
|
+
globalRefreshInterval: {
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
display: 'flex',
|
|
16
|
+
flexDirection: 'row',
|
|
17
|
+
gap: theme.spacing(1)
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
export { useStyles, useGlobalRefreshIntervalStyles };
|
|
@@ -16,11 +16,13 @@ import {
|
|
|
16
16
|
labelRequired
|
|
17
17
|
} from './translatedLabels';
|
|
18
18
|
import { DashboardResource } from './Dashboard.resource';
|
|
19
|
+
import GlobalRefreshFieldOption from './GlobalRefreshFieldOption';
|
|
19
20
|
|
|
20
21
|
export type DashboardFormProps = {
|
|
21
22
|
labels: DashboardFormLabels;
|
|
22
23
|
onSubmit?: FormProps<DashboardResource>['submit'];
|
|
23
24
|
resource?: DashboardResource;
|
|
25
|
+
showRefreshIntervalFields?: boolean;
|
|
24
26
|
variant?: FormVariant;
|
|
25
27
|
} & Pick<FormActionsProps, 'onCancel'>;
|
|
26
28
|
|
|
@@ -34,7 +36,8 @@ const DashboardForm = ({
|
|
|
34
36
|
resource,
|
|
35
37
|
labels,
|
|
36
38
|
onSubmit,
|
|
37
|
-
onCancel
|
|
39
|
+
onCancel,
|
|
40
|
+
showRefreshIntervalFields
|
|
38
41
|
}: DashboardFormProps): ReactElement => {
|
|
39
42
|
const { classes } = useStyles();
|
|
40
43
|
const { t } = useTranslation();
|
|
@@ -58,6 +61,26 @@ const DashboardForm = ({
|
|
|
58
61
|
multilineRows: 3
|
|
59
62
|
},
|
|
60
63
|
type: InputType.Text
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
fieldName: 'refresh.type',
|
|
67
|
+
group: 'main',
|
|
68
|
+
hideInput: () => !showRefreshIntervalFields,
|
|
69
|
+
label: labels?.entity?.globalRefreshInterval?.title,
|
|
70
|
+
radio: {
|
|
71
|
+
options: [
|
|
72
|
+
{
|
|
73
|
+
label: <GlobalRefreshFieldOption />,
|
|
74
|
+
value: 'global'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: labels?.entity?.globalRefreshInterval?.manual,
|
|
78
|
+
value: 'manual'
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
row: false
|
|
82
|
+
},
|
|
83
|
+
type: InputType.Radio
|
|
61
84
|
}
|
|
62
85
|
],
|
|
63
86
|
submit: (values, bag) => onSubmit?.(values, bag),
|
|
@@ -70,6 +93,16 @@ const DashboardForm = ({
|
|
|
70
93
|
`${p.label} ${t(labelMustBeMost)} ${p.max} ${t(labelCharacters)}`
|
|
71
94
|
)
|
|
72
95
|
.nullable(),
|
|
96
|
+
globalRefreshInterval: Yup.object().shape({
|
|
97
|
+
interval: Yup.number().when('type', {
|
|
98
|
+
is: 'global',
|
|
99
|
+
otherwise: Yup.number().nullable(),
|
|
100
|
+
then: Yup.number()
|
|
101
|
+
.min(1, ({ min }) => t(labelMustBeAtLeast, { min }))
|
|
102
|
+
.required(t(labelRequired) as string)
|
|
103
|
+
}),
|
|
104
|
+
type: Yup.string()
|
|
105
|
+
}),
|
|
73
106
|
name: Yup.string()
|
|
74
107
|
.label(labels?.entity?.name)
|
|
75
108
|
.min(3, ({ min, label }) => t(labelMustBeAtLeast, { label, min }))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ChangeEvent } from 'react';
|
|
2
|
+
|
|
3
|
+
import { FormikValues, useFormikContext } from 'formik';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { isEmpty } from 'ramda';
|
|
6
|
+
import pluralize from 'pluralize';
|
|
7
|
+
|
|
8
|
+
import { Typography } from '@mui/material';
|
|
9
|
+
|
|
10
|
+
import { TextField } from '../../..';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
labelGlobalRefreshInterval,
|
|
14
|
+
labelInterval,
|
|
15
|
+
labelSeconds
|
|
16
|
+
} from './translatedLabels';
|
|
17
|
+
import { useGlobalRefreshIntervalStyles } from './DashboardForm.styles';
|
|
18
|
+
|
|
19
|
+
const GlobalRefreshFieldOption = (): JSX.Element => {
|
|
20
|
+
const { classes } = useGlobalRefreshIntervalStyles();
|
|
21
|
+
const { t } = useTranslation();
|
|
22
|
+
|
|
23
|
+
const { values, setFieldValue } = useFormikContext<FormikValues>();
|
|
24
|
+
|
|
25
|
+
const value = values.refresh.interval;
|
|
26
|
+
|
|
27
|
+
const changeInput = (event: ChangeEvent<HTMLInputElement>): void => {
|
|
28
|
+
setFieldValue(
|
|
29
|
+
'refresh.interval',
|
|
30
|
+
!isEmpty(event.target.value) ? Number(event.target.value) || 1 : null
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={classes.globalRefreshInterval}>
|
|
36
|
+
<Typography>{t(labelGlobalRefreshInterval)}</Typography>
|
|
37
|
+
<TextField
|
|
38
|
+
autoSize
|
|
39
|
+
dataTestId={labelInterval}
|
|
40
|
+
inputProps={{
|
|
41
|
+
'aria-label': t(labelInterval) as string,
|
|
42
|
+
min: 1
|
|
43
|
+
}}
|
|
44
|
+
size="compact"
|
|
45
|
+
type="number"
|
|
46
|
+
value={value || ''}
|
|
47
|
+
onChange={changeInput}
|
|
48
|
+
/>
|
|
49
|
+
<Typography>{pluralize(t(labelSeconds), value)}</Typography>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default GlobalRefreshFieldOption;
|
|
@@ -4,3 +4,6 @@ export const labelMustBeAtLeast =
|
|
|
4
4
|
'{{label}} should be at least {{min}} characters long';
|
|
5
5
|
export const labelRequired = 'Required';
|
|
6
6
|
export const labelCharacters = 'characters';
|
|
7
|
+
export const labelGlobalRefreshInterval = 'Global refresh interval';
|
|
8
|
+
export const labelSeconds = 'seconds';
|
|
9
|
+
export const labelInterval = 'Interval';
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { useAtomValue } from 'jotai';
|
|
1
2
|
import { always, cond, equals } from 'ramda';
|
|
2
3
|
|
|
4
|
+
import { refreshIntervalAtom } from '@centreon/ui-context';
|
|
5
|
+
|
|
3
6
|
interface Props {
|
|
4
|
-
globalRefreshInterval
|
|
7
|
+
globalRefreshInterval: {
|
|
8
|
+
interval: number | null;
|
|
9
|
+
type: 'global' | 'manual';
|
|
10
|
+
};
|
|
5
11
|
refreshInterval: 'default' | 'custom' | 'manual';
|
|
6
12
|
refreshIntervalCustom?: number;
|
|
7
13
|
}
|
|
@@ -11,11 +17,20 @@ export const useRefreshInterval = ({
|
|
|
11
17
|
refreshIntervalCustom,
|
|
12
18
|
globalRefreshInterval
|
|
13
19
|
}: Props): number | false => {
|
|
20
|
+
const platformInterval = useAtomValue(refreshIntervalAtom);
|
|
21
|
+
|
|
14
22
|
const refreshIntervalToUse = cond([
|
|
15
|
-
[
|
|
23
|
+
[
|
|
24
|
+
equals('default'),
|
|
25
|
+
always(
|
|
26
|
+
equals(globalRefreshInterval.type, 'manual')
|
|
27
|
+
? false
|
|
28
|
+
: globalRefreshInterval.interval || platformInterval
|
|
29
|
+
)
|
|
30
|
+
],
|
|
16
31
|
[equals('custom'), always(refreshIntervalCustom)],
|
|
17
|
-
[equals('manual'), always(
|
|
32
|
+
[equals('manual'), always(false)]
|
|
18
33
|
])(refreshInterval);
|
|
19
34
|
|
|
20
|
-
return refreshIntervalToUse ? refreshIntervalToUse * 1000 : false;
|
|
35
|
+
return refreshIntervalToUse ? (refreshIntervalToUse as number) * 1000 : false;
|
|
21
36
|
};
|