@griddo/ax 11.12.0 → 11.12.1-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/config/jest/componentsMock.js +7 -5
- package/package.json +2 -2
- package/src/__tests__/components/Browser/Browser.test.tsx +438 -87
- package/src/__tests__/components/Browser/Browser.utils.test.ts +55 -0
- package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +1 -3
- package/src/__tests__/components/Fields/Button/Button.test.tsx +29 -27
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +158 -0
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorsBanner.test.tsx +90 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.test.tsx +178 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +150 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordItem/KeywordItem.test.tsx +91 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +122 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.utils.test.ts +15 -0
- package/src/__tests__/components/KeywordsPreviewModal/atoms.test.tsx +101 -0
- package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +1 -1
- package/src/__tests__/modules/FramePreview/FramePreview.test.tsx +318 -0
- package/src/__tests__/modules/FramePreview/FramePreview.utils.test.ts +242 -0
- package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +185 -0
- package/src/components/Browser/index.tsx +294 -149
- package/src/components/Browser/style.tsx +75 -6
- package/src/components/Browser/utils.tsx +13 -0
- package/src/components/Button/index.tsx +2 -1
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +2 -4
- package/src/components/Fields/AsyncSelect/style.tsx +13 -0
- package/src/components/Fields/FieldGroup/index.tsx +5 -2
- package/src/components/Fields/FieldGroup/style.tsx +32 -7
- package/src/components/Fields/HeadingField/index.tsx +22 -22
- package/src/components/Fields/HiddenField/style.tsx +1 -1
- package/src/components/Fields/NumberField/index.tsx +15 -16
- package/src/components/Fields/NumberField/style.tsx +2 -0
- package/src/components/Fields/ReferenceField/index.tsx +1 -1
- package/src/components/Fields/SEOPreview/index.tsx +36 -0
- package/src/components/Fields/SEOPreview/style.tsx +24 -0
- package/src/components/Fields/Select/index.tsx +5 -1
- package/src/components/Fields/Select/style.tsx +56 -0
- package/src/components/Fields/SummaryButton/index.tsx +18 -9
- package/src/components/Fields/SummaryButton/style.tsx +1 -2
- package/src/components/Fields/TagsField/index.tsx +8 -9
- package/src/components/Fields/UrlField/index.tsx +26 -27
- package/src/components/Fields/index.tsx +2 -0
- package/src/components/FloatingNote/index.tsx +35 -0
- package/src/components/FloatingNote/style.tsx +26 -0
- package/src/components/FloatingPanel/index.tsx +5 -2
- package/src/components/FloatingPanel/style.tsx +2 -1
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +85 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/style.tsx +80 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +57 -0
- package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +82 -0
- package/src/components/HeadingsPreviewModal/HeadingItem/index.tsx +71 -0
- package/src/components/HeadingsPreviewModal/HeadingItem/style.tsx +77 -0
- package/src/components/HeadingsPreviewModal/index.tsx +148 -0
- package/src/components/HeadingsPreviewModal/style.tsx +82 -0
- package/src/components/HeadingsPreviewModal/utils.tsx +329 -0
- package/src/components/Icon/index.tsx +1 -2
- package/src/components/IconAction/index.tsx +1 -1
- package/src/components/KeywordsPreviewModal/KeywordItem/index.tsx +46 -0
- package/src/components/KeywordsPreviewModal/KeywordItem/style.tsx +64 -0
- package/src/components/KeywordsPreviewModal/atoms.tsx +96 -0
- package/src/components/KeywordsPreviewModal/index.tsx +99 -0
- package/src/components/KeywordsPreviewModal/style.tsx +87 -0
- package/src/components/KeywordsPreviewModal/utils.tsx +22 -0
- package/src/components/MainWrapper/AppBar/index.tsx +8 -1
- package/src/components/MainWrapper/index.tsx +7 -1
- package/src/components/Notification/index.tsx +2 -2
- package/src/components/PageFinder/index.tsx +1 -1
- package/src/components/ResizePanel/index.tsx +4 -3
- package/src/components/ResizePanel/style.tsx +1 -1
- package/src/components/SearchField/style.tsx +2 -2
- package/src/components/SideModal/index.tsx +2 -1
- package/src/components/Tabs/index.tsx +13 -4
- package/src/components/Tabs/style.tsx +7 -8
- package/src/components/Toast/index.tsx +4 -2
- package/src/components/Tooltip/index.tsx +4 -3
- package/src/components/index.tsx +8 -0
- package/src/forms/fields.tsx +70 -68
- package/src/hooks/forms.tsx +22 -1
- package/src/hooks/index.tsx +13 -3
- package/src/hooks/modals.tsx +103 -15
- package/src/hooks/users.tsx +25 -8
- package/src/modules/Forms/atoms.tsx +2 -2
- package/src/modules/FramePreview/HeadingsOverlay/index.tsx +116 -0
- package/src/modules/FramePreview/HeadingsOverlay/style.tsx +34 -0
- package/src/modules/FramePreview/index.tsx +55 -16
- package/src/modules/FramePreview/style.tsx +34 -2
- package/src/modules/FramePreview/utils.tsx +140 -0
- package/src/modules/GlobalEditor/Editor/index.tsx +37 -3
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +19 -2
- package/src/modules/GlobalEditor/Preview/index.tsx +0 -2
- package/src/modules/GlobalEditor/Preview/style.tsx +1 -1
- package/src/modules/GlobalEditor/index.tsx +119 -57
- package/src/modules/PageEditor/Editor/index.tsx +33 -2
- package/src/modules/PageEditor/PageBrowser/index.tsx +20 -2
- package/src/modules/PageEditor/Preview/index.tsx +0 -2
- package/src/modules/PageEditor/Preview/style.tsx +1 -1
- package/src/modules/PageEditor/atoms.tsx +1 -1
- package/src/modules/PageEditor/index.tsx +130 -66
- package/src/modules/PublicPreview/index.tsx +5 -2
- package/src/schemas/pages/GlobalPage.ts +87 -70
- package/src/schemas/pages/Page.ts +87 -70
- package/src/types/index.tsx +12 -0
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
CancelScheduleModal,
|
|
8
8
|
ErrorPage,
|
|
9
9
|
ErrorToast,
|
|
10
|
+
HeadingsPreviewModal,
|
|
11
|
+
KeywordsPreviewModal,
|
|
10
12
|
Loading,
|
|
11
13
|
MainWrapper,
|
|
12
14
|
Modal,
|
|
@@ -23,8 +25,9 @@ import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
|
23
25
|
import { usersActions } from "@ax/containers/Users";
|
|
24
26
|
import { RouteLeavingGuard } from "@ax/guards";
|
|
25
27
|
import { dateToString, getDefaultTheme } from "@ax/helpers";
|
|
26
|
-
import { useIsDirty,
|
|
28
|
+
import { useIsDirty, useModals, usePermissions } from "@ax/hooks";
|
|
27
29
|
import type {
|
|
30
|
+
HeadingFilter,
|
|
28
31
|
IErrorItem,
|
|
29
32
|
ILanguage,
|
|
30
33
|
INotification,
|
|
@@ -67,35 +70,46 @@ const GlobalEditor = (props: IProps) => {
|
|
|
67
70
|
getUserCurrentPermissions,
|
|
68
71
|
schedulePublication,
|
|
69
72
|
restorePage,
|
|
73
|
+
updateEditorContent,
|
|
70
74
|
schemaVersion,
|
|
71
75
|
} = props;
|
|
72
76
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const isAllowedTo = usePermissions({
|
|
78
|
+
publishPages: "global.globalData.publishUnpublishAllGlobalData",
|
|
79
|
+
createPages: "global.globalData.createAllGlobalData",
|
|
80
|
+
deletePages: "global.globalData.deleteAllGlobalData",
|
|
81
|
+
editContentPages: "global.globalData.editAllGlobalData",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const defaultTab = isAllowedTo.editContentPages ? "edit" : "view";
|
|
79
85
|
|
|
80
|
-
const { isOpen, toggleModal } = useModal();
|
|
81
|
-
const { isOpen: isUnpublishOpen, toggleModal: toggleUnpublishModal } = useModal();
|
|
82
86
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
|
83
87
|
const [selectedTab, setSelectedTab] = useState(defaultTab);
|
|
84
88
|
const [notification, setNotification] = useState<INotification | null>(null);
|
|
85
89
|
const [toastMsg, setToastMsg] = useState<string | null>(null);
|
|
86
|
-
const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
|
|
87
90
|
const [errorPagesChecked, setErrorPagesChecked] = useState(false);
|
|
91
|
+
const [headingsFilter, setHeadingsFilter] = useState<HeadingFilter>("all");
|
|
92
|
+
const [keywordsFilter, setKeywordsFilter] = useState<string[]>([]);
|
|
88
93
|
const [scheduleDate, setScheduleDate] = useState({
|
|
89
94
|
date: dateToString(new Date(), "yyy/MM/dd"),
|
|
90
95
|
time: "12:00 am",
|
|
91
96
|
});
|
|
92
|
-
const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
|
|
93
|
-
const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
|
|
94
|
-
const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
|
|
95
|
-
const { isOpen: isDirtyNavigateOpen, toggleModal: toggleDirtyNavigateModal } = useModal();
|
|
96
97
|
const [pendingNavigateCallback, setPendingNavigateCallback] = useState<(() => void) | null>(null);
|
|
97
98
|
const browserRef = useRef<HTMLDivElement>(null);
|
|
98
99
|
|
|
100
|
+
const { isOpen, toggleModal } = useModals([
|
|
101
|
+
"userEditing",
|
|
102
|
+
"unpublish",
|
|
103
|
+
"schedule",
|
|
104
|
+
"cancelSchedule",
|
|
105
|
+
"restore",
|
|
106
|
+
"headingsPreview",
|
|
107
|
+
"keywordsPreview",
|
|
108
|
+
"dirtyNavigate",
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
|
|
112
|
+
|
|
99
113
|
const isPublished = props.pageStatus === pageStatus.PUBLISHED || props.pageStatus === pageStatus.UPLOAD_PENDING;
|
|
100
114
|
const isDraft = props.pageStatus === pageStatus.MODIFIED || !!editorContent.draftFromPage;
|
|
101
115
|
const hasDraft: boolean = editorContent && !!editorContent.haveDraftPage;
|
|
@@ -104,6 +118,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
104
118
|
const isEditLive = isPublished && hasDraft;
|
|
105
119
|
const isScheduled = props.pageStatus === pageStatus.SCHEDULED;
|
|
106
120
|
const isDeleted = editorContent.deleted;
|
|
121
|
+
const defaultContentTab = "content";
|
|
107
122
|
|
|
108
123
|
const errorNotificationText =
|
|
109
124
|
"There are some errors on the page so you can not publish yet. Please review them in the error panel.";
|
|
@@ -122,12 +137,11 @@ const GlobalEditor = (props: IProps) => {
|
|
|
122
137
|
|
|
123
138
|
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
124
139
|
useEffect(() => {
|
|
125
|
-
const { pageID, getPage,
|
|
140
|
+
const { pageID, getPage, sendPagePing, setStructuredDataFilter } = props;
|
|
126
141
|
|
|
127
142
|
editorContent?.structuredData && setStructuredDataFilter(editorContent.structuredData);
|
|
128
|
-
const defaultTab = "content";
|
|
129
143
|
const handleGetPage = async () => await getPage(pageID, true);
|
|
130
|
-
setTab(
|
|
144
|
+
setTab(defaultContentTab);
|
|
131
145
|
setToastMsg(null);
|
|
132
146
|
handleGetPage();
|
|
133
147
|
if (!pageID) {
|
|
@@ -150,11 +164,11 @@ const GlobalEditor = (props: IProps) => {
|
|
|
150
164
|
const { pageID, sendPagePing, currentUserID } = props;
|
|
151
165
|
if (userEditing && userEditing.id !== currentUserID) {
|
|
152
166
|
setIsReadOnly(true);
|
|
153
|
-
!isOpen && toggleModal();
|
|
167
|
+
!isOpen("userEditing") && toggleModal("userEditing");
|
|
154
168
|
} else {
|
|
155
169
|
setIsReadOnly(false);
|
|
156
170
|
pageID && sendPagePing(pageID);
|
|
157
|
-
isOpen && toggleModal();
|
|
171
|
+
isOpen("userEditing") && toggleModal("userEditing");
|
|
158
172
|
}
|
|
159
173
|
}, [userEditing]);
|
|
160
174
|
|
|
@@ -280,7 +294,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
280
294
|
const saved = await schedulePublication(dateString, isDraft);
|
|
281
295
|
if (saved) {
|
|
282
296
|
resetDirty();
|
|
283
|
-
|
|
297
|
+
toggleModal("schedule");
|
|
284
298
|
}
|
|
285
299
|
};
|
|
286
300
|
|
|
@@ -289,7 +303,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
289
303
|
if (saved) {
|
|
290
304
|
setScheduleDate({ date: "", time: "12:00 am" });
|
|
291
305
|
resetDirty();
|
|
292
|
-
|
|
306
|
+
toggleModal("cancelSchedule");
|
|
293
307
|
}
|
|
294
308
|
};
|
|
295
309
|
|
|
@@ -310,7 +324,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
310
324
|
}
|
|
311
325
|
: {
|
|
312
326
|
label: status === pageStatus.UPLOAD_PENDING ? "Cancel publication" : "Unpublish",
|
|
313
|
-
action: hasDraft ?
|
|
327
|
+
action: hasDraft ? () => toggleModal("unpublish") : unpublishPage,
|
|
314
328
|
};
|
|
315
329
|
case pageStatus.MODIFIED:
|
|
316
330
|
case pageStatus.SCHEDULED:
|
|
@@ -325,24 +339,24 @@ const GlobalEditor = (props: IProps) => {
|
|
|
325
339
|
|
|
326
340
|
const menuOptions = [];
|
|
327
341
|
|
|
328
|
-
if (
|
|
342
|
+
if (isAllowedTo.publishPages && !isScheduled && !isPublished && props.pageStatus !== pageStatus.OFFLINE_PENDING) {
|
|
329
343
|
menuOptions.push({
|
|
330
344
|
label: "Schedule",
|
|
331
345
|
icon: "calendar",
|
|
332
|
-
action:
|
|
346
|
+
action: () => toggleModal("schedule"),
|
|
333
347
|
});
|
|
334
348
|
}
|
|
335
349
|
|
|
336
|
-
if (
|
|
350
|
+
if (isAllowedTo.publishPages && isScheduled) {
|
|
337
351
|
menuOptions.push({
|
|
338
352
|
label: "Cancel Schedule",
|
|
339
353
|
icon: "cancelEvent",
|
|
340
|
-
action:
|
|
354
|
+
action: () => toggleModal("cancelSchedule"),
|
|
341
355
|
});
|
|
342
356
|
}
|
|
343
357
|
|
|
344
358
|
if (
|
|
345
|
-
|
|
359
|
+
isAllowedTo.editContentPages &&
|
|
346
360
|
(props.pageStatus === pageStatus.PUBLISHED ||
|
|
347
361
|
props.pageStatus === pageStatus.OFFLINE ||
|
|
348
362
|
props.pageStatus === pageStatus.OFFLINE_PENDING ||
|
|
@@ -356,7 +370,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
356
370
|
});
|
|
357
371
|
}
|
|
358
372
|
|
|
359
|
-
if (
|
|
373
|
+
if (isAllowedTo.editContentPages && !isScheduled && (isDraft || (isPublished && isDirty))) {
|
|
360
374
|
menuOptions.push({
|
|
361
375
|
label: "Discard changes",
|
|
362
376
|
icon: "close",
|
|
@@ -364,15 +378,15 @@ const GlobalEditor = (props: IProps) => {
|
|
|
364
378
|
});
|
|
365
379
|
}
|
|
366
380
|
|
|
367
|
-
if (props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty &&
|
|
381
|
+
if (props.pageStatus === pageStatus.PUBLISHED && !hasDraft && isDirty && isAllowedTo.publishPages) {
|
|
368
382
|
menuOptions.push({
|
|
369
383
|
label: "Unpublish",
|
|
370
384
|
icon: "offline",
|
|
371
|
-
action: isDraft ?
|
|
385
|
+
action: isDraft ? () => toggleModal("unpublish") : unpublishPage,
|
|
372
386
|
});
|
|
373
387
|
}
|
|
374
388
|
|
|
375
|
-
if (!isDraft &&
|
|
389
|
+
if (!isDraft && isAllowedTo.deletePages) {
|
|
376
390
|
menuOptions.push({
|
|
377
391
|
label: "Delete page",
|
|
378
392
|
icon: "delete",
|
|
@@ -382,7 +396,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
382
396
|
|
|
383
397
|
const downArrowMenu = {
|
|
384
398
|
displayed: !isReadOnly && !isDeleted,
|
|
385
|
-
button:
|
|
399
|
+
button: isAllowedTo.publishPages ? getPublishButton(props.pageStatus) : undefined,
|
|
386
400
|
options: menuOptions,
|
|
387
401
|
};
|
|
388
402
|
|
|
@@ -430,7 +444,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
430
444
|
}
|
|
431
445
|
};
|
|
432
446
|
|
|
433
|
-
const rightButtonProps =
|
|
447
|
+
const rightButtonProps = isAllowedTo.editContentPages
|
|
434
448
|
? {
|
|
435
449
|
label: isSaving ? "Saving" : getSaveLabel(),
|
|
436
450
|
disabled: (!isDirty && pageID !== null && !isNewTranslation) || isSaving || isReadOnly,
|
|
@@ -462,11 +476,11 @@ const GlobalEditor = (props: IProps) => {
|
|
|
462
476
|
|
|
463
477
|
const onNavigateWithDirty = (navigateCallback: () => void) => {
|
|
464
478
|
setPendingNavigateCallback(() => navigateCallback);
|
|
465
|
-
|
|
479
|
+
toggleModal("dirtyNavigate");
|
|
466
480
|
};
|
|
467
481
|
|
|
468
482
|
const handleConfirmDirtyNavigation = () => {
|
|
469
|
-
|
|
483
|
+
toggleModal("dirtyNavigate");
|
|
470
484
|
if (pendingNavigateCallback) {
|
|
471
485
|
pendingNavigateCallback();
|
|
472
486
|
setPendingNavigateCallback(null);
|
|
@@ -474,7 +488,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
474
488
|
};
|
|
475
489
|
|
|
476
490
|
const handleCancelDirtyNavigation = () => {
|
|
477
|
-
|
|
491
|
+
toggleModal("dirtyNavigate");
|
|
478
492
|
setPendingNavigateCallback(null);
|
|
479
493
|
};
|
|
480
494
|
|
|
@@ -518,7 +532,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
518
532
|
const mainAction = { title: "Preview Page", onClick: toggleModal };
|
|
519
533
|
const secondaryAction = { title: "Ok, go back", onClick: handleGoBack };
|
|
520
534
|
|
|
521
|
-
const mainUnpublishAction = { title: "Ok", onClick:
|
|
535
|
+
const mainUnpublishAction = { title: "Ok", onClick: () => toggleModal("unpublish") };
|
|
522
536
|
|
|
523
537
|
const mainScheduleModalAction = {
|
|
524
538
|
title: "Schedule",
|
|
@@ -527,7 +541,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
527
541
|
|
|
528
542
|
const secondaryScheduleModalAction = {
|
|
529
543
|
title: "Cancel",
|
|
530
|
-
onClick:
|
|
544
|
+
onClick: () => toggleModal("schedule"),
|
|
531
545
|
};
|
|
532
546
|
|
|
533
547
|
const mainCancelScheduleModalAction = {
|
|
@@ -537,10 +551,10 @@ const GlobalEditor = (props: IProps) => {
|
|
|
537
551
|
|
|
538
552
|
const secondaryCancelScheduleModalAction = {
|
|
539
553
|
title: "Back",
|
|
540
|
-
onClick:
|
|
554
|
+
onClick: () => toggleModal("cancelSchedule"),
|
|
541
555
|
};
|
|
542
556
|
|
|
543
|
-
const tabIcons =
|
|
557
|
+
const tabIcons = isAllowedTo.editContentPages
|
|
544
558
|
? [
|
|
545
559
|
{ name: "edit", text: "Edit mode" },
|
|
546
560
|
{ name: "view", text: "Preview mode" },
|
|
@@ -558,6 +572,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
558
572
|
icons: tabIcons,
|
|
559
573
|
selectedTab,
|
|
560
574
|
action: (tab: string) => handleSelectedTab(tab),
|
|
575
|
+
disabled: isOpen("headingsPreview") || isOpen("keywordsPreview"),
|
|
561
576
|
};
|
|
562
577
|
|
|
563
578
|
const filteredLanguages = globalLangs.filter((lang) =>
|
|
@@ -587,7 +602,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
587
602
|
await getPage(selectedPageLanguage.pageId, true, isDraft);
|
|
588
603
|
resetDirty();
|
|
589
604
|
} else {
|
|
590
|
-
|
|
605
|
+
isAllowedTo.editContentPages && setSelectedTab("edit");
|
|
591
606
|
createNewTranslation(true);
|
|
592
607
|
await getPage(pageID, true);
|
|
593
608
|
}
|
|
@@ -596,11 +611,33 @@ const GlobalEditor = (props: IProps) => {
|
|
|
596
611
|
const handleRestorePage = async () => {
|
|
597
612
|
const isRestored = await restorePage(pageID);
|
|
598
613
|
if (isRestored) {
|
|
599
|
-
|
|
614
|
+
isOpen("restore") && toggleModal("restore");
|
|
600
615
|
await getPage(pageID);
|
|
601
616
|
}
|
|
602
617
|
};
|
|
603
618
|
|
|
619
|
+
const handleAddKeywords = (newKeywords: string[]) => {
|
|
620
|
+
const keywords: string[] = [...editorContent.metaKeywords, ...newKeywords];
|
|
621
|
+
updateEditorContent(0, "metaKeywords", keywords);
|
|
622
|
+
handleSavePage();
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const handledeleteKeyword = (keyword: string) => {
|
|
626
|
+
const newKeywords = (editorContent.metaKeywords as string[]).filter((key) => key !== keyword);
|
|
627
|
+
updateEditorContent(0, "metaKeywords", newKeywords);
|
|
628
|
+
handleSavePage();
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const handleToggleHeadingsEditor = () => {
|
|
632
|
+
toggleModal("headingsPreview");
|
|
633
|
+
setTab(defaultContentTab);
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const handleToggleKeywordsEditor = () => {
|
|
637
|
+
toggleModal("keywordsPreview");
|
|
638
|
+
setTab(defaultContentTab);
|
|
639
|
+
};
|
|
640
|
+
|
|
604
641
|
return isLoading ? (
|
|
605
642
|
<Loading />
|
|
606
643
|
) : (
|
|
@@ -616,7 +653,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
616
653
|
pageStatus={props.pageStatus}
|
|
617
654
|
language={lang}
|
|
618
655
|
languageAction={handleLanguage}
|
|
619
|
-
availableLanguages={
|
|
656
|
+
availableLanguages={isAllowedTo.createPages && !isDeleted ? globalLangs : filteredLanguages}
|
|
620
657
|
currentLanguages={contentLanguages}
|
|
621
658
|
currentPageID={pageID}
|
|
622
659
|
fullWidth={true}
|
|
@@ -643,7 +680,7 @@ const GlobalEditor = (props: IProps) => {
|
|
|
643
680
|
type="error"
|
|
644
681
|
text={deletedNotificationText}
|
|
645
682
|
btnText="Restore page"
|
|
646
|
-
onClick={
|
|
683
|
+
onClick={() => toggleModal("restore")}
|
|
647
684
|
/>
|
|
648
685
|
</S.NotificationWrapper>
|
|
649
686
|
)}
|
|
@@ -674,6 +711,12 @@ const GlobalEditor = (props: IProps) => {
|
|
|
674
711
|
setNotification={setNotification}
|
|
675
712
|
isEditLive={isEditLive}
|
|
676
713
|
isDisabled={isDeleted}
|
|
714
|
+
toggleHeadingsPreview={handleToggleHeadingsEditor}
|
|
715
|
+
isHeadingsPreviewOpen={isOpen("headingsPreview")}
|
|
716
|
+
headingsFilter={headingsFilter}
|
|
717
|
+
toggleKeywordsPreview={handleToggleKeywordsEditor}
|
|
718
|
+
isKeywordsPreviewOpen={isOpen("keywordsPreview")}
|
|
719
|
+
keywordsFilter={keywordsFilter}
|
|
677
720
|
/>
|
|
678
721
|
</S.Content>
|
|
679
722
|
</>
|
|
@@ -684,14 +727,14 @@ const GlobalEditor = (props: IProps) => {
|
|
|
684
727
|
</>
|
|
685
728
|
)}
|
|
686
729
|
<Modal
|
|
687
|
-
isOpen={isOpen}
|
|
688
|
-
hide={toggleModal}
|
|
730
|
+
isOpen={isOpen("userEditing")}
|
|
731
|
+
hide={() => toggleModal("userEditing")}
|
|
689
732
|
size="S"
|
|
690
733
|
title="This page is currently being edited"
|
|
691
734
|
mainAction={mainAction}
|
|
692
735
|
secondaryAction={secondaryAction}
|
|
693
736
|
>
|
|
694
|
-
{isOpen && (
|
|
737
|
+
{isOpen("userEditing") && (
|
|
695
738
|
<S.ModalContent>
|
|
696
739
|
<p>
|
|
697
740
|
<strong>{userEditing?.name}</strong> is currently working on this page. You can preview the page but{" "}
|
|
@@ -701,13 +744,13 @@ const GlobalEditor = (props: IProps) => {
|
|
|
701
744
|
)}
|
|
702
745
|
</Modal>
|
|
703
746
|
<Modal
|
|
704
|
-
isOpen={
|
|
705
|
-
hide={
|
|
747
|
+
isOpen={isOpen("unpublish")}
|
|
748
|
+
hide={() => toggleModal("unpublish")}
|
|
706
749
|
size="S"
|
|
707
750
|
title="Unpublish Modified Page"
|
|
708
751
|
mainAction={mainUnpublishAction}
|
|
709
752
|
>
|
|
710
|
-
{
|
|
753
|
+
{isOpen("unpublish") && (
|
|
711
754
|
<S.ModalContent>
|
|
712
755
|
<p>
|
|
713
756
|
There are some saved changes that are not published on this page. To Unpublish this page,{" "}
|
|
@@ -717,31 +760,48 @@ const GlobalEditor = (props: IProps) => {
|
|
|
717
760
|
)}
|
|
718
761
|
</Modal>
|
|
719
762
|
<ScheduleModal
|
|
720
|
-
isOpen={
|
|
721
|
-
toggleModal={
|
|
763
|
+
isOpen={isOpen("schedule")}
|
|
764
|
+
toggleModal={() => toggleModal("schedule")}
|
|
722
765
|
mainModalAction={mainScheduleModalAction}
|
|
723
766
|
secondaryModalAction={secondaryScheduleModalAction}
|
|
724
767
|
scheduleDate={scheduleDate}
|
|
725
768
|
setScheduleDate={setScheduleDate}
|
|
726
769
|
/>
|
|
727
770
|
<CancelScheduleModal
|
|
728
|
-
isOpen={
|
|
729
|
-
toggleModal={
|
|
771
|
+
isOpen={isOpen("cancelSchedule")}
|
|
772
|
+
toggleModal={() => toggleModal("cancelSchedule")}
|
|
730
773
|
mainModalAction={mainCancelScheduleModalAction}
|
|
731
774
|
secondaryModalAction={secondaryCancelScheduleModalAction}
|
|
732
775
|
/>
|
|
733
776
|
<RestoreModal
|
|
734
|
-
isOpen={
|
|
735
|
-
toggleModal={
|
|
777
|
+
isOpen={isOpen("restore")}
|
|
778
|
+
toggleModal={() => toggleModal("restore")}
|
|
736
779
|
{...{
|
|
737
780
|
isChild: false,
|
|
738
781
|
hasIssues: schemaVersion !== editorContent.schemaVersionTimestamp,
|
|
739
782
|
restorePage: handleRestorePage,
|
|
740
783
|
}}
|
|
741
784
|
/>
|
|
785
|
+
<HeadingsPreviewModal
|
|
786
|
+
isOpen={isOpen("headingsPreview")}
|
|
787
|
+
toggleModal={handleToggleHeadingsEditor}
|
|
788
|
+
browserRef={browserRef}
|
|
789
|
+
headingsFilter={headingsFilter}
|
|
790
|
+
setHeadingsFilter={setHeadingsFilter}
|
|
791
|
+
/>
|
|
792
|
+
<KeywordsPreviewModal
|
|
793
|
+
isOpen={isOpen("keywordsPreview")}
|
|
794
|
+
toggleModal={handleToggleKeywordsEditor}
|
|
795
|
+
browserRef={browserRef}
|
|
796
|
+
keywords={editorContent?.metaKeywords || []}
|
|
797
|
+
keywordsFilter={keywordsFilter}
|
|
798
|
+
setKeywordsFilter={setKeywordsFilter}
|
|
799
|
+
addKeywords={handleAddKeywords}
|
|
800
|
+
deleteKeyword={handledeleteKeyword}
|
|
801
|
+
/>
|
|
742
802
|
</MainWrapper>
|
|
743
803
|
<Modal
|
|
744
|
-
isOpen={
|
|
804
|
+
isOpen={isOpen("dirtyNavigate")}
|
|
745
805
|
hide={handleCancelDirtyNavigation}
|
|
746
806
|
size="S"
|
|
747
807
|
title="Unsaved changes"
|
|
@@ -818,6 +878,7 @@ const mapDispatchToProps = {
|
|
|
818
878
|
getUserCurrentPermissions: usersActions.getUserCurrentPermissions,
|
|
819
879
|
schedulePublication: pageEditorActions.schedulePublication,
|
|
820
880
|
restorePage: pageEditorActions.restorePage,
|
|
881
|
+
updateEditorContent: pageEditorActions.updateEditorContent,
|
|
821
882
|
};
|
|
822
883
|
|
|
823
884
|
interface IPageEditorDispatchProps {
|
|
@@ -844,6 +905,7 @@ interface IPageEditorDispatchProps {
|
|
|
844
905
|
getUserCurrentPermissions(): Promise<void>;
|
|
845
906
|
schedulePublication(date: string | null, isDraft: boolean): Promise<boolean>;
|
|
846
907
|
restorePage(id: number | number[]): Promise<boolean>;
|
|
908
|
+
updateEditorContent(selectedEditorID: number, key: string, value: any): void;
|
|
847
909
|
}
|
|
848
910
|
|
|
849
911
|
type IProps = IPageEditorStateProps & IPageEditorDispatchProps & RouteComponentProps;
|
|
@@ -4,7 +4,17 @@ import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
|
4
4
|
import { sitesActions } from "@ax/containers/Sites";
|
|
5
5
|
import { appActions } from "@ax/containers/App";
|
|
6
6
|
import { ConfigPanel, ResizePanel } from "@ax/components";
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
HeadingFilter,
|
|
9
|
+
IBreadcrumbItem,
|
|
10
|
+
IModule,
|
|
11
|
+
INotification,
|
|
12
|
+
IRootState,
|
|
13
|
+
ISchema,
|
|
14
|
+
ISite,
|
|
15
|
+
IUserEditing,
|
|
16
|
+
} from "@ax/types";
|
|
17
|
+
|
|
8
18
|
import PageBrowser from "../PageBrowser";
|
|
9
19
|
|
|
10
20
|
const Editor = (props: IProps) => {
|
|
@@ -40,9 +50,15 @@ const Editor = (props: IProps) => {
|
|
|
40
50
|
pasteModule,
|
|
41
51
|
setNotification,
|
|
42
52
|
restorePageNavigation,
|
|
53
|
+
toggleHeadingsPreview,
|
|
54
|
+
toggleKeywordsPreview,
|
|
43
55
|
content,
|
|
44
56
|
isEditLive,
|
|
45
57
|
isDisabled,
|
|
58
|
+
isHeadingsPreviewOpen,
|
|
59
|
+
headingsFilter,
|
|
60
|
+
isKeywordsPreviewOpen,
|
|
61
|
+
keywordsFilter,
|
|
46
62
|
isDirty,
|
|
47
63
|
onNavigateWithDirty,
|
|
48
64
|
} = props;
|
|
@@ -68,15 +84,24 @@ const Editor = (props: IProps) => {
|
|
|
68
84
|
pasteModuleAction: pasteModule,
|
|
69
85
|
setNotificationAction: setNotification,
|
|
70
86
|
restorePageNavigationAction: restorePageNavigation,
|
|
87
|
+
toggleHeadingsPreviewAction: toggleHeadingsPreview,
|
|
88
|
+
toggleKeywordsPreviewAction: toggleKeywordsPreview,
|
|
71
89
|
};
|
|
72
90
|
|
|
73
91
|
return (
|
|
74
92
|
<ResizePanel
|
|
93
|
+
disabled={isHeadingsPreviewOpen || isKeywordsPreviewOpen}
|
|
75
94
|
leftPanel={
|
|
76
95
|
<PageBrowser
|
|
77
96
|
isTemplateActivated={isTemplateActivated}
|
|
78
97
|
isReadOnly={isReadOnly || isEditLive || isDisabled}
|
|
79
98
|
browserRef={browserRef}
|
|
99
|
+
isHeadingsPreviewOpen={isHeadingsPreviewOpen}
|
|
100
|
+
toggleHeadingsPreview={toggleHeadingsPreview}
|
|
101
|
+
headingsFilter={headingsFilter}
|
|
102
|
+
toggleKeywordsPreview={toggleKeywordsPreview}
|
|
103
|
+
isKeywordsPreviewOpen={isKeywordsPreviewOpen}
|
|
104
|
+
keywordsFilter={keywordsFilter}
|
|
80
105
|
/>
|
|
81
106
|
}
|
|
82
107
|
rightPanel={
|
|
@@ -142,14 +167,20 @@ interface IPageBrowserDispatchProps {
|
|
|
142
167
|
pasteModule(editorID: number, key: string, modulesToPaste: IModule[]): Promise<{ error?: INotification }>;
|
|
143
168
|
setNotification: (notification: INotification) => void;
|
|
144
169
|
restorePageNavigation: (key: string) => void;
|
|
170
|
+
toggleHeadingsPreview: () => void;
|
|
171
|
+
toggleKeywordsPreview: () => void;
|
|
145
172
|
isTemplateActivated: boolean;
|
|
146
173
|
isGlobal: boolean;
|
|
147
174
|
isEditable: boolean;
|
|
148
175
|
pageTitle: string;
|
|
149
176
|
isReadOnly: boolean;
|
|
150
|
-
browserRef:
|
|
177
|
+
browserRef: React.RefObject<HTMLDivElement>;
|
|
151
178
|
isEditLive: boolean;
|
|
152
179
|
isDisabled: boolean;
|
|
180
|
+
isHeadingsPreviewOpen: boolean;
|
|
181
|
+
headingsFilter: HeadingFilter;
|
|
182
|
+
isKeywordsPreviewOpen: boolean;
|
|
183
|
+
keywordsFilter: string[];
|
|
153
184
|
isDirty?: boolean;
|
|
154
185
|
onNavigateWithDirty?: (navigateCallback: () => void) => void;
|
|
155
186
|
}
|
|
@@ -2,7 +2,7 @@ import { connect } from "react-redux";
|
|
|
2
2
|
import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
3
3
|
|
|
4
4
|
import { Browser } from "@ax/components";
|
|
5
|
-
import type { ILanguage, IRootState, ISite, ISocialState } from "@ax/types";
|
|
5
|
+
import type { HeadingFilter, ILanguage, IRootState, ISite, ISocialState } from "@ax/types";
|
|
6
6
|
|
|
7
7
|
const PageBrowser = (props: IProps) => {
|
|
8
8
|
const {
|
|
@@ -16,10 +16,16 @@ const PageBrowser = (props: IProps) => {
|
|
|
16
16
|
isReadOnly,
|
|
17
17
|
isPreview,
|
|
18
18
|
browserRef,
|
|
19
|
+
isHeadingsPreviewOpen = false,
|
|
20
|
+
isKeywordsPreviewOpen = false,
|
|
21
|
+
headingsFilter,
|
|
22
|
+
keywordsFilter,
|
|
19
23
|
deleteModule,
|
|
20
24
|
duplicateModule,
|
|
21
25
|
copyModule,
|
|
22
26
|
setScrollEditorID,
|
|
27
|
+
toggleHeadingsPreview,
|
|
28
|
+
toggleKeywordsPreview,
|
|
23
29
|
} = props;
|
|
24
30
|
|
|
25
31
|
if (!currentSiteInfo) {
|
|
@@ -31,7 +37,8 @@ const PageBrowser = (props: IProps) => {
|
|
|
31
37
|
const slugWithSlash = slug ? (slug.startsWith("/") ? slug : `/${slug}`) : "";
|
|
32
38
|
const pathWithoutSlash = path ? (path.endsWith("/") ? path.slice(0, -1) : path) : "";
|
|
33
39
|
const url = `${pathWithoutSlash}${slugWithSlash}`;
|
|
34
|
-
const disabled = isTemplateActivated === false || isReadOnly;
|
|
40
|
+
const disabled = isTemplateActivated === false || isReadOnly || isHeadingsPreviewOpen || isKeywordsPreviewOpen;
|
|
41
|
+
const editorType = isHeadingsPreviewOpen ? "headings" : isKeywordsPreviewOpen ? "keywords" : "page";
|
|
35
42
|
|
|
36
43
|
const actions = {
|
|
37
44
|
deleteModuleAction: deleteModule,
|
|
@@ -58,6 +65,11 @@ const PageBrowser = (props: IProps) => {
|
|
|
58
65
|
showIframe={true}
|
|
59
66
|
browserRef={browserRef}
|
|
60
67
|
actions={actions}
|
|
68
|
+
editorType={editorType}
|
|
69
|
+
toggleHeadingsPreview={toggleHeadingsPreview}
|
|
70
|
+
toggleKeywordsPreview={toggleKeywordsPreview}
|
|
71
|
+
headingFilter={headingsFilter}
|
|
72
|
+
keywordsFilter={keywordsFilter}
|
|
61
73
|
/>
|
|
62
74
|
);
|
|
63
75
|
};
|
|
@@ -78,10 +90,16 @@ interface IPageBrowserDispatchProps {
|
|
|
78
90
|
isReadOnly: boolean;
|
|
79
91
|
isPreview?: boolean;
|
|
80
92
|
browserRef?: any;
|
|
93
|
+
isHeadingsPreviewOpen?: boolean;
|
|
94
|
+
isKeywordsPreviewOpen?: boolean;
|
|
95
|
+
headingsFilter?: HeadingFilter;
|
|
96
|
+
keywordsFilter?: string[];
|
|
81
97
|
deleteModule(editorID: number[]): void;
|
|
82
98
|
duplicateModule(editorID: number[]): number;
|
|
83
99
|
copyModule(editorID: number[]): number | boolean;
|
|
84
100
|
setScrollEditorID(editorID: number | null): void;
|
|
101
|
+
toggleHeadingsPreview?(): void;
|
|
102
|
+
toggleKeywordsPreview?(): void;
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
type IProps = IPageBrowserStateProps & IPageBrowserDispatchProps;
|