@griddo/ax 10.1.96 → 10.2.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 (201) hide show
  1. package/package.json +3 -2
  2. package/src/__mocks__/axios/Roles.ts +10 -0
  3. package/src/__mocks__/axios/UserList.ts +545 -0
  4. package/src/__mocks__/store/GenericStore.ts +25 -0
  5. package/src/__mocks__/store/Roles.ts +1050 -0
  6. package/src/__mocks__/store/SitesList.ts +7 -1
  7. package/src/__mocks__/store/UserList.ts +482 -0
  8. package/src/__mocks__/store/UsersCreate.ts +298 -0
  9. package/src/__tests__/components/Avatar/Avatar.test.tsx +49 -48
  10. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +2 -0
  11. package/src/__tests__/components/ConfigPanel/Form/Form.test.tsx +2 -0
  12. package/src/__tests__/components/ConfigPanel/GlobalPageForm/GlobalPageForm.test.tsx +25 -0
  13. package/src/__tests__/components/Fields/Button/Button.test.tsx +2 -2
  14. package/src/__tests__/components/Fields/ImageField/ImageField.test.tsx +2 -0
  15. package/src/__tests__/components/Fields/IntegrationsField/IntegrationsField.test.tsx +44 -2
  16. package/src/__tests__/components/Fields/Tooltip/Tooltip.test.tsx +0 -1
  17. package/src/__tests__/components/Gallery/Gallery.test.tsx +4 -0
  18. package/src/__tests__/components/Gallery/GalleryPanel/DetailPanel/DetailPanel.test.tsx +14 -0
  19. package/src/__tests__/components/Gallery/GalleryPanel/GalleryPanel.test.tsx +2 -1
  20. package/src/__tests__/components/Lists/Lists.test.tsx +3 -3
  21. package/src/__tests__/components/Login/Login.test.tsx +1 -1
  22. package/src/__tests__/components/TableFilters/DateFilter/DateFilter.test.tsx +1 -1
  23. package/src/__tests__/components/TableFilters/NameFilter/NameFilter.test.tsx +1 -1
  24. package/src/__tests__/components/TableFilters/RoleFilter/RoleFilter.test.tsx +165 -0
  25. package/src/__tests__/components/TableFilters/StatusFilter/StatusFilter.test.tsx +2 -2
  26. package/src/__tests__/components/TableFilters/UsersFilter/UsersFilter.test.tsx +153 -0
  27. package/src/__tests__/components/Tabs/Tabs.test.tsx +1 -1
  28. package/src/__tests__/modules/Settings/Integrations/Integrations.test.tsx +6 -0
  29. package/src/__tests__/modules/Sites/Sites.test.tsx +2 -1
  30. package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +14 -5
  31. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +6 -4
  32. package/src/__tests__/modules/Users/Roles/BulkHeader/BulkHeader.test.tsx +158 -0
  33. package/src/__tests__/modules/Users/Roles/Roles.test.tsx +619 -0
  34. package/src/__tests__/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/RoleItem.test.tsx +107 -0
  35. package/src/__tests__/modules/Users/UserCreate/SiteItem/RolesModal/RolesModal.test.tsx +159 -0
  36. package/src/__tests__/modules/Users/UserCreate/SiteItem/SiteItem.test.tsx +175 -0
  37. package/src/__tests__/modules/Users/UserCreate/UserCreate.test.tsx +320 -0
  38. package/src/__tests__/modules/Users/UserList/UserItem/UserItem.test.tsx +417 -0
  39. package/src/__tests__/modules/Users/UserList/UserList.test.tsx +310 -0
  40. package/src/api/index.tsx +2 -0
  41. package/src/api/roles.tsx +77 -0
  42. package/src/api/users.tsx +22 -2
  43. package/src/components/ActionMenu/index.tsx +12 -6
  44. package/src/components/Avatar/index.tsx +5 -3
  45. package/src/components/Avatar/style.tsx +8 -9
  46. package/src/components/BulkSelectionOptions/index.tsx +19 -12
  47. package/src/components/BulkSelectionOptions/style.tsx +6 -11
  48. package/src/components/ConfigPanel/Form/index.tsx +24 -1
  49. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +17 -4
  50. package/src/components/ElementsTooltip/index.tsx +1 -1
  51. package/src/components/Fields/IntegrationsField/index.tsx +5 -6
  52. package/src/components/Fields/RadioField/index.tsx +1 -1
  53. package/src/components/Fields/ReferenceField/AutoPanel/index.tsx +3 -3
  54. package/src/components/Fields/ReferenceField/ItemList/index.tsx +1 -1
  55. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +3 -3
  56. package/src/components/Fields/TagsField/index.tsx +4 -2
  57. package/src/components/Fields/TextField/index.tsx +3 -0
  58. package/src/components/Fields/UrlField/index.tsx +5 -5
  59. package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +42 -15
  60. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +1 -1
  61. package/src/components/Gallery/GalleryPanel/index.tsx +20 -5
  62. package/src/components/Gallery/index.tsx +12 -6
  63. package/src/components/Icon/index.tsx +9 -1
  64. package/src/components/MainWrapper/AppBar/atoms.tsx +2 -2
  65. package/src/components/MainWrapper/AppBar/index.tsx +12 -10
  66. package/src/components/MainWrapper/AppBar/style.tsx +2 -1
  67. package/src/components/Modal/style.tsx +2 -2
  68. package/src/components/Nav/index.tsx +12 -2
  69. package/src/components/PageFinder/index.tsx +2 -2
  70. package/src/components/SearchField/index.tsx +3 -8
  71. package/src/components/TableFilters/PermissionsFilter/index.tsx +50 -0
  72. package/src/{modules/Users/UserList/HeaderMenus/Name → components/TableFilters/PermissionsFilter}/style.tsx +6 -2
  73. package/src/components/TableFilters/RoleFilter/index.tsx +61 -0
  74. package/src/components/TableFilters/RoleFilter/style.tsx +28 -0
  75. package/src/components/TableFilters/UsersFilter/index.tsx +55 -0
  76. package/src/components/TableFilters/UsersFilter/style.tsx +31 -0
  77. package/src/components/TableFilters/index.tsx +6 -0
  78. package/src/components/TableList/TableItem/style.tsx +2 -2
  79. package/src/components/TableList/style.tsx +1 -1
  80. package/src/components/index.tsx +7 -1
  81. package/src/containers/App/actions.tsx +3 -3
  82. package/src/containers/PageEditor/actions.tsx +14 -20
  83. package/src/containers/Redirects/actions.tsx +1 -0
  84. package/src/containers/Sites/actions.tsx +22 -14
  85. package/src/containers/Sites/interfaces.tsx +3 -3
  86. package/src/containers/Sites/reducer.tsx +1 -1
  87. package/src/containers/StructuredData/actions.tsx +15 -3
  88. package/src/containers/Users/actions.tsx +160 -26
  89. package/src/containers/Users/constants.tsx +8 -10
  90. package/src/containers/Users/interfaces.tsx +25 -3
  91. package/src/containers/Users/reducer.tsx +24 -15
  92. package/src/guards/index.tsx +2 -1
  93. package/src/guards/restricted/index.tsx +21 -0
  94. package/src/hooks/index.tsx +3 -0
  95. package/src/hooks/users.tsx +38 -0
  96. package/src/modules/App/Routing/NavMenu/NavItem/index.tsx +10 -5
  97. package/src/modules/App/Routing/NavMenu/index.tsx +16 -7
  98. package/src/modules/App/Routing/PrivateRoute/index.tsx +13 -5
  99. package/src/modules/App/Routing/index.tsx +17 -6
  100. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/style.tsx +1 -0
  101. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +2 -10
  102. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +27 -12
  103. package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +4 -2
  104. package/src/modules/Categories/CategoriesList/index.tsx +27 -8
  105. package/src/modules/Content/BulkHeader/index.tsx +7 -3
  106. package/src/modules/Content/PageImporter/index.tsx +1 -1
  107. package/src/modules/Content/PageItem/index.tsx +45 -31
  108. package/src/modules/Content/PageItem/style.tsx +2 -1
  109. package/src/modules/Content/index.tsx +22 -13
  110. package/src/modules/FramePreview/index.tsx +2 -2
  111. package/src/modules/GlobalEditor/index.tsx +68 -53
  112. package/src/modules/GlobalSettings/index.tsx +2 -0
  113. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +2 -10
  114. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +9 -11
  115. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +5 -1
  116. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +10 -5
  117. package/src/modules/Navigation/Defaults/Item/index.tsx +41 -27
  118. package/src/modules/Navigation/Defaults/Item/style.tsx +2 -1
  119. package/src/modules/Navigation/Defaults/index.tsx +29 -10
  120. package/src/modules/Navigation/Menus/List/Table/Header/index.tsx +7 -5
  121. package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +10 -10
  122. package/src/modules/Navigation/Menus/List/Table/Item/style.tsx +2 -1
  123. package/src/modules/Navigation/Menus/List/index.tsx +6 -2
  124. package/src/modules/Navigation/Menus/index.tsx +12 -7
  125. package/src/modules/PageEditor/Editor/index.tsx +5 -1
  126. package/src/modules/PageEditor/PageBrowser/index.tsx +9 -3
  127. package/src/modules/PageEditor/index.tsx +67 -57
  128. package/src/modules/Redirects/index.tsx +97 -98
  129. package/src/modules/Settings/ContentTypes/DataPacks/AddModal/index.tsx +5 -1
  130. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +8 -9
  131. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +7 -3
  132. package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +5 -1
  133. package/src/modules/Settings/ContentTypes/DataPacks/Item/index.tsx +5 -1
  134. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +2 -17
  135. package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +6 -2
  136. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +18 -8
  137. package/src/modules/Settings/Integrations/IntegrationItem/style.tsx +6 -3
  138. package/src/modules/Settings/Integrations/index.tsx +32 -7
  139. package/src/modules/Settings/Languages/LanguagePanel/index.tsx +6 -2
  140. package/src/modules/Settings/Languages/Table/Header/index.tsx +4 -2
  141. package/src/modules/Settings/Languages/Table/Item/index.tsx +19 -43
  142. package/src/modules/Settings/Languages/Table/Item/style.tsx +11 -54
  143. package/src/modules/Settings/Languages/index.tsx +2 -2
  144. package/src/modules/Settings/SeoAnalyticsSettings/Analytics/index.tsx +2 -4
  145. package/src/modules/Settings/SeoAnalyticsSettings/index.tsx +4 -2
  146. package/src/modules/Settings/Social/index.tsx +1 -1
  147. package/src/modules/Settings/index.tsx +17 -11
  148. package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +31 -18
  149. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +21 -12
  150. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +31 -18
  151. package/src/modules/Sites/SitesList/RecentSiteItem/index.tsx +1 -1
  152. package/src/modules/Sites/SitesList/index.tsx +16 -24
  153. package/src/modules/Sites/index.tsx +7 -3
  154. package/src/modules/StructuredData/Form/index.tsx +1 -1
  155. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +8 -5
  156. package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +8 -5
  157. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +29 -15
  158. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +50 -19
  159. package/src/modules/StructuredData/StructuredDataList/index.tsx +9 -6
  160. package/src/modules/Users/Profile/index.tsx +16 -4
  161. package/src/modules/Users/Roles/BulkHeader/TableHeader/index.tsx +52 -0
  162. package/src/modules/Users/Roles/BulkHeader/TableHeader/style.tsx +43 -0
  163. package/src/modules/Users/Roles/BulkHeader/index.tsx +74 -0
  164. package/src/modules/Users/Roles/RoleItem/index.tsx +104 -0
  165. package/src/modules/Users/Roles/RoleItem/style.tsx +127 -0
  166. package/src/modules/Users/Roles/SideModal/index.tsx +81 -0
  167. package/src/modules/Users/Roles/SideModal/style.tsx +132 -0
  168. package/src/modules/Users/Roles/hooks.tsx +78 -0
  169. package/src/modules/Users/Roles/index.tsx +256 -0
  170. package/src/modules/Users/Roles/style.tsx +23 -0
  171. package/src/modules/Users/Roles/utils.tsx +12 -0
  172. package/src/modules/Users/UserCreate/OptionItem/index.tsx +45 -0
  173. package/src/modules/Users/UserCreate/OptionItem/style.tsx +48 -0
  174. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +48 -0
  175. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/style.tsx +42 -0
  176. package/src/modules/Users/UserCreate/SiteItem/RolesModal/index.tsx +140 -0
  177. package/src/modules/Users/UserCreate/SiteItem/RolesModal/style.tsx +94 -0
  178. package/src/modules/Users/UserCreate/SiteItem/index.tsx +103 -22
  179. package/src/modules/Users/UserCreate/SiteItem/style.tsx +49 -6
  180. package/src/modules/Users/UserCreate/index.tsx +278 -121
  181. package/src/modules/Users/UserCreate/style.tsx +71 -4
  182. package/src/modules/Users/UserEdit/index.tsx +71 -24
  183. package/src/modules/Users/UserForm/atoms.tsx +40 -8
  184. package/src/modules/Users/UserForm/index.tsx +335 -116
  185. package/src/modules/Users/UserForm/style.tsx +70 -6
  186. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +61 -31
  187. package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +18 -4
  188. package/src/modules/Users/UserList/BulkHeader/index.tsx +10 -3
  189. package/src/modules/Users/UserList/UserItem/index.tsx +121 -38
  190. package/src/modules/Users/UserList/UserItem/style.tsx +32 -14
  191. package/src/modules/Users/UserList/hooks.tsx +13 -8
  192. package/src/modules/Users/UserList/index.tsx +67 -29
  193. package/src/modules/Users/UserList/utils.tsx +1 -1
  194. package/src/modules/Users/index.tsx +20 -3
  195. package/src/routes/index.tsx +9 -17
  196. package/src/routes/multisite.tsx +73 -8
  197. package/src/routes/site.tsx +96 -10
  198. package/src/types/index.tsx +42 -1
  199. package/tsconfig.paths.json +1 -0
  200. package/src/__tests__/components/Avatar/__snapshots__/Avatar.test.tsx.snap +0 -61
  201. package/src/modules/Users/UserList/HeaderMenus/Name/index.tsx +0 -55
@@ -0,0 +1,81 @@
1
+ import React, { useRef } from "react";
2
+ import { createPortal } from "react-dom";
3
+
4
+ import { Icon } from "@ax/components";
5
+ import { IPermissions } from "@ax/types";
6
+
7
+ import * as S from "./style";
8
+
9
+ const SideModal = (props: ISideModalProps): JSX.Element | null => {
10
+ const {
11
+ name,
12
+ description,
13
+ permissions: { totalPermissions, sitePermissions, globalPermissions },
14
+ isOpen,
15
+ closeModal,
16
+ } = props;
17
+
18
+ const node = useRef<HTMLDivElement>(null);
19
+
20
+ return isOpen
21
+ ? createPortal(
22
+ <S.Wrapper ref={node} data-testid="role-side-modal">
23
+ <S.Header>
24
+ <S.Title data-testid="role-side-modal-title">{name} Permissions</S.Title>
25
+ <S.CloseIconWrapper onClick={closeModal}>
26
+ <Icon name="close" />
27
+ </S.CloseIconWrapper>
28
+ </S.Header>
29
+ <S.ColumnsWrapper>
30
+ <S.RoleInfo>
31
+ <S.RoleTitle>
32
+ <S.RoleName>{name}</S.RoleName> role
33
+ </S.RoleTitle>
34
+ <S.RoleDescription>{description}</S.RoleDescription>
35
+ </S.RoleInfo>
36
+ <S.ListTitleWrapper>
37
+ <S.ListTitle>Role permissions</S.ListTitle>
38
+ <S.TotalPermissions data-testid="role-side-modal-total-permissions">
39
+ {totalPermissions}
40
+ </S.TotalPermissions>
41
+ </S.ListTitleWrapper>
42
+ <S.RolePermissionsList>
43
+ <S.PermissionGroupTitle>Global Permissions</S.PermissionGroupTitle>
44
+ <S.PermissionGroup first={true}>
45
+ {globalPermissions.map((permission: { name: string; key: string }) => (
46
+ <S.Permission key={permission.key} data-testid="role-permission">
47
+ <S.IconWrapper data-testid="success-icon">
48
+ <Icon name="done" size={"16"} />
49
+ </S.IconWrapper>
50
+ {permission.name}
51
+ </S.Permission>
52
+ ))}
53
+ </S.PermissionGroup>
54
+ <S.PermissionGroupTitle>Site Permissions</S.PermissionGroupTitle>
55
+ <S.PermissionGroup>
56
+ {sitePermissions.map((permission: { name: string; key: string }) => (
57
+ <S.Permission key={permission.key} data-testid="role-permission">
58
+ <S.IconWrapper data-testid="success-icon">
59
+ <Icon name="done" size={"16"} />
60
+ </S.IconWrapper>
61
+ {permission.name}
62
+ </S.Permission>
63
+ ))}
64
+ </S.PermissionGroup>
65
+ </S.RolePermissionsList>
66
+ </S.ColumnsWrapper>
67
+ </S.Wrapper>,
68
+ document.body
69
+ )
70
+ : null;
71
+ };
72
+
73
+ interface ISideModalProps {
74
+ name: string;
75
+ description: string;
76
+ permissions: IPermissions;
77
+ isOpen: boolean;
78
+ closeModal: () => void;
79
+ }
80
+
81
+ export default SideModal;
@@ -0,0 +1,132 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div<{ optionsType?: string }>`
4
+ position: fixed;
5
+ right: 0;
6
+ top: 0;
7
+ z-index: 1000;
8
+ width: 384px;
9
+ height: 100vh;
10
+ background: ${(p) => p.theme.colors.uiBackground01};
11
+ box-shadow: ${(p) => p.theme.shadow.rightPanel};
12
+ `;
13
+
14
+ const Header = styled.div`
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: space-between;
18
+ height: ${(p) => p.theme.spacing.xl};
19
+ width: 100%;
20
+ padding: 0 ${(p) => p.theme.spacing.m};
21
+ background-color: ${(p) => p.theme.colors.uiBackground02};
22
+ border-bottom: 1px solid ${(p) => p.theme.colors.uiLine};
23
+ h6 {
24
+ ${(p) => p.theme.textStyle.headingM}
25
+ color: ${(p) => p.theme.colors.textHighEmphasis};
26
+ text-transform: capitalize;
27
+ }
28
+ `;
29
+
30
+ const Title = styled.h6``;
31
+
32
+ const IconWrapper = styled.span`
33
+ padding-right: ${(p) => p.theme.spacing.xs};
34
+ height: 16px;
35
+ `;
36
+
37
+ const CloseIconWrapper = styled(IconWrapper)`
38
+ cursor: pointer;
39
+ `;
40
+
41
+ const ColumnsWrapper = styled.div`
42
+ padding: ${(p) => p.theme.spacing.m};
43
+ `;
44
+
45
+ const RoleInfo = styled.div`
46
+ width: 336px;
47
+ background-color: ${(p) => p.theme.colors.uiBackground03};
48
+ padding: 16px;
49
+ border-radius: 4px;
50
+ margin-bottom: 33px;
51
+ `;
52
+
53
+ const RoleTitle = styled.div`
54
+ ${(p) => p.theme.textStyle.headingXS}
55
+ color: ${(p) => p.theme.color.textMediumEmphasis};
56
+ text-transform: lowercase;
57
+ `;
58
+
59
+ const RoleName = styled.span`
60
+ text-transform: capitalize;
61
+ `;
62
+
63
+ const RoleDescription = styled.div`
64
+ ${(p) => p.theme.textStyle.uiXS};
65
+ color: ${(p) => p.theme.color.textMediumEmphasis};
66
+ `;
67
+
68
+ const ListTitleWrapper = styled.div`
69
+ display: flex;
70
+ margin-bottom: 10px;
71
+ `;
72
+
73
+ const ListTitle = styled.div`
74
+ ${(p) => p.theme.textStyle.headingXS};
75
+ margin-bottom: ${(p) => p.theme.spacing.xs};
76
+ `;
77
+
78
+ const TotalPermissions = styled.div`
79
+ ${(p) => p.theme.textStyle.headingXS};
80
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
81
+ padding-left: ${(p) => p.theme.spacing.xs};
82
+ `;
83
+
84
+ const RolePermissionsList = styled.div`
85
+ overflow: auto;
86
+ height: ${(p) => `calc(100vh - 250px)`};
87
+ `;
88
+
89
+ const RolePermissionsListTitle = styled.div``;
90
+
91
+ const Permission = styled.div`
92
+ display: flex;
93
+ margin-bottom: ${(p) => p.theme.spacing.s};
94
+ font-size: 16px;
95
+ &:last-child{
96
+ margin-bottom: 0;
97
+ }
98
+ `;
99
+
100
+ const PermissionGroupTitle = styled.div`
101
+ ${(p) => p.theme.textStyle.uiS};
102
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
103
+ margin-bottom: ${(p) => p.theme.spacing.xs};
104
+ `;
105
+
106
+ const PermissionGroup = styled.div<{ first?: boolean }>`
107
+ padding-bottom: ${(p) => p.theme.spacing.s};
108
+ border-bottom: ${(p) => p.first ? `1px solid ${p.theme.color.uiLine}` : "none"};
109
+ margin-bottom: ${(p) => p.first ? p.theme.spacing.s : 0};
110
+ `;
111
+
112
+
113
+ export {
114
+ Wrapper,
115
+ IconWrapper,
116
+ CloseIconWrapper,
117
+ Header,
118
+ Title,
119
+ ColumnsWrapper,
120
+ RoleInfo,
121
+ RoleTitle,
122
+ RoleName,
123
+ RoleDescription,
124
+ ListTitleWrapper,
125
+ ListTitle,
126
+ TotalPermissions,
127
+ RolePermissionsList,
128
+ RolePermissionsListTitle,
129
+ Permission,
130
+ PermissionGroup,
131
+ PermissionGroupTitle,
132
+ };
@@ -0,0 +1,78 @@
1
+ import { useState } from "react";
2
+ import { IRolesQueryValues } from "@ax/types";
3
+
4
+ const useSortedListStatus = (): any => {
5
+ const sortedInitialState: {
6
+ isAscending: boolean;
7
+ sortedByTitle: boolean;
8
+ sortedByUsers: boolean;
9
+ sortedByPermissions: boolean;
10
+ } = {
11
+ isAscending: false,
12
+ sortedByTitle: false,
13
+ sortedByUsers: false,
14
+ sortedByPermissions: false,
15
+ };
16
+
17
+ const [sortedListStatus, setSortedListStatus] = useState(sortedInitialState);
18
+
19
+ return {
20
+ sortedListStatus,
21
+ setSortedListStatus,
22
+ };
23
+ };
24
+
25
+ const useFilterQuery = (): IRolesFilterQuery => {
26
+ const initialQueryValues = {
27
+ order: "",
28
+ };
29
+
30
+ const [query, setQuery] = useState(initialQueryValues);
31
+
32
+ const setFilterQuery = (filterValues: IRolesQueryValues) => {
33
+ const { order } = filterValues;
34
+ let filterQuery = "";
35
+
36
+ const currentQuery = (pointer: string, values: string) => {
37
+ return filterQuery ? filterQuery.concat(`&${pointer}=${values}`) : `?${pointer}=${values}`;
38
+ };
39
+
40
+ const isNotInitialValue = (pointer: keyof IRolesQueryValues) => {
41
+ return filterValues[pointer] && initialQueryValues[pointer] !== filterValues[pointer];
42
+ };
43
+
44
+ if (isNotInitialValue("order")) filterQuery = currentQuery("order", order);
45
+
46
+ return filterQuery;
47
+ };
48
+
49
+ const setFiltersSelection = (pointer: string, filter: string, isAscendent?: boolean) => {
50
+ const { order } = query;
51
+ const orderMethod = isAscendent ? "asc" : "desc";
52
+ const filterValues = {
53
+ order: pointer === "order" ? `${filter}-${orderMethod}` : order,
54
+ };
55
+
56
+ setQuery(filterValues);
57
+
58
+ return filterValues;
59
+ };
60
+
61
+ const resetFilterQuery = () => setQuery(initialQueryValues);
62
+
63
+ return {
64
+ setFiltersSelection,
65
+ setFilterQuery,
66
+ resetFilterQuery,
67
+ filterValues: query,
68
+ };
69
+ };
70
+
71
+ interface IRolesFilterQuery {
72
+ setFiltersSelection(pointer: string, filter: string, isAscendent?: boolean): IRolesQueryValues;
73
+ setFilterQuery(filterValues: IRolesQueryValues): string;
74
+ resetFilterQuery(): void;
75
+ filterValues: IRolesQueryValues;
76
+ }
77
+
78
+ export { useSortedListStatus, useFilterQuery };
@@ -0,0 +1,256 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import { IRootState, IRole, IGetRoles, IActivateRole, ISite } from "@ax/types";
5
+ import { useBulkSelection, useToast } from "@ax/hooks";
6
+ import { appActions } from "@ax/containers/App";
7
+ import { usersActions } from "@ax/containers/Users";
8
+
9
+ import { MainWrapper, TableList, ErrorToast, EmptyState, Nav, Toast } from "@ax/components";
10
+
11
+ import BulkHeader from "./BulkHeader";
12
+ import RoleItem from "./RoleItem";
13
+ import { useFilterQuery, useSortedListStatus } from "./hooks";
14
+ import { getSortedListStatus } from "./utils";
15
+ import SideModal from "./SideModal";
16
+ import * as S from "./style";
17
+
18
+ const Roles = (props: IRolesProps): JSX.Element => {
19
+ const { currentSiteInfo, roles, getRoles, navItems, currentNavItem, setHistoryPush, activateRoles } = props;
20
+
21
+ const itemsPerPage = 50;
22
+ const firstPage = 1;
23
+
24
+ const [page, setPage] = useState(1);
25
+ const [isScrolling, setIsScrolling] = useState(false);
26
+ const [currentFilterQuery, setCurrentFilterQuery] = useState("");
27
+ const [activatedItems, setActivatedItems] = useState<number[]>([]);
28
+ const [undoToastActionValue, setUndoToastActionValue] = useState<boolean>(false);
29
+ const [isOpen, setIsOpen] = useState(false);
30
+ const [selectedRole, setSelectedRole] = useState<IRole | null>(null);
31
+
32
+ const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
33
+ const { setFiltersSelection, setFilterQuery, filterValues } = useFilterQuery();
34
+
35
+ const tableRef = useRef<HTMLDivElement>(null);
36
+ const wrapperRef = useRef<HTMLDivElement>(null);
37
+ const isSiteView = !!currentSiteInfo;
38
+ const siteId = isSiteView ? currentSiteInfo.id : "global";
39
+ const { isVisible, toggleToast, setIsVisible } = useToast();
40
+
41
+ const rolesIds = roles?.map((role: IRole) => role.id);
42
+
43
+ const {
44
+ resetBulkSelection,
45
+ isSelected,
46
+ areItemsSelected,
47
+ checkState,
48
+ addToBulkSelection,
49
+ selectAllItems,
50
+ selectedItems,
51
+ } = useBulkSelection(rolesIds);
52
+
53
+ useEffect(() => {
54
+ const { order } = filterValues;
55
+ const handleGetRoles = async () => await getRoles({ siteId, order });
56
+ handleGetRoles();
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, [currentFilterQuery]);
59
+
60
+ useEffect(() => {
61
+ if (wrapperRef.current) {
62
+ wrapperRef.current.scrollIntoView({ block: "start", behavior: "smooth" });
63
+ }
64
+ // eslint-disable-next-line react-hooks/exhaustive-deps
65
+ }, [roles]);
66
+
67
+ const handleMenuClick = (path: string) => setHistoryPush(path);
68
+
69
+ const unselectAllItems = () => resetBulkSelection();
70
+
71
+ const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
72
+
73
+ const handleSelectAll = () => selectAllItems();
74
+
75
+ const onScroll = (e: any) => setIsScrolling(e.target.scrollTop > 0);
76
+
77
+ const totalItems = roles?.length;
78
+
79
+ const firstPageUser = (page - 1) * itemsPerPage;
80
+ const pageRoles = roles?.slice(firstPageUser, firstPageUser + itemsPerPage);
81
+ const isEmpty = roles && roles?.length === 0;
82
+ const pagination = {
83
+ setPage,
84
+ itemsPerPage,
85
+ totalItems,
86
+ currPage: page,
87
+ };
88
+
89
+ const sortItems = async (orderPointer: string, isAscending: boolean) => {
90
+ setPage(firstPage);
91
+ const sortedState = getSortedListStatus(orderPointer, isAscending);
92
+ setSortedListStatus(sortedState);
93
+ const filtersSelection = setFiltersSelection("order", orderPointer, isAscending);
94
+ const filterQuery = setFilterQuery(filtersSelection);
95
+ setCurrentFilterQuery(filterQuery);
96
+ };
97
+
98
+ const filterItems = async (filterPointer: string, filtersSelected: string) => {
99
+ setPage(firstPage);
100
+ const filtersSelection = setFiltersSelection(filterPointer, filtersSelected);
101
+ const filterQuery = setFilterQuery(filtersSelection);
102
+ setCurrentFilterQuery(filterQuery);
103
+ };
104
+
105
+ const activateCurrentRoles = (active: boolean) => {
106
+ const allEditableRoles = roles.filter((role: IRole) => role.editable).map((role: IRole) => role.id);
107
+ const selectedEditableRoles = selectedItems.all.filter((role: number) => allEditableRoles.includes(role));
108
+ handleActivateRoles(selectedEditableRoles, active);
109
+ };
110
+
111
+ const handleActivateRoles = async (roleIds: number[], active: boolean) => {
112
+ const params = {
113
+ id: roleIds,
114
+ siteId,
115
+ active,
116
+ };
117
+ setUndoToastActionValue(!active);
118
+ const activated = await activateRoles(params);
119
+ if (activated) {
120
+ setActivatedItems(roleIds);
121
+ toggleToast();
122
+ }
123
+ };
124
+
125
+ const restorePreviousRoleStatus = async () => {
126
+ const params = { id: activatedItems, siteId, active: undoToastActionValue };
127
+ await activateRoles(params);
128
+ };
129
+
130
+ const undoAction = () => {
131
+ if (activatedItems) {
132
+ restorePreviousRoleStatus();
133
+ }
134
+ setIsVisible(false);
135
+ };
136
+
137
+ const activateText = undoToastActionValue ? "Deactivated" : "Activated";
138
+
139
+ const toastProps = {
140
+ action: () => undoAction(),
141
+ setIsVisible,
142
+ message: activatedItems.length > 1 ? `${activatedItems.length} Roles ${activateText}` : `1 Role ${activateText}`,
143
+ };
144
+
145
+ const TableHeader = (
146
+ <BulkHeader
147
+ filterValues={filterValues}
148
+ showBulk={areItemsSelected(rolesIds)}
149
+ activateRoles={activateCurrentRoles}
150
+ selectAllItems={handleSelectAll}
151
+ totalItems={totalItems}
152
+ selectItems={selectItems}
153
+ checkState={checkState}
154
+ isScrolling={isScrolling}
155
+ sortItems={sortItems}
156
+ filterItems={filterItems}
157
+ sortedListStatus={sortedListStatus}
158
+ isSiteView={isSiteView}
159
+ roles={roles}
160
+ selectedRoles={selectedItems.all}
161
+ />
162
+ );
163
+
164
+ const openSideModal = (role: IRole) => {
165
+ setIsOpen(true);
166
+ setSelectedRole(role);
167
+ };
168
+
169
+ const closeSideModal = () => {
170
+ setIsOpen(false);
171
+ setSelectedRole(null);
172
+ };
173
+
174
+ return (
175
+ <MainWrapper title="Users & Roles">
176
+ <S.UsersWrapper data-testid="users-wrapper" ref={wrapperRef}>
177
+ <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
178
+ <S.TableListWrapper data-testid="roles-table-list-wrapper">
179
+ <ErrorToast />
180
+ <TableList
181
+ tableHeader={TableHeader}
182
+ pagination={pagination}
183
+ onScroll={onScroll}
184
+ hasFixedHeader={true}
185
+ tableRef={tableRef}
186
+ >
187
+ {isEmpty ? (
188
+ <S.EmptyWrapper data-testid="empty-wrapper">
189
+ <EmptyState message="No roles found" />
190
+ </S.EmptyWrapper>
191
+ ) : (
192
+ pageRoles &&
193
+ pageRoles.map((role: IRole) => {
194
+ const isItemSelected = isSelected(role.id);
195
+ const handleActivateRole = async (value: boolean) => handleActivateRoles([role.id], value);
196
+
197
+ return (
198
+ <RoleItem
199
+ key={role.id}
200
+ role={role}
201
+ isSelected={isItemSelected}
202
+ onChange={addToBulkSelection}
203
+ onClick={() => openSideModal(role)}
204
+ siteId={siteId}
205
+ activateRole={handleActivateRole}
206
+ />
207
+ );
208
+ })
209
+ )}
210
+ </TableList>
211
+ </S.TableListWrapper>
212
+ </S.UsersWrapper>
213
+ {isVisible && <Toast {...toastProps} />}
214
+ {selectedRole && (
215
+ <SideModal
216
+ name={selectedRole?.name}
217
+ description={selectedRole?.description}
218
+ permissions={selectedRole?.permissions}
219
+ isOpen={isOpen}
220
+ closeModal={() => closeSideModal()}
221
+ />
222
+ )}
223
+ </MainWrapper>
224
+ );
225
+ };
226
+
227
+ const mapStateToProps = (state: IRootState) => ({
228
+ roles: state.users.roles,
229
+ users: state.users.users,
230
+ currentSiteInfo: state.sites.currentSiteInfo,
231
+ });
232
+
233
+ const mapDispatchToProps = {
234
+ getRoles: usersActions.getRoles,
235
+ activateRoles: usersActions.activateRoles,
236
+ resetUserData: usersActions.resetUserData,
237
+ setHistoryPush: appActions.setHistoryPush,
238
+ };
239
+
240
+ interface IDispatchProps {
241
+ getRoles(params?: IGetRoles): Promise<void>;
242
+ resetUserData(): void;
243
+ setHistoryPush(route: string): void;
244
+ activateRoles(params: IActivateRole): Promise<boolean>;
245
+ }
246
+
247
+ interface IProps {
248
+ navItems: any[];
249
+ currentNavItem: any;
250
+ roles: IRole[];
251
+ currentSiteInfo: ISite | null;
252
+ }
253
+
254
+ export type IRolesProps = IProps & IDispatchProps;
255
+
256
+ export default connect(mapStateToProps, mapDispatchToProps)(Roles);
@@ -0,0 +1,23 @@
1
+ import styled from "styled-components";
2
+
3
+ const UsersWrapper = styled.div`
4
+ display: flex;
5
+ min-height: 100%;
6
+ `;
7
+
8
+ const TableListWrapper = styled.div`
9
+ display: flex;
10
+ flex-direction: column;
11
+ width: 100%;
12
+ position: relative;
13
+ height: calc(100vh - ${(p) => p.theme.spacing.xl});
14
+ overflow: auto;
15
+ `;
16
+
17
+ const EmptyWrapper = styled.div`
18
+ height: ${(p) => `calc(100vh - (${p.theme.spacing.xl} * 3))`};
19
+ display: flex;
20
+ align-items: center;
21
+ `;
22
+
23
+ export { UsersWrapper, TableListWrapper, EmptyWrapper };
@@ -0,0 +1,12 @@
1
+ const getSortedListStatus = (orderPointer: string, isAscending: boolean) => {
2
+ const sortedListStatus = {
3
+ isAscending,
4
+ sortedByTitle: orderPointer === "name",
5
+ sortedByUsers: orderPointer === "users",
6
+ sortedByPermissions: orderPointer === "permissions",
7
+ };
8
+
9
+ return sortedListStatus;
10
+ };
11
+
12
+ export { getSortedListStatus };
@@ -0,0 +1,45 @@
1
+ import React from "react";
2
+
3
+ import { RadioField } from "@ax/components";
4
+
5
+ import * as S from "./style";
6
+
7
+ const OptionItem = (props: IOptionItemProps): JSX.Element => {
8
+ const { name, title, description, value, onChange, readOnly = false } = props;
9
+
10
+ const handleClick = (e: React.MouseEvent) => {
11
+ if (readOnly) return;
12
+ e.preventDefault();
13
+ e.stopPropagation();
14
+ onChange(name);
15
+ };
16
+
17
+ const handleChange = () => onChange(name);
18
+
19
+ return (
20
+ <>
21
+ <S.OptionItem role="rowgroup" data-testid="user-option-item" readOnly={readOnly} selected={!readOnly && value}>
22
+ {!readOnly && (
23
+ <S.RadioCell role="cell" onClick={handleClick} data-testid="radio-cell">
24
+ <RadioField name={name} value={name} checked={value} onChange={handleChange} />
25
+ </S.RadioCell>
26
+ )}
27
+ <S.WrapperCell role="cell" data-testid="wrapper-cell-option">
28
+ <S.Name onClick={handleClick}>{title}</S.Name>
29
+ <S.Description onClick={handleClick}>{description}</S.Description>
30
+ </S.WrapperCell>
31
+ </S.OptionItem>
32
+ </>
33
+ );
34
+ };
35
+
36
+ export interface IOptionItemProps {
37
+ name: string;
38
+ title: string;
39
+ description: string;
40
+ onChange: (value: string) => void;
41
+ value: boolean;
42
+ readOnly?: boolean;
43
+ }
44
+
45
+ export default OptionItem;
@@ -0,0 +1,48 @@
1
+ import styled from "styled-components";
2
+ import { Cell, Row } from "@ax/components/TableList/TableItem/style";
3
+
4
+ const RadioCell = styled(Cell)`
5
+ padding: ${(p) => p.theme.spacing.s};
6
+ width: 32px;
7
+ justify-content: flex-start;
8
+ label {
9
+ margin-bottom: ${(p) => p.theme.spacing.s};
10
+ }
11
+ `;
12
+
13
+ const OptionItem = styled(Row)<{ selected: boolean; readOnly?: boolean }>`
14
+ border: ${(p) => (p.selected ? `2px solid ${p.theme.color.interactive01}` : `1px solid ${p.theme.color.uiLine}`)};
15
+ :before {
16
+ background-color: transparent;
17
+ }
18
+ &:hover {
19
+ cursor: ${(p) => (p.readOnly ? "default" : "pointer")};
20
+ background-color: ${(p) =>
21
+ p.readOnly
22
+ ? p.theme.color.uiBackground02
23
+ : p.selected
24
+ ? p.theme.color.overlayPressedPrimary
25
+ : p.theme.color.overlayHoverPrimary};
26
+ &:before {
27
+ background-color: ${(p) => (p.readOnly ? "transparent" : p.theme.color.overlayHoverPrimary)};
28
+ }
29
+ }
30
+ `;
31
+
32
+ const WrapperCell = styled(Cell)`
33
+ width: 100%;
34
+ `;
35
+
36
+ const Name = styled.div`
37
+ ${(p) => p.theme.textStyle.uiL};
38
+ color: ${(p) => p.theme.color.textHighEmphasis};
39
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
40
+ `;
41
+
42
+ const Description = styled.div`
43
+ ${(p) => p.theme.textStyle.uiXS};
44
+ color: ${(p) => p.theme.color.textMediumEmphasis};
45
+ margin-bottom: ${(p) => p.theme.spacing.s};
46
+ `;
47
+
48
+ export { RadioCell, OptionItem, WrapperCell, Name, Description };