@griddo/ax 1.65.27 → 1.66.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 (57) hide show
  1. package/package.json +2 -2
  2. package/src/GlobalStore.tsx +4 -3
  3. package/src/api/structuredData.tsx +15 -1
  4. package/src/components/Browser/index.tsx +13 -16
  5. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +12 -0
  6. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +11 -0
  7. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +13 -0
  8. package/src/components/ConfigPanel/Form/index.tsx +3 -1
  9. package/src/components/ConfigPanel/index.tsx +19 -5
  10. package/src/components/ConfigPanel/style.tsx +5 -0
  11. package/src/components/FieldContainer/index.tsx +4 -0
  12. package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx +49 -0
  13. package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +51 -11
  14. package/src/components/Fields/ComponentArray/MixableComponentArray/style.tsx +14 -1
  15. package/src/components/Fields/ComponentContainer/index.tsx +24 -5
  16. package/src/components/Fields/ImageField/index.tsx +18 -5
  17. package/src/components/Image/index.tsx +25 -0
  18. package/src/components/Notification/index.tsx +3 -1
  19. package/src/components/index.tsx +2 -0
  20. package/src/containers/Navigation/Defaults/actions.tsx +27 -9
  21. package/src/containers/PageEditor/actions.tsx +104 -5
  22. package/src/containers/PageEditor/constants.tsx +2 -0
  23. package/src/containers/PageEditor/interfaces.tsx +12 -0
  24. package/src/containers/PageEditor/reducer.tsx +8 -0
  25. package/src/containers/PageEditor/utils.tsx +2 -2
  26. package/src/helpers/index.tsx +6 -0
  27. package/src/helpers/schemas.tsx +36 -7
  28. package/src/modules/App/Routing/index.tsx +1 -1
  29. package/src/modules/Content/OptionTable/index.tsx +44 -43
  30. package/src/modules/Content/OptionTable/store.tsx +1 -1
  31. package/src/modules/Content/OptionTable/style.tsx +27 -12
  32. package/src/modules/Content/PageItem/index.tsx +14 -4
  33. package/src/modules/Content/atoms.tsx +19 -2
  34. package/src/modules/Content/index.tsx +37 -14
  35. package/src/modules/Content/utils.tsx +27 -12
  36. package/src/modules/GlobalEditor/Editor/index.tsx +12 -1
  37. package/src/modules/GlobalEditor/index.tsx +20 -2
  38. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +13 -0
  39. package/src/modules/Navigation/Defaults/atoms.tsx +28 -0
  40. package/src/modules/Navigation/Defaults/index.tsx +30 -4
  41. package/src/modules/Navigation/Defaults/style.tsx +32 -1
  42. package/src/modules/PageEditor/Editor/index.tsx +16 -1
  43. package/src/modules/PageEditor/index.tsx +14 -1
  44. package/src/modules/PublicPreview/index.tsx +15 -18
  45. package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/index.tsx +35 -0
  46. package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/style.tsx +22 -0
  47. package/src/modules/Settings/Globals/NavigationModules/SideModal/index.tsx +111 -0
  48. package/src/modules/Settings/Globals/NavigationModules/SideModal/style.tsx +64 -0
  49. package/src/modules/Settings/Globals/NavigationModules/index.tsx +89 -0
  50. package/src/modules/Settings/Globals/NavigationModules/style.tsx +36 -0
  51. package/src/modules/Settings/Globals/index.tsx +38 -1
  52. package/src/modules/Sites/SitesList/SiteItem/index.tsx +7 -5
  53. package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +14 -3
  54. package/src/modules/StructuredData/StructuredDataList/OptionTable/style.tsx +11 -2
  55. package/src/modules/StructuredData/StructuredDataList/atoms.tsx +19 -2
  56. package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -13
  57. package/src/types/index.tsx +11 -0
@@ -1,11 +1,10 @@
1
1
  import React, { useReducer, useEffect, useLayoutEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { IDataPack, IRootState } from "@ax/types";
5
4
  import { structuredDataActions } from "@ax/containers/StructuredData";
6
5
  import { getStructuredDataTitle, getThumbnailProps, isGlobalStructuredData } from "@ax/helpers";
7
- import { dataPacksActions } from "@ax/containers/Settings";
8
6
  import { MenuItem, RadioGroup } from "@ax/components";
7
+ import { SecondaryActionButton, MainActionButton } from "./../atoms";
9
8
 
10
9
  import { reducer, IOptionTableStore, setColumnValues, setShowThumbnail, setSelectedType, setOption } from "./store";
11
10
 
@@ -20,11 +19,15 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
20
19
  selectedValue,
21
20
  setIsStructuredData,
22
21
  theme,
23
- getDataPack,
24
- dataPacks,
22
+ mainAction,
23
+ secondaryAction,
25
24
  } = props;
26
-
27
25
  const filterOptions = (value: string, objKey: string) => values.filter((item: any) => item[objKey] === value);
26
+ const filterOptionsByDataPack = (value: string) => {
27
+ return values.filter((item: any) => {
28
+ return item.dataPacks ? item.dataPacks.includes(value.toUpperCase()) : item.type === value;
29
+ });
30
+ };
28
31
  const currentOption = filterOptions(selectedValue, "value");
29
32
  const currentType = currentOption[0] ? currentOption[0].type : "static";
30
33
 
@@ -49,7 +52,7 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
49
52
  }
50
53
  };
51
54
 
52
- let filteredByTypeOptions = filterOptions(state.selectedType, "type");
55
+ let filteredOptionsByDataPack: any = filterOptionsByDataPack(state.selectedType);
53
56
 
54
57
  useLayoutEffect(() => {
55
58
  displayOptions({ value: state.selectedType });
@@ -57,19 +60,11 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
57
60
  }, []);
58
61
 
59
62
  useEffect(() => {
60
- const firstOption = filteredByTypeOptions[0] && filteredByTypeOptions[0].value;
61
- selectOption(firstOption, filteredByTypeOptions);
63
+ const firstOption = filteredOptionsByDataPack[0] && filteredOptionsByDataPack[0].value;
64
+ selectOption(firstOption, filteredOptionsByDataPack);
62
65
  // eslint-disable-next-line
63
66
  }, []);
64
67
 
65
- useEffect(() => {
66
- if (currentType !== "static") {
67
- const dataPack = dataPacks.find((pack) => pack.templates.some((template) => template.id === state.selectedOption));
68
- dataPack && getDataPack(dataPack.id);
69
- }
70
- // eslint-disable-next-line react-hooks/exhaustive-deps
71
- }, [state.selectedOption]);
72
-
73
68
  const setValue = (value: string, isStructuredData: boolean) => {
74
69
  setIsStructuredData(isStructuredData);
75
70
  isStructuredData ? selectData(value) : selectPage(value);
@@ -87,29 +82,31 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
87
82
 
88
83
  const thumbnailProps =
89
84
  state.showThumbnail &&
90
- isOptionInType(filteredByTypeOptions) &&
85
+ isOptionInType(filteredOptionsByDataPack) &&
91
86
  getThumbnailProps(state.selectedOption, true, theme);
92
87
 
93
88
  const displayOptions = (item: any) => {
94
89
  const { value } = item;
95
90
  setType(value);
96
- filteredByTypeOptions = filterOptions(value, "type");
97
-
98
- const isGlobal = value !== "static" && isGlobalStructuredData(value.toUpperCase());
99
- if (isGlobal) {
100
- filteredByTypeOptions = filteredByTypeOptions.map((option: any) => {
101
- if (option.mode !== "detail") return option;
102
- const title = `Get ${getStructuredDataTitle(value.toUpperCase())} from Global`;
103
- return { ...option, title };
104
- });
105
- const globalOptionIdx = filteredByTypeOptions.findIndex((option: any) => option.mode === "detail");
106
- const globalOption = filteredByTypeOptions.splice(globalOptionIdx, 1);
107
- filteredByTypeOptions.push(...globalOption);
108
- }
109
-
110
- setValues(filteredByTypeOptions);
111
- const firstOption = filteredByTypeOptions[0] && filteredByTypeOptions[0].value;
112
- selectOption(firstOption, filteredByTypeOptions);
91
+ filteredOptionsByDataPack = filterOptionsByDataPack(value);
92
+ filteredOptionsByDataPack.forEach((option: any) => {
93
+ const { type } = option;
94
+ const isGlobal = value !== "static" && type && isGlobalStructuredData(type.toUpperCase());
95
+ if (isGlobal) {
96
+ filteredOptionsByDataPack = filteredOptionsByDataPack.map((option: any) => {
97
+ if (option.mode !== "detail") return option;
98
+ const title = `Get ${getStructuredDataTitle(type?.toUpperCase())} from Global`;
99
+ return { ...option, title };
100
+ });
101
+ const globalOptionIdx = filteredOptionsByDataPack.findIndex((option: any) => option.mode === "detail");
102
+ const globalOption = filteredOptionsByDataPack.splice(globalOptionIdx, 1);
103
+ filteredOptionsByDataPack.push(...globalOption);
104
+ }
105
+ });
106
+
107
+ setValues(filteredOptionsByDataPack);
108
+ const firstOption = filteredOptionsByDataPack[0] && filteredOptionsByDataPack[0].value;
109
+ selectOption(firstOption, filteredOptionsByDataPack);
113
110
 
114
111
  const optionList = filterOptions(state.selectedOption, "value");
115
112
  const displayThumbnail = optionList.type !== value;
@@ -117,7 +114,7 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
117
114
  showThumbnail(displayThumbnail);
118
115
  };
119
116
 
120
- const hasThumbnail = state.showThumbnail && thumbnailProps && isOptionInType(filteredByTypeOptions);
117
+ const hasThumbnail = state.showThumbnail && thumbnailProps && isOptionInType(filteredOptionsByDataPack);
121
118
 
122
119
  return (
123
120
  <S.Table>
@@ -144,30 +141,34 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
144
141
  {thumbnailProps && <S.Thumbnail backgroundUrl={thumbnailProps.src} />}
145
142
  </S.ThumbnailWrapper>
146
143
  )}
144
+ <S.Actions>
145
+ <SecondaryActionButton onClick={secondaryAction.onClick} title={secondaryAction.title} />
146
+ <MainActionButton onClick={mainAction.onClick} title={mainAction.title} />
147
+ </S.Actions>
147
148
  </S.Column>
148
149
  </S.Table>
149
150
  );
150
151
  };
151
152
 
153
+ interface IAction {
154
+ title: string;
155
+ onClick: any;
156
+ }
157
+
152
158
  interface IOptionTableProps {
153
159
  selectData: (value: string) => void;
154
160
  selectPage: (value: string) => void;
155
161
  setIsStructuredData: (isActive: boolean) => void;
156
- getDataPack: (id: string) => void;
157
162
  filters: any;
158
163
  values: any;
159
164
  selectedValue: string;
160
165
  theme: string;
161
- dataPacks: IDataPack[];
166
+ mainAction: IAction;
167
+ secondaryAction: IAction;
162
168
  }
163
169
 
164
- const mapStateToProps = (state: IRootState) => ({
165
- dataPacks: state.dataPacks.activated,
166
- });
167
-
168
170
  const mapDispatchToProps = {
169
171
  setIsStructuredData: structuredDataActions.setIsActive,
170
- getDataPack: dataPacksActions.getSiteDataPack,
171
172
  };
172
173
 
173
- export default connect(mapStateToProps, mapDispatchToProps)(OptionTable);
174
+ export default connect(null, mapDispatchToProps)(OptionTable);
@@ -43,4 +43,4 @@ export interface IOptionTableStore {
43
43
  showThumbnail: boolean;
44
44
  selectedOption: string;
45
45
  selectedType: any;
46
- }
46
+ }
@@ -3,20 +3,20 @@ import styled from "styled-components";
3
3
  export const Table = styled.div`
4
4
  display: flex;
5
5
  height: 100%;
6
- padding: ${p => p.theme.spacing.m} 0;
6
+ padding-top: ${(p) => p.theme.spacing.m};
7
7
  `;
8
8
 
9
9
  export const Column = styled.div`
10
- width: calc(${p => p.theme.spacing.m} * 10);
11
- padding: 0 ${p => p.theme.spacing.m};
12
- height: 100%;
10
+ width: calc(${(p) => p.theme.spacing.m} * 10);
11
+ padding: 0 ${(p) => p.theme.spacing.m};
12
+ height: calc(100% - ${(p) => p.theme.spacing.m});
13
13
  &:first-child {
14
14
  ::-webkit-scrollbar {
15
15
  -webkit-appearance: none;
16
16
  width: 2px;
17
17
  height: 100%;
18
18
  }
19
-
19
+
20
20
  ::-webkit-scrollbar-thumb {
21
21
  border-radius: 4px;
22
22
  background-color: ${(p) => p.theme.color.iconNonActive};
@@ -24,31 +24,46 @@ export const Column = styled.div`
24
24
  border-right: 1px solid ${(p) => p.theme.color.uiLine};
25
25
  overflow: scroll;
26
26
  }
27
+
28
+ &:last-child {
29
+ padding-bottom: ${(p) => p.theme.spacing.s};
30
+ }
27
31
  `;
28
32
 
29
33
  export const NavLink = styled.a`
34
+ text-transform: capitalize;
30
35
  &.selected {
31
- background-color: ${(p) => p.theme.color.overlayPressedDark };
36
+ background-color: ${(p) => p.theme.color.overlayPressedDark};
32
37
  }
33
38
  `;
34
39
 
35
40
  export const ThumbnailWrapper = styled.div`
36
41
  overflow: hidden;
37
42
  border-radius: 4px;
38
- padding: ${p => p.theme.spacing.xs};
39
- height: 100%;
43
+ padding: ${(p) => p.theme.spacing.xs};
44
+ margin-bottom: ${(p) => p.theme.spacing.m};
45
+ height: 220px;
40
46
  background-color: #ffffff;
41
47
  `;
42
48
 
43
- export const Thumbnail = styled.div<{backgroundUrl: string | boolean}>`
44
- background: ${p => p.backgroundUrl ? `url(${p.backgroundUrl}) no-repeat` : ""};
49
+ export const Thumbnail = styled.div<{ backgroundUrl: string | boolean }>`
50
+ background: ${(p) => (p.backgroundUrl ? `url(${p.backgroundUrl}) no-repeat` : "")};
45
51
  background-size: cover;
46
52
  background-position: top;
47
53
  border-radius: 4px;
48
- min-width: calc(${p => p.theme.spacing.l} * 3);
54
+ min-width: calc(${(p) => p.theme.spacing.l} * 3);
49
55
  height: 100%;
50
56
  `;
51
57
 
52
58
  export const Link = styled.div<{ active: boolean }>`
53
- color: ${p => p.active ? p.theme.color.textHighEmphasis : p.theme.color.textMediumEmphasis};
59
+ color: ${(p) => (p.active ? p.theme.color.textHighEmphasis : p.theme.color.textMediumEmphasis)};
54
60
  `;
61
+
62
+ export const Actions = styled.div`
63
+ position: fixed;
64
+ bottom: ${(p) => p.theme.spacing.m};
65
+ right: ${(p) => p.theme.spacing.m};
66
+ button {
67
+ margin-left: ${(p) => p.theme.spacing.s};
68
+ }
69
+ `;
@@ -35,7 +35,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
35
35
  addCategoryColors,
36
36
  dataPacks,
37
37
  } = props;
38
- const { isSelected, siteLanguages, page, lang } = item;
38
+ const { isSelected, siteLanguages, page, lang, isDuplicable } = item;
39
39
  const {
40
40
  onClick,
41
41
  onCheck,
@@ -49,6 +49,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
49
49
  removePageFromSite,
50
50
  deleteBulk,
51
51
  getDataPack,
52
+ setTemplateInstanceError,
52
53
  } = functions;
53
54
  const { locale } = lang;
54
55
  const {
@@ -225,10 +226,17 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
225
226
  };
226
227
 
227
228
  const handleDuplicatePage = () => {
228
- duplicatePage(page.id, modalState).then(() => {
229
+ setTemplateInstanceError({ error: false, templateName: "" });
230
+ if (isDuplicable) {
231
+ duplicatePage(page.id, modalState).then(() => {
232
+ toggleModal();
233
+ setRoute("pages/editor");
234
+ });
235
+ } else {
229
236
  toggleModal();
230
- setRoute("pages/editor");
231
- });
237
+ setModalState({ title: "", slug: "" });
238
+ setTemplateInstanceError({ error: true, templateName: displayName });
239
+ }
232
240
  };
233
241
 
234
242
  const mainModalAction = {
@@ -553,6 +561,7 @@ interface IPageItemProps {
553
561
  page: IPage;
554
562
  site: ISite;
555
563
  lang: { locale: string; id: number };
564
+ isDuplicable: boolean;
556
565
  };
557
566
  functions: {
558
567
  languageActions?: {
@@ -570,6 +579,7 @@ interface IPageItemProps {
570
579
  duplicatePage(pageID: number, data: any): Promise<void>;
571
580
  removePageFromSite(pageID: number): Promise<boolean>;
572
581
  deleteBulk(ids: number[]): void;
582
+ setTemplateInstanceError(error: any): void;
573
583
  getDataPack: (id: string) => Promise<void>;
574
584
  };
575
585
  activatedTemplates: any[];
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
 
3
3
  import { IModal } from "@ax/types";
4
- import { Modal, FieldsBehavior } from "@ax/components";
4
+ import { Modal, FieldsBehavior, Button } from "@ax/components";
5
5
 
6
6
  import * as S from "./style";
7
7
 
@@ -68,6 +68,18 @@ const DeleteModal = (props: IDeleteModal): JSX.Element => {
68
68
  );
69
69
  };
70
70
 
71
+ const MainActionButton = (props: IActionButton): JSX.Element => (
72
+ <Button type="button" onClick={props.onClick}>
73
+ {props.title}
74
+ </Button>
75
+ );
76
+
77
+ const SecondaryActionButton = (props: IActionButton): JSX.Element => (
78
+ <Button type="button" buttonStyle="text" onClick={props.onClick}>
79
+ {props.title}
80
+ </Button>
81
+ );
82
+
71
83
  interface IDeleteModal extends IModal {
72
84
  isTranslated: boolean;
73
85
  deleteAllVersions: boolean;
@@ -75,4 +87,9 @@ interface IDeleteModal extends IModal {
75
87
  title?: string;
76
88
  }
77
89
 
78
- export { DeleteModal };
90
+ interface IActionButton {
91
+ onClick: () => void;
92
+ title: string;
93
+ }
94
+
95
+ export { DeleteModal, MainActionButton, SecondaryActionButton };
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useCallback, useState, useRef } from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { useLocation } from "react-router-dom";
4
+ import { schemas } from "components";
4
5
 
5
6
  import { useModal, useBulkSelection, useToast, useCategoryColors } from "@ax/hooks";
6
7
  import {
@@ -15,7 +16,7 @@ import {
15
16
  IColumn,
16
17
  ISite,
17
18
  } from "@ax/types";
18
- import { MainWrapper, Modal, TableList, ErrorToast, Toast, EmptyState } from "@ax/components";
19
+ import { MainWrapper, Modal, TableList, ErrorToast, Toast, EmptyState, Notification } from "@ax/components";
19
20
  import { getFilteredStructuredData, isGlobalStructuredData, isStructuredDataFromPage } from "@ax/helpers";
20
21
 
21
22
  import { appActions } from "@ax/containers/App";
@@ -85,7 +86,6 @@ const Content = (props: IProps): JSX.Element => {
85
86
  restorePage,
86
87
  getDataPack,
87
88
  dataPacks,
88
- setCurrentDataID,
89
89
  } = props;
90
90
 
91
91
  const itemsPerPage = 50;
@@ -108,6 +108,7 @@ const Content = (props: IProps): JSX.Element => {
108
108
  const pagesIds = currentSitePages && currentSitePages.map((page: any) => page.id);
109
109
  const dataIds = currentDataContent && currentDataContent.map((data: any) => data.id);
110
110
  const contentIds = isStructuredData ? dataIds : pagesIds;
111
+ const currentSitePagesTemplatesIds = currentSitePages && currentSitePages.map((page: any) => page.templateId);
111
112
 
112
113
  const categoryColumns =
113
114
  currentStructuredData && currentStructuredData.schema
@@ -145,6 +146,7 @@ const Content = (props: IProps): JSX.Element => {
145
146
  const [removedPage, setRemovedPage] = useState<number | number[] | null>(null);
146
147
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
147
148
  const [arePagesTranslated, setArePagesTranslated] = useState(false);
149
+ const [templateInstanceError, setTemplateInstanceError] = useState({ error: false, templateName: "" });
148
150
 
149
151
  const {
150
152
  resetBulkSelection,
@@ -234,7 +236,6 @@ const Content = (props: IProps): JSX.Element => {
234
236
  setFilter("unique-pages");
235
237
  }
236
238
  resetPageEditor();
237
- setCurrentDataID(null);
238
239
  // eslint-disable-next-line react-hooks/exhaustive-deps
239
240
  }, []);
240
241
 
@@ -280,12 +281,22 @@ const Content = (props: IProps): JSX.Element => {
280
281
  const addNewStructuredData = (value: string) => setSelectedStructuredData(value, "site");
281
282
 
282
283
  const addNewPage = () => {
283
- addTemplate(template);
284
- setCurrentPageID(null);
285
- setCurrentPageStatus("offline");
286
- setCurrentPageName("New Page");
287
- const path = "/sites/pages/editor/new";
288
- setHistoryPush(path, true);
284
+ const selectedTemplate = schemas?.templates[template];
285
+ const { singleInstance, displayName } = selectedTemplate;
286
+ const isAlreadyActive = currentSitePagesTemplatesIds.includes(template);
287
+
288
+ if (singleInstance && isAlreadyActive) {
289
+ setTemplateInstanceError({ error: true, templateName: displayName });
290
+ toggleNewModal();
291
+ } else {
292
+ setTemplateInstanceError({ error: false, templateName: "" });
293
+ addTemplate(template);
294
+ setCurrentPageID(null);
295
+ setCurrentPageStatus("offline");
296
+ setCurrentPageName("New Page");
297
+ const path = "/sites/pages/editor/new";
298
+ setHistoryPush(path, true);
299
+ }
289
300
  };
290
301
 
291
302
  const createContent = isData ? addNewData : addNewPage;
@@ -467,6 +478,10 @@ const Content = (props: IProps): JSX.Element => {
467
478
  currentSitePages &&
468
479
  currentSitePages.map((pageItem: IPage) => {
469
480
  const isItemSelected = isSelected(pageItem.id);
481
+ const selectedTemplate = schemas?.templates[pageItem.templateId];
482
+ const { singleInstance } = selectedTemplate;
483
+ const isAlreadyActive = currentSitePagesTemplatesIds.includes(template);
484
+ const isDuplicable = !singleInstance || (singleInstance && !isAlreadyActive);
470
485
 
471
486
  const item = {
472
487
  isSelected: isItemSelected,
@@ -474,6 +489,7 @@ const Content = (props: IProps): JSX.Element => {
474
489
  site: currentSiteInfo,
475
490
  lang,
476
491
  siteLanguages,
492
+ isDuplicable,
477
493
  };
478
494
 
479
495
  const deleteCurrentPage = async () => {
@@ -516,6 +532,7 @@ const Content = (props: IProps): JSX.Element => {
516
532
  languageActions: pageLanguageActions,
517
533
  deleteBulk: deleteCurrentPageBulk,
518
534
  getDataPack: getDataPack,
535
+ setTemplateInstanceError,
519
536
  };
520
537
 
521
538
  return (
@@ -589,7 +606,7 @@ const Content = (props: IProps): JSX.Element => {
589
606
  const selectedOptionType = selectedOptionObject.type.toUpperCase();
590
607
  const isOptionGlobal = selectedOptionType !== "STATIC" && isGlobalStructuredData(selectedOptionType);
591
608
  const isOptionImportable =
592
- isOptionGlobal && isStructuredDataFromPage(selectedOptionType) && selectedOptionObject.mode === "detail";
609
+ !isData && isOptionGlobal && isStructuredDataFromPage(selectedOptionType) && selectedOptionObject.mode === "detail";
593
610
 
594
611
  const handleImport = () => {
595
612
  toggleNewModal();
@@ -605,7 +622,7 @@ const Content = (props: IProps): JSX.Element => {
605
622
 
606
623
  const createContentAction = isOptionImportable
607
624
  ? { onClick: handleImport, title: "NEXT" }
608
- : { onClick: createContent, title: "ADD" };
625
+ : { onClick: createContent, title: "Create new" };
609
626
 
610
627
  const resetFilter = () => {
611
628
  resetFilterQuery();
@@ -648,6 +665,12 @@ const Content = (props: IProps): JSX.Element => {
648
665
  <ContentFilters current={filter} dynamicValues={structuredData} resetFilter={resetFilter} />
649
666
  <S.TableWrapper>
650
667
  <ErrorToast />
668
+ {templateInstanceError.error && (
669
+ <Notification
670
+ type="error"
671
+ text={`There can be only one ${templateInstanceError.templateName} page and you already have it.`}
672
+ />
673
+ )}
651
674
  <TableList
652
675
  tableHeader={Header}
653
676
  pagination={pagination}
@@ -665,7 +688,7 @@ const Content = (props: IProps): JSX.Element => {
665
688
  </TableList>
666
689
  </S.TableWrapper>
667
690
  </S.ContentListWrapper>
668
- <Modal isOpen={isNewOpen} hide={toggleNewModal} size="M" title="New content" mainAction={createContentAction}>
691
+ <Modal isOpen={isNewOpen} hide={toggleNewModal} size="M" title="New content">
669
692
  <OptionTable
670
693
  selectPage={addTemplate}
671
694
  selectData={addNewStructuredData}
@@ -673,6 +696,8 @@ const Content = (props: IProps): JSX.Element => {
673
696
  values={options.values}
674
697
  selectedValue={selectedOption}
675
698
  theme={currentSiteInfo.theme}
699
+ mainAction={createContentAction}
700
+ secondaryAction={{ title: "Cancel", onClick: toggleNewModal }}
676
701
  />
677
702
  </Modal>
678
703
  <Modal
@@ -745,7 +770,6 @@ interface IDispatchProps {
745
770
  importPageFromGlobal(pageID: number | number[]): Promise<boolean>;
746
771
  restorePage(id: number | number[]): Promise<boolean>;
747
772
  getDataPack: (id: string) => Promise<void>;
748
- setCurrentDataID(id: number | null): void;
749
773
  }
750
774
 
751
775
  const mapDispatchToProps = {
@@ -774,7 +798,6 @@ const mapDispatchToProps = {
774
798
  importPageFromGlobal: sitesActions.importPageFromGlobal,
775
799
  restorePage: pageEditorActions.restorePage,
776
800
  getDataPack: dataPacksActions.getSiteDataPack,
777
- setCurrentDataID: structuredDataActions.setCurrentDataID,
778
801
  };
779
802
 
780
803
  interface IPagesProps {
@@ -4,33 +4,44 @@ import { pageStatus } from "@ax/containers/PageEditor/interfaces";
4
4
  import { IPage } from "@ax/types";
5
5
 
6
6
  const getTemplatesFilters = (activatedDataPacks: any) => {
7
- const types: any[] = [];
7
+ const filters: any[] = [];
8
8
  const activatedDataPacksIds = getActivatedDataPacksIds(activatedDataPacks);
9
9
  const { templates } = schemas;
10
10
 
11
11
  Object.keys(templates).forEach((schema: any) => {
12
12
  const { type, dataPacks } = templates[schema];
13
13
  const isActivated = !dataPacks || activatedDataPacksIds.some((id: string) => dataPacks.indexOf(id) >= 0);
14
- isActivated && types.push(type);
15
- });
16
14
 
17
- const uniqueTypes = filterDuplicatedValues(types);
15
+ if (isActivated) {
16
+ !dataPacks
17
+ ? filters.push({ ...type })
18
+ : dataPacks.map((datapack: string) =>
19
+ filters.push({
20
+ label: datapack.toLowerCase(),
21
+ value: datapack.toLowerCase(),
22
+ type: type.value,
23
+ mode: type.mode,
24
+ })
25
+ );
26
+ }
27
+ });
18
28
 
19
- return uniqueTypes;
29
+ const uniqueFilters = filterDuplicatedValues(filters);
30
+ return uniqueFilters;
20
31
  };
21
32
 
22
33
  const mapStructuredOptions = (options: any[]) => {
23
34
  const pureOptions = options.filter((option: any) => !option.fromPage);
24
35
 
25
36
  return pureOptions.map((option: any) => {
26
- const { id, title } = option;
27
-
37
+ const { id, title, dataPacks } = option;
28
38
  return {
29
39
  name: id,
30
40
  value: id,
31
41
  title,
32
42
  type: id,
33
43
  isData: true,
44
+ dataPacks,
34
45
  };
35
46
  });
36
47
  };
@@ -41,7 +52,7 @@ const getOptionValues = (options: any) => {
41
52
 
42
53
  Object.keys(templates).forEach((schema: any) => {
43
54
  const currSchema = templates[schema];
44
- const { component, displayName } = currSchema;
55
+ const { component, displayName, dataPacks } = currSchema;
45
56
  const type = currSchema.type && currSchema.type.value && currSchema.type.value.toLowerCase();
46
57
  const mode = currSchema.type && currSchema.type.mode;
47
58
 
@@ -52,6 +63,7 @@ const getOptionValues = (options: any) => {
52
63
  type,
53
64
  value: component,
54
65
  mode,
66
+ dataPacks,
55
67
  });
56
68
  });
57
69
 
@@ -62,10 +74,13 @@ const getOptionFilters = (options: any, activatedDataPacks: any) => {
62
74
  const pureOptions = options.filter((option: any) => !option.fromPage);
63
75
  const templateFilters = getTemplatesFilters(activatedDataPacks);
64
76
 
65
- const mappedOptions = pureOptions.map((option: any) => ({
66
- label: option.title,
67
- value: option.id.toLowerCase(),
68
- }));
77
+ const mappedOptions = pureOptions.flatMap((option: any) =>
78
+ option.dataPacks.map((dataPack: string) => ({
79
+ label: dataPack.toLowerCase(),
80
+ value: dataPack.toLowerCase(),
81
+ isData: true,
82
+ }))
83
+ );
69
84
 
70
85
  const filters = [...templateFilters, ...mappedOptions];
71
86
  const uniqueFilters = [...new Map(filters.map((item: any) => [item.value, item])).values()];
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import { connect } from "react-redux";
3
3
  import { pageEditorActions } from "@ax/containers/PageEditor";
4
4
  import { ConfigPanel, ResizePanel } from "@ax/components";
5
- import { IBreadcrumbItem, IRootState, ISchema, IUserEditing } from "@ax/types";
5
+ import { IBreadcrumbItem, INotification, IRootState, ISchema, IUserEditing } from "@ax/types";
6
6
  import PageBrowser from "../PageBrowser";
7
7
 
8
8
  const Editor = (props: IProps) => {
@@ -26,7 +26,10 @@ const Editor = (props: IProps) => {
26
26
  isEditable,
27
27
  isReadOnly,
28
28
  userEditing,
29
+ copyModule,
30
+ pasteModule,
29
31
  theme,
32
+ setNotification,
30
33
  } = props;
31
34
 
32
35
  const actions = {
@@ -37,6 +40,9 @@ const Editor = (props: IProps) => {
37
40
  replaceModuleAction: replaceModule,
38
41
  duplicateModuleAction: duplicateModule,
39
42
  replaceElementsInCollectionAction: replaceElementsInCollection,
43
+ copyModuleAction: copyModule,
44
+ pasteModuleAction: pasteModule,
45
+ setNotificationAction: setNotification,
40
46
  };
41
47
 
42
48
  return (
@@ -86,6 +92,9 @@ interface IPageBrowserDispatchProps {
86
92
  moveElement(moduleID: number, selectedContent: any, isPush: boolean, key: string): void;
87
93
  replaceModule(module: any, parent: any, objKey: string): void;
88
94
  replaceElementsInCollection(newValue: string, reference: string): void;
95
+ copyModule(editorID: number): boolean;
96
+ pasteModule(editorID: number): Promise<{ error?: INotification }>;
97
+ setNotification: (notification: INotification) => void;
89
98
  isGlobal: boolean;
90
99
  isEditable: boolean;
91
100
  isReadOnly: boolean;
@@ -114,6 +123,8 @@ const mapDispatchToProps = {
114
123
  moveElement: pageEditorActions.moveElement,
115
124
  replaceModule: pageEditorActions.replaceModule,
116
125
  replaceElementsInCollection: pageEditorActions.replaceElementsInCollection,
126
+ copyModule: pageEditorActions.copyModule,
127
+ pasteModule: pageEditorActions.pasteModule,
117
128
  };
118
129
 
119
130
  export default connect(mapStateToProps, mapDispatchToProps)(Editor);