@griddo/ax 10.6.4 → 10.6.6
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 +3 -2
- package/src/__tests__/components/TableFilters/CustomizeFilters/CustomizeFilters.test.tsx +39 -35
- package/src/api/structuredData.tsx +21 -1
- package/src/components/BulkSelectionOptions/index.tsx +4 -2
- package/src/components/CategoryCell/style.tsx +1 -1
- package/src/components/ExportButton/index.tsx +52 -0
- package/src/components/ExportButton/style.tsx +36 -0
- package/src/components/MainWrapper/AppBar/index.tsx +4 -0
- package/src/components/MainWrapper/index.tsx +1 -0
- package/src/components/TableFilters/CategoryFilter/index.tsx +2 -2
- package/src/components/TableFilters/CategoryFilter/style.tsx +1 -1
- package/src/components/TableFilters/CustomizeFilters/index.tsx +15 -12
- package/src/components/index.tsx +2 -0
- package/src/containers/StructuredData/actions.tsx +43 -1
- package/src/helpers/arrays.tsx +10 -1
- package/src/helpers/customColumns.tsx +45 -0
- package/src/helpers/index.tsx +6 -1
- package/src/modules/Content/BulkHeader/TableHeader/index.tsx +11 -27
- package/src/modules/Content/BulkHeader/TableHeader/style.tsx +4 -2
- package/src/modules/Content/BulkHeader/index.tsx +22 -14
- package/src/modules/Content/PageItem/index.tsx +4 -3
- package/src/modules/Content/PageItem/style.tsx +6 -3
- package/src/modules/Content/index.tsx +43 -24
- package/src/modules/Content/utils.tsx +36 -3
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +11 -8
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/style.tsx +8 -7
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +30 -16
- package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +4 -6
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -7
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +3 -2
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +27 -18
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/style.tsx +15 -5
- package/src/modules/StructuredData/StructuredDataList/hooks.tsx +11 -11
- package/src/modules/StructuredData/StructuredDataList/index.tsx +58 -47
- package/src/modules/StructuredData/StructuredDataList/utils.tsx +52 -1
- package/src/types/index.tsx +8 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "10.6.
|
|
4
|
+
"version": "10.6.6",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"mini-css-extract-plugin": "0.11.3",
|
|
113
113
|
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
|
114
114
|
"pkg-dir": "^5.0.0",
|
|
115
|
+
"pnp-webpack-plugin": "^1.7.0",
|
|
115
116
|
"polished": "3.4.1",
|
|
116
117
|
"postcss": "^8.4.5",
|
|
117
118
|
"postcss-flexbugs-fixes": "4.1.0",
|
|
@@ -232,5 +233,5 @@
|
|
|
232
233
|
"publishConfig": {
|
|
233
234
|
"access": "public"
|
|
234
235
|
},
|
|
235
|
-
"gitHead": "
|
|
236
|
+
"gitHead": "9ffcd2b0b5b1f5983045cf9d3c77769cec0a2a8d"
|
|
236
237
|
}
|
|
@@ -15,14 +15,15 @@ const defaultProps = mock<ICustomizeFiltersProps>();
|
|
|
15
15
|
|
|
16
16
|
describe("CustomizeFilters component rendering", () => {
|
|
17
17
|
it("should render customize filter", () => {
|
|
18
|
-
defaultProps.columns =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
defaultProps.columns = [
|
|
19
|
+
{ id: "live", title: "Live", show: true },
|
|
20
|
+
{ id: "profile", title: "Profile", show: true },
|
|
21
|
+
{ id: "site", title: "Site", show: true },
|
|
22
|
+
{ id: "status", title: "Status", show: true },
|
|
23
|
+
{ id: "translation", title: "Trans.", show: true },
|
|
24
|
+
];
|
|
25
25
|
defaultProps.value = ["site", "profile", "live", "status", "translation"];
|
|
26
|
+
defaultProps.limit = { value: 5, text: "five" };
|
|
26
27
|
defaultProps.setColumns = jest.fn() as CalledWithMock<void, [newValue: any]> & ((newValue: any) => void);
|
|
27
28
|
|
|
28
29
|
render(
|
|
@@ -38,14 +39,15 @@ describe("CustomizeFilters component rendering", () => {
|
|
|
38
39
|
|
|
39
40
|
describe("CustomizeFilters events", () => {
|
|
40
41
|
it("should render check group on click floating menu button", () => {
|
|
41
|
-
defaultProps.columns =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
defaultProps.columns = [
|
|
43
|
+
{ id: "live", title: "Live", show: true },
|
|
44
|
+
{ id: "profile", title: "Profile", show: true },
|
|
45
|
+
{ id: "site", title: "Site", show: true },
|
|
46
|
+
{ id: "status", title: "Status", show: true },
|
|
47
|
+
{ id: "translation", title: "Trans.", show: true },
|
|
48
|
+
];
|
|
48
49
|
defaultProps.value = ["site", "profile", "live", "status", "translation"];
|
|
50
|
+
defaultProps.limit = { value: 5, text: "five" };
|
|
49
51
|
defaultProps.setColumns = jest.fn() as CalledWithMock<void, [newValue: any]> & ((newValue: any) => void);
|
|
50
52
|
|
|
51
53
|
render(
|
|
@@ -66,14 +68,15 @@ describe("CustomizeFilters events", () => {
|
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
it("should check option on click", () => {
|
|
69
|
-
defaultProps.columns =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
defaultProps.columns = [
|
|
72
|
+
{ id: "live", title: "Live", show: true },
|
|
73
|
+
{ id: "profile", title: "Profile", show: true },
|
|
74
|
+
{ id: "site", title: "Site", show: true },
|
|
75
|
+
{ id: "status", title: "Status", show: true },
|
|
76
|
+
{ id: "translation", title: "Trans.", show: true },
|
|
77
|
+
];
|
|
76
78
|
defaultProps.value = ["site", "profile", "live", "status", "translation"];
|
|
79
|
+
defaultProps.limit = { value: 5, text: "five" };
|
|
77
80
|
defaultProps.setColumns = jest.fn() as CalledWithMock<void, [newValue: any]> & ((newValue: any) => void);
|
|
78
81
|
|
|
79
82
|
render(
|
|
@@ -96,14 +99,15 @@ describe("CustomizeFilters events", () => {
|
|
|
96
99
|
});
|
|
97
100
|
|
|
98
101
|
it("should call setColumns action on change", async () => {
|
|
99
|
-
defaultProps.columns =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
defaultProps.columns = [
|
|
103
|
+
{ id: "live", title: "Live", show: true },
|
|
104
|
+
{ id: "profile", title: "Profile", show: true },
|
|
105
|
+
{ id: "site", title: "Site", show: true },
|
|
106
|
+
{ id: "status", title: "Status", show: true },
|
|
107
|
+
{ id: "translation", title: "Trans.", show: true },
|
|
108
|
+
];
|
|
106
109
|
defaultProps.value = ["site", "profile", "live", "status", "translation"];
|
|
110
|
+
defaultProps.limit = { value: 5, text: "five" };
|
|
107
111
|
const onChangeMock = jest.fn() as CalledWithMock<void, [newValue: any]> & ((newValue: any) => void);
|
|
108
112
|
defaultProps.setColumns = onChangeMock;
|
|
109
113
|
|
|
@@ -120,12 +124,12 @@ describe("CustomizeFilters events", () => {
|
|
|
120
124
|
const checkFieldInputs = screen.queryAllByTestId("check-field-input");
|
|
121
125
|
fireEvent.click(checkFieldInputs[0]);
|
|
122
126
|
expect(onChangeMock).toHaveBeenCalledTimes(1);
|
|
123
|
-
expect(onChangeMock).toBeCalledWith(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
expect(onChangeMock).toBeCalledWith([
|
|
128
|
+
{ id: "live", title: "Live", show: false },
|
|
129
|
+
{ id: "profile", title: "Profile", show: true },
|
|
130
|
+
{ id: "site", title: "Site", show: true },
|
|
131
|
+
{ id: "status", title: "Status", show: true },
|
|
132
|
+
{ id: "translation", title: "Trans.", show: true },
|
|
133
|
+
]);
|
|
130
134
|
});
|
|
131
135
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { template } from "./config";
|
|
2
2
|
import { IServiceConfig, sendRequest, sendInitialRequest } from "./utils";
|
|
3
|
-
import { ICategoryGroupParams, IGetStructuredDataParams, IOrderCategoryParams } from "@ax/types";
|
|
3
|
+
import { ICategoryGroupParams, IExportDataParams, IGetStructuredDataParams, IOrderCategoryParams } from "@ax/types";
|
|
4
4
|
import { AxiosResponse } from "axios";
|
|
5
5
|
|
|
6
6
|
const SERVICES: { [key: string]: IServiceConfig } = {
|
|
@@ -114,6 +114,12 @@ const SERVICES: { [key: string]: IServiceConfig } = {
|
|
|
114
114
|
endpoint: "/categories/order/",
|
|
115
115
|
method: "PUT",
|
|
116
116
|
},
|
|
117
|
+
EXPORT_DATA_CONTENT: {
|
|
118
|
+
...template,
|
|
119
|
+
endpoint: ["/structured_data_content/", "/site/", "/export"],
|
|
120
|
+
method: "POST",
|
|
121
|
+
responseType: "blob",
|
|
122
|
+
},
|
|
117
123
|
};
|
|
118
124
|
|
|
119
125
|
const getData = (token: string | null, siteID?: number | null): Promise<AxiosResponse> => {
|
|
@@ -328,6 +334,19 @@ const orderCategory = (data: IOrderCategoryParams): Promise<AxiosResponse> => {
|
|
|
328
334
|
return sendRequest(SERVICES.ORDER_CATEGORY, { ...data });
|
|
329
335
|
};
|
|
330
336
|
|
|
337
|
+
const exportDataContent = (
|
|
338
|
+
structuredDataID: string,
|
|
339
|
+
siteID: number | "global",
|
|
340
|
+
data: IExportDataParams
|
|
341
|
+
): Promise<AxiosResponse> => {
|
|
342
|
+
const { host, endpoint } = SERVICES.EXPORT_DATA_CONTENT;
|
|
343
|
+
const [prefix, infix, suffix] = endpoint;
|
|
344
|
+
|
|
345
|
+
SERVICES.EXPORT_DATA_CONTENT.dynamicUrl = `${host}${prefix}${structuredDataID}${infix}${siteID}${suffix}`;
|
|
346
|
+
|
|
347
|
+
return sendRequest(SERVICES.EXPORT_DATA_CONTENT, { ...data });
|
|
348
|
+
};
|
|
349
|
+
|
|
331
350
|
export default {
|
|
332
351
|
getData,
|
|
333
352
|
getDataContent,
|
|
@@ -351,4 +370,5 @@ export default {
|
|
|
351
370
|
deleteGroupBulk,
|
|
352
371
|
orderCategory,
|
|
353
372
|
getGroup,
|
|
373
|
+
exportDataContent,
|
|
354
374
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { Button, CheckField, TableCounter } from "@ax/components";
|
|
3
|
+
import { Button, CheckField, ExportButton, TableCounter } from "@ax/components";
|
|
4
4
|
import { IBulkAction } from "@ax/types";
|
|
5
5
|
|
|
6
6
|
import * as S from "./style";
|
|
7
7
|
|
|
8
8
|
const BulkSelectionOptions = (props: IBulkSelectionProps): JSX.Element => {
|
|
9
|
-
const { isScrolling, checkState, actions, selectItems, totalItems, className } = props;
|
|
9
|
+
const { isScrolling, checkState, actions, selectItems, totalItems, className, exportAction } = props;
|
|
10
10
|
|
|
11
11
|
const filteredActions = actions.filter((action: IBulkAction | undefined | null) => !!action);
|
|
12
12
|
|
|
@@ -34,6 +34,7 @@ const BulkSelectionOptions = (props: IBulkSelectionProps): JSX.Element => {
|
|
|
34
34
|
</Button>
|
|
35
35
|
)
|
|
36
36
|
)}
|
|
37
|
+
{exportAction && <ExportButton onClick={exportAction} />}
|
|
37
38
|
</S.BulkActions>
|
|
38
39
|
<S.Counter>
|
|
39
40
|
<TableCounter totalItems={totalItems} />
|
|
@@ -50,6 +51,7 @@ interface IBulkSelectionProps {
|
|
|
50
51
|
totalItems: number;
|
|
51
52
|
isScrolling?: boolean;
|
|
52
53
|
className?: string;
|
|
54
|
+
exportAction?: (formats: (string | number)[]) => void;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
export default BulkSelectionOptions;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { CheckGroup, Button, FloatingMenu } from "@ax/components";
|
|
3
|
+
|
|
4
|
+
import * as S from "./style";
|
|
5
|
+
|
|
6
|
+
const ExportButton = (props: IExportButtonProps): JSX.Element => {
|
|
7
|
+
const { onClick } = props;
|
|
8
|
+
|
|
9
|
+
const [state, setState] = useState<(string | number)[]>([]);
|
|
10
|
+
|
|
11
|
+
const handleChange = (value: string | (string | number)[]) => Array.isArray(value) && setState(value);
|
|
12
|
+
|
|
13
|
+
const handleClick = () => onClick(state);
|
|
14
|
+
|
|
15
|
+
const options = [
|
|
16
|
+
{ name: "csv", value: "csv", title: ".csv" },
|
|
17
|
+
{ name: "xml", value: "xml", title: ".xml" },
|
|
18
|
+
{ name: "json", value: "json", title: ".json" },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const LinkButton = () => (
|
|
22
|
+
<S.ButtonWrapper>
|
|
23
|
+
<Button className="button" type="button" buttonStyle="text" icon="download">
|
|
24
|
+
Export Data
|
|
25
|
+
</Button>
|
|
26
|
+
</S.ButtonWrapper>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<S.Wrapper>
|
|
31
|
+
<FloatingMenu Button={LinkButton} position="left" closeOnSelect={false} offset={-25}>
|
|
32
|
+
<S.MenuWrapper>
|
|
33
|
+
<S.HelpText>Select the format(s) in which you want to export.</S.HelpText>
|
|
34
|
+
<S.ChecksWrapper>
|
|
35
|
+
<CheckGroup options={options} value={state} onChange={handleChange} multipleSelection={true} />
|
|
36
|
+
</S.ChecksWrapper>
|
|
37
|
+
<S.ExportWrapper>
|
|
38
|
+
<Button className="button" type="button" buttonStyle="line" onClick={handleClick}>
|
|
39
|
+
Export Data
|
|
40
|
+
</Button>
|
|
41
|
+
</S.ExportWrapper>
|
|
42
|
+
</S.MenuWrapper>
|
|
43
|
+
</FloatingMenu>
|
|
44
|
+
</S.Wrapper>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export interface IExportButtonProps {
|
|
49
|
+
onClick: (formats: (string | number)[]) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default ExportButton;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Wrapper = styled.div`
|
|
4
|
+
position: relative;
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
const ButtonWrapper = styled.div`
|
|
8
|
+
button {
|
|
9
|
+
margin-left: 0 !important;
|
|
10
|
+
margin-right: ${(p) => p.theme.spacing.s} !important;
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const MenuWrapper = styled.div`
|
|
15
|
+
padding: ${(p) => `${p.theme.spacing.xs} ${p.theme.spacing.s}`};
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const HelpText = styled.div`
|
|
19
|
+
${(p) => p.theme.textStyle.uiXS};
|
|
20
|
+
color: ${(p) => p.theme.colors.textHighEmphasis};
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const ChecksWrapper = styled.div`
|
|
24
|
+
margin-top: ${(p) => p.theme.spacing.xs};
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const ExportWrapper = styled.div`
|
|
28
|
+
margin-top: ${(p) => p.theme.spacing.xs};
|
|
29
|
+
button {
|
|
30
|
+
margin-left: 0 !important;
|
|
31
|
+
margin-right: 0 !important;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
export { Wrapper, ChecksWrapper, MenuWrapper, HelpText, ButtonWrapper, ExportWrapper };
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
ErrorCenter,
|
|
17
17
|
SearchField,
|
|
18
18
|
Modal,
|
|
19
|
+
ExportButton,
|
|
19
20
|
} from "@ax/components";
|
|
20
21
|
|
|
21
22
|
import { ActionMenu, ActionSimpleMenu, DownArrowButton } from "./atoms";
|
|
@@ -50,6 +51,7 @@ const AppBar = (props: IProps): JSX.Element => {
|
|
|
50
51
|
hasAnimation,
|
|
51
52
|
searchValue,
|
|
52
53
|
isDirty,
|
|
54
|
+
exportAction,
|
|
53
55
|
} = props;
|
|
54
56
|
|
|
55
57
|
const publishedTooltip: any = {
|
|
@@ -280,6 +282,7 @@ const AppBar = (props: IProps): JSX.Element => {
|
|
|
280
282
|
<S.Separator />
|
|
281
283
|
</>
|
|
282
284
|
)}
|
|
285
|
+
{exportAction && <ExportButton onClick={exportAction} />}
|
|
283
286
|
{rightLineButton && (
|
|
284
287
|
<Button
|
|
285
288
|
className="button"
|
|
@@ -344,6 +347,7 @@ export interface IAppBarProps {
|
|
|
344
347
|
hasAnimation?: boolean;
|
|
345
348
|
searchValue?: string;
|
|
346
349
|
isDirty?: boolean;
|
|
350
|
+
exportAction?(formats: (number | string)[]): void;
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
type IProps = IAppBarProps & RouteComponentProps;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { CheckGroup, FloatingMenu, Icon, ListTitle } from "@ax/components";
|
|
4
|
-
import { areEquals, isReqOk } from "@ax/helpers";
|
|
4
|
+
import { areEquals, isReqOk, trimText } from "@ax/helpers";
|
|
5
5
|
import { checkgroups } from "@ax/api";
|
|
6
6
|
import { IFilterValue, IQueryValue } from "@ax/types";
|
|
7
7
|
|
|
@@ -74,7 +74,7 @@ const CategoryFilter = (props: ICategoryFilterProps): JSX.Element => {
|
|
|
74
74
|
|
|
75
75
|
const Header = () => (
|
|
76
76
|
<S.HeaderWrapper isActive={isActive}>
|
|
77
|
-
{structuredData.title}
|
|
77
|
+
{trimText(structuredData.title, 23)}
|
|
78
78
|
<S.IconsWrapper>
|
|
79
79
|
{isActive ? <Icon name="Filter" size="16" /> : <Icon name="DownArrow" size="16" />}
|
|
80
80
|
</S.IconsWrapper>
|
|
@@ -3,7 +3,7 @@ import styled from "styled-components";
|
|
|
3
3
|
import { Header } from "@ax/components/TableList/style";
|
|
4
4
|
|
|
5
5
|
const HeaderWrapper = styled((props) => <Header {...props} />)<{ isActive: boolean }>`
|
|
6
|
-
width:
|
|
6
|
+
width: 215px;
|
|
7
7
|
justify-content: center;
|
|
8
8
|
&:hover {
|
|
9
9
|
color: ${(p) => p.theme.color.interactive01};
|
|
@@ -5,19 +5,21 @@ import { IColumn } from "@ax/types";
|
|
|
5
5
|
import * as S from "./style";
|
|
6
6
|
|
|
7
7
|
const CustomizeFilters = (props: ICustomizeFiltersProps): JSX.Element => {
|
|
8
|
-
const { columns, setColumns, value } = props;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const { columns, setColumns, value, limit } = props;
|
|
9
|
+
|
|
10
|
+
const options = columns.map((col) => {
|
|
11
|
+
const disabled = value.length >= limit.value && !value.includes(col.id);
|
|
12
|
+
return { name: col.id, title: col.title, value: col.id, disabled };
|
|
12
13
|
});
|
|
13
14
|
|
|
14
15
|
const toggleColumn = (selection: any) => {
|
|
15
|
-
const updatedColumns =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const updatedColumns: IColumn[] = [...columns];
|
|
17
|
+
columns.forEach((val) => {
|
|
18
|
+
const columIndex = updatedColumns.findIndex((col) => col.id === val.id);
|
|
19
|
+
if (selection.includes(val.id)) {
|
|
20
|
+
updatedColumns[columIndex].show = true;
|
|
19
21
|
} else {
|
|
20
|
-
updatedColumns[
|
|
22
|
+
updatedColumns[columIndex].show = false;
|
|
21
23
|
}
|
|
22
24
|
});
|
|
23
25
|
setColumns(updatedColumns);
|
|
@@ -33,7 +35,7 @@ const CustomizeFilters = (props: ICustomizeFiltersProps): JSX.Element => {
|
|
|
33
35
|
<S.Wrapper data-testid="customize-filters-wrapper">
|
|
34
36
|
<FloatingMenu Button={Button} position="right" closeOnSelect={false}>
|
|
35
37
|
<ListTitle>Customize filters</ListTitle>
|
|
36
|
-
<S.Note>You can select up to
|
|
38
|
+
<S.Note>You can select up to {limit.text} options</S.Note>
|
|
37
39
|
<S.ChecksWrapper>
|
|
38
40
|
<CheckGroup options={options} value={value} onChange={toggleColumn} />
|
|
39
41
|
</S.ChecksWrapper>
|
|
@@ -43,9 +45,10 @@ const CustomizeFilters = (props: ICustomizeFiltersProps): JSX.Element => {
|
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export interface ICustomizeFiltersProps {
|
|
46
|
-
setColumns(newValue:
|
|
47
|
-
columns:
|
|
48
|
+
setColumns(newValue: IColumn[]): void;
|
|
49
|
+
columns: IColumn[];
|
|
48
50
|
value: string[];
|
|
51
|
+
limit: { value: number; text: string };
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export default CustomizeFilters;
|
package/src/components/index.tsx
CHANGED
|
@@ -70,6 +70,7 @@ import EmptyState from "./EmptyState";
|
|
|
70
70
|
import ErrorCenter from "./ErrorCenter";
|
|
71
71
|
import ErrorPage from "./ErrorPage";
|
|
72
72
|
import ErrorToast from "./ErrorToast";
|
|
73
|
+
import ExportButton from "./ExportButton";
|
|
73
74
|
import FieldContainer from "./FieldContainer";
|
|
74
75
|
import FieldsBehavior from "./FieldsBehavior";
|
|
75
76
|
import FileGallery from "./FileGallery";
|
|
@@ -182,6 +183,7 @@ export {
|
|
|
182
183
|
ErrorCenter,
|
|
183
184
|
ErrorPage,
|
|
184
185
|
ErrorToast,
|
|
186
|
+
ExportButton,
|
|
185
187
|
FieldContainer,
|
|
186
188
|
FieldsBehavior,
|
|
187
189
|
FileGallery,
|
|
@@ -54,11 +54,12 @@ import {
|
|
|
54
54
|
ICategoryGroupParams,
|
|
55
55
|
IRootState,
|
|
56
56
|
IOrderCategoryParams,
|
|
57
|
+
IExportDataParams,
|
|
57
58
|
} from "@ax/types";
|
|
58
59
|
import { structuredData } from "@ax/api";
|
|
59
60
|
import { setTotalItems } from "@ax/containers/Sites/actions";
|
|
60
61
|
import { appActions } from "@ax/containers/App";
|
|
61
|
-
import { deepClone, handleRequest, isEmptyArray } from "@ax/helpers";
|
|
62
|
+
import { dateToString, deepClone, handleRequest, isEmptyArray } from "@ax/helpers";
|
|
62
63
|
import { findMandatoryStructuredDataErrors } from "@ax/forms";
|
|
63
64
|
|
|
64
65
|
const { setIsLoading, setIsSaving } = appActions;
|
|
@@ -761,6 +762,46 @@ function updateCurrentSearch(query: string): (dispatch: Dispatch) => Promise<voi
|
|
|
761
762
|
};
|
|
762
763
|
}
|
|
763
764
|
|
|
765
|
+
function exportDataContent(
|
|
766
|
+
structuredDataID: string,
|
|
767
|
+
data: IExportDataParams
|
|
768
|
+
): (dispatch: Dispatch, getState: any) => Promise<void> {
|
|
769
|
+
return async (dispatch, getState) => {
|
|
770
|
+
try {
|
|
771
|
+
const {
|
|
772
|
+
sites: { currentSiteInfo },
|
|
773
|
+
}: IRootState = getState();
|
|
774
|
+
|
|
775
|
+
const siteID = currentSiteInfo ? currentSiteInfo.id : "global";
|
|
776
|
+
const date = new Date();
|
|
777
|
+
const dateString = dateToString(date, "yyyy-MM-dd");
|
|
778
|
+
const fileName =
|
|
779
|
+
data.format.length === 1
|
|
780
|
+
? `${structuredDataID}-${dateString}.${data.format[0]}`
|
|
781
|
+
: `${structuredDataID}-${dateString}.zip`;
|
|
782
|
+
|
|
783
|
+
const responseActions = {
|
|
784
|
+
handleSuccess: (response: any) => {
|
|
785
|
+
const url = window.URL.createObjectURL(new Blob([response]));
|
|
786
|
+
const a = document.createElement("a");
|
|
787
|
+
a.href = url;
|
|
788
|
+
a.download = fileName;
|
|
789
|
+
document.body.appendChild(a);
|
|
790
|
+
a.click();
|
|
791
|
+
window.URL.revokeObjectURL(url);
|
|
792
|
+
},
|
|
793
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
const callback = async () => structuredData.exportDataContent(structuredDataID, siteID, data);
|
|
797
|
+
|
|
798
|
+
await handleRequest(callback, responseActions, [])(dispatch);
|
|
799
|
+
} catch (e) {
|
|
800
|
+
console.log(e);
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
764
805
|
export {
|
|
765
806
|
setIsActive,
|
|
766
807
|
setCategories,
|
|
@@ -799,4 +840,5 @@ export {
|
|
|
799
840
|
deleteCategoryGroup,
|
|
800
841
|
orderCategory,
|
|
801
842
|
updateCurrentSearch,
|
|
843
|
+
exportDataContent,
|
|
802
844
|
};
|
package/src/helpers/arrays.tsx
CHANGED
|
@@ -10,4 +10,13 @@ const moveArrayElement = (element: any, arr: any[], newIndex: number): Array<any
|
|
|
10
10
|
return arrCopy;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const arrayInsert = (arr: any[], index: number, newItem: any) => [
|
|
14
|
+
// part of the array before the specified index
|
|
15
|
+
...arr.slice(0, index),
|
|
16
|
+
// inserted item
|
|
17
|
+
...newItem,
|
|
18
|
+
// part of the array after the specified index
|
|
19
|
+
...arr.slice(index),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export { isEmptyArray, moveArrayElement, arrayInsert };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { IColumn } from "@ax/types";
|
|
2
|
+
|
|
3
|
+
const getMaxColumns = (width: number, isSimple: boolean): { value: number; text: string } => {
|
|
4
|
+
let maxColumns = 5;
|
|
5
|
+
if (width >= 1680) {
|
|
6
|
+
maxColumns = 6;
|
|
7
|
+
}
|
|
8
|
+
if (width > 1920) {
|
|
9
|
+
maxColumns = 7;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (isSimple) {
|
|
13
|
+
maxColumns = maxColumns - 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
switch (maxColumns) {
|
|
17
|
+
case 4:
|
|
18
|
+
return { value: 4, text: "four" };
|
|
19
|
+
case 6:
|
|
20
|
+
return { value: 6, text: "six" };
|
|
21
|
+
case 7:
|
|
22
|
+
return { value: 7, text: "seven" };
|
|
23
|
+
default:
|
|
24
|
+
return { value: 5, text: "five" };
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const updateColumns = (columnsState: IColumn[], maxColumns: number): IColumn[] => {
|
|
29
|
+
if (!columnsState) return [];
|
|
30
|
+
const activeColumns = columnsState.filter((col) => col.show).length;
|
|
31
|
+
let availableColumns = maxColumns - activeColumns;
|
|
32
|
+
|
|
33
|
+
return columnsState.map((col) => {
|
|
34
|
+
if (!col.show && availableColumns > 0) {
|
|
35
|
+
col.show = true;
|
|
36
|
+
availableColumns = availableColumns - 1;
|
|
37
|
+
}
|
|
38
|
+
if (col.show && !col.default && availableColumns < 0) {
|
|
39
|
+
col.show = false;
|
|
40
|
+
}
|
|
41
|
+
return col;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { getMaxColumns, updateColumns };
|
package/src/helpers/index.tsx
CHANGED
|
@@ -94,7 +94,7 @@ import {
|
|
|
94
94
|
|
|
95
95
|
import { imageResizeCropAndCompress, compressImage } from "./imageResize";
|
|
96
96
|
|
|
97
|
-
import { isEmptyArray, moveArrayElement } from "./arrays";
|
|
97
|
+
import { isEmptyArray, moveArrayElement, arrayInsert } from "./arrays";
|
|
98
98
|
|
|
99
99
|
import { getActivatedDataPacksIds, isModuleDisabled, getDeactivatedModules } from "./dataPacks";
|
|
100
100
|
|
|
@@ -113,6 +113,8 @@ import { parseTheme } from "./parseTheme";
|
|
|
113
113
|
|
|
114
114
|
import { getFileIcon } from "./files";
|
|
115
115
|
|
|
116
|
+
import { getMaxColumns, updateColumns } from "./customColumns";
|
|
117
|
+
|
|
116
118
|
export {
|
|
117
119
|
isComponentEmpty,
|
|
118
120
|
setAsContainedComponent,
|
|
@@ -204,4 +206,7 @@ export {
|
|
|
204
206
|
stripHtml,
|
|
205
207
|
encodeData,
|
|
206
208
|
getFileIcon,
|
|
209
|
+
arrayInsert,
|
|
210
|
+
getMaxColumns,
|
|
211
|
+
updateColumns,
|
|
207
212
|
};
|