@griddo/ax 11.11.1 → 11.11.3-rc.0
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/components/Button/style.tsx +1 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +1 -1
- package/src/forms/editor.tsx +9 -14
- package/src/forms/index.tsx +16 -20
- package/src/hooks/forms.tsx +76 -11
- package/src/modules/GlobalEditor/index.tsx +43 -2
- package/src/modules/Navigation/Menus/List/index.tsx +6 -7
- package/src/modules/Navigation/Menus/index.tsx +4 -7
- package/src/modules/PageEditor/index.tsx +10 -2
- package/src/modules/Settings/ContentTypes/DataPacks/hooks.tsx +9 -4
- package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +15 -15
- package/src/modules/StructuredData/Form/index.tsx +12 -5
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +19 -14
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "11.11.
|
|
4
|
+
"version": "11.11.3-rc.0",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Diego M. Béjar <diego.bejar@secuoyas.com>",
|
|
@@ -217,5 +217,5 @@
|
|
|
217
217
|
"publishConfig": {
|
|
218
218
|
"access": "public"
|
|
219
219
|
},
|
|
220
|
-
"gitHead": "
|
|
220
|
+
"gitHead": "9711475ea006516930779a19a6ed56502b6c2454"
|
|
221
221
|
}
|
package/src/forms/editor.tsx
CHANGED
|
@@ -63,7 +63,7 @@ const generateEditorIDs = (originalPageContent: any) => {
|
|
|
63
63
|
component.parentEditorID = parentID;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const nextParentID = Object.
|
|
66
|
+
const nextParentID = Object.hasOwn(component, "editorID") ? component.editorID : parentID;
|
|
67
67
|
|
|
68
68
|
for (const key in component) {
|
|
69
69
|
component[key] = setEditorID(component[key], nextParentID);
|
|
@@ -107,9 +107,6 @@ const getNewBreadcrumb = (componentsTree: any, editorID: number, isFiltered?: bo
|
|
|
107
107
|
return newBreadcrumb;
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
const setIsSavedData = (isSaved: boolean) => localStorage.setItem("isSaved", `${isSaved}`);
|
|
111
|
-
const getIsSavedData = () => localStorage.getItem("isSaved");
|
|
112
|
-
|
|
113
110
|
const cleanPageValues = (updatedValues: any, originalValues: any) => {
|
|
114
111
|
delete updatedValues["modified"];
|
|
115
112
|
delete updatedValues["entity"];
|
|
@@ -240,19 +237,17 @@ function findByComponent(obj: any, componentName: string) {
|
|
|
240
237
|
}
|
|
241
238
|
|
|
242
239
|
export {
|
|
243
|
-
|
|
240
|
+
checkMaxModules,
|
|
244
241
|
cleanContent,
|
|
242
|
+
cleanPageValues,
|
|
243
|
+
evaluateComputedFields,
|
|
244
|
+
filterBreadcrumb,
|
|
245
|
+
findByComponent,
|
|
245
246
|
findByEditorID,
|
|
246
247
|
generateEditorIDs,
|
|
247
|
-
filterBreadcrumb,
|
|
248
|
-
getNewBreadcrumb,
|
|
249
|
-
setIsSavedData,
|
|
250
|
-
getIsSavedData,
|
|
251
|
-
cleanPageValues,
|
|
252
|
-
getLastModuleEditorID,
|
|
253
248
|
getLastComponentEditorID,
|
|
249
|
+
getLastModuleEditorID,
|
|
250
|
+
getNewBreadcrumb,
|
|
254
251
|
getParentKey,
|
|
255
|
-
|
|
256
|
-
evaluateComputedFields,
|
|
257
|
-
findByComponent,
|
|
252
|
+
parseData,
|
|
258
253
|
};
|
package/src/forms/index.tsx
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
checkMaxModules,
|
|
3
3
|
cleanContent,
|
|
4
|
+
cleanPageValues,
|
|
5
|
+
evaluateComputedFields,
|
|
6
|
+
findByComponent,
|
|
4
7
|
findByEditorID,
|
|
5
8
|
generateEditorIDs,
|
|
6
|
-
getNewBreadcrumb,
|
|
7
|
-
setIsSavedData,
|
|
8
|
-
getIsSavedData,
|
|
9
|
-
cleanPageValues,
|
|
10
|
-
getLastModuleEditorID,
|
|
11
9
|
getLastComponentEditorID,
|
|
10
|
+
getLastModuleEditorID,
|
|
11
|
+
getNewBreadcrumb,
|
|
12
12
|
getParentKey,
|
|
13
|
-
|
|
14
|
-
evaluateComputedFields,
|
|
15
|
-
findByComponent,
|
|
13
|
+
parseData,
|
|
16
14
|
} from "./editor";
|
|
17
15
|
import {
|
|
16
|
+
addElement,
|
|
17
|
+
deleteComponent,
|
|
18
18
|
getUpdatedComponents,
|
|
19
19
|
getUpdatedSections,
|
|
20
|
-
updateComponent,
|
|
21
|
-
updateByEditorID,
|
|
22
|
-
moveModule,
|
|
23
20
|
moveElement,
|
|
24
|
-
|
|
21
|
+
moveModule,
|
|
25
22
|
replaceElements,
|
|
23
|
+
updateByEditorID,
|
|
24
|
+
updateComponent,
|
|
26
25
|
updateElementCollection,
|
|
27
|
-
addElement,
|
|
28
26
|
} from "./elements";
|
|
29
27
|
import { getInnerFields, getStructuredDataInnerFields } from "./fields";
|
|
30
28
|
import {
|
|
31
|
-
|
|
32
|
-
findPackagesActivationErrors,
|
|
29
|
+
checkH1content,
|
|
33
30
|
findFieldsErrors,
|
|
34
|
-
isTemplateActivated,
|
|
35
31
|
findMandatoryStructuredDataErrors,
|
|
36
|
-
|
|
32
|
+
findPackagesActivationErrors,
|
|
33
|
+
getValidity,
|
|
34
|
+
isTemplateActivated,
|
|
37
35
|
parseValidationErrors,
|
|
38
36
|
} from "./validators";
|
|
39
37
|
|
|
@@ -43,8 +41,6 @@ export {
|
|
|
43
41
|
findByEditorID,
|
|
44
42
|
generateEditorIDs,
|
|
45
43
|
getNewBreadcrumb,
|
|
46
|
-
setIsSavedData,
|
|
47
|
-
getIsSavedData,
|
|
48
44
|
cleanPageValues,
|
|
49
45
|
getUpdatedComponents,
|
|
50
46
|
updateComponent,
|
package/src/hooks/forms.tsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { memo, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { memo, useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { cleanPageValues
|
|
3
|
+
import { cleanPageValues } from "@ax/forms";
|
|
4
4
|
import { deepClone, isEmptyObj } from "@ax/helpers";
|
|
5
5
|
import type { FormContent, IUser } from "@ax/types";
|
|
6
6
|
|
|
7
7
|
import isEqual from "lodash.isequal";
|
|
8
8
|
|
|
9
|
+
const DEBUG_DIRTY = true;
|
|
10
|
+
|
|
9
11
|
const useDebounce = (value: any) => {
|
|
10
12
|
// State and setters for debounced value
|
|
11
13
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
@@ -55,7 +57,6 @@ const usePrevious = (
|
|
|
55
57
|
const { isSaved, force } = options || {};
|
|
56
58
|
const valueStr = value && JSON.stringify(value);
|
|
57
59
|
const ref = useRef<Record<string, unknown> | string | undefined>(undefined);
|
|
58
|
-
const isSavedData = getIsSavedData();
|
|
59
60
|
|
|
60
61
|
useEffect(() => {
|
|
61
62
|
const currentValue = valueStr && JSON.parse(valueStr);
|
|
@@ -66,17 +67,32 @@ const usePrevious = (
|
|
|
66
67
|
|
|
67
68
|
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix this
|
|
68
69
|
useEffect(() => {
|
|
69
|
-
if (
|
|
70
|
+
if (isSaved) {
|
|
70
71
|
ref.current = valueStr && JSON.parse(valueStr);
|
|
71
72
|
}
|
|
72
|
-
}, [
|
|
73
|
+
}, [isSaved]);
|
|
73
74
|
|
|
74
75
|
return ref.current;
|
|
75
76
|
};
|
|
76
77
|
|
|
78
|
+
const getChangedKeys = (
|
|
79
|
+
prev: Record<string, unknown>,
|
|
80
|
+
next: Record<string, unknown>,
|
|
81
|
+
): Array<{ key: string; prev: unknown; next: unknown }> => {
|
|
82
|
+
const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
83
|
+
const diffs: Array<{ key: string; prev: unknown; next: unknown }> = [];
|
|
84
|
+
for (const key of allKeys) {
|
|
85
|
+
if (!isEqual(prev[key], next[key])) {
|
|
86
|
+
diffs.push({ key, prev: prev[key], next: next[key] });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return diffs;
|
|
90
|
+
};
|
|
91
|
+
|
|
77
92
|
const useIsDirty = (
|
|
78
93
|
updatedValues: any,
|
|
79
94
|
isNew?: boolean,
|
|
95
|
+
debug = DEBUG_DIRTY,
|
|
80
96
|
): { isDirty: boolean; setIsDirty(state: boolean): void; resetDirty: any } => {
|
|
81
97
|
const [isDirty, setIsDirty] = useState(false);
|
|
82
98
|
const [isSaved, setIsSaved] = useState(false);
|
|
@@ -85,7 +101,11 @@ const useIsDirty = (
|
|
|
85
101
|
|
|
86
102
|
const prevContent = usePrevious(updatedValues, { isSaved, force });
|
|
87
103
|
|
|
88
|
-
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (force) setForce(false);
|
|
106
|
+
}, [force]);
|
|
107
|
+
|
|
108
|
+
const hasChanged = useCallback((): boolean => {
|
|
89
109
|
if (prevContent && updatedValues) {
|
|
90
110
|
const originalValuesCloned = prevContent && deepClone(prevContent);
|
|
91
111
|
const updatedValuesCloned = updatedValues && deepClone(updatedValues);
|
|
@@ -94,17 +114,37 @@ const useIsDirty = (
|
|
|
94
114
|
|
|
95
115
|
const hasChanged = !isEqual(cleanUpdatedValues, cleanOriginalValues);
|
|
96
116
|
|
|
117
|
+
if (debug) {
|
|
118
|
+
console.groupCollapsed(
|
|
119
|
+
`[useIsDirty] hasChanged: %c${hasChanged}`,
|
|
120
|
+
hasChanged ? "color: red; font-weight: bold" : "color: green",
|
|
121
|
+
);
|
|
122
|
+
console.log("Snapshot (cleaned):", cleanOriginalValues);
|
|
123
|
+
console.log("Current (cleaned):", cleanUpdatedValues);
|
|
124
|
+
const diffs = getChangedKeys(
|
|
125
|
+
cleanOriginalValues as Record<string, unknown>,
|
|
126
|
+
cleanUpdatedValues as Record<string, unknown>,
|
|
127
|
+
);
|
|
128
|
+
if (diffs.length > 0) {
|
|
129
|
+
console.table(diffs);
|
|
130
|
+
}
|
|
131
|
+
console.groupEnd();
|
|
132
|
+
}
|
|
133
|
+
|
|
97
134
|
return hasChanged;
|
|
98
135
|
}
|
|
99
136
|
return false;
|
|
100
|
-
};
|
|
137
|
+
}, [prevContent, updatedValues, debug]);
|
|
101
138
|
|
|
102
|
-
const resetDirty = (reseting = true,
|
|
139
|
+
const resetDirty = useCallback((reseting = true, forceUpdate = false) => {
|
|
140
|
+
if (debug) {
|
|
141
|
+
console.log(`[useIsDirty] resetDirty(resetting=${reseting}, force=${forceUpdate})`);
|
|
142
|
+
}
|
|
103
143
|
setIsDirty(false);
|
|
104
144
|
setIsSaved(true);
|
|
105
145
|
reseting && setIsResetting(true);
|
|
106
|
-
setForce(
|
|
107
|
-
};
|
|
146
|
+
setForce(forceUpdate);
|
|
147
|
+
}, [debug]);
|
|
108
148
|
|
|
109
149
|
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
110
150
|
useEffect(() => {
|
|
@@ -114,14 +154,17 @@ const useIsDirty = (
|
|
|
114
154
|
}
|
|
115
155
|
|
|
116
156
|
if (isNew) {
|
|
157
|
+
if (debug) console.log("[useIsDirty] isNew=true → isDirty=false");
|
|
117
158
|
setIsDirty(false);
|
|
118
159
|
} else if (prevContent && updatedValues) {
|
|
119
160
|
const isUpdated = hasChanged();
|
|
120
161
|
|
|
121
162
|
if (isUpdated) {
|
|
163
|
+
if (debug) console.log("[useIsDirty] dirty=true");
|
|
122
164
|
setIsDirty(true);
|
|
123
165
|
setIsSaved(false);
|
|
124
166
|
} else {
|
|
167
|
+
if (debug) console.log("[useIsDirty] dirty=false (no changes)");
|
|
125
168
|
setIsDirty(false);
|
|
126
169
|
}
|
|
127
170
|
}
|
|
@@ -141,7 +184,7 @@ const cleanModified = (updatedValues: any, originalValues: any) => {
|
|
|
141
184
|
};
|
|
142
185
|
};
|
|
143
186
|
|
|
144
|
-
const useShouldBeSaved = (form: Record<string, unknown> | IUser | FormContent) => {
|
|
187
|
+
const useShouldBeSaved = (form: Record<string, unknown> | IUser | FormContent, debug = DEBUG_DIRTY) => {
|
|
145
188
|
const [isDirty, setIsDirty] = useState(false);
|
|
146
189
|
const formRef = useRef();
|
|
147
190
|
|
|
@@ -156,9 +199,31 @@ const useShouldBeSaved = (form: Record<string, unknown> | IUser | FormContent) =
|
|
|
156
199
|
const { cleanUpdatedValues, cleanOriginalValues } = cleanModified(formRef.current, form);
|
|
157
200
|
const isFormDirty =
|
|
158
201
|
cleanOriginalValues.id && !cleanUpdatedValues.id ? false : !isEqual(cleanUpdatedValues, cleanOriginalValues);
|
|
202
|
+
|
|
203
|
+
if (debug) {
|
|
204
|
+
console.groupCollapsed(
|
|
205
|
+
`[useShouldBeSaved] isDirty: %c${isFormDirty}`,
|
|
206
|
+
isFormDirty ? "color: red; font-weight: bold" : "color: green",
|
|
207
|
+
);
|
|
208
|
+
console.log("Stored ref (cleaned):", cleanUpdatedValues);
|
|
209
|
+
console.log("Current (cleaned):", cleanOriginalValues);
|
|
210
|
+
if (cleanOriginalValues.id && !cleanUpdatedValues.id) {
|
|
211
|
+
console.log("Suppressed: original has id, ref does not.");
|
|
212
|
+
}
|
|
213
|
+
const diffs = getChangedKeys(
|
|
214
|
+
cleanUpdatedValues as Record<string, unknown>,
|
|
215
|
+
cleanOriginalValues as Record<string, unknown>,
|
|
216
|
+
);
|
|
217
|
+
if (diffs.length > 0) {
|
|
218
|
+
console.table(diffs);
|
|
219
|
+
}
|
|
220
|
+
console.groupEnd();
|
|
221
|
+
}
|
|
222
|
+
|
|
159
223
|
setIsDirty(isFormDirty);
|
|
160
224
|
formRef.current = stringValue && JSON.parse(stringValue);
|
|
161
225
|
} else {
|
|
226
|
+
if (debug) console.log("[useShouldBeSaved] form=null → isDirty=false");
|
|
162
227
|
setIsDirty(false);
|
|
163
228
|
}
|
|
164
229
|
}, [form]);
|
|
@@ -92,6 +92,8 @@ const GlobalEditor = (props: IProps) => {
|
|
|
92
92
|
const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
|
|
93
93
|
const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
|
|
94
94
|
const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
|
|
95
|
+
const { isOpen: isDirtyNavigateOpen, toggleModal: toggleDirtyNavigateModal } = useModal();
|
|
96
|
+
const [pendingNavigateCallback, setPendingNavigateCallback] = useState<(() => void) | null>(null);
|
|
95
97
|
const browserRef = useRef<HTMLDivElement>(null);
|
|
96
98
|
|
|
97
99
|
const isPublished = props.pageStatus === pageStatus.PUBLISHED || props.pageStatus === pageStatus.UPLOAD_PENDING;
|
|
@@ -443,11 +445,37 @@ const GlobalEditor = (props: IProps) => {
|
|
|
443
445
|
}
|
|
444
446
|
};
|
|
445
447
|
|
|
446
|
-
const
|
|
448
|
+
const executeToggleDraftPage = async () => {
|
|
447
449
|
const { getPage } = props;
|
|
448
|
-
resetDirty(undefined, true);
|
|
449
450
|
const pageID = isDraft ? editorContent.draftFromPage : editorContent.haveDraftPage;
|
|
450
451
|
await getPage(pageID);
|
|
452
|
+
resetDirty(true, true);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const toggleDraftPage = () => {
|
|
456
|
+
if (isDirty) {
|
|
457
|
+
onNavigateWithDirty(executeToggleDraftPage);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
executeToggleDraftPage();
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const onNavigateWithDirty = (navigateCallback: () => void) => {
|
|
464
|
+
setPendingNavigateCallback(() => navigateCallback);
|
|
465
|
+
toggleDirtyNavigateModal();
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const handleConfirmDirtyNavigation = () => {
|
|
469
|
+
toggleDirtyNavigateModal();
|
|
470
|
+
if (pendingNavigateCallback) {
|
|
471
|
+
pendingNavigateCallback();
|
|
472
|
+
setPendingNavigateCallback(null);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const handleCancelDirtyNavigation = () => {
|
|
477
|
+
toggleDirtyNavigateModal();
|
|
478
|
+
setPendingNavigateCallback(null);
|
|
451
479
|
};
|
|
452
480
|
|
|
453
481
|
let pageStatusActions =
|
|
@@ -712,6 +740,19 @@ const GlobalEditor = (props: IProps) => {
|
|
|
712
740
|
}}
|
|
713
741
|
/>
|
|
714
742
|
</MainWrapper>
|
|
743
|
+
<Modal
|
|
744
|
+
isOpen={isDirtyNavigateOpen}
|
|
745
|
+
hide={handleCancelDirtyNavigation}
|
|
746
|
+
size="S"
|
|
747
|
+
title="Unsaved changes"
|
|
748
|
+
mainAction={{ title: "Yes, discard changes", onClick: handleConfirmDirtyNavigation }}
|
|
749
|
+
secondaryAction={{ title: "Cancel", onClick: handleCancelDirtyNavigation }}
|
|
750
|
+
>
|
|
751
|
+
<S.ModalContent>
|
|
752
|
+
Some content <strong>is not saved</strong> on this page. If you exit without saving it, it will be lost. Do
|
|
753
|
+
you want to discard your changes?
|
|
754
|
+
</S.ModalContent>
|
|
755
|
+
</Modal>
|
|
715
756
|
</>
|
|
716
757
|
);
|
|
717
758
|
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback, useEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { IGetSitePagesParams, IRootState, IMenuItem } from "@ax/types";
|
|
5
|
-
|
|
6
|
-
import { sitesActions } from "@ax/containers/Sites";
|
|
7
4
|
import { menuActions } from "@ax/containers/Navigation";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { sitesActions } from "@ax/containers/Sites";
|
|
6
|
+
import type { IGetSitePagesParams, IMenuItem, IRootState } from "@ax/types";
|
|
10
7
|
|
|
8
|
+
import { useShouldBeSaved } from "./helpers";
|
|
11
9
|
import Nav from "./Nav";
|
|
12
10
|
import Table from "./Table";
|
|
11
|
+
|
|
13
12
|
import * as S from "./style";
|
|
14
13
|
|
|
15
14
|
const List = (props: IMenuList): JSX.Element => {
|
|
@@ -28,6 +27,7 @@ const List = (props: IMenuList): JSX.Element => {
|
|
|
28
27
|
[getSitePages],
|
|
29
28
|
);
|
|
30
29
|
|
|
30
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: Fix this
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
const params = {
|
|
33
33
|
siteID: currentSiteID,
|
|
@@ -37,7 +37,6 @@ const List = (props: IMenuList): JSX.Element => {
|
|
|
37
37
|
};
|
|
38
38
|
memoizedGetSitePages(params);
|
|
39
39
|
memoizedGetMenus();
|
|
40
|
-
setIsSavedData(true);
|
|
41
40
|
return () => {
|
|
42
41
|
setCurrentType("");
|
|
43
42
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { connect } from "react-redux";
|
|
3
2
|
|
|
4
|
-
import { menuActions } from "@ax/containers/Navigation";
|
|
5
|
-
import { appActions } from "@ax/containers/App";
|
|
6
|
-
import { ILanguage, IRootState } from "@ax/types";
|
|
7
|
-
import { setIsSavedData } from "@ax/forms";
|
|
8
3
|
import { MainWrapper } from "@ax/components";
|
|
4
|
+
import { appActions } from "@ax/containers/App";
|
|
5
|
+
import { menuActions } from "@ax/containers/Navigation";
|
|
9
6
|
import { usePermission } from "@ax/hooks";
|
|
7
|
+
import type { ILanguage, IRootState } from "@ax/types";
|
|
10
8
|
|
|
11
9
|
import List from "./List";
|
|
12
10
|
|
|
@@ -16,8 +14,7 @@ const MenuView = (props: IMenus) => {
|
|
|
16
14
|
const isAllowedToMangageMenus = usePermission("navigation.manageSiteMenu");
|
|
17
15
|
|
|
18
16
|
const saveMenu = () => {
|
|
19
|
-
|
|
20
|
-
return updateMenu();
|
|
17
|
+
updateMenu();
|
|
21
18
|
};
|
|
22
19
|
|
|
23
20
|
const rightButtonProps = isAllowedToMangageMenus
|
|
@@ -481,11 +481,19 @@ const PageEditor = (props: IProps) => {
|
|
|
481
481
|
}
|
|
482
482
|
};
|
|
483
483
|
|
|
484
|
-
const
|
|
484
|
+
const executeToggleDraftPage = async () => {
|
|
485
485
|
const { getPage } = props;
|
|
486
|
-
resetDirty(undefined, true);
|
|
487
486
|
const pageID = isDraft ? editorContent.draftFromPage : editorContent.haveDraftPage;
|
|
488
487
|
await getPage(pageID);
|
|
488
|
+
resetDirty(true, true);
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const toggleDraftPage = () => {
|
|
492
|
+
if (isDirty) {
|
|
493
|
+
onNavigateWithDirty(executeToggleDraftPage);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
executeToggleDraftPage();
|
|
489
497
|
};
|
|
490
498
|
|
|
491
499
|
let pageStatusActions =
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
1
3
|
import isEqual from "lodash.isequal";
|
|
2
|
-
import { useEffect, useState, useRef } from "react";
|
|
3
4
|
|
|
4
|
-
const useShouldBeSaved = (
|
|
5
|
+
const useShouldBeSaved = (
|
|
6
|
+
selected: any | null,
|
|
7
|
+
form: any | null,
|
|
8
|
+
): { isDirty: boolean; setIsDirty: (isDirty: boolean) => void } => {
|
|
5
9
|
const formRef = useRef();
|
|
6
10
|
const [isDirty, setIsDirty] = useState(false);
|
|
7
|
-
const savedValues = selected
|
|
11
|
+
const savedValues = selected?.config && JSON.stringify(selected.config);
|
|
8
12
|
const stringValue = form && JSON.stringify(form);
|
|
9
13
|
|
|
14
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
10
15
|
useEffect(() => {
|
|
11
16
|
if (!formRef.current) {
|
|
12
17
|
formRef.current = stringValue && JSON.parse(stringValue);
|
|
@@ -15,7 +20,7 @@ const useShouldBeSaved = (selected: any | null, form: any | null): { isDirty: bo
|
|
|
15
20
|
savedValues ? setIsDirty(savedValues !== stringValue) : setIsDirty(!isEqual(stringFormRef, stringValue));
|
|
16
21
|
}, [form]);
|
|
17
22
|
|
|
18
|
-
return { isDirty };
|
|
23
|
+
return { isDirty, setIsDirty };
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
export { useShouldBeSaved };
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { useModal, useToast } from "@ax/hooks";
|
|
6
|
-
import { RouteLeavingGuard } from "@ax/guards";
|
|
7
|
-
import { setIsSavedData } from "@ax/forms";
|
|
8
|
-
import { dataPacksActions } from "@ax/containers/Settings";
|
|
4
|
+
import { EmptyState, ErrorToast, Loading, MainWrapper, Modal, Toast } from "@ax/components";
|
|
9
5
|
import { appActions } from "@ax/containers/App";
|
|
10
|
-
import {
|
|
6
|
+
import { dataPacksActions } from "@ax/containers/Settings";
|
|
7
|
+
import { RouteLeavingGuard } from "@ax/guards";
|
|
8
|
+
import { useModal, useToast } from "@ax/hooks";
|
|
9
|
+
import type { IDataPack, IRootState } from "@ax/types";
|
|
11
10
|
|
|
12
|
-
import
|
|
11
|
+
import AddModal from "./AddModal";
|
|
13
12
|
import Config from "./Config";
|
|
14
|
-
import
|
|
13
|
+
import { useShouldBeSaved } from "./hooks";
|
|
15
14
|
import List from "./List";
|
|
16
|
-
import
|
|
15
|
+
import Nav from "./Nav";
|
|
17
16
|
|
|
18
17
|
import * as S from "./style";
|
|
19
18
|
|
|
@@ -34,9 +33,9 @@ const DataPacks = (props: IProps): JSX.Element => {
|
|
|
34
33
|
routerAction,
|
|
35
34
|
} = props;
|
|
36
35
|
|
|
37
|
-
const isFromPage = selected
|
|
36
|
+
const isFromPage = selected?.templates && selected.templates.length > 0;
|
|
38
37
|
|
|
39
|
-
const { isDirty } = useShouldBeSaved(selected, form);
|
|
38
|
+
const { isDirty, setIsDirty } = useShouldBeSaved(selected, form);
|
|
40
39
|
const { isOpen, toggleModal } = useModal();
|
|
41
40
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
42
41
|
const errorRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -47,10 +46,11 @@ const DataPacks = (props: IProps): JSX.Element => {
|
|
|
47
46
|
const toggleDeleteModal = () => setIsDeleteModalOpen(!isDeleteModalOpen);
|
|
48
47
|
|
|
49
48
|
const saveConfig = async () => {
|
|
50
|
-
setIsSavedData(true);
|
|
51
49
|
const saved = await updateDataPack(selected.id);
|
|
52
50
|
if (!saved && errorRef.current) {
|
|
53
51
|
errorRef.current.scrollIntoView();
|
|
52
|
+
} else {
|
|
53
|
+
setIsDirty(false);
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -103,9 +103,9 @@ const DataPacks = (props: IProps): JSX.Element => {
|
|
|
103
103
|
|
|
104
104
|
const deleteModalText = (
|
|
105
105
|
<>
|
|
106
|
-
You are deactivating a <strong>{selected
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
You are deactivating a <strong>{selected?.title}</strong> package. Some of its content is being used in existing
|
|
107
|
+
pages. By deactivating it, you won't be able to edit the content nor add new modules tied to it. You will
|
|
108
|
+
only be able to delete it.
|
|
109
109
|
</>
|
|
110
110
|
);
|
|
111
111
|
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import { appActions } from "@ax/containers/App";
|
|
14
14
|
import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
|
|
15
15
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
16
|
-
import { setIsSavedData } from "@ax/forms";
|
|
17
16
|
import { RouteLeavingGuard } from "@ax/guards";
|
|
18
17
|
import { dateToString, getActivatedDataPacksIds, getDefaultTheme } from "@ax/helpers";
|
|
19
18
|
import { useIsDirty, useModal } from "@ax/hooks";
|
|
@@ -64,7 +63,10 @@ const Form = (props: IProps) => {
|
|
|
64
63
|
|
|
65
64
|
const [isNewStructuredData, setIsNewStructuredData] = useState(!currentStructuredDataId);
|
|
66
65
|
const [notification, setNotification] = useState<INotification | null>(null);
|
|
67
|
-
const [scheduleDate, setScheduleDate] = useState({
|
|
66
|
+
const [scheduleDate, setScheduleDate] = useState({
|
|
67
|
+
date: dateToString(new Date(), "yyy/MM/dd"),
|
|
68
|
+
time: "12:00 am",
|
|
69
|
+
});
|
|
68
70
|
const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
|
|
69
71
|
const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
|
|
70
72
|
const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
|
|
@@ -141,7 +143,6 @@ const Form = (props: IProps) => {
|
|
|
141
143
|
const validated = publish && !skipReviewOnPublish ? await validateForm(true) : true;
|
|
142
144
|
|
|
143
145
|
if (validated) {
|
|
144
|
-
setIsSavedData(true);
|
|
145
146
|
const status = isNewStructuredData ? true : form.draft;
|
|
146
147
|
|
|
147
148
|
const payload: any = {
|
|
@@ -334,14 +335,20 @@ const Form = (props: IProps) => {
|
|
|
334
335
|
onClick: handleSchedulePublication,
|
|
335
336
|
};
|
|
336
337
|
|
|
337
|
-
const secondaryScheduleModalAction = {
|
|
338
|
+
const secondaryScheduleModalAction = {
|
|
339
|
+
title: "Cancel",
|
|
340
|
+
onClick: toggleScheduleModal,
|
|
341
|
+
};
|
|
338
342
|
|
|
339
343
|
const mainCancelScheduleModalAction = {
|
|
340
344
|
title: "Cancel Schedule",
|
|
341
345
|
onClick: handleCancelSchedulePublication,
|
|
342
346
|
};
|
|
343
347
|
|
|
344
|
-
const secondaryCancelScheduleModalAction = {
|
|
348
|
+
const secondaryCancelScheduleModalAction = {
|
|
349
|
+
title: "Back",
|
|
350
|
+
onClick: toggleCancelScheduleModal,
|
|
351
|
+
};
|
|
345
352
|
|
|
346
353
|
return isLoading ? (
|
|
347
354
|
<Loading />
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import React, { useRef } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
-
import { useTheme } from "styled-components";
|
|
4
3
|
|
|
4
|
+
import { CategoryCell, CheckField, Flag, FloatingMenu, Icon, LanguageMenu, Tooltip } from "@ax/components";
|
|
5
|
+
import { appActions } from "@ax/containers/App";
|
|
6
|
+
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
7
|
+
import { getActivatedDataPacksIds, getHumanLastModifiedDate, getScheduleFormatDate } from "@ax/helpers";
|
|
8
|
+
import { useAdaptiveText, usePermission } from "@ax/hooks";
|
|
5
9
|
import type {
|
|
6
|
-
IRootState,
|
|
7
|
-
IStructuredDataContent,
|
|
8
10
|
ICheck,
|
|
11
|
+
IColumn,
|
|
12
|
+
IDataLanguage,
|
|
9
13
|
IDataPack,
|
|
10
14
|
ILanguage,
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
IRootState,
|
|
16
|
+
ISchemaField,
|
|
13
17
|
ISite,
|
|
14
18
|
IStructuredData,
|
|
15
|
-
|
|
19
|
+
IStructuredDataContent,
|
|
16
20
|
} from "@ax/types";
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
import { appActions } from "@ax/containers/App";
|
|
21
|
-
import { CheckField, FloatingMenu, Icon, Flag, LanguageMenu, Tooltip, CategoryCell } from "@ax/components";
|
|
22
|
-
import { useAdaptiveText, usePermission } from "@ax/hooks";
|
|
21
|
+
|
|
22
|
+
import { useTheme } from "styled-components";
|
|
23
|
+
|
|
23
24
|
import { getPermission } from "./utils";
|
|
24
25
|
|
|
25
26
|
import * as S from "./style";
|
|
@@ -102,7 +103,6 @@ const StructuredDataItem = (props: IStructuredDataItemProps): JSX.Element => {
|
|
|
102
103
|
|
|
103
104
|
const duplicateItem = async () => {
|
|
104
105
|
resetForm();
|
|
105
|
-
setIsSavedData(false);
|
|
106
106
|
updateForm({ content: structuredData.content });
|
|
107
107
|
handleClick();
|
|
108
108
|
};
|
|
@@ -148,7 +148,12 @@ const StructuredDataItem = (props: IStructuredDataItemProps): JSX.Element => {
|
|
|
148
148
|
const isNew = currentLanguages.find((l) => l.id === language.id) ? false : true;
|
|
149
149
|
|
|
150
150
|
if (isNew) {
|
|
151
|
-
updateForm({
|
|
151
|
+
updateForm({
|
|
152
|
+
...structuredData,
|
|
153
|
+
id: null,
|
|
154
|
+
language: id,
|
|
155
|
+
canBeTranslated: true,
|
|
156
|
+
});
|
|
152
157
|
} else {
|
|
153
158
|
getDataContent(dataId, lang);
|
|
154
159
|
}
|