@griddo/ax 1.66.13 → 1.67.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 (74) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/Fields/ConditionalField/ConditionalField.test.tsx +95 -0
  3. package/src/api/pages.tsx +15 -3
  4. package/src/api/redirects.tsx +4 -2
  5. package/src/api/sites.tsx +12 -4
  6. package/src/components/Browser/index.tsx +9 -22
  7. package/src/components/Browser/style.tsx +1 -6
  8. package/src/components/ErrorCenter/index.tsx +8 -5
  9. package/src/components/ErrorCenter/style.tsx +21 -8
  10. package/src/components/Fields/ComponentArray/MixableComponentArray/AddItemButton/index.tsx +3 -3
  11. package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +60 -25
  12. package/src/components/Fields/ComponentContainer/index.tsx +21 -7
  13. package/src/components/Fields/ConditionalField/index.tsx +1 -1
  14. package/src/components/Fields/LinkField/index.tsx +111 -0
  15. package/src/components/Fields/ReferenceField/ItemList/index.tsx +4 -0
  16. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +12 -2
  17. package/src/components/Fields/ReferenceField/index.tsx +24 -12
  18. package/src/components/Fields/ReferenceField/style.tsx +12 -1
  19. package/src/components/Fields/UrlField/index.tsx +13 -1
  20. package/src/components/Fields/VisualUniqueSelection/utils.tsx +1 -6
  21. package/src/components/Fields/index.tsx +2 -0
  22. package/src/components/FieldsBehavior/index.tsx +14 -1
  23. package/src/components/Icon/components/Copy.js +14 -0
  24. package/src/components/Icon/components/Copy2.js +14 -0
  25. package/src/components/Icon/components/Duplicate.js +3 -5
  26. package/src/components/Icon/components/Page.js +12 -0
  27. package/src/components/Icon/svgs/Copy.svg +3 -0
  28. package/src/components/Icon/svgs/Copy2.svg +3 -0
  29. package/src/components/Icon/svgs/Duplicate.svg +1 -1
  30. package/src/components/Icon/svgs/page.svg +3 -0
  31. package/src/components/MainWrapper/AppBar/index.tsx +21 -10
  32. package/src/components/MainWrapper/AppBar/style.tsx +11 -3
  33. package/src/components/MainWrapper/index.tsx +2 -0
  34. package/src/components/Notification/index.tsx +1 -3
  35. package/src/components/SearchField/index.tsx +37 -4
  36. package/src/components/SearchField/style.tsx +23 -10
  37. package/src/components/index.tsx +2 -0
  38. package/src/containers/Navigation/Defaults/actions.tsx +2 -0
  39. package/src/containers/PageEditor/actions.tsx +92 -17
  40. package/src/containers/PageEditor/utils.tsx +2 -1
  41. package/src/containers/Sites/actions.tsx +53 -24
  42. package/src/containers/Sites/constants.tsx +2 -0
  43. package/src/containers/Sites/interfaces.tsx +12 -5
  44. package/src/containers/Sites/reducer.tsx +8 -0
  45. package/src/containers/StructuredData/actions.tsx +5 -8
  46. package/src/forms/index.tsx +9 -1
  47. package/src/forms/validators.tsx +119 -12
  48. package/src/helpers/index.tsx +2 -0
  49. package/src/helpers/objects.tsx +10 -2
  50. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +3 -1
  51. package/src/modules/Categories/CategoriesList/CategoryPanel/index.tsx +15 -9
  52. package/src/modules/Categories/CategoriesList/index.tsx +2 -1
  53. package/src/modules/Content/PageItem/index.tsx +52 -2
  54. package/src/modules/Content/atoms.tsx +41 -3
  55. package/src/modules/Content/index.tsx +44 -2
  56. package/src/modules/Content/style.tsx +8 -1
  57. package/src/modules/FramePreview/index.tsx +85 -0
  58. package/src/modules/FramePreview/style.tsx +18 -0
  59. package/src/modules/GlobalEditor/Editor/index.tsx +3 -1
  60. package/src/modules/GlobalEditor/PageBrowser/index.tsx +3 -0
  61. package/src/modules/GlobalEditor/index.tsx +22 -6
  62. package/src/modules/PageEditor/Editor/index.tsx +5 -1
  63. package/src/modules/PageEditor/PageBrowser/index.tsx +4 -5
  64. package/src/modules/PageEditor/index.tsx +27 -9
  65. package/src/modules/Redirects/index.tsx +40 -10
  66. package/src/modules/Settings/Globals/index.tsx +1 -1
  67. package/src/modules/Sites/index.tsx +2 -2
  68. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +1 -1
  69. package/src/modules/StructuredData/StructuredDataList/index.tsx +19 -2
  70. package/src/modules/Users/Profile/index.tsx +3 -3
  71. package/src/routes/multisite.tsx +12 -4
  72. package/src/routes/site.tsx +1 -1
  73. package/src/types/index.tsx +13 -4
  74. package/tsconfig.paths.json +2 -1
@@ -35,4 +35,11 @@ const ModalContent = styled.div`
35
35
  }
36
36
  `;
37
37
 
38
- export { ContentListWrapper, TableWrapper, PaginationWrapper, EmptyWrapper, ModalContent };
38
+ const SelectWrapper = styled.div`
39
+ & .react-select__control,
40
+ .react-select__menu {
41
+ min-width: 100%;
42
+ }
43
+ `;
44
+
45
+ export { ContentListWrapper, TableWrapper, PaginationWrapper, EmptyWrapper, ModalContent, SelectWrapper };
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import * as components from "components";
5
+ import { SiteProvider } from "components";
6
+ import { Preview } from "@griddo/core";
7
+ import { getDefaultTheme } from "@ax/helpers";
8
+ import { Loading } from "@ax/components";
9
+ import { ILanguage, IRootState, ISocialState } from "@ax/types";
10
+
11
+ import * as S from "./style";
12
+
13
+ const FramePreview = (props: IProps) => {
14
+ const { content, socials, selectedEditorID, cloudinaryName, isLoading, currentSiteInfo, siteLangs, globalLangs } =
15
+ props;
16
+
17
+ const {
18
+ editorContent: { canonicalSite, language, pageLanguages },
19
+ header,
20
+ footer,
21
+ } = content;
22
+
23
+ document.body.classList.add("preview");
24
+
25
+ const API_URL = process.env.REACT_APP_API_ENDPOINT;
26
+ const PUBLIC_API_URL = process.env.REACT_APP_PUBLIC_API_ENDPOINT;
27
+
28
+ const globalTheme = getDefaultTheme();
29
+ const theme = currentSiteInfo ? currentSiteInfo.theme : globalTheme;
30
+ const langs = currentSiteInfo ? siteLangs : globalLangs;
31
+ const siteID = currentSiteInfo ? currentSiteInfo.id : canonicalSite;
32
+
33
+ if (isLoading) return <Loading />;
34
+
35
+ return (
36
+ <SiteProvider
37
+ cloudinaryCloudName={cloudinaryName}
38
+ theme={theme}
39
+ socials={socials}
40
+ siteLangs={langs}
41
+ selectEditorID={selectedEditorID}
42
+ renderer="editor"
43
+ apiUrl={API_URL}
44
+ publicApiUrl={PUBLIC_API_URL}
45
+ siteId={siteID}
46
+ >
47
+ <S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
48
+ <Preview
49
+ isPage={true}
50
+ apiUrl={API_URL}
51
+ library={components}
52
+ content={content.editorContent}
53
+ header={currentSiteInfo && header}
54
+ footer={currentSiteInfo && footer}
55
+ languageId={language}
56
+ pageLanguages={pageLanguages}
57
+ />
58
+ </S.Wrapper>
59
+ </SiteProvider>
60
+ );
61
+ };
62
+
63
+ interface IProps {
64
+ content: any;
65
+ selectedEditorID: number;
66
+ socials: ISocialState;
67
+ cloudinaryName: string | null;
68
+ currentSiteInfo: any;
69
+ siteLangs: ILanguage[];
70
+ globalLangs: ILanguage[];
71
+ isLoading: boolean;
72
+ }
73
+
74
+ const mapStateToProps = (state: IRootState) => ({
75
+ content: { ...state.pageEditor.editorContent },
76
+ selectedEditorID: state.pageEditor.selectedEditorID as number,
77
+ socials: state.social,
78
+ cloudinaryName: state.app.globalSettings.cloudinaryName,
79
+ currentSiteInfo: state.sites.currentSiteInfo,
80
+ siteLangs: state.sites.currentSiteLanguages,
81
+ globalLangs: state.app.globalLangs,
82
+ isLoading: state.app.isLoading,
83
+ });
84
+
85
+ export default connect(mapStateToProps)(FramePreview);
@@ -0,0 +1,18 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ overflow: auto;
5
+ scroll-behavior: smooth;
6
+ height: 100%;
7
+ position: relative;
8
+
9
+ .headroom {
10
+ position: relative !important;
11
+ }
12
+
13
+ body.preview:not(&) {
14
+ min-width: 0 !important;
15
+ }
16
+ `;
17
+
18
+ export { Wrapper };
@@ -29,6 +29,7 @@ const Editor = (props: IProps) => {
29
29
  copyModule,
30
30
  pasteModule,
31
31
  theme,
32
+ browserRef,
32
33
  setNotification,
33
34
  } = props;
34
35
 
@@ -47,7 +48,7 @@ const Editor = (props: IProps) => {
47
48
 
48
49
  return (
49
50
  <ResizePanel
50
- leftPanel={<PageBrowser isReadOnly={isReadOnly} theme={theme} />}
51
+ leftPanel={<PageBrowser isReadOnly={isReadOnly} theme={theme} browserRef={browserRef} />}
51
52
  rightPanel={
52
53
  <ConfigPanel
53
54
  schema={schema}
@@ -99,6 +100,7 @@ interface IPageBrowserDispatchProps {
99
100
  isEditable: boolean;
100
101
  isReadOnly: boolean;
101
102
  theme: string;
103
+ browserRef: any;
102
104
  }
103
105
 
104
106
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -18,6 +18,7 @@ const PageBrowser = (props: IProps) => {
18
18
  theme,
19
19
  isReadOnly,
20
20
  isPreview,
21
+ browserRef,
21
22
  } = props;
22
23
 
23
24
  const slugWithSlash = slug ? (slug.startsWith("/") ? slug : `/${slug}`) : "";
@@ -38,6 +39,7 @@ const PageBrowser = (props: IProps) => {
38
39
  disabled={isReadOnly}
39
40
  siteID={canonicalSite}
40
41
  isPreview={isPreview}
42
+ browserRef={browserRef}
41
43
  />
42
44
  );
43
45
  };
@@ -60,6 +62,7 @@ interface IPageBrowserDispatchProps {
60
62
  theme: string;
61
63
  isReadOnly: boolean;
62
64
  isPreview?: boolean;
65
+ browserRef?: any;
63
66
  }
64
67
 
65
68
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useState, useRef } from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { RouteComponentProps } from "react-router-dom";
4
4
 
@@ -42,6 +42,7 @@ const GlobalEditor = (props: IProps) => {
42
42
  savedSiteInfo,
43
43
  userEditing,
44
44
  isNewTranslation,
45
+ currentSiteErrorPages,
45
46
  } = props;
46
47
 
47
48
  const { isOpen, toggleModal } = useModal();
@@ -50,6 +51,8 @@ const GlobalEditor = (props: IProps) => {
50
51
  const [selectedTab, setSelectedTab] = useState("edit");
51
52
  const [notification, setNotification] = useState<INotification | null>(null);
52
53
  const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent.editorContent, isNewTranslation);
54
+ const [errorPagesChecked, setErrorPagesChecked] = useState(false);
55
+ const browserRef = useRef<HTMLDivElement>(null);
53
56
 
54
57
  const isPublished = props.pageStatus === pageStatus.PUBLISHED || props.pageStatus === pageStatus.UPLOAD_PENDING;
55
58
  const isDraft = props.pageStatus === pageStatus.MODIFIED;
@@ -98,6 +101,16 @@ const GlobalEditor = (props: IProps) => {
98
101
  // eslint-disable-next-line react-hooks/exhaustive-deps
99
102
  }, [userEditing]);
100
103
 
104
+ useEffect(() => {
105
+ if (!errorPagesChecked && !isLoading && editorContent.editorContent) {
106
+ const pageId = editorContent.editorContent.id;
107
+ const hasErrors = currentSiteErrorPages.some((errorPageId) => pageId === errorPageId);
108
+ hasErrors && reviewPage();
109
+ setErrorPagesChecked(true);
110
+ }
111
+ // eslint-disable-next-line react-hooks/exhaustive-deps
112
+ }, [editorContent]);
113
+
101
114
  const setRoute = (path: string) => props.setHistoryPush(path, true);
102
115
 
103
116
  const removePage = async () => {
@@ -114,7 +127,7 @@ const GlobalEditor = (props: IProps) => {
114
127
  const publishPage = async () => {
115
128
  const { updatePageStatus, savePage, pageID, validatePage } = props;
116
129
 
117
- const validated = await validatePage(true);
130
+ const validated = await validatePage(true, browserRef);
118
131
 
119
132
  if (validated) {
120
133
  const publishPage = {
@@ -134,7 +147,7 @@ const GlobalEditor = (props: IProps) => {
134
147
  const publishChanges = async () => {
135
148
  const { savePage, validatePage } = props;
136
149
 
137
- const validated = await validatePage(true);
150
+ const validated = await validatePage(true, browserRef);
138
151
 
139
152
  if (validated) {
140
153
  const publishPage = {
@@ -163,13 +176,13 @@ const GlobalEditor = (props: IProps) => {
163
176
 
164
177
  const reviewPage = () => {
165
178
  const { validatePage } = props;
166
- validatePage();
179
+ validatePage(undefined, browserRef);
167
180
  };
168
181
 
169
182
  const handlePublishDraft = async () => {
170
183
  const { savePage, validatePage } = props;
171
184
 
172
- const validated = await validatePage(true);
185
+ const validated = await validatePage(true, browserRef);
173
186
 
174
187
  if (validated) {
175
188
  const isSaved = await savePage(false, null, true);
@@ -462,6 +475,7 @@ const GlobalEditor = (props: IProps) => {
462
475
  isEditable={isEditable}
463
476
  isReadOnly={isReadOnly}
464
477
  theme={theme}
478
+ browserRef={browserRef}
465
479
  setNotification={setNotification}
466
480
  />
467
481
  </S.Content>
@@ -524,6 +538,7 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
524
538
  userEditing: state.pageEditor.userEditing,
525
539
  currentUserID: state.users.currentUser.id,
526
540
  isNewTranslation: state.pageEditor.isNewTranslation,
541
+ currentSiteErrorPages: state.sites.currentSiteErrorPages,
527
542
  });
528
543
 
529
544
  interface IPageEditorStateProps {
@@ -543,6 +558,7 @@ interface IPageEditorStateProps {
543
558
  userEditing: IUserEditing | null;
544
559
  currentUserID: number | null;
545
560
  isNewTranslation: boolean;
561
+ currentSiteErrorPages: number[];
546
562
  }
547
563
 
548
564
  const mapDispatchToProps = {
@@ -567,7 +583,7 @@ interface IPageEditorDispatchProps {
567
583
  getPage(pageID?: number, global?: boolean): Promise<void>;
568
584
  savePage(createDraft: boolean, publishPage?: any, publishDraft?: boolean): Promise<boolean>;
569
585
  deletePage(params?: ISavePageParams): Promise<boolean>;
570
- validatePage(publish?: boolean): Promise<boolean>;
586
+ validatePage(publish?: boolean, browserRef?: any): Promise<boolean>;
571
587
  updatePageStatus(id: number[], status: string): Promise<boolean>;
572
588
  setHistoryPush(path: string, isEditor: boolean): void;
573
589
  setLanguage?(lang: { locale: string; id: number | null }): void;
@@ -35,6 +35,7 @@ const Editor = (props: IProps) => {
35
35
  isReadOnly,
36
36
  userEditing,
37
37
  site,
38
+ browserRef,
38
39
  lastElementAddedId,
39
40
  copyModule,
40
41
  pasteModule,
@@ -58,7 +59,9 @@ const Editor = (props: IProps) => {
58
59
 
59
60
  return (
60
61
  <ResizePanel
61
- leftPanel={<PageBrowser isTemplateActivated={isTemplateActivated} isReadOnly={isReadOnly} />}
62
+ leftPanel={
63
+ <PageBrowser isTemplateActivated={isTemplateActivated} isReadOnly={isReadOnly} browserRef={browserRef} />
64
+ }
62
65
  rightPanel={
63
66
  <ConfigPanel
64
67
  schema={schema}
@@ -119,6 +122,7 @@ interface IPageBrowserDispatchProps {
119
122
  isEditable: boolean;
120
123
  pageTitle: string;
121
124
  isReadOnly: boolean;
125
+ browserRef: any;
122
126
  }
123
127
 
124
128
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -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 { IBreadcrumbItem, ILanguage, IRootState, ISchema, ISocialState } from "@ax/types";
6
+ import { ILanguage, IRootState, ISocialState } from "@ax/types";
7
7
 
8
8
  const PageBrowser = (props: IProps) => {
9
9
  const {
@@ -21,6 +21,7 @@ const PageBrowser = (props: IProps) => {
21
21
  isTemplateActivated,
22
22
  isReadOnly,
23
23
  isPreview,
24
+ browserRef,
24
25
  } = props;
25
26
 
26
27
  const slugWithSlash = slug ? (slug.startsWith("/") ? slug : `/${slug}`) : "";
@@ -44,6 +45,7 @@ const PageBrowser = (props: IProps) => {
44
45
  disabled={disabled}
45
46
  siteID={siteID}
46
47
  isPreview={isPreview}
48
+ browserRef={browserRef}
47
49
  />
48
50
  );
49
51
  };
@@ -56,8 +58,6 @@ interface IEditorStateProps {
56
58
  socials: ISocialState;
57
59
  cloudinaryName: string | null;
58
60
  siteLangs: ILanguage[];
59
- schema: ISchema | Record<string, unknown>;
60
- breadcrumb: IBreadcrumbItem[];
61
61
  selectedParent: any;
62
62
  activatedModules: string[];
63
63
  }
@@ -67,6 +67,7 @@ interface IPageBrowserDispatchProps {
67
67
  isTemplateActivated: boolean;
68
68
  isReadOnly: boolean;
69
69
  isPreview?: boolean;
70
+ browserRef?: any;
70
71
  }
71
72
 
72
73
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -78,8 +79,6 @@ const mapStateToProps = (state: IRootState): IEditorStateProps => ({
78
79
  socials: state.social,
79
80
  cloudinaryName: state.app.globalSettings.cloudinaryName,
80
81
  siteLangs: state.sites.currentSiteLanguages,
81
- schema: state.pageEditor.schema,
82
- breadcrumb: state.pageEditor.breadcrumb,
83
82
  selectedParent: state.pageEditor.selectedParent,
84
83
  activatedModules: state.dataPacks.modules,
85
84
  });
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useState, useRef } from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { RouteComponentProps } from "react-router-dom";
4
4
 
@@ -44,6 +44,7 @@ const PageEditor = (props: IProps) => {
44
44
  pageEditor: { editorContent, schema },
45
45
  userEditing,
46
46
  isNewTranslation,
47
+ currentSiteErrorPages,
47
48
  } = props;
48
49
 
49
50
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
@@ -54,6 +55,8 @@ const PageEditor = (props: IProps) => {
54
55
  const { isOpen, toggleModal } = useModal();
55
56
  const { isOpen: isUnpublishOpen, toggleModal: toggleUnpublishModal } = useModal();
56
57
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
58
+ const [errorPagesChecked, setErrorPagesChecked] = useState(false);
59
+ const browserRef = useRef<HTMLDivElement>(null);
57
60
 
58
61
  const isGlobal = editorContent.editorContent && editorContent.editorContent.origin === "GLOBAL";
59
62
  const isEditable = editorContent.editorContent && editorContent.editorContent.editable;
@@ -101,6 +104,16 @@ const PageEditor = (props: IProps) => {
101
104
  // eslint-disable-next-line react-hooks/exhaustive-deps
102
105
  }, [lang]);
103
106
 
107
+ useEffect(() => {
108
+ if (!errorPagesChecked && !isLoading && editorContent.editorContent) {
109
+ const pageId = editorContent.editorContent.id;
110
+ const hasErrors = currentSiteErrorPages.some((errorPageId) => pageId === errorPageId);
111
+ hasErrors && reviewPage();
112
+ setErrorPagesChecked(true);
113
+ }
114
+ // eslint-disable-next-line react-hooks/exhaustive-deps
115
+ }, [editorContent]);
116
+
104
117
  const setRoute = (path: string) => props.setHistoryPush(path, true);
105
118
 
106
119
  const removePage = async () => {
@@ -118,7 +131,7 @@ const PageEditor = (props: IProps) => {
118
131
  const publishPage = async () => {
119
132
  const { updatePageStatus, savePage, pageID, validatePage } = props;
120
133
 
121
- const validated = await validatePage(true);
134
+ const validated = await validatePage(true, browserRef);
122
135
 
123
136
  if (validated) {
124
137
  const publishPage = {
@@ -138,7 +151,7 @@ const PageEditor = (props: IProps) => {
138
151
  const publishChanges = async () => {
139
152
  const { savePage, validatePage } = props;
140
153
 
141
- const validated = await validatePage(true);
154
+ const validated = await validatePage(true, browserRef);
142
155
 
143
156
  if (validated) {
144
157
  const publishPage = {
@@ -167,13 +180,13 @@ const PageEditor = (props: IProps) => {
167
180
 
168
181
  const reviewPage = () => {
169
182
  const { validatePage } = props;
170
- validatePage();
183
+ validatePage(undefined, browserRef);
171
184
  };
172
185
 
173
186
  const handlePublishDraft = async () => {
174
187
  const { savePage, validatePage } = props;
175
188
 
176
- const validated = await validatePage(true);
189
+ const validated = await validatePage(true, browserRef);
177
190
 
178
191
  if (validated) {
179
192
  const isSaved = await savePage(false, null, true);
@@ -295,10 +308,12 @@ const PageEditor = (props: IProps) => {
295
308
 
296
309
  const goToPages = (path: string) => setRoute(path);
297
310
 
298
- const goToError = (editorID: number, tab: string, template: boolean) => {
311
+ const goToError = (editorID: number | null, tab: string, template: boolean) => {
299
312
  const realEditorID = template ? 0 : editorID;
300
- setSelectedContent(realEditorID);
301
- setTab(tab);
313
+ if (realEditorID !== null) {
314
+ setSelectedContent(realEditorID);
315
+ setTab(tab);
316
+ }
302
317
  };
303
318
 
304
319
  const modalText = (
@@ -506,6 +521,7 @@ const PageEditor = (props: IProps) => {
506
521
  isEditable={isEditable}
507
522
  pageTitle={pageName}
508
523
  isReadOnly={isReadOnly}
524
+ browserRef={browserRef}
509
525
  setNotification={setNotification}
510
526
  />
511
527
  </S.Content>
@@ -576,6 +592,7 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
576
592
  userEditing: state.pageEditor.userEditing,
577
593
  currentUserID: state.users.currentUser.id,
578
594
  isNewTranslation: state.pageEditor.isNewTranslation,
595
+ currentSiteErrorPages: state.sites.currentSiteErrorPages,
579
596
  });
580
597
 
581
598
  interface IPageEditorStateProps {
@@ -596,6 +613,7 @@ interface IPageEditorStateProps {
596
613
  userEditing: IUserEditing | null;
597
614
  currentUserID: number | null;
598
615
  isNewTranslation: boolean;
616
+ currentSiteErrorPages: number[];
599
617
  }
600
618
 
601
619
  const mapDispatchToProps = {
@@ -622,7 +640,7 @@ interface IPageEditorDispatchProps {
622
640
  getPage(pageID?: number): Promise<void>;
623
641
  savePage(createDraft: boolean, publishPage?: any, publishDraft?: boolean): Promise<boolean>;
624
642
  deletePage(params?: ISavePageParams): Promise<boolean>;
625
- validatePage(publish?: boolean): Promise<boolean>;
643
+ validatePage(publish?: boolean, browserRef?: any): Promise<boolean>;
626
644
  updatePageStatus(id: number[], status: string): Promise<boolean>;
627
645
  setHistoryPush(path: string, isEditor: boolean): void;
628
646
  setLanguage?(lang: { locale: string; id: number | null }): void;
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { INavItem, IRedirect, IRootState } from "@ax/types";
4
+ import { IEmptyStateProps, INavItem, IRedirect, IRootState } from "@ax/types";
5
5
  import { appActions } from "@ax/containers/App";
6
6
  import { redirectsActions } from "@ax/containers/Redirects";
7
7
  import { MainWrapper, ErrorToast, Nav, TableList, EmptyState, Toast } from "@ax/components";
@@ -30,6 +30,7 @@ const Redirects = (props: IProps): JSX.Element => {
30
30
  importRedirects,
31
31
  imports,
32
32
  totalImports,
33
+ isLoading,
33
34
  } = props;
34
35
 
35
36
  const itemsPerPage = 50;
@@ -50,6 +51,10 @@ const Redirects = (props: IProps): JSX.Element => {
50
51
  const { isOpen: isOpenCheckImport, toggleModal: toggleCheckImportModal } = useModal();
51
52
  const [importData, setImportData] = useState<{ from: string; to: string }[]>([]);
52
53
  const [isUploading, setIsUploading] = useState(false);
54
+ const [searchQuery, setSearchQuery] = useState<string>("");
55
+ const [searchFilter, setSearchFilter] = useState<string>("");
56
+ const [isEmpty, setIsEmpty] = useState(false);
57
+ const [emptyStateProps, setEmptyStateProps] = useState<IEmptyStateProps>({});
53
58
 
54
59
  const initState = {
55
60
  from: "",
@@ -62,17 +67,18 @@ const Redirects = (props: IProps): JSX.Element => {
62
67
  const [formValues, setFormValues] = useState<IRedirect>(initState);
63
68
 
64
69
  const redIds = redirects && redirects.map((red: any) => red.id);
65
- const isEmpty = redirects && redirects.length === 0;
66
70
 
67
71
  const getParams = useCallback(() => {
68
72
  const params = {
69
73
  page,
70
74
  itemsPerPage,
71
75
  pagination: true,
76
+ query: searchQuery.trim(),
77
+ filterBy: searchFilter === "filterby" || searchQuery.trim() === "" ? "" : searchFilter,
72
78
  };
73
79
 
74
80
  return params;
75
- }, [page]);
81
+ }, [page, searchQuery, searchFilter]);
76
82
 
77
83
  useEffect(() => {
78
84
  const params = getParams();
@@ -81,7 +87,26 @@ const Redirects = (props: IProps): JSX.Element => {
81
87
  tableRef.current.scrollTo(0, 0);
82
88
  }
83
89
  // eslint-disable-next-line react-hooks/exhaustive-deps
84
- }, [page, currentFilterQuery]);
90
+ }, [page, currentFilterQuery, searchQuery, searchFilter]);
91
+
92
+ useEffect(() => {
93
+ if (!isLoading) {
94
+ const emptyState: IEmptyStateProps = {};
95
+ const isSearching = searchQuery.length > 0;
96
+ if (isSearching) {
97
+ emptyState.icon = "search";
98
+ emptyState.title = "Oh! No Results Found";
99
+ emptyState.message = "We couldn’t find what you are looking for. Please, try another search.";
100
+ } else {
101
+ emptyState.message = "To have a redirects on your site, create as many redirects as you want.";
102
+ emptyState.button = "Create New redirect";
103
+ emptyState.action = handleModal;
104
+ }
105
+ setIsEmpty(!redirects.length);
106
+ setEmptyStateProps(emptyState);
107
+ }
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, [isLoading]);
85
110
 
86
111
  const {
87
112
  resetBulkSelection,
@@ -176,12 +201,6 @@ const Redirects = (props: IProps): JSX.Element => {
176
201
  toggleOverwriteModal();
177
202
  };
178
203
 
179
- const emptyStateProps = {
180
- message: "To have a redirects on your site, create as many redirects as you want.",
181
- button: "Create New redirect",
182
- action: handleModal,
183
- };
184
-
185
204
  const pagination = {
186
205
  setPage,
187
206
  itemsPerPage,
@@ -236,6 +255,12 @@ const Redirects = (props: IProps): JSX.Element => {
236
255
 
237
256
  const secondaryImportModalAction = { title: "Cancel", onClick: toggleCheckImportModal };
238
257
 
258
+ const searchFilters = [
259
+ { value: "filterby", label: "Filter by" },
260
+ { value: "from", label: "Old URL" },
261
+ { value: "to", label: "New URL" },
262
+ ];
263
+
239
264
  return (
240
265
  <>
241
266
  <MainWrapper
@@ -243,6 +268,9 @@ const Redirects = (props: IProps): JSX.Element => {
243
268
  title="SEO Settings"
244
269
  rightButton={rightButtonProps}
245
270
  rightLineButton={rightLineButtonProps}
271
+ searchAction={setSearchQuery}
272
+ filterSearchAction={setSearchFilter}
273
+ searchFilters={searchFilters}
246
274
  >
247
275
  <S.Wrapper>
248
276
  <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
@@ -341,6 +369,7 @@ const mapStateToProps = (state: IRootState) => ({
341
369
  currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
342
370
  totalImports: state.redirects.totalImports,
343
371
  imports: state.redirects.imports,
372
+ isLoading: state.app.isLoading,
344
373
  });
345
374
 
346
375
  const mapDispatchToProps = {
@@ -357,6 +386,7 @@ interface IRedirectsProps {
357
386
  totalItems: number;
358
387
  currentSiteID: number | null;
359
388
  totalImports: number;
389
+ isLoading: boolean;
360
390
  imports: null | {
361
391
  error: IRedirect[];
362
392
  existing: IRedirect[];
@@ -87,7 +87,7 @@ const Globals = (props: IProps): JSX.Element => {
87
87
  text="The design of the navigation modules has changed. Please, check the previously created."
88
88
  btnText="Go to check"
89
89
  onClick={goToNavigationModules}
90
- onClose={closeNavigationNotification}
90
+ resetError={closeNavigationNotification}
91
91
  />
92
92
  )}
93
93
  <S.FormWrapper>
@@ -28,7 +28,7 @@ const Sites = (props: IProps): JSX.Element => {
28
28
  const fetchInitialData = async () => {
29
29
  setCurrentSiteInfo(null);
30
30
  await getStructuredData(token);
31
- await getSites(token);
31
+ await getSites();
32
32
  await getAllDataPacks();
33
33
  await getUser("me");
34
34
 
@@ -68,7 +68,7 @@ interface IStateProps {
68
68
 
69
69
  interface IDispatchProps {
70
70
  setCurrentSiteInfo(currentSiteInfo: any): void;
71
- getSites(token: string): Promise<void>;
71
+ getSites(): Promise<void>;
72
72
  getStructuredData(token: string, siteId?: number): Promise<void>;
73
73
  setLanguage(lang: { locale: string; id: number | null }): void;
74
74
  getAllDataPacks: () => Promise<void>;
@@ -405,7 +405,7 @@ interface IGlobalPageItemProps {
405
405
  isAllPages?: boolean;
406
406
  globalPage: IPage;
407
407
  updatePageStatus(ids: number[], status: string, updatedFromList: boolean): Promise<boolean>;
408
- duplicatePage(pageID: number, data: { title: string; slug: string }): Promise<void>;
408
+ duplicatePage(pageID: number, data: { title: string; slug: string }, siteID?: number): Promise<boolean>;
409
409
  setHistoryPush(path: string, isEditor: boolean): void;
410
410
  deletePage(params?: ISavePageParams, currentLanguage?: string): Promise<boolean>;
411
411
  getGlobalPages(): void;