@griddo/ax 1.62.6 → 1.63.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 +6 -5
- package/src/GlobalStore.tsx +1 -1
- package/src/Style/index.tsx +2 -0
- package/src/api/pages.tsx +20 -0
- package/src/api/utils.tsx +5 -2
- package/src/components/Browser/index.tsx +144 -39
- package/src/components/Browser/style.tsx +47 -11
- package/src/components/ConfigPanel/index.tsx +3 -3
- package/src/components/Fields/RichText/index.tsx +2 -2
- package/src/components/Fields/UrlField/utils.tsx +10 -1
- package/src/components/Icon/components/Desktop.js +9 -4
- package/src/components/Icon/components/Phone.js +4 -4
- package/src/components/Icon/components/Share.js +12 -0
- package/src/components/Icon/components/Tablet.js +4 -4
- package/src/components/Icon/svgs/Share.svg +3 -0
- package/src/components/MainWrapper/AppBar/index.tsx +19 -7
- package/src/components/MainWrapper/AppBar/style.tsx +20 -4
- package/src/components/MainWrapper/index.tsx +1 -1
- package/src/components/SideModal/index.tsx +1 -1
- package/src/components/TableFilters/DateFilter/index.tsx +48 -0
- package/src/components/TableFilters/DateFilter/style.tsx +29 -0
- package/src/components/TableFilters/index.tsx +2 -0
- package/src/components/Tabs/index.tsx +17 -7
- package/src/components/Tabs/style.tsx +29 -16
- package/src/components/Tooltip/index.tsx +1 -1
- package/src/components/Tooltip/style.tsx +2 -0
- package/src/components/index.tsx +2 -0
- package/src/containers/App/reducer.tsx +3 -1
- package/src/containers/Navigation/Defaults/actions.tsx +2 -1
- package/src/containers/PageEditor/actions.tsx +65 -10
- package/src/containers/Settings/DataPacks/actions.tsx +4 -1
- package/src/containers/Sites/actions.tsx +3 -1
- package/src/helpers/dates.tsx +3 -4
- package/src/helpers/index.tsx +2 -0
- package/src/helpers/strings.tsx +38 -7
- package/src/hooks/forms.tsx +1 -2
- package/src/index.tsx +2 -2
- package/src/modules/Content/OptionTable/index.tsx +29 -2
- package/src/modules/Content/PageItem/index.tsx +11 -2
- package/src/modules/Content/index.tsx +9 -0
- package/src/modules/GlobalEditor/Editor/index.tsx +7 -9
- package/src/modules/GlobalEditor/{Editor/PageBrowser → PageBrowser}/index.tsx +14 -6
- package/src/modules/GlobalEditor/Preview/index.tsx +19 -0
- package/src/modules/GlobalEditor/Preview/style.tsx +9 -0
- package/src/modules/GlobalEditor/index.tsx +41 -9
- package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +5 -1
- package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +9 -7
- package/src/modules/PageEditor/Editor/index.tsx +5 -5
- package/src/modules/PageEditor/{Editor/PageBrowser → PageBrowser}/index.tsx +11 -4
- package/src/modules/PageEditor/Preview/index.tsx +14 -0
- package/src/modules/PageEditor/Preview/style.tsx +9 -0
- package/src/modules/PageEditor/index.tsx +40 -16
- package/src/modules/PublicPreview/index.tsx +92 -0
- package/src/modules/PublicPreview/style.tsx +18 -0
- package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +16 -3
- package/src/modules/Redirects/BulkHeader/TableHeader/style.tsx +9 -3
- package/src/modules/Redirects/BulkHeader/index.tsx +7 -1
- package/src/modules/Redirects/RedirectItem/index.tsx +4 -0
- package/src/modules/Redirects/RedirectItem/style.tsx +19 -3
- package/src/modules/Redirects/atoms.tsx +0 -1
- package/src/modules/Redirects/hooks.tsx +67 -0
- package/src/modules/Redirects/index.tsx +23 -9
- package/src/modules/Redirects/utils.tsx +10 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/index.tsx +107 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/style.tsx +54 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/index.tsx +66 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/style.tsx +42 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +58 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +41 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +93 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/style.tsx +25 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/index.tsx +48 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/style.tsx +53 -0
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +40 -36
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/style.tsx +4 -5
- package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +4 -4
- package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +5 -1
- package/src/modules/Settings/{Analytics → SeoAnalyticsSettings/Analytics}/atoms.tsx +0 -0
- package/src/modules/Settings/{Analytics → SeoAnalyticsSettings/Analytics}/index.tsx +20 -11
- package/src/modules/Settings/{Analytics → SeoAnalyticsSettings/Analytics}/style.tsx +6 -0
- package/src/modules/Settings/{SeoSettings → SeoAnalyticsSettings}/index.tsx +11 -4
- package/src/routes/publicRoutes.tsx +3 -1
- package/src/routes/site.tsx +14 -10
- package/src/types/index.tsx +9 -0
|
@@ -9,10 +9,11 @@ import { appActions } from "@ax/containers/App";
|
|
|
9
9
|
import { navigationActions } from "@ax/containers/Navigation";
|
|
10
10
|
import { pageStatus } from "@ax/containers/PageEditor/interfaces";
|
|
11
11
|
import { RouteLeavingGuard } from "@ax/guards";
|
|
12
|
-
import { useIsDirty, useModal } from "@ax/hooks";
|
|
12
|
+
import { useIsDirty, useModal, useToast } from "@ax/hooks";
|
|
13
13
|
import { isModuleDisabled } from "@ax/helpers";
|
|
14
14
|
import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
|
|
15
15
|
import Editor from "./Editor";
|
|
16
|
+
import Preview from "./Preview";
|
|
16
17
|
|
|
17
18
|
import * as S from "./style";
|
|
18
19
|
|
|
@@ -44,7 +45,8 @@ const PageEditor = (props: IProps) => {
|
|
|
44
45
|
isNewTranslation,
|
|
45
46
|
} = props;
|
|
46
47
|
|
|
47
|
-
const [
|
|
48
|
+
const [isReadOnly, setIsReadOnly] = useState(false);
|
|
49
|
+
const [selectedTab, setSelectedTab] = useState("edit");
|
|
48
50
|
const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent.editorContent, isNewTranslation);
|
|
49
51
|
const { isOpen, toggleModal } = useModal();
|
|
50
52
|
const { isOpen: isUnpublishOpen, toggleModal: toggleUnpublishModal } = useModal();
|
|
@@ -55,6 +57,7 @@ const PageEditor = (props: IProps) => {
|
|
|
55
57
|
const isDraft = props.pageStatus === pageStatus.MODIFIED;
|
|
56
58
|
const hasDraft = editorContent.editorContent && editorContent.editorContent.haveDraftPage;
|
|
57
59
|
const isLivePageChanged = editorContent.editorContent && editorContent.editorContent.liveChanged;
|
|
60
|
+
const structuredData = editorContent.editorContent ? editorContent.editorContent.structuredData : "";
|
|
58
61
|
|
|
59
62
|
useEffect(() => {
|
|
60
63
|
const { pageID, getPage, setTab, sendPagePing } = props;
|
|
@@ -77,10 +80,10 @@ const PageEditor = (props: IProps) => {
|
|
|
77
80
|
useEffect(() => {
|
|
78
81
|
const { pageID, sendPagePing, currentUserID } = props;
|
|
79
82
|
if (userEditing && userEditing.id !== currentUserID) {
|
|
80
|
-
|
|
83
|
+
setIsReadOnly(true);
|
|
81
84
|
!isOpen && toggleModal();
|
|
82
85
|
} else {
|
|
83
|
-
|
|
86
|
+
setIsReadOnly(false);
|
|
84
87
|
pageID && sendPagePing(pageID);
|
|
85
88
|
isOpen && toggleModal();
|
|
86
89
|
}
|
|
@@ -89,6 +92,7 @@ const PageEditor = (props: IProps) => {
|
|
|
89
92
|
|
|
90
93
|
useEffect(() => {
|
|
91
94
|
getDefaults();
|
|
95
|
+
resetDirty();
|
|
92
96
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
97
|
}, [lang]);
|
|
94
98
|
|
|
@@ -204,9 +208,14 @@ const PageEditor = (props: IProps) => {
|
|
|
204
208
|
}
|
|
205
209
|
};
|
|
206
210
|
|
|
211
|
+
const handleNewTranlation = (isNewTranslation: boolean) => {
|
|
212
|
+
setSelectedTab("edit");
|
|
213
|
+
createNewTranslation && createNewTranslation(isNewTranslation);
|
|
214
|
+
};
|
|
215
|
+
|
|
207
216
|
const languageActions = {
|
|
208
217
|
setLanguage,
|
|
209
|
-
createNewTranslation,
|
|
218
|
+
createNewTranslation: handleNewTranlation,
|
|
210
219
|
getContent: getPage,
|
|
211
220
|
};
|
|
212
221
|
|
|
@@ -246,7 +255,7 @@ const PageEditor = (props: IProps) => {
|
|
|
246
255
|
: [];
|
|
247
256
|
|
|
248
257
|
const downArrowMenu = {
|
|
249
|
-
displayed: !
|
|
258
|
+
displayed: !isReadOnly,
|
|
250
259
|
button: getPublishButton(props.pageStatus),
|
|
251
260
|
options: menuOptions,
|
|
252
261
|
};
|
|
@@ -316,7 +325,7 @@ const PageEditor = (props: IProps) => {
|
|
|
316
325
|
disabled:
|
|
317
326
|
(!isDirty && pageID !== null && !isNewTranslation) ||
|
|
318
327
|
isSaving ||
|
|
319
|
-
|
|
328
|
+
isReadOnly ||
|
|
320
329
|
(!isTemplateActivated && !isGlobal),
|
|
321
330
|
action: handleSavePage,
|
|
322
331
|
};
|
|
@@ -392,6 +401,15 @@ const PageEditor = (props: IProps) => {
|
|
|
392
401
|
|
|
393
402
|
const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
|
|
394
403
|
|
|
404
|
+
const tabsPreview = {
|
|
405
|
+
icons: [
|
|
406
|
+
{ name: "edit", text: "Edit mode" },
|
|
407
|
+
{ name: "view", text: "Preview mode" },
|
|
408
|
+
],
|
|
409
|
+
selectedTab,
|
|
410
|
+
action: (tab: string) => setSelectedTab(tab),
|
|
411
|
+
};
|
|
412
|
+
|
|
395
413
|
return isLoading ? (
|
|
396
414
|
<Loading />
|
|
397
415
|
) : (
|
|
@@ -399,6 +417,7 @@ const PageEditor = (props: IProps) => {
|
|
|
399
417
|
<RouteLeavingGuard when={isDirty} action={goToPages} text={modalText} />
|
|
400
418
|
<MainWrapper
|
|
401
419
|
title={pageName}
|
|
420
|
+
subtitle={structuredData}
|
|
402
421
|
backLink={backLinkRoute}
|
|
403
422
|
rightButton={rightButtonProps}
|
|
404
423
|
downArrowMenu={downArrowMenu}
|
|
@@ -414,6 +433,7 @@ const PageEditor = (props: IProps) => {
|
|
|
414
433
|
errors={errors}
|
|
415
434
|
errorActions={{ goToError }}
|
|
416
435
|
isFromEditor={true}
|
|
436
|
+
tabs={tabsPreview}
|
|
417
437
|
>
|
|
418
438
|
{(!isTemplateActivated || hasDeactivatedModules) && !isGlobal && (
|
|
419
439
|
<S.NotificationWrapper>
|
|
@@ -441,15 +461,19 @@ const PageEditor = (props: IProps) => {
|
|
|
441
461
|
</S.NotificationWrapper>
|
|
442
462
|
)}
|
|
443
463
|
<ErrorToast size="l" />
|
|
444
|
-
|
|
445
|
-
<
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
464
|
+
{selectedTab === "edit" ? (
|
|
465
|
+
<S.Content>
|
|
466
|
+
<Editor
|
|
467
|
+
isTemplateActivated={isTemplateActivated}
|
|
468
|
+
isGlobal={isGlobal}
|
|
469
|
+
isEditable={isEditable}
|
|
470
|
+
pageTitle={pageName}
|
|
471
|
+
isReadOnly={isReadOnly}
|
|
472
|
+
/>
|
|
473
|
+
</S.Content>
|
|
474
|
+
) : (
|
|
475
|
+
<Preview />
|
|
476
|
+
)}
|
|
453
477
|
<Modal
|
|
454
478
|
isOpen={isOpen}
|
|
455
479
|
hide={toggleModal}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
import * as components from "components";
|
|
5
|
+
import { providers, translations, cloudinaryDefaults, themes, griddoDamDefaults } from "components";
|
|
6
|
+
import { Preview } from "@griddo/core";
|
|
7
|
+
import { pages } from "@ax/api";
|
|
8
|
+
import { isReqOk } from "@ax/helpers";
|
|
9
|
+
import { Loading } from "@ax/components";
|
|
10
|
+
|
|
11
|
+
import * as S from "./style";
|
|
12
|
+
|
|
13
|
+
const PublicPreview = () => {
|
|
14
|
+
const { id: pageID, entity } = useParams<{ id: string; entity: string }>();
|
|
15
|
+
const [state, setState] = useState<any>(null);
|
|
16
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
let isMounted = true;
|
|
20
|
+
document.body.classList.add("preview");
|
|
21
|
+
|
|
22
|
+
const getPage = async () => {
|
|
23
|
+
if (!isMounted) return;
|
|
24
|
+
|
|
25
|
+
setIsLoading(true);
|
|
26
|
+
const response = await pages.getPublicPage(parseInt(pageID), entity);
|
|
27
|
+
if (isReqOk(response.status)) {
|
|
28
|
+
setState(response.data);
|
|
29
|
+
} else {
|
|
30
|
+
console.log("Error en getPublicPage");
|
|
31
|
+
}
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
getPage();
|
|
36
|
+
|
|
37
|
+
return function cleanup() {
|
|
38
|
+
isMounted = false;
|
|
39
|
+
document.body.classList.remove("preview");
|
|
40
|
+
};
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const API_URL = process.env.REACT_APP_API_ENDPOINT;
|
|
45
|
+
const PUBLIC_API_URL = process.env.REACT_APP_PUBLIC_API_ENDPOINT;
|
|
46
|
+
const { SiteProvider, AnimationProvider } = providers;
|
|
47
|
+
|
|
48
|
+
const defaultTheme = themes.find((theme: any) => theme.default);
|
|
49
|
+
const globalTheme = defaultTheme ? defaultTheme.value : themes[0].value;
|
|
50
|
+
const theme = state && state.site ? state.siteInfo.theme : globalTheme;
|
|
51
|
+
const socials = state && state.site ? state.siteInfo.socials : [];
|
|
52
|
+
const langs = state && state.site ? state.siteInfo.siteLanguages : [];
|
|
53
|
+
|
|
54
|
+
if (isLoading) return <Loading />;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<SiteProvider
|
|
58
|
+
cloudinaryCloudName={state && state.cloudinaryName}
|
|
59
|
+
damDomain={state && state.damDomain}
|
|
60
|
+
theme={theme}
|
|
61
|
+
socials={socials}
|
|
62
|
+
siteLangs={langs}
|
|
63
|
+
translations={translations}
|
|
64
|
+
selectEditorID={0}
|
|
65
|
+
cloudinaryDefaults={cloudinaryDefaults}
|
|
66
|
+
griddoDamDefaults={griddoDamDefaults}
|
|
67
|
+
renderer="editor"
|
|
68
|
+
apiUrl={API_URL}
|
|
69
|
+
publicApiUrl={PUBLIC_API_URL}
|
|
70
|
+
siteId={state && state.site}
|
|
71
|
+
>
|
|
72
|
+
<AnimationProvider showOnScroll={{ active: false }}>
|
|
73
|
+
<S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
|
|
74
|
+
{state && (
|
|
75
|
+
<Preview
|
|
76
|
+
isPage={true}
|
|
77
|
+
apiUrl={API_URL}
|
|
78
|
+
library={components}
|
|
79
|
+
content={state}
|
|
80
|
+
header={state && state.headerContent}
|
|
81
|
+
footer={state && state.footerContent}
|
|
82
|
+
languageId={state && state.language}
|
|
83
|
+
pageLanguages={state && state.pageLanguages}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</S.Wrapper>
|
|
87
|
+
</AnimationProvider>
|
|
88
|
+
</SiteProvider>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default PublicPreview;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Wrapper = styled.div`
|
|
4
|
+
overflow: auto;
|
|
5
|
+
scroll-behavior: smooth;
|
|
6
|
+
height: 100%;
|
|
7
|
+
position: relative;
|
|
8
|
+
|
|
9
|
+
.headroom {
|
|
10
|
+
position: relative !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body.preview:not(&) {
|
|
14
|
+
min-width: 0 !important;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
export { Wrapper };
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { CheckField, SiteFilter, TableCounter } from "@ax/components";
|
|
3
|
+
import { CheckField, SiteFilter, TableCounter, DateFilter } from "@ax/components";
|
|
4
4
|
import * as S from "./style";
|
|
5
5
|
|
|
6
|
-
const TableHeader = (props: IProps) => {
|
|
7
|
-
const {
|
|
6
|
+
const TableHeader = (props: IProps): JSX.Element => {
|
|
7
|
+
const {
|
|
8
|
+
totalItems,
|
|
9
|
+
selectAllItems,
|
|
10
|
+
isScrolling,
|
|
11
|
+
filterItems,
|
|
12
|
+
filterValues,
|
|
13
|
+
isSiteItem,
|
|
14
|
+
sortItems,
|
|
15
|
+
sortedListStatus,
|
|
16
|
+
} = props;
|
|
17
|
+
|
|
8
18
|
return (
|
|
9
19
|
<S.TableHeader isScrolling={isScrolling}>
|
|
10
20
|
<S.CheckHeader>
|
|
@@ -25,6 +35,7 @@ const TableHeader = (props: IProps) => {
|
|
|
25
35
|
)}
|
|
26
36
|
<S.UrlHeader>Old URL</S.UrlHeader>
|
|
27
37
|
<S.UrlHeader>New URL</S.UrlHeader>
|
|
38
|
+
<DateFilter sortItems={sortItems} sortedState={sortedListStatus} />
|
|
28
39
|
<S.ActionsHeader>
|
|
29
40
|
<TableCounter totalItems={totalItems} />
|
|
30
41
|
</S.ActionsHeader>
|
|
@@ -39,6 +50,8 @@ interface IProps {
|
|
|
39
50
|
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
40
51
|
filterValues: any;
|
|
41
52
|
isSiteItem: boolean;
|
|
53
|
+
sortItems: (orderPointer: string, isAscending: boolean) => void;
|
|
54
|
+
sortedListStatus: { isAscending: boolean; sortedByDate: boolean };
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
export default TableHeader;
|
|
@@ -15,11 +15,11 @@ const CheckHeader = styled(Header)`
|
|
|
15
15
|
`;
|
|
16
16
|
|
|
17
17
|
const SiteHeader = styled(Header)`
|
|
18
|
-
width:
|
|
18
|
+
width: 200px;
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
21
|
const UrlHeader = styled(Header)`
|
|
22
|
-
|
|
22
|
+
flex: 1;
|
|
23
23
|
`;
|
|
24
24
|
|
|
25
25
|
const ActionsHeader = styled(Header)`
|
|
@@ -28,4 +28,10 @@ const ActionsHeader = styled(Header)`
|
|
|
28
28
|
justify-content: flex-end;
|
|
29
29
|
`;
|
|
30
30
|
|
|
31
|
-
export {
|
|
31
|
+
export {
|
|
32
|
+
TableHeader,
|
|
33
|
+
CheckHeader,
|
|
34
|
+
SiteHeader,
|
|
35
|
+
UrlHeader,
|
|
36
|
+
ActionsHeader,
|
|
37
|
+
};
|
|
@@ -14,6 +14,8 @@ const BulkHeader = (props: IProps): JSX.Element => {
|
|
|
14
14
|
filterItems,
|
|
15
15
|
filterValues,
|
|
16
16
|
isSiteItem,
|
|
17
|
+
sortItems,
|
|
18
|
+
sortedListStatus,
|
|
17
19
|
} = props;
|
|
18
20
|
|
|
19
21
|
const bulkActions = [
|
|
@@ -39,7 +41,9 @@ const BulkHeader = (props: IProps): JSX.Element => {
|
|
|
39
41
|
filterItems={filterItems}
|
|
40
42
|
filterValues={filterValues}
|
|
41
43
|
isSiteItem={isSiteItem}
|
|
42
|
-
|
|
44
|
+
sortItems={sortItems}
|
|
45
|
+
sortedListStatus={sortedListStatus}
|
|
46
|
+
/>
|
|
43
47
|
);
|
|
44
48
|
};
|
|
45
49
|
|
|
@@ -54,6 +58,8 @@ interface IProps {
|
|
|
54
58
|
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
55
59
|
filterValues: any;
|
|
56
60
|
isSiteItem: boolean;
|
|
61
|
+
sortItems: (orderPointer: string, isAscending: boolean) => void;
|
|
62
|
+
sortedListStatus: { isAscending: boolean; sortedByDate: boolean };
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
export default BulkHeader;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
+
import { format } from "date-fns";
|
|
3
4
|
|
|
4
5
|
import { useModal } from "@ax/hooks";
|
|
5
6
|
import { ICheck, IRedirect } from "@ax/types";
|
|
@@ -84,6 +85,9 @@ const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
|
|
|
84
85
|
<S.UrlCell role="cell" onClick={handleClick}>
|
|
85
86
|
{redirect.to?.url.replace(regex, "")}
|
|
86
87
|
</S.UrlCell>
|
|
88
|
+
<S.DateCell role="cell" onClick={handleClick}>
|
|
89
|
+
{redirect.date && format(new Date(redirect.date), "dd MMM yyyy")}
|
|
90
|
+
</S.DateCell>
|
|
87
91
|
<S.ActionsCell role="cell">
|
|
88
92
|
<S.StyledActionMenu icon="more" options={menuOptions} tooltip="Actions" />
|
|
89
93
|
</S.ActionsCell>
|
|
@@ -12,12 +12,19 @@ const CheckCell = styled(Cell)`
|
|
|
12
12
|
`;
|
|
13
13
|
|
|
14
14
|
const SiteCell = styled(Cell)`
|
|
15
|
-
width:
|
|
15
|
+
width: 200px;
|
|
16
16
|
`;
|
|
17
17
|
|
|
18
18
|
const UrlCell = styled(Cell)`
|
|
19
19
|
${(p) => p.theme.textStyle.uiXS};
|
|
20
|
-
|
|
20
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
21
|
+
flex: 1;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const DateCell = styled(Cell)`
|
|
25
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
26
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
27
|
+
width: 100px;
|
|
21
28
|
`;
|
|
22
29
|
|
|
23
30
|
const ActionsCell = styled(Cell)`
|
|
@@ -47,4 +54,13 @@ const ModalContent = styled.div`
|
|
|
47
54
|
}
|
|
48
55
|
`;
|
|
49
56
|
|
|
50
|
-
export {
|
|
57
|
+
export {
|
|
58
|
+
CheckCell,
|
|
59
|
+
SiteCell,
|
|
60
|
+
ActionsCell,
|
|
61
|
+
UrlCell,
|
|
62
|
+
StyledActionMenu,
|
|
63
|
+
ItemRow,
|
|
64
|
+
ModalContent,
|
|
65
|
+
DateCell,
|
|
66
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
const useSortedListStatus = () => {
|
|
4
|
+
const sortedInitialState: { isAscending: boolean; sortedByDate: boolean; } = {
|
|
5
|
+
isAscending: false,
|
|
6
|
+
sortedByDate: false,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const [sortedListStatus, setSortedListStatus] = useState(sortedInitialState);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
sortedListStatus,
|
|
13
|
+
setSortedListStatus,
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const useFilterQuery = (currentSiteID: number | null) => {
|
|
18
|
+
const initialQueryValues = {
|
|
19
|
+
sites: currentSiteID || "all",
|
|
20
|
+
order: "",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const [query, setQuery] = useState(initialQueryValues);
|
|
24
|
+
|
|
25
|
+
const setFilterQuery = (filterValues: any) => {
|
|
26
|
+
const { sites, order } = filterValues;
|
|
27
|
+
let filterQuery = "";
|
|
28
|
+
|
|
29
|
+
const currentQuery = (pointer: string, values: string) => {
|
|
30
|
+
return filterQuery.concat(`&${pointer}=${values}`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (sites) {
|
|
34
|
+
filterQuery = currentQuery("sites", sites);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (order) {
|
|
38
|
+
filterQuery = currentQuery("order", order);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return filterQuery;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const setFiltersSelection = (pointer: string, filter: string, isAscendent?: boolean) => {
|
|
45
|
+
const { sites, order } = query;
|
|
46
|
+
const orderMethod = isAscendent ? "asc" : "desc";
|
|
47
|
+
const filterValues = {
|
|
48
|
+
sites: pointer === "sites" ? filter : sites,
|
|
49
|
+
order: pointer === "order" ? `${filter}-${orderMethod}` : order,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
setQuery(filterValues);
|
|
53
|
+
|
|
54
|
+
return filterValues;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const resetFilterQuery = () => setQuery(initialQueryValues);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
setFiltersSelection,
|
|
61
|
+
setFilterQuery,
|
|
62
|
+
resetFilterQuery,
|
|
63
|
+
filterValues: query,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export { useSortedListStatus, useFilterQuery };
|
|
@@ -6,10 +6,13 @@ import { appActions } from "@ax/containers/App";
|
|
|
6
6
|
import { redirectsActions } from "@ax/containers/Redirects";
|
|
7
7
|
import { MainWrapper, ErrorToast, Nav, TableList, EmptyState, Toast } from "@ax/components";
|
|
8
8
|
import { useBulkSelection, useModal, useToast } from "@ax/hooks";
|
|
9
|
+
|
|
9
10
|
import BulkHeader from "./BulkHeader";
|
|
10
11
|
import RedirectItem from "./RedirectItem";
|
|
11
12
|
import RedirectPanel from "./RedirectPanel";
|
|
12
13
|
import { DeleteModal, ImportCheckModal, ImportModal, OverwriteModal } from "./atoms";
|
|
14
|
+
import { getSortedListStatus } from "./utils";
|
|
15
|
+
import { useSortedListStatus, useFilterQuery } from "./hooks";
|
|
13
16
|
|
|
14
17
|
import * as S from "./style";
|
|
15
18
|
|
|
@@ -30,14 +33,16 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
30
33
|
} = props;
|
|
31
34
|
|
|
32
35
|
const itemsPerPage = 50;
|
|
36
|
+
const firstPage = 1;
|
|
33
37
|
const [page, setPage] = useState(1);
|
|
34
38
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
35
39
|
const [isOpenedSecond, setIsOpenedSecond] = useState(false);
|
|
36
40
|
const { isOpen, toggleModal } = useModal();
|
|
37
41
|
const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
|
|
38
42
|
const tableRef = useRef<HTMLDivElement>(null);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
43
|
+
const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
|
|
44
|
+
const { setFiltersSelection, setFilterQuery, filterValues } = useFilterQuery(currentSiteID);
|
|
45
|
+
const [currentFilterQuery, setCurrentFilterQuery] = useState(`&sites=${currentSiteID}`);
|
|
41
46
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
42
47
|
const { isVisible: isImportVisible, toggleToast: toggleImportToast, setIsVisible: setIsImportVisible } = useToast();
|
|
43
48
|
const { isOpen: isOpenOverwrite, toggleModal: toggleOverwriteModal } = useModal();
|
|
@@ -71,8 +76,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
71
76
|
|
|
72
77
|
useEffect(() => {
|
|
73
78
|
const params = getParams();
|
|
74
|
-
|
|
75
|
-
getRedirects(params, siteFilterQuery);
|
|
79
|
+
getRedirects(params, currentFilterQuery);
|
|
76
80
|
if (tableRef.current) {
|
|
77
81
|
tableRef.current.scrollTo(0, 0);
|
|
78
82
|
}
|
|
@@ -103,12 +107,20 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
103
107
|
|
|
104
108
|
const handleSelectAll = () => selectAllItems();
|
|
105
109
|
|
|
110
|
+
const sortItems = async (orderPointer: string, isAscending: boolean) => {
|
|
111
|
+
setPage(firstPage);
|
|
112
|
+
const sortedState = getSortedListStatus(orderPointer, isAscending);
|
|
113
|
+
setSortedListStatus(sortedState);
|
|
114
|
+
|
|
115
|
+
const filtersSelection = setFiltersSelection("order", orderPointer, isAscending);
|
|
116
|
+
const filterQuery = setFilterQuery(filtersSelection);
|
|
117
|
+
setCurrentFilterQuery(filterQuery);
|
|
118
|
+
};
|
|
119
|
+
|
|
106
120
|
const filterItems = async (filterPointer: string, filtersSelected: string) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
setFilterValues({ sites: filtersSelected });
|
|
121
|
+
setPage(firstPage);
|
|
122
|
+
const filtersSelection = setFiltersSelection(filterPointer, filtersSelected);
|
|
123
|
+
const filterQuery = setFilterQuery(filtersSelection);
|
|
112
124
|
setCurrentFilterQuery(filterQuery);
|
|
113
125
|
};
|
|
114
126
|
|
|
@@ -124,6 +136,8 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
124
136
|
filterItems={filterItems}
|
|
125
137
|
filterValues={filterValues}
|
|
126
138
|
isSiteItem={!!currentSiteID}
|
|
139
|
+
sortItems={sortItems}
|
|
140
|
+
sortedListStatus={sortedListStatus}
|
|
127
141
|
/>
|
|
128
142
|
);
|
|
129
143
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const getSortedListStatus = (orderPointer: string, isAscending: boolean): { isAscending: boolean, sortedByDate: boolean } => {
|
|
2
|
+
const sortedListStatus = {
|
|
3
|
+
isAscending,
|
|
4
|
+
sortedByDate: orderPointer === "date",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
return sortedListStatus;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { getSortedListStatus }
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { useModal } from "@ax/hooks";
|
|
4
|
+
import { SideModal, ActionMenu } from "@ax/components";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const pageEditorID = 0;
|
|
9
|
+
|
|
10
|
+
const Field = (props: IField): JSX.Element => {
|
|
11
|
+
const {
|
|
12
|
+
type,
|
|
13
|
+
template,
|
|
14
|
+
defaults,
|
|
15
|
+
updateEditorContent,
|
|
16
|
+
content,
|
|
17
|
+
updateDataPackFormValue,
|
|
18
|
+
dataPackConfigFormData,
|
|
19
|
+
theme,
|
|
20
|
+
} = props;
|
|
21
|
+
const { isOpen, toggleModal } = useModal();
|
|
22
|
+
const options = defaults.filter((component: any) =>
|
|
23
|
+
component.type === type && content[component.type]?.id !== component.id && !component.setAsDefault
|
|
24
|
+
);
|
|
25
|
+
const optionsType = `${type}s`;
|
|
26
|
+
|
|
27
|
+
const actionMenuOptions = [
|
|
28
|
+
{
|
|
29
|
+
label: "replace",
|
|
30
|
+
icon: "change",
|
|
31
|
+
action: toggleModal,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const actionMenuIcon = "more";
|
|
36
|
+
|
|
37
|
+
const handleReplace = (option: any) => {
|
|
38
|
+
const configFormData = {
|
|
39
|
+
...dataPackConfigFormData,
|
|
40
|
+
templates: {
|
|
41
|
+
...dataPackConfigFormData.templates,
|
|
42
|
+
[template]: {
|
|
43
|
+
...(!!dataPackConfigFormData.templates && dataPackConfigFormData.templates[template]),
|
|
44
|
+
...(type === "header" && { defaultHeader: option.id }),
|
|
45
|
+
...(type === "footer" && { defaultFooter: option.id }),
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
updateDataPackFormValue(configFormData);
|
|
50
|
+
updateEditorContent(pageEditorID, type, option.id);
|
|
51
|
+
toggleModal();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const isDefaultNavigation = () => {
|
|
55
|
+
const defaultType = type === "header" ? "defaultHeader" : "defaultFooter";
|
|
56
|
+
const templateNavigation = dataPackConfigFormData.templates && dataPackConfigFormData.templates[template]
|
|
57
|
+
const templateDefaultNavigation = templateNavigation && templateNavigation[defaultType];
|
|
58
|
+
if (!templateDefaultNavigation) return true;
|
|
59
|
+
const siteDefaultNavigation = defaults.find((navigation: any) => navigation.id === templateDefaultNavigation);
|
|
60
|
+
return !!siteDefaultNavigation?.isDefault;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const setDefault = {
|
|
64
|
+
title: `Put the default ${type}`,
|
|
65
|
+
action: () => handleReplace({ id: null }),
|
|
66
|
+
checked: isDefaultNavigation(),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const hasOptions: boolean | undefined = options?.length > 0 || !isDefaultNavigation();
|
|
70
|
+
|
|
71
|
+
if (!content[type]) return <></>;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<S.Component isArray disabled>
|
|
76
|
+
<S.Label>{content[type].title}</S.Label>
|
|
77
|
+
<S.HiddenButtonsWrapper>
|
|
78
|
+
{hasOptions && <ActionMenu options={actionMenuOptions} icon={actionMenuIcon} />}
|
|
79
|
+
</S.HiddenButtonsWrapper>
|
|
80
|
+
</S.Component>
|
|
81
|
+
{hasOptions && (
|
|
82
|
+
<SideModal
|
|
83
|
+
optionsType={optionsType}
|
|
84
|
+
whiteList={options}
|
|
85
|
+
handleClick={handleReplace}
|
|
86
|
+
toggleModal={toggleModal}
|
|
87
|
+
isOpen={isOpen}
|
|
88
|
+
setDefault={setDefault}
|
|
89
|
+
theme={theme}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
</>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface IField {
|
|
97
|
+
type: string;
|
|
98
|
+
template: string;
|
|
99
|
+
defaults: any;
|
|
100
|
+
content: any,
|
|
101
|
+
dataPackConfigFormData: any;
|
|
102
|
+
theme: string;
|
|
103
|
+
updateEditorContent: (selectedEditorID: number, key: string, value: any) => void;
|
|
104
|
+
updateDataPackFormValue: (value: any) => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { Field }
|