@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.
Files changed (36) hide show
  1. package/package.json +3 -2
  2. package/src/__tests__/components/TableFilters/CustomizeFilters/CustomizeFilters.test.tsx +39 -35
  3. package/src/api/structuredData.tsx +21 -1
  4. package/src/components/BulkSelectionOptions/index.tsx +4 -2
  5. package/src/components/CategoryCell/style.tsx +1 -1
  6. package/src/components/ExportButton/index.tsx +52 -0
  7. package/src/components/ExportButton/style.tsx +36 -0
  8. package/src/components/MainWrapper/AppBar/index.tsx +4 -0
  9. package/src/components/MainWrapper/index.tsx +1 -0
  10. package/src/components/TableFilters/CategoryFilter/index.tsx +2 -2
  11. package/src/components/TableFilters/CategoryFilter/style.tsx +1 -1
  12. package/src/components/TableFilters/CustomizeFilters/index.tsx +15 -12
  13. package/src/components/index.tsx +2 -0
  14. package/src/containers/StructuredData/actions.tsx +43 -1
  15. package/src/helpers/arrays.tsx +10 -1
  16. package/src/helpers/customColumns.tsx +45 -0
  17. package/src/helpers/index.tsx +6 -1
  18. package/src/modules/Content/BulkHeader/TableHeader/index.tsx +11 -27
  19. package/src/modules/Content/BulkHeader/TableHeader/style.tsx +4 -2
  20. package/src/modules/Content/BulkHeader/index.tsx +22 -14
  21. package/src/modules/Content/PageItem/index.tsx +4 -3
  22. package/src/modules/Content/PageItem/style.tsx +6 -3
  23. package/src/modules/Content/index.tsx +43 -24
  24. package/src/modules/Content/utils.tsx +36 -3
  25. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +11 -8
  26. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/style.tsx +8 -7
  27. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +30 -16
  28. package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +4 -6
  29. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -7
  30. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +3 -2
  31. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +27 -18
  32. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/style.tsx +15 -5
  33. package/src/modules/StructuredData/StructuredDataList/hooks.tsx +11 -11
  34. package/src/modules/StructuredData/StructuredDataList/index.tsx +58 -47
  35. package/src/modules/StructuredData/StructuredDataList/utils.tsx +52 -1
  36. 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",
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": "c611871f53ec5d5bc4b97e6d215b9988fd2d6f5b"
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
- live: { title: "Live", show: true },
20
- profile: { title: "Profile", show: true },
21
- site: { title: "Site", show: true },
22
- status: { title: "Status", show: true },
23
- translation: { title: "Trans.", show: true },
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
- live: { title: "Live", show: true },
43
- profile: { title: "Profile", show: true },
44
- site: { title: "Site", show: true },
45
- status: { title: "Status", show: true },
46
- translation: { title: "Trans.", show: true },
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
- live: { title: "Live", show: true },
71
- profile: { title: "Profile", show: true },
72
- site: { title: "Site", show: true },
73
- status: { title: "Status", show: true },
74
- translation: { title: "Trans.", show: true },
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
- live: { title: "Live", show: true },
101
- profile: { title: "Profile", show: true },
102
- site: { title: "Site", show: true },
103
- status: { title: "Status", show: true },
104
- translation: { title: "Trans.", show: true },
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
- live: { title: "Live", show: false },
125
- profile: { title: "Profile", show: true },
126
- site: { title: "Site", show: true },
127
- status: { title: "Status", show: true },
128
- translation: { title: "Trans.", show: true },
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;
@@ -4,7 +4,7 @@ import styled from "styled-components";
4
4
  import { Cell } from "@ax/components/TableList/TableItem/style";
5
5
 
6
6
  const CategoryCell = styled((props) => <Cell {...props} />)`
7
- flex: 0 0 150px;
7
+ flex: 0 0 215px;
8
8
  align-items: center;
9
9
  position: relative;
10
10
  `;
@@ -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;
@@ -56,6 +56,7 @@ export interface IWrapperProps {
56
56
  hasAnimation?: boolean;
57
57
  searchValue?: string;
58
58
  isDirty?: boolean;
59
+ exportAction?(formats: (number | string)[]): void;
59
60
  }
60
61
 
61
62
  export default MainWrapper;
@@ -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: 150px;
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
- const options = Object.keys(columns).map((col: string) => {
10
- const disabled = value.length >= 5 && !value.includes(col);
11
- return { name: col, title: columns[col].title, value: col, disabled };
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 = { ...columns };
16
- Object.keys(columns).forEach((val: string, index: number) => {
17
- if (selection.includes(val)) {
18
- updatedColumns[val].show = true;
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[val].show = false;
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 five options</S.Note>
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: any): void;
47
- columns: Record<string, IColumn>;
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;
@@ -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
  };
@@ -10,4 +10,13 @@ const moveArrayElement = (element: any, arr: any[], newIndex: number): Array<any
10
10
  return arrCopy;
11
11
  };
12
12
 
13
- export { isEmptyArray, moveArrayElement };
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 };
@@ -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
  };