@griddo/ax 10.1.77 → 10.1.79
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 +3 -6
- package/public/index.html +2 -2
- package/src/components/Fields/Wysiwyg/config.tsx +26 -16
- package/src/components/Fields/Wysiwyg/helpers.tsx +17 -0
- package/src/components/Fields/Wysiwyg/style.tsx +2 -2
- package/src/components/Fields/Wysiwyg/vendors.tsx +3 -0
- package/src/containers/Navigation/Defaults/actions.tsx +0 -16
- package/src/containers/PageEditor/actions.tsx +6 -14
- package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +36 -12
- package/src/modules/Categories/CategoriesList/atoms.tsx +59 -0
- package/src/modules/Categories/CategoriesList/index.tsx +35 -8
- package/src/modules/Categories/CategoriesList/style.tsx +9 -1
- package/src/modules/Categories/CategoriesList/utils.tsx +20 -0
- package/src/modules/Content/PageItem/index.tsx +1 -1
- package/src/modules/Content/index.tsx +5 -0
- package/src/modules/PageEditor/index.tsx +9 -10
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "10.1.
|
|
4
|
+
"version": "10.1.79",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"react-dom": "*",
|
|
128
128
|
"react-draft-wysiwyg": "^1.14.5",
|
|
129
129
|
"react-frame-component": "^5.2.1",
|
|
130
|
-
"react-froala-wysiwyg": "3.
|
|
130
|
+
"react-froala-wysiwyg": "^3.2.7",
|
|
131
131
|
"react-redux": "^7.2.8",
|
|
132
132
|
"react-refresh": "^0.10.0",
|
|
133
133
|
"react-router-dom": "5.1.2",
|
|
@@ -189,9 +189,6 @@
|
|
|
189
189
|
"ts-jest": "^27.1.4",
|
|
190
190
|
"ts-node": "^10.9.1"
|
|
191
191
|
},
|
|
192
|
-
"resolutions": {
|
|
193
|
-
"react-error-overlay": "6.0.9"
|
|
194
|
-
},
|
|
195
192
|
"babel": {
|
|
196
193
|
"presets": [
|
|
197
194
|
"react-app",
|
|
@@ -233,5 +230,5 @@
|
|
|
233
230
|
"publishConfig": {
|
|
234
231
|
"access": "public"
|
|
235
232
|
},
|
|
236
|
-
"gitHead": "
|
|
233
|
+
"gitHead": "7983caa7394962e73188ec16d3335997293056ca"
|
|
237
234
|
}
|
package/public/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
8
|
<meta name="theme-color" content="#2C50EE" />
|
|
9
|
-
<meta name="description" content="
|
|
9
|
+
<meta name="description" content="Griddo AX" />
|
|
10
10
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
11
11
|
<link rel="stylesheet" href="fonts/fonts.css" />
|
|
12
12
|
<title></title>
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
<div id="root"></div>
|
|
19
19
|
</body>
|
|
20
20
|
|
|
21
|
-
</html>
|
|
21
|
+
</html>
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
import { getRichTextConfig, parseClassNames } from "./helpers";
|
|
2
|
+
|
|
3
|
+
const richTextConfig = getRichTextConfig();
|
|
4
|
+
const paragraphStyles = richTextConfig?.paragraphStyles ? parseClassNames(richTextConfig.paragraphStyles) : undefined;
|
|
5
|
+
|
|
6
|
+
const miscButtons = [
|
|
7
|
+
"underline",
|
|
8
|
+
"textColor",
|
|
9
|
+
"formatOLSimple",
|
|
10
|
+
"specialCharacters",
|
|
11
|
+
"insertHR",
|
|
12
|
+
"alignLeft",
|
|
13
|
+
"alignRight",
|
|
14
|
+
"alignJustify",
|
|
15
|
+
"outdent",
|
|
16
|
+
"indent",
|
|
17
|
+
"undo",
|
|
18
|
+
"redo",
|
|
19
|
+
"selectAll",
|
|
20
|
+
"html",
|
|
21
|
+
"insertTable",
|
|
22
|
+
paragraphStyles ? "paragraphStyle" : undefined,
|
|
23
|
+
];
|
|
24
|
+
|
|
1
25
|
const buttonsFull = {
|
|
2
26
|
moreText: {
|
|
3
27
|
buttons: ["bold", "italic", "paragraphFormat", "formatUL", "quote", "alignCenter"],
|
|
@@ -8,22 +32,7 @@ const buttonsFull = {
|
|
|
8
32
|
buttonsVisible: 5,
|
|
9
33
|
},
|
|
10
34
|
moreMisc: {
|
|
11
|
-
buttons:
|
|
12
|
-
"underline",
|
|
13
|
-
"textColor",
|
|
14
|
-
"formatOLSimple",
|
|
15
|
-
"specialCharacters",
|
|
16
|
-
"insertHR",
|
|
17
|
-
"alignLeft",
|
|
18
|
-
"alignRight",
|
|
19
|
-
"alignJustify",
|
|
20
|
-
"outdent",
|
|
21
|
-
"indent",
|
|
22
|
-
"undo",
|
|
23
|
-
"redo",
|
|
24
|
-
"selectAll",
|
|
25
|
-
"html",
|
|
26
|
-
],
|
|
35
|
+
buttons: miscButtons,
|
|
27
36
|
align: "right",
|
|
28
37
|
buttonsVisible: 0,
|
|
29
38
|
},
|
|
@@ -79,6 +88,7 @@ const wysiwygConfig = {
|
|
|
79
88
|
imageMaxSize: 20 * 1024 * 1024,
|
|
80
89
|
imageManagerLoadURL: `${process.env.REACT_APP_API_ENDPOINT}/images/wysiwyg`,
|
|
81
90
|
requestWithCORS: false,
|
|
91
|
+
paragraphStyles,
|
|
82
92
|
};
|
|
83
93
|
|
|
84
94
|
export { buttonsFull, buttons, wysiwygConfig };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { schemas } from "components";
|
|
2
|
+
|
|
3
|
+
const getRichTextConfig = (): IRichTextConfig | null => {
|
|
4
|
+
return schemas?.richTextConfig ? schemas.richTextConfig : null;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const parseClassNames = (paragraphStyles: { label: string; className: string }[]) =>
|
|
8
|
+
paragraphStyles.reduce(
|
|
9
|
+
(acc, current: { label: string; className: string }) => ({ ...acc, [current.className]: current.label }),
|
|
10
|
+
{}
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
interface IRichTextConfig {
|
|
14
|
+
paragraphStyles?: { label: string; className: string }[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { getRichTextConfig, parseClassNames };
|
|
@@ -56,7 +56,7 @@ export const EditorWrapper = styled.div<{ error: boolean | undefined; disabled?:
|
|
|
56
56
|
margin: 0 10px;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
.second-toolbar {
|
|
59
|
+
.fr-second-toolbar {
|
|
60
60
|
border-color: ${(p) =>
|
|
61
61
|
p.error === true ? p.theme.color.error : p.disabled ? p.theme.color.interactiveDisabled : p.theme.color.uiLine};
|
|
62
62
|
border-radius: 0 0 4px 4px;
|
|
@@ -82,7 +82,7 @@ export const EditorWrapper = styled.div<{ error: boolean | undefined; disabled?:
|
|
|
82
82
|
}
|
|
83
83
|
${Wrapper}:focus-within & {
|
|
84
84
|
.fr-toolbar,
|
|
85
|
-
.second-toolbar {
|
|
85
|
+
.fr-second-toolbar {
|
|
86
86
|
border-color: ${(p) =>
|
|
87
87
|
p.error === true
|
|
88
88
|
? p.theme.color.error
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "froala-editor/css/froala_style.min.css";
|
|
2
2
|
import "froala-editor/css/froala_editor.pkgd.min.css";
|
|
3
|
+
import "froala-editor/css/plugins/table.min.css";
|
|
3
4
|
import "froala-editor/js/plugins/align.min.js";
|
|
4
5
|
import "froala-editor/js/plugins/colors.min.js";
|
|
5
6
|
import "froala-editor/js/plugins/fullscreen.min.js";
|
|
@@ -15,3 +16,5 @@ import "froala-editor/js/plugins/special_characters.min.js";
|
|
|
15
16
|
import "froala-editor/js/plugins/word_paste.min.js";
|
|
16
17
|
import "froala-editor/js/plugins/paragraph_format.min.js";
|
|
17
18
|
import "froala-editor/js/plugins/char_counter.min.js";
|
|
19
|
+
import "froala-editor/js/plugins/paragraph_style.min.js";
|
|
20
|
+
import "froala-editor/js/plugins/table.min.js";
|
|
@@ -419,21 +419,6 @@ function setDefaultNavigation(navID: number, type: string): (dispatch: Dispatch,
|
|
|
419
419
|
};
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
function getSiteDefaults(): (dispatch: Dispatch, getState: any) => Promise<void> {
|
|
423
|
-
return async (dispatch, getState) => {
|
|
424
|
-
const { currentSiteInfo } = getStateValues(getState);
|
|
425
|
-
const siteID = currentSiteInfo.id;
|
|
426
|
-
|
|
427
|
-
const headerResponse = await navigation.getHeaders(siteID);
|
|
428
|
-
const footerResponse = await navigation.getFooters(siteID);
|
|
429
|
-
const header = headerResponse && headerResponse.data;
|
|
430
|
-
const footer = footerResponse && footerResponse.data;
|
|
431
|
-
|
|
432
|
-
dispatch(setHeader(header.id));
|
|
433
|
-
dispatch(setFooter(footer.id));
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
422
|
function generateNewDefault(dispatch: Dispatch, getState: any) {
|
|
438
423
|
const {
|
|
439
424
|
selectedDefault,
|
|
@@ -879,7 +864,6 @@ export {
|
|
|
879
864
|
setHeader,
|
|
880
865
|
setFooter,
|
|
881
866
|
createNewTranslation,
|
|
882
|
-
getSiteDefaults,
|
|
883
867
|
getValues,
|
|
884
868
|
addModule,
|
|
885
869
|
addComponent,
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
} from "./interfaces";
|
|
105
105
|
|
|
106
106
|
const { setIsLoading, setIsSaving, handleError } = appActions;
|
|
107
|
-
const {
|
|
107
|
+
const { getDefaults } = navigationActions;
|
|
108
108
|
|
|
109
109
|
// AUDIT: THIS FILE IS WAY TOO LONG - LOOK FOR A REFACTOR SOLUTION
|
|
110
110
|
// FIXME: CHECK EDITOR CONTENT STRUCTURE (editorContent.editorContent)
|
|
@@ -234,25 +234,18 @@ function setTranslatedParent(): (dispatch: Dispatch, getState: any) => void {
|
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
function createNewTranslation(isNewTranslation: boolean): (dispatch: Dispatch
|
|
238
|
-
return async (dispatch
|
|
237
|
+
function createNewTranslation(isNewTranslation: boolean): (dispatch: Dispatch) => void {
|
|
238
|
+
return async (dispatch) => {
|
|
239
239
|
try {
|
|
240
|
-
const {
|
|
241
|
-
sites: { currentSiteInfo },
|
|
242
|
-
} = getState();
|
|
243
|
-
|
|
244
240
|
dispatch(setIsNewTranslation(isNewTranslation));
|
|
245
241
|
dispatch(setCurrentPageStatus("offline"));
|
|
246
|
-
if (currentSiteInfo) {
|
|
247
|
-
await getSiteDefaults()(dispatch, getState);
|
|
248
|
-
}
|
|
249
242
|
} catch (e) {
|
|
250
243
|
console.log(e); // TODO: capturar error bien
|
|
251
244
|
}
|
|
252
245
|
};
|
|
253
246
|
}
|
|
254
247
|
|
|
255
|
-
const setRootEditorID = (dispatch: Dispatch
|
|
248
|
+
const setRootEditorID = (dispatch: Dispatch) => {
|
|
256
249
|
const rootEditorID = 0;
|
|
257
250
|
|
|
258
251
|
dispatch(setSelectedEditorID(rootEditorID));
|
|
@@ -294,7 +287,7 @@ function generateNewPage(dispatch: Dispatch, getState: any, baseSchema: string)
|
|
|
294
287
|
dispatch(setUserEditing(null));
|
|
295
288
|
|
|
296
289
|
// SET PAGE AS ROOT WHEN NEW PAGE
|
|
297
|
-
setRootEditorID(dispatch
|
|
290
|
+
setRootEditorID(dispatch);
|
|
298
291
|
generatePageContent(page, dispatch, getState);
|
|
299
292
|
}
|
|
300
293
|
|
|
@@ -334,7 +327,6 @@ function getPage(pageID?: number, global?: boolean): (dispatch: Dispatch, getSta
|
|
|
334
327
|
|
|
335
328
|
if (currentSiteInfo) {
|
|
336
329
|
await getPageLanguages(pageID, currentSiteInfo.id, page.entity)(dispatch);
|
|
337
|
-
await getSiteDefaults()(dispatch, getState);
|
|
338
330
|
} else {
|
|
339
331
|
await getPageLanguages(pageID, null, page.entity)(dispatch);
|
|
340
332
|
}
|
|
@@ -357,7 +349,7 @@ function getPage(pageID?: number, global?: boolean): (dispatch: Dispatch, getSta
|
|
|
357
349
|
|
|
358
350
|
if (isReqOk(response.status)) {
|
|
359
351
|
addTemplate(page.templateId)(dispatch);
|
|
360
|
-
setRootEditorID(dispatch
|
|
352
|
+
setRootEditorID(dispatch);
|
|
361
353
|
generatePageContent(page, dispatch, getState);
|
|
362
354
|
dispatch(setCurrentPageName(page.title));
|
|
363
355
|
dispatch(setCurrentPageID(page.id));
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
4
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
5
5
|
import { useModal } from "@ax/hooks";
|
|
6
|
-
import { IStructuredDataContent, ICategory, ICheck } from "@ax/types";
|
|
6
|
+
import { IStructuredDataContent, ICategory, ICheck, IDataLanguage } from "@ax/types";
|
|
7
7
|
import { CheckField, FloatingMenu, Flag, LanguageMenu } from "@ax/components";
|
|
8
8
|
import CategoryPanel from "../CategoryPanel";
|
|
9
|
+
import { DeleteModal } from "../atoms";
|
|
9
10
|
|
|
10
11
|
import * as S from "./style";
|
|
11
12
|
|
|
@@ -28,9 +29,12 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
28
29
|
} = props;
|
|
29
30
|
|
|
30
31
|
const { isOpen, toggleModal } = useModal();
|
|
32
|
+
const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
|
|
33
|
+
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
31
34
|
|
|
32
35
|
const { locale } = lang;
|
|
33
36
|
const { dataLanguages, content } = category;
|
|
37
|
+
const isTranslated = dataLanguages.length > 1;
|
|
34
38
|
|
|
35
39
|
const handleClick = () => {
|
|
36
40
|
resetCategoryValues();
|
|
@@ -40,19 +44,23 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
40
44
|
|
|
41
45
|
const handleOnChange = (value: ICheck) => onChange(value);
|
|
42
46
|
|
|
43
|
-
const removeItem = async () =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const removeItem = async () => {
|
|
48
|
+
const allPageVersions = dataLanguages.map((lang: IDataLanguage) => lang.id);
|
|
49
|
+
const catIds = deleteAllVersions ? allPageVersions : category.id;
|
|
50
|
+
const deleted = await deleteStructuredDataContent(catIds);
|
|
51
|
+
|
|
52
|
+
if (deleted) {
|
|
53
|
+
setDeletedItem(catIds);
|
|
54
|
+
isDeleteOpen && toggleDeleteModal();
|
|
55
|
+
toggleToast();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
50
58
|
|
|
51
59
|
const menuOptions = [
|
|
52
60
|
{
|
|
53
61
|
label: "delete",
|
|
54
62
|
icon: "delete",
|
|
55
|
-
action: removeItem,
|
|
63
|
+
action: isTranslated ? toggleDeleteModal : removeItem,
|
|
56
64
|
},
|
|
57
65
|
];
|
|
58
66
|
|
|
@@ -122,6 +130,13 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
122
130
|
"Not translatable"
|
|
123
131
|
);
|
|
124
132
|
|
|
133
|
+
const mainDeleteModalAction = {
|
|
134
|
+
title: "Delete category",
|
|
135
|
+
onClick: removeItem,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
|
|
139
|
+
|
|
125
140
|
return (
|
|
126
141
|
<>
|
|
127
142
|
<S.CategoryRow role="rowgroup" selected={isSelected}>
|
|
@@ -140,6 +155,15 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
140
155
|
</S.ActionsCell>
|
|
141
156
|
</S.CategoryRow>
|
|
142
157
|
<CategoryPanel isOpen={isOpen} toggleModal={toggleModal} item={category} getContents={getContents} />
|
|
158
|
+
{isDeleteOpen && (
|
|
159
|
+
<DeleteModal
|
|
160
|
+
isOpen={isDeleteOpen}
|
|
161
|
+
toggleModal={toggleDeleteModal}
|
|
162
|
+
mainModalAction={mainDeleteModalAction}
|
|
163
|
+
secondaryModalAction={secondaryDeleteModalAction}
|
|
164
|
+
{...{ deleteAllVersions, setDeleteAllVersions, title: category.title }}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
143
167
|
</>
|
|
144
168
|
);
|
|
145
169
|
};
|
|
@@ -152,13 +176,13 @@ interface IProps {
|
|
|
152
176
|
isSelected: boolean;
|
|
153
177
|
onChange: (e: any) => void;
|
|
154
178
|
toggleToast(): void;
|
|
155
|
-
setDeletedItem(item: number): void;
|
|
179
|
+
setDeletedItem(item: number | number[]): void;
|
|
156
180
|
getContents(dataId: string): void;
|
|
157
181
|
}
|
|
158
182
|
|
|
159
183
|
interface IDispatchProps {
|
|
160
184
|
setCategoryValues(value: ICategory): void;
|
|
161
|
-
deleteStructuredDataContent(id: number): Promise<boolean>;
|
|
185
|
+
deleteStructuredDataContent(id: number | number[]): Promise<boolean>;
|
|
162
186
|
resetCategoryValues(): void;
|
|
163
187
|
setEntity(entity: string): void;
|
|
164
188
|
getTranslatedCategory(id: number, lang: { locale: string; id: number }): any;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { IModal } from "@ax/types";
|
|
4
|
+
import { Modal, FieldsBehavior } from "@ax/components";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const DeleteModal = (props: IDeleteModal): JSX.Element => {
|
|
9
|
+
const { isOpen, toggleModal, mainModalAction, secondaryModalAction, deleteAllVersions, setDeleteAllVersions, title } =
|
|
10
|
+
props;
|
|
11
|
+
|
|
12
|
+
const options = [
|
|
13
|
+
{
|
|
14
|
+
title: "Delete only this version",
|
|
15
|
+
name: "deletePage",
|
|
16
|
+
value: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: "Delete all languages versions",
|
|
20
|
+
name: "deleteAll",
|
|
21
|
+
value: true,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const categoryTitle = title ? <strong>{title}</strong> : "this";
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Modal
|
|
29
|
+
isOpen={isOpen}
|
|
30
|
+
hide={toggleModal}
|
|
31
|
+
title="Delete Category?"
|
|
32
|
+
secondaryAction={secondaryModalAction}
|
|
33
|
+
mainAction={mainModalAction}
|
|
34
|
+
size="S"
|
|
35
|
+
>
|
|
36
|
+
<S.ModalContent>
|
|
37
|
+
<p>
|
|
38
|
+
You are going to delete {categoryTitle} category that have some translations associated. Choose if you want to
|
|
39
|
+
delete all languages versions or only this one.
|
|
40
|
+
</p>
|
|
41
|
+
<FieldsBehavior
|
|
42
|
+
name="removeAllVersions"
|
|
43
|
+
fieldType="RadioGroup"
|
|
44
|
+
value={deleteAllVersions}
|
|
45
|
+
options={options}
|
|
46
|
+
onChange={setDeleteAllVersions}
|
|
47
|
+
/>
|
|
48
|
+
</S.ModalContent>
|
|
49
|
+
</Modal>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
interface IDeleteModal extends IModal {
|
|
54
|
+
deleteAllVersions: boolean;
|
|
55
|
+
setDeleteAllVersions: React.Dispatch<React.SetStateAction<boolean>>;
|
|
56
|
+
title?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { DeleteModal };
|
|
@@ -7,6 +7,8 @@ import { IRootState, IStructuredData, IStructuredDataContent, IGetStructuredData
|
|
|
7
7
|
import { useBulkSelection, useModal, useToast } from "@ax/hooks";
|
|
8
8
|
import { MainWrapper, TableList, ErrorToast, Toast, EmptyState } from "@ax/components";
|
|
9
9
|
|
|
10
|
+
import { DeleteModal } from "./atoms";
|
|
11
|
+
import { getAllLangCategoriesIds } from "./utils";
|
|
10
12
|
import CategoryItem from "./CategoryItem";
|
|
11
13
|
import CategoryPanel from "./CategoryPanel";
|
|
12
14
|
import CategoryNav from "./CategoryNav";
|
|
@@ -39,7 +41,9 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
39
41
|
const [page, setPage] = useState(1);
|
|
40
42
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
41
43
|
const [deletedItem, setDeletedItem] = useState<number | number[] | null>(null);
|
|
44
|
+
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
42
45
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
46
|
+
const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
|
|
43
47
|
const tableRef = useRef<HTMLDivElement>(null);
|
|
44
48
|
|
|
45
49
|
const scope = currentSiteID ? "site" : "global";
|
|
@@ -104,13 +108,22 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
104
108
|
action: openModal,
|
|
105
109
|
};
|
|
106
110
|
|
|
107
|
-
const bulkDelete = () =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const bulkDelete = async () => {
|
|
112
|
+
const idsToBeDeleted = getAllLangCategoriesIds(currentDataContent, selectedItems, deleteAllVersions);
|
|
113
|
+
const deleted = await deleteDataContent(idsToBeDeleted);
|
|
114
|
+
if (deleted) {
|
|
115
|
+
unselectAllItems();
|
|
116
|
+
setDeletedItem(idsToBeDeleted);
|
|
117
|
+
isDeleteOpen && toggleDeleteModal();
|
|
118
|
+
toggleToast();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const handleToggleDeleteModal = () => {
|
|
123
|
+
const selectedCategories = currentDataContent.filter((category) => selectedItems.all.includes(category.id));
|
|
124
|
+
const hasTranslations = selectedCategories.some((category) => category.dataLanguages.length > 1);
|
|
125
|
+
hasTranslations ? toggleDeleteModal() : bulkDelete();
|
|
126
|
+
};
|
|
114
127
|
|
|
115
128
|
const unselectAllItems = () => resetBulkSelection();
|
|
116
129
|
|
|
@@ -136,7 +149,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
136
149
|
const TableHeader = (
|
|
137
150
|
<BulkHeader
|
|
138
151
|
showBulk={areItemsSelected(catIds)}
|
|
139
|
-
bulkDelete={
|
|
152
|
+
bulkDelete={handleToggleDeleteModal}
|
|
140
153
|
selectAllItems={handleSelectAll}
|
|
141
154
|
totalItems={totalItems}
|
|
142
155
|
selectItems={selectItems}
|
|
@@ -171,6 +184,13 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
171
184
|
action: hasCategories ? openModal : () => setHistoryPush("/sites/settings/content-types"),
|
|
172
185
|
};
|
|
173
186
|
|
|
187
|
+
const mainDeleteModalAction = {
|
|
188
|
+
title: "Delete categories",
|
|
189
|
+
onClick: bulkDelete,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
|
|
193
|
+
|
|
174
194
|
return (
|
|
175
195
|
<MainWrapper
|
|
176
196
|
title="Categories"
|
|
@@ -224,6 +244,13 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
224
244
|
</S.TableWrapper>
|
|
225
245
|
</S.CategoryListWrapper>
|
|
226
246
|
<CategoryPanel isOpen={isOpen} toggleModal={toggleModal} getContents={getContents} />
|
|
247
|
+
{isDeleteOpen && <DeleteModal
|
|
248
|
+
isOpen={isDeleteOpen}
|
|
249
|
+
toggleModal={toggleDeleteModal}
|
|
250
|
+
mainModalAction={mainDeleteModalAction}
|
|
251
|
+
secondaryModalAction={secondaryDeleteModalAction}
|
|
252
|
+
{...{ deleteAllVersions, setDeleteAllVersions }}
|
|
253
|
+
/>}
|
|
227
254
|
{isVisible && <Toast {...toastProps} />}
|
|
228
255
|
</MainWrapper>
|
|
229
256
|
);
|
|
@@ -19,4 +19,12 @@ const EmptyWrapper = styled.div`
|
|
|
19
19
|
align-items: center;
|
|
20
20
|
`;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const ModalContent = styled.div`
|
|
23
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
24
|
+
|
|
25
|
+
p {
|
|
26
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export { CategoryListWrapper, TableWrapper, EmptyWrapper, ModalContent };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IDataLanguage, IStructuredDataContent } from "@ax/types";
|
|
2
|
+
|
|
3
|
+
const getAllLangCategoriesIds = (
|
|
4
|
+
currentCategories: IStructuredDataContent[],
|
|
5
|
+
selectedItems: Record<string, number[]>,
|
|
6
|
+
getAllVersions: boolean
|
|
7
|
+
): number[] => {
|
|
8
|
+
let langsCategoryIds: number[] = [];
|
|
9
|
+
if (getAllVersions) {
|
|
10
|
+
const selectedCategories = currentCategories.filter((category) => selectedItems.all.includes(category.id));
|
|
11
|
+
langsCategoryIds = selectedCategories.reduce((ids: number[], category: IStructuredDataContent) => {
|
|
12
|
+
const langsCategoryIds = category.dataLanguages.map((lang: IDataLanguage) => lang.id);
|
|
13
|
+
return [...ids, ...langsCategoryIds];
|
|
14
|
+
}, []);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return langsCategoryIds.length ? langsCategoryIds : selectedItems.all;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { getAllLangCategoriesIds }
|
|
@@ -176,7 +176,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
176
176
|
|
|
177
177
|
const handleDeleteItem = async () => {
|
|
178
178
|
const allPageVersions = pageLanguages.map((lang: IPageLanguage) => lang.pageId);
|
|
179
|
-
deleteAllVersions ?
|
|
179
|
+
deleteAllVersions ? deleteBulk(allPageVersions) : deletePage();
|
|
180
180
|
toggleDeleteModal();
|
|
181
181
|
};
|
|
182
182
|
|
|
@@ -26,6 +26,7 @@ import { appActions } from "@ax/containers/App";
|
|
|
26
26
|
import { sitesActions } from "@ax/containers/Sites";
|
|
27
27
|
import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
28
28
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
29
|
+
import { navigationActions } from "@ax/containers/Navigation/Defaults";
|
|
29
30
|
import { INITIAL_TEMPLATE } from "@ax/containers/PageEditor/constants";
|
|
30
31
|
import { dataPacksActions } from "@ax/containers/Settings";
|
|
31
32
|
import {
|
|
@@ -104,6 +105,7 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
104
105
|
contentFilters,
|
|
105
106
|
deleteAndRemoveFromSiteBulk,
|
|
106
107
|
checkUserSession,
|
|
108
|
+
getDefaults,
|
|
107
109
|
} = props;
|
|
108
110
|
|
|
109
111
|
if (!currentSiteInfo) {
|
|
@@ -282,6 +284,7 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
282
284
|
resetCurrentSiteErrorPages();
|
|
283
285
|
fetchSitesByLang();
|
|
284
286
|
getIntegrations(currentSiteInfo.id);
|
|
287
|
+
getDefaults();
|
|
285
288
|
resetForm();
|
|
286
289
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
287
290
|
}, []);
|
|
@@ -888,6 +891,7 @@ interface IDispatchProps {
|
|
|
888
891
|
setContentFilters(contentFilters: Record<string, string> | null): void;
|
|
889
892
|
deleteAndRemoveFromSiteBulk(pageIds: number[], globalPageIds: number[]): Promise<boolean>;
|
|
890
893
|
checkUserSession(): Promise<void>;
|
|
894
|
+
getDefaults(): Promise<void>;
|
|
891
895
|
}
|
|
892
896
|
|
|
893
897
|
const mapDispatchToProps = {
|
|
@@ -924,6 +928,7 @@ const mapDispatchToProps = {
|
|
|
924
928
|
setContentFilters: sitesActions.setContentFilters,
|
|
925
929
|
deleteAndRemoveFromSiteBulk: sitesActions.deleteAndRemoveFromSiteBulk,
|
|
926
930
|
checkUserSession: appActions.checkUserSession,
|
|
931
|
+
getDefaults: navigationActions.getDefaults,
|
|
927
932
|
};
|
|
928
933
|
|
|
929
934
|
interface IPagesProps {
|
|
@@ -80,6 +80,7 @@ const PageEditor = (props: IProps) => {
|
|
|
80
80
|
const handleGetPage = async () => await getPage(pageID);
|
|
81
81
|
|
|
82
82
|
setTab(defaultTab);
|
|
83
|
+
resetDirty();
|
|
83
84
|
handleGetPage();
|
|
84
85
|
|
|
85
86
|
if (!pageID) {
|
|
@@ -106,12 +107,6 @@ const PageEditor = (props: IProps) => {
|
|
|
106
107
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
107
108
|
}, [userEditing]);
|
|
108
109
|
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
getDefaults();
|
|
111
|
-
resetDirty();
|
|
112
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
113
|
-
}, [lang]);
|
|
114
|
-
|
|
115
110
|
useEffect(() => {
|
|
116
111
|
if (!errorPagesChecked && !isLoading && editorContent) {
|
|
117
112
|
const pageId = editorContent.id;
|
|
@@ -122,6 +117,12 @@ const PageEditor = (props: IProps) => {
|
|
|
122
117
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
118
|
}, [editorContent]);
|
|
124
119
|
|
|
120
|
+
const handleGetTranlation = async (pageID: number) => {
|
|
121
|
+
resetDirty();
|
|
122
|
+
await getDefaults();
|
|
123
|
+
await getPage(pageID);
|
|
124
|
+
}
|
|
125
|
+
|
|
125
126
|
const setRoute = (path: string) => props.setHistoryPush(path, true);
|
|
126
127
|
|
|
127
128
|
const removePage = async () => {
|
|
@@ -269,7 +270,7 @@ const PageEditor = (props: IProps) => {
|
|
|
269
270
|
const languageActions = {
|
|
270
271
|
setLanguage,
|
|
271
272
|
createNewTranslation: handleNewTranlation,
|
|
272
|
-
getContent:
|
|
273
|
+
getContent: handleGetTranlation,
|
|
273
274
|
};
|
|
274
275
|
|
|
275
276
|
const unpublishOption =
|
|
@@ -648,7 +649,6 @@ const mapDispatchToProps = {
|
|
|
648
649
|
setLanguage: appActions.setLanguage,
|
|
649
650
|
createNewTranslation: pageEditorActions.createNewTranslation,
|
|
650
651
|
setTab: pageEditorActions.setTab,
|
|
651
|
-
getSiteDefaults: navigationActions.getSiteDefaults,
|
|
652
652
|
getSiteDataPackbyTemplate: dataPacksActions.getSiteDataPackbyTemplate,
|
|
653
653
|
getSiteDataPackbyModule: dataPacksActions.getSiteDataPackbyModule,
|
|
654
654
|
getDefaults: navigationActions.getDefaults,
|
|
@@ -668,10 +668,9 @@ interface IPageEditorDispatchProps {
|
|
|
668
668
|
setLanguage?(lang: { locale: string; id: number | null }): void;
|
|
669
669
|
createNewTranslation?(isNewTranslation: boolean): void;
|
|
670
670
|
setTab(tab: string): void;
|
|
671
|
-
getSiteDefaults(): void;
|
|
672
671
|
getSiteDataPackbyTemplate(templateType: string): void;
|
|
673
672
|
getSiteDataPackbyModule(module: string): void;
|
|
674
|
-
getDefaults(): void
|
|
673
|
+
getDefaults(): Promise<void>;
|
|
675
674
|
setSelectedContent(editorID: number): Promise<void>;
|
|
676
675
|
sendPagePing(pageID: number): Promise<boolean>;
|
|
677
676
|
discardDraft(): Promise<void>;
|