@centreon/ui 24.5.0 → 24.5.2
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/Item.tsx +2 -11
- package/src/Dashboard/Layout.tsx +2 -4
- 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/TopCounterElements/TopCounterLayout.tsx +4 -3
- package/src/TopCounterElements/useCloseOnLegacyPage.tsx +6 -9
- 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/utils/index.ts +0 -1
- package/src/utils/usePluralizedTranslation.ts +3 -20
- 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/package.json
CHANGED
package/src/Dashboard/Item.tsx
CHANGED
|
@@ -15,7 +15,6 @@ import { useMemoComponent } from '../utils';
|
|
|
15
15
|
import { useDashboardItemStyles } from './Dashboard.styles';
|
|
16
16
|
|
|
17
17
|
interface DashboardItemProps {
|
|
18
|
-
additionalMemoProps?: Array<unknown>;
|
|
19
18
|
canMove?: boolean;
|
|
20
19
|
children: ReactElement;
|
|
21
20
|
className?: string;
|
|
@@ -40,8 +39,7 @@ const Item = forwardRef<HTMLDivElement, DashboardItemProps>(
|
|
|
40
39
|
onTouchEnd,
|
|
41
40
|
id,
|
|
42
41
|
disablePadding = false,
|
|
43
|
-
canMove = false
|
|
44
|
-
additionalMemoProps = []
|
|
42
|
+
canMove = false
|
|
45
43
|
}: DashboardItemProps,
|
|
46
44
|
ref: ForwardedRef<HTMLDivElement>
|
|
47
45
|
): ReactElement => {
|
|
@@ -94,14 +92,7 @@ const Item = forwardRef<HTMLDivElement, DashboardItemProps>(
|
|
|
94
92
|
</Card>
|
|
95
93
|
</div>
|
|
96
94
|
),
|
|
97
|
-
memoProps: [
|
|
98
|
-
style,
|
|
99
|
-
className,
|
|
100
|
-
header,
|
|
101
|
-
theme.palette.mode,
|
|
102
|
-
canMove,
|
|
103
|
-
...additionalMemoProps
|
|
104
|
-
]
|
|
95
|
+
memoProps: [style, className, header, theme.palette.mode, canMove]
|
|
105
96
|
});
|
|
106
97
|
}
|
|
107
98
|
);
|
package/src/Dashboard/Layout.tsx
CHANGED
|
@@ -16,7 +16,6 @@ import Grid from './Grid';
|
|
|
16
16
|
const ReactGridLayout = WidthProvider(GridLayout);
|
|
17
17
|
|
|
18
18
|
interface DashboardLayoutProps<T> {
|
|
19
|
-
additionalMemoProps?: Array<unknown>;
|
|
20
19
|
changeLayout?: (newLayout: Array<Layout>) => void;
|
|
21
20
|
children: Array<JSX.Element>;
|
|
22
21
|
displayGrid?: boolean;
|
|
@@ -29,8 +28,7 @@ const DashboardLayout = <T extends Layout>({
|
|
|
29
28
|
changeLayout,
|
|
30
29
|
displayGrid,
|
|
31
30
|
layout,
|
|
32
|
-
isStatic = false
|
|
33
|
-
additionalMemoProps = []
|
|
31
|
+
isStatic = false
|
|
34
32
|
}: DashboardLayoutProps<T>): JSX.Element => {
|
|
35
33
|
const { classes } = useDashboardLayoutStyles(isStatic);
|
|
36
34
|
|
|
@@ -74,7 +72,7 @@ const DashboardLayout = <T extends Layout>({
|
|
|
74
72
|
</ParentSize>
|
|
75
73
|
</ResponsiveHeight>
|
|
76
74
|
),
|
|
77
|
-
memoProps: [columns, layout, displayGrid, isStatic
|
|
75
|
+
memoProps: [columns, layout, displayGrid, isStatic]
|
|
78
76
|
});
|
|
79
77
|
};
|
|
80
78
|
|
package/src/Graph/Tree/Links.tsx
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LinkHorizontal,
|
|
3
|
-
LinkHorizontalStep,
|
|
4
|
-
LinkHorizontalLine
|
|
5
|
-
} from '@visx/shape';
|
|
1
|
+
import { LinkHorizontal } from '@visx/shape';
|
|
6
2
|
import { HierarchyPointLink } from '@visx/hierarchy/lib/types';
|
|
7
|
-
import { always, cond, equals, T } from 'ramda';
|
|
8
3
|
|
|
9
4
|
import { useTheme } from '@mui/material';
|
|
10
5
|
|
|
@@ -14,12 +9,6 @@ interface Props<TData> extends Pick<TreeProps<TData>, 'treeLink'> {
|
|
|
14
9
|
links: Array<HierarchyPointLink<Node<TData>>>;
|
|
15
10
|
}
|
|
16
11
|
|
|
17
|
-
const getLinkComponent = cond([
|
|
18
|
-
[equals('line'), always(LinkHorizontalLine)],
|
|
19
|
-
[equals('step'), always(LinkHorizontalStep)],
|
|
20
|
-
[T, always(LinkHorizontal)]
|
|
21
|
-
]);
|
|
22
|
-
|
|
23
12
|
const Links = <TData extends BaseProp>({
|
|
24
13
|
links,
|
|
25
14
|
treeLink
|
|
@@ -35,12 +24,10 @@ const Links = <TData extends BaseProp>({
|
|
|
35
24
|
.descendants()
|
|
36
25
|
.map((ancestor) => ancestor.data.data.id);
|
|
37
26
|
|
|
38
|
-
const LinkComponent = getLinkComponent(treeLink?.type);
|
|
39
|
-
|
|
40
27
|
const key = `${link.source.data.data.id}-${link.source.data.data.name}-${ancestorIds}_${link.target.data.data.id}-${link.target.data.data.name}-${descendantIds}`;
|
|
41
28
|
|
|
42
29
|
return (
|
|
43
|
-
<
|
|
30
|
+
<LinkHorizontal
|
|
44
31
|
data={link}
|
|
45
32
|
data-testid={`${link.source.data.data.id}_to_${link.target.data.data.id}`}
|
|
46
33
|
fill="none"
|
|
@@ -168,28 +168,4 @@ describe('Complex data tree', () => {
|
|
|
168
168
|
|
|
169
169
|
cy.makeSnapshot();
|
|
170
170
|
});
|
|
171
|
-
|
|
172
|
-
it('displays the tree with step links when a prop is set', () => {
|
|
173
|
-
initializeStandaloneTree({
|
|
174
|
-
treeLink: {
|
|
175
|
-
type: 'step'
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
cy.contains('T').should('be.visible');
|
|
180
|
-
|
|
181
|
-
cy.makeSnapshot();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('displays the tree with line links when a prop is set', () => {
|
|
185
|
-
initializeStandaloneTree({
|
|
186
|
-
treeLink: {
|
|
187
|
-
type: 'line'
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
cy.contains('T').should('be.visible');
|
|
192
|
-
|
|
193
|
-
cy.makeSnapshot();
|
|
194
|
-
});
|
|
195
171
|
});
|
|
@@ -82,21 +82,6 @@ export const WithDefaultExpandFilter: Story = {
|
|
|
82
82
|
render: StandaloneTreeTemplate
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
export const WithStepLink: Story = {
|
|
86
|
-
args: {
|
|
87
|
-
children: SimpleContent,
|
|
88
|
-
node: {
|
|
89
|
-
height: 90,
|
|
90
|
-
width: 90
|
|
91
|
-
},
|
|
92
|
-
tree: simpleData,
|
|
93
|
-
treeLink: {
|
|
94
|
-
type: 'step'
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
render: StandaloneTreeTemplate
|
|
98
|
-
};
|
|
99
|
-
|
|
100
85
|
export const WithCustomLinks: Story = {
|
|
101
86
|
args: {
|
|
102
87
|
children: SimpleContent,
|
|
@@ -110,8 +95,7 @@ export const WithCustomLinks: Story = {
|
|
|
110
95
|
getStrokeDasharray: ({ target }) =>
|
|
111
96
|
target.status === 'ok' ? '5,5' : '0',
|
|
112
97
|
getStrokeOpacity: ({ target }) => (target.status === 'ok' ? 0.8 : 1),
|
|
113
|
-
getStrokeWidth: ({ target }) => (target.status === 'ok' ? 1 : 2)
|
|
114
|
-
type: 'line'
|
|
98
|
+
getStrokeWidth: ({ target }) => (target.status === 'ok' ? 1 : 2)
|
|
115
99
|
}
|
|
116
100
|
},
|
|
117
101
|
render: StandaloneTreeTemplate
|
package/src/Graph/Tree/models.ts
CHANGED
|
@@ -14,8 +14,6 @@ export interface BaseProp {
|
|
|
14
14
|
name: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export type Link = 'curve' | 'line' | 'step';
|
|
18
|
-
|
|
19
17
|
export interface ChildrenProps<TData> {
|
|
20
18
|
ancestors: Array<Node<TData>>;
|
|
21
19
|
depth: number;
|
|
@@ -50,6 +48,5 @@ export interface TreeProps<TData> {
|
|
|
50
48
|
) => string | number | undefined;
|
|
51
49
|
getStrokeOpacity?: (props: LinkProps<TData>) => string | number | undefined;
|
|
52
50
|
getStrokeWidth?: (props: LinkProps<TData>) => string | number | undefined;
|
|
53
|
-
type?: Link;
|
|
54
51
|
};
|
|
55
52
|
}
|
|
@@ -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,6 +1,5 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { isNil } from 'ramda';
|
|
4
3
|
import { useLocation } from 'react-router-dom';
|
|
5
4
|
|
|
6
5
|
interface Props {
|
|
@@ -15,23 +14,21 @@ const useCloseOnLegacyPage = ({ setToggled }: Props): void => {
|
|
|
15
14
|
};
|
|
16
15
|
|
|
17
16
|
useEffect(() => {
|
|
18
|
-
const iframe = document.getElementById(
|
|
19
|
-
'main-content'
|
|
20
|
-
) as HTMLIFrameElement | null;
|
|
17
|
+
const iframe = document.getElementById('main-content') as HTMLIFrameElement;
|
|
21
18
|
|
|
22
|
-
if (!isLegacyRoute
|
|
19
|
+
if (!isLegacyRoute) {
|
|
23
20
|
return () => undefined;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
const closeSubMenuOnLegacyPage = (): void => {
|
|
27
|
-
iframe
|
|
24
|
+
iframe.contentWindow?.document?.addEventListener('click', closeSubMenu);
|
|
28
25
|
};
|
|
29
26
|
|
|
30
|
-
iframe
|
|
27
|
+
iframe.addEventListener('load', closeSubMenuOnLegacyPage);
|
|
31
28
|
|
|
32
29
|
return () => {
|
|
33
|
-
iframe
|
|
34
|
-
iframe
|
|
30
|
+
iframe.removeEventListener('load', closeSubMenuOnLegacyPage);
|
|
31
|
+
iframe.contentWindow?.document?.removeEventListener(
|
|
35
32
|
'click',
|
|
36
33
|
closeSubMenu
|
|
37
34
|
);
|
|
@@ -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: {
|