@griddo/ax 10.4.32 → 10.4.34
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/__tests__/components/ConfigPanel/Form/ConnectedField/ConnectedField.test.tsx +1 -0
- package/src/__tests__/components/ConfigPanel/Form/ConnectedField/NavConnectedField/NavConnectedField.test.tsx +1 -0
- package/src/__tests__/components/ConfigPanel/Form/ConnectedField/PageConnectedField/PageConnectedField.test.tsx +5 -0
- package/src/__tests__/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/TemplateManager.test.tsx +5 -0
- package/src/__tests__/components/ConfigPanel/Form/Form.test.tsx +1 -0
- package/src/__tests__/components/ConfigPanel/GlobalPageForm/GlobalPageForm.test.tsx +1 -0
- package/src/__tests__/components/ConfigPanel/Header/Header.test.tsx +6 -0
- package/src/components/ConfigPanel/Form/ConnectedField/NavConnectedField/index.tsx +6 -2
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +10 -7
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +24 -8
- package/src/components/ErrorToast/index.tsx +2 -2
- package/src/containers/Settings/DataPacks/actions.tsx +9 -8
- package/src/containers/Sites/actions.tsx +17 -2
- package/src/containers/Sites/constants.tsx +1 -0
- package/src/containers/Sites/interfaces.tsx +7 -1
- package/src/containers/Sites/reducer.tsx +5 -1
- package/src/helpers/index.tsx +13 -1
- package/src/helpers/themes.tsx +82 -3
- package/src/modules/Content/OptionTable/index.tsx +45 -36
- package/src/modules/Content/OptionTable/store.tsx +4 -3
- package/src/modules/Settings/Globals/index.tsx +37 -4
- package/src/modules/Settings/Globals/style.tsx +5 -1
- package/src/types/index.tsx +23 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "10.4.
|
|
4
|
+
"version": "10.4.34",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -232,5 +232,5 @@
|
|
|
232
232
|
"publishConfig": {
|
|
233
233
|
"access": "public"
|
|
234
234
|
},
|
|
235
|
-
"gitHead": "
|
|
235
|
+
"gitHead": "21a939f48088c711230a887664d83565dd7caa92"
|
|
236
236
|
}
|
|
@@ -52,6 +52,7 @@ const initialStore = {
|
|
|
52
52
|
editorID: 1,
|
|
53
53
|
},
|
|
54
54
|
],
|
|
55
|
+
themeElements: null,
|
|
55
56
|
},
|
|
56
57
|
app: {
|
|
57
58
|
lang: { locale: "es-ES", id: 0 },
|
|
@@ -201,6 +202,7 @@ describe("PageConnectedField component rendering", () => {
|
|
|
201
202
|
editorID: 1,
|
|
202
203
|
},
|
|
203
204
|
],
|
|
205
|
+
themeElements: null,
|
|
204
206
|
},
|
|
205
207
|
app: {
|
|
206
208
|
lang: { locale: "es-ES", id: 0 },
|
|
@@ -254,6 +256,7 @@ describe("PageConnectedField component rendering", () => {
|
|
|
254
256
|
editorID: 1,
|
|
255
257
|
},
|
|
256
258
|
],
|
|
259
|
+
themeElements: null,
|
|
257
260
|
},
|
|
258
261
|
app: {
|
|
259
262
|
lang: { locale: "es-ES", id: 0 },
|
|
@@ -307,6 +310,7 @@ describe("PageConnectedField component rendering", () => {
|
|
|
307
310
|
editorID: 1,
|
|
308
311
|
},
|
|
309
312
|
],
|
|
313
|
+
themeElements: null,
|
|
310
314
|
},
|
|
311
315
|
app: {
|
|
312
316
|
lang: { locale: "es-ES", id: 0 },
|
|
@@ -362,6 +366,7 @@ describe("PageConnectedField component rendering", () => {
|
|
|
362
366
|
editorID: 1,
|
|
363
367
|
},
|
|
364
368
|
],
|
|
369
|
+
themeElements: null,
|
|
365
370
|
},
|
|
366
371
|
app: {
|
|
367
372
|
lang: { locale: "es-ES", id: 0 },
|
|
@@ -72,6 +72,8 @@ const defaultProps = {
|
|
|
72
72
|
theme: "griddo-alt-theme",
|
|
73
73
|
moduleCopy: null,
|
|
74
74
|
availableDataPacks: [],
|
|
75
|
+
activatedModules: [],
|
|
76
|
+
lang: 4,
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
const initialStore = {
|
|
@@ -112,6 +114,9 @@ const initialStore = {
|
|
|
112
114
|
available: [],
|
|
113
115
|
modules: [],
|
|
114
116
|
},
|
|
117
|
+
sites: {
|
|
118
|
+
themeElements: null,
|
|
119
|
+
},
|
|
115
120
|
};
|
|
116
121
|
|
|
117
122
|
const store = mockStore(initialStore);
|
|
@@ -10,10 +10,16 @@ import { parseTheme } from "@ax/helpers";
|
|
|
10
10
|
import Header, { IHeaderProps } from "@ax/components/ConfigPanel/Header";
|
|
11
11
|
import globalTheme from "@ax/themes/theme.json";
|
|
12
12
|
|
|
13
|
+
window.scrollTo = jest.fn();
|
|
14
|
+
|
|
13
15
|
beforeEach(() => {
|
|
14
16
|
cleanup();
|
|
15
17
|
});
|
|
16
18
|
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
17
23
|
const middlewares: any = [];
|
|
18
24
|
const mockStore = configureStore(middlewares);
|
|
19
25
|
const updateEditorContentMock = jest.fn();
|
|
@@ -5,7 +5,7 @@ import { FieldContainer } from "@ax/components";
|
|
|
5
5
|
import { navigationActions } from "@ax/containers/Navigation";
|
|
6
6
|
import { getInnerFields } from "@ax/forms";
|
|
7
7
|
import { IRootState } from "@ax/types";
|
|
8
|
-
import { areEqual } from "@ax/helpers";
|
|
8
|
+
import { areEqual, filterThemeElements } from "@ax/helpers";
|
|
9
9
|
|
|
10
10
|
const NavConnectedField = (props: any) => {
|
|
11
11
|
const {
|
|
@@ -26,6 +26,7 @@ const NavConnectedField = (props: any) => {
|
|
|
26
26
|
theme,
|
|
27
27
|
disabled,
|
|
28
28
|
moduleCopy,
|
|
29
|
+
themeElements,
|
|
29
30
|
} = props;
|
|
30
31
|
|
|
31
32
|
const updateValue = (key: string, value: any) => {
|
|
@@ -68,9 +69,11 @@ const NavConnectedField = (props: any) => {
|
|
|
68
69
|
);
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
const filteredWhiteList = whiteList ? filterThemeElements(themeElements, whiteList, "modules") : whiteList;
|
|
73
|
+
|
|
71
74
|
return (
|
|
72
75
|
<FieldContainer
|
|
73
|
-
whiteList={
|
|
76
|
+
whiteList={filteredWhiteList}
|
|
74
77
|
key={objKey}
|
|
75
78
|
objKey={objKey}
|
|
76
79
|
field={field}
|
|
@@ -99,6 +102,7 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
99
102
|
activatedTemplates: state.dataPacks.templates,
|
|
100
103
|
menus: state.menu.savedMenus,
|
|
101
104
|
moduleCopy: state.navigation.moduleCopy,
|
|
105
|
+
themeElements: state.sites.themeElements,
|
|
102
106
|
});
|
|
103
107
|
|
|
104
108
|
const mapDispatchToProps = {
|
package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { IDataPack, IErrorItem, IRootState, ISchemaField, ISite } from "@ax/types";
|
|
5
|
-
import { getModuleCategories } from "@ax/helpers";
|
|
4
|
+
import { IDataPack, IErrorItem, IRootState, ISchemaField, ISite, IThemeElements } from "@ax/types";
|
|
5
|
+
import { filterThemeElements, getModuleCategories } from "@ax/helpers";
|
|
6
6
|
import Field from "../Field";
|
|
7
7
|
|
|
8
8
|
import * as S from "./style";
|
|
@@ -29,6 +29,7 @@ export const TemplateManager = (props: IProps): JSX.Element => {
|
|
|
29
29
|
availableDataPacks,
|
|
30
30
|
setHistoryPush,
|
|
31
31
|
lang,
|
|
32
|
+
themeElements,
|
|
32
33
|
} = props;
|
|
33
34
|
|
|
34
35
|
const isConfig = selectedTab === "config";
|
|
@@ -60,10 +61,11 @@ export const TemplateManager = (props: IProps): JSX.Element => {
|
|
|
60
61
|
}, []);
|
|
61
62
|
|
|
62
63
|
const mappedWhiteList: string[] = whiteList ? [...whiteList, ...addedModules].sort() : [...addedModules.sort()];
|
|
63
|
-
const
|
|
64
|
+
const filteredWhiteList = filterThemeElements(themeElements, mappedWhiteList, "modules");
|
|
65
|
+
const categories = getModuleCategories(filteredWhiteList);
|
|
64
66
|
|
|
65
67
|
return {
|
|
66
|
-
whiteList:
|
|
68
|
+
whiteList: filteredWhiteList,
|
|
67
69
|
categories,
|
|
68
70
|
key,
|
|
69
71
|
fieldObjKey,
|
|
@@ -93,7 +95,7 @@ export const TemplateManager = (props: IProps): JSX.Element => {
|
|
|
93
95
|
handleUpdate,
|
|
94
96
|
error,
|
|
95
97
|
readonly,
|
|
96
|
-
disabledField
|
|
98
|
+
disabledField,
|
|
97
99
|
} = getFieldProps(templateField);
|
|
98
100
|
|
|
99
101
|
return (
|
|
@@ -141,7 +143,7 @@ interface IProps {
|
|
|
141
143
|
site?: ISite;
|
|
142
144
|
activatedPacks: IDataPack[];
|
|
143
145
|
disabled?: boolean;
|
|
144
|
-
activatedModules
|
|
146
|
+
activatedModules: string[];
|
|
145
147
|
isTemplateActivated: boolean;
|
|
146
148
|
deleteError(error: IErrorItem): void;
|
|
147
149
|
errors: IErrorItem[];
|
|
@@ -150,12 +152,13 @@ interface IProps {
|
|
|
150
152
|
availableDataPacks: Record<string, any>[];
|
|
151
153
|
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
152
154
|
lang: number;
|
|
155
|
+
themeElements: IThemeElements | null;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
const mapStateToProps = (state: IRootState) => ({
|
|
156
159
|
activatedPacks: state.dataPacks.activated,
|
|
157
|
-
activatedModules: state.dataPacks.modules,
|
|
158
160
|
availableDataPacks: state.dataPacks.available,
|
|
161
|
+
themeElements: state.sites.themeElements,
|
|
159
162
|
});
|
|
160
163
|
|
|
161
164
|
export default connect(mapStateToProps)(TemplateManager);
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import React, { memo, useEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getTemplate,
|
|
6
|
+
isModuleDisabled,
|
|
7
|
+
slugify,
|
|
8
|
+
areEqual,
|
|
9
|
+
filterThemeElements,
|
|
10
|
+
isTemplateExcludedFromTheme,
|
|
11
|
+
} from "@ax/helpers";
|
|
5
12
|
import { IRootState } from "@ax/types";
|
|
6
13
|
import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
7
14
|
|
|
@@ -39,6 +46,7 @@ const PageConnectedField = (props: any) => {
|
|
|
39
46
|
setHistoryPush,
|
|
40
47
|
languages,
|
|
41
48
|
isReadOnly,
|
|
49
|
+
themeElements,
|
|
42
50
|
} = props;
|
|
43
51
|
|
|
44
52
|
const isTemplate = field.type === "template";
|
|
@@ -51,9 +59,11 @@ const PageConnectedField = (props: any) => {
|
|
|
51
59
|
|
|
52
60
|
let isTemplateActivated = true;
|
|
53
61
|
if (selectedContent.template && !isGlobal) {
|
|
54
|
-
isTemplateActivated =
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
isTemplateActivated =
|
|
63
|
+
activatedTemplates.find((temp: any) => temp.id === selectedContent.template.templateType) &&
|
|
64
|
+
!isTemplateExcludedFromTheme(themeElements, selectedContent.template.templateType)
|
|
65
|
+
? true
|
|
66
|
+
: false;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
const parentIsReadOnly =
|
|
@@ -72,10 +82,14 @@ const PageConnectedField = (props: any) => {
|
|
|
72
82
|
|
|
73
83
|
const isFieldReadOnly = (["parent", "slug"].includes(objKey) && isPageHome) || parentIsReadOnly || field.readonly;
|
|
74
84
|
|
|
85
|
+
const filteredActivatedModules = filterThemeElements(themeElements, activatedModules, "modules");
|
|
86
|
+
const filteredWhiteList = whiteList ? filterThemeElements(themeElements, whiteList, "modules") : whiteList;
|
|
87
|
+
|
|
75
88
|
const isDisabled =
|
|
76
89
|
(!isGlobal &&
|
|
77
|
-
(isModuleDisabled(selectedContent.component, componentType,
|
|
78
|
-
isFieldReadOnly ||
|
|
90
|
+
(isModuleDisabled(selectedContent.component, componentType, filteredActivatedModules) || !isTemplateActivated)) ||
|
|
91
|
+
isFieldReadOnly ||
|
|
92
|
+
field.disabled;
|
|
79
93
|
|
|
80
94
|
const isMetaTitleModified = selectedContent.metaTitle && selectedContent.metaTitle !== selectedContent.title;
|
|
81
95
|
|
|
@@ -160,13 +174,14 @@ const PageConnectedField = (props: any) => {
|
|
|
160
174
|
moduleCopy={moduleCopy}
|
|
161
175
|
setHistoryPush={setHistoryPush}
|
|
162
176
|
lang={lang.id}
|
|
177
|
+
activatedModules={filteredActivatedModules}
|
|
163
178
|
/>
|
|
164
179
|
);
|
|
165
180
|
}
|
|
166
181
|
|
|
167
182
|
return (
|
|
168
183
|
<Field
|
|
169
|
-
whiteList={
|
|
184
|
+
whiteList={filteredWhiteList}
|
|
170
185
|
objKey={objKey}
|
|
171
186
|
field={field}
|
|
172
187
|
selectedContent={selectedContent}
|
|
@@ -178,7 +193,7 @@ const PageConnectedField = (props: any) => {
|
|
|
178
193
|
lang={lang.id}
|
|
179
194
|
disabled={isDisabled}
|
|
180
195
|
readonly={isFieldReadOnly}
|
|
181
|
-
activatedModules={
|
|
196
|
+
activatedModules={filteredActivatedModules}
|
|
182
197
|
isTemplateActivated={isTemplateActivated}
|
|
183
198
|
error={error}
|
|
184
199
|
deleteError={deleteError}
|
|
@@ -207,6 +222,7 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
207
222
|
availableDataPacks: state.dataPacks.available,
|
|
208
223
|
template: state.pageEditor.template,
|
|
209
224
|
languages: state.sites.currentSiteLanguages,
|
|
225
|
+
themeElements: state.sites.themeElements,
|
|
210
226
|
});
|
|
211
227
|
|
|
212
228
|
const mapDispatchToProps = {
|
|
@@ -2,8 +2,8 @@ import React, { forwardRef } from "react";
|
|
|
2
2
|
|
|
3
3
|
import * as S from "./style";
|
|
4
4
|
|
|
5
|
-
const ErrorToast = (props: IProps) => {
|
|
6
|
-
return <S.ErrorWrapper id="error" data-testid="error-wrapper" {...props} />;
|
|
5
|
+
const ErrorToast = (props: IProps, ref: any) => {
|
|
6
|
+
return <S.ErrorWrapper id="error" data-testid="error-wrapper" {...props} ref={ref} />;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export interface IProps {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dispatch } from "redux";
|
|
2
2
|
|
|
3
|
-
import { handleRequest, sortBy } from "@ax/helpers";
|
|
3
|
+
import { filterThemeDatapacks, handleRequest, sortBy } from "@ax/helpers";
|
|
4
4
|
import { dataPack } from "@ax/api";
|
|
5
5
|
import { appActions } from "@ax/containers/App";
|
|
6
6
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
ISetModules,
|
|
26
26
|
ISetTemplates,
|
|
27
27
|
} from "./interfaces";
|
|
28
|
-
import { IDataPack, IDataPackCategory } from "@ax/types";
|
|
28
|
+
import { IDataPack, IDataPackCategory, IRootState } from "@ax/types";
|
|
29
29
|
import { dataPacksInitialState } from "./reducer";
|
|
30
30
|
|
|
31
31
|
function setActivated(activated: any): ISetActivated {
|
|
@@ -258,19 +258,20 @@ function resetForm(): (dispatch: Dispatch) => void {
|
|
|
258
258
|
|
|
259
259
|
function getAvailableSiteDataPacks(
|
|
260
260
|
queryParams: string | null,
|
|
261
|
-
loading = true
|
|
261
|
+
loading = true
|
|
262
262
|
): (dispatch: Dispatch, getState: any) => Promise<void> {
|
|
263
263
|
return async (dispatch, getState) => {
|
|
264
264
|
try {
|
|
265
265
|
const {
|
|
266
|
-
sites: { currentSiteInfo },
|
|
267
|
-
} = getState();
|
|
268
|
-
|
|
266
|
+
sites: { currentSiteInfo, themeElements },
|
|
267
|
+
}: IRootState = getState();
|
|
268
|
+
|
|
269
269
|
const query = queryParams ? `?status=deactivated${queryParams}` : `?status=deactivated`;
|
|
270
270
|
|
|
271
271
|
const handleSuccess = (data: any) => {
|
|
272
272
|
const { items } = data;
|
|
273
|
-
|
|
273
|
+
const filteredDatapacks = filterThemeDatapacks(themeElements, items);
|
|
274
|
+
dispatch(setAvailable(filteredDatapacks));
|
|
274
275
|
};
|
|
275
276
|
|
|
276
277
|
const responseActions = {
|
|
@@ -278,7 +279,7 @@ function getAvailableSiteDataPacks(
|
|
|
278
279
|
handleError: () => console.log("Error en getAvailableSiteDataPacks"),
|
|
279
280
|
};
|
|
280
281
|
|
|
281
|
-
const callback = async () => dataPack.getSiteDataPack(
|
|
282
|
+
const callback = async () => currentSiteInfo && dataPack.getSiteDataPack(currentSiteInfo.id, query);
|
|
282
283
|
|
|
283
284
|
const setLoading = loading ? [appActions.setIsLoading] : [];
|
|
284
285
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
SET_CONTENT_FILTERS,
|
|
16
16
|
SET_CONFIG,
|
|
17
17
|
SET_CURRENT_SEARCH,
|
|
18
|
+
SET_THEME_ELEMENTS,
|
|
18
19
|
} from "./constants";
|
|
19
20
|
|
|
20
21
|
import {
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
ISetSitesTotalItems,
|
|
33
34
|
ISetConfig,
|
|
34
35
|
ISetCurrentSearch,
|
|
36
|
+
ISetThemeElements,
|
|
35
37
|
} from "./interfaces";
|
|
36
38
|
|
|
37
39
|
import {
|
|
@@ -43,6 +45,7 @@ import {
|
|
|
43
45
|
IGetSitesParams,
|
|
44
46
|
ISiteListConfig,
|
|
45
47
|
IQueryValue,
|
|
48
|
+
IThemeElements,
|
|
46
49
|
} from "@ax/types";
|
|
47
50
|
import { sites, languages, dataPack, social, structuredData, analytics, pages } from "@ax/api";
|
|
48
51
|
import { appActions } from "@ax/containers/App";
|
|
@@ -53,7 +56,7 @@ import { analyticsActions } from "@ax/containers/Analytics";
|
|
|
53
56
|
import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
|
|
54
57
|
import { socialActions } from "@ax/containers/Settings/Social";
|
|
55
58
|
import { integrationsActions } from "@ax/containers/Integrations";
|
|
56
|
-
import { getDefaultTheme, handleRequest, isReqOk, sortBy } from "@ax/helpers";
|
|
59
|
+
import { getDefaultTheme, getThemeElements, handleRequest, isReqOk, sortBy } from "@ax/helpers";
|
|
57
60
|
import { usersActions } from "@ax/containers/Users";
|
|
58
61
|
|
|
59
62
|
const { setIsLoading, setIsSaving, setLanguage } = appActions;
|
|
@@ -119,6 +122,10 @@ function setCurrentSearch(currentSearch: string): ISetCurrentSearch {
|
|
|
119
122
|
return { type: SET_CURRENT_SEARCH, payload: { currentSearch } };
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
function setThemeElements(themeElements: IThemeElements | null): ISetThemeElements {
|
|
126
|
+
return { type: SET_THEME_ELEMENTS, payload: { themeElements } };
|
|
127
|
+
}
|
|
128
|
+
|
|
122
129
|
// TODO: hay que controlar que cuando da error la API borrar los sites ya guardados y sacar el error (ver los siguientes FIXME)
|
|
123
130
|
function getSites(params: IGetSitesParams = { recentSitesNumber: 7 }): (dispatch: Dispatch) => Promise<void> {
|
|
124
131
|
return async (dispatch) => {
|
|
@@ -192,7 +199,11 @@ function saveSettings(form: ISettingsForm): (dispatch: Dispatch, getState: any)
|
|
|
192
199
|
const theme = getDefaultTheme();
|
|
193
200
|
|
|
194
201
|
const responseActions = {
|
|
195
|
-
handleSuccess: (response:
|
|
202
|
+
handleSuccess: (response: ISite) => {
|
|
203
|
+
setSiteInfo(response)(dispatch, getState);
|
|
204
|
+
const themeElements = getThemeElements(response.theme) || null;
|
|
205
|
+
dispatch(setThemeElements(themeElements));
|
|
206
|
+
},
|
|
196
207
|
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
197
208
|
};
|
|
198
209
|
|
|
@@ -245,6 +256,9 @@ function setSiteInfo(currentSiteInfo: ISite): (dispatch: any, getState: any) =>
|
|
|
245
256
|
dispatch(setCurrentSiteInfo(currentSiteInfo));
|
|
246
257
|
await getRoles({ siteId: currentSiteInfo.id }, undefined, false)(dispatch);
|
|
247
258
|
|
|
259
|
+
const themeElements = getThemeElements(currentSiteInfo.theme) || null;
|
|
260
|
+
dispatch(setThemeElements(themeElements));
|
|
261
|
+
|
|
248
262
|
// get site languages
|
|
249
263
|
const response: any = await languages.getSiteLanguages(currentSiteInfo.id);
|
|
250
264
|
if (isReqOk(response.status)) {
|
|
@@ -486,6 +500,7 @@ function resetSiteValues(siteID: number): (dispatch: Dispatch) => void {
|
|
|
486
500
|
dispatch(setTotalItems(0));
|
|
487
501
|
getAnalytics(siteID)(dispatch);
|
|
488
502
|
dispatch(setCurrentSearch(""));
|
|
503
|
+
dispatch(setThemeElements(null));
|
|
489
504
|
};
|
|
490
505
|
}
|
|
491
506
|
|
|
@@ -15,6 +15,7 @@ export const SET_CURRENT_SITE_ERROR_PAGES = `${NAME}/SET_CURRENT_SITE_ERROR_PAGE
|
|
|
15
15
|
export const SET_CONTENT_FILTERS = `${NAME}/SET_CONTENT_FILTERS`;
|
|
16
16
|
export const SET_CONFIG = `${NAME}/SET_CONFIG`;
|
|
17
17
|
export const SET_CURRENT_SEARCH = `${NAME}/SET_CURRENT_SEARCH`;
|
|
18
|
+
export const SET_THEME_ELEMENTS = `${NAME}/SET_THEME_ELEMENTS`;
|
|
18
19
|
|
|
19
20
|
export const ITEMS_PER_PAGE = 50;
|
|
20
21
|
|
|
@@ -14,8 +14,9 @@ import {
|
|
|
14
14
|
SET_SITES_TOTAL_ITEMS,
|
|
15
15
|
SET_CONFIG,
|
|
16
16
|
SET_CURRENT_SEARCH,
|
|
17
|
+
SET_THEME_ELEMENTS,
|
|
17
18
|
} from "./constants";
|
|
18
|
-
import { IQueryValue, ISite, ISiteListConfig } from "@ax/types";
|
|
19
|
+
import { IQueryValue, ISite, ISiteListConfig, IThemeElements } from "@ax/types";
|
|
19
20
|
|
|
20
21
|
export interface ISetFilter {
|
|
21
22
|
type: typeof SET_FILTER;
|
|
@@ -92,4 +93,9 @@ export interface ISetCurrentSearch {
|
|
|
92
93
|
payload: { currentSearch: string };
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
export interface ISetThemeElements {
|
|
97
|
+
type: typeof SET_THEME_ELEMENTS;
|
|
98
|
+
payload: { themeElements: IThemeElements | null };
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
export type SitesActionsCreators = ISetSitesAction & ISetCurrentSiteInfoAction;
|
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
SET_CONTENT_FILTERS,
|
|
15
15
|
SET_CONFIG,
|
|
16
16
|
SET_CURRENT_SEARCH,
|
|
17
|
+
SET_THEME_ELEMENTS,
|
|
17
18
|
} from "./constants";
|
|
18
19
|
|
|
19
|
-
import { ISite, IPage, ILanguage, ISiteListConfig, IQueryValue } from "@ax/types";
|
|
20
|
+
import { ISite, IPage, ILanguage, ISiteListConfig, IQueryValue, IThemeElements } from "@ax/types";
|
|
20
21
|
|
|
21
22
|
import { SitesActionsCreators } from "./interfaces";
|
|
22
23
|
|
|
@@ -36,6 +37,7 @@ export interface ISitesState {
|
|
|
36
37
|
contentFilters: Record<string, IQueryValue[]> | null;
|
|
37
38
|
config: ISiteListConfig;
|
|
38
39
|
currentSearch: string;
|
|
40
|
+
themeElements: IThemeElements | null;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
const config = {
|
|
@@ -67,6 +69,7 @@ export const initialState = {
|
|
|
67
69
|
contentFilters: null,
|
|
68
70
|
config,
|
|
69
71
|
currentSearch: "",
|
|
72
|
+
themeElements: null,
|
|
70
73
|
};
|
|
71
74
|
|
|
72
75
|
export function reducer(state = initialState, action: any): ISitesState {
|
|
@@ -86,6 +89,7 @@ export function reducer(state = initialState, action: any): ISitesState {
|
|
|
86
89
|
case SET_CONTENT_FILTERS:
|
|
87
90
|
case SET_CONFIG:
|
|
88
91
|
case SET_CURRENT_SEARCH:
|
|
92
|
+
case SET_THEME_ELEMENTS:
|
|
89
93
|
return { ...state, ...action.payload };
|
|
90
94
|
default:
|
|
91
95
|
return state;
|
package/src/helpers/index.tsx
CHANGED
|
@@ -100,7 +100,14 @@ import { getActivatedDataPacksIds, isModuleDisabled, getDeactivatedModules } fro
|
|
|
100
100
|
|
|
101
101
|
import { isDevelopment } from "./environment";
|
|
102
102
|
|
|
103
|
-
import {
|
|
103
|
+
import {
|
|
104
|
+
getDefaultTheme,
|
|
105
|
+
getThemeElements,
|
|
106
|
+
filterThemeElements,
|
|
107
|
+
filterThemeTemplates,
|
|
108
|
+
filterThemeDatapacks,
|
|
109
|
+
isTemplateExcludedFromTheme,
|
|
110
|
+
} from "./themes";
|
|
104
111
|
|
|
105
112
|
import { parseTheme } from "./parseTheme";
|
|
106
113
|
|
|
@@ -183,6 +190,11 @@ export {
|
|
|
183
190
|
splitAndJoin,
|
|
184
191
|
splitAndTrim,
|
|
185
192
|
getDefaultTheme,
|
|
193
|
+
getThemeElements,
|
|
194
|
+
filterThemeElements,
|
|
195
|
+
filterThemeTemplates,
|
|
196
|
+
filterThemeDatapacks,
|
|
197
|
+
isTemplateExcludedFromTheme,
|
|
186
198
|
copyTextToClipboard,
|
|
187
199
|
getNavigationModules,
|
|
188
200
|
getDefaultNavigationModules,
|
package/src/helpers/themes.tsx
CHANGED
|
@@ -1,9 +1,88 @@
|
|
|
1
|
+
import { IDataPack, IGriddoTheme, ITemplateOption, IThemeElements } from "@ax/types";
|
|
1
2
|
import { themes } from "components";
|
|
2
3
|
|
|
3
4
|
const getDefaultTheme = (): string => {
|
|
4
|
-
const defaultTheme = themes.find((theme
|
|
5
|
+
const defaultTheme = (themes as IGriddoTheme[]).find((theme) => theme.default);
|
|
5
6
|
const theme = defaultTheme ? defaultTheme.value : themes[0].value;
|
|
6
7
|
return theme;
|
|
7
|
-
}
|
|
8
|
+
};
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const getThemeElements = (theme: string): IThemeElements | undefined => {
|
|
11
|
+
return (themes as IGriddoTheme[]).find((griddoTheme) => griddoTheme.value === theme)?.elements;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const filterThemeElements = (
|
|
15
|
+
themeElements: IThemeElements | null,
|
|
16
|
+
elements: string[],
|
|
17
|
+
type: "modules" | "templates" | "datapacks"
|
|
18
|
+
): string[] => {
|
|
19
|
+
if (themeElements === null) return elements;
|
|
20
|
+
|
|
21
|
+
const { exclude, include } = themeElements;
|
|
22
|
+
|
|
23
|
+
let filteredElements: string[] = elements;
|
|
24
|
+
if (exclude && Array.isArray(exclude[type])) {
|
|
25
|
+
filteredElements = elements.filter((element: string) => !exclude[type]?.includes(element));
|
|
26
|
+
} else if (include && Array.isArray(include[type])) {
|
|
27
|
+
filteredElements = elements.filter((element: string) => include[type]?.includes(element));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return filteredElements;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const filterThemeTemplates = (
|
|
34
|
+
themeElements: IThemeElements | null,
|
|
35
|
+
templates: ITemplateOption[]
|
|
36
|
+
): ITemplateOption[] => {
|
|
37
|
+
if (themeElements === null) return templates;
|
|
38
|
+
|
|
39
|
+
const { exclude, include } = themeElements;
|
|
40
|
+
|
|
41
|
+
let filteredElements: ITemplateOption[] = templates;
|
|
42
|
+
if (exclude && Array.isArray(exclude.templates)) {
|
|
43
|
+
filteredElements = templates.filter((template) => !exclude.templates?.includes(template.value));
|
|
44
|
+
} else if (include && Array.isArray(include.templates)) {
|
|
45
|
+
filteredElements = templates.filter((template) => include.templates?.includes(template.value));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return filteredElements;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const filterThemeDatapacks = (themeElements: IThemeElements | null, datapacks: IDataPack[]): IDataPack[] => {
|
|
52
|
+
if (themeElements === null) return datapacks;
|
|
53
|
+
|
|
54
|
+
const { exclude, include } = themeElements;
|
|
55
|
+
|
|
56
|
+
let filteredElements: IDataPack[] = datapacks;
|
|
57
|
+
if (exclude && Array.isArray(exclude.datapacks)) {
|
|
58
|
+
filteredElements = datapacks.filter((datapack) => !exclude.datapacks?.includes(datapack.id));
|
|
59
|
+
} else if (include && Array.isArray(include.datapacks)) {
|
|
60
|
+
filteredElements = datapacks.filter((datapack) => include.datapacks?.includes(datapack.id));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return filteredElements;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const isTemplateExcludedFromTheme = (themeElements: IThemeElements | null, template: string): boolean => {
|
|
67
|
+
if (themeElements === null) return false;
|
|
68
|
+
|
|
69
|
+
const { exclude, include } = themeElements;
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
(exclude && Array.isArray(exclude.templates) && exclude.templates.includes(template)) ||
|
|
73
|
+
(include && Array.isArray(include.templates) && !include.templates.includes(template))
|
|
74
|
+
) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
getDefaultTheme,
|
|
83
|
+
getThemeElements,
|
|
84
|
+
filterThemeElements,
|
|
85
|
+
filterThemeTemplates,
|
|
86
|
+
filterThemeDatapacks,
|
|
87
|
+
isTemplateExcludedFromTheme,
|
|
88
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React, { useReducer, useEffect, useLayoutEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { IRootState, IStructuredData } from "@ax/types";
|
|
5
|
-
import { getThumbnailProps } from "@ax/helpers";
|
|
4
|
+
import { IRootState, IStructuredData, ITemplateOption, IThemeElements } from "@ax/types";
|
|
5
|
+
import { filterThemeTemplates, getThumbnailProps } from "@ax/helpers";
|
|
6
6
|
import { MenuItem, RadioGroup } from "@ax/components";
|
|
7
7
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
8
8
|
import { SecondaryActionButton, MainActionButton } from "./../atoms";
|
|
9
9
|
|
|
10
|
-
import { reducer, IOptionTableStore, setColumnValues,
|
|
10
|
+
import { reducer, IOptionTableStore, setColumnValues, setSelectedType, setOption } from "./store";
|
|
11
11
|
|
|
12
12
|
import * as S from "./style";
|
|
13
13
|
|
|
@@ -23,12 +23,16 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
23
23
|
mainAction,
|
|
24
24
|
secondaryAction,
|
|
25
25
|
structuredData,
|
|
26
|
+
themeElements,
|
|
26
27
|
} = props;
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const filteredValues = filterThemeTemplates(themeElements, values);
|
|
30
|
+
|
|
31
|
+
const filterOptions = (value: string, objKey: "value" | "type") =>
|
|
32
|
+
filteredValues.filter((item) => item[objKey] === value);
|
|
29
33
|
|
|
30
34
|
const filterOptionsByDataPack = (value: string) => {
|
|
31
|
-
return
|
|
35
|
+
return filteredValues.filter((item) => {
|
|
32
36
|
return (
|
|
33
37
|
item.editable !== false && (item.dataPacks ? item.dataPacks.includes(value.toUpperCase()) : item.type === value)
|
|
34
38
|
);
|
|
@@ -47,11 +51,11 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
47
51
|
|
|
48
52
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
49
53
|
|
|
50
|
-
const selectOption = (option:
|
|
54
|
+
const selectOption = (option: string, currentOptions?: ITemplateOption[]) => {
|
|
51
55
|
const { columnValues } = state;
|
|
52
56
|
const availableOptions = currentOptions ? currentOptions : columnValues;
|
|
53
57
|
|
|
54
|
-
const optionObj = availableOptions.find((item
|
|
58
|
+
const optionObj = availableOptions.find((item) => item.value === option);
|
|
55
59
|
setSelectedOption(option);
|
|
56
60
|
if (optionObj) {
|
|
57
61
|
const { isData } = optionObj;
|
|
@@ -59,10 +63,10 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
59
63
|
}
|
|
60
64
|
};
|
|
61
65
|
|
|
62
|
-
let filteredOptionsByDataPack
|
|
66
|
+
let filteredOptionsByDataPack = filterOptionsByDataPack(state.selectedType);
|
|
63
67
|
|
|
64
68
|
useLayoutEffect(() => {
|
|
65
|
-
displayOptions({ value: state.selectedType });
|
|
69
|
+
displayOptions({ value: state.selectedType, label: state.selectedType });
|
|
66
70
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
71
|
}, []);
|
|
68
72
|
|
|
@@ -77,12 +81,11 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
77
81
|
isStructuredData ? selectData(value) : selectPage(value);
|
|
78
82
|
};
|
|
79
83
|
|
|
80
|
-
const showThumbnail = (isDisplayed: boolean) => dispatch(setShowThumbnail(isDisplayed));
|
|
81
84
|
const setType = (type: string) => dispatch(setSelectedType(type));
|
|
82
|
-
const setValues = (columns:
|
|
83
|
-
const setSelectedOption = (selectedOption:
|
|
85
|
+
const setValues = (columns: ITemplateOption[]) => dispatch(setColumnValues(columns));
|
|
86
|
+
const setSelectedOption = (selectedOption: string) => dispatch(setOption({ selectedOption }));
|
|
84
87
|
|
|
85
|
-
const isOptionInType = (column:
|
|
88
|
+
const isOptionInType = (column: ITemplateOption[]) => {
|
|
86
89
|
const optionValues = filterOptions(state.selectedOption, "value");
|
|
87
90
|
return column.includes(optionValues[0]);
|
|
88
91
|
};
|
|
@@ -92,20 +95,19 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
92
95
|
isOptionInType(filteredOptionsByDataPack) &&
|
|
93
96
|
getThumbnailProps(state.selectedOption, true, theme);
|
|
94
97
|
|
|
95
|
-
const displayOptions = (item:
|
|
98
|
+
const displayOptions = (item: IOptionFilter) => {
|
|
96
99
|
const { value } = item;
|
|
100
|
+
|
|
97
101
|
setType(value);
|
|
98
102
|
filteredOptionsByDataPack = filterOptionsByDataPack(value);
|
|
99
|
-
|
|
100
|
-
filteredOptionsByDataPack.forEach((option: any) => {
|
|
103
|
+
filteredOptionsByDataPack.forEach((option) => {
|
|
101
104
|
const { name } = option;
|
|
102
105
|
|
|
103
|
-
const currentGlobalStructuredData = structuredData.global.find((data
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const currentLocalStructuredData = structuredData.site.find((data: any) => data.schema.templates?.includes(name));
|
|
106
|
+
const currentGlobalStructuredData = structuredData.global.find((data) => data.schema.templates?.includes(name));
|
|
107
|
+
const currentLocalStructuredData = structuredData.site.find((data) => data.schema.templates?.includes(name));
|
|
108
|
+
|
|
107
109
|
if (currentGlobalStructuredData || currentLocalStructuredData) {
|
|
108
|
-
filteredOptionsByDataPack = filteredOptionsByDataPack.map((option
|
|
110
|
+
filteredOptionsByDataPack = filteredOptionsByDataPack.map((option) => {
|
|
109
111
|
if (option.mode !== "detail") return option;
|
|
110
112
|
const optionType = option.type;
|
|
111
113
|
const currentStructuredDataId = currentGlobalStructuredData?.id || currentLocalStructuredData?.id;
|
|
@@ -113,17 +115,17 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
113
115
|
const title = currentGlobalStructuredData
|
|
114
116
|
? `Get ${currentGlobalStructuredData.title} from Global`
|
|
115
117
|
: currentLocalStructuredData?.title;
|
|
116
|
-
return { ...option, title };
|
|
118
|
+
return { ...option, title: title || "" };
|
|
117
119
|
}
|
|
118
120
|
return option;
|
|
119
121
|
});
|
|
120
|
-
const globalOptionIdx = filteredOptionsByDataPack.findIndex((option
|
|
122
|
+
const globalOptionIdx = filteredOptionsByDataPack.findIndex((option) => option.mode === "detail");
|
|
121
123
|
const globalOption = filteredOptionsByDataPack.splice(globalOptionIdx, 1);
|
|
122
124
|
filteredOptionsByDataPack.push(...globalOption);
|
|
123
125
|
}
|
|
124
126
|
});
|
|
125
127
|
|
|
126
|
-
filteredOptionsByDataPack.sort((ele
|
|
128
|
+
filteredOptionsByDataPack.sort((ele, comp) => {
|
|
127
129
|
// Use localeCompare to compare strings alphabetically
|
|
128
130
|
return ele.title.localeCompare(comp.title);
|
|
129
131
|
});
|
|
@@ -131,24 +133,21 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
131
133
|
setValues(filteredOptionsByDataPack);
|
|
132
134
|
const firstOption = filteredOptionsByDataPack[0] && filteredOptionsByDataPack[0].value;
|
|
133
135
|
selectOption(firstOption, filteredOptionsByDataPack);
|
|
134
|
-
|
|
135
|
-
const optionList = filterOptions(state.selectedOption, "value");
|
|
136
|
-
const displayThumbnail = optionList.type !== value;
|
|
137
|
-
|
|
138
|
-
showThumbnail(displayThumbnail);
|
|
139
136
|
};
|
|
140
137
|
|
|
138
|
+
const handleChange = (value: string | boolean) => typeof value === "string" && selectOption(value);
|
|
139
|
+
|
|
141
140
|
const hasThumbnail = state.showThumbnail && thumbnailProps && isOptionInType(filteredOptionsByDataPack);
|
|
142
141
|
|
|
143
142
|
return (
|
|
144
143
|
<S.Table>
|
|
145
144
|
<S.LeftColumn>
|
|
146
|
-
{filters.map((item
|
|
147
|
-
const displayFilteredOptions = () => state.selectedType !== item.
|
|
145
|
+
{filters.map((item, i: number) => {
|
|
146
|
+
const displayFilteredOptions = () => state.selectedType !== item.value && displayOptions(item);
|
|
148
147
|
const isSelected = item.value === state.selectedType;
|
|
149
148
|
const selectedClass = isSelected ? "selected" : "";
|
|
150
149
|
return (
|
|
151
|
-
<MenuItem key={`${item.
|
|
150
|
+
<MenuItem key={`${item.value}${i}`}>
|
|
152
151
|
<S.NavLink onClick={displayFilteredOptions} className={selectedClass}>
|
|
153
152
|
<S.Link active={isSelected}>{item.label}</S.Link>
|
|
154
153
|
</S.NavLink>
|
|
@@ -157,7 +156,7 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
157
156
|
})}
|
|
158
157
|
</S.LeftColumn>
|
|
159
158
|
<S.Column>
|
|
160
|
-
<RadioGroup options={state.columnValues} onChange={
|
|
159
|
+
<RadioGroup options={state.columnValues} onChange={handleChange} value={state.selectedOption} name="" />
|
|
161
160
|
</S.Column>
|
|
162
161
|
<S.Column>
|
|
163
162
|
{hasThumbnail && (
|
|
@@ -176,24 +175,34 @@ const OptionTable = (props: IOptionTableProps): JSX.Element => {
|
|
|
176
175
|
|
|
177
176
|
interface IAction {
|
|
178
177
|
title: string;
|
|
179
|
-
onClick:
|
|
178
|
+
onClick: () => void;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface IOptionFilter {
|
|
182
|
+
label: string;
|
|
183
|
+
value: string;
|
|
184
|
+
isData?: boolean;
|
|
185
|
+
special?: string;
|
|
186
|
+
mode?: string;
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
interface IOptionTableProps {
|
|
183
190
|
selectData: (value: string) => void;
|
|
184
191
|
selectPage: (value: string) => void;
|
|
185
192
|
setIsStructuredData: (isActive: boolean) => void;
|
|
186
|
-
filters:
|
|
187
|
-
values:
|
|
193
|
+
filters: IOptionFilter[];
|
|
194
|
+
values: ITemplateOption[];
|
|
188
195
|
selectedValue: string;
|
|
189
196
|
theme: string;
|
|
190
197
|
mainAction: IAction;
|
|
191
198
|
secondaryAction: IAction;
|
|
192
199
|
structuredData: { global: IStructuredData[]; site: IStructuredData[] };
|
|
200
|
+
themeElements: IThemeElements | null;
|
|
193
201
|
}
|
|
194
202
|
|
|
195
203
|
const mapStateToProps = (state: IRootState) => ({
|
|
196
204
|
structuredData: state.structuredData.structuredData,
|
|
205
|
+
themeElements: state.sites.themeElements,
|
|
197
206
|
});
|
|
198
207
|
|
|
199
208
|
const mapDispatchToProps = {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ITemplateOption } from "@ax/types";
|
|
1
2
|
|
|
2
3
|
const SET_COLUMN_VALUES = "SET_COLUMN_VALUES";
|
|
3
4
|
const SET_SHOW_THUMBNAIL = "SET_SHOW_THUMBNAIL";
|
|
@@ -39,8 +40,8 @@ export function setOption(data: any) {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export interface IOptionTableStore {
|
|
42
|
-
columnValues:
|
|
43
|
+
columnValues: ITemplateOption[];
|
|
43
44
|
showThumbnail: boolean;
|
|
44
45
|
selectedOption: string;
|
|
45
|
-
selectedType:
|
|
46
|
-
}
|
|
46
|
+
selectedType: string;
|
|
47
|
+
}
|
|
@@ -2,11 +2,11 @@ import React, { useState } from "react";
|
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
import { themes } from "components";
|
|
4
4
|
|
|
5
|
-
import { IImage, INavItem, IRootState, ISettingsForm } from "@ax/types";
|
|
5
|
+
import { IGriddoTheme, IImage, INavItem, IRootState, ISettingsForm } from "@ax/types";
|
|
6
6
|
import { RouteLeavingGuard } from "@ax/guards";
|
|
7
|
-
import { useShouldBeSaved } from "@ax/hooks";
|
|
7
|
+
import { useModal, useShouldBeSaved } from "@ax/hooks";
|
|
8
8
|
import { appActions } from "@ax/containers/App";
|
|
9
|
-
import { FieldsBehavior, FieldGroup, MainWrapper, ErrorToast, Nav, Notification } from "@ax/components";
|
|
9
|
+
import { FieldsBehavior, FieldGroup, MainWrapper, ErrorToast, Nav, Notification, Modal } from "@ax/components";
|
|
10
10
|
import { sitesActions } from "@ax/containers/Sites";
|
|
11
11
|
import { getNavigationModules } from "@ax/helpers";
|
|
12
12
|
|
|
@@ -18,6 +18,8 @@ const Globals = (props: IProps): JSX.Element => {
|
|
|
18
18
|
const { isSaving, currentSiteInfo, saveSettings, setHistoryPush, navItems, currentNavItem } = props;
|
|
19
19
|
|
|
20
20
|
const [isNavigationNotificationOpen, setIsNavigationNotificationOpen] = useState(false);
|
|
21
|
+
const [tempTheme, setTempTheme] = useState<string | undefined>();
|
|
22
|
+
const { isOpen, toggleModal } = useModal();
|
|
21
23
|
|
|
22
24
|
const title = "General settings";
|
|
23
25
|
|
|
@@ -38,13 +40,26 @@ const Globals = (props: IProps): JSX.Element => {
|
|
|
38
40
|
|
|
39
41
|
const setNameValue = (value: string) => setValue({ name: value });
|
|
40
42
|
const setTimezoneValue = (value: string) => setValue({ timezone: value });
|
|
41
|
-
const setThemeValue = (value: string) =>
|
|
43
|
+
const setThemeValue = (value: string) => {
|
|
44
|
+
const theme = (themes as IGriddoTheme[]).find((th) => th.value === value);
|
|
45
|
+
if (theme && theme.elements) {
|
|
46
|
+
setTempTheme(value);
|
|
47
|
+
toggleModal();
|
|
48
|
+
} else {
|
|
49
|
+
setValue({ theme: value });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
42
52
|
const setFaviconValue = (value: IImage) => setValue({ favicon: value.url ? value.url : null });
|
|
43
53
|
const setSmallAvatarValue = (value: IImage) => setValue({ smallAvatar: value.url ? value.url : null });
|
|
44
54
|
const setBigAvatarValue = (value: IImage) => setValue({ bigAvatar: value.url ? value.url : null });
|
|
45
55
|
const setThumbnailValue = (value: IImage) => setValue({ thumbnail: value.url ? value.url : null });
|
|
46
56
|
const setNavigationModulesValue = (value: any) => setValue({ navigationModules: value });
|
|
47
57
|
|
|
58
|
+
const handleChangeTheme = () => {
|
|
59
|
+
tempTheme && setValue({ theme: tempTheme });
|
|
60
|
+
toggleModal();
|
|
61
|
+
};
|
|
62
|
+
|
|
48
63
|
const saveForm = async () => {
|
|
49
64
|
const isSaved = await saveSettings(form);
|
|
50
65
|
const isNavigationModulesChanging =
|
|
@@ -73,6 +88,9 @@ const Globals = (props: IProps): JSX.Element => {
|
|
|
73
88
|
const closeNavigationNotification = () => setIsNavigationNotificationOpen(false);
|
|
74
89
|
const goToNavigationModules = () => setHistoryPush("/sites/navigations/modules");
|
|
75
90
|
|
|
91
|
+
const mainDeleteAction = { title: "Understood", onClick: handleChangeTheme };
|
|
92
|
+
const secondaryDeleteAction = { title: "Cancel", onClick: toggleModal };
|
|
93
|
+
|
|
76
94
|
return (
|
|
77
95
|
<>
|
|
78
96
|
<RouteLeavingGuard when={isDirty} action={setRoute} text={modalText} />
|
|
@@ -163,6 +181,21 @@ const Globals = (props: IProps): JSX.Element => {
|
|
|
163
181
|
</S.FormWrapper>
|
|
164
182
|
</S.ContentWrapper>
|
|
165
183
|
</S.Wrapper>
|
|
184
|
+
<Modal
|
|
185
|
+
isOpen={isOpen}
|
|
186
|
+
hide={toggleModal}
|
|
187
|
+
title="Theme Change Warning"
|
|
188
|
+
secondaryAction={secondaryDeleteAction}
|
|
189
|
+
mainAction={mainDeleteAction}
|
|
190
|
+
size="S"
|
|
191
|
+
>
|
|
192
|
+
<S.ModalContent>
|
|
193
|
+
<p>
|
|
194
|
+
The selected theme has some restrictions. By switching to this theme,{" "}
|
|
195
|
+
<strong>you may lose access to some modules or templates.</strong>
|
|
196
|
+
</p>
|
|
197
|
+
</S.ModalContent>
|
|
198
|
+
</Modal>
|
|
166
199
|
</MainWrapper>
|
|
167
200
|
</>
|
|
168
201
|
);
|
|
@@ -17,4 +17,8 @@ const FormWrapper = styled.div`
|
|
|
17
17
|
margin: ${(p) => `${p.theme.spacing.m} 0 0 ${p.theme.spacing.m}`};
|
|
18
18
|
`;
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
const ModalContent = styled.div`
|
|
21
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export { Wrapper, ContentWrapper, FormWrapper, ModalContent };
|
package/src/types/index.tsx
CHANGED
|
@@ -983,6 +983,29 @@ export interface IBulkAction {
|
|
|
983
983
|
action: () => void;
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
export interface IGriddoTheme {
|
|
987
|
+
label: string;
|
|
988
|
+
value: string;
|
|
989
|
+
default: boolean;
|
|
990
|
+
elements?: IThemeElements;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export interface IThemeElements {
|
|
994
|
+
include?: { datapacks?: string[]; templates?: string[]; modules?: string[] };
|
|
995
|
+
exclude?: { datapacks?: string[]; templates?: string[]; modules?: string[] };
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
export interface ITemplateOption {
|
|
999
|
+
dataPacks?: string[];
|
|
1000
|
+
value: string;
|
|
1001
|
+
name: string;
|
|
1002
|
+
title: string;
|
|
1003
|
+
type: string;
|
|
1004
|
+
mode?: string;
|
|
1005
|
+
editable?: boolean;
|
|
1006
|
+
isData?: boolean;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
986
1009
|
export type Field =
|
|
987
1010
|
| "AsyncCheckGroup"
|
|
988
1011
|
| "AsyncSelect"
|