@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
@@ -1,16 +1,16 @@
1
1
  import React, { useState } from "react";
2
-
3
2
  import { connect } from "react-redux";
4
- import { menuActions } from "@ax/containers/Navigation";
5
3
 
6
- import { useModal } from "@ax/hooks";
4
+ import { menuActions } from "@ax/containers/Navigation";
5
+ import { useModal, usePermission } from "@ax/hooks";
7
6
  import { IMenuItem } from "@ax/types";
8
7
  import { Icon, IconAction, Tooltip } from "@ax/components";
8
+
9
9
  import SidePanel from "./../SidePanel";
10
+ import ConfigPanel from "./../ConfigPanel";
10
11
  import { RemoveModal } from "./atoms";
11
12
 
12
13
  import * as S from "./style";
13
- import ConfigPanel from "../ConfigPanel";
14
14
 
15
15
  const DragButton = ({ dragHandleProps }: any) => (
16
16
  <S.IconWrapperDrag {...dragHandleProps}>
@@ -23,9 +23,9 @@ const Item = (props: IItemProps): JSX.Element => {
23
23
  const deleteItem = (item: IMenuItem, deleteChildren?: boolean) => deleteMenuItem(item.id, deleteChildren);
24
24
  const hasChildren = !!item.children.length;
25
25
 
26
- const removeItemAction = () => {
27
- hasChildren ? toggleRemoveModal() : deleteItem(item);
28
- };
26
+ const isAllowedToMangageMenus = usePermission("navigation.manageSiteMenu");
27
+
28
+ const removeItemAction = () => (hasChildren ? toggleRemoveModal() : deleteItem(item));
29
29
 
30
30
  const actionMenuOptions = [
31
31
  {
@@ -72,7 +72,7 @@ const Item = (props: IItemProps): JSX.Element => {
72
72
 
73
73
  return (
74
74
  <>
75
- <S.Container>
75
+ <S.Container disabled={!isAllowedToMangageMenus}>
76
76
  <S.Component onClick={openModal} isGroup={isGroupingElement}>
77
77
  <S.FlexWrapper>
78
78
  <DragButton dragHandleProps={dragHandleProps} />
@@ -85,14 +85,14 @@ const Item = (props: IItemProps): JSX.Element => {
85
85
  </S.FlexWrapper>
86
86
  <S.FlexWrapper>
87
87
  <S.IconWrapper>{icon}</S.IconWrapper>
88
- {depth === 1 && hasChildren && (
88
+ {depth === 1 && hasChildren && isAllowedToMangageMenus && (
89
89
  <S.IconWrapper>
90
90
  <Tooltip content="Options view">
91
91
  <IconAction icon="settings" size="m" onClick={openConfigModal} />
92
92
  </Tooltip>
93
93
  </S.IconWrapper>
94
94
  )}
95
- <S.StyledActionMenu options={actionMenuOptions} icon="more" />
95
+ {isAllowedToMangageMenus && <S.StyledActionMenu options={actionMenuOptions} icon="more" />}
96
96
  </S.FlexWrapper>
97
97
  </S.Component>
98
98
  {isOpen && (
@@ -1,10 +1,11 @@
1
1
  import styled from "styled-components";
2
2
  import { ActionMenu } from "@ax/components";
3
3
 
4
- const Container = styled.div`
4
+ const Container = styled.div<{ disabled: boolean }>`
5
5
  display: flex;
6
6
  align-items: center;
7
7
  width: 100%;
8
+ pointer-events: ${(p) => (p.disabled ? "none" : "auto")};
8
9
  `;
9
10
 
10
11
  const IconWrapper = styled.div`
@@ -15,6 +15,10 @@ import * as S from "./style";
15
15
  const List = (props: IMenuList): JSX.Element => {
16
16
  const { getMenus, lang, getSitePages, currentSiteID, editorMenu, savedMenu, currentType } = props;
17
17
 
18
+ if (!currentSiteID) {
19
+ throw new Error(`ERROR: User reached Menu List with null site info`);
20
+ }
21
+
18
22
  const { isDirty } = useShouldBeSaved(editorMenu, savedMenu, currentType);
19
23
  const memoizedGetMenus = useCallback(() => getMenus(), [getMenus]);
20
24
  const memoizedGetSitePages = useCallback(
@@ -47,7 +51,7 @@ const List = (props: IMenuList): JSX.Element => {
47
51
  };
48
52
 
49
53
  const mapStateToProps = (state: IRootState) => ({
50
- currentSiteID: state.sites.currentSiteInfo.id,
54
+ currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
51
55
  lang: state.app.lang,
52
56
  currentType: state.menu.type,
53
57
  editorMenu: state.menu.editorMenu.elements,
@@ -60,7 +64,7 @@ const mapDispatchToProps = {
60
64
  };
61
65
 
62
66
  interface IStateProps {
63
- currentSiteID: number;
67
+ currentSiteID: number | null;
64
68
  lang: { locale: string; id: number | null };
65
69
  currentType: string;
66
70
  editorMenu: IMenuItem[] | undefined;
@@ -1,27 +1,32 @@
1
1
  import React from "react";
2
-
3
2
  import { connect } from "react-redux";
3
+
4
4
  import { menuActions } from "@ax/containers/Navigation";
5
5
  import { appActions } from "@ax/containers/App";
6
-
7
6
  import { IRootState } from "@ax/types";
8
7
  import { setIsSavedData } from "@ax/forms";
9
8
  import { MainWrapper } from "@ax/components";
9
+ import { usePermission } from "@ax/hooks";
10
+
10
11
  import List from "./List";
11
12
 
12
13
  const MenuView = (props: IMenus) => {
13
14
  const { updateMenu, setLanguage, isSaving, lang, siteLanguages } = props;
14
15
 
16
+ const isAllowedToMangageMenus = usePermission("navigation.manageSiteMenu");
17
+
15
18
  const saveMenu = () => {
16
19
  setIsSavedData(true);
17
20
  return updateMenu();
18
21
  };
19
22
 
20
- const rightButtonProps = {
21
- label: isSaving ? "Saving" : "Save",
22
- disabled: isSaving,
23
- action: saveMenu,
24
- };
23
+ const rightButtonProps = isAllowedToMangageMenus
24
+ ? {
25
+ label: isSaving ? "Saving" : "Save",
26
+ disabled: isSaving,
27
+ action: saveMenu,
28
+ }
29
+ : undefined;
25
30
 
26
31
  const languageActions = {
27
32
  setLanguage,
@@ -45,6 +45,10 @@ const Editor = (props: IProps) => {
45
45
  isEditLive,
46
46
  } = props;
47
47
 
48
+ if (!site) {
49
+ throw new Error(`ERROR: User reached Editor with null site info`);
50
+ }
51
+
48
52
  const { header, footer, theme: pageTheme } = content;
49
53
  const { theme: siteTheme } = site;
50
54
 
@@ -111,7 +115,7 @@ interface IEditorStateProps {
111
115
  selectedTab: string;
112
116
  isLoading: boolean;
113
117
  userEditing: IUserEditing | null;
114
- site: ISite;
118
+ site: ISite | null;
115
119
  lastElementAddedId: null | number;
116
120
  content: any;
117
121
  }
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
3
3
  import { pageEditorActions } from "@ax/containers/PageEditor";
4
4
 
5
5
  import { Browser } from "@ax/components";
6
- import { ILanguage, IRootState, ISocialState } from "@ax/types";
6
+ import { ILanguage, IRootState, ISite, ISocialState } from "@ax/types";
7
7
 
8
8
  const PageBrowser = (props: IProps) => {
9
9
  const {
@@ -11,7 +11,7 @@ const PageBrowser = (props: IProps) => {
11
11
  cloudinaryName,
12
12
  content: { header, footer, path, slug },
13
13
  setSelectedContent,
14
- currentSiteInfo: { theme, id: siteID },
14
+ currentSiteInfo,
15
15
  siteLangs,
16
16
  isTemplateActivated,
17
17
  isReadOnly,
@@ -21,6 +21,12 @@ const PageBrowser = (props: IProps) => {
21
21
  duplicateModule,
22
22
  } = props;
23
23
 
24
+ if (!currentSiteInfo) {
25
+ throw new Error(`ERROR: User reached Page Editor with null site info`);
26
+ }
27
+
28
+ const { theme, id: siteID } = currentSiteInfo;
29
+
24
30
  const slugWithSlash = slug ? (slug.startsWith("/") ? slug : `/${slug}`) : "";
25
31
  const pathWithoutSlash = path ? (path.endsWith("/") ? path.slice(0, -1) : path) : "";
26
32
  const url = `${pathWithoutSlash}${slugWithSlash}`;
@@ -56,7 +62,7 @@ const PageBrowser = (props: IProps) => {
56
62
  interface IPageBrowserStateProps {
57
63
  // TODO: Define content Type
58
64
  content: any;
59
- currentSiteInfo: any;
65
+ currentSiteInfo: ISite | null;
60
66
  socials: ISocialState;
61
67
  cloudinaryName: string | null;
62
68
  siteLangs: ILanguage[];
@@ -9,7 +9,7 @@ import { appActions } from "@ax/containers/App";
9
9
  import { navigationActions } from "@ax/containers/Navigation";
10
10
  import { pageStatus } from "@ax/containers/PageEditor/interfaces";
11
11
  import { RouteLeavingGuard } from "@ax/guards";
12
- import { useIsDirty, useModal } from "@ax/hooks";
12
+ import { useIsDirty, useModal, usePermission } from "@ax/hooks";
13
13
  import { isModuleDisabled, getDeactivatedModules } from "@ax/helpers";
14
14
  import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
15
15
  import { DeleteModal } from "./atoms";
@@ -47,9 +47,17 @@ const PageEditor = (props: IProps) => {
47
47
  currentSiteErrorPages,
48
48
  } = props;
49
49
 
50
+ const isAllowedToPublishPages = usePermission("content.publishUnpublishPages");
51
+ const isAllowedToCreatePages = usePermission("content.createPages");
52
+ const isAllowedToDeletePage = usePermission("content.deletePages");
53
+ const isAllowedToDeletePublishedPage = usePermission("content.deletePublishedPages");
54
+ const isAllowedToEditContentPage = usePermission("content.editContentPages");
55
+
56
+ const defaultTab = isAllowedToEditContentPage ? "edit" : "view";
57
+
50
58
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
51
59
  const [isReadOnly, setIsReadOnly] = useState(false);
52
- const [selectedTab, setSelectedTab] = useState("edit");
60
+ const [selectedTab, setSelectedTab] = useState(defaultTab);
53
61
  const [notification, setNotification] = useState<INotification | null>(null);
54
62
  const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
55
63
  const { isOpen, toggleModal } = useModal();
@@ -67,6 +75,7 @@ const PageEditor = (props: IProps) => {
67
75
  const isTranslated = pageLanguages.length > 1;
68
76
  const structuredData = editorContent ? editorContent.structuredData : "";
69
77
  const isEditLive = isPublished && hasDraft;
78
+ const isAllowedToDelete = (isPublished && isAllowedToDeletePublishedPage) || (!isPublished && isAllowedToDeletePage);
70
79
  const canBeUnpublished = editorContent && editorContent.canBeUnpublished;
71
80
  const deleteHelpText = !canBeUnpublished ? "This is the canonical site of the page. You cannot unpublish it." : null;
72
81
 
@@ -263,7 +272,7 @@ const PageEditor = (props: IProps) => {
263
272
  };
264
273
 
265
274
  const handleNewTranlation = (isNewTranslation: boolean) => {
266
- setSelectedTab("edit");
275
+ isAllowedToEditContentPage && setSelectedTab("edit");
267
276
  createNewTranslation && createNewTranslation(isNewTranslation);
268
277
  };
269
278
 
@@ -273,49 +282,44 @@ const PageEditor = (props: IProps) => {
273
282
  getContent: handleGetTranlation,
274
283
  };
275
284
 
276
- const unpublishOption =
277
- props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty
278
- ? {
279
- label: "Unpublish",
280
- icon: "offline",
281
- action: isDraft ? toggleUnpublishModal : unpublishPage,
282
- }
283
- : undefined;
284
-
285
- const discardOption =
286
- isDraft || (isPublished && isDirty)
287
- ? {
288
- label: "Discard changes",
289
- icon: "close",
290
- action: isDraft ? handleDiscardDraft : handleDiscarChanges,
291
- }
292
- : undefined;
293
-
294
- const deleteOption = !isDraft
295
- ? {
296
- label: "Delete",
297
- icon: "delete",
298
- action: toggleDeleteModal,
299
- }
300
- : undefined;
285
+ const menuOptions = [];
301
286
 
302
- const menuOptions = !isGlobal
303
- ? [
304
- {
305
- label: "Review",
306
- icon: "question",
307
- action: reviewPage,
308
- },
309
- unpublishOption,
310
- discardOption,
311
- deleteOption,
312
- ]
313
- : [];
287
+ if (isAllowedToEditContentPage) {
288
+ menuOptions.push({
289
+ label: "Review",
290
+ icon: "question",
291
+ action: reviewPage,
292
+ });
293
+ }
294
+
295
+ if (props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty && isAllowedToPublishPages) {
296
+ menuOptions.push({
297
+ label: "Unpublish",
298
+ icon: "offline",
299
+ action: isDraft ? toggleUnpublishModal : unpublishPage,
300
+ });
301
+ }
302
+
303
+ if (isAllowedToEditContentPage && (isDraft || (isPublished && isDirty))) {
304
+ menuOptions.push({
305
+ label: "Discard changes",
306
+ icon: "close",
307
+ action: isDraft ? handleDiscardDraft : handleDiscarChanges,
308
+ });
309
+ }
310
+
311
+ if (!isDraft && isAllowedToDelete) {
312
+ menuOptions.push({
313
+ label: "Delete",
314
+ icon: "delete",
315
+ action: toggleDeleteModal,
316
+ });
317
+ }
314
318
 
315
319
  const downArrowMenu = {
316
320
  displayed: !isReadOnly,
317
- button: getPublishButton(props.pageStatus),
318
- options: menuOptions,
321
+ button: isAllowedToPublishPages ? getPublishButton(props.pageStatus) : undefined,
322
+ options: !isGlobal ? menuOptions : [],
319
323
  };
320
324
 
321
325
  const handleSavePage = async () => {
@@ -361,7 +365,7 @@ const PageEditor = (props: IProps) => {
361
365
  }
362
366
 
363
367
  let availableLanguages = siteLanguages;
364
- if (!isTemplateActivated || hasDeactivatedModules) {
368
+ if (!isTemplateActivated || hasDeactivatedModules || !isAllowedToCreatePages) {
365
369
  const pageLanguagesIDs = pageLanguages.map((language) => language.languageId);
366
370
  availableLanguages = siteLanguages.filter((language) => {
367
371
  return pageLanguagesIDs.includes(language.id);
@@ -386,15 +390,17 @@ const PageEditor = (props: IProps) => {
386
390
  }
387
391
  };
388
392
 
389
- const rightButtonProps = {
390
- label: isSaving ? "Saving" : getSaveLabel(),
391
- disabled:
392
- (!isDirty && pageID !== null && !isNewTranslation) ||
393
- isSaving ||
394
- isReadOnly ||
395
- (!isTemplateActivated && !isGlobal),
396
- action: isPublished ? publishChanges : handleSavePage,
397
- };
393
+ const rightButtonProps = isAllowedToEditContentPage
394
+ ? {
395
+ label: isSaving ? "Saving" : getSaveLabel(),
396
+ disabled:
397
+ (!isDirty && pageID !== null && !isNewTranslation) ||
398
+ isSaving ||
399
+ isReadOnly ||
400
+ (!isTemplateActivated && !isGlobal),
401
+ action: isPublished ? publishChanges : handleSavePage,
402
+ }
403
+ : undefined;
398
404
 
399
405
  const goToLivePage = () => {
400
406
  const currentPageUrl = editorContent.fullUrl;
@@ -470,11 +476,15 @@ const PageEditor = (props: IProps) => {
470
476
 
471
477
  const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
472
478
 
479
+ const tabIcons = isAllowedToEditContentPage
480
+ ? [
481
+ { name: "edit", text: "Edit mode" },
482
+ { name: "view", text: "Preview mode" },
483
+ ]
484
+ : [{ name: "view", text: "Preview mode" }];
485
+
473
486
  const tabsPreview = {
474
- icons: [
475
- { name: "edit", text: "Edit mode" },
476
- { name: "view", text: "Preview mode" },
477
- ],
487
+ icons: tabIcons,
478
488
  selectedTab,
479
489
  action: (tab: string) => setSelectedTab(tab),
480
490
  };
@@ -611,7 +621,7 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
611
621
  activatedModules: state.dataPacks.modules,
612
622
  errors: state.pageEditor.errors,
613
623
  userEditing: state.pageEditor.userEditing,
614
- currentUserID: state.users.currentUser.id,
624
+ currentUserID: state.users.currentUser && state.users.currentUser.id,
615
625
  isNewTranslation: state.pageEditor.isNewTranslation,
616
626
  currentSiteErrorPages: state.sites.currentSiteErrorPages,
617
627
  skipReviewOnPublish: state.app.globalSettings.skipReviewOnPublish,
@@ -35,6 +35,7 @@ const Redirects = (props: IProps): JSX.Element => {
35
35
 
36
36
  const itemsPerPage = 50;
37
37
  const firstPage = 1;
38
+ const siteID = currentSiteID ? currentSiteID : "global";
38
39
  const [page, setPage] = useState(1);
39
40
  const [isScrolling, setIsScrolling] = useState(false);
40
41
  const [isOpenedSecond, setIsOpenedSecond] = useState(false);
@@ -43,7 +44,7 @@ const Redirects = (props: IProps): JSX.Element => {
43
44
  const tableRef = useRef<HTMLDivElement>(null);
44
45
  const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
45
46
  const { setFiltersSelection, setFilterQuery, filterValues } = useFilterQuery(currentSiteID);
46
- const [currentFilterQuery, setCurrentFilterQuery] = useState(`&sites=${currentSiteID}`);
47
+ const [currentFilterQuery, setCurrentFilterQuery] = useState(`&sites=${siteID}`);
47
48
  const { isVisible, toggleToast, setIsVisible } = useToast();
48
49
  const { isVisible: isImportVisible, toggleToast: toggleImportToast, setIsVisible: setIsImportVisible } = useToast();
49
50
  const { isOpen: isOpenOverwrite, toggleModal: toggleOverwriteModal } = useModal();
@@ -262,104 +263,102 @@ const Redirects = (props: IProps): JSX.Element => {
262
263
  ];
263
264
 
264
265
  return (
265
- <>
266
- <MainWrapper
267
- backLink={false}
268
- title="SEO Settings"
269
- rightButton={rightButtonProps}
270
- rightLineButton={rightLineButtonProps}
271
- searchAction={setSearchQuery}
272
- filterSearchAction={setSearchFilter}
273
- searchFilters={searchFilters}
274
- >
275
- <S.Wrapper>
276
- <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
277
- <S.ContentWrapper>
278
- <ErrorToast />
279
- <S.TitleWrapper>
280
- <S.Title>URL Redirects Manager</S.Title>
281
- <S.Description>
282
- You can create 301 redirects to direct users and search engines from an old URL to a new one.
283
- </S.Description>
284
- </S.TitleWrapper>
285
- <S.TableWrapper>
286
- <TableList
287
- tableHeader={TableHeader}
288
- pagination={pagination}
289
- onScroll={onScroll}
290
- hasFixedHeader={true}
291
- tableRef={tableRef}
292
- >
293
- {isEmpty ? (
294
- <S.EmptyWrapper>
295
- <EmptyState {...emptyStateProps} />
296
- </S.EmptyWrapper>
297
- ) : (
298
- redirects.map((redirect: any) => {
299
- const isItemSelected = isSelected(redirect.id);
300
- return (
301
- <RedirectItem
302
- key={redirect.id}
303
- redirect={redirect}
304
- isSelected={isItemSelected}
305
- onChange={addToBulkSelection}
306
- toggleToast={toggleToast}
307
- setFormValues={setFormValues}
308
- formValues={formValues}
309
- addRedirect={handleAddRedirect}
310
- currentFilter={currentFilterQuery}
311
- isSiteItem={!!currentSiteID}
312
- />
313
- );
314
- })
315
- )}
316
- </TableList>
317
- </S.TableWrapper>
318
- {isVisible && <Toast {...toastProps} />}
319
- {isImportVisible && <Toast {...toastImportProps} />}
320
- </S.ContentWrapper>
321
- </S.Wrapper>
322
- {isOpen && (
323
- <RedirectPanel
324
- isOpen={isOpen}
325
- isOpenedSecond={isOpenedSecond}
326
- toggleModal={toggleModal}
327
- toggleSecondaryPanel={toggleSecondaryPanel}
328
- formValues={formValues}
329
- setFormValues={setFormValues}
330
- addRedirect={handleAddRedirect}
331
- currentFilter={currentFilterQuery}
332
- />
333
- )}
334
- <DeleteModal
335
- isOpen={isOpenDelete}
336
- toggleModal={toggleModalDelete}
337
- secondaryModalAction={secondaryDeleteModalAction}
338
- mainModalAction={mainDeleteModalAction}
266
+ <MainWrapper
267
+ backLink={false}
268
+ title="SEO Settings"
269
+ rightButton={rightButtonProps}
270
+ rightLineButton={rightLineButtonProps}
271
+ searchAction={setSearchQuery}
272
+ filterSearchAction={setSearchFilter}
273
+ searchFilters={searchFilters}
274
+ >
275
+ <S.Wrapper>
276
+ <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
277
+ <S.ContentWrapper>
278
+ <ErrorToast />
279
+ <S.TitleWrapper>
280
+ <S.Title>URL Redirects Manager</S.Title>
281
+ <S.Description>
282
+ You can create 301 redirects to direct users and search engines from an old URL to a new one.
283
+ </S.Description>
284
+ </S.TitleWrapper>
285
+ <S.TableWrapper>
286
+ <TableList
287
+ tableHeader={TableHeader}
288
+ pagination={pagination}
289
+ onScroll={onScroll}
290
+ hasFixedHeader={true}
291
+ tableRef={tableRef}
292
+ >
293
+ {isEmpty ? (
294
+ <S.EmptyWrapper>
295
+ <EmptyState {...emptyStateProps} />
296
+ </S.EmptyWrapper>
297
+ ) : (
298
+ redirects.map((redirect: any) => {
299
+ const isItemSelected = isSelected(redirect.id);
300
+ return (
301
+ <RedirectItem
302
+ key={redirect.id}
303
+ redirect={redirect}
304
+ isSelected={isItemSelected}
305
+ onChange={addToBulkSelection}
306
+ toggleToast={toggleToast}
307
+ setFormValues={setFormValues}
308
+ formValues={formValues}
309
+ addRedirect={handleAddRedirect}
310
+ currentFilter={currentFilterQuery}
311
+ isSiteItem={!!currentSiteID}
312
+ />
313
+ );
314
+ })
315
+ )}
316
+ </TableList>
317
+ </S.TableWrapper>
318
+ {isVisible && <Toast {...toastProps} />}
319
+ {isImportVisible && <Toast {...toastImportProps} />}
320
+ </S.ContentWrapper>
321
+ </S.Wrapper>
322
+ {isOpen && (
323
+ <RedirectPanel
324
+ isOpen={isOpen}
325
+ isOpenedSecond={isOpenedSecond}
326
+ toggleModal={toggleModal}
327
+ toggleSecondaryPanel={toggleSecondaryPanel}
328
+ formValues={formValues}
329
+ setFormValues={setFormValues}
330
+ addRedirect={handleAddRedirect}
331
+ currentFilter={currentFilterQuery}
339
332
  />
340
- <OverwriteModal
341
- isOpen={isOpenOverwrite}
342
- toggleModal={toggleOverwriteModal}
343
- secondaryModalAction={secondaryOverwriteModalAction}
344
- mainModalAction={mainOverwriteModalAction}
345
- />
346
- <ImportModal
347
- isOpen={isOpenImport}
348
- toggleModal={toggleImportModal}
349
- checkImportData={handleCheckImportData}
350
- isUploading={isUploading}
351
- setIsUploading={setIsUploading}
352
- />
353
- <ImportCheckModal
354
- isOpen={isOpenCheckImport}
355
- toggleModal={toggleCheckImportModal}
356
- mainModalAction={mainImportModalAction}
357
- secondaryModalAction={secondaryImportModalAction}
358
- imports={imports}
359
- totalImports={totalImports}
360
- />
361
- </MainWrapper>
362
- </>
333
+ )}
334
+ <DeleteModal
335
+ isOpen={isOpenDelete}
336
+ toggleModal={toggleModalDelete}
337
+ secondaryModalAction={secondaryDeleteModalAction}
338
+ mainModalAction={mainDeleteModalAction}
339
+ />
340
+ <OverwriteModal
341
+ isOpen={isOpenOverwrite}
342
+ toggleModal={toggleOverwriteModal}
343
+ secondaryModalAction={secondaryOverwriteModalAction}
344
+ mainModalAction={mainOverwriteModalAction}
345
+ />
346
+ <ImportModal
347
+ isOpen={isOpenImport}
348
+ toggleModal={toggleImportModal}
349
+ checkImportData={handleCheckImportData}
350
+ isUploading={isUploading}
351
+ setIsUploading={setIsUploading}
352
+ />
353
+ <ImportCheckModal
354
+ isOpen={isOpenCheckImport}
355
+ toggleModal={toggleCheckImportModal}
356
+ mainModalAction={mainImportModalAction}
357
+ secondaryModalAction={secondaryImportModalAction}
358
+ imports={imports}
359
+ totalImports={totalImports}
360
+ />
361
+ </MainWrapper>
363
362
  );
364
363
  };
365
364
 
@@ -15,6 +15,10 @@ import * as S from "./style";
15
15
  const AddModal = (props: IProps) => {
16
16
  const { toggleModal, getAvailableDataPacks, isLoading, available, addDataPacks, categories, currentSiteInfo } = props;
17
17
 
18
+ if (!currentSiteInfo) {
19
+ throw new Error(`ERROR: User reached DataPacks with null site info`);
20
+ }
21
+
18
22
  const initialState: IState = {
19
23
  packsSelected: [],
20
24
  currentFilter: null,
@@ -117,7 +121,7 @@ interface IProps {
117
121
  isLoading: boolean;
118
122
  available: IDataPack[];
119
123
  categories: IDataPackCategory[];
120
- currentSiteInfo: ISite;
124
+ currentSiteInfo: ISite | null;
121
125
  toggleModal: () => void;
122
126
  getAvailableDataPacks: (queryParams: string | null) => void;
123
127
  addDataPacks: (dataPackID: string, fromConfig?: boolean) => void;
@@ -2,17 +2,16 @@ import React from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { Browser } from "@ax/components";
5
- import { ILanguage, IRootState, ISocialState } from "@ax/types";
5
+ import { ILanguage, IRootState, ISite, ISocialState } from "@ax/types";
6
6
 
7
7
  const TemplateBrowser = (props: IProps) => {
8
- const {
9
- socials,
10
- cloudinaryName,
11
- content,
12
- currentSiteInfo: { theme, id: siteID },
13
- siteLangs,
14
- } = props;
8
+ const { socials, cloudinaryName, content, currentSiteInfo, siteLangs } = props;
15
9
 
10
+ if (!currentSiteInfo) {
11
+ throw new Error(`ERROR: User reached Template Editor with null site info`);
12
+ }
13
+
14
+ const { theme, id: siteID } = currentSiteInfo;
16
15
  const { header, footer } = content;
17
16
 
18
17
  return (
@@ -33,7 +32,7 @@ const TemplateBrowser = (props: IProps) => {
33
32
 
34
33
  interface IProps {
35
34
  content: any;
36
- currentSiteInfo: any;
35
+ currentSiteInfo: ISite | null;
37
36
  socials: ISocialState;
38
37
  cloudinaryName: string | null;
39
38
  siteLangs: ILanguage[];