@griddo/ax 1.73.28 → 1.74.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 (98) hide show
  1. package/config/griddo-config/index.js +24 -3
  2. package/config/webpackSchemas.config.js +3 -5
  3. package/package.json +3 -2
  4. package/scripts/griddo-sync-schemas.js +4 -3
  5. package/src/__mocks__/axios/SitesList.ts +84 -0
  6. package/src/__mocks__/store/SitesList.ts +714 -0
  7. package/src/__tests__/components/Avatar/Avatar.test.tsx +119 -0
  8. package/src/__tests__/components/Avatar/__snapshots__/Avatar.test.tsx.snap +61 -0
  9. package/src/__tests__/components/Fields/ReferenceField/ReferenceField.test.tsx +10 -10
  10. package/src/__tests__/components/LanguageMenu/LanguageMenu.test.tsx +221 -0
  11. package/src/__tests__/components/Loading/Loading.test.tsx +23 -0
  12. package/src/__tests__/components/Login/Login.test.tsx +247 -0
  13. package/src/__tests__/components/Login/RecoveryModal/RecoveryModal.test.tsx +185 -0
  14. package/src/__tests__/components/TableFilters/LiveFilter/LiveFilter.test.tsx +6 -6
  15. package/src/__tests__/modules/Sites/Sites.test.tsx +259 -0
  16. package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +51 -0
  17. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +896 -0
  18. package/src/api/sites.tsx +43 -4
  19. package/src/components/ActionMenu/index.tsx +1 -1
  20. package/src/components/Avatar/index.tsx +4 -4
  21. package/src/components/Browser/index.tsx +27 -20
  22. package/src/components/BrowserContent/index.tsx +6 -0
  23. package/src/components/Fields/AsyncSelect/style.tsx +6 -3
  24. package/src/components/Fields/CheckField/index.tsx +1 -0
  25. package/src/components/Fields/DateField/style.tsx +3 -1
  26. package/src/components/Fields/HeadingField/index.tsx +14 -5
  27. package/src/components/Fields/ImageField/index.tsx +3 -0
  28. package/src/components/Fields/ImageField/style.tsx +2 -2
  29. package/src/components/Fields/Select/style.tsx +2 -0
  30. package/src/components/Fields/SliderField/index.tsx +2 -1
  31. package/src/components/Fields/TextField/index.tsx +1 -0
  32. package/src/components/Icon/components/BulletList.js +16 -0
  33. package/src/components/Icon/components/Grid2.js +16 -0
  34. package/src/components/Icon/svgs/Bullet-list.svg +3 -0
  35. package/src/components/Icon/svgs/Grid-2.svg +3 -0
  36. package/src/components/IconAction/index.tsx +4 -2
  37. package/src/components/IconAction/style.tsx +8 -2
  38. package/src/components/LanguageMenu/index.tsx +13 -6
  39. package/src/components/Login/RecoveryModal/index.tsx +5 -4
  40. package/src/components/Login/index.tsx +13 -3
  41. package/src/components/Login/style.tsx +12 -25
  42. package/src/components/Pagination/style.tsx +1 -1
  43. package/src/components/SearchField/index.tsx +9 -1
  44. package/src/components/SideModal/style.tsx +8 -8
  45. package/src/components/TableFilters/LastAccessFilter/index.tsx +52 -0
  46. package/src/components/TableFilters/LastAccessFilter/style.tsx +31 -0
  47. package/src/components/TableFilters/LiveFilter/index.tsx +7 -5
  48. package/src/components/TableFilters/NameFilter/index.tsx +4 -3
  49. package/src/components/TableFilters/index.tsx +2 -0
  50. package/src/components/TableList/index.tsx +2 -1
  51. package/src/components/TableList/style.tsx +2 -2
  52. package/src/components/index.tsx +2 -0
  53. package/src/containers/App/actions.tsx +5 -0
  54. package/src/containers/App/interfaces.tsx +1 -1
  55. package/src/containers/App/reducer.tsx +1 -1
  56. package/src/containers/Navigation/Defaults/actions.tsx +3 -1
  57. package/src/containers/PageEditor/actions.tsx +6 -3
  58. package/src/containers/Sites/actions.tsx +76 -11
  59. package/src/containers/Sites/constants.tsx +2 -0
  60. package/src/containers/Sites/interfaces.tsx +12 -0
  61. package/src/containers/Sites/reducer.tsx +8 -0
  62. package/src/helpers/schemas.tsx +27 -1
  63. package/src/hooks/iframe.ts +56 -0
  64. package/src/hooks/index.tsx +3 -0
  65. package/src/modules/Content/index.tsx +4 -3
  66. package/src/modules/FramePreview/index.tsx +25 -39
  67. package/src/modules/GlobalEditor/Editor/index.tsx +2 -2
  68. package/src/modules/GlobalEditor/PageBrowser/index.tsx +16 -4
  69. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +11 -7
  70. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +4 -3
  71. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +1 -0
  72. package/src/modules/PageEditor/Editor/index.tsx +2 -2
  73. package/src/modules/PageEditor/PageBrowser/index.tsx +16 -4
  74. package/src/modules/PageEditor/index.tsx +8 -7
  75. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/index.tsx +12 -11
  76. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/index.tsx +3 -17
  77. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/style.tsx +2 -10
  78. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +72 -0
  79. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/style.tsx +32 -0
  80. package/src/modules/Sites/SitesList/{SiteItem → GridView/GridSiteItem}/index.tsx +17 -27
  81. package/src/modules/Sites/SitesList/{SiteItem → GridView/GridSiteItem}/style.tsx +14 -25
  82. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +64 -0
  83. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/style.tsx +50 -0
  84. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +75 -0
  85. package/src/modules/Sites/SitesList/ListView/BulkHeader/style.tsx +8 -0
  86. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +200 -0
  87. package/src/modules/Sites/SitesList/ListView/ListSiteItem/style.tsx +112 -0
  88. package/src/modules/Sites/SitesList/RecentSiteItem/index.tsx +50 -0
  89. package/src/modules/Sites/SitesList/RecentSiteItem/style.tsx +28 -0
  90. package/src/modules/Sites/SitesList/SiteModal/index.tsx +4 -3
  91. package/src/modules/Sites/SitesList/atoms.tsx +47 -0
  92. package/src/modules/Sites/SitesList/hooks.tsx +102 -0
  93. package/src/modules/Sites/SitesList/index.tsx +272 -19
  94. package/src/modules/Sites/SitesList/style.tsx +157 -4
  95. package/src/modules/Sites/SitesList/utils.tsx +33 -0
  96. package/src/modules/Sites/index.tsx +6 -11
  97. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +1 -1
  98. package/src/types/index.tsx +25 -2
@@ -1,39 +1,112 @@
1
- import React, { useState } from "react";
1
+ import React, { useEffect, useRef, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { appActions } from "@ax/containers/App";
5
5
  import { sitesActions } from "@ax/containers/Sites";
6
- import { IRootState, ISettingsForm, ISite, IUser } from "@ax/types";
7
- import { useModal } from "@ax/hooks";
8
- import { MainWrapper, Modal, ErrorToast } from "@ax/components";
6
+ import { ICheck, IGetSitesParams, IRootState, ISettingsForm, ISite, IUser } from "@ax/types";
7
+ import { useBulkSelection, useModal } from "@ax/hooks";
8
+ import { MainWrapper, Modal, ErrorToast, Icon, IconAction, TableList, EmptyState } from "@ax/components";
9
+
10
+ import { useFilterQuery, useSortedListStatus, useIsMount } from "./hooks";
11
+ import { filterByStatus, getSortedListStatus } from "./utils";
9
12
 
10
13
  import SiteModal from "./SiteModal";
11
- import SiteItem from "./SiteItem";
14
+ import RecentSiteItem from "./RecentSiteItem";
15
+ import GridHeaderFilter from "./GridView/GridHeaderFilter";
16
+ import GridSiteItem from "./GridView/GridSiteItem";
17
+ import BulkHeader from "./ListView/BulkHeader";
18
+ import ListSiteItem from "./ListView/ListSiteItem";
12
19
 
13
20
  import * as S from "./style";
14
21
 
15
- const SitesList = (props: IProps): JSX.Element => {
16
- const { sites, saveSettings, setHistoryPush, currentUser } = props;
22
+ const SitesList = (props: ISitesListProps): JSX.Element => {
23
+ const {
24
+ token,
25
+ sites,
26
+ recentSites,
27
+ totalItems,
28
+ saveSettings,
29
+ setHistoryPush,
30
+ currentUser,
31
+ getSites,
32
+ publishSitesBulk,
33
+ unpublishSitesBulk,
34
+ } = props;
17
35
 
36
+ const isMount = useIsMount();
18
37
  const initialState = {
19
38
  name: "",
20
39
  defaultLanguage: null,
21
40
  path: "",
22
41
  domain: null,
23
42
  };
43
+
44
+ const itemsPerPage = 30;
45
+ const firstPage = 1;
46
+ const [sitesIds, setsitesIds] = useState<any[]>([]);
47
+
48
+ const [page, setPage] = useState(firstPage);
24
49
  const [form, setForm] = useState(initialState);
50
+ const [isRecentSitesListDisplayed, setIsRecentSitesListDisplayed] = useState<boolean>(true);
51
+ const [displayMode, setDisplayMode] = useState<"list" | "grid">("grid");
52
+ const [searchQuery, setSearchQuery] = useState<string | null>("");
53
+ const [currentFilterQuery, setCurrentFilterQuery] = useState("&order=lastAccess-desc");
25
54
 
26
- const { isOpen, toggleModal } = useModal();
55
+ const tableRef = useRef<HTMLDivElement>(null);
27
56
 
28
- const openModal = () => {
29
- toggleModal();
57
+ const pagination = {
58
+ setPage,
59
+ itemsPerPage,
60
+ totalItems,
61
+ currPage: page,
30
62
  };
31
63
 
64
+ const { isOpen, toggleModal } = useModal();
65
+
66
+ const openModal = () => toggleModal();
32
67
  const rightButtonProps = {
33
68
  label: "New",
34
69
  action: openModal,
35
70
  };
36
71
 
72
+ const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
73
+ const { setFiltersSelection, setFilterQuery, filterValues } = useFilterQuery();
74
+
75
+ useEffect(() => {
76
+ if (token) {
77
+ const query = searchQuery ? `&query=${searchQuery}` : "";
78
+ const params = {
79
+ pagination: true,
80
+ language: undefined,
81
+ recentSitesNumber: 7,
82
+ page,
83
+ itemsPerPage,
84
+ filterQuery: currentFilterQuery,
85
+ searchQuery: query,
86
+ };
87
+ if (isMount) {
88
+ getSites({ ...params, token });
89
+ } else {
90
+ getSites(params);
91
+ }
92
+ }
93
+ // eslint-disable-next-line react-hooks/exhaustive-deps
94
+ }, [token, currentFilterQuery, searchQuery, page]);
95
+
96
+ useEffect(() => {
97
+ if (sites.length > 0) {
98
+ const currentSitesId = sites?.map((site: any) => site.id);
99
+ setsitesIds(currentSitesId);
100
+ }
101
+ }, [sites]);
102
+
103
+ useEffect(() => {
104
+ if (tableRef.current) {
105
+ tableRef.current.scrollTo(0, 0);
106
+ }
107
+ // eslint-disable-next-line react-hooks/exhaustive-deps
108
+ }, [page, searchQuery, filterValues]);
109
+
37
110
  const saveSiteSettings = async () => {
38
111
  const result = await saveSettings(form);
39
112
  if (result) {
@@ -51,17 +124,184 @@ const SitesList = (props: IProps): JSX.Element => {
51
124
  const secondaryAction = { title: "Cancel", onClick: toggleModal };
52
125
 
53
126
  const userHasSite = (siteID: number) => currentUser.sites.includes("all") || currentUser.sites.includes(siteID);
127
+ const toggleRecentSites = () => setIsRecentSitesListDisplayed(!isRecentSitesListDisplayed);
54
128
 
55
- return (
56
- <MainWrapper title="Sites" rightButton={rightButtonProps}>
57
- <ErrorToast />
58
- <S.Sites>
59
- {sites
129
+ const sortItems = (orderPointer: string, isAscending: boolean) => {
130
+ setIsRecentSitesListDisplayed(false);
131
+ setPage(firstPage);
132
+ const orderMethod = isAscending ? "asc" : "desc";
133
+ const sortedState = getSortedListStatus(orderPointer, isAscending);
134
+ setSortedListStatus(sortedState);
135
+ const pointer = orderPointer === "title" ? "name" : orderPointer;
136
+ const filtersSelection = setFiltersSelection("order", pointer, orderMethod);
137
+ const filterQuery = setFilterQuery(filtersSelection);
138
+
139
+ setCurrentFilterQuery(filterQuery);
140
+ };
141
+
142
+ const filterItems = async (filterPointer: string, filtersSelected: string) => {
143
+ setIsRecentSitesListDisplayed(false);
144
+ setPage(firstPage);
145
+ const filtersSelection = setFiltersSelection(filterPointer, filtersSelected);
146
+ const filterQuery = setFilterQuery(filtersSelection);
147
+ setCurrentFilterQuery(filterQuery);
148
+ };
149
+
150
+ const RecentSitesHeader = () => (
151
+ <S.SectionHeader data-testid="recent-sites-header" isRecentSites={true}>
152
+ <S.Title data-testid="recent-sites-title" isActive={isRecentSitesListDisplayed}>
153
+ Recent sites
154
+ </S.Title>
155
+ <S.CollapseButton data-testid="recent-sites-collapse-button" onClick={toggleRecentSites}>
156
+ {isRecentSitesListDisplayed ? (
157
+ <>
158
+ <S.Label data-testid="recent-sites-hide-label">Hide recent sites </S.Label> <Icon name="UpArrow" />
159
+ </>
160
+ ) : (
161
+ <>
162
+ <S.Label data-testid="recent-sites-show-label">Show recent sites </S.Label> <Icon name="DownArrow" />
163
+ </>
164
+ )}
165
+ </S.CollapseButton>
166
+ </S.SectionHeader>
167
+ );
168
+
169
+ const RecentSitesList = () => (
170
+ <S.RecentSites data-testid="recent-sites-list">
171
+ <RecentSitesHeader />
172
+ <S.RecentSitesItemsWrapper data-testid="recent-sites-items-wrapper" isHidden={!isRecentSitesListDisplayed}>
173
+ {recentSites
60
174
  .filter((site: ISite) => userHasSite(site.id))
61
- .map((site: ISite) => (
62
- <SiteItem key={site.id} site={site} />
175
+ .map((site: ISite, i: number) => (
176
+ <RecentSiteItem key={i} site={site} />
63
177
  ))}
64
- </S.Sites>
178
+ </S.RecentSitesItemsWrapper>
179
+ </S.RecentSites>
180
+ );
181
+
182
+ const AllSitesHeader = () => (
183
+ <S.SectionHeader data-testid="all-sites-header">
184
+ <S.Title isActive={true} data-testid="all-sites-title">
185
+ All sites
186
+ </S.Title>
187
+ <S.HeaderIconsWrapper data-testid="all-sites-header-icons">
188
+ {displayMode === "grid" ? (
189
+ <S.FilterSelect data-testid="all-sites-grid-filter">
190
+ <S.FilterSelectLabel data-testid="all-sites-grid-filter-label">Sort by:</S.FilterSelectLabel>{" "}
191
+ <GridHeaderFilter sortItems={sortItems} sortedState={sortedListStatus} />{" "}
192
+ </S.FilterSelect>
193
+ ) : null}
194
+ <IconAction icon="Grid2" onClick={() => setDisplayMode("grid")} active={displayMode === "grid"} />
195
+ <IconAction icon="BulletList" onClick={() => setDisplayMode("list")} active={displayMode === "list"} />
196
+ </S.HeaderIconsWrapper>
197
+ </S.SectionHeader>
198
+ );
199
+
200
+ const {
201
+ resetBulkSelection,
202
+ selectedItems,
203
+ isSelected,
204
+ areItemsSelected,
205
+ checkState,
206
+ addToBulkSelection,
207
+ selectAllItems,
208
+ } = useBulkSelection(sitesIds);
209
+
210
+ const bulkFilter = (bulkSelection: number[]) => filterByStatus(bulkSelection, sites);
211
+
212
+ const handleAddToBulk = (item: ICheck) => addToBulkSelection(item, bulkFilter);
213
+
214
+ const handleSelectAll = () => selectAllItems(bulkFilter);
215
+
216
+ const unselectAllItems = () => resetBulkSelection();
217
+
218
+ const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
219
+
220
+ const bulkPublishAction = async (isPublish: boolean) => {
221
+ const { notPublished, published } = selectedItems;
222
+ if (notPublished.length > 0 && isPublish) {
223
+ publishSitesBulk(notPublished);
224
+ }
225
+ if (published.length > 0 && !isPublish) {
226
+ unpublishSitesBulk(published);
227
+ }
228
+
229
+ const query = searchQuery ? `&query=${searchQuery}` : "";
230
+ const params = {
231
+ pagination: true,
232
+ language: undefined,
233
+ recentSitesNumber: 7,
234
+ page,
235
+ itemsPerPage,
236
+ filterQuery: currentFilterQuery,
237
+ searchQuery: query,
238
+ };
239
+ getSites(params);
240
+ unselectAllItems();
241
+ };
242
+
243
+ const bulkPublish = () => bulkPublishAction(true);
244
+ const bulkUnpublish = () => bulkPublishAction(false);
245
+
246
+ const Header = (
247
+ <BulkHeader
248
+ showBulk={areItemsSelected(sitesIds)}
249
+ bulkPublish={bulkPublish}
250
+ bulkUnpublish={bulkUnpublish}
251
+ checkState={checkState}
252
+ selectAllItems={handleSelectAll}
253
+ totalItems={totalItems}
254
+ selectItems={selectItems}
255
+ sortItems={sortItems}
256
+ sortedListStatus={sortedListStatus}
257
+ filterItems={filterItems}
258
+ filterValues={filterValues}
259
+ />
260
+ );
261
+
262
+ const mappedSites =
263
+ sites.length > 0 ? (
264
+ sites
265
+ .filter((site: ISite) => userHasSite(site.id))
266
+ .map((site: ISite) => {
267
+ const isItemSelected = isSelected(site.id);
268
+ return displayMode === "grid" ? (
269
+ <GridSiteItem key={site.id} site={site} />
270
+ ) : (
271
+ <ListSiteItem key={site.id} site={site} isSelected={isItemSelected} onCheck={handleAddToBulk} />
272
+ );
273
+ })
274
+ ) : (
275
+ <S.EmptyStateWrapper data-testid="empty-state">
276
+ <EmptyState icon="search" message="We couldn't find what you are looking for. Please, try another search." />
277
+ </S.EmptyStateWrapper>
278
+ );
279
+
280
+ const GridList = () => (
281
+ <S.GridList data-testid="sites-grid-list" isEmpty={sites.length === 0}>
282
+ {mappedSites}
283
+ </S.GridList>
284
+ );
285
+ const ListTable = () => (
286
+ <TableList
287
+ tableHeader={Header}
288
+ pagination={pagination}
289
+ hasFixedHeader={true}
290
+ tableRef={tableRef}
291
+ overflow="visible"
292
+ >
293
+ {mappedSites}
294
+ </TableList>
295
+ );
296
+
297
+ const AllSitesList = () => (displayMode === "grid" ? <GridList /> : <ListTable />);
298
+
299
+ return (
300
+ <MainWrapper title="Sites" rightButton={rightButtonProps} searchAction={setSearchQuery}>
301
+ <ErrorToast />
302
+ {recentSites?.length > 0 ? <RecentSitesList /> : null}
303
+ <AllSitesHeader />
304
+ <AllSitesList />
65
305
  <Modal
66
306
  isOpen={isOpen}
67
307
  hide={toggleModal}
@@ -78,23 +318,36 @@ const SitesList = (props: IProps): JSX.Element => {
78
318
 
79
319
  interface ISitesProps {
80
320
  sites: ISite[];
321
+ recentSites: ISite[];
81
322
  currentUser: IUser;
323
+ totalItems: number;
324
+ token: string;
82
325
  }
83
326
 
84
327
  const mapStateToProps = (state: IRootState) => ({
85
328
  currentUser: state.users.currentUser,
329
+ totalItems: state.sites.sitesTotalItems,
330
+ token: state.app.token,
331
+ sites: state.sites.sites,
332
+ recentSites: state.sites.recentSites,
86
333
  });
87
334
 
88
335
  interface IDispatchProps {
89
336
  setHistoryPush(path: string, isEditor?: boolean): void;
90
337
  saveSettings(form: ISettingsForm): Promise<boolean>;
338
+ getSites(params: IGetSitesParams): void;
339
+ publishSitesBulk(ids: number[]): void;
340
+ unpublishSitesBulk(ids: number[]): void;
91
341
  }
92
342
 
93
343
  const mapDispatchToProps = {
94
344
  setHistoryPush: appActions.setHistoryPush,
95
345
  saveSettings: sitesActions.saveSettings,
346
+ getSites: sitesActions.getSites,
347
+ publishSitesBulk: sitesActions.publishSitesBulk,
348
+ unpublishSitesBulk: sitesActions.unpublishSitesBulk,
96
349
  };
97
350
 
98
- type IProps = ISitesProps & IDispatchProps;
351
+ export type ISitesListProps = ISitesProps & IDispatchProps;
99
352
 
100
353
  export default connect(mapStateToProps, mapDispatchToProps)(SitesList);
@@ -1,8 +1,161 @@
1
- import styled from "styled-components";
1
+ import styled, { css } from "styled-components";
2
2
 
3
- export const Sites = styled.div`
3
+ const SectionHeader = styled.div<{ isRecentSites?: boolean }>`
4
+ display: flex;
5
+ justify-content: space-between;
6
+ margin: 0 ${(p) => (p.isRecentSites ? p.theme.spacing.s : p.theme.spacing.m)};
7
+ margin-top: ${(p) => p.theme.spacing.m};
8
+ `;
9
+
10
+ const Label = styled.span`
11
+ margin-right: 14px;
12
+ `;
13
+
14
+ const Title = styled.h1<{ isActive?: boolean }>`
15
+ margin: 0;
16
+ margin-right: ${(p) => p.theme.spacing.m};
17
+ ${(p) => p.theme.textStyle.headingM};
18
+ color: ${(p) => (p.isActive ? p.theme.colors.textHighEmphasis : p.theme.colors.textMediumEmphasis)};
19
+ `;
20
+
21
+ const HeaderIconsWrapper = styled.div`
22
+ display: flex;
23
+ align-items: center;
24
+ `;
25
+
26
+ const FilterSelectLabel = styled.span`
27
+ color: ${(p) => p.theme.color.interactive01};
28
+ `;
29
+
30
+ const FilterSelect = styled.div`
31
+ ${(p) => p.theme.textStyle.uiS};
32
+ line-height: 32px;
33
+ display: flex;
34
+ align-items: center;
35
+ position: relative;
36
+ color: ${(p) => p.theme.color.textLowEmphasis};
37
+ `;
38
+
39
+ const CollapseButton = styled.div`
40
+ display: flex;
41
+ align-items: center;
42
+ ${(p) => p.theme.textStyle.uiS};
43
+ color: ${(p) => p.theme.colors.textMediumEmphasis};
44
+ padding: 0;
45
+ margin: 0;
46
+ cursor: pointer;
47
+ `;
48
+
49
+ const RecentSites = styled.div`
50
+ padding-bottom: ${(p) => p.theme.spacing.s};
51
+ border-radius: ${(p) => p.theme.radii.s};
52
+ background: ${(p) => p.theme.color.uiBackground02};
53
+ min-height: 56px;
54
+ overflow: hidden;
55
+ margin: ${(p) => p.theme.spacing.m};
56
+ margin-bottom: 0;
57
+
58
+ h1 {
59
+ padding-left: 0;
60
+ }
61
+ `;
62
+
63
+ const RecentSitesItemsWrapper = styled.div<{ isHidden: boolean }>`
4
64
  display: grid;
5
- grid-template-columns: repeat(auto-fit, 270px);
65
+ grid-auto-flow: column;
66
+ overflow: auto;
6
67
  grid-gap: ${(p) => p.theme.spacing.m};
7
- padding: ${(p) => p.theme.spacing.m};
68
+ padding: ${(p) => p.theme.spacing.s} ${(p) => p.theme.spacing.s} 0px ${(p) => p.theme.spacing.s};
69
+ overflow: hidden;
70
+
71
+ div {
72
+ min-width: 100%;
73
+ }
74
+
75
+ ${(props) =>
76
+ props.isHidden &&
77
+ css`
78
+ height: 0;
79
+ padding: 0;
80
+ padding-left: ${(p) => p.theme.spacing.s};
81
+ padding-top: ${(p) => p.theme.spacing.xs};
82
+ `};
83
+
84
+ @media (min-width: 1200px) {
85
+ div:nth-child(6),
86
+ div:nth-child(7) {
87
+ display: none;
88
+ }
89
+ }
90
+
91
+ @media (min-width: 1600px) {
92
+ div:nth-child(6) {
93
+ display: block;
94
+ }
95
+ div:nth-child(7) {
96
+ display: none;
97
+ }
98
+ }
99
+
100
+ @media (min-width: 1700px) {
101
+ div:nth-child(6),
102
+ div:nth-child(7) {
103
+ display: block;
104
+ }
105
+ }
106
+ `;
107
+
108
+ const GridList = styled.div<{ isEmpty: boolean }>`
109
+ padding-top: ${(p) => p.theme.spacing.s};
110
+ padding-bottom: ${(p) => p.theme.spacing.m};
111
+ margin: 0 ${(p) => p.theme.spacing.m};
112
+
113
+ ${({ isEmpty, theme }) =>
114
+ isEmpty
115
+ ? `
116
+ display: flex;
117
+ justify-content: center;
118
+ `
119
+ : `display: grid;
120
+ grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
121
+ grid-gap: ${theme.spacing.m};
122
+ `}
8
123
  `;
124
+
125
+ const EmptyStateWrapper = styled.div`
126
+ text-align: center;
127
+ `;
128
+
129
+ const ImageWrapper = styled.div<{ borderRadius?: boolean }>`
130
+ img {
131
+ border-radius: ${(p) => (p.borderRadius ? p.theme.spacing.xs : 0)};
132
+ object-fit: cover;
133
+ }
134
+ `;
135
+
136
+ const Thumbnail = styled.div<{ backgroundUrl: string; height: number; width: number; borderRadius?: boolean }>`
137
+ background: url(${(p) => p.backgroundUrl}) no-repeat center;
138
+ background-size: cover;
139
+ height: ${(p) => p.height}px;
140
+ width: ${(p) => p.width}px;
141
+ border-radius: ${(p) => (p.borderRadius ? p.theme.spacing.xs : 0)};
142
+ `;
143
+
144
+ const ItemName = styled.span``;
145
+
146
+ export {
147
+ SectionHeader,
148
+ CollapseButton,
149
+ Label,
150
+ Title,
151
+ HeaderIconsWrapper,
152
+ FilterSelect,
153
+ FilterSelectLabel,
154
+ RecentSites,
155
+ RecentSitesItemsWrapper,
156
+ GridList,
157
+ EmptyStateWrapper,
158
+ ImageWrapper,
159
+ Thumbnail,
160
+ ItemName,
161
+ };
@@ -0,0 +1,33 @@
1
+ import { ISite } from "@ax/types";
2
+
3
+ const getSortedListStatus = (orderPointer: string, isAscending: boolean): any => {
4
+ const sortedListStatus = {
5
+ isAscending,
6
+ sortedByTitle: orderPointer === "name",
7
+ sortedByLastAccess: orderPointer === "lastAccess",
8
+ sortedByDateCreated: orderPointer === "dateCreated",
9
+ };
10
+
11
+ return sortedListStatus;
12
+ };
13
+
14
+ const filterByStatus = (bulkSelection: number[], sites: ISite[]): Record<string, number[]> => {
15
+ const notPublishedItems: number[] = [];
16
+ const publishedItems: number[] = [];
17
+
18
+ sites.forEach((site: ISite) => {
19
+ const { id, isPublished } = site;
20
+ if (bulkSelection.includes(id)) {
21
+ isPublished ? publishedItems.push(id) : notPublishedItems.push(id);
22
+ }
23
+ });
24
+
25
+ const filteredItems = {
26
+ all: bulkSelection,
27
+ notPublished: notPublishedItems,
28
+ published: publishedItems,
29
+ };
30
+
31
+ return filteredItems;
32
+ };
33
+ export { getSortedListStatus, filterByStatus };
@@ -1,23 +1,23 @@
1
1
  import React, { useEffect } from "react";
2
+
2
3
  import { connect } from "react-redux";
3
4
  import { withRouter, RouteComponentProps } from "react-router-dom";
4
5
 
5
- import { ISite, IRootState } from "@ax/types";
6
+ import { IRootState } from "@ax/types";
6
7
  import { Loading } from "@ax/components";
7
8
  import { appActions } from "@ax/containers/App";
8
9
  import { sitesActions } from "@ax/containers/Sites";
9
10
  import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
10
11
  import { structuredDataActions } from "@ax/containers/StructuredData";
11
12
  import { usersActions } from "@ax/containers/Users";
13
+
12
14
  import SitesList from "./SitesList";
13
15
 
14
- const Sites = (props: IProps): JSX.Element => {
16
+ const Sites = (props: ISitesProps): JSX.Element => {
15
17
  const {
16
18
  isLoading,
17
- sites,
18
19
  token,
19
20
  setCurrentSiteInfo,
20
- getSites,
21
21
  getStructuredData,
22
22
  setLanguage,
23
23
  getAllDataPacks,
@@ -28,7 +28,6 @@ const Sites = (props: IProps): JSX.Element => {
28
28
  const fetchInitialData = async () => {
29
29
  setCurrentSiteInfo(null);
30
30
  await getStructuredData(token);
31
- await getSites();
32
31
  await getAllDataPacks();
33
32
  await getUser("me");
34
33
 
@@ -49,37 +48,33 @@ const Sites = (props: IProps): JSX.Element => {
49
48
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
49
  }, [token]);
51
50
 
52
- return isLoading && sites.length === 0 ? <Loading /> : <SitesList sites={sites} />;
51
+ return isLoading ? <Loading /> : <SitesList />;
53
52
  };
54
53
 
55
54
  const mapStateToProps = (state: IRootState) => ({
56
55
  isLoading: state.app.isLoading,
57
- sites: state.sites.sites,
58
56
  token: state.app.token,
59
57
  globalLangs: state.app.globalLangs,
60
58
  });
61
59
 
62
60
  interface IStateProps {
63
61
  isLoading: boolean;
64
- sites: ISite[];
65
62
  token: string;
66
63
  globalLangs: any[];
67
64
  }
68
65
 
69
66
  interface IDispatchProps {
70
67
  setCurrentSiteInfo(currentSiteInfo: any): void;
71
- getSites(): Promise<void>;
72
68
  getStructuredData(token: string, siteId?: number): Promise<void>;
73
69
  setLanguage(lang: { locale: string; id: number | null }): void;
74
70
  getAllDataPacks: () => Promise<void>;
75
71
  getUser: (id: string) => Promise<void>;
76
72
  }
77
73
 
78
- type IProps = IStateProps & IDispatchProps & RouteComponentProps;
74
+ export type ISitesProps = IStateProps & IDispatchProps & RouteComponentProps;
79
75
 
80
76
  const mapDispatchToProps = {
81
77
  setCurrentSiteInfo: sitesActions.setCurrentSiteInfo,
82
- getSites: sitesActions.getSites,
83
78
  getStructuredData: structuredDataActions.getStructuredData,
84
79
  setLanguage: appActions.setLanguage,
85
80
  getAllDataPacks: dataPacksActions.getAllDataPacks,
@@ -105,7 +105,7 @@ const TableHeader = (props: IProps): JSX.Element => {
105
105
  </S.NameWrapper>
106
106
  {activeColumns.includes("live") && (
107
107
  <S.HeaderWrapper>
108
- <LiveFilter filterItems={filterItems} value={filterValues.liveStatus} isStructuredData={true} />
108
+ <LiveFilter filterItems={filterItems} value={filterValues.liveStatus} hasBasicStatus={true} />
109
109
  </S.HeaderWrapper>
110
110
  )}
111
111
  {CategoryColumns}
@@ -97,10 +97,22 @@ export interface IPage extends IAPIPage {
97
97
  isGlobal?: boolean;
98
98
  }
99
99
 
100
+ export interface IGetSitesParams {
101
+ token?: string;
102
+ language?: number;
103
+ recentSitesNumber?: number;
104
+ pagination?: boolean;
105
+ page?: number;
106
+ itemsPerPage?: number;
107
+ searchQuery?: string;
108
+ filterQuery?: string;
109
+ }
110
+
100
111
  export interface ISite {
101
112
  author: string;
102
113
  id: number;
103
114
  name: string;
115
+ home?: string;
104
116
  pages: IPage[];
105
117
  published: string;
106
118
  isPublished: boolean;
@@ -114,6 +126,12 @@ export interface ISite {
114
126
  bigAvatar: string;
115
127
  thumbnail: string;
116
128
  domain: number | null;
129
+ lastAccess?: string;
130
+ }
131
+
132
+ export interface IRecentSite {
133
+ lastAccess: string;
134
+ siteId: number;
117
135
  }
118
136
 
119
137
  export interface ISchema {
@@ -169,8 +187,8 @@ export interface IUrlField {
169
187
  }
170
188
 
171
189
  export interface IHeadingField {
172
- content: string;
173
- tag: string;
190
+ content?: string;
191
+ tag?: string;
174
192
  }
175
193
 
176
194
  export interface IRootState {
@@ -625,6 +643,11 @@ export interface IUsersQueryValues {
625
643
  filterSites: string;
626
644
  }
627
645
 
646
+ export interface ISitesQueryValues {
647
+ order: string;
648
+ liveStatus: string;
649
+ }
650
+
628
651
  export interface IDataPackConfigImportCategory {
629
652
  id: number;
630
653
  title: string;