@griddo/ax 1.60.7 → 1.61.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 +4 -2
- package/public/fonts/fonts.css +48 -660
- package/public/index.html +19 -16
- package/public/templates/template-redirects.csv +2 -0
- package/src/GlobalStore.tsx +3 -0
- package/src/Style/fonts.tsx +98 -72
- package/src/api/analytics.tsx +78 -0
- package/src/api/index.tsx +2 -0
- package/src/api/redirects.tsx +30 -2
- package/src/components/Button/style.tsx +1 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +1 -1
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +4 -3
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +6 -1
- package/src/components/FieldContainer/index.tsx +2 -2
- package/src/components/Fields/AnalyticsField/PageAnalytics/atoms.tsx +75 -0
- package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +139 -0
- package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +77 -0
- package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +89 -0
- package/src/components/Fields/AnalyticsField/index.tsx +38 -0
- package/src/components/Fields/AnalyticsField/style.tsx +13 -0
- package/src/components/Fields/AnalyticsField/utils.tsx +13 -0
- package/src/components/Fields/FieldGroup/style.tsx +1 -1
- package/src/components/Fields/ImageField/index.tsx +1 -1
- package/src/components/Fields/MultiCheckSelect/index.tsx +23 -6
- package/src/components/Fields/MultiCheckSelect/style.tsx +1 -0
- package/src/components/Fields/NoteField/index.tsx +1 -1
- package/src/components/Fields/RadioGroup/index.tsx +4 -3
- package/src/components/Fields/RadioGroup/style.tsx +8 -3
- package/src/components/Fields/RichText/index.tsx +13 -8
- package/src/components/Fields/TextArea/index.tsx +1 -3
- package/src/components/Fields/TextArea/style.tsx +3 -3
- package/src/components/Fields/UrlField/style.tsx +1 -0
- package/src/components/Fields/index.tsx +2 -0
- package/src/components/Modal/index.tsx +1 -1
- package/src/components/Modal/style.tsx +5 -1
- package/src/components/Notification/style.tsx +2 -5
- package/src/components/TableFilters/LiveFilter/index.tsx +4 -0
- package/src/components/TableFilters/NameFilter/style.tsx +1 -0
- package/src/components/TableFilters/TypeFilter/index.tsx +7 -2
- package/src/containers/Analytics/actions.tsx +58 -0
- package/src/containers/Analytics/constants.tsx +5 -0
- package/src/containers/Analytics/index.tsx +4 -0
- package/src/containers/Analytics/interfaces.tsx +9 -0
- package/src/containers/Analytics/reducer.tsx +26 -0
- package/src/containers/PageEditor/actions.tsx +1 -0
- package/src/containers/Redirects/actions.tsx +49 -3
- package/src/containers/Redirects/constants.tsx +12 -1
- package/src/containers/Redirects/interfaces.tsx +11 -1
- package/src/containers/Redirects/reducer.tsx +12 -1
- package/src/containers/Sites/actions.tsx +3 -0
- package/src/global.d.ts +2 -0
- package/src/helpers/index.tsx +4 -0
- package/src/helpers/requests.tsx +12 -7
- package/src/helpers/strings.tsx +13 -0
- package/src/hooks/forms.tsx +1 -1
- package/src/index.tsx +1 -0
- package/src/modules/Analytics/DimensionItem/index.tsx +71 -0
- package/src/modules/Analytics/DimensionItem/style.tsx +59 -0
- package/src/modules/Analytics/DimensionPanel/index.tsx +110 -0
- package/src/modules/Analytics/DimensionPanel/style.tsx +14 -0
- package/src/modules/Analytics/GroupItem/index.tsx +75 -0
- package/src/modules/Analytics/GroupItem/style.tsx +80 -0
- package/src/modules/Analytics/GroupPanel/index.tsx +178 -0
- package/src/modules/Analytics/GroupPanel/style.tsx +67 -0
- package/src/modules/Analytics/GroupPanel/utils.tsx +29 -0
- package/src/modules/Analytics/index.tsx +207 -0
- package/src/modules/Analytics/style.tsx +68 -0
- package/src/modules/Content/BulkHeader/TableHeader/index.tsx +1 -1
- package/src/modules/Content/PageItem/index.tsx +3 -3
- package/src/modules/Content/PageItem/style.tsx +1 -1
- package/src/modules/Content/hooks.tsx +2 -2
- package/src/modules/Content/index.tsx +22 -20
- package/src/modules/GlobalEditor/index.tsx +1 -1
- package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +2 -2
- package/src/modules/PageEditor/index.tsx +1 -1
- package/src/modules/Redirects/RedirectItem/index.tsx +6 -2
- package/src/modules/Redirects/RedirectPanel/index.tsx +2 -0
- package/src/modules/Redirects/atoms.tsx +212 -0
- package/src/modules/Redirects/index.tsx +85 -27
- package/src/modules/Redirects/style.tsx +124 -3
- package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +1 -1
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +12 -3
- package/src/modules/StructuredData/StructuredDataList/index.tsx +17 -2
- package/src/routes/multisite.tsx +20 -2
- package/src/schemas/pages/GlobalPage.tsx +75 -64
- package/src/schemas/pages/Page.tsx +79 -67
- package/src/types/index.tsx +25 -3
- package/public/fonts/Source_Sans_Pro-200-cyrillic-ext107.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-cyrillic-ext149.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-cyrillic108.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-cyrillic150.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-greek-ext109.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-greek-ext151.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-greek110.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-greek152.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-vietnamese111.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-200-vietnamese153.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-cyrillic-ext114.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-cyrillic-ext156.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-cyrillic115.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-cyrillic157.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-greek-ext116.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-greek-ext158.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-greek117.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-greek159.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-vietnamese118.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-300-vietnamese160.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-cyrillic-ext121.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-cyrillic-ext163.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-cyrillic122.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-cyrillic164.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-greek-ext123.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-greek-ext165.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-greek124.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-greek166.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-vietnamese125.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-400-vietnamese167.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-cyrillic-ext128.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-cyrillic-ext170.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-cyrillic129.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-cyrillic171.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-greek-ext130.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-greek-ext172.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-greek131.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-greek173.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-vietnamese132.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-600-vietnamese174.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-cyrillic-ext135.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-cyrillic-ext177.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-cyrillic136.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-cyrillic178.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-greek-ext137.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-greek-ext179.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-greek138.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-greek180.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-vietnamese139.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-700-vietnamese181.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-cyrillic-ext142.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-cyrillic-ext184.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-cyrillic143.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-cyrillic185.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-greek-ext144.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-greek-ext186.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-greek145.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-greek187.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-vietnamese146.woff2 +0 -0
- package/public/fonts/Source_Sans_Pro-900-vietnamese188.woff2 +0 -0
|
@@ -59,8 +59,12 @@ const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
|
|
|
59
59
|
title: "Delete redirect",
|
|
60
60
|
onClick: removeItem,
|
|
61
61
|
};
|
|
62
|
+
|
|
62
63
|
const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };
|
|
63
64
|
|
|
65
|
+
const target = `(${redirect.domain || ""}|${redirect.site?.siteUrl || ""})`;
|
|
66
|
+
const regex = new RegExp(target, "g");
|
|
67
|
+
|
|
64
68
|
return (
|
|
65
69
|
<>
|
|
66
70
|
<S.ItemRow role="rowgroup" selected={isSelected}>
|
|
@@ -75,10 +79,10 @@ const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
|
|
|
75
79
|
</S.SiteCell>
|
|
76
80
|
)}
|
|
77
81
|
<S.UrlCell role="cell" onClick={handleClick}>
|
|
78
|
-
{redirect.from}
|
|
82
|
+
{redirect.from.replace(regex, "")}
|
|
79
83
|
</S.UrlCell>
|
|
80
84
|
<S.UrlCell role="cell" onClick={handleClick}>
|
|
81
|
-
{redirect.to?.url}
|
|
85
|
+
{redirect.to?.url.replace(regex, "")}
|
|
82
86
|
</S.UrlCell>
|
|
83
87
|
<S.ActionsCell role="cell">
|
|
84
88
|
<S.StyledActionMenu icon="more" options={menuOptions} tooltip="Actions" />
|
|
@@ -67,6 +67,7 @@ const RedirectPanel = (props: IProps): JSX.Element => {
|
|
|
67
67
|
value={formValues.from}
|
|
68
68
|
onChange={handleOldUrl}
|
|
69
69
|
autoComplete="redirect-old"
|
|
70
|
+
helptext="Type the complete old page url you want to redirect."
|
|
70
71
|
/>
|
|
71
72
|
<FieldsBehavior
|
|
72
73
|
title="New URL"
|
|
@@ -77,6 +78,7 @@ const RedirectPanel = (props: IProps): JSX.Element => {
|
|
|
77
78
|
handlePanel={toggleSecondaryPanel}
|
|
78
79
|
inFloatingPanel={true}
|
|
79
80
|
autoComplete="redirect-new"
|
|
81
|
+
helptext="Select an internal page or paste an external url."
|
|
80
82
|
/>
|
|
81
83
|
<S.Footer>
|
|
82
84
|
{redirect ? (
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
|
|
3
|
+
import { IModal, IRedirect } from "@ax/types";
|
|
4
|
+
import { Button, Icon, Modal } from "@ax/components";
|
|
5
|
+
import { trimText } from "@ax/helpers";
|
|
6
|
+
|
|
7
|
+
import * as S from "./style";
|
|
8
|
+
|
|
9
|
+
const DeleteModal = (props: IModal): JSX.Element => {
|
|
10
|
+
const { isOpen, toggleModal, mainModalAction, secondaryModalAction } = props;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<Modal
|
|
14
|
+
isOpen={isOpen}
|
|
15
|
+
hide={toggleModal}
|
|
16
|
+
title="Delete Redirects"
|
|
17
|
+
secondaryAction={secondaryModalAction}
|
|
18
|
+
mainAction={mainModalAction}
|
|
19
|
+
size="S"
|
|
20
|
+
>
|
|
21
|
+
<S.ModalContent>
|
|
22
|
+
Are you sure you want to delete the selected <strong>redirects</strong>? This action{" "}
|
|
23
|
+
<strong>cannot be undone</strong>.
|
|
24
|
+
</S.ModalContent>
|
|
25
|
+
</Modal>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const OverwriteModal = (props: IModal): JSX.Element => {
|
|
30
|
+
const { isOpen, toggleModal, mainModalAction, secondaryModalAction } = props;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Modal
|
|
34
|
+
isOpen={isOpen}
|
|
35
|
+
hide={toggleModal}
|
|
36
|
+
title="Overwrite Redirect"
|
|
37
|
+
secondaryAction={secondaryModalAction}
|
|
38
|
+
mainAction={mainModalAction}
|
|
39
|
+
size="S"
|
|
40
|
+
>
|
|
41
|
+
<S.ModalContent>
|
|
42
|
+
This Redirect is already created. It will overwrite.
|
|
43
|
+
<br />
|
|
44
|
+
This action <strong>cannot be undone</strong>.
|
|
45
|
+
</S.ModalContent>
|
|
46
|
+
</Modal>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const ImportModal = (props: IImportModal): JSX.Element => {
|
|
51
|
+
const { isOpen, toggleModal, checkImportData, isUploading, setIsUploading } = props;
|
|
52
|
+
|
|
53
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
54
|
+
|
|
55
|
+
const handleUploadClick = () => fileInputRef.current && fileInputRef.current.click();
|
|
56
|
+
|
|
57
|
+
const csvToArray = (str: string, delimiter = ";") => {
|
|
58
|
+
const fixedStr = str.replace(/,/g, ";").replace(/\r/g, "");
|
|
59
|
+
const rows = fixedStr.slice(fixedStr.indexOf("\n") + 1).split("\n");
|
|
60
|
+
|
|
61
|
+
const arr = rows.map((row: string) => {
|
|
62
|
+
const values = row.split(delimiter);
|
|
63
|
+
return {
|
|
64
|
+
from: values[0],
|
|
65
|
+
to: values[1],
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return arr;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleFileChange = (e: any) => {
|
|
73
|
+
setIsUploading(true);
|
|
74
|
+
const file = e.currentTarget.files[0];
|
|
75
|
+
const reader = new FileReader();
|
|
76
|
+
|
|
77
|
+
reader.onload = (e: any) => {
|
|
78
|
+
const text = e.target.result;
|
|
79
|
+
const data = csvToArray(text);
|
|
80
|
+
checkImportData(data);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
reader.readAsText(file);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Modal isOpen={isOpen} hide={toggleModal} title="Import Redirects" size="M">
|
|
88
|
+
<S.ModalCenterContent>
|
|
89
|
+
{isUploading ? (
|
|
90
|
+
<>
|
|
91
|
+
<S.ModalIconWrapper>
|
|
92
|
+
<Icon name="uploadFile" size="48" />
|
|
93
|
+
</S.ModalIconWrapper>
|
|
94
|
+
<S.ModalUploadingText>Uploading...</S.ModalUploadingText>
|
|
95
|
+
</>
|
|
96
|
+
) : (
|
|
97
|
+
<S.ModalContentWrapper>
|
|
98
|
+
<S.ModalIcon>
|
|
99
|
+
<Icon name="uploadFile" size="32" />
|
|
100
|
+
</S.ModalIcon>
|
|
101
|
+
<S.ModalTitle>Import 301 Redirects</S.ModalTitle>
|
|
102
|
+
<S.ModalText>
|
|
103
|
+
Import your URL redirects by uploading a CSV file.
|
|
104
|
+
<br />
|
|
105
|
+
To use the right format, please download the template available below.
|
|
106
|
+
</S.ModalText>
|
|
107
|
+
<S.ModalButtons>
|
|
108
|
+
<input
|
|
109
|
+
onChange={handleFileChange}
|
|
110
|
+
multiple={false}
|
|
111
|
+
ref={fileInputRef}
|
|
112
|
+
type="file"
|
|
113
|
+
accept=".csv,.xlsx,.xls"
|
|
114
|
+
hidden
|
|
115
|
+
/>
|
|
116
|
+
<Button className="button" type="button" buttonStyle="line">
|
|
117
|
+
<a
|
|
118
|
+
href={process.env.PUBLIC_URL + "/templates/template-redirects.csv"}
|
|
119
|
+
download={"template-redirects.csv"}
|
|
120
|
+
>
|
|
121
|
+
Download Template
|
|
122
|
+
</a>
|
|
123
|
+
</Button>
|
|
124
|
+
<Button className="button" type="button" onClick={handleUploadClick}>
|
|
125
|
+
Upload CSV file
|
|
126
|
+
</Button>
|
|
127
|
+
</S.ModalButtons>
|
|
128
|
+
</S.ModalContentWrapper>
|
|
129
|
+
)}
|
|
130
|
+
</S.ModalCenterContent>
|
|
131
|
+
</Modal>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const ImportCheckModal = (props: IImportCheckModal): JSX.Element => {
|
|
136
|
+
const { isOpen, toggleModal, mainModalAction, secondaryModalAction, imports, totalImports } = props;
|
|
137
|
+
|
|
138
|
+
const mapModalItems = (items: IRedirect[], icon: string, type: "online" | "error" | "info") => {
|
|
139
|
+
return items.map((item: IRedirect) => (
|
|
140
|
+
<S.ModalImportItem key={item.from}>
|
|
141
|
+
<S.ModalItemIcon type={type}>
|
|
142
|
+
<Icon name={icon} size="16" />
|
|
143
|
+
</S.ModalItemIcon>
|
|
144
|
+
<S.ModalUrl>{item.from}</S.ModalUrl>
|
|
145
|
+
<S.ModalItemArrow>
|
|
146
|
+
<Icon name="fullArrowRight" size="16" />
|
|
147
|
+
</S.ModalItemArrow>
|
|
148
|
+
<S.ModalUrl>{typeof item.to === "string" && item.to}</S.ModalUrl>
|
|
149
|
+
</S.ModalImportItem>
|
|
150
|
+
));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<Modal
|
|
155
|
+
isOpen={isOpen}
|
|
156
|
+
hide={toggleModal}
|
|
157
|
+
mainAction={mainModalAction}
|
|
158
|
+
secondaryAction={secondaryModalAction}
|
|
159
|
+
title="Import Redirects"
|
|
160
|
+
size="L"
|
|
161
|
+
>
|
|
162
|
+
{imports && (
|
|
163
|
+
<S.ModalContent>
|
|
164
|
+
<S.ModalImportResult>
|
|
165
|
+
We found <strong>{totalImports} redirects</strong>. Continue to finish the uploading.
|
|
166
|
+
</S.ModalImportResult>
|
|
167
|
+
{imports.error.length > 0 && (
|
|
168
|
+
<S.ModalImportWrapper>
|
|
169
|
+
<S.ModalImportSection>
|
|
170
|
+
<strong>{imports.error.length}</strong> redirects that do not correspond to any site.
|
|
171
|
+
</S.ModalImportSection>
|
|
172
|
+
{mapModalItems(imports.error, "close", "error")}
|
|
173
|
+
</S.ModalImportWrapper>
|
|
174
|
+
)}
|
|
175
|
+
{imports.existing.length > 0 && (
|
|
176
|
+
<S.ModalImportWrapper>
|
|
177
|
+
<S.ModalImportSection>
|
|
178
|
+
<strong>{imports.existing.length}</strong> redirects already created. It will overwrite.
|
|
179
|
+
</S.ModalImportSection>
|
|
180
|
+
{mapModalItems(imports.existing, "info", "info")}
|
|
181
|
+
</S.ModalImportWrapper>
|
|
182
|
+
)}
|
|
183
|
+
{imports.ok.length > 0 && (
|
|
184
|
+
<S.ModalImportWrapper>
|
|
185
|
+
<S.ModalImportSection>
|
|
186
|
+
<strong>{imports.ok.length}</strong> redirects imported correctly.
|
|
187
|
+
</S.ModalImportSection>
|
|
188
|
+
{mapModalItems(imports.ok, "successSolid", "online")}
|
|
189
|
+
</S.ModalImportWrapper>
|
|
190
|
+
)}
|
|
191
|
+
</S.ModalContent>
|
|
192
|
+
)}
|
|
193
|
+
</Modal>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
interface IImportModal extends IModal {
|
|
198
|
+
isUploading: boolean;
|
|
199
|
+
setIsUploading: React.Dispatch<React.SetStateAction<boolean>>;
|
|
200
|
+
checkImportData: (data: { from: string; to: string }[]) => void;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
interface IImportCheckModal extends IModal {
|
|
204
|
+
totalImports: number;
|
|
205
|
+
imports: null | {
|
|
206
|
+
error: IRedirect[];
|
|
207
|
+
existing: IRedirect[];
|
|
208
|
+
ok: IRedirect[];
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export { DeleteModal, ImportModal, OverwriteModal, ImportCheckModal };
|
|
@@ -4,11 +4,12 @@ import { connect } from "react-redux";
|
|
|
4
4
|
import { INavItem, IRedirect, IRootState } from "@ax/types";
|
|
5
5
|
import { appActions } from "@ax/containers/App";
|
|
6
6
|
import { redirectsActions } from "@ax/containers/Redirects";
|
|
7
|
-
import { MainWrapper, ErrorToast, Nav, TableList, EmptyState,
|
|
7
|
+
import { MainWrapper, ErrorToast, Nav, TableList, EmptyState, Toast } from "@ax/components";
|
|
8
8
|
import { useBulkSelection, useModal, useToast } from "@ax/hooks";
|
|
9
9
|
import BulkHeader from "./BulkHeader";
|
|
10
10
|
import RedirectItem from "./RedirectItem";
|
|
11
11
|
import RedirectPanel from "./RedirectPanel";
|
|
12
|
+
import { DeleteModal, ImportCheckModal, ImportModal, OverwriteModal } from "./atoms";
|
|
12
13
|
|
|
13
14
|
import * as S from "./style";
|
|
14
15
|
|
|
@@ -23,6 +24,9 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
23
24
|
deleteRedirect,
|
|
24
25
|
addRedirect,
|
|
25
26
|
currentSiteID,
|
|
27
|
+
importRedirects,
|
|
28
|
+
imports,
|
|
29
|
+
totalImports,
|
|
26
30
|
} = props;
|
|
27
31
|
|
|
28
32
|
const itemsPerPage = 50;
|
|
@@ -35,7 +39,12 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
35
39
|
const [currentFilterQuery, setCurrentFilterQuery] = useState("");
|
|
36
40
|
const [filterValues, setFilterValues] = useState({ sites: "all" });
|
|
37
41
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
42
|
+
const { isVisible: isImportVisible, toggleToast: toggleImportToast, setIsVisible: setIsImportVisible } = useToast();
|
|
38
43
|
const { isOpen: isOpenOverwrite, toggleModal: toggleOverwriteModal } = useModal();
|
|
44
|
+
const { isOpen: isOpenImport, toggleModal: toggleImportModal } = useModal();
|
|
45
|
+
const { isOpen: isOpenCheckImport, toggleModal: toggleCheckImportModal } = useModal();
|
|
46
|
+
const [importData, setImportData] = useState<{ from: string; to: string }[]>([]);
|
|
47
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
39
48
|
|
|
40
49
|
const initState = {
|
|
41
50
|
from: "",
|
|
@@ -127,6 +136,11 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
127
136
|
action: () => handleModal(),
|
|
128
137
|
};
|
|
129
138
|
|
|
139
|
+
const rightLineButtonProps = {
|
|
140
|
+
label: "Import Redirects",
|
|
141
|
+
action: () => toggleImportModal(),
|
|
142
|
+
};
|
|
143
|
+
|
|
130
144
|
const handleMenuClick = (path: string) => {
|
|
131
145
|
setHistoryPush(path);
|
|
132
146
|
};
|
|
@@ -166,6 +180,11 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
166
180
|
message: "Redirect deleted",
|
|
167
181
|
};
|
|
168
182
|
|
|
183
|
+
const toastImportProps = {
|
|
184
|
+
setIsVisible: setIsImportVisible,
|
|
185
|
+
message: `${(imports?.ok.length || 0) + (imports?.existing.length || 0)} Redirects imported`,
|
|
186
|
+
};
|
|
187
|
+
|
|
169
188
|
const mainDeleteModalAction = {
|
|
170
189
|
title: "Delete redirects",
|
|
171
190
|
onClick: bulkDelete,
|
|
@@ -180,9 +199,37 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
180
199
|
|
|
181
200
|
const secondaryOverwriteModalAction = { title: "Cancel", onClick: toggleOverwriteModal };
|
|
182
201
|
|
|
202
|
+
const handleCheckImportData = async (data: { from: string; to: string }[]) => {
|
|
203
|
+
setImportData(data);
|
|
204
|
+
await importRedirects(data, true);
|
|
205
|
+
toggleImportModal();
|
|
206
|
+
setIsUploading(false);
|
|
207
|
+
toggleCheckImportModal();
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const handleImportData = async () => {
|
|
211
|
+
const imported = await importRedirects(importData, false);
|
|
212
|
+
if (imported) {
|
|
213
|
+
toggleImportToast();
|
|
214
|
+
}
|
|
215
|
+
toggleCheckImportModal();
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const mainImportModalAction = {
|
|
219
|
+
title: "Import correct directs",
|
|
220
|
+
onClick: handleImportData,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const secondaryImportModalAction = { title: "Cancel", onClick: toggleCheckImportModal };
|
|
224
|
+
|
|
183
225
|
return (
|
|
184
226
|
<>
|
|
185
|
-
<MainWrapper
|
|
227
|
+
<MainWrapper
|
|
228
|
+
backLink={false}
|
|
229
|
+
title="SEO Settings"
|
|
230
|
+
rightButton={rightButtonProps}
|
|
231
|
+
rightLineButton={rightLineButtonProps}
|
|
232
|
+
>
|
|
186
233
|
<S.Wrapper>
|
|
187
234
|
<Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
|
|
188
235
|
<S.ContentWrapper>
|
|
@@ -227,6 +274,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
227
274
|
</TableList>
|
|
228
275
|
</S.TableWrapper>
|
|
229
276
|
{isVisible && <Toast {...toastProps} />}
|
|
277
|
+
{isImportVisible && <Toast {...toastImportProps} />}
|
|
230
278
|
</S.ContentWrapper>
|
|
231
279
|
</S.Wrapper>
|
|
232
280
|
{isOpen && (
|
|
@@ -241,33 +289,33 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
241
289
|
currentFilter={currentFilterQuery}
|
|
242
290
|
/>
|
|
243
291
|
)}
|
|
244
|
-
<
|
|
292
|
+
<DeleteModal
|
|
245
293
|
isOpen={isOpenDelete}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
>
|
|
252
|
-
<S.ModalContent>
|
|
253
|
-
Are you sure you want to delete the selected <strong>redirects</strong>? This action{" "}
|
|
254
|
-
<strong>cannot be undone</strong>.
|
|
255
|
-
</S.ModalContent>
|
|
256
|
-
</Modal>
|
|
257
|
-
<Modal
|
|
294
|
+
toggleModal={toggleModalDelete}
|
|
295
|
+
secondaryModalAction={secondaryDeleteModalAction}
|
|
296
|
+
mainModalAction={mainDeleteModalAction}
|
|
297
|
+
/>
|
|
298
|
+
<OverwriteModal
|
|
258
299
|
isOpen={isOpenOverwrite}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
300
|
+
toggleModal={toggleOverwriteModal}
|
|
301
|
+
secondaryModalAction={secondaryOverwriteModalAction}
|
|
302
|
+
mainModalAction={mainOverwriteModalAction}
|
|
303
|
+
/>
|
|
304
|
+
<ImportModal
|
|
305
|
+
isOpen={isOpenImport}
|
|
306
|
+
toggleModal={toggleImportModal}
|
|
307
|
+
checkImportData={handleCheckImportData}
|
|
308
|
+
isUploading={isUploading}
|
|
309
|
+
setIsUploading={setIsUploading}
|
|
310
|
+
/>
|
|
311
|
+
<ImportCheckModal
|
|
312
|
+
isOpen={isOpenCheckImport}
|
|
313
|
+
toggleModal={toggleCheckImportModal}
|
|
314
|
+
mainModalAction={mainImportModalAction}
|
|
315
|
+
secondaryModalAction={secondaryImportModalAction}
|
|
316
|
+
imports={imports}
|
|
317
|
+
totalImports={totalImports}
|
|
318
|
+
/>
|
|
271
319
|
</MainWrapper>
|
|
272
320
|
</>
|
|
273
321
|
);
|
|
@@ -277,6 +325,8 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
277
325
|
redirects: state.redirects.redirects,
|
|
278
326
|
totalItems: state.redirects.totalItems,
|
|
279
327
|
currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
|
|
328
|
+
totalImports: state.redirects.totalImports,
|
|
329
|
+
imports: state.redirects.imports,
|
|
280
330
|
});
|
|
281
331
|
|
|
282
332
|
const mapDispatchToProps = {
|
|
@@ -284,6 +334,7 @@ const mapDispatchToProps = {
|
|
|
284
334
|
getRedirects: redirectsActions.getRedirects,
|
|
285
335
|
deleteRedirect: redirectsActions.deleteRedirect,
|
|
286
336
|
addRedirect: redirectsActions.addRedirect,
|
|
337
|
+
importRedirects: redirectsActions.importRedirects,
|
|
287
338
|
};
|
|
288
339
|
interface IRedirectsProps {
|
|
289
340
|
navItems: INavItem[];
|
|
@@ -291,6 +342,12 @@ interface IRedirectsProps {
|
|
|
291
342
|
redirects: IRedirect[];
|
|
292
343
|
totalItems: number;
|
|
293
344
|
currentSiteID: number | null;
|
|
345
|
+
totalImports: number;
|
|
346
|
+
imports: null | {
|
|
347
|
+
error: IRedirect[];
|
|
348
|
+
existing: IRedirect[];
|
|
349
|
+
ok: IRedirect[];
|
|
350
|
+
};
|
|
294
351
|
}
|
|
295
352
|
|
|
296
353
|
interface IDispatchProps {
|
|
@@ -303,6 +360,7 @@ interface IDispatchProps {
|
|
|
303
360
|
force?: boolean,
|
|
304
361
|
filter?: string
|
|
305
362
|
): Promise<void>;
|
|
363
|
+
importRedirects(redirects: { from: string; to: string | number }[], check: boolean): Promise<boolean>;
|
|
306
364
|
}
|
|
307
365
|
|
|
308
366
|
type IProps = IRedirectsProps & IDispatchProps;
|
|
@@ -43,10 +43,131 @@ const EmptyWrapper = styled.div`
|
|
|
43
43
|
|
|
44
44
|
const ModalContent = styled.div`
|
|
45
45
|
padding: ${(p) => p.theme.spacing.m};
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const ModalCenterContent = styled.div`
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
align-items: center;
|
|
53
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const ModalContentWrapper = styled.div`
|
|
57
|
+
width: 350px;
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const ModalIcon = styled.div`
|
|
61
|
+
display: flex;
|
|
62
|
+
background-color: ${(p) => p.theme.color.uiBackground03};
|
|
63
|
+
width: ${(p) => p.theme.spacing.xl};
|
|
64
|
+
height: ${(p) => p.theme.spacing.xl};
|
|
65
|
+
justify-content: center;
|
|
66
|
+
align-items: center;
|
|
67
|
+
border-radius: 50%;
|
|
68
|
+
margin: ${(p) => `0 auto ${p.theme.spacing.m} auto`};
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const ModalIconWrapper = styled.div`
|
|
72
|
+
width: ${(p) => p.theme.spacing.l};
|
|
73
|
+
height: ${(p) => p.theme.spacing.l};
|
|
74
|
+
margin-top: ${(p) => p.theme.spacing.xl};
|
|
75
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
const ModalUploadingText = styled.div`
|
|
79
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
80
|
+
color: ${(p) => p.theme.color.interactive01};
|
|
81
|
+
text-align: center;
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
const ModalTitle = styled.div`
|
|
85
|
+
${(p) => p.theme.textStyle.headingS};
|
|
86
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
87
|
+
text-align: center;
|
|
88
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
const ModalText = styled.div`
|
|
92
|
+
${(p) => p.theme.textStyle.uiM};
|
|
93
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
94
|
+
text-align: center;
|
|
95
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
96
|
+
`;
|
|
46
97
|
|
|
47
|
-
|
|
48
|
-
|
|
98
|
+
const ModalButtons = styled.div`
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: space-evenly;
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const ModalImportResult = styled.div`
|
|
104
|
+
${(p) => p.theme.textStyle.uiM};
|
|
105
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
106
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
const ModalImportWrapper = styled.div`
|
|
110
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
111
|
+
border-bottom: ${(p) => `1px solid ${p.theme.color.uiLine}`};
|
|
112
|
+
padding-bottom: ${(p) => p.theme.spacing.xs};
|
|
113
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const ModalImportSection = styled.div`
|
|
117
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
118
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
const ModalImportItem = styled.div`
|
|
122
|
+
display: flex;
|
|
123
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
124
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
const ModalItemIcon = styled.div<{ type: "error" | "info" | "online" }>`
|
|
128
|
+
width: ${(p) => p.theme.spacing.s};
|
|
129
|
+
height: ${(p) => p.theme.spacing.s};
|
|
130
|
+
margin-right: ${(p) => p.theme.spacing.xs};
|
|
131
|
+
svg {
|
|
132
|
+
path {
|
|
133
|
+
fill: ${(p) => p.theme.color[p.type]};
|
|
134
|
+
}
|
|
49
135
|
}
|
|
50
136
|
`;
|
|
51
137
|
|
|
52
|
-
|
|
138
|
+
const ModalItemArrow = styled.div`
|
|
139
|
+
width: ${(p) => p.theme.spacing.s};
|
|
140
|
+
height: ${(p) => p.theme.spacing.s};
|
|
141
|
+
margin-right: ${(p) => p.theme.spacing.xs};
|
|
142
|
+
margin-left: ${(p) => p.theme.spacing.xs};
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const ModalUrl = styled.div`
|
|
146
|
+
width: 50%;
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
Wrapper,
|
|
151
|
+
ContentWrapper,
|
|
152
|
+
TitleWrapper,
|
|
153
|
+
Title,
|
|
154
|
+
Description,
|
|
155
|
+
TableWrapper,
|
|
156
|
+
EmptyWrapper,
|
|
157
|
+
ModalContent,
|
|
158
|
+
ModalCenterContent,
|
|
159
|
+
ModalIcon,
|
|
160
|
+
ModalTitle,
|
|
161
|
+
ModalText,
|
|
162
|
+
ModalButtons,
|
|
163
|
+
ModalContentWrapper,
|
|
164
|
+
ModalImportResult,
|
|
165
|
+
ModalImportSection,
|
|
166
|
+
ModalImportWrapper,
|
|
167
|
+
ModalImportItem,
|
|
168
|
+
ModalItemIcon,
|
|
169
|
+
ModalItemArrow,
|
|
170
|
+
ModalUrl,
|
|
171
|
+
ModalIconWrapper,
|
|
172
|
+
ModalUploadingText,
|
|
173
|
+
};
|
|
@@ -199,7 +199,7 @@ const DataPacks = (props: IProps): JSX.Element => {
|
|
|
199
199
|
)}
|
|
200
200
|
</S.ContentWrapper>
|
|
201
201
|
</S.ListWrapper>
|
|
202
|
-
<Modal isOpen={isOpen} hide={toggleModal} size="
|
|
202
|
+
<Modal isOpen={isOpen} hide={toggleModal} size="XL" title="Available packages">
|
|
203
203
|
{isOpen ? <AddModal toggleModal={toggleModal} /> : null}
|
|
204
204
|
</Modal>
|
|
205
205
|
</MainWrapper>
|
|
@@ -72,7 +72,7 @@ const TableHeader = (props: IProps): JSX.Element => {
|
|
|
72
72
|
</S.NameWrapper>
|
|
73
73
|
{isAllPages && activeColumns.includes("type") && (
|
|
74
74
|
<S.HeaderWrapper>
|
|
75
|
-
<TypeFilter filterItems={filterItems} filters={typeFilters} pointer="types" />
|
|
75
|
+
<TypeFilter filterItems={filterItems} filters={typeFilters} value={filterValues.types} pointer="types" />
|
|
76
76
|
</S.HeaderWrapper>
|
|
77
77
|
)}
|
|
78
78
|
{!isAllPages && activeColumns.includes("site") && (
|
|
@@ -51,7 +51,16 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
|
|
|
51
51
|
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
52
52
|
|
|
53
53
|
const { locale } = lang;
|
|
54
|
-
const {
|
|
54
|
+
const {
|
|
55
|
+
title,
|
|
56
|
+
pageLanguages,
|
|
57
|
+
metaDescription,
|
|
58
|
+
metaTitle,
|
|
59
|
+
isIndexed,
|
|
60
|
+
availableSites,
|
|
61
|
+
structuredData,
|
|
62
|
+
structuredDataContent,
|
|
63
|
+
} = globalPage;
|
|
55
64
|
|
|
56
65
|
const publishedTooltip: Record<string, string> = {
|
|
57
66
|
active: "Live",
|
|
@@ -305,8 +314,8 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
|
|
|
305
314
|
const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
|
|
306
315
|
|
|
307
316
|
const CategoryColumns = categoryColumns.map((col: any) => {
|
|
308
|
-
const type = structuredDataContent[col.from];
|
|
309
|
-
const categories = type && type.map((cat: any) => cat.label || cat.title);
|
|
317
|
+
const type = structuredDataContent && structuredDataContent[col.from];
|
|
318
|
+
const categories = Array.isArray(type) && type.map((cat: any) => cat.label || cat.title);
|
|
310
319
|
return (
|
|
311
320
|
activeColumns.includes(col.key) && (
|
|
312
321
|
<CategoryCell
|