@griddo/ax 11.14.2-rc.0 → 11.14.2

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 (148) hide show
  1. package/config/jest/reactEasyCropMock.js +15 -0
  2. package/config/jest/reactTimezoneMock.js +13 -0
  3. package/package.json +221 -219
  4. package/public/img/welcome.svg +127 -0
  5. package/src/__tests__/components/Browser/Browser.test.tsx +27 -51
  6. package/src/__tests__/components/CategoryCell/CategoryCell.test.tsx +10 -5
  7. package/src/__tests__/components/ElementsTooltip/ElementsTooltip.test.tsx +27 -14
  8. package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +2 -0
  9. package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +138 -1
  10. package/src/__tests__/components/ImageDragAndDrop/CropStep/CropStep.test.tsx +84 -0
  11. package/src/__tests__/components/ImageDragAndDrop/ImageDragAndDrop.test.tsx +173 -0
  12. package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +3 -4
  13. package/src/__tests__/components/ProfileImage/ProfileImage.test.tsx +120 -0
  14. package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +8 -0
  15. package/src/__tests__/components/UserRolesAndSites/RoleItem/RoleItem.test.tsx +190 -0
  16. package/src/__tests__/components/UserRolesAndSites/UserRolesAndSites.test.tsx +471 -0
  17. package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +15 -2
  18. package/src/__tests__/modules/Sites/Sites.test.tsx +68 -224
  19. package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +21 -17
  20. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +65 -565
  21. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/DataStep/DataStep.test.tsx +109 -0
  22. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/FinalStep/FinalStep.test.tsx +157 -0
  23. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/CropView.test.tsx +51 -0
  24. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/ImageStep.test.tsx +70 -0
  25. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/UploadView.test.tsx +92 -0
  26. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/TimezoneStep/TimezoneStep.test.tsx +94 -0
  27. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeModal.test.tsx +78 -0
  28. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeStep/WelcomeStep.test.tsx +39 -0
  29. package/src/__tests__/modules/Sites/SitesList/WelcomeModal/utils.test.ts +55 -0
  30. package/src/api/sites.tsx +4 -4
  31. package/src/components/Avatar/index.tsx +26 -5
  32. package/src/components/Avatar/style.tsx +20 -10
  33. package/src/components/Browser/index.tsx +7 -1
  34. package/src/components/ConfigPanel/index.tsx +11 -7
  35. package/src/components/ElementsTooltip/index.tsx +96 -34
  36. package/src/components/ElementsTooltip/style.tsx +12 -1
  37. package/src/components/Fields/FileField/index.tsx +16 -18
  38. package/src/components/Fields/HeadingField/index.tsx +1 -1
  39. package/src/components/Fields/ImageField/index.tsx +9 -38
  40. package/src/components/Fields/ImageField/style.tsx +12 -1
  41. package/src/components/Fields/ToggleField/index.tsx +1 -1
  42. package/src/components/Fields/Wysiwyg/index.tsx +25 -20
  43. package/src/components/FileGallery/GalleryPanel/index.tsx +15 -7
  44. package/src/components/FileGallery/index.tsx +33 -28
  45. package/src/components/Gallery/GalleryPanel/index.tsx +5 -16
  46. package/src/components/Gallery/index.tsx +0 -2
  47. package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +11 -2
  48. package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +21 -3
  49. package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +2 -2
  50. package/src/components/HeadingsPreviewModal/index.tsx +13 -3
  51. package/src/components/HeadingsPreviewModal/style.tsx +18 -0
  52. package/src/components/HeadingsPreviewModal/utils.tsx +31 -3
  53. package/src/components/Image/index.tsx +2 -2
  54. package/src/components/ImageDragAndDrop/CropStep/index.tsx +95 -0
  55. package/src/components/ImageDragAndDrop/CropStep/style.tsx +101 -0
  56. package/src/{modules/MediaGallery → components}/ImageDragAndDrop/index.tsx +103 -40
  57. package/src/{modules/MediaGallery → components}/ImageDragAndDrop/style.tsx +14 -2
  58. package/src/components/KeywordsPreviewModal/atoms.tsx +2 -2
  59. package/src/components/KeywordsPreviewModal/index.tsx +6 -6
  60. package/src/components/KeywordsPreviewModal/utils.tsx +2 -2
  61. package/src/components/ProfileImage/index.tsx +55 -0
  62. package/src/components/ProfileImage/style.tsx +58 -0
  63. package/src/components/ResizePanel/ResizeHandle/index.tsx +44 -6
  64. package/src/components/ResizePanel/ResizeHandle/style.tsx +7 -0
  65. package/src/components/ResizePanel/index.tsx +25 -4
  66. package/src/components/Tabs/style.tsx +1 -1
  67. package/src/components/Tag/index.tsx +0 -1
  68. package/src/components/UserRolesAndSites/RoleItem/index.tsx +42 -0
  69. package/src/components/UserRolesAndSites/RoleItem/style.tsx +29 -0
  70. package/src/components/UserRolesAndSites/index.tsx +102 -0
  71. package/src/components/UserRolesAndSites/style.tsx +67 -0
  72. package/src/components/index.tsx +6 -0
  73. package/src/constants/index.ts +13 -1
  74. package/src/containers/App/actions.tsx +8 -1
  75. package/src/containers/Sites/actions.tsx +26 -0
  76. package/src/containers/Sites/constants.tsx +1 -0
  77. package/src/containers/Sites/interfaces.tsx +6 -0
  78. package/src/containers/Sites/reducer.tsx +5 -1
  79. package/src/containers/Users/reducer.tsx +6 -5
  80. package/src/guards/routeLeaving/index.tsx +9 -11
  81. package/src/helpers/images.tsx +50 -3
  82. package/src/helpers/index.tsx +2 -1
  83. package/src/hooks/forms.tsx +45 -48
  84. package/src/hooks/index.tsx +2 -1
  85. package/src/hooks/modals.tsx +4 -3
  86. package/src/hooks/window.ts +50 -2
  87. package/src/modules/ActivityLog/ItemLogUser/UserItem/index.tsx +1 -1
  88. package/src/modules/App/Routing/Logout/index.tsx +3 -5
  89. package/src/modules/App/Routing/NavMenu/NavItem/index.tsx +73 -52
  90. package/src/modules/App/Routing/NavMenu/NavItem/style.tsx +21 -7
  91. package/src/modules/App/Routing/NavMenu/index.tsx +59 -54
  92. package/src/modules/App/Routing/NavMenu/style.tsx +13 -11
  93. package/src/modules/CreatePass/index.tsx +1 -1
  94. package/src/modules/FileDrive/FileDragAndDrop/index.tsx +11 -8
  95. package/src/modules/FileDrive/FileModal/index.tsx +8 -9
  96. package/src/modules/FileDrive/index.tsx +1 -18
  97. package/src/modules/Forms/FormEditor/index.tsx +1 -1
  98. package/src/modules/FramePreview/HeadingsOverlay/index.tsx +22 -11
  99. package/src/modules/FramePreview/HeadingsOverlay/style.tsx +1 -1
  100. package/src/modules/MediaGallery/ImageModal/index.tsx +1 -5
  101. package/src/modules/MediaGallery/index.tsx +1 -3
  102. package/src/modules/Settings/Globals/constants.tsx +942 -106
  103. package/src/modules/Sites/SitesList/AllSitesHeader/index.tsx +33 -0
  104. package/src/modules/Sites/SitesList/AllSitesHeader/style.tsx +35 -0
  105. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +5 -5
  106. package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +23 -119
  107. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +4 -4
  108. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +4 -3
  109. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +23 -120
  110. package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/index.tsx +4 -5
  111. package/src/modules/Sites/SitesList/RecentSites/index.tsx +49 -0
  112. package/src/modules/Sites/SitesList/RecentSites/style.tsx +92 -0
  113. package/src/modules/Sites/SitesList/SiteModal/index.tsx +8 -7
  114. package/src/modules/Sites/SitesList/WelcomeModal/DataStep/index.tsx +72 -0
  115. package/src/modules/Sites/SitesList/WelcomeModal/DataStep/style.tsx +59 -0
  116. package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/constants.tsx +78 -0
  117. package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +78 -0
  118. package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/style.tsx +141 -0
  119. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/index.tsx +93 -0
  120. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/style.tsx +77 -0
  121. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/index.tsx +100 -0
  122. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/style.tsx +94 -0
  123. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/index.tsx +44 -0
  124. package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/style.tsx +31 -0
  125. package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/index.tsx +51 -0
  126. package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/style.tsx +52 -0
  127. package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/index.tsx +40 -0
  128. package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/style.tsx +53 -0
  129. package/src/modules/Sites/SitesList/WelcomeModal/index.tsx +215 -0
  130. package/src/modules/Sites/SitesList/WelcomeModal/style.tsx +12 -0
  131. package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +26 -0
  132. package/src/modules/Sites/SitesList/atoms.tsx +4 -4
  133. package/src/modules/Sites/SitesList/hooks.tsx +149 -16
  134. package/src/modules/Sites/SitesList/index.tsx +127 -125
  135. package/src/modules/Sites/SitesList/style.tsx +1 -117
  136. package/src/modules/Sites/SitesList/utils.tsx +9 -2
  137. package/src/modules/Sites/index.tsx +19 -8
  138. package/src/modules/Users/Profile/index.tsx +169 -31
  139. package/src/modules/Users/Profile/style.tsx +81 -1
  140. package/src/modules/Users/Roles/RoleItem/index.tsx +2 -2
  141. package/src/modules/Users/UserCreate/SiteItem/index.tsx +11 -14
  142. package/src/modules/Users/UserForm/atoms.tsx +3 -3
  143. package/src/modules/Users/UserForm/index.tsx +25 -29
  144. package/src/modules/Users/UserForm/style.tsx +15 -2
  145. package/src/modules/Users/UserList/UserItem/index.tsx +4 -4
  146. package/src/routes/index.tsx +1 -0
  147. package/src/types/index.tsx +2 -0
  148. /package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/style.tsx +0 -0
@@ -1,20 +1,19 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import { IQueryValue } from "@ax/types";
3
-
4
- const useSortedListStatus = () => {
5
- const sortedInitialState: {
6
- isAscending: boolean;
7
- sortedByTitle: boolean;
8
- sortedByLastAccess: boolean;
9
- sortedByDateCreated: boolean;
10
- } = {
11
- isAscending: false,
12
- sortedByTitle: false,
13
- sortedByLastAccess: true,
14
- sortedByDateCreated: false,
15
- };
16
2
 
17
- const [sortedListStatus, setSortedListStatus] = useState(sortedInitialState);
3
+ import { useModal, usePermission } from "@ax/hooks";
4
+ import type { IGetSitesParams, IQueryValue, ISite } from "@ax/types";
5
+
6
+ import type { ISortedListStatus } from "./utils";
7
+
8
+ const sortedListDefaultState: ISortedListStatus = {
9
+ isAscending: false,
10
+ sortedByTitle: false,
11
+ sortedByLastAccess: true,
12
+ sortedByDateCreated: false,
13
+ };
14
+
15
+ const useSortedListStatus = (initialValue?: ISortedListStatus) => {
16
+ const [sortedListStatus, setSortedListStatus] = useState<ISortedListStatus>(initialValue || sortedListDefaultState);
18
17
 
19
18
  return {
20
19
  sortedListStatus,
@@ -111,4 +110,138 @@ interface ISitesFilterQuery {
111
110
  filterQuery: string;
112
111
  }
113
112
 
114
- export { useSortedListStatus, useFilterQuery, useIsMount };
113
+ interface IUseSiteActionsParams {
114
+ site: ISite;
115
+ setSiteInfo(currentSiteInfo: ISite): Promise<void>;
116
+ setHistoryPush(page: string, isEditor: boolean): void;
117
+ deleteSite(siteID: number, params?: IGetSitesParams): Promise<void>;
118
+ publishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
119
+ unpublishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
120
+ getParams: () => IGetSitesParams;
121
+ }
122
+
123
+ const publishedTooltip: Record<string, string> = {
124
+ active: "Live",
125
+ "upload-pending": "Publication pending",
126
+ offline: "Offline",
127
+ "offline-pending": "Offline pending",
128
+ };
129
+
130
+ const getPublishedState = (isPublished: boolean, updated: boolean): string => {
131
+ switch (isPublished) {
132
+ case true:
133
+ return updated ? "active" : "upload-pending";
134
+ case false:
135
+ return updated ? "offline" : "offline-pending";
136
+ }
137
+ };
138
+
139
+ const useSiteActions = ({
140
+ site,
141
+ setSiteInfo,
142
+ setHistoryPush,
143
+ deleteSite,
144
+ publishSite,
145
+ unpublishSite,
146
+ getParams,
147
+ }: IUseSiteActionsParams) => {
148
+ const { isOpen: isOpenDelete, toggleModal: toggleDeleteModal } = useModal();
149
+ const { isOpen: isOpenPublish, toggleModal: togglePublishModal } = useModal();
150
+ const [inputValue, setInputValue] = useState("");
151
+
152
+ const allowedToDeleteSite = usePermission("general.deleteSite");
153
+ const allowedToPublishSite = usePermission("general.publishSite");
154
+ const allowedToUnpublishSite = usePermission("general.unpublishSite");
155
+
156
+ const { updated, isPublished } = site;
157
+ const publishedState = getPublishedState(isPublished, updated);
158
+
159
+ const setSite = async () => {
160
+ await setSiteInfo(site);
161
+ setHistoryPush("/sites/pages", false);
162
+ };
163
+
164
+ const deleteOption = allowedToDeleteSite ? { label: "delete", icon: "delete", action: toggleDeleteModal } : undefined;
165
+
166
+ const publishOptionProps =
167
+ isPublished && allowedToUnpublishSite
168
+ ? { label: "Unpublish", icon: "offlinePending" }
169
+ : !isPublished && allowedToPublishSite
170
+ ? { label: "Publish", icon: "uploadPending" }
171
+ : null;
172
+
173
+ const publishOption = publishOptionProps
174
+ ? { label: publishOptionProps.label, icon: publishOptionProps.icon, action: togglePublishModal }
175
+ : undefined;
176
+
177
+ const menuOptions = [deleteOption, publishOption];
178
+
179
+ const handleDeleteSite = async () => {
180
+ const params = getParams();
181
+ await deleteSite(site.id, params);
182
+ toggleDeleteModal();
183
+ };
184
+
185
+ const mainDeleteAction = {
186
+ title: "Delete Site",
187
+ onClick: handleDeleteSite,
188
+ disabled: inputValue !== site.name.toUpperCase(),
189
+ };
190
+ const secondaryDeleteAction = { title: "Cancel", onClick: toggleDeleteModal };
191
+
192
+ const handlePublishSite = async () => {
193
+ const params = getParams();
194
+ await publishSite(site.id, params);
195
+ togglePublishModal();
196
+ };
197
+
198
+ const handleUnpublishSite = async () => {
199
+ const params = getParams();
200
+ await unpublishSite(site.id, params);
201
+ togglePublishModal();
202
+ };
203
+
204
+ const publishModal = isPublished
205
+ ? {
206
+ mainAction: { title: "Unpublish Site", onClick: handleUnpublishSite },
207
+ secondaryAction: { title: "Cancel", onClick: togglePublishModal },
208
+ title: "Unpublish Site",
209
+ content: (
210
+ <p>
211
+ You are going to unpublish <strong>{site.name}</strong> site.
212
+ <br />
213
+ This action can take several minutes.
214
+ </p>
215
+ ),
216
+ }
217
+ : {
218
+ mainAction: { title: "Publish Site", onClick: handlePublishSite },
219
+ secondaryAction: { title: "Cancel", onClick: togglePublishModal },
220
+ title: "Publish Site",
221
+ content: (
222
+ <p>
223
+ You are going to publish <strong>{site.name}</strong> site.
224
+ <br />
225
+ Make sure everything is ok before you do it.
226
+ </p>
227
+ ),
228
+ };
229
+
230
+ return {
231
+ publishedState,
232
+ publishedTooltip,
233
+ setSite,
234
+ menuOptions,
235
+ inputValue,
236
+ setInputValue,
237
+ isOpenDelete,
238
+ toggleDeleteModal,
239
+ mainDeleteAction,
240
+ secondaryDeleteAction,
241
+ isOpenPublish,
242
+ togglePublishModal,
243
+ publishModal,
244
+ };
245
+ };
246
+
247
+ export { useSortedListStatus, useFilterQuery, useIsMount, useSiteActions };
@@ -1,12 +1,10 @@
1
- import { useCallback, useEffect, useRef, useState } from "react";
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import {
5
5
  EmptyState,
6
6
  ErrorToast,
7
7
  FilterTagsBar,
8
- Icon,
9
- IconAction,
10
8
  Loading,
11
9
  MainWrapper,
12
10
  Modal,
@@ -26,20 +24,36 @@ import type {
26
24
  ISettingsForm,
27
25
  ISite,
28
26
  ISiteListConfig,
27
+ IUser,
29
28
  } from "@ax/types";
30
29
 
31
- import GridHeaderFilter from "./GridView/GridHeaderFilter";
30
+ import AllSitesHeader from "./AllSitesHeader";
32
31
  import GridSiteItem from "./GridView/GridSiteItem";
33
32
  import { useFilterQuery, useIsMount, useSortedListStatus } from "./hooks";
34
33
  import BulkHeader from "./ListView/BulkHeader";
35
34
  import ListSiteItem from "./ListView/ListSiteItem";
36
- import RecentSiteItem from "./RecentSiteItem";
35
+ import RecentSitesList from "./RecentSites";
37
36
  import SiteModal from "./SiteModal";
38
37
  import { filterByStatus, getSortedListStatus } from "./utils";
38
+ import WelcomeModal from "./WelcomeModal";
39
39
 
40
40
  import * as S from "./style";
41
41
 
42
- const SitesList = (props: ISitesListProps): JSX.Element => {
42
+ const initialState = {
43
+ name: "",
44
+ defaultLanguage: null,
45
+ path: "",
46
+ domain: null,
47
+ };
48
+
49
+ const itemsPerPage = 30;
50
+ const firstPage = 1;
51
+
52
+ const filterLabels = {
53
+ liveStatus: "Live",
54
+ };
55
+
56
+ const SitesList = (props: ISitesListProps) => {
43
57
  const {
44
58
  token,
45
59
  sites,
@@ -57,55 +71,71 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
57
71
  setHasAnimation,
58
72
  isLoading,
59
73
  isSitesLoading,
74
+ currentUser,
60
75
  } = props;
61
76
 
62
77
  const isMount = useIsMount();
63
- const initialState = {
64
- name: "",
65
- defaultLanguage: null,
66
- path: "",
67
- domain: null,
68
- };
69
-
70
- const itemsPerPage = 30;
71
- const firstPage = 1;
72
78
 
73
79
  const [page, setPage] = useState(firstPage);
74
- const [form, setForm] = useState(initialState);
75
- const currentConfig = localStorage.getItem("sitesConfig")
76
- ? JSON.parse(localStorage.getItem("sitesConfig") || "{}")
77
- : config;
80
+ const [form, setForm] = useState<ISettingsForm>(initialState);
81
+
82
+ const currentConfig = useMemo(() => {
83
+ const raw = localStorage.getItem("sitesConfig");
84
+ return raw ? JSON.parse(raw) : config;
85
+ }, [config]);
86
+
87
+ const initialHasAnimation = useRef(hasAnimation);
88
+ const currentConfigRef = useRef(currentConfig);
89
+ useLayoutEffect(() => {
90
+ currentConfigRef.current = currentConfig;
91
+ }, [currentConfig]);
92
+
78
93
  const [isRecentSitesListDisplayed, setIsRecentSitesListDisplayed] = useState<boolean>(
79
94
  currentConfig.displayRecentSites,
80
95
  );
81
- const [displayMode, setDisplayMode] = useState<string | "list" | "grid">(currentConfig.mode);
96
+
97
+ const [displayMode, setDisplayMode] = useState<"list" | "grid">(currentConfig.mode);
82
98
  const [searchQuery, setSearchQuery] = useState<string>("");
83
99
  const [isBulkLoading, setIsBulkLoading] = useState(false);
84
100
  const tableRef = useRef<HTMLDivElement>(null);
85
101
  const errorRef = useRef<HTMLDivElement>(null);
86
102
 
103
+ const scrollToTop = useCallback(() => {
104
+ if (tableRef.current) {
105
+ tableRef.current.scrollTo(0, 0);
106
+ }
107
+ }, []);
108
+
109
+ const handlePageChange = useCallback(
110
+ (newPage: number) => {
111
+ scrollToTop();
112
+ setPage(newPage);
113
+ },
114
+ [scrollToTop],
115
+ );
116
+
87
117
  const sitesIds = sites?.map((site: ISite) => site.id);
88
118
 
89
119
  const allowedToCreateSite = usePermission("general.createSite");
90
120
 
91
121
  const pagination = {
92
- setPage,
122
+ setPage: handlePageChange,
93
123
  itemsPerPage,
94
124
  totalItems,
95
125
  currPage: page,
96
126
  };
97
127
 
98
128
  const { isOpen, toggleModal } = useModal();
129
+ const { isOpen: isWelcomeOpen, toggleModal: toggleWelcomeModal } = useModal();
99
130
 
100
- const openModal = () => toggleModal();
101
131
  const rightButtonProps = allowedToCreateSite
102
132
  ? {
103
133
  label: "New",
104
- action: openModal,
134
+ action: toggleModal,
105
135
  }
106
136
  : undefined;
107
137
 
108
- const { setSortedListStatus } = useSortedListStatus();
138
+ const { sortedListStatus, setSortedListStatus } = useSortedListStatus(currentConfig.sortedListStatus);
109
139
  const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery } = useFilterQuery(
110
140
  currentConfig.filterValues,
111
141
  );
@@ -126,6 +156,7 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
126
156
  const getSiteList = useCallback(async () => {
127
157
  if (token) {
128
158
  const params = getParams();
159
+ // On first mount, token is required for initial authentication
129
160
  if (isMount) {
130
161
  await getSites({ ...params, token });
131
162
  } else {
@@ -134,19 +165,19 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
134
165
  }
135
166
  }, [getParams, getSites, isMount, token]);
136
167
 
137
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
138
168
  useEffect(() => {
139
169
  getSiteList();
140
- }, [filterQuery, searchQuery, page]);
170
+ }, [getSiteList]);
141
171
 
142
- // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
143
172
  useEffect(() => {
144
- if (tableRef.current) {
145
- tableRef.current.scrollTo(0, 0);
146
- const updatedConfig = { ...currentConfig, filterValues };
147
- setListConfig(updatedConfig);
173
+ if (!initialHasAnimation.current && !currentUser.profileCreated) {
174
+ toggleWelcomeModal();
148
175
  }
149
- }, [page, searchQuery, filterValues]);
176
+ }, [toggleWelcomeModal, currentUser]);
177
+
178
+ useEffect(() => {
179
+ setListConfig({ ...currentConfigRef.current, filterValues });
180
+ }, [filterValues, setListConfig]);
150
181
 
151
182
  useEffect(() => {
152
183
  const errorCode = error?.code;
@@ -163,27 +194,27 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
163
194
  }
164
195
  };
165
196
 
166
- const mandatoryKeysLength = 4;
167
197
  const isSubmitDisabled =
168
- Object.values(form).filter((el: any) => (typeof el === "string" ? el.trim().length : el)).length !==
169
- mandatoryKeysLength;
198
+ Object.values(form).filter((el) => (typeof el === "string" ? el.trim().length : el)).length !==
199
+ Object.keys(initialState).length;
170
200
 
171
201
  const mainAction = { title: "Create site", onClick: saveSiteSettings, disabled: isSubmitDisabled };
172
202
  const secondaryAction = { title: "Cancel", onClick: toggleModal };
173
203
 
174
204
  const toggleRecentSites = () => {
175
- const updatedConfig = { ...config, displayRecentSites: !isRecentSitesListDisplayed };
205
+ const updatedConfig = { ...currentConfig, displayRecentSites: !isRecentSitesListDisplayed };
176
206
  setListConfig(updatedConfig);
177
207
  setIsRecentSitesListDisplayed(!isRecentSitesListDisplayed);
178
208
  };
179
209
 
180
210
  const changeDisplayMode = (mode: "grid" | "list") => {
181
- const updatedConfig = { ...config, mode };
211
+ const updatedConfig = { ...currentConfig, mode };
182
212
  setListConfig(updatedConfig);
183
213
  setDisplayMode(mode);
184
214
  };
185
215
 
186
216
  const sortItems = (orderPointer: IQueryValue[], isAscending: boolean) => {
217
+ scrollToTop();
187
218
  setPage(firstPage);
188
219
  const orderString = orderPointer[0].value.toString();
189
220
  const sortedState = getSortedListStatus(orderString, isAscending);
@@ -195,62 +226,13 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
195
226
  };
196
227
 
197
228
  const filterItems = async (filterPointer: string, filtersSelected: IQueryValue[]) => {
229
+ scrollToTop();
198
230
  setPage(firstPage);
199
231
  setFiltersSelection(filterPointer, filtersSelected);
200
232
  const updatedConfig = { ...currentConfig, filter: filterQuery };
201
233
  setListConfig(updatedConfig);
202
234
  };
203
235
 
204
- const RecentSitesHeader = () => (
205
- <S.SectionHeader data-testid="recent-sites-header" isRecentSites={true}>
206
- <S.Title data-testid="recent-sites-title" isActive={isRecentSitesListDisplayed}>
207
- Recent sites
208
- </S.Title>
209
- <S.CollapseButton data-testid="recent-sites-collapse-button" onClick={toggleRecentSites}>
210
- {isRecentSitesListDisplayed ? (
211
- <>
212
- <S.Label data-testid="recent-sites-hide-label">Hide recent sites </S.Label> <Icon name="UpArrow" />
213
- </>
214
- ) : (
215
- <>
216
- <S.Label data-testid="recent-sites-show-label">Show recent sites </S.Label> <Icon name="DownArrow" />
217
- </>
218
- )}
219
- </S.CollapseButton>
220
- </S.SectionHeader>
221
- );
222
-
223
- const RecentSitesList = () => {
224
- return (
225
- <S.RecentSites data-testid="recent-sites-list" fullWidth={recentSites.length >= 5}>
226
- <RecentSitesHeader />
227
- <S.RecentSitesItemsWrapper data-testid="recent-sites-items-wrapper" isHidden={!isRecentSitesListDisplayed}>
228
- {recentSites.map((site: ISite, i: number) => (
229
- <RecentSiteItem key={i} site={site} />
230
- ))}
231
- </S.RecentSitesItemsWrapper>
232
- </S.RecentSites>
233
- );
234
- };
235
-
236
- const AllSitesHeader = () => (
237
- <S.SectionHeader data-testid="all-sites-header">
238
- <S.Title isActive={true} data-testid="all-sites-title">
239
- All sites
240
- </S.Title>
241
- <S.HeaderIconsWrapper data-testid="all-sites-header-icons">
242
- {displayMode === "grid" ? (
243
- <S.FilterSelect data-testid="all-sites-grid-filter">
244
- <S.FilterSelectLabel data-testid="all-sites-grid-filter-label">Sort by:</S.FilterSelectLabel>{" "}
245
- <GridHeaderFilter sortItems={sortItems} sortedState={currentConfig.sortedListStatus} />{" "}
246
- </S.FilterSelect>
247
- ) : null}
248
- <IconAction icon="Grid2" onClick={() => changeDisplayMode("grid")} active={displayMode === "grid"} />
249
- <IconAction icon="BulletList" onClick={() => changeDisplayMode("list")} active={displayMode === "list"} />
250
- </S.HeaderIconsWrapper>
251
- </S.SectionHeader>
252
- );
253
-
254
236
  const {
255
237
  resetBulkSelection,
256
238
  selectedItems,
@@ -262,15 +244,16 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
262
244
  setHoverCheck,
263
245
  } = useBulkSelection(sitesIds);
264
246
 
265
- const bulkFilter = (bulkSelection: number[]) => filterByStatus(bulkSelection, sites);
247
+ const bulkFilter = useCallback((bulkSelection: number[]) => filterByStatus(bulkSelection, sites), [sites]);
266
248
 
267
- const handleAddToBulk = (item: ICheck) => addToBulkSelection(item, bulkFilter);
249
+ const handleAddToBulk = useCallback(
250
+ (item: ICheck) => addToBulkSelection(item, bulkFilter),
251
+ [addToBulkSelection, bulkFilter],
252
+ );
268
253
 
269
254
  const handleSelectAll = () => selectAllItems(bulkFilter);
270
255
 
271
- const unselectAllItems = () => resetBulkSelection();
272
-
273
- const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
256
+ const selectItems = () => (checkState.isAllSelected ? resetBulkSelection() : handleSelectAll());
274
257
 
275
258
  const bulkPublishAction = async (isPublish: boolean) => {
276
259
  setIsBulkLoading(true);
@@ -283,7 +266,7 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
283
266
  if (published.length > 0 && !isPublish) {
284
267
  await unpublishSitesBulk(published, params);
285
268
  }
286
- unselectAllItems();
269
+ resetBulkSelection();
287
270
  setIsBulkLoading(false);
288
271
  };
289
272
 
@@ -300,7 +283,7 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
300
283
  totalItems={totalItems}
301
284
  selectItems={selectItems}
302
285
  sortItems={sortItems}
303
- sortedListStatus={currentConfig.sortedListStatus}
286
+ sortedListStatus={sortedListStatus}
304
287
  filterItems={filterItems}
305
288
  filterValues={currentConfig.filterValues}
306
289
  setHoverCheck={setHoverCheck}
@@ -309,28 +292,31 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
309
292
  />
310
293
  );
311
294
 
312
- const mappedSites =
313
- sites.length > 0 ? (
314
- sites.map((site: ISite) => {
315
- const isItemSelected = isSelected(site.id);
316
- return displayMode === "grid" ? (
317
- <GridSiteItem key={site.id} site={site} getParams={getParams} />
318
- ) : (
319
- <ListSiteItem
320
- key={site.id}
321
- site={site}
322
- isSelected={isItemSelected}
323
- onCheck={handleAddToBulk}
324
- getParams={getParams}
325
- hoverCheck={checkState.hoverCheck}
326
- />
327
- );
328
- })
329
- ) : (
330
- <S.EmptyStateWrapper data-testid="empty-state">
331
- <EmptyState icon="search" message="We couldn't find what you are looking for. Please, try another search." />
332
- </S.EmptyStateWrapper>
333
- );
295
+ const mappedSites = useMemo(
296
+ () =>
297
+ sites.length > 0 ? (
298
+ sites.map((site: ISite) => {
299
+ const isItemSelected = isSelected(site.id);
300
+ return displayMode === "grid" ? (
301
+ <GridSiteItem key={site.id} site={site} getParams={getParams} />
302
+ ) : (
303
+ <ListSiteItem
304
+ key={site.id}
305
+ site={site}
306
+ isSelected={isItemSelected}
307
+ onCheck={handleAddToBulk}
308
+ getParams={getParams}
309
+ hoverCheck={checkState.hoverCheck}
310
+ />
311
+ );
312
+ })
313
+ ) : (
314
+ <S.EmptyStateWrapper data-testid="empty-state">
315
+ <EmptyState icon="search" message="We couldn't find what you are looking for. Please, try another search." />
316
+ </S.EmptyStateWrapper>
317
+ ),
318
+ [sites, displayMode, isSelected, handleAddToBulk, getParams, checkState.hoverCheck],
319
+ );
334
320
 
335
321
  const showPagination = totalItems > itemsPerPage;
336
322
 
@@ -344,17 +330,14 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
344
330
  </S.GridList>
345
331
  {showPagination && (
346
332
  <S.PaginationWrapper>
347
- <Pagination totalItems={totalItems} setPage={setPage} itemsPerPage={itemsPerPage} currPage={page} />
333
+ <Pagination totalItems={totalItems} setPage={handlePageChange} itemsPerPage={itemsPerPage} currPage={page} />
348
334
  </S.PaginationWrapper>
349
335
  )}
350
336
  </>
351
337
  );
352
338
 
353
- const filterLabels = {
354
- liveStatus: "Live",
355
- };
356
-
357
339
  const handleSearch = (query: string) => {
340
+ scrollToTop();
358
341
  setSearchQuery(query);
359
342
  setPage(1);
360
343
  };
@@ -376,7 +359,12 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
376
359
  </TableList>
377
360
  );
378
361
 
379
- const handleAnimationEnd = () => setHasAnimation(false);
362
+ const handleAnimationEnd = () => {
363
+ setHasAnimation(false);
364
+ if (!currentUser.profileCreated) {
365
+ toggleWelcomeModal();
366
+ }
367
+ };
380
368
 
381
369
  if (isLoading && !hasAnimation) {
382
370
  return <Loading />;
@@ -392,13 +380,24 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
392
380
  >
393
381
  <ErrorToast ref={errorRef} />
394
382
  <S.SitesListWrapper className={hasAnimation ? "animate" : ""}>
395
- {recentSites?.length > 0 ? <RecentSitesList /> : null}
396
- <AllSitesHeader />
383
+ {recentSites?.length > 0 ? (
384
+ <RecentSitesList
385
+ recentSites={recentSites}
386
+ isRecentSitesListDisplayed={isRecentSitesListDisplayed}
387
+ toggleRecentSites={toggleRecentSites}
388
+ />
389
+ ) : null}
390
+ <AllSitesHeader
391
+ displayMode={displayMode}
392
+ sortItems={sortItems}
393
+ sortedListStatus={sortedListStatus}
394
+ changeDisplayMode={changeDisplayMode}
395
+ />
397
396
  {isSitesLoading && !hasAnimation ? (
398
397
  <Loading />
399
398
  ) : (
400
399
  <S.AllSitesListWrapper className={hasAnimation ? "animate" : ""} onAnimationEnd={handleAnimationEnd}>
401
- {displayMode === "grid" ? <>{gridList}</> : <>{listTable}</>}
400
+ {displayMode === "grid" ? gridList : listTable}
402
401
  </S.AllSitesListWrapper>
403
402
  )}
404
403
  </S.SitesListWrapper>
@@ -412,6 +411,7 @@ const SitesList = (props: ISitesListProps): JSX.Element => {
412
411
  >
413
412
  <SiteModal form={form} setForm={setForm} />
414
413
  </Modal>
414
+ <WelcomeModal isOpen={isWelcomeOpen} toggleModal={toggleWelcomeModal} />
415
415
  </MainWrapper>
416
416
  );
417
417
  };
@@ -426,6 +426,7 @@ interface ISitesProps {
426
426
  hasAnimation: boolean;
427
427
  isLoading: boolean;
428
428
  isSitesLoading: boolean;
429
+ currentUser: IUser;
429
430
  }
430
431
 
431
432
  const mapStateToProps = (state: IRootState) => ({
@@ -438,6 +439,7 @@ const mapStateToProps = (state: IRootState) => ({
438
439
  hasAnimation: state.app.hasAnimation,
439
440
  isLoading: state.app.isLoading,
440
441
  isSitesLoading: state.sites.isSitesLoading,
442
+ currentUser: state.users.currentUser!,
441
443
  });
442
444
 
443
445
  interface IDispatchProps {