@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.
Files changed (148) hide show
  1. package/package.json +4 -2
  2. package/public/fonts/fonts.css +48 -660
  3. package/public/index.html +19 -16
  4. package/public/templates/template-redirects.csv +2 -0
  5. package/src/GlobalStore.tsx +3 -0
  6. package/src/Style/fonts.tsx +98 -72
  7. package/src/api/analytics.tsx +78 -0
  8. package/src/api/index.tsx +2 -0
  9. package/src/api/redirects.tsx +30 -2
  10. package/src/components/Button/style.tsx +1 -0
  11. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +1 -1
  12. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +4 -3
  13. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +6 -1
  14. package/src/components/FieldContainer/index.tsx +2 -2
  15. package/src/components/Fields/AnalyticsField/PageAnalytics/atoms.tsx +75 -0
  16. package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +139 -0
  17. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +77 -0
  18. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +89 -0
  19. package/src/components/Fields/AnalyticsField/index.tsx +38 -0
  20. package/src/components/Fields/AnalyticsField/style.tsx +13 -0
  21. package/src/components/Fields/AnalyticsField/utils.tsx +13 -0
  22. package/src/components/Fields/FieldGroup/style.tsx +1 -1
  23. package/src/components/Fields/ImageField/index.tsx +1 -1
  24. package/src/components/Fields/MultiCheckSelect/index.tsx +23 -6
  25. package/src/components/Fields/MultiCheckSelect/style.tsx +1 -0
  26. package/src/components/Fields/NoteField/index.tsx +1 -1
  27. package/src/components/Fields/RadioGroup/index.tsx +4 -3
  28. package/src/components/Fields/RadioGroup/style.tsx +8 -3
  29. package/src/components/Fields/RichText/index.tsx +13 -8
  30. package/src/components/Fields/TextArea/index.tsx +1 -3
  31. package/src/components/Fields/TextArea/style.tsx +3 -3
  32. package/src/components/Fields/UrlField/style.tsx +1 -0
  33. package/src/components/Fields/index.tsx +2 -0
  34. package/src/components/Modal/index.tsx +1 -1
  35. package/src/components/Modal/style.tsx +5 -1
  36. package/src/components/Notification/style.tsx +2 -5
  37. package/src/components/TableFilters/LiveFilter/index.tsx +4 -0
  38. package/src/components/TableFilters/NameFilter/style.tsx +1 -0
  39. package/src/components/TableFilters/TypeFilter/index.tsx +7 -2
  40. package/src/containers/Analytics/actions.tsx +58 -0
  41. package/src/containers/Analytics/constants.tsx +5 -0
  42. package/src/containers/Analytics/index.tsx +4 -0
  43. package/src/containers/Analytics/interfaces.tsx +9 -0
  44. package/src/containers/Analytics/reducer.tsx +26 -0
  45. package/src/containers/PageEditor/actions.tsx +1 -0
  46. package/src/containers/Redirects/actions.tsx +49 -3
  47. package/src/containers/Redirects/constants.tsx +12 -1
  48. package/src/containers/Redirects/interfaces.tsx +11 -1
  49. package/src/containers/Redirects/reducer.tsx +12 -1
  50. package/src/containers/Sites/actions.tsx +3 -0
  51. package/src/global.d.ts +2 -0
  52. package/src/helpers/index.tsx +4 -0
  53. package/src/helpers/requests.tsx +12 -7
  54. package/src/helpers/strings.tsx +13 -0
  55. package/src/hooks/forms.tsx +1 -1
  56. package/src/index.tsx +1 -0
  57. package/src/modules/Analytics/DimensionItem/index.tsx +71 -0
  58. package/src/modules/Analytics/DimensionItem/style.tsx +59 -0
  59. package/src/modules/Analytics/DimensionPanel/index.tsx +110 -0
  60. package/src/modules/Analytics/DimensionPanel/style.tsx +14 -0
  61. package/src/modules/Analytics/GroupItem/index.tsx +75 -0
  62. package/src/modules/Analytics/GroupItem/style.tsx +80 -0
  63. package/src/modules/Analytics/GroupPanel/index.tsx +178 -0
  64. package/src/modules/Analytics/GroupPanel/style.tsx +67 -0
  65. package/src/modules/Analytics/GroupPanel/utils.tsx +29 -0
  66. package/src/modules/Analytics/index.tsx +207 -0
  67. package/src/modules/Analytics/style.tsx +68 -0
  68. package/src/modules/Content/BulkHeader/TableHeader/index.tsx +1 -1
  69. package/src/modules/Content/PageItem/index.tsx +3 -3
  70. package/src/modules/Content/PageItem/style.tsx +1 -1
  71. package/src/modules/Content/hooks.tsx +2 -2
  72. package/src/modules/Content/index.tsx +22 -20
  73. package/src/modules/GlobalEditor/index.tsx +1 -1
  74. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +2 -2
  75. package/src/modules/PageEditor/index.tsx +1 -1
  76. package/src/modules/Redirects/RedirectItem/index.tsx +6 -2
  77. package/src/modules/Redirects/RedirectPanel/index.tsx +2 -0
  78. package/src/modules/Redirects/atoms.tsx +212 -0
  79. package/src/modules/Redirects/index.tsx +85 -27
  80. package/src/modules/Redirects/style.tsx +124 -3
  81. package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +1 -1
  82. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
  83. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +12 -3
  84. package/src/modules/StructuredData/StructuredDataList/index.tsx +17 -2
  85. package/src/routes/multisite.tsx +20 -2
  86. package/src/schemas/pages/GlobalPage.tsx +75 -64
  87. package/src/schemas/pages/Page.tsx +79 -67
  88. package/src/types/index.tsx +25 -3
  89. package/public/fonts/Source_Sans_Pro-200-cyrillic-ext107.woff2 +0 -0
  90. package/public/fonts/Source_Sans_Pro-200-cyrillic-ext149.woff2 +0 -0
  91. package/public/fonts/Source_Sans_Pro-200-cyrillic108.woff2 +0 -0
  92. package/public/fonts/Source_Sans_Pro-200-cyrillic150.woff2 +0 -0
  93. package/public/fonts/Source_Sans_Pro-200-greek-ext109.woff2 +0 -0
  94. package/public/fonts/Source_Sans_Pro-200-greek-ext151.woff2 +0 -0
  95. package/public/fonts/Source_Sans_Pro-200-greek110.woff2 +0 -0
  96. package/public/fonts/Source_Sans_Pro-200-greek152.woff2 +0 -0
  97. package/public/fonts/Source_Sans_Pro-200-vietnamese111.woff2 +0 -0
  98. package/public/fonts/Source_Sans_Pro-200-vietnamese153.woff2 +0 -0
  99. package/public/fonts/Source_Sans_Pro-300-cyrillic-ext114.woff2 +0 -0
  100. package/public/fonts/Source_Sans_Pro-300-cyrillic-ext156.woff2 +0 -0
  101. package/public/fonts/Source_Sans_Pro-300-cyrillic115.woff2 +0 -0
  102. package/public/fonts/Source_Sans_Pro-300-cyrillic157.woff2 +0 -0
  103. package/public/fonts/Source_Sans_Pro-300-greek-ext116.woff2 +0 -0
  104. package/public/fonts/Source_Sans_Pro-300-greek-ext158.woff2 +0 -0
  105. package/public/fonts/Source_Sans_Pro-300-greek117.woff2 +0 -0
  106. package/public/fonts/Source_Sans_Pro-300-greek159.woff2 +0 -0
  107. package/public/fonts/Source_Sans_Pro-300-vietnamese118.woff2 +0 -0
  108. package/public/fonts/Source_Sans_Pro-300-vietnamese160.woff2 +0 -0
  109. package/public/fonts/Source_Sans_Pro-400-cyrillic-ext121.woff2 +0 -0
  110. package/public/fonts/Source_Sans_Pro-400-cyrillic-ext163.woff2 +0 -0
  111. package/public/fonts/Source_Sans_Pro-400-cyrillic122.woff2 +0 -0
  112. package/public/fonts/Source_Sans_Pro-400-cyrillic164.woff2 +0 -0
  113. package/public/fonts/Source_Sans_Pro-400-greek-ext123.woff2 +0 -0
  114. package/public/fonts/Source_Sans_Pro-400-greek-ext165.woff2 +0 -0
  115. package/public/fonts/Source_Sans_Pro-400-greek124.woff2 +0 -0
  116. package/public/fonts/Source_Sans_Pro-400-greek166.woff2 +0 -0
  117. package/public/fonts/Source_Sans_Pro-400-vietnamese125.woff2 +0 -0
  118. package/public/fonts/Source_Sans_Pro-400-vietnamese167.woff2 +0 -0
  119. package/public/fonts/Source_Sans_Pro-600-cyrillic-ext128.woff2 +0 -0
  120. package/public/fonts/Source_Sans_Pro-600-cyrillic-ext170.woff2 +0 -0
  121. package/public/fonts/Source_Sans_Pro-600-cyrillic129.woff2 +0 -0
  122. package/public/fonts/Source_Sans_Pro-600-cyrillic171.woff2 +0 -0
  123. package/public/fonts/Source_Sans_Pro-600-greek-ext130.woff2 +0 -0
  124. package/public/fonts/Source_Sans_Pro-600-greek-ext172.woff2 +0 -0
  125. package/public/fonts/Source_Sans_Pro-600-greek131.woff2 +0 -0
  126. package/public/fonts/Source_Sans_Pro-600-greek173.woff2 +0 -0
  127. package/public/fonts/Source_Sans_Pro-600-vietnamese132.woff2 +0 -0
  128. package/public/fonts/Source_Sans_Pro-600-vietnamese174.woff2 +0 -0
  129. package/public/fonts/Source_Sans_Pro-700-cyrillic-ext135.woff2 +0 -0
  130. package/public/fonts/Source_Sans_Pro-700-cyrillic-ext177.woff2 +0 -0
  131. package/public/fonts/Source_Sans_Pro-700-cyrillic136.woff2 +0 -0
  132. package/public/fonts/Source_Sans_Pro-700-cyrillic178.woff2 +0 -0
  133. package/public/fonts/Source_Sans_Pro-700-greek-ext137.woff2 +0 -0
  134. package/public/fonts/Source_Sans_Pro-700-greek-ext179.woff2 +0 -0
  135. package/public/fonts/Source_Sans_Pro-700-greek138.woff2 +0 -0
  136. package/public/fonts/Source_Sans_Pro-700-greek180.woff2 +0 -0
  137. package/public/fonts/Source_Sans_Pro-700-vietnamese139.woff2 +0 -0
  138. package/public/fonts/Source_Sans_Pro-700-vietnamese181.woff2 +0 -0
  139. package/public/fonts/Source_Sans_Pro-900-cyrillic-ext142.woff2 +0 -0
  140. package/public/fonts/Source_Sans_Pro-900-cyrillic-ext184.woff2 +0 -0
  141. package/public/fonts/Source_Sans_Pro-900-cyrillic143.woff2 +0 -0
  142. package/public/fonts/Source_Sans_Pro-900-cyrillic185.woff2 +0 -0
  143. package/public/fonts/Source_Sans_Pro-900-greek-ext144.woff2 +0 -0
  144. package/public/fonts/Source_Sans_Pro-900-greek-ext186.woff2 +0 -0
  145. package/public/fonts/Source_Sans_Pro-900-greek145.woff2 +0 -0
  146. package/public/fonts/Source_Sans_Pro-900-greek187.woff2 +0 -0
  147. package/public/fonts/Source_Sans_Pro-900-vietnamese146.woff2 +0 -0
  148. package/public/fonts/Source_Sans_Pro-900-vietnamese188.woff2 +0 -0
@@ -0,0 +1,178 @@
1
+ import React, { useState, useEffect } from "react";
2
+
3
+ import { IDimension, IDimensionsGroup } from "@ax/types";
4
+ import { FloatingPanel, Button, FieldsBehavior, CheckGroup, RadioGroup } from "@ax/components";
5
+ import { splitAndTrim } from "@ax/helpers";
6
+
7
+ import * as S from "./style";
8
+ import { getTemplateOptions } from "./utils";
9
+
10
+ const GroupPanel = (props: IProps): JSX.Element => {
11
+ const { isOpen, toggleModal, item, setGroupItem, dimensions } = props;
12
+
13
+ const [name, setName] = useState("");
14
+ const [selectedDimensions, setSelectedDimensions] = useState<(string | never)[]>(["all"]);
15
+ const [selectedTemplates, setSelectedTemplates] = useState<(string | never)[]>([]);
16
+ const [hasTemplate, setHasTemplate] = useState(false);
17
+
18
+ const templates = getTemplateOptions();
19
+ const resetState = () => {
20
+ setName(item?.name || "");
21
+
22
+ const dimensionOptions = [];
23
+ const dimensionsArr = splitAndTrim(item?.dimensions, ";");
24
+ const isAllDimensions = dimensionsArr.length === dimensions.length;
25
+ (!item || isAllDimensions)
26
+ ? dimensionOptions.push("all")
27
+ : dimensionOptions.push(...dimensionsArr);
28
+ setSelectedDimensions(dimensionOptions);
29
+
30
+ const templateOptions = [];
31
+ const templatesArr = splitAndTrim(item?.templates, ";");
32
+ !!item && templateOptions.push(...templatesArr);
33
+ const _templateOptions = templateOptions.filter((option) => option !== "null");
34
+ setSelectedTemplates(_templateOptions);
35
+
36
+ const hasTemplate = !!templateOptions.length && !templateOptions.includes("null");
37
+ setHasTemplate(hasTemplate);
38
+ }
39
+
40
+ useEffect(() => {
41
+ isOpen && resetState();
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ }, [isOpen]);
44
+
45
+ const editItemAction = () => {
46
+ const dimensionsArr = selectedDimensions.includes("all")
47
+ ? dimensions.map((dimension) => dimension.name)
48
+ : selectedDimensions;
49
+ const dimensionsStr = `;${dimensionsArr.join(";")}`;
50
+ const templatesStr = `;${selectedTemplates.join(";")}`;
51
+
52
+ const value = {
53
+ ...item,
54
+ name,
55
+ dimensions: dimensionsStr,
56
+ templates: templatesStr
57
+ };
58
+ setGroupItem(value);
59
+
60
+ toggleModal();
61
+ };
62
+
63
+ const title = item ? "Update Dimensions Group" : "Create Dimensions Group";
64
+
65
+ const editButton = {
66
+ label: title,
67
+ action: editItemAction,
68
+ };
69
+
70
+ const initialDimensionOption = {
71
+ name: "all",
72
+ value: "all",
73
+ title: "All Dimensions",
74
+ };
75
+ const dimensionOptions = [{ ...initialDimensionOption }];
76
+ dimensions?.forEach((dimension) => {
77
+ const { name } = dimension;
78
+ const option = { name, value: name, title: name };
79
+ return dimensionOptions.push(option);
80
+ });
81
+
82
+ const setDimensions = (selection: any) => {
83
+ if (!selection.length) selection = ["all"];
84
+ setSelectedDimensions([...selection]);
85
+ }
86
+
87
+ const setTemplates = (selection: any) => {
88
+ setSelectedTemplates([...selection]);
89
+ }
90
+
91
+ const selectedDimensionsN = selectedDimensions.includes("all")
92
+ ? dimensions?.length
93
+ : selectedDimensions.length;
94
+
95
+ const hasTemplateOpts = [
96
+ {
97
+ title: "No",
98
+ value: "no",
99
+ name: "no",
100
+ },
101
+ {
102
+ title: "Yes",
103
+ value: "yes",
104
+ name: "yes",
105
+ },
106
+ ];
107
+ const handleChangeHasTemplate = (value: string | boolean) => {
108
+ const hasTemplate = value === "yes";
109
+ setHasTemplate(hasTemplate);
110
+ !hasTemplate && setSelectedTemplates([]);
111
+
112
+ }
113
+
114
+ return (
115
+ <FloatingPanel title={title} toggleModal={toggleModal} isOpen={isOpen}>
116
+ <S.ContentWrapper>
117
+ <FieldsBehavior
118
+ title="Dimension"
119
+ name="name"
120
+ fieldType="TextField"
121
+ value={name}
122
+ onChange={setName}
123
+ mandatory
124
+ />
125
+ <S.Divider />
126
+ <S.Heading>
127
+ <S.HeadingText>Dimensions</S.HeadingText>
128
+ <S.HeadingCounter>{selectedDimensionsN}/{dimensions?.length}</S.HeadingCounter>
129
+ </S.Heading>
130
+ <S.Description>Select the dimensions to this content.</S.Description>
131
+ <CheckGroup
132
+ options={dimensionOptions}
133
+ value={selectedDimensions}
134
+ onChange={setDimensions}
135
+ selectAllOption="all"
136
+ />
137
+ <S.Divider />
138
+ <S.Heading $spaceBetween>
139
+ <S.HeadingText>Dimensions</S.HeadingText>
140
+ <S.RadioTemplate>
141
+ <RadioGroup
142
+ name="hasTemplate"
143
+ value={hasTemplate ? "yes" : "no"}
144
+ options={hasTemplateOpts}
145
+ onChange={handleChangeHasTemplate}
146
+ inline
147
+ />
148
+ </S.RadioTemplate>
149
+ </S.Heading>
150
+ <S.Description>If you create a page of this template, it will appear with these dimensions by default.</S.Description>
151
+ {hasTemplate ? (
152
+ <CheckGroup
153
+ options={templates}
154
+ value={selectedTemplates}
155
+ onChange={setTemplates}
156
+ />
157
+ ) : (
158
+ <></>
159
+ )}
160
+ </S.ContentWrapper>
161
+ <S.Footer>
162
+ <Button className="button" type="button" onClick={editButton.action}>
163
+ {editButton.label}
164
+ </Button>
165
+ </S.Footer>
166
+ </FloatingPanel>
167
+ );
168
+ };
169
+
170
+ interface IProps {
171
+ item?: IDimensionsGroup;
172
+ isOpen: boolean;
173
+ toggleModal: () => void;
174
+ setGroupItem(content: IDimensionsGroup): void;
175
+ dimensions: IDimension[];
176
+ }
177
+
178
+ export default GroupPanel;
@@ -0,0 +1,67 @@
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 Divider = styled.div`
10
+ position: relative;
11
+ height: 1px;
12
+ margin-top: ${(p) => p.theme.spacing.s};
13
+ margin-bottom: ${(p) => p.theme.spacing.s};
14
+
15
+ &:after {
16
+ content: "";
17
+ position: absolute;
18
+ height: 1px;
19
+ width: calc(100% + ${(p) => p.theme.spacing.m} * 2);
20
+ top: 0;
21
+ left: calc(-${(p) => p.theme.spacing.m});
22
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
23
+ }
24
+ `;
25
+
26
+ const Heading = styled.div<{ $spaceBetween?: boolean }>`
27
+ ${(p) => p.theme.textStyle.headingXS};
28
+ display: flex;
29
+ justify-content: ${(p) => p.$spaceBetween ? "space-between" : "flex-start"};
30
+ `;
31
+
32
+ const HeadingText = styled.div`
33
+ color: ${(p) => p.theme.color.textHighEmphasis};
34
+ `;
35
+
36
+ const HeadingCounter = styled.div`
37
+ color: ${(p) => p.theme.color.textMediumEmphasis};
38
+ margin-left: ${(p) => p.theme.spacing.xs};
39
+ `;
40
+
41
+ const Description = styled.div`
42
+ ${(p) => p.theme.textStyle.uiXS};
43
+ color: ${(p) => p.theme.color.textHighEmphasis};
44
+ padding-bottom: ${(p) => p.theme.spacing.s};
45
+ `;
46
+
47
+ const RadioTemplate = styled.div``;
48
+
49
+ const ContentWrapper = styled.div`
50
+ height: calc(100% - ${(p) => p.theme.spacing.s} * 10);
51
+ overflow-y: scroll;
52
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
53
+ position: absolute;
54
+ left: 0;
55
+ padding: 0 ${(p) => p.theme.spacing.m} ${(p) => p.theme.spacing.s} ${(p) => p.theme.spacing.m};
56
+ `;
57
+
58
+ export {
59
+ Footer,
60
+ Divider,
61
+ Heading,
62
+ HeadingText,
63
+ HeadingCounter,
64
+ Description,
65
+ RadioTemplate,
66
+ ContentWrapper
67
+ };
@@ -0,0 +1,29 @@
1
+ import { schemas } from "components";
2
+
3
+ const getTemplateOptions = (): ITemplateOption[] => {
4
+ const { templates } = schemas;
5
+ const templatesOptionsValues: ITemplateOption[] = [];
6
+
7
+ Object.keys(templates).forEach((schema: string) => {
8
+ const currSchema = templates[schema];
9
+ const { component, displayName } = currSchema;
10
+
11
+ !!currSchema.type &&
12
+ ["list", "detail"].includes(currSchema.type.mode) &&
13
+ templatesOptionsValues.push({
14
+ name: component,
15
+ title: displayName,
16
+ value: component,
17
+ });
18
+ });
19
+
20
+ return templatesOptionsValues;
21
+ };
22
+
23
+ interface ITemplateOption {
24
+ name: string,
25
+ title: string,
26
+ value: string,
27
+ }
28
+
29
+ export { getTemplateOptions };
@@ -0,0 +1,207 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import { IAnalytics, IDimension, IDimensionsGroup, IRootState } from "@ax/types";
5
+ import { appActions } from "@ax/containers/App";
6
+ import { analyticsActions } from "@ax/containers/Analytics";
7
+ import { MainWrapper, ErrorToast, Loading, FieldsBehavior } from "@ax/components";
8
+ import { RouteLeavingGuard } from "@ax/guards";
9
+ import { useIsDirty } from "@ax/hooks";
10
+ import { useModal } from "@ax/hooks";
11
+
12
+ import DimensionItem from "./DimensionItem";
13
+ import DimensionPanel from "./DimensionPanel";
14
+ import GroupItem from "./GroupItem";
15
+ import GroupPanel from "./GroupPanel";
16
+ import * as S from "./style";
17
+
18
+ const Analytics = (props: IProps): JSX.Element => {
19
+ const {
20
+ isSaving,
21
+ isLoading,
22
+ analytics,
23
+ getAnalytics,
24
+ setHistoryPush,
25
+ site,
26
+ updateAnalytics,
27
+ } = props;
28
+
29
+ const [analyticsState, setAnalyticsState] = useState(analytics);
30
+ const { isDirty, resetDirty } = useIsDirty(analyticsState);
31
+ const { isOpen: isDimensionOpen, toggleModal: toggleDimensionModal } = useModal();
32
+ const { isOpen: isGroupOpen, toggleModal: toggleGroupModal } = useModal();
33
+
34
+ const { scriptCode, dimensions, groups } = analyticsState;
35
+
36
+ const changeScriptCode = (scriptCode: string) => setAnalyticsState((state) => ({ ...state, scriptCode }));
37
+ const changeDimensions = (dimensions: IDimension[]) => setAnalyticsState((state) => ({ ...state, dimensions }));
38
+ const changeGroups = (groups: IDimensionsGroup[]) => setAnalyticsState((state) => ({ ...state, groups }));
39
+
40
+ useEffect(() => {
41
+ getAnalytics(site?.id);
42
+ }, [getAnalytics, site?.id]);
43
+
44
+ useEffect(() => {
45
+ setAnalyticsState(analytics);
46
+ }, [analytics]);
47
+
48
+ const handleSave = async () => {
49
+ const isSaved = await updateAnalytics(analyticsState, site?.id);
50
+ if (isSaved) resetDirty();
51
+ };
52
+
53
+ const rightButtonProps = {
54
+ label: isSaving ? "Saving" : "Save",
55
+ disabled: isSaving,
56
+ action: () => handleSave(),
57
+ };
58
+
59
+ const mapDimensionList = (items: IDimension[]) =>
60
+ Array.isArray(items) &&
61
+ items.map((item: IDimension, i: number) => {
62
+ const handleSetItem = (newContent: IDimension) => {
63
+ const newItems = [...dimensions];
64
+ newItems[i] = newContent;
65
+ changeDimensions(newItems);
66
+ };
67
+ const handleRemoveDimension = () => {
68
+ const newItems = [...dimensions];
69
+ newItems.splice(i, 1);
70
+ changeDimensions(newItems);
71
+ };
72
+ return (
73
+ <DimensionItem
74
+ key={`${item.id}${item.name}${i}`}
75
+ item={item}
76
+ setDimensionItem={handleSetItem}
77
+ removeDimension={handleRemoveDimension}
78
+ />
79
+ );
80
+ });
81
+
82
+ const mapGroupList = (items: IDimensionsGroup[]) =>
83
+ Array.isArray(items) &&
84
+ items.map((item: IDimensionsGroup, i: number) => {
85
+ const handleSetItem = (newContent: IDimensionsGroup) => {
86
+ const newItems = [...groups];
87
+ newItems[i] = newContent;
88
+ changeGroups(newItems);
89
+ };
90
+ const handleRemoveGroup = () => {
91
+ const newItems = [...groups];
92
+ newItems.splice(i, 1);
93
+ changeGroups(newItems);
94
+ };
95
+ return (
96
+ <GroupItem
97
+ key={`${item.id}${item.name}${i}`}
98
+ item={item}
99
+ setGroupItem={handleSetItem}
100
+ dimensions={dimensions}
101
+ removeGroup={handleRemoveGroup}
102
+ />
103
+ )
104
+ });
105
+
106
+ const handleAddDimension = (item: IDimension) => {
107
+ const { dimensions } = analyticsState;
108
+ const updatedDimensions = dimensions ? [...dimensions, item] : [item];
109
+ changeDimensions(updatedDimensions);
110
+ }
111
+
112
+ const handleAddGroup = (item: IDimensionsGroup) => {
113
+ const { groups } = analyticsState;
114
+ const updatedGroups = groups ? [...groups, item] : [item];
115
+ changeGroups(updatedGroups);
116
+ }
117
+
118
+ const setRoute = (path: string) => setHistoryPush(path);
119
+ const modalText = (
120
+ <>
121
+ Some analytics <strong>are not saved</strong>.{" "}
122
+ </>
123
+ );
124
+
125
+ const isGroupDisabled = dimensions?.length < 2;
126
+
127
+ if (isLoading) return <Loading />;
128
+
129
+ return (
130
+ <>
131
+ <RouteLeavingGuard when={isDirty} action={setRoute} text={modalText} />
132
+ <MainWrapper backLink={false} title="Analytics Settings" rightButton={rightButtonProps}>
133
+ <ErrorToast />
134
+ <S.Wrapper>
135
+ <S.FormWrapper>
136
+ <S.ScriptCodeWrapper>
137
+ <FieldsBehavior
138
+ title="Analytics Tracking ID or Script Code"
139
+ name="scriptCode"
140
+ fieldType="TextArea"
141
+ value={scriptCode || ""}
142
+ onChange={changeScriptCode}
143
+ placeholder="Type the Google Analytics or Google Tag Manager code."
144
+ />
145
+ </S.ScriptCodeWrapper>
146
+ <S.SettingsWrapper>
147
+ <S.Heading>Data layer configuration</S.Heading>
148
+ <S.SettingContent>
149
+ <S.SettingText>
150
+ You can configure Data Layer to measure dimensions and data values when loading a page.
151
+ </S.SettingText>
152
+ <S.StyledButton type="button" buttonStyle="text" icon="addCircle" onClick={toggleDimensionModal}>
153
+ Add dimension
154
+ </S.StyledButton>
155
+ <S.Table>{dimensions && mapDimensionList(dimensions)}</S.Table>
156
+ </S.SettingContent>
157
+ </S.SettingsWrapper>
158
+ <S.SettingsWrapper>
159
+ <S.Heading>Dimensions group by content</S.Heading>
160
+ <S.SettingContent>
161
+ <S.SettingText>
162
+ You can create groups with the dimensions to measure on a page based on its content..
163
+ </S.SettingText>
164
+ <S.StyledButton type="button" buttonStyle="text" icon="addCircle" onClick={toggleGroupModal} disabled={isGroupDisabled}>
165
+ Define group
166
+ </S.StyledButton>
167
+ <S.Table>{groups && mapGroupList(groups)}</S.Table>
168
+ </S.SettingContent>
169
+ </S.SettingsWrapper>
170
+ </S.FormWrapper>
171
+ </S.Wrapper>
172
+ <DimensionPanel isOpen={isDimensionOpen} toggleModal={toggleDimensionModal} setDimensionItem={handleAddDimension} />
173
+ <GroupPanel isOpen={isGroupOpen} toggleModal={toggleGroupModal} setGroupItem={handleAddGroup} dimensions={dimensions} />
174
+ </MainWrapper>
175
+ </>
176
+ );
177
+ };
178
+
179
+ const mapStateToProps = (state: IRootState) => ({
180
+ isSaving: state.app.isSaving,
181
+ isLoading: state.app.isLoading,
182
+ analytics: state.analytics,
183
+ site: state.sites.currentSiteInfo,
184
+ });
185
+
186
+ const mapDispatchToProps = {
187
+ setHistoryPush: appActions.setHistoryPush,
188
+ getAnalytics: analyticsActions.getAnalytics,
189
+ updateAnalytics: analyticsActions.updateAnalytics,
190
+ };
191
+
192
+ interface IAnalyticsProps {
193
+ isSaving: boolean;
194
+ isLoading: boolean;
195
+ analytics: IAnalytics;
196
+ site: any;
197
+ }
198
+
199
+ interface IDispatchProps {
200
+ getAnalytics(siteId?: number): void;
201
+ setHistoryPush(path: string, isEditor?: boolean): void;
202
+ updateAnalytics(analyticsState: IAnalytics, siteId?: number): Promise<boolean>;
203
+ }
204
+
205
+ type IProps = IAnalyticsProps & IDispatchProps;
206
+
207
+ export default connect(mapStateToProps, mapDispatchToProps)(Analytics);
@@ -0,0 +1,68 @@
1
+ import styled from "styled-components";
2
+ import { Button } from "@ax/components";
3
+
4
+ const Wrapper = styled.div`
5
+ display: flex;
6
+ height: 100%;
7
+ overflow: auto;
8
+ flex-direction: column;
9
+ width: 100%;
10
+ padding: ${(p) => p.theme.spacing.m};
11
+ `;
12
+
13
+ const Table = styled.div`
14
+ display: flex;
15
+ flex-direction: column;
16
+ width: 100%;
17
+ margin-top: ${(p) => p.theme.spacing.m};
18
+ `;
19
+
20
+ const Heading = styled.div`
21
+ ${(p) => p.theme.textStyle.headingXS};
22
+ color: ${(p) => p.theme.color.textHighEmphasis};
23
+ padding-bottom: ${(p) => p.theme.spacing.xs};
24
+ `;
25
+
26
+ const SettingsWrapper = styled.div`
27
+ position: relative;
28
+ `;
29
+
30
+ const ScriptCodeWrapper = styled.div`
31
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
32
+ margin-bottom: ${(p) => p.theme.spacing.m};
33
+ `;
34
+
35
+ const SettingContent = styled.div`
36
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
37
+ margin-bottom: ${(p) => p.theme.spacing.m};
38
+ padding-bottom: ${(p) => p.theme.spacing.m};
39
+ `;
40
+
41
+ const SettingText = styled.div`
42
+ ${(p) => p.theme.textStyle.uiM};
43
+ color: ${(p) => p.theme.color.textMediumEmphasis};
44
+ width: calc(${(p) => p.theme.spacing.l} * 12);
45
+ `;
46
+
47
+ const StyledButton = styled(Button)`
48
+ position: absolute;
49
+ top: 0;
50
+ right: 0;
51
+ `;
52
+
53
+ const FormWrapper = styled.div`
54
+ width: 720px;
55
+ margin: ${(p) => `${p.theme.spacing.m} 0 0 ${p.theme.spacing.m}`};
56
+ `;
57
+
58
+ export {
59
+ Wrapper,
60
+ Table,
61
+ Heading,
62
+ SettingsWrapper,
63
+ SettingContent,
64
+ SettingText,
65
+ StyledButton,
66
+ FormWrapper,
67
+ ScriptCodeWrapper
68
+ };
@@ -94,7 +94,7 @@ const TableHeader = (props: IProps): JSX.Element => {
94
94
  {CategoryColumns}
95
95
  {activeColumns.includes("type") && (
96
96
  <S.HeaderWrapper>
97
- <TypeFilter filterItems={filterItems} filters={typeFilters} pointer="type" />
97
+ <TypeFilter filterItems={filterItems} filters={typeFilters} value={filterValues.types} pointer="type" />
98
98
  </S.HeaderWrapper>
99
99
  )}
100
100
  {activeColumns.includes("live") && (
@@ -261,8 +261,8 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
261
261
  isGlobal && !manuallyImported
262
262
  ? "This page has been provided by subscription. You cannot remove it."
263
263
  : canBeUnpublished === false
264
- ? "This is the canonical site of the page. You cannot remove it."
265
- : null;
264
+ ? "This is the canonical site of the page. You cannot remove it."
265
+ : null;
266
266
 
267
267
  let menuOptions: IPageOption[] = [
268
268
  {
@@ -370,7 +370,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
370
370
  isGlobal &&
371
371
  categoryColumns.map((col: any) => {
372
372
  const type = structuredDataContent[col.from];
373
- const categories = type && type.map((cat: any) => cat.label || cat.title);
373
+ const categories = Array.isArray(type) && type.map((cat: any) => cat.label || cat.title);
374
374
  return (
375
375
  activeColumns.includes(col.key) && (
376
376
  <CategoryCell
@@ -90,6 +90,7 @@ const Wrapper = styled.div`
90
90
 
91
91
  const ActionsCell = styled(Cell)`
92
92
  flex: 0 0 100px;
93
+ text-align: center;
93
94
  `;
94
95
 
95
96
  const CategoryCell = styled(Cell)`
@@ -102,7 +103,6 @@ const StyledActionMenu = styled(ActionMenu)`
102
103
  opacity: 0;
103
104
  width: 32px;
104
105
  display: flex;
105
- margin-left: auto;
106
106
  `;
107
107
 
108
108
  const PageRow = styled(Row)<{ global?: boolean }>`
@@ -23,8 +23,8 @@ const useSortedListStatus = () => {
23
23
 
24
24
  const useFilterQuery = () => {
25
25
  const initialQueryValues = {
26
- type: "",
27
- translated: "",
26
+ type: "all",
27
+ translated: "all",
28
28
  liveStatus: "all",
29
29
  order: "",
30
30
  categories: "all",
@@ -161,25 +161,26 @@ const Content = (props: IProps): JSX.Element => {
161
161
 
162
162
  const getParams = useCallback(() => {
163
163
  const siteID = currentSiteInfo ? currentSiteInfo.id : null;
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
- };
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
+ : {
177
+ siteID,
178
+ deleted: false,
179
+ page,
180
+ itemsPerPage,
181
+ query: searchQuery,
182
+ format: "list",
183
+ };
183
184
 
184
185
  return params;
185
186
  }, [filter, currentSiteInfo, isStructuredData, page, searchQuery]);
@@ -237,7 +238,8 @@ const Content = (props: IProps): JSX.Element => {
237
238
  if (!isLoading) {
238
239
  const isContentType = filter !== "unique-pages";
239
240
  const emptyState: IEmptyStateProps = {};
240
- const isSearching = searchQuery.length > 0;
241
+ const { liveStatus, translated, type } = filterValues;
242
+ const isSearching = searchQuery.length > 0 || liveStatus !== "all" || translated !== "all" || type !== "all";
241
243
  if (isSearching) {
242
244
  emptyState.icon = "search";
243
245
  emptyState.title = "Oh! No Results Found";
@@ -62,7 +62,7 @@ const GlobalEditor = (props: IProps) => {
62
62
  setTab(defaultTab);
63
63
  handleGetPage();
64
64
  if (!pageID) {
65
- setIsDirty(true);
65
+ setIsDirty(false);
66
66
  }
67
67
 
68
68
  const interval = setInterval(() => {
@@ -45,7 +45,7 @@ const DefaultsEditor = (props: IProps) => {
45
45
  useEffect(() => {
46
46
  getValues();
47
47
  if (isNew) {
48
- setIsDirty(true);
48
+ setIsDirty(false);
49
49
  }
50
50
  // eslint-disable-next-line react-hooks/exhaustive-deps
51
51
  }, []);
@@ -88,7 +88,7 @@ const DefaultsEditor = (props: IProps) => {
88
88
  };
89
89
 
90
90
  const createNewTranslation = (isNewTranslation: boolean) => {
91
- setIsDirty(true);
91
+ setIsDirty(false);
92
92
  createTranslation(isNewTranslation);
93
93
  };
94
94