@griddo/ax 1.59.11 → 1.60.3
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/webpackSchemas.config.js +7 -0
- package/package.json +3 -2
- package/src/GlobalStore.tsx +3 -0
- package/src/api/index.tsx +2 -0
- package/src/api/redirects.tsx +71 -0
- package/src/api/sites.tsx +11 -25
- package/src/components/Fields/TextArea/index.tsx +3 -1
- package/src/components/Fields/TextArea/style.tsx +11 -11
- package/src/components/FloatingPanel/style.tsx +7 -2
- package/src/components/TableFilters/SiteFilter/index.tsx +7 -6
- package/src/components/TableFilters/SiteFilter/style.tsx +2 -2
- package/src/containers/Redirects/actions.tsx +129 -0
- package/src/containers/Redirects/constants.tsx +18 -0
- package/src/containers/Redirects/index.tsx +4 -0
- package/src/containers/Redirects/interfaces.tsx +13 -0
- package/src/containers/Redirects/reducer.tsx +24 -0
- package/src/containers/Settings/DataPacks/constants.tsx +11 -11
- package/src/containers/Sites/actions.tsx +1 -25
- package/src/helpers/thumbnails.tsx +3 -3
- package/src/modules/Content/PageItem/index.tsx +5 -8
- package/src/modules/Content/index.tsx +25 -35
- package/src/modules/GlobalSettings/Robots/Item/index.tsx +1 -1
- package/src/modules/GlobalSettings/Robots/index.tsx +5 -2
- package/src/modules/GlobalSettings/index.tsx +6 -0
- package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +44 -0
- package/src/modules/Redirects/BulkHeader/TableHeader/style.tsx +31 -0
- package/src/modules/Redirects/BulkHeader/index.tsx +59 -0
- package/src/modules/Redirects/RedirectItem/index.tsx +139 -0
- package/src/modules/Redirects/RedirectItem/style.tsx +50 -0
- package/src/modules/Redirects/RedirectPanel/index.tsx +117 -0
- package/src/modules/Redirects/RedirectPanel/style.tsx +13 -0
- package/src/modules/Redirects/index.tsx +310 -0
- package/src/modules/Redirects/style.tsx +52 -0
- package/src/modules/Settings/SeoSettings/index.tsx +25 -0
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +3 -3
- package/src/modules/StructuredData/StructuredDataList/index.tsx +1 -0
- package/src/modules/StructuredData/StructuredDataList/utils.tsx +1 -1
- package/src/routes/multisite.tsx +6 -0
- package/src/routes/site.tsx +6 -0
- package/src/types/index.tsx +13 -0
|
@@ -17,7 +17,7 @@ const getThumbnailProps = (option: any, template: boolean, path?: string) => {
|
|
|
17
17
|
if (path) {
|
|
18
18
|
PATH = `/img/${path}/${option}/thumbnails/`;
|
|
19
19
|
thumbnails["1x"] = imageExists(PATH) ? `${PATH}thumbnail@1x.png` : "/img/placeholder/thumbnail@1x.png";
|
|
20
|
-
thumbnails["2x"] = imageExists(PATH) ? `${PATH}thumbnail@2x.png` : "/img/placeholder/thumbnail@2x.png"
|
|
20
|
+
thumbnails["2x"] = imageExists(PATH) ? `${PATH}thumbnail@2x.png` : "/img/placeholder/thumbnail@2x.png";
|
|
21
21
|
} else {
|
|
22
22
|
if (template) {
|
|
23
23
|
thumbnails = getTemplateThumbnails(option);
|
|
@@ -32,10 +32,10 @@ const getThumbnailProps = (option: any, template: boolean, path?: string) => {
|
|
|
32
32
|
|
|
33
33
|
const fallback = thumbnails["2x"];
|
|
34
34
|
const imgDensity1x = thumbnails["1x"];
|
|
35
|
-
const
|
|
35
|
+
const srcSet = `${imgDensity1x} 1x, ${fallback} 2x`;
|
|
36
36
|
|
|
37
37
|
return {
|
|
38
|
-
|
|
38
|
+
srcSet,
|
|
39
39
|
src: fallback,
|
|
40
40
|
alt: option,
|
|
41
41
|
};
|
|
@@ -47,8 +47,8 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
47
47
|
removePageFromSite,
|
|
48
48
|
} = functions;
|
|
49
49
|
const { locale } = lang;
|
|
50
|
-
const { pageLanguages,
|
|
51
|
-
const displayName = getTemplateDisplayName(
|
|
50
|
+
const { pageLanguages, isHome, fullPath, canBeUnpublished, origin, manuallyImported, templateId, structuredDataContent } = page;
|
|
51
|
+
const displayName = getTemplateDisplayName(templateId);
|
|
52
52
|
|
|
53
53
|
const initValue = { title: "", slug: "" };
|
|
54
54
|
const [modalState, setModalState] = useState(initValue);
|
|
@@ -217,10 +217,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
217
217
|
|
|
218
218
|
const currentLanguages = getCurrentPageLanguages();
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
if (page && page.template) {
|
|
222
|
-
isTemplateActivated = activatedTemplates.find((temp: any) => temp.id === template.templateType) ? true : false;
|
|
223
|
-
}
|
|
220
|
+
const isTemplateActivated = activatedTemplates.some((temp: any) => temp.id === templateId);
|
|
224
221
|
|
|
225
222
|
let availableLanguages = siteLanguages;
|
|
226
223
|
|
|
@@ -372,8 +369,8 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
372
369
|
const CategoryColumns =
|
|
373
370
|
isGlobal &&
|
|
374
371
|
categoryColumns.map((col: any) => {
|
|
375
|
-
const type =
|
|
376
|
-
const categories = type && type.map((cat: any) => cat.title);
|
|
372
|
+
const type = structuredDataContent[col.from];
|
|
373
|
+
const categories = type && type.map((cat: any) => cat.label || cat.title);
|
|
377
374
|
return (
|
|
378
375
|
activeColumns.includes(col.key) && (
|
|
379
376
|
<CategoryCell
|
|
@@ -55,7 +55,6 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
55
55
|
setCurrentPageName,
|
|
56
56
|
addTemplate,
|
|
57
57
|
getSitePages,
|
|
58
|
-
getOrderedSitePages,
|
|
59
58
|
updatePageStatus,
|
|
60
59
|
deletePage,
|
|
61
60
|
setLanguage,
|
|
@@ -162,26 +161,27 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
162
161
|
|
|
163
162
|
const getParams = useCallback(() => {
|
|
164
163
|
const siteID = currentSiteInfo ? currentSiteInfo.id : null;
|
|
165
|
-
const params = isStructuredData
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
: {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
const params = isStructuredData ?
|
|
165
|
+
{
|
|
166
|
+
siteID,
|
|
167
|
+
dataID: typeof filter === "object" ? filter.value : filter,
|
|
168
|
+
page,
|
|
169
|
+
itemsPerPage,
|
|
170
|
+
pagination: true,
|
|
171
|
+
deleted: false,
|
|
172
|
+
include_draft: true,
|
|
173
|
+
query: searchQuery,
|
|
174
|
+
format: "list",
|
|
175
|
+
} : {
|
|
176
|
+
siteID,
|
|
177
|
+
deleted: false,
|
|
178
|
+
page,
|
|
179
|
+
itemsPerPage,
|
|
180
|
+
query: searchQuery,
|
|
181
|
+
format: "list",
|
|
182
|
+
};
|
|
183
183
|
|
|
184
|
-
return
|
|
184
|
+
return params;
|
|
185
185
|
}, [filter, currentSiteInfo, isStructuredData, page, searchQuery]);
|
|
186
186
|
|
|
187
187
|
const bulkFilter = (bulkSelection: number[]) => filterByStatus(bulkSelection, currentSitePages);
|
|
@@ -194,24 +194,16 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
194
194
|
|
|
195
195
|
const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
|
|
196
196
|
|
|
197
|
-
const getPages = (params: any,
|
|
197
|
+
const getPages = (params: any, filterQuery?: any) => {
|
|
198
198
|
const isStructuredDataPage = filter !== "unique-pages";
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const fullQuery = filterQuery.concat(
|
|
202
|
-
`&deleted=${deleted}&page=${page}&itemsPerPage=${itemsPerPage}&query=${query}`
|
|
203
|
-
);
|
|
204
|
-
getOrderedSitePages(siteID, fullQuery);
|
|
205
|
-
} else {
|
|
206
|
-
const pageFilter = isStructuredDataPage ? filter : undefined;
|
|
207
|
-
getSitePages(params, pageFilter, filterQuery);
|
|
208
|
-
}
|
|
199
|
+
const pageFilter = isStructuredDataPage ? filter : undefined;
|
|
200
|
+
getSitePages(params, pageFilter, filterQuery);
|
|
209
201
|
};
|
|
210
202
|
|
|
211
203
|
const getSiteContent = useCallback(
|
|
212
204
|
(filterQuery?: any) => {
|
|
213
|
-
const
|
|
214
|
-
isStructuredData ? getStructuredDataContents(params, siteID) : getPages(params,
|
|
205
|
+
const params = getParams();
|
|
206
|
+
isStructuredData ? getStructuredDataContents(params, params.siteID) : getPages(params, filterQuery);
|
|
215
207
|
},
|
|
216
208
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
217
209
|
[getParams, getSitePages, getStructuredDataContents, currentSiteInfo, isStructuredData, lang]
|
|
@@ -657,7 +649,6 @@ interface IDispatchProps {
|
|
|
657
649
|
resetForm(): void;
|
|
658
650
|
deleteBulk(ids: any): Promise<boolean>;
|
|
659
651
|
duplicatePage(pageID: number, data: any): Promise<void>;
|
|
660
|
-
getOrderedSitePages(siteID: number, query: string): Promise<void>;
|
|
661
652
|
deleteDataContent(dataID: number[]): Promise<boolean>;
|
|
662
653
|
restoreDataContent(catID: number | number[]): void;
|
|
663
654
|
setFilter(value: string): void;
|
|
@@ -670,7 +661,6 @@ interface IDispatchProps {
|
|
|
670
661
|
const mapDispatchToProps = {
|
|
671
662
|
setHistoryPush: appActions.setHistoryPush,
|
|
672
663
|
setLanguage: appActions.setLanguage,
|
|
673
|
-
getOrderedSitePages: sitesActions.getOrderedSitePages,
|
|
674
664
|
getSitePages: sitesActions.getSitePages,
|
|
675
665
|
setCurrentPageID: pageEditorActions.setCurrentPageID,
|
|
676
666
|
setCurrentPageStatus: pageEditorActions.setCurrentPageStatus,
|
|
@@ -13,7 +13,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
13
13
|
|
|
14
14
|
const handleClick = () => toggleModal();
|
|
15
15
|
|
|
16
|
-
const buttonText = item.content.trim() !== "" ? "Edit robots.txt" : "Add robots.txt";
|
|
16
|
+
const buttonText = item.content && item.content.trim() !== "" ? "Edit robots.txt" : "Add robots.txt";
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
19
|
<>
|
|
@@ -24,14 +24,17 @@ const Robots = (props: IProps): JSX.Element => {
|
|
|
24
24
|
} = props;
|
|
25
25
|
|
|
26
26
|
const [robotItems, setRobotItems] = useState(robots);
|
|
27
|
-
const { isDirty, setIsDirty } = useIsDirty(robotItems);
|
|
27
|
+
const { isDirty, setIsDirty, resetDirty } = useIsDirty(robotItems);
|
|
28
28
|
|
|
29
29
|
useEffect(() => {
|
|
30
30
|
getDomainsRobots();
|
|
31
|
-
|
|
31
|
+
resetDirty();
|
|
32
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
|
+
}, []);
|
|
32
34
|
|
|
33
35
|
useEffect(() => {
|
|
34
36
|
setRobotItems(robots);
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
38
|
}, [robots]);
|
|
36
39
|
|
|
37
40
|
const handleSave = async () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Switch, Route } from "react-router-dom";
|
|
3
3
|
|
|
4
|
+
import Redirects from "../Redirects";
|
|
4
5
|
import Robots from "./Robots";
|
|
5
6
|
|
|
6
7
|
const navItems = [
|
|
@@ -9,6 +10,11 @@ const navItems = [
|
|
|
9
10
|
path: "/settings/robots",
|
|
10
11
|
component: Robots,
|
|
11
12
|
},
|
|
13
|
+
{
|
|
14
|
+
title: "URL Redirect Manager",
|
|
15
|
+
path: "/settings/redirects",
|
|
16
|
+
component: Redirects,
|
|
17
|
+
},
|
|
12
18
|
];
|
|
13
19
|
|
|
14
20
|
const GlobalSettings = (): JSX.Element => {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { CheckField, SiteFilter, TableCounter } from "@ax/components";
|
|
4
|
+
import * as S from "./style";
|
|
5
|
+
|
|
6
|
+
const TableHeader = (props: IProps) => {
|
|
7
|
+
const { totalItems, selectAllItems, isScrolling, filterItems, filterValues, isSiteItem } = props;
|
|
8
|
+
return (
|
|
9
|
+
<S.TableHeader isScrolling={isScrolling}>
|
|
10
|
+
<S.CheckHeader>
|
|
11
|
+
<CheckField
|
|
12
|
+
key="selectAll"
|
|
13
|
+
name="selectAll"
|
|
14
|
+
value="selectAll"
|
|
15
|
+
onChange={selectAllItems}
|
|
16
|
+
checked={false}
|
|
17
|
+
disabled={false}
|
|
18
|
+
error={false}
|
|
19
|
+
/>
|
|
20
|
+
</S.CheckHeader>
|
|
21
|
+
{!isSiteItem && (
|
|
22
|
+
<S.SiteHeader>
|
|
23
|
+
<SiteFilter filterItems={filterItems} value={filterValues} pointer="sites" center={false} />
|
|
24
|
+
</S.SiteHeader>
|
|
25
|
+
)}
|
|
26
|
+
<S.UrlHeader>Old URL</S.UrlHeader>
|
|
27
|
+
<S.UrlHeader>New URL</S.UrlHeader>
|
|
28
|
+
<S.ActionsHeader>
|
|
29
|
+
<TableCounter totalItems={totalItems} />
|
|
30
|
+
</S.ActionsHeader>
|
|
31
|
+
</S.TableHeader>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
interface IProps {
|
|
36
|
+
totalItems: number;
|
|
37
|
+
isScrolling: boolean;
|
|
38
|
+
selectAllItems: () => void;
|
|
39
|
+
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
40
|
+
filterValues: any;
|
|
41
|
+
isSiteItem: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default TableHeader;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { Header } from "@ax/components/TableList/style";
|
|
3
|
+
|
|
4
|
+
const TableHeader = styled.div<{ isScrolling?: boolean }>`
|
|
5
|
+
width: 100%;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: row;
|
|
8
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
9
|
+
border-bottom: ${(p) => (p.isScrolling ? `1px solid ${p.theme.color.uiLine};` : "")};
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const CheckHeader = styled(Header)`
|
|
13
|
+
padding-left: ${(p) => p.theme.spacing.m};
|
|
14
|
+
width: 32px;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const SiteHeader = styled(Header)`
|
|
18
|
+
width: 270px;
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const UrlHeader = styled(Header)`
|
|
22
|
+
width: 50%;
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const ActionsHeader = styled(Header)`
|
|
26
|
+
width: 125px;
|
|
27
|
+
padding-right: 0;
|
|
28
|
+
justify-content: flex-end;
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
export { TableHeader, CheckHeader, SiteHeader, UrlHeader, ActionsHeader };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BulkSelectionOptions } from "@ax/components";
|
|
3
|
+
import TableHeader from "./TableHeader";
|
|
4
|
+
|
|
5
|
+
const BulkHeader = (props: IProps): JSX.Element => {
|
|
6
|
+
const {
|
|
7
|
+
showBulk,
|
|
8
|
+
checkState,
|
|
9
|
+
bulkDelete,
|
|
10
|
+
selectItems,
|
|
11
|
+
selectAllItems,
|
|
12
|
+
totalItems,
|
|
13
|
+
isScrolling,
|
|
14
|
+
filterItems,
|
|
15
|
+
filterValues,
|
|
16
|
+
isSiteItem,
|
|
17
|
+
} = props;
|
|
18
|
+
|
|
19
|
+
const bulkActions = [
|
|
20
|
+
{
|
|
21
|
+
icon: "delete",
|
|
22
|
+
text: "delete",
|
|
23
|
+
action: bulkDelete,
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
return showBulk ? (
|
|
28
|
+
<BulkSelectionOptions
|
|
29
|
+
checkState={checkState}
|
|
30
|
+
actions={bulkActions}
|
|
31
|
+
selectItems={selectItems}
|
|
32
|
+
totalItems={totalItems}
|
|
33
|
+
/>
|
|
34
|
+
) : (
|
|
35
|
+
<TableHeader
|
|
36
|
+
totalItems={totalItems}
|
|
37
|
+
selectAllItems={selectAllItems}
|
|
38
|
+
isScrolling={isScrolling}
|
|
39
|
+
filterItems={filterItems}
|
|
40
|
+
filterValues={filterValues}
|
|
41
|
+
isSiteItem={isSiteItem}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
interface IProps {
|
|
47
|
+
showBulk: boolean;
|
|
48
|
+
checkState: any;
|
|
49
|
+
bulkDelete: () => void;
|
|
50
|
+
selectItems: () => void;
|
|
51
|
+
selectAllItems: () => void;
|
|
52
|
+
totalItems: number;
|
|
53
|
+
isScrolling: boolean;
|
|
54
|
+
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
55
|
+
filterValues: any;
|
|
56
|
+
isSiteItem: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default BulkHeader;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
|
|
4
|
+
import { useModal } from "@ax/hooks";
|
|
5
|
+
import { ICheck, IRedirect } from "@ax/types";
|
|
6
|
+
import { CheckField, ElementsTooltip, Modal } from "@ax/components";
|
|
7
|
+
import { redirectsActions } from "@ax/containers/Redirects";
|
|
8
|
+
import RedirectPanel from "../RedirectPanel";
|
|
9
|
+
|
|
10
|
+
import * as S from "./style";
|
|
11
|
+
|
|
12
|
+
const RedirectItem = (props: IRedirectItemProps): JSX.Element => {
|
|
13
|
+
const {
|
|
14
|
+
redirect,
|
|
15
|
+
isSelected,
|
|
16
|
+
onChange,
|
|
17
|
+
deleteRedirect,
|
|
18
|
+
toggleToast,
|
|
19
|
+
setFormValues,
|
|
20
|
+
formValues,
|
|
21
|
+
addRedirect,
|
|
22
|
+
currentFilter,
|
|
23
|
+
isSiteItem,
|
|
24
|
+
} = props;
|
|
25
|
+
|
|
26
|
+
const { isOpen, toggleModal } = useModal();
|
|
27
|
+
const [isOpenedSecond, setIsOpenedSecond] = useState(false);
|
|
28
|
+
const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
|
|
29
|
+
|
|
30
|
+
const toggleSecondaryPanel = () => setIsOpenedSecond(!isOpenedSecond);
|
|
31
|
+
|
|
32
|
+
const handleClick = () => {
|
|
33
|
+
const mappedRedirect = { ...redirect, to: { ...redirect.to, linkTo: redirect.to.pageId, href: redirect.to.url } };
|
|
34
|
+
setFormValues(mappedRedirect);
|
|
35
|
+
setIsOpenedSecond(false);
|
|
36
|
+
toggleModal();
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleOnChange = (value: ICheck) => onChange(value);
|
|
40
|
+
|
|
41
|
+
const removeItem = async () => {
|
|
42
|
+
if (redirect.id) {
|
|
43
|
+
const deleted = await deleteRedirect(redirect.id, currentFilter);
|
|
44
|
+
if (deleted) {
|
|
45
|
+
toggleToast();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const menuOptions = [
|
|
51
|
+
{
|
|
52
|
+
label: "delete",
|
|
53
|
+
icon: "delete",
|
|
54
|
+
action: toggleModalDelete,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const mainDeleteModalAction = {
|
|
59
|
+
title: "Delete redirect",
|
|
60
|
+
onClick: removeItem,
|
|
61
|
+
};
|
|
62
|
+
const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<>
|
|
66
|
+
<S.ItemRow role="rowgroup" selected={isSelected}>
|
|
67
|
+
<S.CheckCell role="cell">
|
|
68
|
+
<CheckField name="check" value={redirect.id ?? ""} checked={isSelected} onChange={handleOnChange} />
|
|
69
|
+
</S.CheckCell>
|
|
70
|
+
{!isSiteItem && (
|
|
71
|
+
<S.SiteCell role="cell" onClick={handleClick}>
|
|
72
|
+
{redirect.site && redirect.site.siteName && (
|
|
73
|
+
<ElementsTooltip elements={[redirect.site.siteName]} maxChar={20} />
|
|
74
|
+
)}
|
|
75
|
+
</S.SiteCell>
|
|
76
|
+
)}
|
|
77
|
+
<S.UrlCell role="cell" onClick={handleClick}>
|
|
78
|
+
{redirect.from}
|
|
79
|
+
</S.UrlCell>
|
|
80
|
+
<S.UrlCell role="cell" onClick={handleClick}>
|
|
81
|
+
{redirect.to?.url}
|
|
82
|
+
</S.UrlCell>
|
|
83
|
+
<S.ActionsCell role="cell">
|
|
84
|
+
<S.StyledActionMenu icon="more" options={menuOptions} tooltip="Actions" />
|
|
85
|
+
</S.ActionsCell>
|
|
86
|
+
</S.ItemRow>
|
|
87
|
+
{isOpen && (
|
|
88
|
+
<RedirectPanel
|
|
89
|
+
isOpen={isOpen}
|
|
90
|
+
isOpenedSecond={isOpenedSecond}
|
|
91
|
+
toggleModal={toggleModal}
|
|
92
|
+
toggleSecondaryPanel={toggleSecondaryPanel}
|
|
93
|
+
redirect={redirect}
|
|
94
|
+
setFormValues={setFormValues}
|
|
95
|
+
formValues={formValues}
|
|
96
|
+
addRedirect={addRedirect}
|
|
97
|
+
currentFilter={currentFilter}
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
100
|
+
<Modal
|
|
101
|
+
isOpen={isOpenDelete}
|
|
102
|
+
hide={toggleModalDelete}
|
|
103
|
+
title="Delete Redirect"
|
|
104
|
+
secondaryAction={secondaryDeleteModalAction}
|
|
105
|
+
mainAction={mainDeleteModalAction}
|
|
106
|
+
size="S"
|
|
107
|
+
>
|
|
108
|
+
<S.ModalContent>
|
|
109
|
+
Are you sure you want to delete <strong>{redirect.from} redirect</strong>? This action{" "}
|
|
110
|
+
<strong>cannot be undone</strong>.
|
|
111
|
+
</S.ModalContent>
|
|
112
|
+
</Modal>
|
|
113
|
+
</>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
interface IProps {
|
|
118
|
+
redirect: IRedirect;
|
|
119
|
+
isSelected: boolean;
|
|
120
|
+
onChange: (e: any) => void;
|
|
121
|
+
toggleToast: () => void;
|
|
122
|
+
setFormValues: (redirect: IRedirect) => void;
|
|
123
|
+
formValues: IRedirect;
|
|
124
|
+
addRedirect: (force?: boolean) => void;
|
|
125
|
+
currentFilter: string;
|
|
126
|
+
isSiteItem: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface IDispatchProps {
|
|
130
|
+
deleteRedirect(redirectID: number, filter?: string): Promise<boolean>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
type IRedirectItemProps = IProps & IDispatchProps;
|
|
134
|
+
|
|
135
|
+
const mapDispatchToProps = {
|
|
136
|
+
deleteRedirect: redirectsActions.deleteRedirect,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export default connect(null, mapDispatchToProps)(RedirectItem);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
import { Cell, Row } from "@ax/components/TableList/TableItem/style";
|
|
4
|
+
import { ActionMenu } from "@ax/components";
|
|
5
|
+
|
|
6
|
+
const CheckCell = styled(Cell)`
|
|
7
|
+
padding-left: ${(p) => p.theme.spacing.m};
|
|
8
|
+
width: 32px;
|
|
9
|
+
label {
|
|
10
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const SiteCell = styled(Cell)`
|
|
15
|
+
width: 270px;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const UrlCell = styled(Cell)`
|
|
19
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
20
|
+
width: 50%;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const ActionsCell = styled(Cell)`
|
|
24
|
+
width: 125px;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const StyledActionMenu = styled(ActionMenu)`
|
|
28
|
+
opacity: 0;
|
|
29
|
+
width: 32px;
|
|
30
|
+
display: flex;
|
|
31
|
+
margin-left: auto;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const ItemRow = styled(Row)`
|
|
35
|
+
&:hover {
|
|
36
|
+
${StyledActionMenu} {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const ModalContent = styled.div`
|
|
43
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
44
|
+
|
|
45
|
+
p {
|
|
46
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export { CheckCell, SiteCell, ActionsCell, UrlCell, StyledActionMenu, ItemRow, ModalContent };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
|
|
4
|
+
import { IRedirect } from "@ax/types";
|
|
5
|
+
import { Button, FieldsBehavior, FloatingPanel } from "@ax/components";
|
|
6
|
+
import { redirectsActions } from "@ax/containers/Redirects";
|
|
7
|
+
|
|
8
|
+
import * as S from "./style";
|
|
9
|
+
|
|
10
|
+
const RedirectPanel = (props: IProps): JSX.Element => {
|
|
11
|
+
const {
|
|
12
|
+
isOpen,
|
|
13
|
+
toggleModal,
|
|
14
|
+
redirect,
|
|
15
|
+
isOpenedSecond,
|
|
16
|
+
toggleSecondaryPanel,
|
|
17
|
+
addRedirect,
|
|
18
|
+
updateRedirect,
|
|
19
|
+
formValues,
|
|
20
|
+
setFormValues,
|
|
21
|
+
currentFilter,
|
|
22
|
+
} = props;
|
|
23
|
+
|
|
24
|
+
const handleOldUrl = (newValue: string) => setFormValues({ ...formValues, from: newValue });
|
|
25
|
+
|
|
26
|
+
const handleNewUrl = (newValue: any) => {
|
|
27
|
+
const url = !newValue ? "" : newValue.linkTo ? newValue.linkToURL : newValue.href;
|
|
28
|
+
const pageId = newValue ? newValue.linkTo : undefined;
|
|
29
|
+
const toValue = { ...newValue, pageId, url };
|
|
30
|
+
setFormValues({ ...formValues, to: toValue });
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const addItemAction = () => addRedirect();
|
|
34
|
+
|
|
35
|
+
const editItemAction = async () => {
|
|
36
|
+
const toPage = formValues.to.pageId ? formValues.to.pageId : formValues.to.url;
|
|
37
|
+
formValues.id && (await updateRedirect({ from: formValues.from, to: toPage }, formValues.id, currentFilter));
|
|
38
|
+
toggleModal();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const addButton = {
|
|
42
|
+
label: "Add Redirect",
|
|
43
|
+
action: addItemAction,
|
|
44
|
+
disabled: false,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const editButton = {
|
|
48
|
+
label: "Update Redirect",
|
|
49
|
+
action: editItemAction,
|
|
50
|
+
disabled: false,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const title = redirect ? "Update Redirect" : "Add Redirect";
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<FloatingPanel
|
|
57
|
+
title={title}
|
|
58
|
+
toggleModal={toggleModal}
|
|
59
|
+
isOpen={isOpen}
|
|
60
|
+
isOpenedSecond={isOpenedSecond}
|
|
61
|
+
handlePanel={toggleSecondaryPanel}
|
|
62
|
+
>
|
|
63
|
+
<FieldsBehavior
|
|
64
|
+
title="Old URL"
|
|
65
|
+
name="oldUrl"
|
|
66
|
+
fieldType="TextField"
|
|
67
|
+
value={formValues.from}
|
|
68
|
+
onChange={handleOldUrl}
|
|
69
|
+
autoComplete="redirect-old"
|
|
70
|
+
/>
|
|
71
|
+
<FieldsBehavior
|
|
72
|
+
title="New URL"
|
|
73
|
+
name="newUrl"
|
|
74
|
+
fieldType="UrlField"
|
|
75
|
+
value={formValues.to}
|
|
76
|
+
onChange={handleNewUrl}
|
|
77
|
+
handlePanel={toggleSecondaryPanel}
|
|
78
|
+
inFloatingPanel={true}
|
|
79
|
+
autoComplete="redirect-new"
|
|
80
|
+
/>
|
|
81
|
+
<S.Footer>
|
|
82
|
+
{redirect ? (
|
|
83
|
+
<Button className="button" type="button" onClick={editButton.action} disabled={editButton.disabled}>
|
|
84
|
+
{editButton.label}
|
|
85
|
+
</Button>
|
|
86
|
+
) : (
|
|
87
|
+
<Button className="button" type="button" onClick={addButton.action} disabled={addButton.disabled}>
|
|
88
|
+
{addButton.label}
|
|
89
|
+
</Button>
|
|
90
|
+
)}
|
|
91
|
+
</S.Footer>
|
|
92
|
+
</FloatingPanel>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const mapDispatchToProps = {
|
|
97
|
+
updateRedirect: redirectsActions.updateRedirect,
|
|
98
|
+
};
|
|
99
|
+
interface ICategoryPanelProps {
|
|
100
|
+
isOpen: boolean;
|
|
101
|
+
isOpenedSecond: boolean;
|
|
102
|
+
toggleSecondaryPanel(): any;
|
|
103
|
+
toggleModal(): any;
|
|
104
|
+
redirect?: IRedirect;
|
|
105
|
+
formValues: IRedirect;
|
|
106
|
+
setFormValues(redirect: IRedirect): void;
|
|
107
|
+
currentFilter: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface IDispatchProps {
|
|
111
|
+
addRedirect(force?: boolean): void;
|
|
112
|
+
updateRedirect(redirect: { from: string; to: string | number }, redirectID: number, filter?: string): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type IProps = ICategoryPanelProps & IDispatchProps;
|
|
116
|
+
|
|
117
|
+
export default connect(null, mapDispatchToProps)(RedirectPanel);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Footer = styled.div`
|
|
4
|
+
position: absolute;
|
|
5
|
+
bottom: ${(p) => p.theme.spacing.m};
|
|
6
|
+
right: ${(p) => p.theme.spacing.m};
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
const ModalContent = styled.div`
|
|
10
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
export { Footer, ModalContent };
|