@griddo/ax 1.59.8 → 1.60.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.
Files changed (48) hide show
  1. package/config/webpackSchemas.config.js +7 -0
  2. package/package.json +3 -2
  3. package/src/GlobalStore.tsx +3 -0
  4. package/src/api/index.tsx +2 -0
  5. package/src/api/redirects.tsx +71 -0
  6. package/src/api/sites.tsx +11 -25
  7. package/src/components/Fields/ReferenceField/Context/index.tsx +1 -1
  8. package/src/components/Fields/ReferenceField/ManualPanel/Item/index.tsx +4 -3
  9. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +4 -2
  10. package/src/components/Fields/ReferenceField/index.tsx +2 -2
  11. package/src/components/Fields/TextArea/index.tsx +3 -1
  12. package/src/components/Fields/TextArea/style.tsx +11 -11
  13. package/src/components/FloatingPanel/style.tsx +7 -2
  14. package/src/components/TableFilters/SiteFilter/index.tsx +7 -6
  15. package/src/components/TableFilters/SiteFilter/style.tsx +2 -2
  16. package/src/containers/Redirects/actions.tsx +129 -0
  17. package/src/containers/Redirects/constants.tsx +18 -0
  18. package/src/containers/Redirects/index.tsx +4 -0
  19. package/src/containers/Redirects/interfaces.tsx +13 -0
  20. package/src/containers/Redirects/reducer.tsx +24 -0
  21. package/src/containers/Settings/DataPacks/constants.tsx +11 -11
  22. package/src/containers/Sites/actions.tsx +1 -25
  23. package/src/helpers/environment.tsx +5 -0
  24. package/src/helpers/index.tsx +3 -0
  25. package/src/helpers/thumbnails.tsx +3 -3
  26. package/src/modules/Content/PageItem/index.tsx +5 -8
  27. package/src/modules/Content/index.tsx +25 -35
  28. package/src/modules/GlobalSettings/Robots/Item/index.tsx +1 -1
  29. package/src/modules/GlobalSettings/Robots/index.tsx +5 -2
  30. package/src/modules/GlobalSettings/index.tsx +6 -0
  31. package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +44 -0
  32. package/src/modules/Redirects/BulkHeader/TableHeader/style.tsx +31 -0
  33. package/src/modules/Redirects/BulkHeader/index.tsx +59 -0
  34. package/src/modules/Redirects/RedirectItem/index.tsx +139 -0
  35. package/src/modules/Redirects/RedirectItem/style.tsx +50 -0
  36. package/src/modules/Redirects/RedirectPanel/index.tsx +117 -0
  37. package/src/modules/Redirects/RedirectPanel/style.tsx +13 -0
  38. package/src/modules/Redirects/index.tsx +310 -0
  39. package/src/modules/Redirects/style.tsx +52 -0
  40. package/src/modules/Settings/SeoSettings/index.tsx +25 -0
  41. package/src/modules/Sites/SitesList/SiteItem/index.tsx +6 -2
  42. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
  43. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +3 -3
  44. package/src/modules/StructuredData/StructuredDataList/index.tsx +1 -0
  45. package/src/modules/StructuredData/StructuredDataList/utils.tsx +1 -1
  46. package/src/routes/multisite.tsx +6 -0
  47. package/src/routes/site.tsx +6 -0
  48. package/src/types/index.tsx +13 -0
@@ -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 };
@@ -0,0 +1,310 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import { INavItem, IRedirect, IRootState } from "@ax/types";
5
+ import { appActions } from "@ax/containers/App";
6
+ import { redirectsActions } from "@ax/containers/Redirects";
7
+ import { MainWrapper, ErrorToast, Nav, TableList, EmptyState, Modal, Toast } from "@ax/components";
8
+ import { useBulkSelection, useModal, useToast } from "@ax/hooks";
9
+ import BulkHeader from "./BulkHeader";
10
+ import RedirectItem from "./RedirectItem";
11
+ import RedirectPanel from "./RedirectPanel";
12
+
13
+ import * as S from "./style";
14
+
15
+ const Redirects = (props: IProps): JSX.Element => {
16
+ const {
17
+ navItems,
18
+ currentNavItem,
19
+ setHistoryPush,
20
+ getRedirects,
21
+ redirects,
22
+ totalItems,
23
+ deleteRedirect,
24
+ addRedirect,
25
+ currentSiteID,
26
+ } = props;
27
+
28
+ const itemsPerPage = 50;
29
+ const [page, setPage] = useState(1);
30
+ const [isScrolling, setIsScrolling] = useState(false);
31
+ const [isOpenedSecond, setIsOpenedSecond] = useState(false);
32
+ const { isOpen, toggleModal } = useModal();
33
+ const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
34
+ const tableRef = useRef<HTMLDivElement>(null);
35
+ const [currentFilterQuery, setCurrentFilterQuery] = useState("");
36
+ const [filterValues, setFilterValues] = useState({ sites: "all" });
37
+ const { isVisible, toggleToast, setIsVisible } = useToast();
38
+ const { isOpen: isOpenOverwrite, toggleModal: toggleOverwriteModal } = useModal();
39
+
40
+ const initState = {
41
+ from: "",
42
+ to: {
43
+ pageId: undefined,
44
+ url: "",
45
+ },
46
+ };
47
+
48
+ const [formValues, setFormValues] = useState<IRedirect>(initState);
49
+
50
+ const redIds = redirects && redirects.map((red: any) => red.id);
51
+ const isEmpty = redirects && redirects.length === 0;
52
+
53
+ const getParams = useCallback(() => {
54
+ const params = {
55
+ page,
56
+ itemsPerPage,
57
+ pagination: true,
58
+ };
59
+
60
+ return params;
61
+ }, [page]);
62
+
63
+ useEffect(() => {
64
+ const params = getParams();
65
+ const siteFilterQuery = currentSiteID ? `&sites=${currentSiteID}` : currentFilterQuery;
66
+ getRedirects(params, siteFilterQuery);
67
+ if (tableRef.current) {
68
+ tableRef.current.scrollTo(0, 0);
69
+ }
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ }, [page, currentFilterQuery]);
72
+
73
+ const {
74
+ resetBulkSelection,
75
+ selectedItems,
76
+ isSelected,
77
+ areItemsSelected,
78
+ checkState,
79
+ addToBulkSelection,
80
+ selectAllItems,
81
+ } = useBulkSelection(redIds);
82
+
83
+ const bulkDelete = async () => {
84
+ const deleted = await deleteRedirect(selectedItems.all);
85
+ if (deleted) {
86
+ toggleToast();
87
+ }
88
+ toggleModalDelete();
89
+ };
90
+
91
+ const unselectAllItems = () => resetBulkSelection();
92
+
93
+ const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
94
+
95
+ const handleSelectAll = () => selectAllItems();
96
+
97
+ const filterItems = async (filterPointer: string, filtersSelected: string) => {
98
+ let filterQuery = "";
99
+ if (filtersSelected !== "all") {
100
+ filterQuery = `&${filterPointer}=${filtersSelected}`;
101
+ }
102
+ setFilterValues({ sites: filtersSelected });
103
+ setCurrentFilterQuery(filterQuery);
104
+ };
105
+
106
+ const TableHeader = (
107
+ <BulkHeader
108
+ showBulk={areItemsSelected(redIds)}
109
+ bulkDelete={toggleModalDelete}
110
+ selectAllItems={handleSelectAll}
111
+ totalItems={totalItems}
112
+ selectItems={selectItems}
113
+ checkState={checkState}
114
+ isScrolling={isScrolling}
115
+ filterItems={filterItems}
116
+ filterValues={filterValues}
117
+ isSiteItem={!!currentSiteID}
118
+ />
119
+ );
120
+
121
+ const onScroll = (e: any) => setIsScrolling(e.target.scrollTop > 0);
122
+
123
+ const toggleSecondaryPanel = () => setIsOpenedSecond(!isOpenedSecond);
124
+
125
+ const rightButtonProps = {
126
+ label: "New Redirect",
127
+ action: () => handleModal(),
128
+ };
129
+
130
+ const handleMenuClick = (path: string) => {
131
+ setHistoryPush(path);
132
+ };
133
+
134
+ const handleModal = () => {
135
+ setFormValues(initState);
136
+ setIsOpenedSecond(false);
137
+ toggleModal();
138
+ };
139
+
140
+ const handleAddRedirect = async (force?: boolean) => {
141
+ const toPage = formValues.to.pageId ? formValues.to.pageId : formValues.to.url;
142
+ await addRedirect({ from: formValues.from, to: toPage }, toggleOverwriteModal, force, currentFilterQuery);
143
+ isOpen && toggleModal();
144
+ };
145
+
146
+ const addItemActionForce = () => {
147
+ handleAddRedirect(true);
148
+ toggleOverwriteModal();
149
+ };
150
+
151
+ const emptyStateProps = {
152
+ message: "To have a redirects on your site, create as many redirects as you want.",
153
+ button: "Create New redirect",
154
+ action: handleModal,
155
+ };
156
+
157
+ const pagination = {
158
+ setPage,
159
+ itemsPerPage,
160
+ totalItems,
161
+ currPage: page,
162
+ };
163
+
164
+ const toastProps = {
165
+ setIsVisible,
166
+ message: "Redirect deleted",
167
+ };
168
+
169
+ const mainDeleteModalAction = {
170
+ title: "Delete redirects",
171
+ onClick: bulkDelete,
172
+ };
173
+
174
+ const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };
175
+
176
+ const mainOverwriteModalAction = {
177
+ title: "Overwrite redirect",
178
+ onClick: addItemActionForce,
179
+ };
180
+
181
+ const secondaryOverwriteModalAction = { title: "Cancel", onClick: toggleOverwriteModal };
182
+
183
+ return (
184
+ <>
185
+ <MainWrapper backLink={false} title="SEO Settings" rightButton={rightButtonProps}>
186
+ <S.Wrapper>
187
+ <Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
188
+ <S.ContentWrapper>
189
+ <ErrorToast />
190
+ <S.TitleWrapper>
191
+ <S.Title>URL Redirects Manager</S.Title>
192
+ <S.Description>
193
+ You can create 301 redirects to direct users and search engines from an old URL to a new one.
194
+ </S.Description>
195
+ </S.TitleWrapper>
196
+ <S.TableWrapper>
197
+ <TableList
198
+ tableHeader={TableHeader}
199
+ pagination={pagination}
200
+ onScroll={onScroll}
201
+ hasFixedHeader={true}
202
+ tableRef={tableRef}
203
+ >
204
+ {isEmpty ? (
205
+ <S.EmptyWrapper>
206
+ <EmptyState {...emptyStateProps} />
207
+ </S.EmptyWrapper>
208
+ ) : (
209
+ redirects.map((redirect: any) => {
210
+ const isItemSelected = isSelected(redirect.id);
211
+ return (
212
+ <RedirectItem
213
+ key={redirect.id}
214
+ redirect={redirect}
215
+ isSelected={isItemSelected}
216
+ onChange={addToBulkSelection}
217
+ toggleToast={toggleToast}
218
+ setFormValues={setFormValues}
219
+ formValues={formValues}
220
+ addRedirect={handleAddRedirect}
221
+ currentFilter={currentFilterQuery}
222
+ isSiteItem={!!currentSiteID}
223
+ />
224
+ );
225
+ })
226
+ )}
227
+ </TableList>
228
+ </S.TableWrapper>
229
+ {isVisible && <Toast {...toastProps} />}
230
+ </S.ContentWrapper>
231
+ </S.Wrapper>
232
+ {isOpen && (
233
+ <RedirectPanel
234
+ isOpen={isOpen}
235
+ isOpenedSecond={isOpenedSecond}
236
+ toggleModal={toggleModal}
237
+ toggleSecondaryPanel={toggleSecondaryPanel}
238
+ formValues={formValues}
239
+ setFormValues={setFormValues}
240
+ addRedirect={handleAddRedirect}
241
+ currentFilter={currentFilterQuery}
242
+ />
243
+ )}
244
+ <Modal
245
+ isOpen={isOpenDelete}
246
+ hide={toggleModalDelete}
247
+ title="Delete Redirects"
248
+ secondaryAction={secondaryDeleteModalAction}
249
+ mainAction={mainDeleteModalAction}
250
+ size="S"
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
258
+ isOpen={isOpenOverwrite}
259
+ hide={toggleOverwriteModal}
260
+ title="Overwrite Redirect"
261
+ secondaryAction={secondaryOverwriteModalAction}
262
+ mainAction={mainOverwriteModalAction}
263
+ size="S"
264
+ >
265
+ <S.ModalContent>
266
+ This Redirect is already created. It will overwrite.
267
+ <br />
268
+ This action <strong>cannot be undone</strong>.
269
+ </S.ModalContent>
270
+ </Modal>
271
+ </MainWrapper>
272
+ </>
273
+ );
274
+ };
275
+
276
+ const mapStateToProps = (state: IRootState) => ({
277
+ redirects: state.redirects.redirects,
278
+ totalItems: state.redirects.totalItems,
279
+ currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
280
+ });
281
+
282
+ const mapDispatchToProps = {
283
+ setHistoryPush: appActions.setHistoryPush,
284
+ getRedirects: redirectsActions.getRedirects,
285
+ deleteRedirect: redirectsActions.deleteRedirect,
286
+ addRedirect: redirectsActions.addRedirect,
287
+ };
288
+ interface IRedirectsProps {
289
+ navItems: INavItem[];
290
+ currentNavItem: INavItem;
291
+ redirects: IRedirect[];
292
+ totalItems: number;
293
+ currentSiteID: number | null;
294
+ }
295
+
296
+ interface IDispatchProps {
297
+ setHistoryPush(path: string, isEditor?: boolean): void;
298
+ getRedirects(params: any, filters: string): void;
299
+ deleteRedirect(redirectID: number[]): Promise<boolean>;
300
+ addRedirect(
301
+ redirect: { from: string; to: string | number },
302
+ errorAction: () => void,
303
+ force?: boolean,
304
+ filter?: string
305
+ ): Promise<void>;
306
+ }
307
+
308
+ type IProps = IRedirectsProps & IDispatchProps;
309
+
310
+ export default connect(mapStateToProps, mapDispatchToProps)(Redirects);
@@ -0,0 +1,52 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ display: flex;
5
+ height: 100%;
6
+ `;
7
+
8
+ const ContentWrapper = styled.div`
9
+ display: flex;
10
+ overflow: auto;
11
+ flex-direction: column;
12
+ width: 100%;
13
+ `;
14
+
15
+ const TitleWrapper = styled.div`
16
+ padding: ${(p) => p.theme.spacing.m};
17
+ border-bottom: ${(p) => `1px solid ${p.theme.color.uiLine}`};
18
+ `;
19
+
20
+ const Title = styled.div`
21
+ ${(p) => p.theme.textStyle.headingXS};
22
+ margin-bottom: ${(p) => p.theme.spacing.xs};
23
+ `;
24
+
25
+ const Description = styled.div`
26
+ ${(p) => p.theme.textStyle.uiM};
27
+ `;
28
+
29
+ const TableWrapper = styled.div`
30
+ display: flex;
31
+ flex-direction: column;
32
+ width: 100%;
33
+ position: relative;
34
+ height: calc(100vh - ${(p) => p.theme.spacing.xl});
35
+ overflow: auto;
36
+ `;
37
+
38
+ const EmptyWrapper = styled.div`
39
+ height: ${(p) => `calc(100vh - (${p.theme.spacing.xl} * 3))`};
40
+ display: flex;
41
+ align-items: center;
42
+ `;
43
+
44
+ const ModalContent = styled.div`
45
+ padding: ${(p) => p.theme.spacing.m};
46
+
47
+ p {
48
+ margin-bottom: ${(p) => p.theme.spacing.m};
49
+ }
50
+ `;
51
+
52
+ export { Wrapper, ContentWrapper, TitleWrapper, Title, Description, TableWrapper, EmptyWrapper, ModalContent };
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import { Switch, Route } from "react-router-dom";
3
+ import Redirects from "../../Redirects";
4
+
5
+ const navItems = [
6
+ {
7
+ title: "SEO",
8
+ path: "/sites/settings/seo-settings",
9
+ component: Redirects,
10
+ },
11
+ ];
12
+
13
+ const SeoSettings = (): JSX.Element => {
14
+ return (
15
+ <Switch>
16
+ {navItems.map((item, index) => (
17
+ <Route exact path={item.path} key={index}>
18
+ {React.createElement(item.component, { navItems, currentNavItem: item }, null)}
19
+ </Route>
20
+ ))}
21
+ </Switch>
22
+ );
23
+ };
24
+
25
+ export default SeoSettings;
@@ -2,7 +2,7 @@ import React, { useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { useModal } from "@ax/hooks";
5
- import { trimText } from "@ax/helpers";
5
+ import { trimText, isDevelopment } from "@ax/helpers";
6
6
  import { ISite } from "@ax/types";
7
7
  import { appActions } from "@ax/containers/App";
8
8
  import { sitesActions } from "@ax/containers/Sites";
@@ -122,10 +122,14 @@ const SiteItem = (props: ISiteItemProps): JSX.Element => {
122
122
  site.name
123
123
  );
124
124
 
125
+ const siteTooltip = isDevelopment() && `Site id: ${site.id}`;
126
+
125
127
  return (
126
128
  <>
127
129
  <S.SiteWrapper key={site.id} onClick={setSite}>
128
- <S.Thumbnail backgroundUrl={thumbnail} />
130
+ <Tooltip content={siteTooltip}>
131
+ <S.Thumbnail backgroundUrl={thumbnail} />
132
+ </Tooltip>
129
133
  <S.Wrapper>
130
134
  <S.Title>{siteName}</S.Title>
131
135
  <S.IconsWrapper>
@@ -77,7 +77,7 @@ const TableHeader = (props: IProps): JSX.Element => {
77
77
  )}
78
78
  {!isAllPages && activeColumns.includes("site") && (
79
79
  <S.HeaderWrapper>
80
- <SiteFilter filterItems={filterItems} value={filterValues.filterSites} />
80
+ <SiteFilter filterItems={filterItems} value={filterValues} pointer="filterSites" />
81
81
  </S.HeaderWrapper>
82
82
  )}
83
83
  {activeColumns.includes("live") && (
@@ -51,7 +51,7 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
51
51
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
52
52
 
53
53
  const { locale } = lang;
54
- const { title, pageLanguages, metaDescription, metaTitle, isIndexed, availableSites, structuredData } = globalPage;
54
+ const { title, pageLanguages, metaDescription, metaTitle, isIndexed, availableSites, structuredData, structuredDataContent } = globalPage;
55
55
 
56
56
  const publishedTooltip: Record<string, string> = {
57
57
  active: "Live",
@@ -305,8 +305,8 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
305
305
  const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
306
306
 
307
307
  const CategoryColumns = categoryColumns.map((col: any) => {
308
- const type = globalPage.template[col.from];
309
- const categories = type && type.map((cat: any) => cat.title);
308
+ const type = structuredDataContent[col.from];
309
+ const categories = type && type.map((cat: any) => cat.label || cat.title);
310
310
  return (
311
311
  activeColumns.includes(col.key) && (
312
312
  <CategoryCell
@@ -161,6 +161,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
161
161
  deleted: false,
162
162
  include_draft: true,
163
163
  query: searchQuery,
164
+ format: "list",
164
165
  };
165
166
 
166
167
  return params;
@@ -49,7 +49,7 @@ const getOptionValues = (options: { global: IStructuredData[]; site: IStructured
49
49
  const type =
50
50
  !!currSchema.type && currSchema.type.value && currSchema.type.value && currSchema.type.value.toLowerCase();
51
51
 
52
- const isDetailTemplate = component.includes("Detail");
52
+ const isDetailTemplate = currSchema.type.mode === "detail";
53
53
 
54
54
  !!currSchema.type &&
55
55
  isDetailTemplate &&
@@ -33,4 +33,10 @@ export default [
33
33
  showInNav: true,
34
34
  icon: "Settings",
35
35
  },
36
+ {
37
+ path: `/settings/redirects`,
38
+ component: GlobalSettings,
39
+ name: "Settings",
40
+ showInNav: false,
41
+ },
36
42
  ];
@@ -7,6 +7,7 @@ import ContentTypes from "./../modules/Settings/ContentTypes";
7
7
  import Settings from "./../modules/Settings";
8
8
  import CategoriesList from "./../modules/Categories/CategoriesList";
9
9
  import StructuredData from "./../modules/StructuredData";
10
+ import SeoSettings from "./../modules/Settings/SeoSettings";
10
11
 
11
12
  const BASE_PATH = "/sites";
12
13
 
@@ -78,6 +79,11 @@ export default [
78
79
  component: ContentTypes,
79
80
  name: "Content types",
80
81
  },
82
+ {
83
+ path: `${BASE_PATH}/settings/seo-settings`,
84
+ component: SeoSettings,
85
+ name: "SEO Settings",
86
+ },
81
87
  ],
82
88
  },
83
89
  ],
@@ -9,6 +9,7 @@ import { IDataPacksState } from "@ax/containers/Settings/DataPacks/reducer";
9
9
  import { IUsersState } from "@ax/containers/Users/reducer";
10
10
  import { IGalleryState } from "@ax/containers/Gallery/reducer";
11
11
  import { IDomainsState } from "@ax/containers/Domains/reducer";
12
+ import { IRedirectsState } from "@ax/containers/Redirects/reducer";
12
13
 
13
14
  export interface IBreadcrumbItem {
14
15
  editorID: number;
@@ -86,6 +87,8 @@ export interface IAPIPage {
86
87
  haveDraftPage: number | null;
87
88
  draftFromPage: number | null;
88
89
  liveChanged: boolean;
90
+ templateId: string;
91
+ structuredDataContent: Record<string, []>
89
92
  }
90
93
 
91
94
  export interface IPage extends IAPIPage {
@@ -181,6 +184,7 @@ export interface IRootState {
181
184
  users: IUsersState;
182
185
  gallery: IGalleryState;
183
186
  domains: IDomainsState;
187
+ redirects: IRedirectsState;
184
188
  }
185
189
 
186
190
  export interface IStyledProps {
@@ -267,6 +271,7 @@ export interface IGetSitePagesParams {
267
271
  query?: string;
268
272
  lang?: number;
269
273
  filterStructuredData?: string;
274
+ format?: string;
270
275
  }
271
276
 
272
277
  export interface IGetGlobalPagesParams {
@@ -279,6 +284,7 @@ export interface IGetGlobalPagesParams {
279
284
  filterStructuredData?: boolean | string;
280
285
  excludeSite?: number;
281
286
  liveStatus?: string;
287
+ format?: string;
282
288
  }
283
289
 
284
290
  export interface IGetStructuredDataParams {
@@ -673,6 +679,13 @@ export interface IDomainRobot {
673
679
  content: string;
674
680
  }
675
681
 
682
+ export interface IRedirect {
683
+ id?: number;
684
+ from: string;
685
+ to: { pageId?: number; url: string; linkTo?: number; href?: string };
686
+ site?: { siteId: number | null; siteName: string | null };
687
+ }
688
+
676
689
  export type Field =
677
690
  | "AsyncCheckGroup"
678
691
  | "AsyncSelect"