@centreon/ui 23.10.39 → 23.10.41

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 (68) hide show
  1. package/package.json +3 -1
  2. package/src/Listing/Cell/DataCell.tsx +16 -4
  3. package/src/Listing/Cell/index.tsx +82 -2
  4. package/src/Listing/Listing.cypress.spec.tsx +120 -0
  5. package/src/Listing/Listing.styles.ts +2 -2
  6. package/src/Listing/ListingDocs.mdx +61 -0
  7. package/src/Listing/Row/Row.tsx +5 -2
  8. package/src/Listing/index.stories.tsx +104 -31
  9. package/src/Listing/index.tsx +103 -33
  10. package/src/Listing/models.ts +1 -0
  11. package/src/Listing/tableAtoms.ts +2 -0
  12. package/src/Panel/index.tsx +1 -1
  13. package/src/ThemeProvider/index.tsx +0 -24
  14. package/src/ThemeProvider/palettes.ts +36 -6
  15. package/src/api/useMutationQuery/index.ts +16 -6
  16. package/src/components/Form/AccessRights/AccessRights.resource.ts +45 -0
  17. package/src/components/Form/AccessRights/AccessRightsForm.stories.tsx +59 -0
  18. package/src/components/Form/AccessRights/AccessRightsForm.styles.ts +21 -0
  19. package/src/components/Form/AccessRights/AccessRightsForm.tsx +67 -0
  20. package/src/components/Form/AccessRights/AccessRightsFormActions.tsx +80 -0
  21. package/src/components/Form/AccessRights/Input/AddAction.tsx +31 -0
  22. package/src/components/Form/AccessRights/Input/ContactAccessRightInput.stories.tsx +54 -0
  23. package/src/components/Form/AccessRights/Input/ContactAccessRightInput.tsx +72 -0
  24. package/src/components/Form/AccessRights/Input/ContactAccessRightsInput.styles.ts +22 -0
  25. package/src/components/Form/AccessRights/Input/ContactInputField.tsx +105 -0
  26. package/src/components/Form/AccessRights/Input/RoleInputField.tsx +29 -0
  27. package/src/components/Form/AccessRights/List/ContactAccessRightsList.stories.tsx +97 -0
  28. package/src/components/Form/AccessRights/List/ContactAccessRightsList.styles.ts +71 -0
  29. package/src/components/Form/AccessRights/List/ContactAccessRightsList.tsx +51 -0
  30. package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.stories.tsx +116 -0
  31. package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.tsx +118 -0
  32. package/src/components/Form/AccessRights/List/ContactAccessRightsListItemSkeleton.tsx +26 -0
  33. package/src/components/Form/AccessRights/List/ContactAccessRightsListSkeleton.tsx +28 -0
  34. package/src/components/Form/AccessRights/Stats/AccessRightsStats.styles.ts +18 -0
  35. package/src/components/Form/AccessRights/Stats/AccessRightsStats.tsx +41 -0
  36. package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +52 -0
  37. package/src/components/Form/AccessRights/common/GroupLabel.styles.ts +18 -0
  38. package/src/components/Form/AccessRights/common/GroupLabel.tsx +15 -0
  39. package/src/components/Form/AccessRights/common/Input.styles.ts +48 -0
  40. package/src/components/Form/AccessRights/common/RoleInputSelect.styles.ts +11 -0
  41. package/src/components/Form/AccessRights/common/RoleInputSelect.tsx +57 -0
  42. package/src/components/Form/AccessRights/index.ts +3 -0
  43. package/src/components/Form/AccessRights/useAccessRightsForm.test.tsx +531 -0
  44. package/src/components/Form/AccessRights/useAccessRightsForm.tsx +282 -0
  45. package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +41 -0
  46. package/src/components/Form/Dashboard/DashboardForm.styles.ts +1 -7
  47. package/src/components/Form/Dashboard/DashboardForm.tsx +9 -12
  48. package/src/components/Form/Form.resource.ts +1 -0
  49. package/src/components/Form/Form.styles.ts +17 -0
  50. package/src/components/Form/{Dashboard/DashboardFormActions.tsx → FormActions.tsx} +17 -16
  51. package/src/components/Form/index.ts +1 -0
  52. package/src/components/Header/PageHeader/PageHeader.stories.tsx +9 -0
  53. package/src/components/List/Item/Avatar.tsx +21 -0
  54. package/src/components/List/Item/AvatarSkeleton.tsx +17 -0
  55. package/src/components/List/Item/ListItem.stories.tsx +41 -0
  56. package/src/components/List/Item/ListItem.styles.ts +57 -0
  57. package/src/components/List/Item/ListItem.tsx +32 -0
  58. package/src/components/List/Item/Text.tsx +25 -0
  59. package/src/components/List/Item/TextSkeleton.tsx +22 -0
  60. package/src/components/List/Item/index.ts +14 -0
  61. package/src/components/List/List.stories.tsx +70 -0
  62. package/src/components/List/List.styles.ts +8 -0
  63. package/src/components/List/List.tsx +19 -0
  64. package/src/components/List/index.ts +6 -0
  65. package/src/components/Modal/ModalActions.tsx +4 -1
  66. package/src/components/index.ts +1 -0
  67. package/src/utils/index.ts +1 -0
  68. package/src/utils/useInfiniteScrollListing.ts +106 -0
@@ -0,0 +1,80 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { Link as LinkIcon } from '@mui/icons-material';
4
+
5
+ import { Button } from '../../Button';
6
+ import { useStyles } from '../Form.styles';
7
+ import { useCopyToClipboard } from '../../../utils';
8
+
9
+ import { useAccessRightsForm } from './useAccessRightsForm';
10
+
11
+ export type AccessRightsFormActionsProps = {
12
+ labels: AccessRightsFormActionsLabels;
13
+ onCancel?: () => void;
14
+ resourceLink: string;
15
+ };
16
+
17
+ type AccessRightsFormActionsLabels = {
18
+ cancel: string;
19
+ copyLink: string;
20
+ copyLinkMessages: {
21
+ error: string;
22
+ success: string;
23
+ };
24
+ submit: string;
25
+ };
26
+
27
+ const AccessRightsFormActions = ({
28
+ labels,
29
+ onCancel,
30
+ resourceLink
31
+ }: AccessRightsFormActionsProps): ReactElement => {
32
+ const { classes } = useStyles();
33
+ const { isDirty, submit } = useAccessRightsForm();
34
+ const { copy } = useCopyToClipboard({
35
+ errorMessage: labels.copyLinkMessages.error,
36
+ successMessage: labels.copyLinkMessages.success
37
+ });
38
+
39
+ return (
40
+ <div className={classes.actions}>
41
+ <span>
42
+ <Button
43
+ aria-label={labels.copyLink}
44
+ data-testid="copy-link"
45
+ icon={<LinkIcon />}
46
+ iconVariant="start"
47
+ size="small"
48
+ variant="ghost"
49
+ onClick={() => copy(resourceLink)}
50
+ >
51
+ {labels.copyLink}
52
+ </Button>
53
+ </span>
54
+ <span>
55
+ <Button
56
+ aria-label={labels.cancel}
57
+ data-testid="cancel"
58
+ size="medium"
59
+ variant="secondary"
60
+ onClick={() => onCancel?.()}
61
+ >
62
+ {labels.cancel}
63
+ </Button>
64
+ <Button
65
+ aria-label={labels.submit}
66
+ data-testid="submit"
67
+ disabled={!isDirty}
68
+ size="medium"
69
+ type="submit"
70
+ variant="primary"
71
+ onClick={submit}
72
+ >
73
+ {labels.submit}
74
+ </Button>
75
+ </span>
76
+ </div>
77
+ );
78
+ };
79
+
80
+ export { AccessRightsFormActions };
@@ -0,0 +1,31 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { useFormikContext } from 'formik';
4
+
5
+ import { Add as AddIcon } from '@mui/icons-material';
6
+
7
+ import { ContactAccessRightResource } from '../AccessRights.resource';
8
+ import { IconButton } from '../../../Button';
9
+
10
+ type AddActionProps = {
11
+ label?: string;
12
+ };
13
+
14
+ const AddAction = ({ label }: AddActionProps): ReactElement => {
15
+ const { dirty, isValid, submitForm } =
16
+ useFormikContext<Partial<ContactAccessRightResource>>();
17
+
18
+ return (
19
+ <IconButton
20
+ aria-label={label}
21
+ data-testid="add"
22
+ disabled={!dirty || !isValid}
23
+ icon={<AddIcon />}
24
+ size="medium"
25
+ variant="primary"
26
+ onClick={submitForm}
27
+ />
28
+ );
29
+ };
30
+
31
+ export { AddAction };
@@ -0,0 +1,54 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { Meta, StoryObj } from '@storybook/react';
4
+
5
+ import {
6
+ AccessRightsFormProvider,
7
+ AccessRightsFormProviderProps
8
+ } from '../useAccessRightsForm';
9
+ import { contactsAndGroupsMock } from '../__fixtures__/contactAccessRight.mock';
10
+
11
+ import { ContactAccessRightInput } from './ContactAccessRightInput';
12
+
13
+ const meta: Meta<typeof ContactAccessRightInput> = {
14
+ component: ContactAccessRightInput,
15
+ title: 'components/Form/AccessRights/ContactAccessRightInput'
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof ContactAccessRightInput>;
20
+
21
+ const Wrapper = ({ children }: { children: ReactNode }): ReactElement => {
22
+ const options: AccessRightsFormProviderProps['options'] = {
23
+ contacts: contactsAndGroupsMock(25),
24
+ roles: [{ role: 'viewer' }, { role: 'editor' }]
25
+ };
26
+
27
+ return (
28
+ <AccessRightsFormProvider options={options}>
29
+ {children}
30
+ </AccessRightsFormProvider>
31
+ );
32
+ };
33
+
34
+ export const Default: Story = {
35
+ args: {
36
+ labels: {
37
+ actions: {
38
+ add: 'Add'
39
+ },
40
+ fields: {
41
+ contact: {
42
+ group: 'group',
43
+ noOptionsText: 'No contacts found',
44
+ placeholder: 'Add Contact...'
45
+ }
46
+ }
47
+ }
48
+ },
49
+ render: (args) => (
50
+ <Wrapper>
51
+ <ContactAccessRightInput labels={args.labels} />
52
+ </Wrapper>
53
+ )
54
+ };
@@ -0,0 +1,72 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { FormikProvider, useFormik } from 'formik';
4
+ import { mixed, object } from 'yup';
5
+
6
+ import { useAccessRightsForm } from '../useAccessRightsForm';
7
+ import { ContactAccessRightResource } from '../AccessRights.resource';
8
+
9
+ import { useStyles } from './ContactAccessRightsInput.styles';
10
+ import { ContactInputField, ContactInputFieldProps } from './ContactInputField';
11
+ import { AddAction } from './AddAction';
12
+ import { RoleInputField } from './RoleInputField';
13
+
14
+ export type ContactAccessRightInputProps = {
15
+ labels: ContactAccessRightInputLabels;
16
+ onAddContactAccessRight?: (value: ContactAccessRightResource) => void;
17
+ };
18
+
19
+ type ContactAccessRightInputLabels = {
20
+ actions?: {
21
+ add: string;
22
+ };
23
+ fields: {
24
+ contact: ContactInputFieldProps['labels'];
25
+ role?: {
26
+ label: string;
27
+ };
28
+ };
29
+ };
30
+
31
+ const ContactAccessRightInput = ({
32
+ labels,
33
+ onAddContactAccessRight
34
+ }: ContactAccessRightInputProps): ReactElement => {
35
+ const { classes } = useStyles();
36
+ const { addContactAccessRight, options } = useAccessRightsForm();
37
+
38
+ const formik = useFormik<ContactAccessRightResource>({
39
+ initialValues: { contact: null, role: 'viewer' },
40
+ onSubmit: (values, { resetForm }) => {
41
+ addContactAccessRight(values);
42
+ onAddContactAccessRight?.(values);
43
+ resetForm();
44
+ },
45
+ validationSchema: object({
46
+ contact: mixed()
47
+ .test(
48
+ 'is-available-contact',
49
+ (d) => `${d.path} is not a contact available from the list`,
50
+ (value) => !!options.contacts?.find((c) => c.id === value?.id)
51
+ )
52
+ .required(),
53
+ role: mixed().oneOf(['viewer', 'editor']).required()
54
+ })
55
+ });
56
+
57
+ return (
58
+ <div className={classes.contactAccessRightsInput}>
59
+ <FormikProvider value={formik}>
60
+ <ContactInputField
61
+ id="contact"
62
+ labels={labels.fields.contact}
63
+ name="contact"
64
+ />
65
+ <RoleInputField id="role" name="role" {...labels?.fields?.role} />
66
+ <AddAction />
67
+ </FormikProvider>
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export { ContactAccessRightInput };
@@ -0,0 +1,22 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ const useStyles = makeStyles()((theme) => ({
4
+ contactAccessRightsInput: {
5
+ alignItems: 'center',
6
+ display: 'flex',
7
+ flexGrow: 1,
8
+ gap: theme.spacing(2),
9
+ maxWidth: '520px',
10
+
11
+ width: '100%'
12
+ },
13
+ contactInput: {
14
+ '& .MuiOutlinedInput-root, & .MuiInputBase-input.MuiOutlinedInput-input': {
15
+ height: 'unset',
16
+ lineHeight: '1.5'
17
+ },
18
+ flexGrow: 1
19
+ }
20
+ }));
21
+
22
+ export { useStyles };
@@ -0,0 +1,105 @@
1
+ import { ReactElement, useCallback, useEffect, useState } from 'react';
2
+
3
+ import { useField } from 'formik';
4
+
5
+ import {
6
+ Autocomplete as MuiAutocomplete,
7
+ MenuItem as MuiMenuItem,
8
+ TextField as MuiTextField
9
+ } from '@mui/material';
10
+
11
+ import { useAccessRightsForm } from '../useAccessRightsForm';
12
+ import {
13
+ ContactGroupResource,
14
+ ContactResource,
15
+ isContactGroupResource
16
+ } from '../AccessRights.resource';
17
+ import { useInputStyles } from '../common/Input.styles';
18
+ import { GroupLabel } from '../common/GroupLabel';
19
+
20
+ import { useStyles } from './ContactAccessRightsInput.styles';
21
+
22
+ export type ContactInputFieldProps = {
23
+ id: string;
24
+ labels: ContactInputFieldLabels;
25
+ name: string;
26
+ };
27
+
28
+ type ContactInputFieldLabels = {
29
+ group: string;
30
+ noOptionsText: string;
31
+ placeholder: string;
32
+ };
33
+
34
+ const ContactInputField = ({
35
+ labels,
36
+ ...props
37
+ }: ContactInputFieldProps): ReactElement => {
38
+ const { classes } = useStyles();
39
+ const { classes: inputClasses } = useInputStyles();
40
+
41
+ const {
42
+ options: { contacts }
43
+ } = useAccessRightsForm();
44
+
45
+ const [field, meta, helpers] = useField(props);
46
+
47
+ const [value, setValue] = useState<
48
+ ContactResource | ContactGroupResource | null
49
+ >(meta.initialValue ?? null);
50
+
51
+ const onInputChange = useCallback(
52
+ (_, _value) => {
53
+ helpers.setValue(_value);
54
+ },
55
+ [helpers]
56
+ );
57
+
58
+ const renderInput = useCallback(
59
+ (params) => (
60
+ <MuiTextField
61
+ {...props}
62
+ {...params}
63
+ placeholder={labels.placeholder}
64
+ size="small"
65
+ />
66
+ ),
67
+ [labels, props]
68
+ );
69
+
70
+ useEffect(() => {
71
+ setValue(field.value ?? null);
72
+ }, [field.value]);
73
+
74
+ return (
75
+ <MuiAutocomplete<ContactResource | ContactGroupResource, false, false, true>
76
+ autoComplete
77
+ autoHighlight
78
+ openOnFocus
79
+ className={classes.contactInput}
80
+ componentsProps={{
81
+ popper: {
82
+ className: inputClasses.inputDropdown
83
+ }
84
+ }}
85
+ getOptionLabel={(option) =>
86
+ typeof option === 'string' ? option : option.name
87
+ }
88
+ noOptionsText={labels.noOptionsText}
89
+ options={contacts}
90
+ renderInput={renderInput}
91
+ renderOption={(attr, option) => (
92
+ <MuiMenuItem {...attr} key={`${option.type}--${option.name}`}>
93
+ {option.name}{' '}
94
+ {isContactGroupResource(option) && (
95
+ <GroupLabel label={labels.group} />
96
+ )}
97
+ </MuiMenuItem>
98
+ )}
99
+ value={value}
100
+ onChange={onInputChange}
101
+ />
102
+ );
103
+ };
104
+
105
+ export { ContactInputField };
@@ -0,0 +1,29 @@
1
+ import { ReactElement, useCallback } from 'react';
2
+
3
+ import { useField } from 'formik';
4
+
5
+ import { RoleInputSelect } from '../common/RoleInputSelect';
6
+
7
+ type RoleFieldProps = {
8
+ id: string;
9
+ label?: string;
10
+ name: string;
11
+ };
12
+
13
+ const RoleInputField = ({ label, ...props }: RoleFieldProps): ReactElement => {
14
+ const [field, meta] = useField(props);
15
+
16
+ const onInputChange = useCallback((e) => field.onChange(e), [field]);
17
+
18
+ return (
19
+ <RoleInputSelect
20
+ defaultValue={meta.initialValue}
21
+ {...field}
22
+ {...props}
23
+ {...(label && { label })}
24
+ onChange={onInputChange}
25
+ />
26
+ );
27
+ };
28
+
29
+ export { RoleInputField };
@@ -0,0 +1,97 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { Meta, StoryObj } from '@storybook/react';
4
+
5
+ import {
6
+ AccessRightsFormProvider,
7
+ AccessRightsFormProviderProps
8
+ } from '../useAccessRightsForm';
9
+ import { contactAccessRightsMock } from '../__fixtures__/contactAccessRight.mock';
10
+
11
+ import { ContactAccessRightsList } from './ContactAccessRightsList';
12
+
13
+ const meta: Meta<typeof ContactAccessRightsList> = {
14
+ component: ContactAccessRightsList,
15
+ title: 'components/Form/AccessRights/ContactAccessRightsList'
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof ContactAccessRightsList>;
20
+
21
+ const Wrapper = ({
22
+ children,
23
+ amountOfContacts = 12
24
+ }: {
25
+ amountOfContacts?: number;
26
+ children: ReactNode;
27
+ }): ReactElement => {
28
+ const options: AccessRightsFormProviderProps['options'] = {
29
+ contacts: [],
30
+ roles: [{ role: 'viewer' }, { role: 'editor' }]
31
+ };
32
+
33
+ const initialValues: AccessRightsFormProviderProps['initialValues'] =
34
+ contactAccessRightsMock(amountOfContacts);
35
+
36
+ return (
37
+ <AccessRightsFormProvider initialValues={initialValues} options={options}>
38
+ {children}
39
+ </AccessRightsFormProvider>
40
+ );
41
+ };
42
+
43
+ export const Default: Story = {
44
+ args: {
45
+ labels: {
46
+ emptyState: 'No Contacts with access rights',
47
+ item: {
48
+ group: 'Group',
49
+ state: {
50
+ added: 'Add',
51
+ removed: 'Remove',
52
+ updated: 'Update'
53
+ }
54
+ }
55
+ }
56
+ },
57
+ render: (args) => (
58
+ <Wrapper>
59
+ <ContactAccessRightsList {...args} />
60
+ </Wrapper>
61
+ )
62
+ };
63
+
64
+ export const AsEmptyState: Story = {
65
+ args: Default.args,
66
+ render: (args) => (
67
+ <Wrapper amountOfContacts={0}>
68
+ <ContactAccessRightsList {...args} />
69
+ </Wrapper>
70
+ )
71
+ };
72
+
73
+ const LoadingWrapper = ({
74
+ children
75
+ }: {
76
+ children: ReactNode;
77
+ }): ReactElement => {
78
+ const options: AccessRightsFormProviderProps['options'] = {
79
+ contacts: [],
80
+ roles: [{ role: 'viewer' }, { role: 'editor' }]
81
+ };
82
+
83
+ return (
84
+ <AccessRightsFormProvider loadingStatus="loading" options={options}>
85
+ {children}
86
+ </AccessRightsFormProvider>
87
+ );
88
+ };
89
+
90
+ export const AsLoadingState: Story = {
91
+ args: Default.args,
92
+ render: (args) => (
93
+ <LoadingWrapper>
94
+ <ContactAccessRightsList {...args} />
95
+ </LoadingWrapper>
96
+ )
97
+ };
@@ -0,0 +1,71 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ const useStyles = makeStyles()((theme) => ({
4
+ contactAccessRightsList: {
5
+ borderBottom: `1px solid ${theme.palette.divider}`,
6
+ borderTop: `1px solid ${theme.palette.divider}`,
7
+ maxHeight: '29.5rem',
8
+ maxWidth: '520px',
9
+ overflowY: 'auto'
10
+ },
11
+ contactAccessRightsListEmpty: {
12
+ alignItems: 'center',
13
+ color: theme.palette.text.secondary,
14
+ fontSize: '0.75rem',
15
+ padding: theme.spacing(2, 8),
16
+ textAlign: 'center'
17
+ },
18
+ contactAccessRightsListItem: {
19
+ '& .MuiChip-root': {
20
+ '& .MuiChip-label': {
21
+ lineHeight: 'unset',
22
+ padding: theme.spacing(0, 0.75),
23
+ textOverflow: 'unset'
24
+ },
25
+ '&[data-state="added"]': {
26
+ borderColor: theme.palette.chip.color.success,
27
+ color: theme.palette.chip.color.success
28
+ },
29
+ '&[data-state="removed"]': {
30
+ borderColor: theme.palette.chip.color.error,
31
+ color: theme.palette.chip.color.error
32
+ },
33
+ '&[data-state="updated"]': {
34
+ borderColor: theme.palette.chip.color.info,
35
+ color: theme.palette.chip.color.info
36
+ },
37
+ borderRadius: '0.625rem',
38
+ flexBasis: '3.5rem',
39
+ flexShrink: 0,
40
+ fontSize: '0.6875rem',
41
+
42
+ fontWeight: 500,
43
+ height: 'auto',
44
+ lineHeight: '1.125rem',
45
+ minHeight: 'unset',
46
+ textTransform: 'lowercase'
47
+ },
48
+ '& .MuiListItemSecondaryAction-root': {
49
+ paddingRight: theme.spacing(0.625)
50
+ },
51
+ '& > span:first-of-type': {
52
+ alignItems: 'center',
53
+ display: 'flex',
54
+ flexGrow: 1,
55
+ gap: theme.spacing(2),
56
+ overflow: 'hidden'
57
+ },
58
+ '&[data-is-removed="true"]': {
59
+ '& > span:first-of-type': {
60
+ opacity: 0.3
61
+ }
62
+ },
63
+ gap: theme.spacing(3),
64
+ justifyContent: 'space-between',
65
+
66
+ maxWidth: '520px',
67
+ paddingRight: theme.spacing(7)
68
+ }
69
+ }));
70
+
71
+ export { useStyles };
@@ -0,0 +1,51 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { List } from '../../../List';
4
+ import { useAccessRightsForm } from '../useAccessRightsForm';
5
+
6
+ import {
7
+ ContactAccessRightsListItem,
8
+ ContactAccessRightsListItemProps
9
+ } from './ContactAccessRightsListItem';
10
+ import { useStyles } from './ContactAccessRightsList.styles';
11
+ import { ContactAccessRightsListSkeleton } from './ContactAccessRightsListSkeleton';
12
+
13
+ export type ContactAccessRightsListProps = {
14
+ labels: ContactAccessRightsListLabels;
15
+ };
16
+
17
+ type ContactAccessRightsListLabels = {
18
+ emptyState: string;
19
+ item: ContactAccessRightsListItemProps['labels'];
20
+ };
21
+
22
+ const ContactAccessRightsList = ({
23
+ labels
24
+ }: ContactAccessRightsListProps): ReactElement => {
25
+ const { classes } = useStyles();
26
+ const { contactAccessRights, isLoading } = useAccessRightsForm();
27
+
28
+ return isLoading ? (
29
+ <ContactAccessRightsListSkeleton length={3} />
30
+ ) : (
31
+ <div className={classes.contactAccessRightsList}>
32
+ {contactAccessRights.length === 0 ? (
33
+ <div className={classes.contactAccessRightsListEmpty}>
34
+ {labels.emptyState}
35
+ </div>
36
+ ) : (
37
+ <List>
38
+ {contactAccessRights.map((resource) => (
39
+ <ContactAccessRightsListItem
40
+ key={resource.contactAccessRight.contact?.id}
41
+ labels={labels.item}
42
+ resource={resource}
43
+ />
44
+ ))}
45
+ </List>
46
+ )}
47
+ </div>
48
+ );
49
+ };
50
+
51
+ export { ContactAccessRightsList };