@griddo/ax 11.10.30 → 11.10.34-rc.1
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/PageConnectedField/PageConnectedField.test.tsx +85 -92
- package/src/components/Modal/style.tsx +0 -1
- package/src/containers/App/actions.tsx +1 -1
- package/src/hooks/forms.tsx +31 -21
- package/src/modules/Content/index.tsx +10 -1
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +38 -44
- package/src/modules/StructuredData/Form/index.tsx +21 -23
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "11.10.
|
|
4
|
+
"version": "11.10.34-rc.1",
|
|
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": "11975ec49f376d26b53ebd7c338b5eddd961bc5f"
|
|
221
221
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import configureStore from "redux-mock-store";
|
|
3
|
+
import thunk from "redux-thunk";
|
|
3
4
|
import { ThemeProvider } from "styled-components";
|
|
4
5
|
|
|
5
6
|
import { cleanup, fireEvent, render, screen } from "../../../../../../../config/jest/test-utils";
|
|
@@ -9,7 +10,7 @@ import PageConnectedField from "@ax/components/ConfigPanel/Form/ConnectedField/P
|
|
|
9
10
|
import { parseTheme } from "@ax/helpers";
|
|
10
11
|
import globalTheme from "@ax/themes/theme.json";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// Mocks configuration
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
15
16
|
cleanup();
|
|
@@ -18,7 +19,7 @@ beforeEach(() => {
|
|
|
18
19
|
jest.mock("axios");
|
|
19
20
|
const mockedAxios = axios as jest.MockedFunction<typeof axios>;
|
|
20
21
|
|
|
21
|
-
// Mock
|
|
22
|
+
// Mock @ax/services
|
|
22
23
|
jest.mock("@ax/services", () => ({
|
|
23
24
|
schemasService: {
|
|
24
25
|
isLoaded: jest.fn(() => true),
|
|
@@ -79,15 +80,54 @@ jest.mock("@ax/services", () => ({
|
|
|
79
80
|
},
|
|
80
81
|
}));
|
|
81
82
|
|
|
82
|
-
// Mock schemas
|
|
83
|
+
// Mock @ax/schemas
|
|
83
84
|
jest.mock("@ax/schemas", () => ({
|
|
84
85
|
getPageSchemas: jest.fn(() => ({})),
|
|
85
86
|
getFixedSchemas: jest.fn(() => ({})),
|
|
86
87
|
}));
|
|
87
88
|
|
|
89
|
+
// Mock @ax/containers/PageEditor
|
|
90
|
+
jest.mock("@ax/containers/PageEditor", () => ({
|
|
91
|
+
pageEditorActions: {
|
|
92
|
+
updateEditorContent: jest.fn((_editorID: number, _key: string | string[], _value: any) => {
|
|
93
|
+
return (dispatch: any, getState: any) => {
|
|
94
|
+
const state = getState();
|
|
95
|
+
dispatch({
|
|
96
|
+
type: "pageEditor/SET_EDITOR_CONTENT",
|
|
97
|
+
payload: { editorContent: state.pageEditor.editorContent || {} },
|
|
98
|
+
});
|
|
99
|
+
dispatch({
|
|
100
|
+
type: "pageEditor/SET_SELECTED_PAGE_CONTENT",
|
|
101
|
+
payload: { selectedContent: state.pageEditor.selectedContent || {} },
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
}),
|
|
105
|
+
overwriteHeaderConfig: jest.fn(() => ({
|
|
106
|
+
type: "pageEditor/OVERWRITE_HEADER_CONFIG",
|
|
107
|
+
payload: {},
|
|
108
|
+
})),
|
|
109
|
+
setCurrentPageName: jest.fn(() => ({
|
|
110
|
+
type: "pageEditor/SET_CURRENT_PAGE_NAME",
|
|
111
|
+
payload: {},
|
|
112
|
+
})),
|
|
113
|
+
getPageBreadcrumb: jest.fn(() => (dispatch: any) => {
|
|
114
|
+
dispatch({
|
|
115
|
+
type: "pageEditor/SET_BREADCRUMB",
|
|
116
|
+
payload: {},
|
|
117
|
+
});
|
|
118
|
+
}),
|
|
119
|
+
deleteError: jest.fn(() => ({
|
|
120
|
+
type: "pageEditor/DELETE_ERROR",
|
|
121
|
+
payload: {},
|
|
122
|
+
})),
|
|
123
|
+
},
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
// Configuración de datos (store & props)
|
|
127
|
+
|
|
88
128
|
const middlewares: any = [thunk];
|
|
89
129
|
const mockStore = configureStore(middlewares);
|
|
90
|
-
|
|
130
|
+
|
|
91
131
|
const initialStore = {
|
|
92
132
|
pageEditor: {
|
|
93
133
|
selectedContent: {
|
|
@@ -132,7 +172,9 @@ const initialStore = {
|
|
|
132
172
|
savedMenus: null,
|
|
133
173
|
},
|
|
134
174
|
navigationActions: {
|
|
135
|
-
|
|
175
|
+
// Aquí usamos un jest.fn() simple placeholder si es necesario,
|
|
176
|
+
// o podemos omitirlo si el componente no lo llama directamente desde el state
|
|
177
|
+
updateEditorContent: jest.fn(),
|
|
136
178
|
},
|
|
137
179
|
};
|
|
138
180
|
|
|
@@ -249,7 +291,6 @@ describe("PageConnectedField component rendering", () => {
|
|
|
249
291
|
{ store },
|
|
250
292
|
);
|
|
251
293
|
|
|
252
|
-
// Check that "Template Options" title is rendered
|
|
253
294
|
const templateTitles = screen.getAllByText("Template Options");
|
|
254
295
|
expect(templateTitles).toHaveLength(2); // Title and label
|
|
255
296
|
const titleElement = templateTitles[0]; // First one should be the title
|
|
@@ -257,9 +298,13 @@ describe("PageConnectedField component rendering", () => {
|
|
|
257
298
|
});
|
|
258
299
|
|
|
259
300
|
it("should call getPageBreadCrumb", () => {
|
|
260
|
-
|
|
261
|
-
const
|
|
301
|
+
// Clonamos para no mutar el original en otros tests si corren en paralelo/secuencia
|
|
302
|
+
const props = { ...defaultProps, field: { ...defaultProps.field, key: "parent" } };
|
|
303
|
+
|
|
304
|
+
const localStoreState = {
|
|
305
|
+
...initialStore,
|
|
262
306
|
pageEditor: {
|
|
307
|
+
...initialStore.pageEditor,
|
|
263
308
|
selectedEditorID: 1,
|
|
264
309
|
selectedContent: {
|
|
265
310
|
type: "TextField",
|
|
@@ -272,29 +317,13 @@ describe("PageConnectedField component rendering", () => {
|
|
|
272
317
|
headerConfig: {},
|
|
273
318
|
},
|
|
274
319
|
},
|
|
275
|
-
errors: [{}],
|
|
276
|
-
},
|
|
277
|
-
sites: {
|
|
278
|
-
currentSitePages: [
|
|
279
|
-
{
|
|
280
|
-
editorID: 1,
|
|
281
|
-
},
|
|
282
|
-
],
|
|
283
|
-
themeElements: null,
|
|
284
|
-
},
|
|
285
|
-
app: {
|
|
286
|
-
lang: { locale: "es-ES", id: 0 },
|
|
287
|
-
},
|
|
288
|
-
dataPacks: {
|
|
289
|
-
templates: [{ id: "default" }, { id: "BasicTemplate" }],
|
|
290
|
-
activated: [],
|
|
291
320
|
},
|
|
292
321
|
};
|
|
293
322
|
|
|
294
|
-
const store = mockStore(
|
|
323
|
+
const store = mockStore(localStoreState);
|
|
295
324
|
const component = (
|
|
296
325
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
297
|
-
<PageConnectedField {...
|
|
326
|
+
<PageConnectedField {...props} />
|
|
298
327
|
</ThemeProvider>
|
|
299
328
|
);
|
|
300
329
|
|
|
@@ -304,16 +333,19 @@ describe("PageConnectedField component rendering", () => {
|
|
|
304
333
|
const inputComponent = screen.getByTestId<HTMLInputElement>("input-component");
|
|
305
334
|
expect(inputComponent).toBeTruthy();
|
|
306
335
|
fireEvent.change(inputComponent, { target: { value: "value" } });
|
|
336
|
+
|
|
307
337
|
expect(store.getActions()).toContainEqual({
|
|
308
|
-
payload: { editorContent:
|
|
338
|
+
payload: { editorContent: localStoreState.pageEditor.editorContent },
|
|
309
339
|
type: "pageEditor/SET_EDITOR_CONTENT",
|
|
310
340
|
});
|
|
311
341
|
});
|
|
312
342
|
|
|
313
343
|
it("should call updateEditorContent if key is title and isPageTitle", () => {
|
|
314
|
-
defaultProps.field
|
|
315
|
-
const
|
|
344
|
+
const props = { ...defaultProps, field: { ...defaultProps.field, key: "title" } };
|
|
345
|
+
const localStoreState = {
|
|
346
|
+
...initialStore,
|
|
316
347
|
pageEditor: {
|
|
348
|
+
...initialStore.pageEditor,
|
|
317
349
|
selectedEditorID: 1,
|
|
318
350
|
selectedContent: {
|
|
319
351
|
type: "TextField",
|
|
@@ -326,48 +358,33 @@ describe("PageConnectedField component rendering", () => {
|
|
|
326
358
|
headerConfig: {},
|
|
327
359
|
},
|
|
328
360
|
},
|
|
329
|
-
errors: [{}],
|
|
330
|
-
},
|
|
331
|
-
sites: {
|
|
332
|
-
currentSitePages: [
|
|
333
|
-
{
|
|
334
|
-
editorID: 1,
|
|
335
|
-
},
|
|
336
|
-
],
|
|
337
|
-
themeElements: null,
|
|
338
|
-
},
|
|
339
|
-
app: {
|
|
340
|
-
lang: { locale: "es-ES", id: 0 },
|
|
341
|
-
},
|
|
342
|
-
dataPacks: {
|
|
343
|
-
templates: [{ id: "default" }, { id: "BasicTemplate" }],
|
|
344
|
-
activated: [],
|
|
345
361
|
},
|
|
346
362
|
};
|
|
347
363
|
|
|
348
|
-
const store = mockStore(
|
|
364
|
+
const store = mockStore(localStoreState);
|
|
349
365
|
const component = (
|
|
350
366
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
351
|
-
<PageConnectedField {...
|
|
367
|
+
<PageConnectedField {...props} />
|
|
352
368
|
</ThemeProvider>
|
|
353
369
|
);
|
|
354
370
|
|
|
355
371
|
render(component, { store });
|
|
356
372
|
|
|
357
|
-
// It's loading Field -> FieldContainer with a TextField
|
|
358
373
|
const inputComponent = screen.getByTestId<HTMLInputElement>("input-component");
|
|
359
374
|
expect(inputComponent).toBeTruthy();
|
|
360
375
|
fireEvent.change(inputComponent, { target: { value: "value" } });
|
|
361
376
|
expect(store.getActions()).toContainEqual({
|
|
362
|
-
payload: { editorContent:
|
|
377
|
+
payload: { editorContent: localStoreState.pageEditor.editorContent },
|
|
363
378
|
type: "pageEditor/SET_EDITOR_CONTENT",
|
|
364
379
|
});
|
|
365
380
|
});
|
|
366
381
|
|
|
367
382
|
it("should call updateEditorContent if key is file and isComponentImage", () => {
|
|
368
|
-
defaultProps.field
|
|
369
|
-
const
|
|
383
|
+
const props = { ...defaultProps, field: { ...defaultProps.field, key: "file" } };
|
|
384
|
+
const localStoreState = {
|
|
385
|
+
...initialStore,
|
|
370
386
|
pageEditor: {
|
|
387
|
+
...initialStore.pageEditor,
|
|
371
388
|
selectedEditorID: 1,
|
|
372
389
|
selectedContent: {
|
|
373
390
|
type: "TextField",
|
|
@@ -380,29 +397,13 @@ describe("PageConnectedField component rendering", () => {
|
|
|
380
397
|
headerConfig: {},
|
|
381
398
|
},
|
|
382
399
|
},
|
|
383
|
-
errors: [{}],
|
|
384
|
-
},
|
|
385
|
-
sites: {
|
|
386
|
-
currentSitePages: [
|
|
387
|
-
{
|
|
388
|
-
editorID: 1,
|
|
389
|
-
},
|
|
390
|
-
],
|
|
391
|
-
themeElements: null,
|
|
392
|
-
},
|
|
393
|
-
app: {
|
|
394
|
-
lang: { locale: "es-ES", id: 0 },
|
|
395
|
-
},
|
|
396
|
-
dataPacks: {
|
|
397
|
-
templates: [{ id: "default" }, { id: "BasicTemplate" }],
|
|
398
|
-
activated: [],
|
|
399
400
|
},
|
|
400
401
|
};
|
|
401
402
|
|
|
402
|
-
const store = mockStore(
|
|
403
|
+
const store = mockStore(localStoreState);
|
|
403
404
|
const component = (
|
|
404
405
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
405
|
-
<PageConnectedField {...
|
|
406
|
+
<PageConnectedField {...props} />
|
|
406
407
|
</ThemeProvider>
|
|
407
408
|
);
|
|
408
409
|
|
|
@@ -413,17 +414,25 @@ describe("PageConnectedField component rendering", () => {
|
|
|
413
414
|
expect(inputComponent).toBeTruthy();
|
|
414
415
|
fireEvent.change(inputComponent, { target: { value: "value" } });
|
|
415
416
|
expect(store.getActions()).toContainEqual({
|
|
416
|
-
payload: { editorContent:
|
|
417
|
+
payload: { editorContent: localStoreState.pageEditor.editorContent },
|
|
417
418
|
type: "pageEditor/SET_EDITOR_CONTENT",
|
|
418
419
|
});
|
|
419
420
|
});
|
|
420
421
|
|
|
421
422
|
it("should call updateEditorContent if key is slugTo is present", () => {
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
const props = {
|
|
424
|
+
...defaultProps,
|
|
425
|
+
field: {
|
|
426
|
+
...defaultProps.field,
|
|
427
|
+
key: "file",
|
|
428
|
+
slugTo: "value",
|
|
429
|
+
},
|
|
430
|
+
};
|
|
424
431
|
|
|
425
|
-
const
|
|
432
|
+
const localStoreState = {
|
|
433
|
+
...initialStore,
|
|
426
434
|
pageEditor: {
|
|
435
|
+
...initialStore.pageEditor,
|
|
427
436
|
selectedEditorID: 1,
|
|
428
437
|
selectedContent: {
|
|
429
438
|
type: "TextField",
|
|
@@ -436,29 +445,13 @@ describe("PageConnectedField component rendering", () => {
|
|
|
436
445
|
headerConfig: {},
|
|
437
446
|
},
|
|
438
447
|
},
|
|
439
|
-
errors: [{}],
|
|
440
|
-
},
|
|
441
|
-
sites: {
|
|
442
|
-
currentSitePages: [
|
|
443
|
-
{
|
|
444
|
-
editorID: 1,
|
|
445
|
-
},
|
|
446
|
-
],
|
|
447
|
-
themeElements: null,
|
|
448
|
-
},
|
|
449
|
-
app: {
|
|
450
|
-
lang: { locale: "es-ES", id: 0 },
|
|
451
|
-
},
|
|
452
|
-
dataPacks: {
|
|
453
|
-
templates: [{ id: "default" }, { id: "BasicTemplate" }],
|
|
454
|
-
activated: [],
|
|
455
448
|
},
|
|
456
449
|
};
|
|
457
450
|
|
|
458
|
-
const store = mockStore(
|
|
451
|
+
const store = mockStore(localStoreState);
|
|
459
452
|
const component = (
|
|
460
453
|
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
461
|
-
<PageConnectedField {...
|
|
454
|
+
<PageConnectedField {...props} />
|
|
462
455
|
</ThemeProvider>
|
|
463
456
|
);
|
|
464
457
|
|
|
@@ -469,7 +462,7 @@ describe("PageConnectedField component rendering", () => {
|
|
|
469
462
|
expect(inputComponent).toBeTruthy();
|
|
470
463
|
fireEvent.change(inputComponent, { target: { value: "value" } });
|
|
471
464
|
expect(store.getActions()).toContainEqual({
|
|
472
|
-
payload: { editorContent:
|
|
465
|
+
payload: { editorContent: localStoreState.pageEditor.editorContent },
|
|
473
466
|
type: "pageEditor/SET_EDITOR_CONTENT",
|
|
474
467
|
});
|
|
475
468
|
});
|
|
@@ -66,7 +66,7 @@ function setHistoryPush(path: string, isEditor?: boolean): (dispatch: Dispatch)
|
|
|
66
66
|
if (isEditor) {
|
|
67
67
|
resetStateValues(dispatch);
|
|
68
68
|
}
|
|
69
|
-
history.push(path);
|
|
69
|
+
history.push(path, isEditor ? { isFromEditor: true } : undefined);
|
|
70
70
|
} catch (e) {
|
|
71
71
|
console.log("Error", e);
|
|
72
72
|
}
|
package/src/hooks/forms.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import isEqual from "lodash.isequal";
|
|
1
|
+
import { memo, useEffect, useRef, useState } from "react";
|
|
3
2
|
|
|
4
|
-
import { deepClone } from "@ax/helpers";
|
|
5
3
|
import { cleanPageValues, getIsSavedData } from "@ax/forms";
|
|
6
|
-
import {
|
|
4
|
+
import { deepClone, isEmptyObj } from "@ax/helpers";
|
|
5
|
+
import type { FormContent, IUser } from "@ax/types";
|
|
6
|
+
|
|
7
|
+
import isEqual from "lodash.isequal";
|
|
7
8
|
|
|
8
9
|
const useDebounce = (value: any) => {
|
|
9
10
|
// State and setters for debounced value
|
|
@@ -26,28 +27,38 @@ const useDebounce = (value: any) => {
|
|
|
26
27
|
const useEqualStructured = (component: any) => {
|
|
27
28
|
return memo(component, (prevProps: any, newProps: any) => {
|
|
28
29
|
const { fieldKey } = prevProps;
|
|
29
|
-
const prevValue = prevProps.form.content
|
|
30
|
+
const prevValue = prevProps.form.content?.[fieldKey];
|
|
30
31
|
const newValue = newProps.form.content[fieldKey];
|
|
31
32
|
|
|
32
33
|
return prevValue === newValue;
|
|
33
34
|
});
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Stores the provided value (object or string) and returns the previous value,
|
|
39
|
+
* updating it only when a "saved" state is detected (either from the external state
|
|
40
|
+
* manager, or explicitly via the second argument). Useful for detecting changes
|
|
41
|
+
* and implementing dirty checks.
|
|
42
|
+
*
|
|
43
|
+
* @param value - The current value to track.
|
|
44
|
+
* @param isSaved - Optional flag to indicate if the value has been saved/reset externally.
|
|
45
|
+
* @returns The previously saved value, or undefined for first render.
|
|
46
|
+
*/
|
|
47
|
+
const usePrevious = (value: Record<string, unknown> | string, isSaved?: boolean) => {
|
|
48
|
+
const valueStr = value && JSON.stringify(value);
|
|
49
|
+
const ref = useRef<Record<string, unknown> | string | undefined>(undefined);
|
|
39
50
|
const isSavedData = getIsSavedData();
|
|
40
51
|
|
|
41
52
|
useEffect(() => {
|
|
42
|
-
if (!ref.current) {
|
|
43
|
-
ref.current =
|
|
53
|
+
if (!ref.current || isEmptyObj(ref.current)) {
|
|
54
|
+
ref.current = valueStr && JSON.parse(valueStr);
|
|
44
55
|
}
|
|
45
|
-
}, [
|
|
56
|
+
}, [valueStr]);
|
|
46
57
|
|
|
47
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO
|
|
58
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix this
|
|
48
59
|
useEffect(() => {
|
|
49
60
|
if (isSavedData || isSaved) {
|
|
50
|
-
ref.current =
|
|
61
|
+
ref.current = valueStr && JSON.parse(valueStr);
|
|
51
62
|
}
|
|
52
63
|
}, [isSavedData, isSaved]);
|
|
53
64
|
|
|
@@ -62,14 +73,12 @@ const useIsDirty = (
|
|
|
62
73
|
const [isSaved, setIsSaved] = useState(false);
|
|
63
74
|
const [isResetting, setIsResetting] = useState(false);
|
|
64
75
|
|
|
65
|
-
const updatedValuesStr = JSON.stringify(updatedValues);
|
|
66
|
-
|
|
67
76
|
const prevContent = usePrevious(updatedValues, isSaved);
|
|
68
77
|
|
|
69
78
|
const hasChanged = (): boolean => {
|
|
70
|
-
if (prevContent &&
|
|
71
|
-
const updatedValuesCloned = updatedValuesStr && deepClone(updatedValuesStr);
|
|
79
|
+
if (prevContent && updatedValues) {
|
|
72
80
|
const originalValuesCloned = prevContent && deepClone(prevContent);
|
|
81
|
+
const updatedValuesCloned = updatedValues && deepClone(updatedValues);
|
|
73
82
|
|
|
74
83
|
const { cleanUpdatedValues, cleanOriginalValues } = cleanPageValues(updatedValuesCloned, originalValuesCloned);
|
|
75
84
|
|
|
@@ -95,7 +104,7 @@ const useIsDirty = (
|
|
|
95
104
|
|
|
96
105
|
if (isNew) {
|
|
97
106
|
setIsDirty(false);
|
|
98
|
-
} else if (prevContent &&
|
|
107
|
+
} else if (prevContent && updatedValues) {
|
|
99
108
|
const isUpdated = hasChanged();
|
|
100
109
|
|
|
101
110
|
if (isUpdated) {
|
|
@@ -105,15 +114,15 @@ const useIsDirty = (
|
|
|
105
114
|
setIsDirty(false);
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
|
-
}, [
|
|
117
|
+
}, [updatedValues]);
|
|
109
118
|
|
|
110
119
|
return { isDirty, setIsDirty, resetDirty };
|
|
111
120
|
};
|
|
112
121
|
|
|
113
122
|
const cleanModified = (updatedValues: any, originalValues: any) => {
|
|
114
|
-
delete updatedValues
|
|
123
|
+
delete updatedValues.modified;
|
|
115
124
|
|
|
116
|
-
delete originalValues
|
|
125
|
+
delete originalValues.modified;
|
|
117
126
|
|
|
118
127
|
return {
|
|
119
128
|
cleanUpdatedValues: updatedValues,
|
|
@@ -127,6 +136,7 @@ const useShouldBeSaved = (form: Record<string, unknown> | IUser | FormContent) =
|
|
|
127
136
|
|
|
128
137
|
const stringValue = form && JSON.stringify(form);
|
|
129
138
|
|
|
139
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix this
|
|
130
140
|
useEffect(() => {
|
|
131
141
|
if (!formRef.current) {
|
|
132
142
|
formRef.current = stringValue && JSON.parse(stringValue);
|
|
@@ -321,7 +321,16 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
321
321
|
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
322
322
|
useLayoutEffect(() => {
|
|
323
323
|
checkUserSession();
|
|
324
|
-
|
|
324
|
+
const isFromEditor = locationState?.isFromEditor === true;
|
|
325
|
+
const hasFilterSet = filter && filter !== "unique-pages";
|
|
326
|
+
// Only reset filter if:
|
|
327
|
+
// - Not a POP navigation (back button) - preserve filter when going back
|
|
328
|
+
// - Not coming from editor - preserve filter when returning from editor
|
|
329
|
+
// - If POP without filter set and not from editor, reset to default
|
|
330
|
+
if (history.action !== "POP" && !isFromEditor) {
|
|
331
|
+
setFilter("unique-pages");
|
|
332
|
+
} else if (history.action === "POP" && !hasFilterSet && !isFromEditor) {
|
|
333
|
+
// Only reset on POP if no filter was set and not coming from editor
|
|
325
334
|
setFilter("unique-pages");
|
|
326
335
|
}
|
|
327
336
|
resetPageEditor();
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { FieldsBehavior, Modal, Tag } from "@ax/components";
|
|
5
|
+
import { dataPacksActions } from "@ax/containers/Settings";
|
|
6
|
+
import { useModal } from "@ax/hooks";
|
|
7
|
+
import type {
|
|
5
8
|
IDataPack,
|
|
6
9
|
IDataPackConfigImport,
|
|
7
10
|
IDataPackConfigImportCategory,
|
|
@@ -10,11 +13,9 @@ import {
|
|
|
10
13
|
IStructuredData,
|
|
11
14
|
ITemplate,
|
|
12
15
|
} from "@ax/types";
|
|
13
|
-
import { dataPacksActions } from "@ax/containers/Settings";
|
|
14
|
-
import { FieldsBehavior, Modal, Tag } from "@ax/components";
|
|
15
|
-
import { useModal } from "@ax/hooks";
|
|
16
16
|
|
|
17
17
|
import TemplateConfig from "./TemplateConfig";
|
|
18
|
+
|
|
18
19
|
import * as S from "./style";
|
|
19
20
|
|
|
20
21
|
const Form = (props: IProps): JSX.Element => {
|
|
@@ -34,11 +35,10 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
34
35
|
const value: Record<string, ICategoryValue[]> = {};
|
|
35
36
|
if (!importValue) return value;
|
|
36
37
|
importValue.forEach((item: IDataPackConfigImport) => {
|
|
37
|
-
item.categories
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
38
|
+
item.categories?.forEach((cat: IDataPackConfigImportCategory) => {
|
|
39
|
+
const catObj = { value: cat.id, title: cat.title, structuredData: cat.structuredData };
|
|
40
|
+
value[cat.structuredData] = value[cat.structuredData] ? [...value[cat.structuredData], catObj] : [catObj];
|
|
41
|
+
});
|
|
42
42
|
});
|
|
43
43
|
return value;
|
|
44
44
|
};
|
|
@@ -48,12 +48,12 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
48
48
|
const { isOpen, toggleModal } = useModal();
|
|
49
49
|
|
|
50
50
|
const selectOptions: { value: string; label: string }[] = [];
|
|
51
|
-
templates.forEach((template: ITemplate) =>
|
|
51
|
+
templates.forEach((template: ITemplate) => {
|
|
52
52
|
selectOptions.push({
|
|
53
53
|
value: template.id,
|
|
54
54
|
label: template.title,
|
|
55
|
-
})
|
|
56
|
-
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
57
|
|
|
58
58
|
const indexDefaultOptions = [
|
|
59
59
|
{
|
|
@@ -100,18 +100,15 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
const getIsGlobal = (id: string): boolean => {
|
|
103
|
-
const data = allStructuredData
|
|
104
|
-
return data
|
|
103
|
+
const data = allStructuredData?.site.find((elem) => elem.id === id);
|
|
104
|
+
return !data?.local;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
const globalPageData = structuredData.find((data) => data.fromPage && !data.local);
|
|
108
108
|
|
|
109
|
-
const elements =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
globalPageData.relatedCategories.map((cat) => {
|
|
113
|
-
return { source: cat, key: cat, placeholder: getCategoryText(cat), isGlobal: getIsGlobal(cat) };
|
|
114
|
-
});
|
|
109
|
+
const elements = globalPageData?.relatedCategories?.map((cat) => {
|
|
110
|
+
return { source: cat, key: cat, placeholder: getCategoryText(cat), isGlobal: getIsGlobal(cat) };
|
|
111
|
+
});
|
|
115
112
|
|
|
116
113
|
const deleteTag = (catID: number) => {
|
|
117
114
|
const newCategories = configFormData.import.map((type: IDataPackConfigImport) => {
|
|
@@ -124,24 +121,19 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
124
121
|
|
|
125
122
|
const getCategoryLabels = () => (
|
|
126
123
|
<S.CategoriesWrapper isEmpty={!Object.keys(selectedCategories).length}>
|
|
127
|
-
{configFormData
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
{configFormData?.import?.map((data: IDataPackConfigImport) => {
|
|
125
|
+
return data.categories?.map((category: IDataPackConfigImportCategory) => {
|
|
126
|
+
const categoryText = getCategoryText(category.structuredData);
|
|
127
|
+
const handleDeleteTag = () => deleteTag(category.id);
|
|
130
128
|
return (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<Tag
|
|
137
|
-
key={`${categoryText}-${category.title}`}
|
|
138
|
-
text={`${categoryText}: ${category.title}`}
|
|
139
|
-
onDeleteAction={handleDeleteTag}
|
|
140
|
-
/>
|
|
141
|
-
);
|
|
142
|
-
})
|
|
129
|
+
<Tag
|
|
130
|
+
key={`${categoryText}-${category.title}`}
|
|
131
|
+
text={`${categoryText}: ${category.title}`}
|
|
132
|
+
onDeleteAction={handleDeleteTag}
|
|
133
|
+
/>
|
|
143
134
|
);
|
|
144
|
-
})
|
|
135
|
+
});
|
|
136
|
+
})}
|
|
145
137
|
</S.CategoriesWrapper>
|
|
146
138
|
);
|
|
147
139
|
|
|
@@ -156,10 +148,12 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
156
148
|
If you want to get any Global {selected.title} Pages on this site automatically, you can do so by
|
|
157
149
|
selecting the respective categories.
|
|
158
150
|
</S.Text>
|
|
159
|
-
{configFormData
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
151
|
+
{configFormData?.import && getCategoryLabels()}
|
|
152
|
+
{elements && elements.length > 0 ? (
|
|
153
|
+
<S.StyledButton type="button" buttonStyle="text" icon="addCircle" onClick={toggleModal}>
|
|
154
|
+
Add Category
|
|
155
|
+
</S.StyledButton>
|
|
156
|
+
) : null}
|
|
163
157
|
</S.SubscribeWrapper>
|
|
164
158
|
</S.SectionContent>
|
|
165
159
|
)}
|
|
@@ -171,7 +165,7 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
171
165
|
fieldType="AsyncSelect"
|
|
172
166
|
site={currentSite}
|
|
173
167
|
entity={"pages"}
|
|
174
|
-
value={configFormData
|
|
168
|
+
value={configFormData?.defaultParent}
|
|
175
169
|
onChange={setDefaultParent}
|
|
176
170
|
options={{ excludeDetailPages: true }}
|
|
177
171
|
/>
|
|
@@ -179,14 +173,14 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
179
173
|
title="Modifiable in page"
|
|
180
174
|
name="defaultParent"
|
|
181
175
|
fieldType="ToggleField"
|
|
182
|
-
value={
|
|
176
|
+
value={configFormData?.modifiableOnPage || false}
|
|
183
177
|
onChange={setModifiableOnPage}
|
|
184
178
|
/>
|
|
185
179
|
<FieldsBehavior
|
|
186
180
|
title="Meta robots index default"
|
|
187
181
|
name="indexDefault"
|
|
188
182
|
fieldType="RadioGroup"
|
|
189
|
-
value={
|
|
183
|
+
value={configFormData?.indexDefault || false}
|
|
190
184
|
options={indexDefaultOptions}
|
|
191
185
|
onChange={setIndexDefault}
|
|
192
186
|
/>
|
|
@@ -92,7 +92,7 @@ const Form = (props: IProps) => {
|
|
|
92
92
|
const isDeleted = !!form?.deleted;
|
|
93
93
|
const isDisabled = isDeleted || disabledDatapack;
|
|
94
94
|
|
|
95
|
-
const isDataTranslatable = currentStructuredData
|
|
95
|
+
const isDataTranslatable = currentStructuredData?.translate;
|
|
96
96
|
const isScheduled = !!form && !!form.publicationScheduled;
|
|
97
97
|
const status = isScheduled
|
|
98
98
|
? "scheduled"
|
|
@@ -117,27 +117,25 @@ const Form = (props: IProps) => {
|
|
|
117
117
|
}
|
|
118
118
|
}, []);
|
|
119
119
|
|
|
120
|
-
const Fields =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
);
|
|
140
|
-
});
|
|
120
|
+
const Fields = fieldsTranslate
|
|
121
|
+
?.filter((field: ISchemaField) => !field.hidden)
|
|
122
|
+
.map((field: ISchemaField, i: number) => {
|
|
123
|
+
const { type, key } = field;
|
|
124
|
+
if (key === "title") {
|
|
125
|
+
title = form?.content?.[key] ? form.content[key] : "";
|
|
126
|
+
}
|
|
127
|
+
return (
|
|
128
|
+
<ConnectedField
|
|
129
|
+
fieldKey={key}
|
|
130
|
+
field={field}
|
|
131
|
+
key={`${type}${i}`}
|
|
132
|
+
site={site}
|
|
133
|
+
disabled={isDisabled}
|
|
134
|
+
theme={site ? site.theme : theme}
|
|
135
|
+
languages={languages}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
141
139
|
|
|
142
140
|
const handleSave = async (publish: boolean, scheduleDate?: string | null) => {
|
|
143
141
|
const validated = publish && !skipReviewOnPublish ? await validateForm(true) : true;
|
|
@@ -195,7 +193,7 @@ const Form = (props: IProps) => {
|
|
|
195
193
|
|
|
196
194
|
const availableLanguages = isDisabled ? currentLanguages : languages;
|
|
197
195
|
|
|
198
|
-
const setRoute = (path: string) => setHistoryPush(path);
|
|
196
|
+
const setRoute = (path: string) => setHistoryPush(path, true);
|
|
199
197
|
|
|
200
198
|
const modalText = (
|
|
201
199
|
<>
|