@griddo/ax 1.72.11 → 1.73.2

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 (76) hide show
  1. package/package.json +2 -2
  2. package/scripts/griddo-sync-schemas.js +1 -1
  3. package/src/__tests__/components/ErrorCenter/ErrorCenter.test.tsx +186 -0
  4. package/src/__tests__/components/Flag/Flag.test.tsx +60 -0
  5. package/src/__tests__/components/FloatingMenu/FloatingMenu.test.tsx +712 -0
  6. package/src/__tests__/components/FloatingPanel/FloatingPanel.test.tsx +149 -0
  7. package/src/__tests__/components/GuardModal/GuardModal.test.tsx +31 -0
  8. package/src/__tests__/components/Icon/Icon.test.tsx +76 -0
  9. package/src/__tests__/components/IconAction/IconAction.test.tsx +91 -0
  10. package/src/__tests__/components/Notification/Notification.test.tsx +206 -0
  11. package/src/__tests__/components/Notification/SubNotification/Subnotification.test.tsx +46 -0
  12. package/src/__tests__/components/ReorderArrows/ReorderArrows.test.tsx +96 -0
  13. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +200 -0
  14. package/src/__tests__/components/SearchField/SearchField.test.tsx +375 -0
  15. package/src/api/analytics.tsx +4 -4
  16. package/src/components/ActionMenu/style.tsx +1 -0
  17. package/src/components/ConfigPanel/Form/ConnectedField/NavConnectedField/index.tsx +1 -1
  18. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -1
  19. package/src/components/ConfigPanel/Form/index.tsx +22 -1
  20. package/src/components/ConfigPanel/Form/style.tsx +19 -0
  21. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +22 -3
  22. package/src/components/ConfigPanel/GlobalPageForm/style.tsx +18 -2
  23. package/src/components/ConfigPanel/NavigationForm/Field/index.tsx +25 -13
  24. package/src/components/ConfigPanel/index.tsx +8 -0
  25. package/src/components/ErrorCenter/index.tsx +8 -4
  26. package/src/components/Fields/DateField/DatePickerInput/index.tsx +30 -8
  27. package/src/components/Fields/DateField/index.tsx +8 -2
  28. package/src/components/Fields/Select/index.tsx +1 -0
  29. package/src/components/Flag/index.tsx +13 -11
  30. package/src/components/FloatingMenu/index.tsx +23 -7
  31. package/src/components/FloatingMenu/style.tsx +1 -0
  32. package/src/components/FloatingPanel/index.tsx +9 -3
  33. package/src/components/GuardModal/index.tsx +3 -3
  34. package/src/components/Icon/index.tsx +2 -1
  35. package/src/components/IconAction/index.tsx +3 -3
  36. package/src/components/MainWrapper/AppBar/index.tsx +3 -1
  37. package/src/components/MainWrapper/AppBar/style.tsx +3 -0
  38. package/src/components/MenuItem/index.tsx +1 -1
  39. package/src/components/Modal/index.tsx +1 -1
  40. package/src/components/Notification/SubNotification/index.tsx +33 -0
  41. package/src/components/Notification/SubNotification/style.tsx +49 -0
  42. package/src/components/Notification/index.tsx +31 -17
  43. package/src/components/Notification/style.tsx +33 -8
  44. package/src/components/ReorderArrows/index.tsx +3 -3
  45. package/src/components/ResizePanel/ResizeHandle/index.tsx +29 -38
  46. package/src/components/ResizePanel/index.tsx +13 -15
  47. package/src/components/SearchField/index.tsx +9 -8
  48. package/src/containers/Analytics/actions.tsx +14 -4
  49. package/src/containers/App/actions.tsx +18 -6
  50. package/src/containers/App/reducer.tsx +1 -0
  51. package/src/containers/Domains/actions.tsx +8 -1
  52. package/src/containers/Navigation/Defaults/actions.tsx +16 -2
  53. package/src/containers/PageEditor/actions.tsx +82 -6
  54. package/src/containers/PageEditor/utils.tsx +28 -10
  55. package/src/containers/Redirects/actions.tsx +16 -2
  56. package/src/containers/Sites/actions.tsx +80 -3
  57. package/src/containers/StructuredData/actions.tsx +24 -3
  58. package/src/containers/Users/actions.tsx +8 -1
  59. package/src/forms/errors.tsx +1 -0
  60. package/src/forms/fields.tsx +6 -3
  61. package/src/forms/validators.tsx +14 -4
  62. package/src/guards/error/index.tsx +17 -21
  63. package/src/helpers/dates.tsx +2 -0
  64. package/src/helpers/index.tsx +2 -0
  65. package/src/hooks/modals.tsx +4 -4
  66. package/src/modules/Content/OptionTable/index.tsx +20 -7
  67. package/src/modules/Content/index.tsx +4 -7
  68. package/src/modules/Content/utils.tsx +18 -13
  69. package/src/modules/FramePreview/index.tsx +39 -12
  70. package/src/modules/GlobalEditor/index.tsx +17 -20
  71. package/src/modules/Navigation/Menus/List/Table/index.tsx +2 -2
  72. package/src/modules/PageEditor/Editor/index.tsx +13 -0
  73. package/src/modules/PageEditor/index.tsx +17 -20
  74. package/src/modules/Redirects/RedirectItem/index.tsx +17 -3
  75. package/src/modules/Settings/ContentTypes/DataPacks/Item/index.tsx +1 -1
  76. package/src/modules/StructuredData/Form/index.tsx +10 -13
@@ -1,9 +1,10 @@
1
1
  import React, { useReducer, useEffect, useLayoutEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { structuredDataActions } from "@ax/containers/StructuredData";
5
- import { getStructuredDataTitle, getThumbnailProps, isGlobalStructuredData } from "@ax/helpers";
4
+ import { IRootState, IStructuredData } from "@ax/types";
5
+ import { getThumbnailProps } from "@ax/helpers";
6
6
  import { MenuItem, RadioGroup } from "@ax/components";
7
+ import { structuredDataActions } from "@ax/containers/StructuredData";
7
8
  import { SecondaryActionButton, MainActionButton } from "./../atoms";
8
9
 
9
10
  import { reducer, IOptionTableStore, setColumnValues, setShowThumbnail, setSelectedType, setOption } from "./store";
@@ -21,7 +22,9 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
21
22
  theme,
22
23
  mainAction,
23
24
  secondaryAction,
25
+ structuredData,
24
26
  } = props;
27
+
25
28
  const filterOptions = (value: string, objKey: string) => values.filter((item: any) => item[objKey] === value);
26
29
  const filterOptionsByDataPack = (value: string) => {
27
30
  return values.filter((item: any) => {
@@ -89,13 +92,18 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
89
92
  const { value } = item;
90
93
  setType(value);
91
94
  filteredOptionsByDataPack = filterOptionsByDataPack(value);
95
+
92
96
  filteredOptionsByDataPack.forEach((option: any) => {
93
- const { type } = option;
94
- const isGlobal = value !== "static" && type && isGlobalStructuredData(type.toUpperCase());
95
- if (isGlobal) {
97
+ const { name } = option;
98
+
99
+ const currentGlobalStructuredData = structuredData.global.find((data: any) =>
100
+ data.schema.templates?.includes(name)
101
+ );
102
+
103
+ if (currentGlobalStructuredData) {
96
104
  filteredOptionsByDataPack = filteredOptionsByDataPack.map((option: any) => {
97
105
  if (option.mode !== "detail") return option;
98
- const title = `Get ${getStructuredDataTitle(type?.toUpperCase())} from Global`;
106
+ const title = `Get ${currentGlobalStructuredData.title} from Global`;
99
107
  return { ...option, title };
100
108
  });
101
109
  const globalOptionIdx = filteredOptionsByDataPack.findIndex((option: any) => option.mode === "detail");
@@ -165,10 +173,15 @@ interface IOptionTableProps {
165
173
  theme: string;
166
174
  mainAction: IAction;
167
175
  secondaryAction: IAction;
176
+ structuredData: { global: IStructuredData[]; site: IStructuredData[] };
168
177
  }
169
178
 
179
+ const mapStateToProps = (state: IRootState) => ({
180
+ structuredData: state.structuredData.structuredData,
181
+ });
182
+
170
183
  const mapDispatchToProps = {
171
184
  setIsStructuredData: structuredDataActions.setIsActive,
172
185
  };
173
186
 
174
- export default connect(null, mapDispatchToProps)(OptionTable);
187
+ export default connect(mapStateToProps, mapDispatchToProps)(OptionTable);
@@ -99,6 +99,7 @@ const Content = (props: IProps): JSX.Element => {
99
99
  skipReviewOnPublish,
100
100
  setContentFilters,
101
101
  contentFilters,
102
+ deleteAndRemoveFromSiteBulk,
102
103
  } = props;
103
104
 
104
105
  const itemsPerPage = 50;
@@ -349,13 +350,7 @@ const Content = (props: IProps): JSX.Element => {
349
350
  .filter((page: IPage) => pageIds.includes(page.id) && page.origin !== "GLOBAL")
350
351
  .map((page: IPage) => page.id);
351
352
 
352
- let deleted = false;
353
- if (filteredPageIds.length > 0) {
354
- deleted = await deleteBulk(filteredPageIds);
355
- }
356
- if (globalPageIds.length > 0) {
357
- deleted = await removePageFromSite(globalPageIds, false);
358
- }
353
+ const deleted = await deleteAndRemoveFromSiteBulk(filteredPageIds, globalPageIds);
359
354
 
360
355
  if (deleted) {
361
356
  setDeletedItem(pageIds);
@@ -826,6 +821,7 @@ interface IDispatchProps {
826
821
  resetCurrentSiteErrorPages: () => Promise<void>;
827
822
  getSitesByLang(language: number): Promise<void>;
828
823
  setContentFilters(contentFilters: Record<string, string> | null): void;
824
+ deleteAndRemoveFromSiteBulk(pageIds: number[], globalPageIds: number[]): Promise<boolean>;
829
825
  }
830
826
 
831
827
  const mapDispatchToProps = {
@@ -859,6 +855,7 @@ const mapDispatchToProps = {
859
855
  resetCurrentSiteErrorPages: sitesActions.resetCurrentSiteErrorPages,
860
856
  getSitesByLang: sitesActions.getSitesByLang,
861
857
  setContentFilters: sitesActions.setContentFilters,
858
+ deleteAndRemoveFromSiteBulk: sitesActions.deleteAndRemoveFromSiteBulk,
862
859
  };
863
860
 
864
861
  interface IPagesProps {
@@ -15,14 +15,17 @@ const getTemplatesFilters = (activatedDataPacks: any) => {
15
15
  if (isActivated) {
16
16
  !dataPacks
17
17
  ? filters.push({ ...type })
18
- : dataPacks.map((datapack: string) =>
19
- filters.push({
20
- label: datapack.toLowerCase(),
21
- value: datapack.toLowerCase(),
22
- type: type.value,
18
+ : dataPacks.map((dataPack: string) => {
19
+ const currentDataPack = activatedDataPacks.find(
20
+ (activatedDataPack: any) => dataPack === activatedDataPack.id
21
+ );
22
+
23
+ return filters.push({
24
+ label: currentDataPack.title,
25
+ value: currentDataPack.id,
23
26
  mode: type.mode,
24
- })
25
- );
27
+ });
28
+ });
26
29
  }
27
30
  });
28
31
 
@@ -75,13 +78,15 @@ const getOptionFilters = (options: any, activatedDataPacks: any) => {
75
78
  const templateFilters = getTemplatesFilters(activatedDataPacks);
76
79
 
77
80
  const mappedOptions = pureOptions.flatMap((option: any) =>
78
- option.dataPacks.map((dataPack: string) => ({
79
- label: dataPack.toLowerCase(),
80
- value: dataPack.toLowerCase(),
81
- isData: true,
82
- }))
81
+ option.dataPacks.map((dataPack: string) => {
82
+ const currentDataPack = activatedDataPacks.find((activatedDataPack: any) => dataPack === activatedDataPack.id);
83
+ return {
84
+ label: currentDataPack.title,
85
+ value: currentDataPack.id,
86
+ isData: true,
87
+ };
88
+ })
83
89
  );
84
-
85
90
  const filters = [...templateFilters, ...mappedOptions];
86
91
  const uniqueFilters = [...new Map(filters.map((item: any) => [item.value, item])).values()];
87
92
  const sortedUniqueFilters = uniqueFilters.sort((a, b) => a.label.localeCompare(b.label));
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useState } from "react";
1
+ import React, { useCallback, useEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { getDefaultTheme } from "@ax/helpers";
@@ -11,31 +11,56 @@ import { useURLSearchParam } from "@ax/hooks";
11
11
  import * as S from "./style";
12
12
 
13
13
  const FramePreview = (props: IProps) => {
14
- const { content, socials, cloudinaryName, isLoading, currentSiteInfo, siteLangs, globalLangs, setSelectedContent } =
15
- props;
14
+ const {
15
+ content,
16
+ socials,
17
+ cloudinaryName,
18
+ isLoading,
19
+ currentSiteInfo,
20
+ siteLangs,
21
+ globalLangs,
22
+ setSelectedContent,
23
+ setEditorContent,
24
+ } = props;
16
25
 
17
- const [state, setState] = useState(content);
18
26
  const isPreview = useURLSearchParam("preview");
19
27
  const isDisabled = useURLSearchParam("disabled");
20
28
 
21
- const onMessageReceivedFromOutside = useCallback((ev: MessageEvent<{ type: string; message: string }>) => {
29
+ const onMessageReceivedFromOutside = useCallback(
30
+ (ev: MessageEvent<{ type: string; message: Record<string, unknown> }>) => {
31
+ if (typeof ev.data !== "object") return;
32
+ if (!ev.data.type) return;
33
+ if (ev.data.type !== "content-update") return;
34
+ if (!ev.data.message) return;
35
+ setEditorContent(ev.data.message);
36
+ },
37
+ []
38
+ );
39
+
40
+ useEffect(() => {
41
+ window.addEventListener("message", onMessageReceivedFromOutside);
42
+ return () => window.removeEventListener("message", onMessageReceivedFromOutside);
43
+ }, [onMessageReceivedFromOutside]);
44
+
45
+ const onMessageReceivedFromPanel = useCallback((ev: MessageEvent<{ type: string; message: string }>) => {
22
46
  if (typeof ev.data !== "object") return;
23
47
  if (!ev.data.type) return;
24
- if (ev.data.type !== "content-update") return;
48
+ if (ev.data.type !== "selected-content") return;
25
49
  if (!ev.data.message) return;
26
- setState(ev.data.message);
50
+ setSelectedContent(parseInt(ev.data.message));
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
27
52
  }, []);
28
53
 
29
54
  useEffect(() => {
30
- window.addEventListener("message", onMessageReceivedFromOutside);
31
- return () => window.removeEventListener("message", onMessageReceivedFromOutside);
32
- }, [onMessageReceivedFromOutside]);
55
+ window.addEventListener("message", onMessageReceivedFromPanel);
56
+ return () => window.removeEventListener("message", onMessageReceivedFromPanel);
57
+ }, [onMessageReceivedFromPanel]);
33
58
 
34
59
  const {
35
60
  editorContent: { canonicalSite, language, pageLanguages },
36
61
  header,
37
62
  footer,
38
- } = state;
63
+ } = content;
39
64
 
40
65
  document.body.classList.add("preview");
41
66
 
@@ -81,7 +106,7 @@ const FramePreview = (props: IProps) => {
81
106
  selectEditorID={selectEditorID}
82
107
  siteID={siteID}
83
108
  isPage={true}
84
- content={state.editorContent}
109
+ content={content.editorContent}
85
110
  header={currentSiteInfo && header}
86
111
  footer={currentSiteInfo && footer}
87
112
  languageID={language}
@@ -100,6 +125,7 @@ interface IProps {
100
125
  globalLangs: ILanguage[];
101
126
  isLoading: boolean;
102
127
  setSelectedContent(editorID: number): void;
128
+ setEditorContent(editorContent: Record<string, unknown>): void;
103
129
  }
104
130
 
105
131
  const mapStateToProps = (state: IRootState) => ({
@@ -114,6 +140,7 @@ const mapStateToProps = (state: IRootState) => ({
114
140
 
115
141
  const mapDispatchToProps = {
116
142
  setSelectedContent: pageEditorActions.setSelectedContent,
143
+ setEditorContent: pageEditorActions.setEditorContent,
117
144
  };
118
145
 
119
146
  export default connect(mapStateToProps, mapDispatchToProps)(FramePreview);
@@ -33,7 +33,6 @@ const GlobalEditor = (props: IProps) => {
33
33
  errors,
34
34
  setSelectedContent,
35
35
  setTab,
36
- validated,
37
36
  pageEditor: { editorContent },
38
37
  sitePageID,
39
38
  setHistoryPush,
@@ -60,6 +59,10 @@ const GlobalEditor = (props: IProps) => {
60
59
  const isLivePageChanged = editorContent.editorContent && editorContent.editorContent.liveChanged;
61
60
  const structuredData = editorContent.editorContent ? editorContent.editorContent.structuredData : "";
62
61
 
62
+ const errorNotificationText =
63
+ "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
64
+ const validatedNotificationText = "Everything seems ok, you can publish the page.";
65
+
63
66
  const theme = getDefaultTheme();
64
67
 
65
68
  useEffect(() => {
@@ -141,6 +144,8 @@ const GlobalEditor = (props: IProps) => {
141
144
  resetDirty();
142
145
  }
143
146
  });
147
+ } else {
148
+ setNotification({ text: errorNotificationText, type: "error" });
144
149
  }
145
150
  };
146
151
 
@@ -159,6 +164,8 @@ const GlobalEditor = (props: IProps) => {
159
164
  resetDirty();
160
165
  }
161
166
  });
167
+ } else {
168
+ setNotification({ text: errorNotificationText, type: "error" });
162
169
  }
163
170
  };
164
171
 
@@ -174,9 +181,14 @@ const GlobalEditor = (props: IProps) => {
174
181
  updatePageStatus([pageID], pageStatus.OFFLINE);
175
182
  };
176
183
 
177
- const reviewPage = () => {
184
+ const reviewPage = async () => {
178
185
  const { validatePage } = props;
179
- validatePage(false);
186
+ const validated = await validatePage(false);
187
+ if (validated) {
188
+ setNotification({ text: validatedNotificationText, type: "success" });
189
+ } else {
190
+ setNotification({ text: errorNotificationText, type: "error" });
191
+ }
180
192
  };
181
193
 
182
194
  const handlePublishDraft = async () => {
@@ -187,6 +199,8 @@ const GlobalEditor = (props: IProps) => {
187
199
  if (validated) {
188
200
  const isSaved = await savePage(false, null, true);
189
201
  if (isSaved) resetDirty();
202
+ } else {
203
+ setNotification({ text: errorNotificationText, type: "error" });
190
204
  }
191
205
  };
192
206
 
@@ -377,11 +391,6 @@ const GlobalEditor = (props: IProps) => {
377
391
  }
378
392
  };
379
393
 
380
- const errorNotificationText =
381
- "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
382
-
383
- const validatedNotificationText = "Everything seems ok, you can publish the page.";
384
-
385
394
  const globalNotificationText = "You’ re working on the Global Page. Make sure you want to make changes to this page.";
386
395
 
387
396
  const modifiedNotificationText =
@@ -433,16 +442,6 @@ const GlobalEditor = (props: IProps) => {
433
442
  pageStatusActions={pageStatusActions}
434
443
  tabs={tabsPreview}
435
444
  >
436
- {errors.length > 0 && (
437
- <S.NotificationWrapper>
438
- <Notification type="error" text={errorNotificationText} />
439
- </S.NotificationWrapper>
440
- )}
441
- {validated && (
442
- <S.NotificationWrapper>
443
- <Notification type="success" text={validatedNotificationText} />
444
- </S.NotificationWrapper>
445
- )}
446
445
  {isLivePageChanged && (
447
446
  <S.NotificationWrapper>
448
447
  <Notification type="warning" text={modifiedNotificationText} />
@@ -534,7 +533,6 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
534
533
  pageLanguages: state.pageEditor.currentPageLanguages,
535
534
  pageEditor: state.pageEditor,
536
535
  errors: state.pageEditor.errors,
537
- validated: state.pageEditor.validated,
538
536
  sitePageID: state.pageEditor.sitePageID,
539
537
  savedSiteInfo: state.sites.savedSiteInfo,
540
538
  userEditing: state.pageEditor.userEditing,
@@ -555,7 +553,6 @@ interface IPageEditorStateProps {
555
553
  pageLanguages: any[];
556
554
  pageEditor: any;
557
555
  errors: IErrorItem[];
558
- validated: boolean;
559
556
  sitePageID: number | null;
560
557
  savedSiteInfo: any;
561
558
  userEditing: IUserEditing | null;
@@ -33,12 +33,12 @@ const ItemList = (props: IItemList): JSX.Element => {
33
33
  }, [menu]);
34
34
 
35
35
  const getIcon = (item: TreeItem, onExpand: (itemId: ItemId) => void, onCollapse: (itemId: ItemId) => void) => {
36
- const handleCollapse = (e: MouseEvent) => {
36
+ const handleCollapse = (e: React.MouseEvent<HTMLButtonElement>) => {
37
37
  e.stopPropagation();
38
38
  onCollapse(item.id);
39
39
  };
40
40
 
41
- const handleExpand = (e: MouseEvent) => {
41
+ const handleExpand = (e: React.MouseEvent<HTMLButtonElement>) => {
42
42
  e.stopPropagation();
43
43
  onExpand(item.id);
44
44
  };
@@ -40,8 +40,14 @@ const Editor = (props: IProps) => {
40
40
  copyModule,
41
41
  pasteModule,
42
42
  setNotification,
43
+ restorePageNavigation,
44
+ content,
43
45
  } = props;
44
46
 
47
+ const {
48
+ editorContent: { header, footer },
49
+ } = content;
50
+
45
51
  const actions = {
46
52
  deleteModuleAction: deleteModule,
47
53
  addComponentAction: addComponent,
@@ -55,6 +61,7 @@ const Editor = (props: IProps) => {
55
61
  copyModuleAction: copyModule,
56
62
  pasteModuleAction: pasteModule,
57
63
  setNotificationAction: setNotification,
64
+ restorePageNavigationAction: restorePageNavigation,
58
65
  };
59
66
 
60
67
  return (
@@ -82,6 +89,8 @@ const Editor = (props: IProps) => {
82
89
  userEditing={userEditing}
83
90
  theme={site.theme}
84
91
  lastElementAddedId={lastElementAddedId}
92
+ header={header}
93
+ footer={footer}
85
94
  />
86
95
  }
87
96
  />
@@ -99,6 +108,7 @@ interface IEditorStateProps {
99
108
  userEditing: IUserEditing | null;
100
109
  site: ISite;
101
110
  lastElementAddedId: null | number;
111
+ content: any;
102
112
  }
103
113
 
104
114
  interface IPageBrowserDispatchProps {
@@ -117,6 +127,7 @@ interface IPageBrowserDispatchProps {
117
127
  copyModule(editorID: number): boolean;
118
128
  pasteModule(editorID: number, key: string): Promise<{ error?: INotification }>;
119
129
  setNotification: (notification: INotification) => void;
130
+ restorePageNavigation: (key: string) => void;
120
131
  isTemplateActivated: boolean;
121
132
  isGlobal: boolean;
122
133
  isEditable: boolean;
@@ -137,6 +148,7 @@ const mapStateToProps = (state: IRootState): IEditorStateProps => ({
137
148
  userEditing: state.pageEditor.userEditing,
138
149
  site: state.sites.currentSiteInfo,
139
150
  lastElementAddedId: state.pageEditor.lastElementAddedId,
151
+ content: state.pageEditor.editorContent,
140
152
  });
141
153
 
142
154
  const mapDispatchToProps = {
@@ -154,6 +166,7 @@ const mapDispatchToProps = {
154
166
  saveCurrentSiteInfo: sitesActions.saveCurrentSiteInfo,
155
167
  copyModule: pageEditorActions.copyModule,
156
168
  pasteModule: pageEditorActions.pasteModule,
169
+ restorePageNavigation: pageEditorActions.restorePageNavigation,
157
170
  };
158
171
 
159
172
  export default connect(mapStateToProps, mapDispatchToProps)(Editor);
@@ -40,7 +40,6 @@ const PageEditor = (props: IProps) => {
40
40
  errors,
41
41
  setSelectedContent,
42
42
  setTab,
43
- validated,
44
43
  pageEditor: { editorContent, schema },
45
44
  userEditing,
46
45
  isNewTranslation,
@@ -67,6 +66,10 @@ const PageEditor = (props: IProps) => {
67
66
  const isTranslated = pageLanguages.length > 1;
68
67
  const structuredData = editorContent.editorContent ? editorContent.editorContent.structuredData : "";
69
68
 
69
+ const errorNotificationText =
70
+ "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
71
+ const validatedNotificationText = "Everything seems ok, you can publish the page.";
72
+
70
73
  useEffect(() => {
71
74
  const { pageID, getPage, setTab, sendPagePing } = props;
72
75
  const defaultTab = "content";
@@ -145,6 +148,8 @@ const PageEditor = (props: IProps) => {
145
148
  resetDirty();
146
149
  }
147
150
  });
151
+ } else {
152
+ setNotification({ text: errorNotificationText, type: "error" });
148
153
  }
149
154
  };
150
155
 
@@ -163,6 +168,8 @@ const PageEditor = (props: IProps) => {
163
168
  resetDirty();
164
169
  }
165
170
  });
171
+ } else {
172
+ setNotification({ text: errorNotificationText, type: "error" });
166
173
  }
167
174
  };
168
175
 
@@ -178,9 +185,14 @@ const PageEditor = (props: IProps) => {
178
185
  updatePageStatus([pageID], pageStatus.OFFLINE);
179
186
  };
180
187
 
181
- const reviewPage = () => {
188
+ const reviewPage = async () => {
182
189
  const { validatePage } = props;
183
- validatePage(false);
190
+ const validated = await validatePage(false);
191
+ if (validated) {
192
+ setNotification({ text: validatedNotificationText, type: "success" });
193
+ } else {
194
+ setNotification({ text: errorNotificationText, type: "error" });
195
+ }
184
196
  };
185
197
 
186
198
  const handlePublishDraft = async () => {
@@ -191,6 +203,8 @@ const PageEditor = (props: IProps) => {
191
203
  if (validated) {
192
204
  const isSaved = await savePage(false, null, true);
193
205
  if (isSaved) resetDirty();
206
+ } else {
207
+ setNotification({ text: errorNotificationText, type: "error" });
194
208
  }
195
209
  };
196
210
 
@@ -426,11 +440,6 @@ const PageEditor = (props: IProps) => {
426
440
  }
427
441
  };
428
442
 
429
- const errorNotificationText =
430
- "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
431
-
432
- const validatedNotificationText = "Everything seems ok, you can publish the page.";
433
-
434
443
  const modifiedNotificationText =
435
444
  "You made some changes to the Live version that aren't implemented on this Draft. Check it out before you continue working.";
436
445
 
@@ -496,16 +505,6 @@ const PageEditor = (props: IProps) => {
496
505
  />
497
506
  </S.NotificationWrapper>
498
507
  )}
499
- {errors.length > 0 && (
500
- <S.NotificationWrapper>
501
- <Notification type="error" text={errorNotificationText} />
502
- </S.NotificationWrapper>
503
- )}
504
- {validated && (
505
- <S.NotificationWrapper>
506
- <Notification type="success" text={validatedNotificationText} />
507
- </S.NotificationWrapper>
508
- )}
509
508
  {isLivePageChanged && (
510
509
  <S.NotificationWrapper>
511
510
  <Notification type="warning" text={modifiedNotificationText} />
@@ -598,7 +597,6 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
598
597
  activatedTemplates: state.dataPacks.templates,
599
598
  activatedModules: state.dataPacks.modules,
600
599
  errors: state.pageEditor.errors,
601
- validated: state.pageEditor.validated,
602
600
  userEditing: state.pageEditor.userEditing,
603
601
  currentUserID: state.users.currentUser.id,
604
602
  isNewTranslation: state.pageEditor.isNewTranslation,
@@ -620,7 +618,6 @@ interface IPageEditorStateProps {
620
618
  activatedTemplates: any[];
621
619
  activatedModules: string[];
622
620
  errors: IErrorItem[];
623
- validated: boolean;
624
621
  userEditing: IUserEditing | null;
625
622
  currentUserID: number | null;
626
623
  isNewTranslation: boolean;
@@ -62,10 +62,24 @@ const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
62
62
  };
63
63
 
64
64
  const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };
65
-
66
- const target = `(${redirect.domain || ""}|${redirect.site?.siteUrl || ""})`;
65
+ const target =
66
+ redirect.site?.siteUrl === "/"
67
+ ? `(${redirect.domain || ""})`
68
+ : `(${redirect.domain || ""}|${redirect.site?.siteUrl || ""})`;
67
69
  const regex = new RegExp(target, "g");
68
70
 
71
+ const isUrlAndDomainEqual = () => {
72
+ const urlWithSlash = `${redirect.to?.url}/`;
73
+ const domainWithSlash = `${redirect.domain}/`;
74
+
75
+ return (
76
+ urlWithSlash === redirect.domain ||
77
+ urlWithSlash === domainWithSlash ||
78
+ domainWithSlash === redirect.to?.url ||
79
+ redirect.domain === redirect.to?.url
80
+ );
81
+ };
82
+
69
83
  return (
70
84
  <>
71
85
  <S.ItemRow role="rowgroup" selected={isSelected}>
@@ -83,7 +97,7 @@ const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
83
97
  {redirect.from.replace(regex, "")}
84
98
  </S.UrlCell>
85
99
  <S.UrlCell role="cell" onClick={handleClick}>
86
- {redirect.to?.url?.replace(regex, "") || "Deleted page"}
100
+ {isUrlAndDomainEqual() ? "/" : redirect.to?.url?.replace(regex, "") || "Deleted page"}
87
101
  </S.UrlCell>
88
102
  <S.DateCell role="cell" onClick={handleClick}>
89
103
  {redirect.date && format(new Date(redirect.date), "dd MMM yyyy")}
@@ -68,7 +68,7 @@ const Item = (props: IProps): JSX.Element => {
68
68
  </Modal>
69
69
  );
70
70
 
71
- const deleteAction = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
71
+ const deleteAction = (event: React.MouseEvent<HTMLButtonElement>) => {
72
72
  event.preventDefault();
73
73
  event.stopPropagation();
74
74
  const force = false;
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { IDataPack, IErrorItem, IRootState, ISite } from "@ax/types";
4
+ import { IDataPack, IErrorItem, INotification, IRootState, ISite } from "@ax/types";
5
5
  import { structuredDataActions } from "@ax/containers/StructuredData";
6
6
  import { MainWrapper, ErrorToast, Notification, Loading } from "@ax/components";
7
7
  import { getActivatedDataPacksIds, getDefaultTheme } from "@ax/helpers";
@@ -44,6 +44,7 @@ const Form = (props: IProps) => {
44
44
  } = props;
45
45
 
46
46
  const [isNewStructuredData, setIsNewStructuredData] = useState(!currentStructuredDataId);
47
+ const [notification, setNotification] = useState<INotification | null>(null);
47
48
  const { isDirty, resetDirty, setIsDirty } = useIsDirty(form);
48
49
 
49
50
  const { fields } = schema;
@@ -257,11 +258,6 @@ const Form = (props: IProps) => {
257
258
  languageActions: isDataTranslatable ? languageActions : null,
258
259
  };
259
260
 
260
- const errorNotificationText =
261
- "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
262
-
263
- const validatedNotificationText = "Everything seems ok, you can publish the page.";
264
-
265
261
  return isLoading ? (
266
262
  <Loading />
267
263
  ) : (
@@ -293,14 +289,15 @@ const Form = (props: IProps) => {
293
289
  />
294
290
  </S.NotificationWrapper>
295
291
  )}
296
- {errors.length > 0 && (
292
+ {notification && (
297
293
  <S.NotificationWrapper>
298
- <Notification type="error" text={errorNotificationText} />
299
- </S.NotificationWrapper>
300
- )}
301
- {validated && (
302
- <S.NotificationWrapper>
303
- <Notification type="success" text={validatedNotificationText} />
294
+ <Notification
295
+ type={notification.type}
296
+ text={notification.text}
297
+ btnText={notification.btnText}
298
+ onClick={notification.onClick}
299
+ resetError={() => setNotification(null)}
300
+ />
304
301
  </S.NotificationWrapper>
305
302
  )}
306
303
  <ErrorToast size="l" />