@centreon/ui 23.10.39 → 23.10.40
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 +2 -1
- package/src/ThemeProvider/index.tsx +0 -24
- package/src/ThemeProvider/palettes.ts +36 -6
- package/src/api/useMutationQuery/index.ts +16 -6
- package/src/components/Form/AccessRights/AccessRights.resource.ts +45 -0
- package/src/components/Form/AccessRights/AccessRightsForm.stories.tsx +59 -0
- package/src/components/Form/AccessRights/AccessRightsForm.styles.ts +21 -0
- package/src/components/Form/AccessRights/AccessRightsForm.tsx +67 -0
- package/src/components/Form/AccessRights/AccessRightsFormActions.tsx +80 -0
- package/src/components/Form/AccessRights/Input/AddAction.tsx +31 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightInput.stories.tsx +54 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightInput.tsx +72 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightsInput.styles.ts +22 -0
- package/src/components/Form/AccessRights/Input/ContactInputField.tsx +105 -0
- package/src/components/Form/AccessRights/Input/RoleInputField.tsx +29 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.stories.tsx +97 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.styles.ts +71 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.tsx +51 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.stories.tsx +116 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.tsx +118 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItemSkeleton.tsx +26 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListSkeleton.tsx +28 -0
- package/src/components/Form/AccessRights/Stats/AccessRightsStats.styles.ts +18 -0
- package/src/components/Form/AccessRights/Stats/AccessRightsStats.tsx +41 -0
- package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +52 -0
- package/src/components/Form/AccessRights/common/GroupLabel.styles.ts +18 -0
- package/src/components/Form/AccessRights/common/GroupLabel.tsx +15 -0
- package/src/components/Form/AccessRights/common/Input.styles.ts +48 -0
- package/src/components/Form/AccessRights/common/RoleInputSelect.styles.ts +11 -0
- package/src/components/Form/AccessRights/common/RoleInputSelect.tsx +57 -0
- package/src/components/Form/AccessRights/index.ts +3 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.test.tsx +531 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.tsx +282 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +41 -0
- package/src/components/Form/Dashboard/DashboardForm.styles.ts +1 -7
- package/src/components/Form/Dashboard/DashboardForm.tsx +9 -12
- package/src/components/Form/Form.resource.ts +1 -0
- package/src/components/Form/Form.styles.ts +17 -0
- package/src/components/Form/{Dashboard/DashboardFormActions.tsx → FormActions.tsx} +17 -16
- package/src/components/Form/index.ts +1 -0
- package/src/components/Header/PageHeader/PageHeader.stories.tsx +9 -0
- package/src/components/List/Item/Avatar.tsx +21 -0
- package/src/components/List/Item/AvatarSkeleton.tsx +17 -0
- package/src/components/List/Item/ListItem.stories.tsx +41 -0
- package/src/components/List/Item/ListItem.styles.ts +57 -0
- package/src/components/List/Item/ListItem.tsx +32 -0
- package/src/components/List/Item/Text.tsx +25 -0
- package/src/components/List/Item/TextSkeleton.tsx +22 -0
- package/src/components/List/Item/index.ts +14 -0
- package/src/components/List/List.stories.tsx +70 -0
- package/src/components/List/List.styles.ts +8 -0
- package/src/components/List/List.tsx +19 -0
- package/src/components/List/index.ts +6 -0
- package/src/components/Modal/ModalActions.tsx +4 -1
- package/src/components/index.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/useInfiniteScrollListing.ts +106 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ListItemText as MuiListItemText } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { useStyles } from './ListItem.styles';
|
|
6
|
+
|
|
7
|
+
type TextProps = {
|
|
8
|
+
primaryText: ReactNode;
|
|
9
|
+
secondaryText?: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const Text = ({
|
|
13
|
+
primaryText,
|
|
14
|
+
secondaryText
|
|
15
|
+
}: TextProps): ReactElement => {
|
|
16
|
+
const { classes } = useStyles();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<MuiListItemText
|
|
20
|
+
className={classes.text}
|
|
21
|
+
primary={primaryText}
|
|
22
|
+
secondary={secondaryText}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Skeleton as MuiSkeleton } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { useStyles } from './ListItem.styles';
|
|
6
|
+
|
|
7
|
+
type TextSkeletonProps = {
|
|
8
|
+
secondaryText?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const TextSkeleton = ({
|
|
12
|
+
secondaryText
|
|
13
|
+
}: TextSkeletonProps): ReactElement => {
|
|
14
|
+
const { classes } = useStyles();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<span className={classes.textSkeleton}>
|
|
18
|
+
<MuiSkeleton animation="wave" variant="text" />
|
|
19
|
+
{!!secondaryText && <MuiSkeleton animation="wave" variant="text" />}
|
|
20
|
+
</span>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ListItem as ListItemRoot } from './ListItem';
|
|
2
|
+
import { Text as TextRoot } from './Text';
|
|
3
|
+
import { TextSkeleton } from './TextSkeleton';
|
|
4
|
+
import { Avatar as AvatarRoot } from './Avatar';
|
|
5
|
+
import { AvatarSkeleton } from './AvatarSkeleton';
|
|
6
|
+
|
|
7
|
+
export const ListItem = Object.assign(ListItemRoot, {
|
|
8
|
+
Avatar: Object.assign(AvatarRoot, {
|
|
9
|
+
Skeleton: AvatarSkeleton
|
|
10
|
+
}),
|
|
11
|
+
Text: Object.assign(TextRoot, {
|
|
12
|
+
Skeleton: TextSkeleton
|
|
13
|
+
})
|
|
14
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* eslint-disable react/no-array-index-key */
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
5
|
+
import { Box } from '@mui/material';
|
|
6
|
+
|
|
7
|
+
import { List } from '.';
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof List> = {
|
|
10
|
+
component: List
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof List>;
|
|
15
|
+
|
|
16
|
+
const sampleData = Array(50).fill({
|
|
17
|
+
icon: <DeleteIcon />,
|
|
18
|
+
primary: 'Primary text',
|
|
19
|
+
secondary: 'Secondary text'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const Default: Story = {
|
|
23
|
+
render: () => (
|
|
24
|
+
<List>
|
|
25
|
+
{sampleData.map(({ primary }, index) => (
|
|
26
|
+
<List.Item key={`${primary}_${index}`}>
|
|
27
|
+
<List.Item.Text primaryText={primary} />
|
|
28
|
+
</List.Item>
|
|
29
|
+
))}
|
|
30
|
+
</List>
|
|
31
|
+
)
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const withAction: Story = {
|
|
35
|
+
render: () => (
|
|
36
|
+
<List>
|
|
37
|
+
{sampleData.map(({ icon, primary }, index) => (
|
|
38
|
+
<List.Item action={icon} key={`${primary}_${index}`}>
|
|
39
|
+
<List.Item.Text primaryText={primary} />
|
|
40
|
+
</List.Item>
|
|
41
|
+
))}
|
|
42
|
+
</List>
|
|
43
|
+
)
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const withSecondaryText: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<List>
|
|
49
|
+
{sampleData.map(({ primary, secondary }, index) => (
|
|
50
|
+
<List.Item key={`${primary}_${index}`}>
|
|
51
|
+
<List.Item.Text primaryText={primary} secondaryText={secondary} />
|
|
52
|
+
</List.Item>
|
|
53
|
+
))}
|
|
54
|
+
</List>
|
|
55
|
+
)
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const withSmallContainer: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<Box sx={{ height: 400, width: 200 }}>
|
|
61
|
+
<List>
|
|
62
|
+
{sampleData.map(({ icon, primary, secondary }, index) => (
|
|
63
|
+
<List.Item action={icon} key={`${primary}_${index}`}>
|
|
64
|
+
<List.Item.Text primaryText={primary} secondaryText={secondary} />
|
|
65
|
+
</List.Item>
|
|
66
|
+
))}
|
|
67
|
+
</List>
|
|
68
|
+
</Box>
|
|
69
|
+
)
|
|
70
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { List as MuiList } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { useStyles } from './List.styles';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const List = ({ children }: Props): ReactElement => {
|
|
12
|
+
const { classes } = useStyles();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<MuiList dense className={classes.list}>
|
|
16
|
+
{children}
|
|
17
|
+
</MuiList>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -6,6 +6,7 @@ import { useStyles } from './Modal.styles';
|
|
|
6
6
|
|
|
7
7
|
export type ModalActionsProps = {
|
|
8
8
|
children?: React.ReactNode;
|
|
9
|
+
disabled?: boolean;
|
|
9
10
|
isDanger?: boolean;
|
|
10
11
|
labels?: ModalActionsLabels;
|
|
11
12
|
onCancel?: () => void;
|
|
@@ -22,7 +23,8 @@ const ModalActions = ({
|
|
|
22
23
|
labels,
|
|
23
24
|
onCancel,
|
|
24
25
|
onConfirm,
|
|
25
|
-
isDanger = false
|
|
26
|
+
isDanger = false,
|
|
27
|
+
disabled
|
|
26
28
|
}: ModalActionsProps): ReactElement => {
|
|
27
29
|
const { classes } = useStyles();
|
|
28
30
|
|
|
@@ -42,6 +44,7 @@ const ModalActions = ({
|
|
|
42
44
|
<Button
|
|
43
45
|
aria-label={labels?.confirm}
|
|
44
46
|
data-testid="confirm"
|
|
47
|
+
disabled={disabled}
|
|
45
48
|
isDanger={isDanger}
|
|
46
49
|
size="small"
|
|
47
50
|
type="submit"
|
package/src/components/index.ts
CHANGED
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { equals, gte, isNil, reduce } from 'ramda';
|
|
4
|
+
import { PrimitiveAtom, useAtom } from 'jotai';
|
|
5
|
+
import { JsonDecoder } from 'ts.data.json';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildListingEndpoint,
|
|
9
|
+
useFetchQuery,
|
|
10
|
+
useIntersectionObserver
|
|
11
|
+
} from '@centreon/ui';
|
|
12
|
+
|
|
13
|
+
import type { Listing } from '../api/models';
|
|
14
|
+
|
|
15
|
+
const limit = 100;
|
|
16
|
+
|
|
17
|
+
interface UseInfiniteScrollListing<T> {
|
|
18
|
+
elementRef: (node) => void;
|
|
19
|
+
elements: Array<T>;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UseInfiniteScrollListingProps<T> {
|
|
24
|
+
decoder: JsonDecoder.Decoder<Listing<T>>;
|
|
25
|
+
endpoint: string;
|
|
26
|
+
pageAtom: PrimitiveAtom<number>;
|
|
27
|
+
queryKeyName: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const useInfiniteScrollListing = <T>({
|
|
31
|
+
queryKeyName,
|
|
32
|
+
endpoint,
|
|
33
|
+
decoder,
|
|
34
|
+
pageAtom
|
|
35
|
+
}: UseInfiniteScrollListingProps<T>): UseInfiniteScrollListing<T> => {
|
|
36
|
+
const [maxPage, setMaxPage] = useState(1);
|
|
37
|
+
|
|
38
|
+
const elements = useRef<Array<T> | undefined>(undefined);
|
|
39
|
+
|
|
40
|
+
const [page, setPage] = useAtom(pageAtom);
|
|
41
|
+
|
|
42
|
+
const { data, isLoading, prefetchNextPage, fetchStatus } = useFetchQuery<
|
|
43
|
+
Listing<T>
|
|
44
|
+
>({
|
|
45
|
+
decoder,
|
|
46
|
+
getEndpoint: (params) =>
|
|
47
|
+
buildListingEndpoint({
|
|
48
|
+
baseEndpoint: endpoint,
|
|
49
|
+
parameters: { limit, page: params?.page || page }
|
|
50
|
+
}),
|
|
51
|
+
getQueryKey: () => [queryKeyName, page],
|
|
52
|
+
isPaginated: true,
|
|
53
|
+
queryOptions: {
|
|
54
|
+
refetchOnMount: false,
|
|
55
|
+
refetchOnWindowFocus: false,
|
|
56
|
+
suspense: equals(page, 1)
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const elementRef = useIntersectionObserver({
|
|
61
|
+
action: () => {
|
|
62
|
+
setPage((currentPage) => currentPage + 1);
|
|
63
|
+
},
|
|
64
|
+
loading: !equals(fetchStatus, 'idle'),
|
|
65
|
+
maxPage,
|
|
66
|
+
page
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
elements.current = useMemo(() => {
|
|
70
|
+
if (isNil(data) || !equals(fetchStatus, 'idle')) {
|
|
71
|
+
return elements.current;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return reduce<T, Array<T>>(
|
|
75
|
+
(acc, element) => [...acc, element],
|
|
76
|
+
equals(page, 1) ? [] : elements.current || [],
|
|
77
|
+
data.result
|
|
78
|
+
);
|
|
79
|
+
}, [page, data, fetchStatus]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (isNil(data)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const total = data.meta.total || 1;
|
|
87
|
+
const newMaxPage = Math.ceil(total / limit);
|
|
88
|
+
|
|
89
|
+
setMaxPage(newMaxPage);
|
|
90
|
+
|
|
91
|
+
if (gte(page, newMaxPage)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
prefetchNextPage({
|
|
96
|
+
getPrefetchQueryKey: (newPage) => [`dashboards`, newPage],
|
|
97
|
+
page
|
|
98
|
+
});
|
|
99
|
+
}, [data]);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
elementRef,
|
|
103
|
+
elements: elements.current || [],
|
|
104
|
+
isLoading
|
|
105
|
+
};
|
|
106
|
+
};
|