@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.
- package/package.json +2 -2
- package/src/GlobalStore.tsx +4 -3
- package/src/api/structuredData.tsx +15 -1
- package/src/components/Browser/index.tsx +13 -16
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +12 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +11 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +13 -0
- package/src/components/ConfigPanel/Form/index.tsx +3 -1
- package/src/components/ConfigPanel/index.tsx +19 -5
- package/src/components/ConfigPanel/style.tsx +5 -0
- package/src/components/FieldContainer/index.tsx +4 -0
- package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx +49 -0
- package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +51 -11
- package/src/components/Fields/ComponentArray/MixableComponentArray/style.tsx +14 -1
- package/src/components/Fields/ComponentContainer/index.tsx +24 -5
- package/src/components/Fields/ImageField/index.tsx +18 -5
- package/src/components/Image/index.tsx +25 -0
- package/src/components/Notification/index.tsx +3 -1
- package/src/components/index.tsx +2 -0
- package/src/containers/Navigation/Defaults/actions.tsx +27 -9
- package/src/containers/PageEditor/actions.tsx +104 -5
- package/src/containers/PageEditor/constants.tsx +2 -0
- package/src/containers/PageEditor/interfaces.tsx +12 -0
- package/src/containers/PageEditor/reducer.tsx +8 -0
- package/src/containers/PageEditor/utils.tsx +2 -2
- package/src/helpers/index.tsx +6 -0
- package/src/helpers/schemas.tsx +36 -7
- package/src/modules/App/Routing/index.tsx +1 -1
- package/src/modules/Content/OptionTable/index.tsx +44 -43
- package/src/modules/Content/OptionTable/store.tsx +1 -1
- package/src/modules/Content/OptionTable/style.tsx +27 -12
- package/src/modules/Content/PageItem/index.tsx +14 -4
- package/src/modules/Content/atoms.tsx +19 -2
- package/src/modules/Content/index.tsx +37 -14
- package/src/modules/Content/utils.tsx +27 -12
- package/src/modules/GlobalEditor/Editor/index.tsx +12 -1
- package/src/modules/GlobalEditor/index.tsx +20 -2
- package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +13 -0
- package/src/modules/Navigation/Defaults/atoms.tsx +28 -0
- package/src/modules/Navigation/Defaults/index.tsx +30 -4
- package/src/modules/Navigation/Defaults/style.tsx +32 -1
- package/src/modules/PageEditor/Editor/index.tsx +16 -1
- package/src/modules/PageEditor/index.tsx +14 -1
- package/src/modules/PublicPreview/index.tsx +15 -18
- package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/index.tsx +35 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/style.tsx +22 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/index.tsx +111 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/style.tsx +64 -0
- package/src/modules/Settings/Globals/NavigationModules/index.tsx +89 -0
- package/src/modules/Settings/Globals/NavigationModules/style.tsx +36 -0
- package/src/modules/Settings/Globals/index.tsx +38 -1
- package/src/modules/Sites/SitesList/SiteItem/index.tsx +7 -5
- package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +14 -3
- package/src/modules/StructuredData/StructuredDataList/OptionTable/style.tsx +11 -2
- package/src/modules/StructuredData/StructuredDataList/atoms.tsx +19 -2
- package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -13
- 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
|
-
|
|
24
|
-
|
|
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
|
|
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 =
|
|
61
|
-
selectOption(firstOption,
|
|
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(
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
174
|
+
export default connect(null, mapDispatchToProps)(OptionTable);
|
|
@@ -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}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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: "
|
|
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"
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
66
|
-
|
|
67
|
-
|
|
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);
|