@griddo/ax 1.60.9 → 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/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
|
@@ -26,6 +26,7 @@ import { appActions } from "@ax/containers/App";
|
|
|
26
26
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
27
27
|
import { navigationActions, menuActions } from "@ax/containers/Navigation";
|
|
28
28
|
import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
29
|
+
import { analyticsActions } from "@ax/containers/Analytics";
|
|
29
30
|
import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
|
|
30
31
|
import { socialActions } from "@ax/containers/Settings/Social";
|
|
31
32
|
import { handleRequest, isReqOk, sortBy } from "@ax/helpers";
|
|
@@ -34,6 +35,7 @@ const { setIsLoading, setIsSaving, setLanguage } = appActions;
|
|
|
34
35
|
const { resetDefaultsValues } = navigationActions;
|
|
35
36
|
const { resetPageEditor } = pageEditorActions;
|
|
36
37
|
const { resetMenuValues } = menuActions;
|
|
38
|
+
const { getAnalytics } = analyticsActions;
|
|
37
39
|
|
|
38
40
|
function setSites(sitesList: ISite[]): ISetSitesAction {
|
|
39
41
|
return { type: SET_SITES, payload: { sites: sitesList } };
|
|
@@ -345,6 +347,7 @@ const resetSiteValues = (dispatch: Dispatch) => {
|
|
|
345
347
|
resetMenuValues(dispatch);
|
|
346
348
|
dispatch(setCurrentSitePages([]));
|
|
347
349
|
dispatch(setTotalItems(0));
|
|
350
|
+
getAnalytics()(dispatch);
|
|
348
351
|
};
|
|
349
352
|
|
|
350
353
|
function saveCurrentSiteInfo(): (dispatch: Dispatch, getState: any) => Promise<void> {
|
package/src/global.d.ts
CHANGED
package/src/helpers/index.tsx
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
decodeEntities,
|
|
18
18
|
isNumber,
|
|
19
19
|
getFileExtension,
|
|
20
|
+
splitAndJoin,
|
|
21
|
+
splitAndTrim,
|
|
20
22
|
} from "./strings";
|
|
21
23
|
|
|
22
24
|
import {
|
|
@@ -152,4 +154,6 @@ export {
|
|
|
152
154
|
getFileExtension,
|
|
153
155
|
moveArrayElement,
|
|
154
156
|
isDevelopment,
|
|
157
|
+
splitAndJoin,
|
|
158
|
+
splitAndTrim,
|
|
155
159
|
};
|
package/src/helpers/requests.tsx
CHANGED
|
@@ -13,13 +13,18 @@ function handleRequest(callback: any, responseActions: any, loadingActions: any[
|
|
|
13
13
|
|
|
14
14
|
const response = await callback();
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const responseArr = Array.isArray(response) ? response : [response];
|
|
17
|
+
|
|
18
|
+
let result = true;
|
|
19
|
+
|
|
20
|
+
responseArr.forEach((response) => {
|
|
21
|
+
if (!isReqOk(response.status)) {
|
|
22
|
+
result = false;
|
|
23
|
+
handleError(response);
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
if (result) handleSuccess(response.data);
|
|
23
28
|
|
|
24
29
|
loadingActions.map(action => dispatch(action(false)));
|
|
25
30
|
|
package/src/helpers/strings.tsx
CHANGED
|
@@ -81,6 +81,17 @@ const getFileExtension = (fileName: string): string | null => {
|
|
|
81
81
|
return extension && extension[0].slice(1);
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
const splitAndJoin = (str = "", splitterIn = ",", splitterOut = ", "): string => {
|
|
85
|
+
return str?.split(splitterIn)
|
|
86
|
+
.map((str: string) => str.trim())
|
|
87
|
+
.filter((str: string) => str.length)
|
|
88
|
+
.join(splitterOut) || "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const splitAndTrim = (str?: string, splitter = ","): (string | never)[] => {
|
|
92
|
+
return str?.split(splitter).map((str: string) => str.trim()).filter((str: string) => str.length) || [];
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
export {
|
|
85
96
|
filterImageText,
|
|
86
97
|
splitCamelCase,
|
|
@@ -93,4 +104,6 @@ export {
|
|
|
93
104
|
decodeEntities,
|
|
94
105
|
isNumber,
|
|
95
106
|
getFileExtension,
|
|
107
|
+
splitAndJoin,
|
|
108
|
+
splitAndTrim,
|
|
96
109
|
};
|
package/src/hooks/forms.tsx
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { IDimension } from "@ax/types";
|
|
4
|
+
import { useModal } from "@ax/hooks";
|
|
5
|
+
import { splitAndJoin, trimText } from "@ax/helpers";
|
|
6
|
+
import { Modal } from "@ax/components";
|
|
7
|
+
|
|
8
|
+
import DimensionPanel from "../DimensionPanel";
|
|
9
|
+
import * as S from "./style";
|
|
10
|
+
|
|
11
|
+
const Item = (props: IProps): JSX.Element => {
|
|
12
|
+
const { item, setDimensionItem, removeDimension } = props;
|
|
13
|
+
|
|
14
|
+
const { isOpen, toggleModal } = useModal();
|
|
15
|
+
const { isOpen: isRemoveOpen, toggleModal: toggleRemoveModal } = useModal();
|
|
16
|
+
|
|
17
|
+
const handleClick = () => toggleModal();
|
|
18
|
+
|
|
19
|
+
const values = splitAndJoin(item.values, ";", "; ");
|
|
20
|
+
const hasValues = !["", "null"].includes(values);
|
|
21
|
+
const valuesText = hasValues ? values : "The value is defined on page";
|
|
22
|
+
|
|
23
|
+
const dimensionOptions = [
|
|
24
|
+
{
|
|
25
|
+
label: "Delete",
|
|
26
|
+
icon: "delete",
|
|
27
|
+
action: toggleRemoveModal,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const mainRemoveModalAction = {
|
|
32
|
+
title: "Delete Dimension",
|
|
33
|
+
onClick: removeDimension,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const secondaryRemoveModalAction = { title: "Cancel", onClick: toggleRemoveModal };
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<S.Component onClick={handleClick}>
|
|
41
|
+
<S.Name>{item.name}</S.Name><S.Values>{trimText(valuesText, 75)}</S.Values>
|
|
42
|
+
<S.StyledActionMenu icon="more" options={dimensionOptions} tooltip="Dimension actions" />
|
|
43
|
+
</S.Component>
|
|
44
|
+
<DimensionPanel item={item} isOpen={isOpen} toggleModal={toggleModal} setDimensionItem={setDimensionItem} />
|
|
45
|
+
<Modal
|
|
46
|
+
isOpen={isRemoveOpen}
|
|
47
|
+
hide={toggleRemoveModal}
|
|
48
|
+
title="Delete Dimension?"
|
|
49
|
+
secondaryAction={secondaryRemoveModalAction}
|
|
50
|
+
mainAction={mainRemoveModalAction}
|
|
51
|
+
size="S"
|
|
52
|
+
>
|
|
53
|
+
<S.ModalContent>
|
|
54
|
+
<p>
|
|
55
|
+
Are you sure you want to delete <strong>{item.name} dimension</strong>? If you delete it, this dimension will no longer be measurable.
|
|
56
|
+
<br />
|
|
57
|
+
This action <strong>cannot be undone</strong>.
|
|
58
|
+
</p>
|
|
59
|
+
</S.ModalContent>
|
|
60
|
+
</Modal>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
interface IProps {
|
|
66
|
+
item: IDimension;
|
|
67
|
+
setDimensionItem(content: IDimension): void;
|
|
68
|
+
removeDimension: () => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default Item;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { ActionMenu } from "@ax/components";
|
|
3
|
+
|
|
4
|
+
const StyledActionMenu = styled(ActionMenu)`
|
|
5
|
+
opacity: 0;
|
|
6
|
+
width: 32px;
|
|
7
|
+
display: flex;
|
|
8
|
+
margin-left: auto;
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
const Component = styled.div`
|
|
12
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
13
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
14
|
+
position: relative;
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
width: 100%;
|
|
18
|
+
height: ${(p) => p.theme.spacing.l};
|
|
19
|
+
background: ${(p) => p.theme.color.uiBackground02};
|
|
20
|
+
border: 1px solid transparent;
|
|
21
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
22
|
+
padding: 0 ${(p) => p.theme.spacing.s};
|
|
23
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
24
|
+
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
25
|
+
text-align: left;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
background: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:hover ${StyledActionMenu} {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const Name = styled.div`
|
|
38
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
39
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
40
|
+
width: 40%;
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
const Values = styled.div`
|
|
44
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
45
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
46
|
+
`
|
|
47
|
+
|
|
48
|
+
const Button = styled.div`
|
|
49
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
50
|
+
display: flex;
|
|
51
|
+
color: ${(p) => p.theme.color.interactive01};
|
|
52
|
+
margin-left: auto;
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const ModalContent = styled.div`
|
|
56
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
export { Component, Button, Name, Values, StyledActionMenu, ModalContent };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { IDimension } from "@ax/types";
|
|
4
|
+
import { FloatingPanel, Button, FieldsBehavior, NoteField } from "@ax/components";
|
|
5
|
+
import { camelize, splitAndJoin } from "@ax/helpers";
|
|
6
|
+
|
|
7
|
+
import * as S from "./style";
|
|
8
|
+
|
|
9
|
+
const DimensionPanel = (props: IProps): JSX.Element => {
|
|
10
|
+
const { isOpen, toggleModal, item, setDimensionItem } = props;
|
|
11
|
+
|
|
12
|
+
const [type, setType] = useState("dimensionsAndValues");
|
|
13
|
+
const [name, setName] = useState("");
|
|
14
|
+
const [values, setValues] = useState("");
|
|
15
|
+
|
|
16
|
+
const resetState = () => {
|
|
17
|
+
setType("dimensionsAndValues");
|
|
18
|
+
setName(item?.name || "");
|
|
19
|
+
|
|
20
|
+
const values = splitAndJoin(item?.values, ";", ";");
|
|
21
|
+
const _values = values === "null" ? "" : values;
|
|
22
|
+
setValues(_values || "");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
isOpen && resetState();
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
}, [isOpen]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
type === "onlyDimensions" && setValues("")
|
|
32
|
+
}, [type]);
|
|
33
|
+
|
|
34
|
+
const editItemAction = () => {
|
|
35
|
+
setDimensionItem({ ...item, name: camelize(name), values });
|
|
36
|
+
toggleModal();
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const title = item ? "Update Dimension" : "Add Dimension";
|
|
40
|
+
|
|
41
|
+
const editButton = {
|
|
42
|
+
label: title,
|
|
43
|
+
action: editItemAction,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const typeOptions = [
|
|
47
|
+
{
|
|
48
|
+
title: "Dimensions & Values",
|
|
49
|
+
name: "dimensionsAndValues",
|
|
50
|
+
value: "dimensionsAndValues",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "Only Dimensions",
|
|
54
|
+
name: "onlyDimensions",
|
|
55
|
+
value: "onlyDimensions",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const noteTitle = "Dimensions & Values"
|
|
60
|
+
const noteText = "Create a dimension and its values. You define the values now and select them on any page later. Your analytics data will be homogeneous avoiding duplicates that might cause inaccurate data results."
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<FloatingPanel title={title} toggleModal={toggleModal} isOpen={isOpen}>
|
|
64
|
+
<FieldsBehavior
|
|
65
|
+
name="removeItemChildren"
|
|
66
|
+
fieldType="RadioGroup"
|
|
67
|
+
title="Dimension type"
|
|
68
|
+
value={type}
|
|
69
|
+
options={typeOptions}
|
|
70
|
+
onChange={setType}
|
|
71
|
+
mandatory
|
|
72
|
+
/>
|
|
73
|
+
<S.NoteWrapper>
|
|
74
|
+
<NoteField value={{ text: noteText, title: noteTitle }} />
|
|
75
|
+
</S.NoteWrapper>
|
|
76
|
+
<FieldsBehavior
|
|
77
|
+
title="Dimension"
|
|
78
|
+
name="name"
|
|
79
|
+
fieldType="TextField"
|
|
80
|
+
value={name}
|
|
81
|
+
onChange={setName}
|
|
82
|
+
mandatory
|
|
83
|
+
/>
|
|
84
|
+
{type === "dimensionsAndValues" ? (
|
|
85
|
+
<FieldsBehavior
|
|
86
|
+
title="Values"
|
|
87
|
+
name="values"
|
|
88
|
+
fieldType="TextArea"
|
|
89
|
+
value={values}
|
|
90
|
+
onChange={setValues}
|
|
91
|
+
mandatory
|
|
92
|
+
/>
|
|
93
|
+
) : <></>}
|
|
94
|
+
<S.Footer>
|
|
95
|
+
<Button className="button" type="button" onClick={editButton.action}>
|
|
96
|
+
{editButton.label}
|
|
97
|
+
</Button>
|
|
98
|
+
</S.Footer>
|
|
99
|
+
</FloatingPanel>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
interface IProps {
|
|
104
|
+
item?: IDimension;
|
|
105
|
+
isOpen: boolean;
|
|
106
|
+
toggleModal: () => void;
|
|
107
|
+
setDimensionItem(content: IDimension): void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default DimensionPanel;
|
|
@@ -0,0 +1,14 @@
|
|
|
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 NoteWrapper = styled.div`
|
|
10
|
+
margin-bottom: ${(p) => p.theme.spacing.m};
|
|
11
|
+
|
|
12
|
+
`
|
|
13
|
+
|
|
14
|
+
export { Footer, NoteWrapper };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { IDimension, IDimensionsGroup } from "@ax/types";
|
|
4
|
+
import { useModal } from "@ax/hooks";
|
|
5
|
+
import { splitAndJoin } from "@ax/helpers";
|
|
6
|
+
import { Modal } from "@ax/components";
|
|
7
|
+
|
|
8
|
+
import GroupPanel from "../GroupPanel";
|
|
9
|
+
import * as S from "./style";
|
|
10
|
+
|
|
11
|
+
const Item = (props: IProps): JSX.Element => {
|
|
12
|
+
const { item, setGroupItem, dimensions, removeGroup } = props;
|
|
13
|
+
|
|
14
|
+
const { isOpen, toggleModal } = useModal();
|
|
15
|
+
const { isOpen: isRemoveOpen, toggleModal: toggleRemoveModal } = useModal();
|
|
16
|
+
|
|
17
|
+
const handleClick = () => toggleModal();
|
|
18
|
+
|
|
19
|
+
const contentType = splitAndJoin(item.templates, ";", ", ");
|
|
20
|
+
const hasContentType = !["null", ""].includes(contentType);
|
|
21
|
+
const contentTypeText = hasContentType ? "Content Type" : "No Content Type";
|
|
22
|
+
|
|
23
|
+
const groupOptions = [
|
|
24
|
+
{
|
|
25
|
+
label: "Delete",
|
|
26
|
+
icon: "delete",
|
|
27
|
+
action: toggleRemoveModal,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const mainRemoveModalAction = {
|
|
32
|
+
title: "Delete Group",
|
|
33
|
+
onClick: removeGroup,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const secondaryRemoveModalAction = { title: "Cancel", onClick: toggleRemoveModal };
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<S.Component onClick={handleClick}>
|
|
41
|
+
<S.Name>{item.name}</S.Name>
|
|
42
|
+
<S.ComponentInfo>
|
|
43
|
+
<S.Values>{contentTypeText}{hasContentType && <S.ContentType>{contentType}</S.ContentType>}</S.Values>
|
|
44
|
+
<S.StyledActionMenu icon="more" options={groupOptions} tooltip="Dimensions group actions" />
|
|
45
|
+
</S.ComponentInfo>
|
|
46
|
+
</S.Component>
|
|
47
|
+
<GroupPanel item={item} isOpen={isOpen} toggleModal={toggleModal} setGroupItem={setGroupItem} dimensions={dimensions} />
|
|
48
|
+
<Modal
|
|
49
|
+
isOpen={isRemoveOpen}
|
|
50
|
+
hide={toggleRemoveModal}
|
|
51
|
+
title="Delete Group?"
|
|
52
|
+
secondaryAction={secondaryRemoveModalAction}
|
|
53
|
+
mainAction={mainRemoveModalAction}
|
|
54
|
+
size="S"
|
|
55
|
+
>
|
|
56
|
+
<S.ModalContent>
|
|
57
|
+
<p>
|
|
58
|
+
Are you sure you want to delete <strong>{item.name} dimensions</strong>? If you delete it, you will no longer be able to use it.
|
|
59
|
+
<br />
|
|
60
|
+
This action <strong>cannot be undone</strong>.
|
|
61
|
+
</p>
|
|
62
|
+
</S.ModalContent>
|
|
63
|
+
</Modal>
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
interface IProps {
|
|
69
|
+
item: IDimensionsGroup;
|
|
70
|
+
setGroupItem(content: IDimensionsGroup): void;
|
|
71
|
+
dimensions: IDimension[];
|
|
72
|
+
removeGroup: () => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default Item;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { ActionMenu } from "@ax/components";
|
|
3
|
+
|
|
4
|
+
const StyledActionMenu = styled(ActionMenu)`
|
|
5
|
+
opacity: 0;
|
|
6
|
+
width: 32px;
|
|
7
|
+
display: flex;
|
|
8
|
+
margin-left: ${(p) => p.theme.spacing.s};
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
const Component = styled.div`
|
|
12
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
13
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
14
|
+
position: relative;
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: space-between;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: ${(p) => p.theme.spacing.l};
|
|
20
|
+
background: ${(p) => p.theme.color.uiBackground02};
|
|
21
|
+
border: 1px solid transparent;
|
|
22
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
23
|
+
padding: 0 ${(p) => p.theme.spacing.s};
|
|
24
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
25
|
+
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
26
|
+
text-align: left;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
|
|
29
|
+
&:hover {
|
|
30
|
+
background: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&:hover ${StyledActionMenu} {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const Name = styled.div`
|
|
39
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
40
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
const Values = styled.div`
|
|
44
|
+
display: flex;
|
|
45
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
46
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
47
|
+
`
|
|
48
|
+
|
|
49
|
+
const Button = styled.div`
|
|
50
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
51
|
+
display: flex;
|
|
52
|
+
color: ${(p) => p.theme.color.interactive01};
|
|
53
|
+
margin-left: auto;
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const ContentType = styled.div`
|
|
57
|
+
margin-left: ${(p) => p.theme.spacing.xxs};
|
|
58
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
59
|
+
color: ${(p) => p.theme.color.interactive01};
|
|
60
|
+
`
|
|
61
|
+
|
|
62
|
+
const ModalContent = styled.div`
|
|
63
|
+
padding: ${(p) => p.theme.spacing.m};
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const ComponentInfo = styled.div`
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
`
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
Component,
|
|
73
|
+
Button,
|
|
74
|
+
Name,
|
|
75
|
+
Values,
|
|
76
|
+
ContentType,
|
|
77
|
+
StyledActionMenu,
|
|
78
|
+
ModalContent,
|
|
79
|
+
ComponentInfo
|
|
80
|
+
};
|
|
@@ -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;
|