@griddo/ax 10.1.96 → 10.2.1

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
@@ -1,16 +1,22 @@
1
1
  import React from "react";
2
2
  import { Route, Redirect } from "react-router-dom";
3
3
 
4
- const PrivateRoute = (props: IProps) => {
5
- const { component: Component, token, ...rest } = props;
4
+ import { usePermission } from "@ax/hooks";
5
+ import { IUser } from "@ax/types";
6
+
7
+ const PrivateRoute = (props: IProps): JSX.Element => {
8
+ const { component: Component, token, permission, currentUser, ...rest } = props;
9
+
10
+ const isAllowed = usePermission(permission);
6
11
 
7
12
  const getComponent = (routeProps: any) => {
13
+ if (!!currentUser && !isAllowed) {
14
+ return <>{`You don't have permissions to access this section`}</>;
15
+ }
8
16
  return token ? <Component {...routeProps} /> : <Redirect to="/login" />;
9
17
  };
10
18
 
11
- return (
12
- <Route {...rest} render={getComponent} />
13
- );
19
+ return <Route {...rest} render={getComponent} />;
14
20
  };
15
21
 
16
22
  interface IProps {
@@ -18,6 +24,8 @@ interface IProps {
18
24
  path: string;
19
25
  token: string;
20
26
  exact: any;
27
+ permission: string | string[] | undefined;
28
+ currentUser: IUser | null;
21
29
  }
22
30
 
23
31
  export default PrivateRoute;
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
3
3
  import { Switch, Route } from "react-router-dom";
4
4
 
5
5
  import { ErrorGuard } from "@ax/guards";
6
- import { IRootState } from "@ax/types";
6
+ import { IRootState, IUser } from "@ax/types";
7
7
  import { appActions } from "@ax/containers/App";
8
8
  import { publicRoutes, privateRoutes, routes, IRouter } from "@ax/routes";
9
9
  import { ILogoutAction } from "@ax/containers/App/interfaces";
@@ -13,7 +13,7 @@ import PrivateRoute from "./PrivateRoute";
13
13
  import * as S from "./style";
14
14
 
15
15
  const Routing = (props: IProps) => {
16
- const { path, token, setHistoryPush, logout } = props;
16
+ const { path, token, currentUser, setHistoryPush, logout } = props;
17
17
 
18
18
  useEffect(() => {
19
19
  path === "/" && setHistoryPush("/login");
@@ -40,23 +40,32 @@ const Routing = (props: IProps) => {
40
40
  );
41
41
  });
42
42
 
43
- const getRoute = (component: any, path: string, isPrivate: boolean) =>
43
+ const getRoute = (component: any, path: string, isPrivate: boolean, permission: string | string[] | undefined) =>
44
44
  isPrivate ? (
45
- <PrivateRoute token={token} path={path} component={component} exact />
45
+ <PrivateRoute
46
+ token={token}
47
+ path={path}
48
+ component={component}
49
+ permission={permission}
50
+ currentUser={currentUser}
51
+ exact
52
+ />
46
53
  ) : (
47
54
  <Route path={path} component={component} exact />
48
55
  );
49
56
 
50
57
  const mapSubroutes = (subgroup: any, isPrivate: boolean) => {
51
58
  return subgroup.routes.map((subRoute: any, subI: number) => (
52
- <React.Fragment key={subI}>{getRoute(subRoute.component, subRoute.path, isPrivate)}</React.Fragment>
59
+ <React.Fragment key={subI}>
60
+ {getRoute(subRoute.component, subRoute.path, isPrivate, subRoute.permission)}
61
+ </React.Fragment>
53
62
  ));
54
63
  };
55
64
 
56
65
  const mapRoutes = (routeArr: IRouter[], isPrivate: boolean) =>
57
66
  routeArr.map((singleRoute: IRouter, i) => (
58
67
  <React.Fragment key={i}>
59
- {getRoute(singleRoute.component, singleRoute.path, isPrivate)}
68
+ {getRoute(singleRoute.component, singleRoute.path, isPrivate, singleRoute.permission)}
60
69
  {singleRoute.routesGroups && singleRoute.routesGroups.map((subgroup: any) => mapSubroutes(subgroup, isPrivate))}
61
70
  </React.Fragment>
62
71
  ));
@@ -80,6 +89,7 @@ const Routing = (props: IProps) => {
80
89
  const mapStateToProps = (state: IRootState) => ({
81
90
  path: state.router.location.pathname,
82
91
  token: state.app.token,
92
+ currentUser: state.users.currentUser,
83
93
  });
84
94
 
85
95
  interface IDispatchProps {
@@ -90,6 +100,7 @@ interface IDispatchProps {
90
100
  interface IStateProps {
91
101
  path?: string;
92
102
  token: string;
103
+ currentUser: IUser | null;
93
104
  }
94
105
 
95
106
  const mapDispatchToProps = {
@@ -30,6 +30,7 @@ const TransHeader = styled(Header)`
30
30
  const ActionsHeader = styled(Header)`
31
31
  width: 125px;
32
32
  padding-right: 0;
33
+ justify-content: flex-end;
33
34
  `;
34
35
 
35
36
  export { TableHeader, CheckHeader, NameHeader, CodeHeader, TransHeader, ActionsHeader };
@@ -3,15 +3,7 @@ import { BulkSelectionOptions } from "@ax/components";
3
3
  import TableHeader from "./TableHeader";
4
4
 
5
5
  const BulkHeader = (props: IProps): JSX.Element => {
6
- const { showBulk, checkState, bulkDelete, selectItems, selectAllItems, totalItems, isScrolling } = props;
7
-
8
- const bulkActions = [
9
- {
10
- icon: "delete",
11
- text: "delete",
12
- action: bulkDelete,
13
- },
14
- ];
6
+ const { showBulk, checkState, selectItems, selectAllItems, totalItems, isScrolling, bulkActions } = props;
15
7
 
16
8
  return showBulk ? (
17
9
  <BulkSelectionOptions
@@ -28,11 +20,11 @@ const BulkHeader = (props: IProps): JSX.Element => {
28
20
  interface IProps {
29
21
  showBulk: boolean;
30
22
  checkState: any;
31
- bulkDelete: () => void;
32
23
  selectItems: () => void;
33
24
  selectAllItems: () => void;
34
25
  totalItems: number;
35
26
  isScrolling: boolean;
27
+ bulkActions: { icon: string; text: string; action: () => void }[];
36
28
  }
37
29
 
38
30
  export default BulkHeader;
@@ -2,8 +2,8 @@ import React, { useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { structuredDataActions } from "@ax/containers/StructuredData";
5
- import { useModal } from "@ax/hooks";
6
- import { IStructuredDataContent, ICategory, ICheck, IDataLanguage } from "@ax/types";
5
+ import { useModal, usePermission } from "@ax/hooks";
6
+ import { IStructuredDataContent, ICategory, ICheck, IRootState, IDataLanguage } from "@ax/types";
7
7
  import { CheckField, FloatingMenu, Flag, LanguageMenu } from "@ax/components";
8
8
  import CategoryPanel from "../CategoryPanel";
9
9
  import { DeleteModal } from "../atoms";
@@ -26,12 +26,20 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
26
26
  toggleToast,
27
27
  setDeletedItem,
28
28
  getContents,
29
+ currentSiteID,
29
30
  } = props;
30
31
 
31
32
  const { isOpen, toggleModal } = useModal();
32
33
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
33
34
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
34
35
 
36
+ const allowedToEditSiteCategory = usePermission("categories.editSiteTaxonomies");
37
+ const allowedToEditGlobalCategory = usePermission("global.globalData.editTaxonomies");
38
+ const allowedToDeleteSiteCategory = usePermission("categories.deleteSiteTaxonomies");
39
+ const allowedToDeleteGlobalCategory = usePermission("global.globalData.deleteTaxonomies");
40
+ const allowedToEditTaxonomy = currentSiteID ? allowedToEditSiteCategory : allowedToEditGlobalCategory;
41
+ const allowedToDeleteTaxonomy = currentSiteID ? allowedToDeleteSiteCategory : allowedToDeleteGlobalCategory;
42
+
35
43
  const { locale } = lang;
36
44
  const { dataLanguages, content } = category;
37
45
  const isTranslated = dataLanguages.length > 1;
@@ -56,13 +64,15 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
56
64
  }
57
65
  };
58
66
 
59
- const menuOptions = [
60
- {
61
- label: "delete",
62
- icon: "delete",
63
- action: isTranslated ? toggleDeleteModal : removeItem,
64
- },
65
- ];
67
+ const menuOptions = allowedToDeleteTaxonomy
68
+ ? [
69
+ {
70
+ label: "delete",
71
+ icon: "delete",
72
+ action: isTranslated ? toggleDeleteModal : removeItem,
73
+ },
74
+ ]
75
+ : [];
66
76
 
67
77
  const getCurrentLanguages = () => {
68
78
  const availables: any[] = [];
@@ -143,10 +153,10 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
143
153
  <S.CheckCell role="cell">
144
154
  <CheckField name="check" value={category.id} checked={isSelected} onChange={handleOnChange} />
145
155
  </S.CheckCell>
146
- <S.NameCell role="cell" onClick={handleClick}>
156
+ <S.NameCell role="cell" onClick={handleClick} clickable={allowedToEditTaxonomy}>
147
157
  {content.title}
148
158
  </S.NameCell>
149
- <S.CodeCell role="cell" onClick={handleClick}>
159
+ <S.CodeCell role="cell" onClick={handleClick} clickable={allowedToEditTaxonomy}>
150
160
  {content.code}
151
161
  </S.CodeCell>
152
162
  <S.TransCell role="cell">{translations}</S.TransCell>
@@ -178,6 +188,7 @@ interface IProps {
178
188
  toggleToast(): void;
179
189
  setDeletedItem(item: number | number[]): void;
180
190
  getContents(dataId: string): void;
191
+ currentSiteID: number | null;
181
192
  }
182
193
 
183
194
  interface IDispatchProps {
@@ -190,6 +201,10 @@ interface IDispatchProps {
190
201
 
191
202
  type ICategoryItemProps = IProps & IDispatchProps;
192
203
 
204
+ const mapStateToProps = (state: IRootState) => ({
205
+ currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
206
+ });
207
+
193
208
  const mapDispatchToProps = {
194
209
  setCategoryValues: structuredDataActions.setCategoryValues,
195
210
  deleteStructuredDataContent: structuredDataActions.deleteStructuredDataContent,
@@ -198,4 +213,4 @@ const mapDispatchToProps = {
198
213
  getTranslatedCategory: structuredDataActions.getTranslatedCategory,
199
214
  };
200
215
 
201
- export default connect(null, mapDispatchToProps)(CategoryItem);
216
+ export default connect(mapStateToProps, mapDispatchToProps)(CategoryItem);
@@ -11,7 +11,8 @@ const CheckCell = styled(Cell)`
11
11
  }
12
12
  `;
13
13
 
14
- const NameCell = styled(Cell)`
14
+ const NameCell = styled(Cell)<{ clickable: boolean }>`
15
+ pointer-events: ${(p) => (p.clickable ? "auto" : "none")};
15
16
  width: 40%;
16
17
  `;
17
18
 
@@ -25,8 +26,9 @@ const TransCell = styled(Cell)`
25
26
  align-items: center;
26
27
  `;
27
28
 
28
- const CodeCell = styled(Cell)`
29
+ const CodeCell = styled(Cell)<{ clickable: boolean }>`
29
30
  ${(p) => p.theme.textStyle.uiXS};
31
+ pointer-events: ${(p) => (p.clickable ? "auto" : "none")};
30
32
  width: 40%;
31
33
  `;
32
34
 
@@ -4,7 +4,7 @@ import { connect } from "react-redux";
4
4
  import { structuredDataActions } from "@ax/containers/StructuredData";
5
5
  import { appActions } from "@ax/containers/App";
6
6
  import { IRootState, IStructuredData, IStructuredDataContent, IGetStructuredDataParams, IDataPack } from "@ax/types";
7
- import { useBulkSelection, useModal, useToast } from "@ax/hooks";
7
+ import { useBulkSelection, useModal, usePermission, useToast } from "@ax/hooks";
8
8
  import { MainWrapper, TableList, ErrorToast, Toast, EmptyState } from "@ax/components";
9
9
 
10
10
  import { DeleteModal } from "./atoms";
@@ -46,6 +46,13 @@ const CategoriesList = (props: IProps): JSX.Element => {
46
46
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
47
47
  const tableRef = useRef<HTMLDivElement>(null);
48
48
 
49
+ const allowedToCreateSiteCategory = usePermission("categories.createSiteTaxonomies");
50
+ const allowedToCreateGlobalCategory = usePermission("global.globalData.createTaxonomies");
51
+ const allowedToDeleteSiteCategory = usePermission("categories.deleteSiteTaxonomies");
52
+ const allowedToDeleteGlobalCategory = usePermission("global.globalData.deleteTaxonomies");
53
+ const allowedToDeleteTaxonomy = currentSiteID ? allowedToDeleteSiteCategory : allowedToDeleteGlobalCategory;
54
+ const allowedToCreateTaxonomy = currentSiteID ? allowedToCreateSiteCategory : allowedToCreateGlobalCategory;
55
+
49
56
  const scope = currentSiteID ? "site" : "global";
50
57
  const currentCategories = categories[scope];
51
58
 
@@ -103,10 +110,12 @@ const CategoriesList = (props: IProps): JSX.Element => {
103
110
  toggleModal();
104
111
  };
105
112
 
106
- const rightButtonProps = {
107
- label: "New",
108
- action: openModal,
109
- };
113
+ const rightButtonProps = allowedToCreateTaxonomy
114
+ ? {
115
+ label: "New",
116
+ action: openModal,
117
+ }
118
+ : undefined;
110
119
 
111
120
  const bulkDelete = async () => {
112
121
  const idsToBeDeleted = getAllLangCategoriesIds(currentDataContent, selectedItems, deleteAllVersions);
@@ -146,15 +155,25 @@ const CategoriesList = (props: IProps): JSX.Element => {
146
155
  message: deletedItem ? `Categories deleted` : "",
147
156
  };
148
157
 
158
+ const bulkActions = allowedToDeleteTaxonomy
159
+ ? [
160
+ {
161
+ icon: "delete",
162
+ text: "delete",
163
+ action: handleToggleDeleteModal,
164
+ },
165
+ ]
166
+ : [];
167
+
149
168
  const TableHeader = (
150
169
  <BulkHeader
151
170
  showBulk={areItemsSelected(catIds)}
152
- bulkDelete={handleToggleDeleteModal}
153
171
  selectAllItems={handleSelectAll}
154
172
  totalItems={totalItems}
155
173
  selectItems={selectItems}
156
174
  checkState={checkState}
157
175
  isScrolling={isScrolling}
176
+ bulkActions={bulkActions}
158
177
  />
159
178
  );
160
179
 
@@ -269,7 +288,7 @@ const mapStateToProps = (state: IRootState) => ({
269
288
  });
270
289
 
271
290
  interface IDispatchProps {
272
- getStructuredDataContents(params: IGetStructuredDataParams, siteID?: number | undefined): Promise<void>;
291
+ getStructuredDataContents(params: IGetStructuredDataParams, siteID: number | null): Promise<void>;
273
292
  resetCategoryValues(): void;
274
293
  setLanguage(lang: { locale: string; id: number | null }): void;
275
294
  setSelectedCategory(id: string, scope: string): void;
@@ -280,7 +299,7 @@ interface IDispatchProps {
280
299
  }
281
300
 
282
301
  interface ICategoriesProps {
283
- currentSiteID: number;
302
+ currentSiteID: number | null;
284
303
  currentDataContent: IStructuredDataContent[];
285
304
  currentStructuredData: IStructuredData | null;
286
305
  lang: { locale: string; id: number | null };
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { BulkSelectionOptions } from "@ax/components";
3
3
  import { IColumn } from "@ax/types";
4
+ import { usePermission } from "@ax/hooks";
4
5
  import TableHeader from "./TableHeader";
5
6
 
6
7
  const BulkHeader = (props: IProps): JSX.Element => {
@@ -27,13 +28,16 @@ const BulkHeader = (props: IProps): JSX.Element => {
27
28
  siteID,
28
29
  } = props;
29
30
 
31
+ const isAllowedToDeletePage = usePermission("content.deletePages");
32
+ const isAllowedToPublishPages = usePermission("content.publishUnpublishPages");
33
+
30
34
  const deleteAction = {
31
35
  icon: "delete",
32
36
  text: "delete",
33
37
  action: bulkDelete,
34
38
  };
35
39
 
36
- const actions = [
40
+ const actions = isAllowedToPublishPages ? [
37
41
  {
38
42
  icon: "upload-pending",
39
43
  text: "publish",
@@ -44,9 +48,9 @@ const BulkHeader = (props: IProps): JSX.Element => {
44
48
  text: "unpublish",
45
49
  action: bulkUnpublish,
46
50
  },
47
- ];
51
+ ] : [];
48
52
 
49
- const bulkActions = isEditable ? [deleteAction, ...actions] : actions;
53
+ const bulkActions = isEditable && isAllowedToDeletePage ? [deleteAction, ...actions] : actions;
50
54
 
51
55
  return showBulk ? (
52
56
  <BulkSelectionOptions
@@ -133,7 +133,7 @@ const mapStateToProps = (
133
133
  state: IRootState,
134
134
  ownProps: { structuredData: string; setPagesToImport: React.Dispatch<React.SetStateAction<never[]>> }
135
135
  ) => ({
136
- siteID: state.sites.currentSiteInfo.id,
136
+ siteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
137
137
  lang: state.app.lang,
138
138
  structuredData: ownProps.structuredData,
139
139
  setPagesToImport: ownProps.setPagesToImport,
@@ -2,7 +2,7 @@ import React, { memo, useRef, useState } from "react";
2
2
  import { schemas } from "components";
3
3
  import { useTheme } from "styled-components";
4
4
 
5
- import { useAdaptiveText, useModal } from "@ax/hooks";
5
+ import { useAdaptiveText, useModal, usePermission } from "@ax/hooks";
6
6
  import { getHumanLastModifiedDate, getTemplateDisplayName, slugify } from "@ax/helpers";
7
7
  import { IPage, ISite, ISavePageParams, ICheck, IColumn, IPageLanguage, IDataPack } from "@ax/types";
8
8
  import { pageStatus, ISetCurrentPageIDAction } from "@ax/containers/PageEditor/interfaces";
@@ -67,7 +67,9 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
67
67
  manuallyImported,
68
68
  templateId,
69
69
  structuredDataContent,
70
+ liveStatus,
70
71
  } = page;
72
+
71
73
  const displayName = getTemplateDisplayName(templateId);
72
74
 
73
75
  const initValue = { title: "", slug: "" };
@@ -86,6 +88,14 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
86
88
  const title = useAdaptiveText(nameCellRef, page.title, nameCellPadding);
87
89
  const path = useAdaptiveText(nameCellRef, fullPath.page, nameCellPadding);
88
90
 
91
+ const isAllowedToDuplicatePages = usePermission("content.duplicatePages");
92
+ const isAllowedToPublishPages = usePermission("content.publishUnpublishPages");
93
+ const isAllowedToCreatePages = usePermission("content.createPages");
94
+ const isAllowedToCopyPage = usePermission("content.copyPageInOtherSite");
95
+ const isAllowedToDeletePage = usePermission("content.deletePages");
96
+ const isAllowedToDeletePublishedPage = usePermission("content.deletePublishedPages");
97
+ const isAllowedToEditContentPage = usePermission("content.editContentPages");
98
+
89
99
  const currentTemplateDataPacks = schemas.templates[templateId].dataPacks;
90
100
 
91
101
  const isCopyable = !currentTemplateDataPacks;
@@ -93,6 +103,8 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
93
103
  const isTranslated = pageLanguages.length > 1;
94
104
  const activeColumns = Object.keys(columns).filter((col: string) => columns[col].show);
95
105
  const API_URL = process.env.REACT_APP_API_ENDPOINT;
106
+ const isPublished = liveStatus.status === pageStatus.PUBLISHED || liveStatus.status === pageStatus.UPLOAD_PENDING;
107
+ const isAllowedToDelete = (isPublished && isAllowedToDeletePublishedPage) || (!isPublished && isAllowedToDeletePage);
96
108
 
97
109
  const publishedTooltip: any = {
98
110
  active: "Live",
@@ -275,13 +287,9 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
275
287
 
276
288
  let availableLanguages = siteLanguages;
277
289
 
278
- if (!isTemplateActivated || isGlobal) {
290
+ if (!isTemplateActivated || isGlobal || !isAllowedToCreatePages) {
279
291
  const pageLanguagesIDs = pageLanguages.map((language: any) => language.languageId);
280
- availableLanguages =
281
- siteLanguages &&
282
- siteLanguages.filter((language) => {
283
- return pageLanguagesIDs.includes(language.id);
284
- });
292
+ availableLanguages = siteLanguages && siteLanguages.filter((language) => pageLanguagesIDs.includes(language.id));
285
293
  }
286
294
 
287
295
  const languageMenu = () => (
@@ -318,30 +326,33 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
318
326
  ? "This is the canonical site of the page. You cannot remove it."
319
327
  : null;
320
328
 
321
- let menuOptions: IPageOption[] = [
322
- {
329
+ let menuOptions: IPageOption[] = [];
330
+
331
+ if (isAllowedToDelete) {
332
+ menuOptions.push({
323
333
  label: isGlobal ? "Remove from site" : "delete",
324
334
  icon: "delete",
325
335
  action: isGlobal ? toggleRemoveModal : toggleDeleteModal,
326
336
  disabled: deleteDisabled,
327
337
  helpText: deleteHelpText,
328
- },
329
- ];
330
-
331
- const duplicateOption = {
332
- label: "duplicate",
333
- icon: "duplicate",
334
- action: toggleModal,
335
- };
338
+ });
339
+ }
336
340
 
337
- const copyOption = {
338
- label: "Copy page in another site",
339
- icon: "copy",
340
- action: toggleCopyModal,
341
- };
341
+ if (isCopyable && isAllowedToCopyPage) {
342
+ menuOptions.unshift({
343
+ label: "Copy page in another site",
344
+ icon: "copy",
345
+ action: toggleCopyModal,
346
+ });
347
+ }
342
348
 
343
- if (isCopyable) menuOptions.unshift(copyOption);
344
- if (!isGlobal) menuOptions.unshift(duplicateOption);
349
+ if (!isGlobal && isAllowedToDuplicatePages) {
350
+ menuOptions.unshift({
351
+ label: "duplicate",
352
+ icon: "duplicate",
353
+ action: toggleModal,
354
+ });
355
+ }
345
356
 
346
357
  const getPublishItem = (status: string, canBeUnpublished: boolean) => {
347
358
  const helpText =
@@ -387,15 +398,9 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
387
398
 
388
399
  const publishAction = getPublishItem(page.liveStatus.status, canBeUnpublished);
389
400
 
390
- menuOptions = publishAction ? [publishAction, ...menuOptions] : menuOptions;
401
+ menuOptions = publishAction && isAllowedToPublishPages ? [publishAction, ...menuOptions] : menuOptions;
391
402
 
392
403
  const editOptions = [
393
- {
394
- label: "Edit draft",
395
- icon: "modified",
396
- action: goToPage,
397
- color: true,
398
- },
399
404
  {
400
405
  label: "View live",
401
406
  icon: "active",
@@ -404,6 +409,15 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
404
409
  },
405
410
  ];
406
411
 
412
+ if (isAllowedToEditContentPage) {
413
+ editOptions.unshift({
414
+ label: "Edit draft",
415
+ icon: "modified",
416
+ action: goToPage,
417
+ color: true,
418
+ });
419
+ }
420
+
407
421
  menuOptions = page.haveDraftPage ? [...editOptions, ...menuOptions] : menuOptions;
408
422
 
409
423
  menuOptions = page.liveStatus.status === pageStatus.PUBLISHED ? [viewOption, ...menuOptions] : menuOptions;
@@ -91,7 +91,7 @@ const Mark = styled.div`
91
91
  align-items: center;
92
92
  `;
93
93
 
94
- const Home = styled.span`
94
+ const Home = styled.div`
95
95
  ${(p) => p.theme.textStyle.uiXS};
96
96
  width: 63px;
97
97
  height: 20px;
@@ -101,6 +101,7 @@ const Home = styled.span`
101
101
  justify-content: center;
102
102
  align-items: center;
103
103
  padding: 2px 10px 2px 4px;
104
+ word-break: normal;
104
105
  `;
105
106
 
106
107
  const Wrapper = styled.div`
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
3
3
  import { useHistory, useLocation } from "react-router-dom";
4
4
  import { schemas } from "components";
5
5
 
6
- import { useModal, useBulkSelection, useToast, useCategoryColors } from "@ax/hooks";
6
+ import { useModal, useBulkSelection, useToast, useCategoryColors, usePermission } from "@ax/hooks";
7
7
  import {
8
8
  IRootState,
9
9
  IPage,
@@ -18,6 +18,7 @@ import {
18
18
  IUser,
19
19
  IErrorItem,
20
20
  IGetSitesParams,
21
+ ISiteRoles,
21
22
  } from "@ax/types";
22
23
  import { MainWrapper, Modal, TableList, ErrorToast, Toast, EmptyState, Notification } from "@ax/components";
23
24
  import { isGlobalStructuredData, isStructuredDataFromPage } from "@ax/helpers";
@@ -142,8 +143,8 @@ const Content = (props: IProps): JSX.Element => {
142
143
  const dataIds = currentDataContent && currentDataContent.map((data: any) => data.id);
143
144
  const contentIds = isStructuredData ? dataIds : pagesIds;
144
145
  const currentSitePagesTemplatesIds = currentSitePages && currentSitePages.map((page: any) => page.templateId);
145
- const currentSitesByLang = sitesByLang?.filter(
146
- (site) => user?.sites?.includes("all") || user.sites.includes(site.id)
146
+ const currentSitesByLang = sitesByLang?.filter((site: ISite) =>
147
+ user?.roles.find((siteRole: ISiteRoles) => siteRole.siteId === site.id || siteRole.siteId === "all")
147
148
  );
148
149
  const categoryColumns =
149
150
  currentStructuredData && currentStructuredData.schema
@@ -184,6 +185,8 @@ const Content = (props: IProps): JSX.Element => {
184
185
  const [pagesSelected, setPagesSelected] = useState<any[]>([]);
185
186
  const history = useHistory();
186
187
 
188
+ const isAllowedToCreatePages = usePermission("content.createPages");
189
+
187
190
  const {
188
191
  resetBulkSelection,
189
192
  selectedItems,
@@ -302,8 +305,14 @@ const Content = (props: IProps): JSX.Element => {
302
305
  } else {
303
306
  emptyState.message = isContentType
304
307
  ? "You don’t have pages with this content type yet."
305
- : "To start using pages in your site, create as many pages as you need.";
306
- emptyState.button = isContentType ? "View all content" : "Create the first page";
308
+ : isAllowedToCreatePages
309
+ ? "To start using pages in your site, create as many pages as you need."
310
+ : undefined;
311
+ emptyState.button = isContentType
312
+ ? "View all content"
313
+ : isAllowedToCreatePages
314
+ ? "Create the first page"
315
+ : undefined;
307
316
  emptyState.action = isContentType ? () => setFilter("unique-pages") : toggleNewModal;
308
317
  }
309
318
  setIsEmpty(!content.length);
@@ -729,17 +738,17 @@ const Content = (props: IProps): JSX.Element => {
729
738
 
730
739
  const addNewAction = filter === "unique-pages" || isGlobalPages ? toggleNewModal : addNewData;
731
740
 
741
+ const rightButtonProps = isAllowedToCreatePages ? {
742
+ label: "New",
743
+ action: addNewAction,
744
+ disabled: !isDataEditable,
745
+ } : undefined;
746
+
732
747
  const errorPagesText =
733
748
  currentSiteErrorPages.length > 1
734
749
  ? "These pages contains some errors, so you can not publish them yet. Please, review the errors on the pages."
735
750
  : "This page contains some errors, so you can not publish it yet. Please, review the errors on the page.";
736
751
 
737
- const rightButtonProps = {
738
- label: "New",
739
- action: addNewAction,
740
- disabled: !isDataEditable,
741
- };
742
-
743
752
  const notEditableText =
744
753
  "Sorry, this content cannot be edited because it comes from an external source or belongs to a preconfigured system.";
745
754
 
@@ -933,7 +942,7 @@ const mapDispatchToProps = {
933
942
 
934
943
  interface IPagesProps {
935
944
  isData: boolean;
936
- currentSiteInfo: ISite;
945
+ currentSiteInfo: ISite | null;
937
946
  currentSitePages: IPage[];
938
947
  filter: any;
939
948
  template: string;
@@ -954,7 +963,7 @@ interface IPagesProps {
954
963
  currentSiteErrorPages: number[];
955
964
  sites: ISite[];
956
965
  sitesByLang: ISite[];
957
- user: IUser;
966
+ user: IUser | null;
958
967
  skipReviewOnPublish?: boolean;
959
968
  contentFilters: Record<string, string> | null;
960
969
  }
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
3
3
 
4
4
  import { getDefaultTheme } from "@ax/helpers";
5
5
  import { Loading, BrowserContent } from "@ax/components";
6
- import { ILanguage, IRootState, ISocialState } from "@ax/types";
6
+ import { ILanguage, IRootState, ISite, ISocialState } from "@ax/types";
7
7
  import { pageEditorActions } from "@ax/containers/PageEditor";
8
8
  import { findByEditorID } from "@ax/forms";
9
9
  import { useOnMessageReceivedFromOutside, useURLSearchParam } from "@ax/hooks";
@@ -99,7 +99,7 @@ interface IProps {
99
99
  content: any;
100
100
  socials: ISocialState;
101
101
  cloudinaryName: string | null;
102
- currentSiteInfo: any;
102
+ currentSiteInfo: ISite | null;
103
103
  siteLangs: ILanguage[];
104
104
  globalLangs: ILanguage[];
105
105
  isLoading: boolean;