@griddo/ax 1.66.1 → 1.66.4
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/api/pages.tsx +15 -3
- package/src/api/redirects.tsx +4 -2
- package/src/api/sites.tsx +4 -2
- package/src/components/Browser/index.tsx +3 -1
- package/src/components/Browser/style.tsx +2 -2
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +0 -1
- package/src/components/ErrorCenter/index.tsx +8 -5
- package/src/components/ErrorCenter/style.tsx +21 -8
- package/src/components/Fields/ColorPicker/index.tsx +1 -0
- package/src/components/Fields/LinkField/index.tsx +85 -0
- package/src/components/Fields/ReferenceField/ItemList/index.tsx +5 -1
- package/src/components/Fields/ReferenceField/index.tsx +18 -14
- package/src/components/Fields/UrlField/index.tsx +13 -1
- package/src/components/Fields/index.tsx +2 -0
- package/src/components/FieldsBehavior/index.tsx +14 -1
- package/src/components/Icon/components/Copy.js +14 -0
- package/src/components/Icon/svgs/Copy2.svg +3 -0
- package/src/components/Image/index.tsx +17 -9
- package/src/components/Image/utils.ts +55 -0
- package/src/components/MainWrapper/AppBar/index.tsx +21 -10
- package/src/components/MainWrapper/AppBar/style.tsx +11 -3
- package/src/components/MainWrapper/index.tsx +2 -0
- package/src/components/Modal/style.tsx +0 -1
- package/src/components/SearchField/index.tsx +36 -4
- package/src/components/SearchField/style.tsx +23 -10
- package/src/components/SideModal/style.tsx +6 -6
- package/src/components/TableFilters/StatusFilter/index.tsx +2 -2
- package/src/components/index.tsx +2 -0
- package/src/containers/App/actions.tsx +3 -7
- package/src/containers/PageEditor/actions.tsx +91 -22
- package/src/containers/PageEditor/constants.tsx +1 -1
- package/src/containers/PageEditor/interfaces.tsx +6 -6
- package/src/containers/PageEditor/reducer.tsx +4 -4
- package/src/containers/PageEditor/utils.tsx +2 -1
- package/src/containers/Sites/actions.tsx +35 -23
- package/src/containers/Sites/constants.tsx +1 -0
- package/src/containers/Sites/interfaces.tsx +6 -0
- package/src/containers/Sites/reducer.tsx +4 -0
- package/src/forms/editor.tsx +34 -1
- package/src/forms/errors.tsx +1 -0
- package/src/forms/index.tsx +15 -1
- package/src/forms/validators.tsx +168 -9
- package/src/guards/error/index.tsx +1 -1
- package/src/helpers/dataPacks.tsx +8 -1
- package/src/helpers/index.tsx +2 -1
- package/src/modules/Content/PageItem/index.tsx +54 -4
- package/src/modules/Content/atoms.tsx +41 -3
- package/src/modules/Content/index.tsx +111 -64
- package/src/modules/Content/style.tsx +8 -1
- package/src/modules/GlobalEditor/Editor/index.tsx +3 -1
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +3 -0
- package/src/modules/GlobalEditor/index.tsx +8 -6
- package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +8 -0
- package/src/modules/PageEditor/Editor/index.tsx +6 -2
- package/src/modules/PageEditor/PageBrowser/index.tsx +3 -0
- package/src/modules/PageEditor/index.tsx +29 -15
- package/src/modules/Redirects/index.tsx +40 -10
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +1 -1
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +1 -1
- package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +1 -1
- package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +1 -1
- package/src/modules/Sites/index.tsx +3 -3
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +1 -1
- package/src/modules/StructuredData/StructuredDataList/atoms.tsx +1 -1
- package/src/modules/Users/Profile/index.tsx +3 -4
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +1 -1
- package/src/modules/Users/UserCreate/SiteItem/style.tsx +1 -1
- package/src/modules/Users/UserForm/style.tsx +3 -3
- package/src/modules/Users/UserList/UserItem/index.tsx +3 -1
- package/src/modules/Users/UserList/hooks.tsx +1 -1
- package/src/modules/Users/UserList/index.tsx +2 -2
- package/src/types/index.tsx +16 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Dispatch } from "redux";
|
|
2
2
|
import {
|
|
3
3
|
SET_SITES,
|
|
4
|
+
SET_SITES_BY_LANG,
|
|
4
5
|
SET_CURRENT_SITE_INFO,
|
|
5
6
|
SET_CURRENT_SITE_PAGES,
|
|
6
7
|
SET_FILTER,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
} from "./constants";
|
|
12
13
|
import {
|
|
13
14
|
ISetSitesAction,
|
|
15
|
+
ISetSitesByLangAction,
|
|
14
16
|
ISetCurrentSiteInfoAction,
|
|
15
17
|
ISetCurrentSitePagesAction,
|
|
16
18
|
ISetFilter,
|
|
@@ -42,6 +44,10 @@ function setSites(sitesList: ISite[]): ISetSitesAction {
|
|
|
42
44
|
return { type: SET_SITES, payload: { sites: sitesList } };
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
function setSitesByLang(sitesList: ISite[]): ISetSitesByLangAction {
|
|
48
|
+
return { type: SET_SITES_BY_LANG, payload: { sitesByLang: sitesList } };
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
function setCurrentSiteInfo(currentSiteInfo: ISite): ISetCurrentSiteInfoAction {
|
|
46
52
|
return { type: SET_CURRENT_SITE_INFO, payload: { currentSiteInfo } };
|
|
47
53
|
}
|
|
@@ -67,20 +73,34 @@ function setSavedSiteInfo(savedSiteInfo: ISite): ISetSavedSiteInfoAction {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
// TODO: hay que controlar que cuando da error la API borrar los sites ya guardados y sacar el error (ver los siguientes FIXME)
|
|
70
|
-
function getSites(
|
|
76
|
+
function getSites(): (dispatch: Dispatch) => Promise<void> {
|
|
71
77
|
return async (dispatch) => {
|
|
72
78
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
dispatch(setIsLoading(false));
|
|
79
|
+
const responseActions = {
|
|
80
|
+
handleSuccess: (data: any) => dispatch(setSites(data)),
|
|
81
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
82
|
+
};
|
|
83
|
+
const callback = async () => sites.getAllSites();
|
|
84
|
+
|
|
85
|
+
await handleRequest(callback, responseActions, [])(dispatch);
|
|
81
86
|
} catch (e) {
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
console.log(e);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getSitesByLang(language: number): (dispatch: Dispatch) => Promise<void> {
|
|
93
|
+
return async (dispatch) => {
|
|
94
|
+
try {
|
|
95
|
+
const responseActions = {
|
|
96
|
+
handleSuccess: (data: any) => dispatch(setSitesByLang(data)),
|
|
97
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
98
|
+
};
|
|
99
|
+
const callback = async () => sites.getAllSites(language);
|
|
100
|
+
|
|
101
|
+
await handleRequest(callback, responseActions, [])(dispatch);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.log(e);
|
|
84
104
|
}
|
|
85
105
|
};
|
|
86
106
|
}
|
|
@@ -306,10 +326,7 @@ function deleteSite(siteID: number): (dispatch: Dispatch, getState: any) => Prom
|
|
|
306
326
|
try {
|
|
307
327
|
const response = await sites.deleteSite(siteID);
|
|
308
328
|
if (isReqOk(response.status)) {
|
|
309
|
-
|
|
310
|
-
app: { token },
|
|
311
|
-
} = getState();
|
|
312
|
-
getSites(token)(dispatch);
|
|
329
|
+
getSites()(dispatch);
|
|
313
330
|
}
|
|
314
331
|
} catch (e) {
|
|
315
332
|
console.log(e); // TODO: capturar error bien
|
|
@@ -322,10 +339,7 @@ function publishSite(siteID: number): (dispatch: Dispatch, getState: any) => Pro
|
|
|
322
339
|
try {
|
|
323
340
|
const response = await sites.publishSite(siteID);
|
|
324
341
|
if (isReqOk(response.status)) {
|
|
325
|
-
|
|
326
|
-
app: { token },
|
|
327
|
-
} = getState();
|
|
328
|
-
getSites(token)(dispatch);
|
|
342
|
+
getSites()(dispatch);
|
|
329
343
|
}
|
|
330
344
|
} catch (e) {
|
|
331
345
|
console.log(e); // TODO: capturar error bien
|
|
@@ -338,10 +352,7 @@ function unpublishSite(siteID: number): (dispatch: Dispatch, getState: any) => P
|
|
|
338
352
|
try {
|
|
339
353
|
const response = await sites.unpublishSite(siteID);
|
|
340
354
|
if (isReqOk(response.status)) {
|
|
341
|
-
|
|
342
|
-
app: { token },
|
|
343
|
-
} = getState();
|
|
344
|
-
getSites(token)(dispatch);
|
|
355
|
+
getSites()(dispatch);
|
|
345
356
|
}
|
|
346
357
|
} catch (e) {
|
|
347
358
|
console.log(e); // TODO: capturar error bien
|
|
@@ -464,6 +475,7 @@ export {
|
|
|
464
475
|
setTotalItems,
|
|
465
476
|
setCurrentSiteLanguages,
|
|
466
477
|
getSites,
|
|
478
|
+
getSitesByLang,
|
|
467
479
|
setSiteInfo,
|
|
468
480
|
getFilteredContent,
|
|
469
481
|
getSitePages,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const NAME = "sites";
|
|
2
2
|
|
|
3
3
|
export const SET_SITES = `${NAME}/SET_SITES`;
|
|
4
|
+
export const SET_SITES_BY_LANG = `${NAME}/SET_SITES_BY_LANG`;
|
|
4
5
|
export const SET_CURRENT_SITE_INFO = `${NAME}/SET_CURRENT_SITE_INFO`;
|
|
5
6
|
export const SET_CURRENT_SITE_PAGES = `${NAME}/SET_CURRENT_SITE_PAGES`;
|
|
6
7
|
export const SET_FILTER: string | null = `${NAME}/SET_FILTER`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SET_SITES,
|
|
3
|
+
SET_SITES_BY_LANG,
|
|
3
4
|
SET_CURRENT_SITE_INFO,
|
|
4
5
|
SET_CURRENT_SITE_PAGES,
|
|
5
6
|
SET_FILTER,
|
|
@@ -20,6 +21,11 @@ export interface ISetSitesAction {
|
|
|
20
21
|
payload: { sites: ISite[] };
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
export interface ISetSitesByLangAction {
|
|
25
|
+
type: typeof SET_SITES_BY_LANG;
|
|
26
|
+
payload: { sitesByLang: ISite[] };
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
export interface ISetCurrentSiteInfoAction {
|
|
24
30
|
type: typeof SET_CURRENT_SITE_INFO;
|
|
25
31
|
payload: { currentSiteInfo: ISite };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SET_SITES,
|
|
3
|
+
SET_SITES_BY_LANG,
|
|
3
4
|
SET_CURRENT_SITE_INFO,
|
|
4
5
|
SET_CURRENT_SITE_PAGES,
|
|
5
6
|
SET_FILTER,
|
|
@@ -17,6 +18,7 @@ export interface ISitesState {
|
|
|
17
18
|
currentSiteName: string | null;
|
|
18
19
|
currentSitePages: IPage[];
|
|
19
20
|
sites: ISite[];
|
|
21
|
+
sitesByLang: ISite[];
|
|
20
22
|
currentSiteInfo: any;
|
|
21
23
|
currentFilter: string | null;
|
|
22
24
|
totalItems: number;
|
|
@@ -28,6 +30,7 @@ export const initialState = {
|
|
|
28
30
|
currentSiteName: null,
|
|
29
31
|
currentSitePages: [],
|
|
30
32
|
sites: [],
|
|
33
|
+
sitesByLang: [],
|
|
31
34
|
currentSiteInfo: null,
|
|
32
35
|
currentFilter: "unique-pages",
|
|
33
36
|
totalItems: 0,
|
|
@@ -39,6 +42,7 @@ export function reducer(state = initialState, action: SitesActionsCreators): ISi
|
|
|
39
42
|
switch (action.type) {
|
|
40
43
|
case SET_FILTER:
|
|
41
44
|
case SET_SITES:
|
|
45
|
+
case SET_SITES_BY_LANG:
|
|
42
46
|
case SET_CURRENT_SITE_INFO:
|
|
43
47
|
case SET_CURRENT_SITE_PAGES:
|
|
44
48
|
case SET_TOTAL_ITEMS:
|
package/src/forms/editor.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepClone } from "@ax/helpers";
|
|
1
|
+
import { deepClone, getSchema } from "@ax/helpers";
|
|
2
2
|
import { IPage, IBreadcrumbItem } from "@ax/types";
|
|
3
3
|
|
|
4
4
|
const configKeys = ["headerConfig", "footerConfig"];
|
|
@@ -164,6 +164,38 @@ const getParentKey = (parentModule: any, editorID: number) => {
|
|
|
164
164
|
return keyFound;
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
const checkMaxModules = (content: any, type: string): { isMaxModules: boolean; errorMessage?: string } => {
|
|
168
|
+
const { maxModulesPerPage } = getSchema(type);
|
|
169
|
+
const queue: any[] = [content];
|
|
170
|
+
let counter = 0;
|
|
171
|
+
|
|
172
|
+
while (queue.length > 0 && counter < maxModulesPerPage) {
|
|
173
|
+
const obj = queue.shift();
|
|
174
|
+
const currentObj = obj;
|
|
175
|
+
|
|
176
|
+
if (currentObj.component === type) {
|
|
177
|
+
counter++;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const keys = currentObj instanceof Object ? Object.keys(currentObj) : [];
|
|
181
|
+
|
|
182
|
+
for (const key of keys) {
|
|
183
|
+
const objVal = currentObj[key];
|
|
184
|
+
if (objVal instanceof Object) {
|
|
185
|
+
queue.push(objVal);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const isMaxModules = counter >= maxModulesPerPage;
|
|
191
|
+
const errorMessage = `There can be only ${maxModulesPerPage} ${type} on page. You already have it.`;
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
isMaxModules,
|
|
195
|
+
...(isMaxModules && { errorMessage }),
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
167
199
|
export {
|
|
168
200
|
parseData,
|
|
169
201
|
cleanContent,
|
|
@@ -177,4 +209,5 @@ export {
|
|
|
177
209
|
getLastModuleEditorID,
|
|
178
210
|
getLastComponentEditorID,
|
|
179
211
|
getParentKey,
|
|
212
|
+
checkMaxModules,
|
|
180
213
|
};
|
package/src/forms/errors.tsx
CHANGED
|
@@ -40,6 +40,7 @@ const ERRORS: Record<string, string> = {
|
|
|
40
40
|
ERR039: "Sorry, this color doesn't exist. Please add new one.",
|
|
41
41
|
ERR040: "Sorry, the file is not in a valid format.",
|
|
42
42
|
ERR041: "Sorry, the password doesn’t match.",
|
|
43
|
+
ERR042: "This content is part of disabled content type package. To publish it, you must first activate it."
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
export { ERRORS };
|
package/src/forms/index.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getLastModuleEditorID,
|
|
11
11
|
getLastComponentEditorID,
|
|
12
12
|
getParentKey,
|
|
13
|
+
checkMaxModules,
|
|
13
14
|
} from "./editor";
|
|
14
15
|
import {
|
|
15
16
|
getUpdatedComponents,
|
|
@@ -23,7 +24,15 @@ import {
|
|
|
23
24
|
replaceElements,
|
|
24
25
|
} from "./elements";
|
|
25
26
|
import { getInnerFields, getStructuredDataInnerFields } from "./fields";
|
|
26
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
getValidity,
|
|
29
|
+
findPackagesActivationErrors,
|
|
30
|
+
findFieldsErrors,
|
|
31
|
+
isTemplateActivated,
|
|
32
|
+
findMandatoryStructuredDataErrors,
|
|
33
|
+
checkH1content,
|
|
34
|
+
parseValidationErrors,
|
|
35
|
+
} from "./validators";
|
|
27
36
|
|
|
28
37
|
export {
|
|
29
38
|
parseData,
|
|
@@ -49,6 +58,11 @@ export {
|
|
|
49
58
|
getLastComponentEditorID,
|
|
50
59
|
getParentKey,
|
|
51
60
|
getValidity,
|
|
61
|
+
findPackagesActivationErrors,
|
|
52
62
|
findFieldsErrors,
|
|
63
|
+
isTemplateActivated,
|
|
53
64
|
findMandatoryStructuredDataErrors,
|
|
65
|
+
checkMaxModules,
|
|
66
|
+
checkH1content,
|
|
67
|
+
parseValidationErrors,
|
|
54
68
|
};
|
package/src/forms/validators.tsx
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
dateToString,
|
|
3
|
+
getDeactivatedModules,
|
|
4
|
+
getDefaultSchema,
|
|
5
|
+
getSchema,
|
|
6
|
+
getTemplate,
|
|
7
|
+
isComponentEmpty,
|
|
8
|
+
isEmptyContainer,
|
|
9
|
+
isModuleDisabled,
|
|
10
|
+
} from "@ax/helpers";
|
|
11
|
+
import { findByEditorID } from "@ax/forms";
|
|
12
|
+
import { IErrorItem, ITemplate } from "@ax/types";
|
|
3
13
|
import { ERRORS } from "./errors";
|
|
4
14
|
|
|
5
15
|
const VALIDATORS = {
|
|
@@ -96,6 +106,13 @@ const VALIDATORS = {
|
|
|
96
106
|
return { isValid: true, errorCode: "" };
|
|
97
107
|
}
|
|
98
108
|
},
|
|
109
|
+
isMockup: (val: any, field: { type: string; defaultValue: any }): IError => {
|
|
110
|
+
const isValid = !checkMockupByType(field.type, val, field.defaultValue);
|
|
111
|
+
return { isValid, errorCode: "ERR016" };
|
|
112
|
+
},
|
|
113
|
+
apiValidator: (val: any, code: string): IError => {
|
|
114
|
+
return { isValid: false, errorCode: code };
|
|
115
|
+
},
|
|
99
116
|
isSamePass: (pass1: string, pass2: string): IError => {
|
|
100
117
|
const isValid = pass1 === pass2;
|
|
101
118
|
return { isValid, errorCode: "ERR041" };
|
|
@@ -161,6 +178,23 @@ const isEmptyField = (value: any, fieldType: string, multiple: boolean) => {
|
|
|
161
178
|
}
|
|
162
179
|
};
|
|
163
180
|
|
|
181
|
+
const checkMockupByType = (type: string, value: any, defaultValue: any) => {
|
|
182
|
+
if (!value || !defaultValue) return false;
|
|
183
|
+
switch (type) {
|
|
184
|
+
case "HeadingField":
|
|
185
|
+
return value.content && defaultValue.content && value.content.trim() === defaultValue.content.trim();
|
|
186
|
+
case "ImageField":
|
|
187
|
+
return value.publicId === defaultValue.publicId;
|
|
188
|
+
default:
|
|
189
|
+
return value.trim() === defaultValue.trim();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const checkMockupContent = (component: string, key: string, type: string, value: any) => {
|
|
194
|
+
const moduleDefault = getDefaultSchema(component);
|
|
195
|
+
return { isMockup: checkMockupByType(type, value, moduleDefault[key]), defaultValue: moduleDefault[key] };
|
|
196
|
+
};
|
|
197
|
+
|
|
164
198
|
const getValidationErrors = (
|
|
165
199
|
fields: Record<string, unknown>[],
|
|
166
200
|
current: any,
|
|
@@ -176,8 +210,8 @@ const getValidationErrors = (
|
|
|
176
210
|
const isEmpty = isEmptyField(current[field.key], field.type, hasMultipleOptions);
|
|
177
211
|
if (isEmpty) {
|
|
178
212
|
errors.push({
|
|
179
|
-
type: "
|
|
180
|
-
message: "
|
|
213
|
+
type: "error",
|
|
214
|
+
message: getErrorMessage("ERR015", null),
|
|
181
215
|
validator: { mandatory: true },
|
|
182
216
|
editorID: current.editorID ? current.editorID : null,
|
|
183
217
|
component: current.component ? current.component : null,
|
|
@@ -189,14 +223,42 @@ const getValidationErrors = (
|
|
|
189
223
|
}
|
|
190
224
|
}
|
|
191
225
|
|
|
192
|
-
if (
|
|
193
|
-
const {
|
|
226
|
+
if (current.component && field.isMockup) {
|
|
227
|
+
const { isMockup, defaultValue } = checkMockupContent(
|
|
228
|
+
current.component,
|
|
229
|
+
field.key,
|
|
230
|
+
field.type,
|
|
231
|
+
current[field.key]
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (isMockup) {
|
|
235
|
+
errors.push({
|
|
236
|
+
type: "error",
|
|
237
|
+
message: getErrorMessage("ERR016", null),
|
|
238
|
+
validator: { isMockup: { type: field.type, defaultValue } },
|
|
239
|
+
editorID: current.editorID ? current.editorID : null,
|
|
240
|
+
component: current.component ? current.component : null,
|
|
241
|
+
name: name ? name : field.title,
|
|
242
|
+
key: field.key,
|
|
243
|
+
tab,
|
|
244
|
+
template,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let fieldValidators: Record<string, unknown> = field.maxValue ? { maxValue: field.maxValue } : {};
|
|
250
|
+
fieldValidators = field.minValue ? { ...fieldValidators, minValue: field.minValue } : fieldValidators;
|
|
251
|
+
|
|
252
|
+
if (Object.prototype.hasOwnProperty.call(field, "validators") || Object.keys(fieldValidators).length) {
|
|
253
|
+
const allValidators = { ...field.validators, ...fieldValidators };
|
|
254
|
+
|
|
255
|
+
const { isValid, errorText } = getValidity(allValidators, current[field.key]);
|
|
194
256
|
|
|
195
257
|
if (!isValid) {
|
|
196
258
|
errors.push({
|
|
197
|
-
type: "
|
|
259
|
+
type: "error",
|
|
198
260
|
message: errorText,
|
|
199
|
-
validator:
|
|
261
|
+
validator: allValidators,
|
|
200
262
|
editorID: current.editorID ? current.editorID : null,
|
|
201
263
|
component: current.component ? current.component : null,
|
|
202
264
|
name: name ? name : field.title,
|
|
@@ -221,6 +283,57 @@ const getValidationErrors = (
|
|
|
221
283
|
return errors;
|
|
222
284
|
};
|
|
223
285
|
|
|
286
|
+
const isTemplateActivated = (templates: ITemplate[], currentTemplateType: string): boolean =>
|
|
287
|
+
templates.find((temp: ITemplate) => temp.id === currentTemplateType) ? true : false;
|
|
288
|
+
|
|
289
|
+
const findPackagesActivationErrors = (
|
|
290
|
+
pageEditor: any,
|
|
291
|
+
modules: string[],
|
|
292
|
+
templates: ITemplate[]
|
|
293
|
+
): IErrorItem | null => {
|
|
294
|
+
const {
|
|
295
|
+
schema,
|
|
296
|
+
selectedContent: { component },
|
|
297
|
+
} = pageEditor;
|
|
298
|
+
|
|
299
|
+
let deactivatedModules: string[] = [];
|
|
300
|
+
let isCurrentTemplateActivated = true;
|
|
301
|
+
let hasDeactivatedModules = false;
|
|
302
|
+
|
|
303
|
+
const {
|
|
304
|
+
editorContent: { template },
|
|
305
|
+
} = pageEditor?.editorContent;
|
|
306
|
+
|
|
307
|
+
if (template) {
|
|
308
|
+
const mainContentModules = template?.mainContent?.modules;
|
|
309
|
+
|
|
310
|
+
if (mainContentModules) {
|
|
311
|
+
deactivatedModules = getDeactivatedModules(modules, mainContentModules);
|
|
312
|
+
hasDeactivatedModules = deactivatedModules.length > 0;
|
|
313
|
+
} else {
|
|
314
|
+
hasDeactivatedModules = isModuleDisabled(component, schema.schemaType, modules);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
isCurrentTemplateActivated = isTemplateActivated(templates, template.templateType);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!isCurrentTemplateActivated || hasDeactivatedModules) {
|
|
321
|
+
return {
|
|
322
|
+
type: "error",
|
|
323
|
+
message: getErrorMessage("ERR042", null),
|
|
324
|
+
validator: {},
|
|
325
|
+
editorID: null,
|
|
326
|
+
component: "",
|
|
327
|
+
name: "",
|
|
328
|
+
key: "",
|
|
329
|
+
tab: "",
|
|
330
|
+
template: false,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return null;
|
|
335
|
+
};
|
|
336
|
+
|
|
224
337
|
const findFieldsErrors = (content: any): IErrorItem[] => {
|
|
225
338
|
const queue: any[] = [content];
|
|
226
339
|
let errors: IErrorItem[] = [];
|
|
@@ -274,9 +387,55 @@ const findMandatoryStructuredDataErrors = (content: any, schema: any): IErrorIte
|
|
|
274
387
|
return errors;
|
|
275
388
|
};
|
|
276
389
|
|
|
390
|
+
const checkH1content = (content: any): IErrorItem | null => {
|
|
391
|
+
const h1s = content.getElementsByTagName("h1");
|
|
392
|
+
|
|
393
|
+
if (!h1s.length) {
|
|
394
|
+
return {
|
|
395
|
+
type: "warning",
|
|
396
|
+
message: getErrorMessage("ERR018", null),
|
|
397
|
+
validator: {},
|
|
398
|
+
editorID: null,
|
|
399
|
+
component: null,
|
|
400
|
+
name: "",
|
|
401
|
+
key: "",
|
|
402
|
+
tab: "",
|
|
403
|
+
template: false,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return null;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const parseValidationErrors = (errors: any[], content: any) => {
|
|
411
|
+
return errors.map((err: any) => {
|
|
412
|
+
const { element: module } = findByEditorID(content, err.editorID);
|
|
413
|
+
const schema = getSchema(module.component);
|
|
414
|
+
return {
|
|
415
|
+
type: "error",
|
|
416
|
+
message: getErrorMessage(err.error, null),
|
|
417
|
+
validator: { apiValidator: err.error },
|
|
418
|
+
editorID: err.editorID,
|
|
419
|
+
component: module.component,
|
|
420
|
+
name: schema.displayName,
|
|
421
|
+
key: err.key,
|
|
422
|
+
tab: "content",
|
|
423
|
+
template: false,
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
|
|
277
428
|
interface IError {
|
|
278
429
|
isValid: boolean;
|
|
279
430
|
errorCode: string;
|
|
280
431
|
}
|
|
281
432
|
|
|
282
|
-
export {
|
|
433
|
+
export {
|
|
434
|
+
getValidity,
|
|
435
|
+
isTemplateActivated,
|
|
436
|
+
findPackagesActivationErrors,
|
|
437
|
+
findFieldsErrors,
|
|
438
|
+
findMandatoryStructuredDataErrors,
|
|
439
|
+
checkH1content,
|
|
440
|
+
parseValidationErrors,
|
|
441
|
+
};
|
|
@@ -50,7 +50,7 @@ const ErrorGuard = (props: IProps) => {
|
|
|
50
50
|
return domNode && createPortal(Notifications, domNode);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
return code ? isBlocking ? <ErrorView code={code} text={text} /> : createErrorNotification() : null;
|
|
53
|
+
return code || text ? isBlocking ? <ErrorView code={code} text={text} /> : createErrorNotification() : null;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
const mapStateToProps = (state: IRootState) => {
|
|
@@ -8,4 +8,11 @@ const getActivatedDataPacksIds = (activatedDataPacks: any) => {
|
|
|
8
8
|
const isModuleDisabled = (selectedComponent: string, type: string, activatedModules: string[]): boolean =>
|
|
9
9
|
type === "module" && !activatedModules.includes(selectedComponent);
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const getDeactivatedModules = (modules: any, currentModules: any) => {
|
|
12
|
+
const deactivatedModules = currentModules
|
|
13
|
+
.map((module: any) => (isModuleDisabled(module.component, "module", modules) ? module.component : null))
|
|
14
|
+
.filter((module: string | null) => !!module);
|
|
15
|
+
return deactivatedModules;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { getActivatedDataPacksIds, isModuleDisabled, getDeactivatedModules };
|
package/src/helpers/index.tsx
CHANGED
|
@@ -91,7 +91,7 @@ import { imageResizeCropAndCompress, compressImage } from "./imageResize";
|
|
|
91
91
|
|
|
92
92
|
import { isEmptyArray, moveArrayElement } from "./arrays";
|
|
93
93
|
|
|
94
|
-
import { getActivatedDataPacksIds, isModuleDisabled } from "./dataPacks";
|
|
94
|
+
import { getActivatedDataPacksIds, isModuleDisabled, getDeactivatedModules } from "./dataPacks";
|
|
95
95
|
|
|
96
96
|
import { isDevelopment } from "./environment";
|
|
97
97
|
|
|
@@ -153,6 +153,7 @@ export {
|
|
|
153
153
|
handleRequest,
|
|
154
154
|
getActivatedDataPacksIds,
|
|
155
155
|
isModuleDisabled,
|
|
156
|
+
getDeactivatedModules,
|
|
156
157
|
getInitials,
|
|
157
158
|
getSchemaType,
|
|
158
159
|
getModuleCategories,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { memo, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { schemas } from "components";
|
|
3
4
|
import { useModal } from "@ax/hooks";
|
|
4
5
|
import { getHumanLastModifiedDate, getTemplateDisplayName, slugify } from "@ax/helpers";
|
|
5
|
-
import { IPage, ISite, ISavePageParams, ICheck, IColumn,
|
|
6
|
+
import { IPage, ISite, ISavePageParams, ICheck, IColumn, IDataPack, IPageLanguage } from "@ax/types";
|
|
6
7
|
import { pageStatus, ISetCurrentPageIDAction } from "@ax/containers/PageEditor/interfaces";
|
|
7
8
|
|
|
8
9
|
import {
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
CategoryCell,
|
|
19
20
|
} from "@ax/components";
|
|
20
21
|
|
|
21
|
-
import { DeleteModal } from "../atoms";
|
|
22
|
+
import { DeleteModal, CopyModal } from "../atoms";
|
|
22
23
|
|
|
23
24
|
import * as S from "./style";
|
|
24
25
|
|
|
@@ -34,6 +35,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
34
35
|
categoryColors,
|
|
35
36
|
addCategoryColors,
|
|
36
37
|
dataPacks,
|
|
38
|
+
sites,
|
|
37
39
|
} = props;
|
|
38
40
|
const { isSelected, siteLanguages, page, lang, isDuplicable } = item;
|
|
39
41
|
const {
|
|
@@ -47,9 +49,10 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
47
49
|
languageActions,
|
|
48
50
|
duplicatePage,
|
|
49
51
|
removePageFromSite,
|
|
50
|
-
deleteBulk,
|
|
51
52
|
getDataPack,
|
|
53
|
+
deleteBulk,
|
|
52
54
|
setTemplateInstanceError,
|
|
55
|
+
toggleCopiedToast,
|
|
53
56
|
} = functions;
|
|
54
57
|
const { locale } = lang;
|
|
55
58
|
const {
|
|
@@ -62,16 +65,22 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
62
65
|
templateId,
|
|
63
66
|
structuredDataContent,
|
|
64
67
|
} = page;
|
|
68
|
+
|
|
65
69
|
const displayName = getTemplateDisplayName(templateId);
|
|
66
70
|
|
|
67
71
|
const initValue = { title: "", slug: "" };
|
|
72
|
+
const [site, setSite] = useState(null);
|
|
68
73
|
const [modalState, setModalState] = useState(initValue);
|
|
69
74
|
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
70
75
|
const { isOpen, toggleModal } = useModal();
|
|
71
76
|
const { isOpen: isRemoveOpen, toggleModal: toggleRemoveModal } = useModal();
|
|
72
77
|
const { isOpen: isUnpublishOpen, toggleModal: toggleUnpublishModal } = useModal();
|
|
73
78
|
const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
|
|
79
|
+
const { isOpen: isCopyOpen, toggleModal: toggleCopyModal } = useModal();
|
|
80
|
+
|
|
81
|
+
const currentTemplateDataPacks = schemas.templates[templateId].dataPacks;
|
|
74
82
|
|
|
83
|
+
const isCopyable = !currentTemplateDataPacks;
|
|
75
84
|
const isGlobal = origin === "GLOBAL";
|
|
76
85
|
const isTranslated = pageLanguages.length > 1;
|
|
77
86
|
const activeColumns = Object.keys(columns).filter((col: string) => columns[col].show);
|
|
@@ -311,6 +320,13 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
311
320
|
action: toggleModal,
|
|
312
321
|
};
|
|
313
322
|
|
|
323
|
+
const copyOption = {
|
|
324
|
+
label: "Copy page in another site",
|
|
325
|
+
icon: "copy",
|
|
326
|
+
action: toggleCopyModal,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
if (isCopyable) menuOptions.unshift(copyOption);
|
|
314
330
|
if (!isGlobal) menuOptions.unshift(duplicateOption);
|
|
315
331
|
|
|
316
332
|
const getPublishItem = (status: string, canBeUnpublished: boolean) => {
|
|
@@ -403,6 +419,25 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
403
419
|
};
|
|
404
420
|
|
|
405
421
|
const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
|
|
422
|
+
const copyToOtherSite = () => {
|
|
423
|
+
if (site) {
|
|
424
|
+
const siteID = parseInt(site);
|
|
425
|
+
|
|
426
|
+
duplicatePage(page.id, null, siteID).then((successEvent: boolean) => {
|
|
427
|
+
if (successEvent) {
|
|
428
|
+
toggleCopiedToast();
|
|
429
|
+
}
|
|
430
|
+
toggleCopyModal();
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const secondaryCopyModalAction = {
|
|
436
|
+
title: "Cancel",
|
|
437
|
+
onClick: toggleCopyModal,
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const mainCopyModalAction = { title: "Copy page", onClick: copyToOtherSite, disabled: !site };
|
|
406
441
|
|
|
407
442
|
const CategoryColumns =
|
|
408
443
|
isGlobal &&
|
|
@@ -471,6 +506,18 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
471
506
|
<S.StyledActionMenu icon="more" options={menuOptions} tooltip="Page actions" />
|
|
472
507
|
</S.ActionsCell>
|
|
473
508
|
</S.PageRow>
|
|
509
|
+
<CopyModal
|
|
510
|
+
isOpen={isCopyOpen}
|
|
511
|
+
toggleModal={() => {
|
|
512
|
+
setSite(null);
|
|
513
|
+
toggleCopyModal();
|
|
514
|
+
}}
|
|
515
|
+
mainModalAction={mainCopyModalAction}
|
|
516
|
+
secondaryModalAction={secondaryCopyModalAction}
|
|
517
|
+
sites={sites}
|
|
518
|
+
site={site}
|
|
519
|
+
setSite={setSite}
|
|
520
|
+
/>
|
|
474
521
|
<Modal
|
|
475
522
|
isOpen={isOpen}
|
|
476
523
|
hide={toggleModal}
|
|
@@ -534,6 +581,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
534
581
|
</S.ModalContent>
|
|
535
582
|
)}
|
|
536
583
|
</Modal>
|
|
584
|
+
|
|
537
585
|
<DeleteModal
|
|
538
586
|
isOpen={isDeleteOpen}
|
|
539
587
|
toggleModal={toggleDeleteModal}
|
|
@@ -576,11 +624,12 @@ interface IPageItemProps {
|
|
|
576
624
|
setHistoryPush(path: string, isEditor: boolean): void;
|
|
577
625
|
updatePageStatus(ids: number[], status: string, updatedFromList: boolean): Promise<boolean>;
|
|
578
626
|
setCurrentPageID(currentPageID: number | null): ISetCurrentPageIDAction;
|
|
579
|
-
duplicatePage(pageID: number, data: any): Promise<
|
|
627
|
+
duplicatePage(pageID: number, data: any, siteID?: number): Promise<boolean>;
|
|
580
628
|
removePageFromSite(pageID: number): Promise<boolean>;
|
|
581
629
|
deleteBulk(ids: number[]): void;
|
|
582
630
|
setTemplateInstanceError(error: any): void;
|
|
583
631
|
getDataPack: (id: string) => Promise<void>;
|
|
632
|
+
toggleCopiedToast(): void;
|
|
584
633
|
};
|
|
585
634
|
activatedTemplates: any[];
|
|
586
635
|
toggleToast(): void;
|
|
@@ -590,6 +639,7 @@ interface IPageItemProps {
|
|
|
590
639
|
categoryColors: any;
|
|
591
640
|
addCategoryColors(cats: string[]): void;
|
|
592
641
|
dataPacks: IDataPack[];
|
|
642
|
+
sites: ISite[];
|
|
593
643
|
}
|
|
594
644
|
|
|
595
645
|
export default memo(PageItem);
|