@griddo/ax 1.75.258 → 10.1.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 (89) hide show
  1. package/package.json +2 -2
  2. package/src/GlobalStore.tsx +3 -0
  3. package/src/__tests__/components/ConfigPanel/GlobalPageForm/GlobalPageForm.test.tsx +10 -1
  4. package/src/__tests__/components/Fields/IntegrationsField/IntegrationsField.test.tsx +391 -0
  5. package/src/__tests__/modules/Settings/Integrations/Integrations.test.tsx +167 -0
  6. package/src/api/index.tsx +3 -1
  7. package/src/api/integrations.tsx +153 -0
  8. package/src/api/sites.tsx +4 -2
  9. package/src/components/ActionMenu/index.tsx +3 -1
  10. package/src/components/ActionMenu/style.tsx +1 -0
  11. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +4 -1
  12. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +3 -0
  13. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +11 -2
  14. package/src/components/FieldContainer/index.tsx +2 -1
  15. package/src/components/Fields/IntegrationsField/IntegrationItem/CustomPanel/index.tsx +101 -0
  16. package/src/components/Fields/IntegrationsField/IntegrationItem/CustomPanel/style.tsx +23 -0
  17. package/src/components/Fields/IntegrationsField/IntegrationItem/VariablesPanel/helpers.ts +31 -0
  18. package/src/components/Fields/IntegrationsField/IntegrationItem/VariablesPanel/index.tsx +120 -0
  19. package/src/components/Fields/IntegrationsField/IntegrationItem/VariablesPanel/style.tsx +23 -0
  20. package/src/components/Fields/IntegrationsField/IntegrationItem/atoms.tsx +27 -0
  21. package/src/components/Fields/IntegrationsField/IntegrationItem/index.tsx +132 -0
  22. package/src/components/Fields/IntegrationsField/IntegrationItem/style.tsx +63 -0
  23. package/src/components/Fields/IntegrationsField/PasteIntegrationButton/index.tsx +30 -0
  24. package/src/components/Fields/IntegrationsField/SideModal/SideModalOption/index.tsx +52 -0
  25. package/src/components/Fields/IntegrationsField/SideModal/SideModalOption/style.tsx +54 -0
  26. package/src/components/Fields/IntegrationsField/SideModal/index.tsx +57 -0
  27. package/src/components/Fields/IntegrationsField/SideModal/style.tsx +50 -0
  28. package/src/components/Fields/IntegrationsField/index.tsx +145 -0
  29. package/src/components/Fields/IntegrationsField/style.tsx +29 -0
  30. package/src/components/Fields/TextArea/index.tsx +1 -1
  31. package/src/components/Fields/ToggleField/index.tsx +5 -3
  32. package/src/components/Fields/UrlField/index.tsx +8 -8
  33. package/src/components/Fields/index.tsx +2 -0
  34. package/src/components/Icon/components/Deactivate.js +14 -0
  35. package/src/components/Icon/components/Lock.js +15 -0
  36. package/src/components/Icon/svgs/Deactivate.svg +8 -0
  37. package/src/components/Icon/svgs/Lock.svg +6 -0
  38. package/src/components/PageFinder/SelectionListItem/index.tsx +46 -0
  39. package/src/components/PageFinder/SelectionListItem/style.tsx +46 -0
  40. package/src/components/{Fields/UrlField/PageFinder → PageFinder}/index.tsx +99 -21
  41. package/src/components/{Fields/UrlField/PageFinder → PageFinder}/style.tsx +14 -1
  42. package/src/components/TableFilters/CheckGroupFilter/index.tsx +83 -0
  43. package/src/components/TableFilters/CheckGroupFilter/style.tsx +40 -0
  44. package/src/components/TableFilters/StateFilter/index.tsx +66 -0
  45. package/src/components/TableFilters/StateFilter/style.tsx +30 -0
  46. package/src/components/TableFilters/index.tsx +4 -0
  47. package/src/components/index.tsx +9 -1
  48. package/src/containers/Integrations/actions.tsx +190 -0
  49. package/src/containers/Integrations/constants.tsx +14 -0
  50. package/src/containers/Integrations/index.tsx +4 -0
  51. package/src/containers/Integrations/interfaces.tsx +24 -0
  52. package/src/containers/Integrations/reducer.tsx +31 -0
  53. package/src/containers/PageEditor/actions.tsx +11 -1
  54. package/src/containers/PageEditor/utils.tsx +30 -2
  55. package/src/hooks/content.tsx +46 -2
  56. package/src/hooks/index.tsx +2 -1
  57. package/src/hooks/modals.tsx +4 -2
  58. package/src/modules/App/Routing/NavMenu/NavItem/style.tsx +13 -12
  59. package/src/modules/Content/index.tsx +5 -0
  60. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/IntegrationsField/index.tsx +47 -0
  61. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/IntegrationsField/style.tsx +7 -0
  62. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/{Field → NavigationField}/index.tsx +2 -2
  63. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/index.tsx +7 -7
  64. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +1 -1
  65. package/src/modules/Settings/Integrations/BulkHeader/TableHeader/index.tsx +81 -0
  66. package/src/modules/Settings/Integrations/BulkHeader/TableHeader/style.tsx +35 -0
  67. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +69 -0
  68. package/src/modules/Settings/Integrations/IntegrationForm/VariableItem/index.tsx +95 -0
  69. package/src/modules/Settings/Integrations/IntegrationForm/VariableItem/style.tsx +62 -0
  70. package/src/modules/Settings/Integrations/IntegrationForm/VariablePanel/index.tsx +138 -0
  71. package/src/modules/Settings/Integrations/IntegrationForm/VariablePanel/style.tsx +28 -0
  72. package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +319 -0
  73. package/src/modules/Settings/Integrations/IntegrationForm/style.tsx +77 -0
  74. package/src/modules/Settings/Integrations/IntegrationItem/CopyModal/index.tsx +44 -0
  75. package/src/modules/Settings/Integrations/IntegrationItem/CopyModal/style.tsx +13 -0
  76. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +197 -0
  77. package/src/modules/Settings/Integrations/IntegrationItem/style.tsx +81 -0
  78. package/src/modules/Settings/Integrations/atoms.tsx +49 -0
  79. package/src/modules/Settings/Integrations/hooks.tsx +72 -0
  80. package/src/modules/Settings/Integrations/index.tsx +299 -0
  81. package/src/modules/Settings/Integrations/style.tsx +48 -0
  82. package/src/modules/Settings/Integrations/utils.tsx +39 -0
  83. package/src/routes/site.tsx +19 -0
  84. package/src/schemas/pages/Page.tsx +5 -0
  85. package/src/types/index.tsx +35 -0
  86. package/tsconfig.paths.json +2 -0
  87. package/src/components/Fields/UrlField/PageFinder/SelectionListItem/index.tsx +0 -34
  88. package/src/components/Fields/UrlField/PageFinder/SelectionListItem/style.tsx +0 -35
  89. /package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/{Field → NavigationField}/style.tsx +0 -0
@@ -0,0 +1,81 @@
1
+ import styled from "styled-components";
2
+
3
+ import { Cell, Row } from "@ax/components/TableList/TableItem/style";
4
+ import { ActionMenu } from "@ax/components";
5
+
6
+ const CheckCell = styled(Cell)`
7
+ padding-left: ${(p) => p.theme.spacing.m};
8
+ width: 32px;
9
+ label {
10
+ margin-bottom: ${(p) => p.theme.spacing.s};
11
+ }
12
+ `;
13
+
14
+ const NameCell = styled(Cell)`
15
+ ${(p) => p.theme.textStyle.uiM};
16
+ color: ${(p) => p.theme.color.textHighEmphasis};
17
+ width: 200px;
18
+ `;
19
+
20
+ const DescriptionCell = styled(Cell)`
21
+ ${(p) => p.theme.textStyle.uiXS};
22
+ color: ${(p) => p.theme.color.textMediumEmphasis};
23
+ flex: 1;
24
+ `;
25
+
26
+ const AppliedOnCell = styled(Cell)`
27
+ ${(p) => p.theme.textStyle.uiXS};
28
+ color: ${(p) => p.theme.color.textMediumEmphasis};
29
+ flex: 1;
30
+ display: inline;
31
+ align-self: center;
32
+ `;
33
+
34
+ const ContentPresence = styled.span`
35
+ ${(p) => p.theme.textStyle.uiXS};
36
+ color: ${(p) => p.theme.color.textHighEmphasis};
37
+ `;
38
+
39
+ const StateCell = styled(Cell)`
40
+ width: 170px;
41
+ `;
42
+
43
+ const ActionsCell = styled(Cell)`
44
+ width: 125px;
45
+ `;
46
+
47
+ const ModalContent = styled.div`
48
+ padding: ${(p) => p.theme.spacing.m};
49
+
50
+ p {
51
+ margin-bottom: ${(p) => p.theme.spacing.m};
52
+ }
53
+ `;
54
+
55
+ const StyledActionMenu = styled(ActionMenu)`
56
+ opacity: 0;
57
+ width: 32px;
58
+ display: flex;
59
+ margin-left: auto;
60
+ `;
61
+
62
+ const ItemRow = styled(Row)`
63
+ &:hover {
64
+ ${StyledActionMenu} {
65
+ opacity: 1;
66
+ }
67
+ }
68
+ `;
69
+
70
+ export {
71
+ CheckCell,
72
+ NameCell,
73
+ DescriptionCell,
74
+ AppliedOnCell,
75
+ StateCell,
76
+ ActionsCell,
77
+ ItemRow,
78
+ ContentPresence,
79
+ ModalContent,
80
+ StyledActionMenu,
81
+ };
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+
3
+ import { IIntegration, IModal } from "@ax/types";
4
+ import { Modal } from "@ax/components";
5
+
6
+ import { getIntegrationsNames } from "./utils";
7
+ import * as S from "./style";
8
+
9
+ const DeleteModal = (props: IModal & { integrations: IIntegration[]; selectedIds: number[] }): JSX.Element => {
10
+ const { isOpen, toggleModal, mainModalAction, secondaryModalAction, integrations, selectedIds } = props;
11
+
12
+ return (
13
+ <Modal
14
+ isOpen={isOpen}
15
+ hide={toggleModal}
16
+ title="Delete Integrations"
17
+ secondaryAction={secondaryModalAction}
18
+ mainAction={mainModalAction}
19
+ size="S"
20
+ >
21
+ <S.ModalContent>
22
+ Are you sure you want to delete {getIntegrationsNames(integrations, selectedIds)}? This action{" "}
23
+ <strong>cannot be undone</strong>.
24
+ </S.ModalContent>
25
+ </Modal>
26
+ );
27
+ };
28
+
29
+ const DeactivateModal = (props: IModal & { integrations: IIntegration[]; selectedIds: number[] }): JSX.Element => {
30
+ const { isOpen, toggleModal, mainModalAction, secondaryModalAction, integrations, selectedIds } = props;
31
+
32
+ return (
33
+ <Modal
34
+ isOpen={isOpen}
35
+ hide={toggleModal}
36
+ title="Deactivate Integrations"
37
+ secondaryAction={secondaryModalAction}
38
+ mainAction={mainModalAction}
39
+ size="S"
40
+ >
41
+ <S.ModalContent>
42
+ Are you sure you want to disable {getIntegrationsNames(integrations, selectedIds)}? These integrations will stop
43
+ working in the pages previously added.
44
+ </S.ModalContent>
45
+ </Modal>
46
+ );
47
+ };
48
+
49
+ export { DeleteModal, DeactivateModal };
@@ -0,0 +1,72 @@
1
+ import { useState } from "react";
2
+
3
+ const useSortedListStatus = () => {
4
+ const sortedInitialState: { isAscending: boolean; sortedByTitle: boolean } = {
5
+ isAscending: false,
6
+ sortedByTitle: false,
7
+ };
8
+
9
+ const [sortedListStatus, setSortedListStatus] = useState(sortedInitialState);
10
+
11
+ return {
12
+ sortedListStatus,
13
+ setSortedListStatus,
14
+ };
15
+ };
16
+
17
+ const useFilterQuery = () => {
18
+ const initialQueryValues = {
19
+ order: "",
20
+ filterApplication: "all",
21
+ filterState: "all",
22
+ };
23
+
24
+ const [query, setQuery] = useState(initialQueryValues);
25
+
26
+ const setFilterQuery = (filterValues: any) => {
27
+ const { order, filterApplication, filterState } = filterValues;
28
+ let filterQuery = "";
29
+
30
+ const currentQuery = (pointer: string, values: string) => {
31
+ return filterQuery.concat(`&${pointer}=${values}`);
32
+ };
33
+
34
+ const isNotInitialValue = (pointer: "order" | "filterApplication" | "filterState") => {
35
+ return filterValues[pointer] && initialQueryValues[pointer] !== filterValues[pointer];
36
+ };
37
+
38
+ if (isNotInitialValue("order")) filterQuery = currentQuery("order", order);
39
+ if (isNotInitialValue("filterApplication")) filterQuery = currentQuery("filterApplication", filterApplication);
40
+ if (isNotInitialValue("filterState")) filterQuery = currentQuery("filterState", filterState);
41
+
42
+ return filterQuery;
43
+ };
44
+
45
+ const setFiltersSelection = (pointer: string, filter: string, isAscendent?: boolean) => {
46
+ const { order, filterApplication, filterState } = query;
47
+ const orderMethod = isAscendent ? "asc" : "desc";
48
+ const filterValues = {
49
+ order: pointer === "order" ? `${filter}-${orderMethod}` : order,
50
+ filterApplication: pointer === "filterApplication" ? filter : filterApplication,
51
+ filterState: pointer === "filterState" ? filter : filterState,
52
+ };
53
+
54
+ setQuery(filterValues);
55
+
56
+ return filterValues;
57
+ };
58
+
59
+ const resetFilterQuery = () => setQuery(initialQueryValues);
60
+
61
+ const isFiltered = Object.keys(query).some((key: any) => query[key as never] !== initialQueryValues[key as never]);
62
+
63
+ return {
64
+ setFiltersSelection,
65
+ setFilterQuery,
66
+ resetFilterQuery,
67
+ filterValues: query,
68
+ isFiltered,
69
+ };
70
+ };
71
+
72
+ export { useSortedListStatus, useFilterQuery };
@@ -0,0 +1,299 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import { IEmptyStateProps, IIntegration, IRootState } from "@ax/types";
5
+ import { MainWrapper, ErrorToast, TableList, EmptyState, Toast } from "@ax/components";
6
+ import { integrationsActions } from "@ax/containers/Integrations";
7
+ import { appActions } from "@ax/containers/App";
8
+ import { useBulkSelection, useEmptyState, useModal, useToast } from "@ax/hooks";
9
+
10
+ import BulkHeader from "./BulkHeader";
11
+ import IntegrationItem from "./IntegrationItem";
12
+ import { useFilterQuery, useSortedListStatus } from "./hooks";
13
+ import { getSortedListStatus } from "./utils";
14
+ import { DeactivateModal, DeleteModal } from "./atoms";
15
+ import * as S from "./style";
16
+
17
+ const itemsPerPage = 50;
18
+ const firstPage = 1;
19
+
20
+ const Integrations = (props: IIntegrationsProps): JSX.Element => {
21
+ const {
22
+ isLoading,
23
+ getIntegrations,
24
+ integrations,
25
+ totalItems,
26
+ currentSite,
27
+ resetIntegrations,
28
+ deleteIntegration,
29
+ changeIntegrationState,
30
+ setHistoryPush,
31
+ setCurrentIntegration,
32
+ } = props;
33
+
34
+ const [page, setPage] = useState(firstPage);
35
+ const [isScrolling, setIsScrolling] = useState(false);
36
+ const tableRef = useRef<HTMLDivElement>(null);
37
+ const [currentFilterQuery, setCurrentFilterQuery] = useState("");
38
+ const { setFiltersSelection, setFilterQuery, filterValues, isFiltered } = useFilterQuery();
39
+ const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
40
+ const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
41
+ const { isOpen: isOpenDeactivate, toggleModal: toggleModalDeactivate } = useModal();
42
+ const {
43
+ isVisible: isVisibleDelete,
44
+ toggleToast: toggleToastDelete,
45
+ setIsVisible: setIsVisibleDelete,
46
+ state: stateToastDelete,
47
+ } = useToast();
48
+ const {
49
+ isVisible: isVisibleChange,
50
+ toggleToast: toggleToastChange,
51
+ setIsVisible: setIsVisibleChange,
52
+ state: stateToastChange,
53
+ } = useToast();
54
+
55
+ const noElementsProps: IEmptyStateProps = {
56
+ message: "To start using third-party integrations in your site, create as many Custom Code as you need.",
57
+ button: "Create custom code",
58
+ action: () => setHistoryPush(`/sites/settings/integrations/new`),
59
+ };
60
+ const fetchState = { isLoading, isFiltered };
61
+ const { isEmpty, emptyStateProps } = useEmptyState(integrations, fetchState, { noElementsProps });
62
+
63
+ const integrationsIds = integrations && integrations.map((integration: IIntegration) => integration.id);
64
+
65
+ const getParams = useCallback(() => {
66
+ const params = {
67
+ page,
68
+ itemsPerPage,
69
+ pagination: true,
70
+ filter: currentFilterQuery,
71
+ };
72
+
73
+ return params;
74
+ }, [page, currentFilterQuery]);
75
+
76
+ useEffect(() => {
77
+ const params = getParams();
78
+ getIntegrations(currentSite, params);
79
+ setCurrentIntegration(null);
80
+
81
+ return () => {
82
+ resetIntegrations();
83
+ };
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ }, [page, currentSite, currentFilterQuery]);
86
+
87
+ const {
88
+ resetBulkSelection,
89
+ selectedItems,
90
+ isSelected,
91
+ areItemsSelected,
92
+ checkState,
93
+ addToBulkSelection,
94
+ selectAllItems,
95
+ } = useBulkSelection(integrationsIds);
96
+
97
+ const bulkDelete = async () => {
98
+ const params = getParams();
99
+ const deleted = await deleteIntegration(selectedItems.all, params);
100
+ if (deleted) {
101
+ toggleToastDelete({ total: selectedItems.all.length });
102
+ }
103
+ toggleModalDelete();
104
+ resetBulkSelection();
105
+ };
106
+
107
+ const bulkDeactivate = async () => {
108
+ const params = getParams();
109
+ const changed = await changeIntegrationState(selectedItems.all, false, params);
110
+ if (changed) {
111
+ toggleToastChange({ total: selectedItems.all.length, active: false });
112
+ }
113
+ toggleModalDeactivate();
114
+ resetBulkSelection();
115
+ };
116
+
117
+ const unselectAllItems = () => resetBulkSelection();
118
+
119
+ const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
120
+
121
+ const handleSelectAll = () => selectAllItems();
122
+
123
+ const sortItems = async (orderPointer: string, isAscending: boolean) => {
124
+ setPage(firstPage);
125
+ const sortedState = getSortedListStatus(orderPointer, isAscending);
126
+ setSortedListStatus(sortedState);
127
+
128
+ const filtersSelection = setFiltersSelection("order", orderPointer, isAscending);
129
+ const filterQuery = setFilterQuery(filtersSelection);
130
+ setCurrentFilterQuery(filterQuery);
131
+ };
132
+
133
+ const filterItems = async (filterPointer: string, filtersSelected: string) => {
134
+ setPage(firstPage);
135
+ const filtersSelection = setFiltersSelection(filterPointer, filtersSelected);
136
+ const filterQuery = setFilterQuery(filtersSelection);
137
+ setCurrentFilterQuery(filterQuery);
138
+ };
139
+
140
+ const TableHeader = (
141
+ <BulkHeader
142
+ showBulk={areItemsSelected(integrationsIds)}
143
+ bulkDelete={toggleModalDelete}
144
+ bulkDeactivate={toggleModalDeactivate}
145
+ selectAllItems={handleSelectAll}
146
+ totalItems={totalItems}
147
+ selectItems={selectItems}
148
+ checkState={checkState}
149
+ isScrolling={isScrolling}
150
+ filterItems={filterItems}
151
+ filterValues={filterValues}
152
+ sortItems={sortItems}
153
+ sortedListStatus={sortedListStatus}
154
+ />
155
+ );
156
+
157
+ const onScroll = (e: any) => setIsScrolling(e.target.scrollTop > 0);
158
+
159
+ const pagination = {
160
+ setPage,
161
+ itemsPerPage,
162
+ totalItems,
163
+ currPage: page,
164
+ };
165
+
166
+ const mainDeleteModalAction = {
167
+ title: "Delete integrations",
168
+ onClick: bulkDelete,
169
+ };
170
+ const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };
171
+
172
+ const mainDeactivateModalAction = {
173
+ title: "Deactivate integrations",
174
+ onClick: bulkDeactivate,
175
+ };
176
+ const secondaryDeactivateModalAction = { title: "Cancel", onClick: toggleModalDeactivate };
177
+
178
+ const deletedToastProps = {
179
+ setIsVisible: setIsVisibleDelete,
180
+ message: stateToastDelete?.total > 1 ? `${stateToastDelete?.total} integrations deleted` : "Integration deleted",
181
+ };
182
+
183
+ const changedToastProps = {
184
+ setIsVisible: setIsVisibleChange,
185
+ message: `${stateToastChange?.total} integration${stateToastChange?.total > 1 ? "s" : ""} ${
186
+ stateToastChange?.active ? "enabled" : "disabled"
187
+ }`,
188
+ };
189
+
190
+ const rightButtonProps = {
191
+ label: "New Custom Code",
192
+ action: () => setHistoryPush(`/sites/settings/integrations/new`),
193
+ };
194
+
195
+ return (
196
+ <>
197
+ <MainWrapper backLink={false} title="Third-Party Integration" rightButton={rightButtonProps}>
198
+ <S.Wrapper data-testid="integrations-main-wrapper">
199
+ <S.ContentWrapper>
200
+ <ErrorToast />
201
+ <S.TitleWrapper>
202
+ <S.Title>Custom Code</S.Title>
203
+ <S.Description>Add custom code to the head or body of your site&apos;s pages.</S.Description>
204
+ </S.TitleWrapper>
205
+ <S.TableWrapper>
206
+ <TableList
207
+ tableHeader={TableHeader}
208
+ pagination={pagination}
209
+ onScroll={onScroll}
210
+ hasFixedHeader={true}
211
+ tableRef={tableRef}
212
+ >
213
+ {isEmpty ? (
214
+ <S.EmptyWrapper>
215
+ <EmptyState {...emptyStateProps} />
216
+ </S.EmptyWrapper>
217
+ ) : (
218
+ integrations.map((integration: IIntegration) => {
219
+ const isItemSelected = isSelected(integration.id);
220
+ return (
221
+ <IntegrationItem
222
+ key={`${integration.name}${integration.id}`}
223
+ integration={integration}
224
+ isSelected={isItemSelected}
225
+ onChange={addToBulkSelection}
226
+ toggleToastDelete={toggleToastDelete}
227
+ getParams={getParams}
228
+ deleteIntegration={deleteIntegration}
229
+ changeState={changeIntegrationState}
230
+ toggleToastChange={toggleToastChange}
231
+ />
232
+ );
233
+ })
234
+ )}
235
+ </TableList>
236
+ </S.TableWrapper>
237
+ {isVisibleDelete && <Toast {...deletedToastProps} />}
238
+ {isVisibleChange && <Toast {...changedToastProps} />}
239
+ </S.ContentWrapper>
240
+ </S.Wrapper>
241
+ <DeleteModal
242
+ isOpen={isOpenDelete}
243
+ toggleModal={toggleModalDelete}
244
+ secondaryModalAction={secondaryDeleteModalAction}
245
+ mainModalAction={mainDeleteModalAction}
246
+ integrations={integrations}
247
+ selectedIds={selectedItems.all}
248
+ />
249
+ <DeactivateModal
250
+ isOpen={isOpenDeactivate}
251
+ toggleModal={toggleModalDeactivate}
252
+ secondaryModalAction={secondaryDeactivateModalAction}
253
+ mainModalAction={mainDeactivateModalAction}
254
+ integrations={integrations}
255
+ selectedIds={selectedItems.all}
256
+ />
257
+ </MainWrapper>
258
+ </>
259
+ );
260
+ };
261
+
262
+ const mapStateToProps = (state: IRootState) => ({
263
+ isLoading: state.app.isLoading,
264
+ integrations: state.integrations.integrations,
265
+ totalItems: state.integrations.totalItems,
266
+ currentSite: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
267
+ });
268
+
269
+ const mapDispatchToProps = {
270
+ getIntegrations: integrationsActions.getIntegrations,
271
+ resetIntegrations: integrationsActions.resetIntegrations,
272
+ deleteIntegration: integrationsActions.deleteIntegration,
273
+ changeIntegrationState: integrationsActions.changeIntegrationState,
274
+ setHistoryPush: appActions.setHistoryPush,
275
+ setCurrentIntegration: integrationsActions.setCurrentIntegration,
276
+ };
277
+
278
+ interface IStateProps {
279
+ isLoading: boolean;
280
+ integrations: IIntegration[];
281
+ totalItems: number;
282
+ currentSite: number;
283
+ }
284
+
285
+ interface IDispatchProps {
286
+ getIntegrations: (site: number, params: any) => Promise<void>;
287
+ resetIntegrations: () => void;
288
+ deleteIntegration: (
289
+ integrationId: number | number[],
290
+ currentParams: { page: number; itemsPerPage: number; pagination: boolean; filter: any }
291
+ ) => Promise<boolean>;
292
+ changeIntegrationState: (integrationId: number | number[], active: boolean, currentParams?: any) => Promise<boolean>;
293
+ setHistoryPush: (path: string) => void;
294
+ setCurrentIntegration: (integration: IIntegration | null) => void;
295
+ }
296
+
297
+ export type IIntegrationsProps = IStateProps & IDispatchProps;
298
+
299
+ export default connect(mapStateToProps, mapDispatchToProps)(Integrations);
@@ -0,0 +1,48 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ display: flex;
5
+ height: 100%;
6
+ `;
7
+
8
+ const ContentWrapper = styled.div`
9
+ display: flex;
10
+ overflow: auto;
11
+ flex-direction: column;
12
+ width: 100%;
13
+ `;
14
+
15
+ const TitleWrapper = styled.div`
16
+ padding: ${(p) => p.theme.spacing.m};
17
+ border-bottom: ${(p) => `1px solid ${p.theme.color.uiLine}`};
18
+ `;
19
+
20
+ const Title = styled.div`
21
+ ${(p) => p.theme.textStyle.headingXS};
22
+ margin-bottom: ${(p) => p.theme.spacing.xs};
23
+ `;
24
+
25
+ const Description = styled.div`
26
+ ${(p) => p.theme.textStyle.uiM};
27
+ `;
28
+
29
+ const TableWrapper = styled.div`
30
+ display: flex;
31
+ flex-direction: column;
32
+ width: 100%;
33
+ position: relative;
34
+ height: calc(100vh - ${(p) => p.theme.spacing.xl});
35
+ overflow: auto;
36
+ `;
37
+
38
+ const EmptyWrapper = styled.div`
39
+ height: ${(p) => `calc(100vh - (${p.theme.spacing.xl} * 5))`};
40
+ display: flex;
41
+ align-items: center;
42
+ `;
43
+
44
+ const ModalContent = styled.div`
45
+ padding: ${(p) => p.theme.spacing.m};
46
+ `;
47
+
48
+ export { Wrapper, ContentWrapper, TitleWrapper, Title, Description, TableWrapper, EmptyWrapper, ModalContent };
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { IIntegration } from "@ax/types";
3
+
4
+ const getSortedListStatus = (
5
+ orderPointer: string,
6
+ isAscending: boolean
7
+ ): { isAscending: boolean; sortedByTitle: boolean } => {
8
+ const sortedListStatus = {
9
+ isAscending,
10
+ sortedByTitle: orderPointer === "name",
11
+ };
12
+
13
+ return sortedListStatus;
14
+ };
15
+
16
+ const getIntegrationsNames = (integrations: IIntegration[], ids: number[]) => {
17
+ const names = integrations.reduce(
18
+ (prev: string[], curr) => (curr.id && ids.includes(curr.id) ? [...prev, curr.name] : prev),
19
+ []
20
+ );
21
+ const namesString = names.join(", ");
22
+ const lastCommaIndex = namesString.lastIndexOf(", ");
23
+ if (lastCommaIndex > -1) {
24
+ return (
25
+ <>
26
+ <strong>{namesString.substring(0, lastCommaIndex - 1)}</strong> and
27
+ <strong>{namesString.substring(lastCommaIndex + 1)}</strong> integrations
28
+ </>
29
+ );
30
+ } else {
31
+ return (
32
+ <>
33
+ <strong>{namesString}</strong> integration
34
+ </>
35
+ );
36
+ }
37
+ };
38
+
39
+ export { getSortedListStatus, getIntegrationsNames };
@@ -13,6 +13,8 @@ import Users from "./../modules/Users";
13
13
  import UserCreate from "./../modules/Users/UserCreate";
14
14
  import UserEdit from "./../modules/Users/UserEdit";
15
15
  import Profile from "./../modules/Users/Profile";
16
+ import Integrations from "./../modules/Settings/Integrations";
17
+ import IntegrationForm from "./../modules/Settings/Integrations/IntegrationForm";
16
18
 
17
19
  const BASE_PATH = "/sites";
18
20
 
@@ -101,10 +103,27 @@ export default [
101
103
  component: SeoAnalyticsSettings,
102
104
  name: "SEO & Analytics",
103
105
  },
106
+ {
107
+ path: `${BASE_PATH}/settings/integrations`,
108
+ component: Integrations,
109
+ name: "Third-Party Integration",
110
+ },
104
111
  ],
105
112
  },
106
113
  ],
107
114
  },
115
+ {
116
+ path: `${BASE_PATH}/settings/integrations/new`,
117
+ component: IntegrationForm,
118
+ name: "New Integration",
119
+ showInNav: false,
120
+ },
121
+ {
122
+ path: `${BASE_PATH}/settings/integrations/edit`,
123
+ component: IntegrationForm,
124
+ name: "Edit Integration",
125
+ showInNav: false,
126
+ },
108
127
  { path: `${BASE_PATH}/settings/languages`, component: Settings, name: "Languages", showInNav: false },
109
128
  { path: `${BASE_PATH}/settings/social`, component: Settings, name: "Social", showInNav: false },
110
129
  { path: `${BASE_PATH}/settings/analytics`, component: SeoAnalyticsSettings, name: "Analytics", showInNav: false },
@@ -105,6 +105,10 @@ export default {
105
105
  },
106
106
  ],
107
107
  },
108
+ {
109
+ type: "IntegrationsField",
110
+ key: "integrations",
111
+ },
108
112
  ],
109
113
  },
110
114
  {
@@ -267,6 +271,7 @@ export default {
267
271
  socialDescription: "",
268
272
  socialImage: {},
269
273
  dimensions: {},
274
+ integrations: null,
270
275
  theme: null,
271
276
  customizeThemes: false,
272
277
  headerTheme: null,