@griddo/ax 11.15.9 → 11.16.0-rc.0

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 (95) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx +173 -0
  3. package/src/__tests__/components/RestrictionsModal/OptionsStep/OptionsStep.test.tsx +160 -0
  4. package/src/__tests__/components/RestrictionsModal/RestrictionsModal.test.tsx +134 -0
  5. package/src/__tests__/components/RestrictionsModal/RolesList/RolesList.test.tsx +234 -0
  6. package/src/__tests__/components/RestrictionsModal/UsersList/UsersList.test.tsx +288 -0
  7. package/src/__tests__/hooks/users.test.tsx +382 -0
  8. package/src/api/pages.tsx +24 -1
  9. package/src/api/sites.tsx +14 -14
  10. package/src/api/structuredData.tsx +17 -7
  11. package/src/components/Browser/index.tsx +12 -0
  12. package/src/components/Browser/style.tsx +5 -1
  13. package/src/components/ConfigPanel/Form/index.tsx +11 -10
  14. package/src/components/ContentItem/index.tsx +31 -0
  15. package/src/components/ContentItem/style.tsx +43 -0
  16. package/src/components/Fields/ImageField/index.tsx +2 -1
  17. package/src/components/Fields/RadioField/index.tsx +3 -3
  18. package/src/components/Fields/RadioField/style.tsx +1 -1
  19. package/src/components/FloatingNote/index.tsx +12 -1
  20. package/src/components/FloatingNote/style.tsx +2 -1
  21. package/src/components/Modal/style.tsx +11 -0
  22. package/src/components/PageFinder/SelectionListItem/index.tsx +27 -17
  23. package/src/components/PageFinder/index.tsx +65 -28
  24. package/src/components/PageFinder/style.tsx +0 -1
  25. package/src/components/RestrictionsModal/CurrentAccessList/index.tsx +74 -0
  26. package/src/components/RestrictionsModal/CurrentAccessList/style.tsx +155 -0
  27. package/src/components/RestrictionsModal/OptionsStep/index.tsx +55 -0
  28. package/src/components/RestrictionsModal/OptionsStep/style.tsx +29 -0
  29. package/src/components/RestrictionsModal/RolesList/RoleItem/index.tsx +54 -0
  30. package/src/components/RestrictionsModal/RolesList/RoleItem/style.tsx +34 -0
  31. package/src/components/RestrictionsModal/RolesList/index.tsx +107 -0
  32. package/src/components/RestrictionsModal/RolesList/style.tsx +76 -0
  33. package/src/components/RestrictionsModal/UsersList/UserItem/index.tsx +78 -0
  34. package/src/components/RestrictionsModal/UsersList/UserItem/style.tsx +73 -0
  35. package/src/components/RestrictionsModal/UsersList/index.tsx +151 -0
  36. package/src/components/RestrictionsModal/UsersList/style.tsx +55 -0
  37. package/src/components/RestrictionsModal/index.tsx +181 -0
  38. package/src/components/RestrictionsModal/style.tsx +31 -0
  39. package/src/components/RestrictionsModal/utils.tsx +24 -0
  40. package/src/components/ScheduleModal/index.tsx +32 -8
  41. package/src/components/SelectionComponent/index.tsx +65 -0
  42. package/src/components/SelectionComponent/style.tsx +69 -0
  43. package/src/components/TableFilters/RoleFilter/index.tsx +3 -3
  44. package/src/components/TableList/TableItem/style.tsx +7 -13
  45. package/src/components/Tabs/index.tsx +2 -1
  46. package/src/components/UserRolesAndSites/index.tsx +2 -2
  47. package/src/components/index.tsx +6 -0
  48. package/src/containers/PageEditor/actions.tsx +43 -0
  49. package/src/containers/PageEditor/constants.tsx +1 -0
  50. package/src/containers/PageEditor/interfaces.tsx +26 -19
  51. package/src/containers/PageEditor/reducer.tsx +32 -19
  52. package/src/containers/Users/reducer.tsx +1 -0
  53. package/src/helpers/forms.tsx +3 -1
  54. package/src/hooks/index.tsx +2 -1
  55. package/src/hooks/users.tsx +154 -14
  56. package/src/modules/ActivityLog/LogFilters/UserFilter/index.tsx +11 -12
  57. package/src/modules/Content/PageItem/index.tsx +17 -10
  58. package/src/modules/Content/index.tsx +1 -3
  59. package/src/modules/GlobalEditor/Editor/index.tsx +6 -0
  60. package/src/modules/GlobalEditor/PageBrowser/index.tsx +6 -0
  61. package/src/modules/GlobalEditor/Preview/index.tsx +4 -2
  62. package/src/modules/GlobalEditor/index.tsx +72 -45
  63. package/src/modules/Navigation/Menus/List/Table/ConfigPanel/Form/index.tsx +3 -5
  64. package/src/modules/Navigation/Menus/List/Table/ConfigPanel/index.tsx +4 -4
  65. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/ConnectedField/index.tsx +2 -3
  66. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +3 -4
  67. package/src/modules/Navigation/Menus/List/Table/SidePanel/index.tsx +5 -6
  68. package/src/modules/PageEditor/Editor/index.tsx +6 -0
  69. package/src/modules/PageEditor/PageBrowser/index.tsx +6 -0
  70. package/src/modules/PageEditor/Preview/index.tsx +9 -2
  71. package/src/modules/PageEditor/atoms.tsx +110 -3
  72. package/src/modules/PageEditor/index.tsx +76 -95
  73. package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +1 -3
  74. package/src/modules/Sites/SitesList/GridView/GridSiteItem/style.tsx +19 -2
  75. package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +2 -2
  76. package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +2 -2
  77. package/src/modules/StructuredData/Form/ConnectedField/index.tsx +2 -2
  78. package/src/modules/StructuredData/Form/index.tsx +120 -75
  79. package/src/modules/StructuredData/Form/style.tsx +7 -1
  80. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -8
  81. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +15 -9
  82. package/src/modules/StructuredData/{StructuredDataList/StructuredDataItem/utils.tsx → utils.tsx} +16 -2
  83. package/src/modules/Users/Profile/atoms.tsx +62 -4
  84. package/src/modules/Users/Profile/index.tsx +83 -4
  85. package/src/modules/Users/Profile/style.tsx +60 -0
  86. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +3 -5
  87. package/src/modules/Users/UserCreate/SiteItem/index.tsx +2 -2
  88. package/src/modules/Users/UserCreate/atoms.tsx +69 -0
  89. package/src/modules/Users/UserCreate/index.tsx +149 -70
  90. package/src/modules/Users/UserCreate/style.tsx +24 -1
  91. package/src/modules/Users/UserForm/index.tsx +260 -146
  92. package/src/modules/Users/UserForm/style.tsx +13 -1
  93. package/src/modules/Users/UserList/UserItem/index.tsx +3 -3
  94. package/src/types/index.tsx +19 -6
  95. package/src/types/logs.tsx +1 -1
@@ -0,0 +1,43 @@
1
+ import styled from "styled-components";
2
+
3
+ const ListItem = styled.li`
4
+ display: flex;
5
+ width: 100%;
6
+ padding: ${(p) => p.theme.spacing.s};
7
+ background-color: ${(p) => p.theme.color.uiBarBackground};
8
+ border: 1px solid ${(p) => p.theme.color.uiLine};
9
+ border-radius: ${(p) => p.theme.radii.s};
10
+ margin-bottom: ${(p) => p.theme.spacing.xs};
11
+ `;
12
+
13
+ const Title = styled.div`
14
+ ${(p) => p.theme.textStyle.fieldContent}
15
+ color: ${(p) => p.theme.colors.textHighEmphasis};
16
+ `;
17
+
18
+ const Header = styled.div`
19
+ display: flex;
20
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
21
+ `;
22
+
23
+ const Type = styled.div`
24
+ ${(p) => p.theme.textStyle.headingXXS};
25
+ color: ${(p) => p.theme.colors.textLowEmphasis};
26
+ `;
27
+
28
+ const ContentDate = styled.div`
29
+ ${(p) => p.theme.textStyle.uiXS};
30
+ color: ${(p) => p.theme.colors.textLowEmphasis};
31
+ margin-left: auto;
32
+ `;
33
+
34
+ const Content = styled.div`
35
+ width: 100%;
36
+ `;
37
+
38
+ const Slug = styled.span`
39
+ ${({ theme }) => theme.textStyle.uiXS};
40
+ color: ${({ theme }) => theme.colors.textMediumEmphasis};
41
+ `;
42
+
43
+ export { Content, ContentDate, Header, ListItem, Slug, Title, Type };
@@ -134,7 +134,7 @@ const ImageField = (props: IImageFieldProps) => {
134
134
  {imageElement}
135
135
  </S.ImageContainer>
136
136
  )}
137
- {selectPosition && (
137
+ {selectPosition && !disabled && (
138
138
  <S.PositionWrapper>
139
139
  <S.PositionTitle>
140
140
  <Icon name="grid" size="16" />
@@ -166,6 +166,7 @@ const ImageField = (props: IImageFieldProps) => {
166
166
  fieldType="TextField"
167
167
  value={value.alt || ""}
168
168
  onChange={handleAltChange}
169
+ disabled={disabled}
169
170
  />
170
171
  </S.FieldsWrapper>
171
172
  )}
@@ -1,9 +1,7 @@
1
- import React from "react";
2
-
3
1
  import * as S from "./style";
4
2
 
5
3
  const RadioField = (props: IRadioFieldProps): JSX.Element => {
6
- const { name, title, value, checked, error, disabled, onChange } = props;
4
+ const { name, title, value, checked, error, disabled, tabIndex, onChange } = props;
7
5
 
8
6
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
9
7
  if (onChange) {
@@ -23,6 +21,7 @@ const RadioField = (props: IRadioFieldProps): JSX.Element => {
23
21
  disabled={disabled}
24
22
  onChange={handleChange}
25
23
  data-testid="radio-field-input"
24
+ tabIndex={tabIndex}
26
25
  />
27
26
  <S.CheckMark checked={checked} error={error} />
28
27
  </S.Label>
@@ -38,6 +37,7 @@ export interface IRadioFieldProps {
38
37
  error?: boolean;
39
38
  disabled?: boolean;
40
39
  onChange?: (value: string) => void;
40
+ tabIndex?: number;
41
41
  }
42
42
 
43
43
  export default RadioField;
@@ -4,12 +4,12 @@ export const Wrapper = styled.div``;
4
4
 
5
5
  export const Label = styled.label`
6
6
  ${(p) => p.theme.textStyle.fieldContent};
7
- display: block;
8
7
  position: relative;
9
8
  padding-left: calc(${(p) => p.theme.spacing.s} + ${(p) => p.theme.spacing.xs});
10
9
  margin-bottom: ${(p) => p.theme.spacing.xs};
11
10
  cursor: pointer;
12
11
  user-select: none;
12
+ min-height: 16px;
13
13
  `;
14
14
 
15
15
  export const CheckMark = styled.span<{ checked: boolean | undefined; error: boolean | undefined }>`
@@ -5,8 +5,19 @@ import * as S from "./style";
5
5
  const FloatingNote = (props: IFloatingNoteProps): JSX.Element => {
6
6
  const { message, icon, className, btnText, onClick } = props;
7
7
 
8
+ const handleClick = () => {
9
+ if (!btnText && onClick) {
10
+ onClick();
11
+ }
12
+ };
13
+
8
14
  return (
9
- <S.Wrapper data-testid="floating-note-wrapper" className={className}>
15
+ <S.Wrapper
16
+ data-testid="floating-note-wrapper"
17
+ className={className}
18
+ onClick={handleClick}
19
+ interactive={!btnText && !!onClick}
20
+ >
10
21
  {icon && (
11
22
  <S.IconWrapper>
12
23
  <Icon name={icon} size="16" />
@@ -1,11 +1,12 @@
1
1
  import styled from "styled-components";
2
2
 
3
- const Wrapper = styled.div`
3
+ const Wrapper = styled.div<{ interactive: boolean }>`
4
4
  display: flex;
5
5
  background-color: ${(p) => p.theme.color.uiBackground03};
6
6
  padding: ${(p) => p.theme.spacing.s};
7
7
  border-radius: ${(p) => p.theme.radii.s};
8
8
  z-index: 98;
9
+ cursor: ${(p) => (p.interactive ? "pointer" : "default")};
9
10
  `;
10
11
 
11
12
  const Text = styled.div`
@@ -84,6 +84,17 @@ const ModalContent = styled.div<{ overflow: string | undefined }>`
84
84
  position: relative;
85
85
  flex-grow: 1;
86
86
  overflow: ${(p) => p.overflow};
87
+
88
+ ::-webkit-scrollbar {
89
+ -webkit-appearance: none;
90
+ width: 4px;
91
+ height: 4px;
92
+ }
93
+
94
+ ::-webkit-scrollbar-thumb {
95
+ border-radius: 4px;
96
+ background-color: ${(p) => p.theme.colors.iconNonActive};
97
+ }
87
98
  `;
88
99
 
89
100
  const Title = styled.div`
@@ -1,11 +1,12 @@
1
- import { CheckField } from "@ax/components";
1
+ import { CheckField, Tooltip } from "@ax/components";
2
2
  import { getFormattedDateWithTimezone } from "@ax/helpers";
3
3
  import type { ICheck, IPage } from "@ax/types";
4
4
 
5
5
  import * as S from "./style";
6
6
 
7
7
  const SelectionListItem = (props: ISelectionListItemProps): JSX.Element => {
8
- const { option, title, type, date, onClick, pageID, isSelected, onChange, multiple, slug } = props;
8
+ const { option, title, type, date, onClick, pageID, isSelected, onChange, multiple, slug, isCheckboxDisabled } =
9
+ props;
9
10
 
10
11
  const handleClick = () => (multiple ? onChange({ value: option, isChecked: !isSelected }) : onClick(option));
11
12
  const handleChange = (value: ICheck) => onChange({ value: option, isChecked: value.isChecked });
@@ -13,21 +14,29 @@ const SelectionListItem = (props: ISelectionListItemProps): JSX.Element => {
13
14
  const pageType = type || "Page";
14
15
 
15
16
  return (
16
- <S.ListItem>
17
- {multiple && (
18
- <S.CheckWrapper>
19
- <CheckField name="check" value={pageID} checked={isSelected} onChange={handleChange} />
20
- </S.CheckWrapper>
21
- )}
22
- <S.Content onClick={handleClick} data-testid="selection-list-item">
23
- <S.Header>
24
- <S.Type>{pageType}</S.Type>
25
- <S.Date>{getFormattedDateWithTimezone(date, "d MMM Y")}</S.Date>
26
- </S.Header>
27
- <S.Title>{title}</S.Title>
28
- <S.Slug>{slug}</S.Slug>
29
- </S.Content>
30
- </S.ListItem>
17
+ <Tooltip content={isCheckboxDisabled ? "Already selected in another language version" : undefined} bottom>
18
+ <S.ListItem>
19
+ {multiple && (
20
+ <S.CheckWrapper>
21
+ <CheckField
22
+ name="check"
23
+ value={pageID}
24
+ checked={isSelected}
25
+ onChange={handleChange}
26
+ disabled={isCheckboxDisabled}
27
+ />
28
+ </S.CheckWrapper>
29
+ )}
30
+ <S.Content onClick={handleClick} data-testid="selection-list-item">
31
+ <S.Header>
32
+ <S.Type>{pageType}</S.Type>
33
+ <S.Date>{getFormattedDateWithTimezone(date, "d MMM Y")}</S.Date>
34
+ </S.Header>
35
+ <S.Title>{title}</S.Title>
36
+ <S.Slug>{slug}</S.Slug>
37
+ </S.Content>
38
+ </S.ListItem>
39
+ </Tooltip>
31
40
  );
32
41
  };
33
42
 
@@ -42,6 +51,7 @@ interface ISelectionListItemProps {
42
51
  isSelected: boolean;
43
52
  onChange: (item: { value: IPage; isChecked: boolean }) => void;
44
53
  multiple?: boolean;
54
+ isCheckboxDisabled?: boolean;
45
55
  }
46
56
 
47
57
  export default SelectionListItem;
@@ -12,13 +12,28 @@ import SelectionListItem from "./SelectionListItem";
12
12
  import * as S from "./style";
13
13
 
14
14
  const PageFinder = (props: IPageFinderProps): JSX.Element => {
15
- const { onClick, currentSiteID, isOpen, allSites, lang, globalLangs, multiple, hideSites, pages } = props;
15
+ const {
16
+ onClick,
17
+ currentSiteID,
18
+ isOpen,
19
+ allSites,
20
+ lang,
21
+ globalLangs,
22
+ multiple,
23
+ hideSites,
24
+ pages,
25
+ globalContent,
26
+ alwaysShowButton,
27
+ fromPageOnly = true,
28
+ defaultLang,
29
+ disableTranslations = false,
30
+ } = props;
16
31
 
17
32
  const langOptions = globalLangs.map((lang) => ({ label: lang.label, value: lang.id.toString() }));
18
33
 
19
34
  const initialState: IState = {
20
- site: currentSiteID || allSites[0].id,
21
- lang: lang.id,
35
+ site: globalContent ? null : currentSiteID || allSites[0].id,
36
+ lang: defaultLang || lang.id,
22
37
  type: "all",
23
38
  items: [],
24
39
  page: 1,
@@ -32,12 +47,14 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
32
47
  const [showSelected, setShowSelected] = useState(false);
33
48
  const [searchQuery, setSearchQuery] = useState<string>("");
34
49
 
35
- const defultTypeOptions = [
36
- { label: "All content", value: "all" },
37
- { label: "Base templates", value: "basic" },
38
- { label: "List pages", value: "list" },
39
- { label: "Static pages", value: "static" },
40
- ];
50
+ const defultTypeOptions = !globalContent
51
+ ? [
52
+ { label: "All content", value: "all" },
53
+ { label: "Base templates", value: "basic" },
54
+ { label: "List pages", value: "list" },
55
+ { label: "Static pages", value: "static" },
56
+ ]
57
+ : [{ label: "All content", value: "all" }];
41
58
 
42
59
  const [contentTypeOptions, setContentTypeOptions] = useState<{ label: string; value: string }[]>(defultTypeOptions);
43
60
 
@@ -133,10 +150,15 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
133
150
 
134
151
  const getAndSetContentTypes = async () => {
135
152
  const { site, lang } = state;
136
- const { data } = await structuredData.getContentTypes(site, lang);
137
- const contentTypes = data.items
138
- ? data.items.map((type: IContentType) => ({ label: type.title, value: type.id }))
153
+ const { data }: { data: { items: IContentType[] } } = await structuredData.getData(null, site, lang);
154
+ let contentTypes = data.items
155
+ ? data.items.map((type) => ({ label: type.title, value: type.id, fromPage: type.fromPage }))
139
156
  : [];
157
+
158
+ if (fromPageOnly) {
159
+ contentTypes = contentTypes.filter((type) => type.fromPage === true);
160
+ }
161
+
140
162
  changeContentTypeOptions(contentTypes);
141
163
  changeState("type", "all");
142
164
  };
@@ -178,21 +200,31 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
178
200
  };
179
201
 
180
202
  const getPageList = (items: IPage[]) =>
181
- items?.map((page: IPage) => (
182
- <SelectionListItem
183
- option={page}
184
- key={page.id}
185
- title={page.title}
186
- type={getStructuredDataTitle(page.structuredData)}
187
- date={page.modified}
188
- onClick={onClick}
189
- pageID={page.id}
190
- isSelected={selectedPagesIds.includes(page.id)}
191
- onChange={handleChange}
192
- multiple={multiple}
193
- slug={page.slug.replace(/^\//, "")}
194
- />
195
- ));
203
+ items?.map((page: IPage) => {
204
+ const hasTranslationSelected = page.pageLanguages?.some((lang) => selectedPagesIds.includes(lang.pageId));
205
+ const isPageSelected = selectedPagesIds.includes(page.id);
206
+ const isPageOrTranslationSelected = disableTranslations
207
+ ? isPageSelected || hasTranslationSelected
208
+ : isPageSelected;
209
+ const isCheckboxDisabled = disableTranslations && isPageOrTranslationSelected && !isPageSelected;
210
+
211
+ return (
212
+ <SelectionListItem
213
+ option={page}
214
+ key={page.id}
215
+ title={page.title}
216
+ type={getStructuredDataTitle(page.structuredData)}
217
+ date={page.modified}
218
+ onClick={onClick}
219
+ pageID={page.id}
220
+ isSelected={isPageOrTranslationSelected}
221
+ onChange={handleChange}
222
+ multiple={multiple}
223
+ slug={page.slug.replace(/^\//, "")}
224
+ isCheckboxDisabled={isCheckboxDisabled}
225
+ />
226
+ );
227
+ });
196
228
 
197
229
  const showPagination = state.totalItems > itemsPerPage;
198
230
 
@@ -267,7 +299,7 @@ const PageFinder = (props: IPageFinderProps): JSX.Element => {
267
299
  </S.PaginationWrapper>
268
300
  </S.ListWrapper>
269
301
  )}
270
- {selectedPages.length > 0 && (
302
+ {(selectedPages.length > 0 || alwaysShowButton) && (
271
303
  <S.ActionWrapper>
272
304
  <Button type="button" onClick={handleButtonClick}>
273
305
  Add pages
@@ -297,6 +329,11 @@ interface IPageFinderProps {
297
329
  multiple?: boolean;
298
330
  hideSites?: boolean;
299
331
  pages?: number[];
332
+ globalContent?: boolean;
333
+ alwaysShowButton?: boolean;
334
+ fromPageOnly?: boolean;
335
+ defaultLang?: number;
336
+ disableTranslations?: boolean;
300
337
  }
301
338
 
302
339
  const mapStateToProps = (state: IRootState) => ({
@@ -5,7 +5,6 @@ const Wrapper = styled.div`
5
5
  position: relative;
6
6
  width: 100%;
7
7
  height: 100%;
8
- padding-bottom: ${(p) => p.theme.spacing.xl};
9
8
  flex-direction: column;
10
9
 
11
10
  #react-select-7-option-3 {
@@ -0,0 +1,74 @@
1
+ import { useSelector } from "react-redux";
2
+
3
+ import { Button } from "@ax/components";
4
+ import type { IRole, IRootState, IUser } from "@ax/types";
5
+
6
+ import RoleItem from "../RolesList/RoleItem";
7
+ import UserItem from "../UsersList/UserItem";
8
+
9
+ import * as S from "./style";
10
+
11
+ const CurrentAccessList = (props: ICurrentAccessListProps): JSX.Element => {
12
+ const { restrictionType, currentAccessGrants, onEdit, users: allUsers } = props;
13
+ const roles = useSelector((state: IRootState) => state.users.roles);
14
+
15
+ const isRoles = restrictionType === "roles";
16
+ const itemIds = isRoles ? currentAccessGrants?.roles || [] : currentAccessGrants?.users || [];
17
+
18
+ // Filter users to show only those selected
19
+ const selectedUsers = allUsers.filter((user) => itemIds.includes(user.id!));
20
+
21
+ const getTitle = () => {
22
+ return isRoles ? (
23
+ <>
24
+ Only these roles <strong>can edit</strong> this page
25
+ </>
26
+ ) : (
27
+ <>
28
+ Only these users <strong>can edit</strong> this page
29
+ </>
30
+ );
31
+ };
32
+
33
+ return (
34
+ <S.Wrapper>
35
+ <S.Content>
36
+ <S.Text>{getTitle()}</S.Text>
37
+ <S.List>
38
+ {isRoles ? (
39
+ <>
40
+ {(roles as IRole[])
41
+ .filter((role) => itemIds.includes(role.id))
42
+ .map((role) => (
43
+ <RoleItem key={role.id} role={role} selection={false} />
44
+ ))}
45
+ </>
46
+ ) : (
47
+ <>
48
+ {selectedUsers.map((user) => (
49
+ <UserItem key={user.id} user={user} selection={false} />
50
+ ))}
51
+ </>
52
+ )}
53
+ </S.List>
54
+ </S.Content>
55
+ <S.Actions>
56
+ <Button type="button" onClick={onEdit}>
57
+ MANAGE USER ACCESS
58
+ </Button>
59
+ </S.Actions>
60
+ </S.Wrapper>
61
+ );
62
+ };
63
+
64
+ interface ICurrentAccessListProps {
65
+ restrictionType: "users" | "roles";
66
+ currentAccessGrants?: {
67
+ users?: number[];
68
+ roles?: number[];
69
+ };
70
+ onEdit: () => void;
71
+ users: IUser[];
72
+ }
73
+
74
+ export default CurrentAccessList;
@@ -0,0 +1,155 @@
1
+ import { Row } from "@ax/components/TableList/TableItem/style";
2
+ import styled from "styled-components";
3
+
4
+ const Wrapper = styled.div`
5
+ display: flex;
6
+ flex-direction: column;
7
+ width: 100%;
8
+ height: 100%;
9
+ `;
10
+
11
+ const Text = styled.div`
12
+ ${(p) => p.theme.textStyle.uiM};
13
+ margin-bottom: ${(p) => p.theme.spacing.s};
14
+ `;
15
+
16
+ const List = styled.div`
17
+ flex: 1;
18
+ overflow-y: auto;
19
+ min-height: 0;
20
+
21
+ ::-webkit-scrollbar {
22
+ -webkit-appearance: none;
23
+ width: 4px;
24
+ }
25
+
26
+ ::-webkit-scrollbar-thumb {
27
+ border-radius: 4px;
28
+ background-color: ${(p) => p.theme.colors.iconNonActive};
29
+ }
30
+ `;
31
+
32
+ const Content = styled.div`
33
+ flex: 1;
34
+ overflow-y: auto;
35
+ min-height: 0;
36
+ padding: ${(p) => `${p.theme.spacing.m} ${p.theme.spacing.m} 0 ${p.theme.spacing.m}`};
37
+
38
+ ::-webkit-scrollbar {
39
+ -webkit-appearance: none;
40
+ width: 4px;
41
+ }
42
+
43
+ ::-webkit-scrollbar-thumb {
44
+ border-radius: 4px;
45
+ background-color: ${(p) => p.theme.colors.iconNonActive};
46
+ }
47
+ `;
48
+
49
+ const Actions = styled.div`
50
+ display: flex;
51
+ justify-content: flex-end;
52
+ flex-shrink: 0;
53
+ padding: ${(p) => p.theme.spacing.m};
54
+ `;
55
+
56
+ const ItemRow = styled(Row)`
57
+ display: flex;
58
+ align-items: center;
59
+ padding: ${(p) => `0 ${p.theme.spacing.s}`};
60
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
61
+ height: 61px;
62
+ `;
63
+
64
+ const AvatarWrapper = styled.div`
65
+ padding-left: ${(p) => p.theme.spacing.xs};
66
+ padding-right: ${(p) => p.theme.spacing.xs};
67
+ `;
68
+
69
+ const UserInfo = styled.div`
70
+ display: flex;
71
+ flex-flow: column nowrap;
72
+ flex-grow: 1;
73
+ min-width: 0;
74
+ padding-left: ${(p) => p.theme.spacing.xs};
75
+ `;
76
+
77
+ const UserName = styled.div`
78
+ ${(p) => p.theme.textStyle.uiM};
79
+ font-weight: 600;
80
+ color: ${(p) => p.theme.colors.textHighEmphasis};
81
+ width: 100%;
82
+ min-width: 0;
83
+ > * {
84
+ min-width: 0;
85
+ max-width: 100%;
86
+ }
87
+ `;
88
+
89
+ const Name = styled.div`
90
+ width: 100%;
91
+ ${(p) => p.theme.textStyle.uiM};
92
+ font-weight: 600;
93
+ color: ${(p) => p.theme.colors.textHighEmphasis};
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ display: block;
98
+ min-width: 0;
99
+ `;
100
+
101
+ const UserEmail = styled.div`
102
+ ${(p) => p.theme.textStyle.uiXS};
103
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
104
+ width: 100%;
105
+ min-width: 0;
106
+ > * {
107
+ min-width: 0;
108
+ max-width: 100%;
109
+ }
110
+ `;
111
+
112
+ const Email = styled.div`
113
+ width: 100%;
114
+ ${(p) => p.theme.textStyle.uiXS};
115
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
116
+ overflow: hidden;
117
+ text-overflow: ellipsis;
118
+ white-space: nowrap;
119
+ display: block;
120
+ min-width: 0;
121
+ `;
122
+
123
+ const RoleContent = styled.div`
124
+ display: flex;
125
+ flex-flow: column nowrap;
126
+ flex-grow: 1;
127
+ min-width: 0;
128
+ `;
129
+
130
+ const RoleName = styled.div`
131
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
132
+ `;
133
+
134
+ const RoleDescription = styled.div`
135
+ ${(p) => p.theme.textStyle.uiXS};
136
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
137
+ `;
138
+
139
+ export {
140
+ Wrapper,
141
+ Content,
142
+ Text,
143
+ List,
144
+ Actions,
145
+ ItemRow,
146
+ AvatarWrapper,
147
+ UserInfo,
148
+ UserName,
149
+ Name,
150
+ UserEmail,
151
+ Email,
152
+ RoleContent,
153
+ RoleName,
154
+ RoleDescription,
155
+ };
@@ -0,0 +1,55 @@
1
+ import { Button, SelectionComponent } from "@ax/components";
2
+
3
+ import * as S from "./style";
4
+
5
+ const OptionsStep = (props: IOptionsStepProps): JSX.Element => {
6
+ const { restrictionType, onRestrictionTypeChange, onCancel, onNext } = props;
7
+
8
+ return (
9
+ <S.Wrapper>
10
+ <S.Text>
11
+ By default, <strong>all users</strong> have access to the page. However, you can manage access by{" "}
12
+ <strong>granting permissions to specific users or assigning a specific role.</strong>
13
+ </S.Text>
14
+
15
+ <S.OptionsGroup>
16
+ <SelectionComponent
17
+ name="restrictionType"
18
+ label="By users"
19
+ value="users"
20
+ isSelected={restrictionType === "users"}
21
+ onToggle={onRestrictionTypeChange}
22
+ description="Only the selected users can edit the page."
23
+ multi={false}
24
+ />
25
+ <SelectionComponent
26
+ name="restrictionType"
27
+ label="By roles"
28
+ value="roles"
29
+ isSelected={restrictionType === "roles"}
30
+ onToggle={onRestrictionTypeChange}
31
+ description="Only users with a specific roles can edit the page."
32
+ multi={false}
33
+ />
34
+ </S.OptionsGroup>
35
+
36
+ <S.Actions>
37
+ <Button type="button" buttonStyle="text" onClick={onCancel}>
38
+ Cancel
39
+ </Button>
40
+ <Button type="button" onClick={onNext}>
41
+ Manage users access
42
+ </Button>
43
+ </S.Actions>
44
+ </S.Wrapper>
45
+ );
46
+ };
47
+
48
+ interface IOptionsStepProps {
49
+ restrictionType: "users" | "roles";
50
+ onRestrictionTypeChange: (value: string) => void;
51
+ onCancel: () => void;
52
+ onNext: () => void;
53
+ }
54
+
55
+ export default OptionsStep;
@@ -0,0 +1,29 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ display: flex;
5
+ flex-direction: column;
6
+ width: 100%;
7
+ height: 100%;
8
+ padding: ${(p) => `${p.theme.spacing.m} ${p.theme.spacing.m} ${p.theme.spacing.m} ${p.theme.spacing.m}`};
9
+ `;
10
+
11
+ const Text = styled.div`
12
+ ${(p) => p.theme.textStyle.uiM};
13
+ `;
14
+
15
+ const OptionsGroup = styled.div`
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: ${(p) => p.theme.spacing.xs};
19
+ margin: ${(p) => `${p.theme.spacing.m} 0`};
20
+ `;
21
+
22
+ const Actions = styled.div`
23
+ display: flex;
24
+ gap: ${(p) => p.theme.spacing.s};
25
+ justify-content: flex-end;
26
+ margin-top: auto;
27
+ `;
28
+
29
+ export { Wrapper, Text, OptionsGroup, Actions };