@griddo/ax 1.75.92 → 1.75.93

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 (57) hide show
  1. package/package.json +2 -2
  2. package/src/__mocks__/store/SitesList.ts +24 -0
  3. package/src/__tests__/components/Avatar/__snapshots__/Avatar.test.tsx.snap +10 -10
  4. package/src/__tests__/modules/Sites/Sites.test.tsx +3 -0
  5. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +8 -1
  6. package/src/components/ErrorCenter/index.tsx +10 -1
  7. package/src/components/Fields/ComponentContainer/index.tsx +1 -1
  8. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +34 -19
  9. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/style.tsx +5 -0
  10. package/src/components/Notification/style.tsx +2 -2
  11. package/src/components/Tooltip/style.tsx +1 -1
  12. package/src/containers/App/actions.tsx +2 -0
  13. package/src/containers/Gallery/actions.tsx +19 -10
  14. package/src/containers/PageEditor/actions.tsx +19 -22
  15. package/src/containers/PageEditor/utils.tsx +5 -5
  16. package/src/containers/Sites/actions.tsx +35 -8
  17. package/src/containers/Sites/constants.tsx +1 -0
  18. package/src/containers/Sites/interfaces.tsx +8 -2
  19. package/src/containers/Sites/reducer.tsx +18 -1
  20. package/src/containers/Users/actions.tsx +23 -2
  21. package/src/forms/validators.tsx +1 -3
  22. package/src/helpers/requests.tsx +1 -1
  23. package/src/hooks/bulk.tsx +2 -1
  24. package/src/hooks/content.tsx +27 -2
  25. package/src/hooks/index.tsx +4 -1
  26. package/src/hooks/window.ts +16 -0
  27. package/src/modules/App/Routing/NavMenu/index.tsx +3 -2
  28. package/src/modules/App/Routing/NavMenu/style.tsx +2 -2
  29. package/src/modules/Content/PageItem/index.tsx +18 -10
  30. package/src/modules/Content/PageItem/style.tsx +26 -4
  31. package/src/modules/Content/index.tsx +41 -5
  32. package/src/modules/Content/utils.tsx +5 -2
  33. package/src/modules/CreatePass/index.tsx +8 -11
  34. package/src/modules/FramePreview/index.tsx +2 -6
  35. package/src/modules/GlobalEditor/PageBrowser/index.tsx +2 -4
  36. package/src/modules/GlobalEditor/index.tsx +13 -15
  37. package/src/modules/PageEditor/Editor/index.tsx +1 -3
  38. package/src/modules/PageEditor/PageBrowser/index.tsx +2 -6
  39. package/src/modules/PageEditor/index.tsx +23 -20
  40. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/ConfigPanel/Field/index.tsx +1 -1
  41. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +3 -3
  42. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/index.tsx +17 -10
  43. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +23 -25
  44. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/style.tsx +3 -3
  45. package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +2 -2
  46. package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +1 -1
  47. package/src/modules/Sites/SitesList/atoms.tsx +1 -1
  48. package/src/modules/Sites/SitesList/hooks.tsx +3 -4
  49. package/src/modules/Sites/SitesList/index.tsx +47 -19
  50. package/src/modules/Sites/SitesList/style.tsx +7 -1
  51. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +11 -6
  52. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +10 -1
  53. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +1 -1
  54. package/src/modules/StructuredData/StructuredDataList/index.tsx +25 -1
  55. package/src/modules/StructuredData/StructuredDataList/utils.tsx +5 -3
  56. package/src/modules/Users/UserCreate/index.tsx +4 -1
  57. package/src/types/index.tsx +21 -2
@@ -13,6 +13,7 @@ export const SET_INITIAL_VALUES = `${NAME}/SET_INITIAL_VALUES`;
13
13
  export const SET_SAVED_SITE_INFO = `${NAME}/SET_SAVED_SITE_INFO`;
14
14
  export const SET_CURRENT_SITE_ERROR_PAGES = `${NAME}/SET_CURRENT_SITE_ERROR_PAGES`;
15
15
  export const SET_CONTENT_FILTERS = `${NAME}/SET_CONTENT_FILTERS`;
16
+ export const SET_CONFIG = `${NAME}/SET_CONFIG`;
16
17
 
17
18
  export const ITEMS_PER_PAGE = 50;
18
19
 
@@ -12,8 +12,9 @@ import {
12
12
  SET_CURRENT_SITE_ERROR_PAGES,
13
13
  SET_CONTENT_FILTERS,
14
14
  SET_SITES_TOTAL_ITEMS,
15
+ SET_CONFIG
15
16
  } from "./constants";
16
- import { ISite } from "@ax/types";
17
+ import { ISite, ISiteListConfig } from "@ax/types";
17
18
 
18
19
  export interface ISetFilter {
19
20
  type: typeof SET_FILTER;
@@ -77,7 +78,12 @@ export interface ISetCurrentSiteErrorPages {
77
78
 
78
79
  export interface ISetContentFilters {
79
80
  type: typeof SET_CONTENT_FILTERS;
80
- payload: { contentFilters: Record<string,string> | null };
81
+ payload: { contentFilters: Record<string, string> | null };
82
+ }
83
+ export interface ISetConfig {
84
+ type: typeof SET_CONFIG;
85
+ payload: { config: ISiteListConfig };
81
86
  }
82
87
 
83
88
  export type SitesActionsCreators = ISetSitesAction & ISetCurrentSiteInfoAction;
89
+
@@ -12,9 +12,10 @@ import {
12
12
  SET_SAVED_SITE_INFO,
13
13
  SET_CURRENT_SITE_ERROR_PAGES,
14
14
  SET_CONTENT_FILTERS,
15
+ SET_CONFIG,
15
16
  } from "./constants";
16
17
 
17
- import { ISite, IPage, ILanguage } from "@ax/types";
18
+ import { ISite, IPage, ILanguage, ISiteListConfig } from "@ax/types";
18
19
 
19
20
  import { SitesActionsCreators } from "./interfaces";
20
21
 
@@ -32,8 +33,22 @@ export interface ISitesState {
32
33
  savedSiteInfo: any;
33
34
  currentSiteErrorPages: number[];
34
35
  contentFilters: Record<string, string> | null;
36
+ config: ISiteListConfig;
35
37
  }
36
38
 
39
+ const config = {
40
+ displayRecentSites: false,
41
+ mode: "grid",
42
+ filter: "&order=lastAccess-desc",
43
+ filterValues: { order: "desc", liveStatus: "all" },
44
+ sortedListStatus: {
45
+ isAscending: false,
46
+ sortedByDateCreated: false,
47
+ sortedByLastAccess: true,
48
+ sortedByTitle: false,
49
+ },
50
+ };
51
+
37
52
  export const initialState = {
38
53
  currentSiteName: null,
39
54
  currentSitePages: [],
@@ -48,6 +63,7 @@ export const initialState = {
48
63
  savedSiteInfo: null,
49
64
  currentSiteErrorPages: [],
50
65
  contentFilters: null,
66
+ config,
51
67
  };
52
68
 
53
69
  export function reducer(state = initialState, action: SitesActionsCreators): ISitesState {
@@ -65,6 +81,7 @@ export function reducer(state = initialState, action: SitesActionsCreators): ISi
65
81
  case SET_SAVED_SITE_INFO:
66
82
  case SET_CURRENT_SITE_ERROR_PAGES:
67
83
  case SET_CONTENT_FILTERS:
84
+ case SET_CONFIG:
68
85
  return { ...state, ...action.payload };
69
86
  default:
70
87
  return state;
@@ -2,8 +2,8 @@ import { Dispatch } from "redux";
2
2
  import { SET_USERS, SET_USER_FORM, SET_CURRENT_USER } from "./constants";
3
3
 
4
4
  import { ISetUsers, ISetCurrentUser, ISetUserForm } from "./interfaces";
5
- import { IUser } from "@ax/types";
6
- import { users } from "@ax/api";
5
+ import { ICreatePasswordParams, IUser } from "@ax/types";
6
+ import { global, users } from "@ax/api";
7
7
  import { appActions } from "@ax/containers/App";
8
8
  import { handleRequest, isReqOk } from "@ax/helpers";
9
9
 
@@ -130,6 +130,26 @@ function createUser(data: { name: string; email: string; sites: any[] }): (dispa
130
130
  };
131
131
  }
132
132
 
133
+ function createPassword(id: string, params: ICreatePasswordParams): (dispatch: Dispatch) => Promise<boolean> {
134
+ return async (dispatch) => {
135
+ try {
136
+ const callback = async () => global.createPassword(id, params);
137
+
138
+ const responseActions = {
139
+ handleSuccess: async (response: any) => {
140
+ await getUser("me", response.token)(dispatch);
141
+ },
142
+ handleError: (response: any) => appActions.handleError(response)(dispatch),
143
+ };
144
+
145
+ return await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
146
+ } catch (e) {
147
+ console.log(e); // TODO: capturar error bien
148
+ return false;
149
+ }
150
+ };
151
+ }
152
+
133
153
  function deleteUser(id: number | number[]): (dispatch: Dispatch) => Promise<boolean> {
134
154
  return async (dispatch) => {
135
155
  try {
@@ -201,4 +221,5 @@ export {
201
221
  setCurrentUser,
202
222
  deleteUser,
203
223
  resendInvitation,
224
+ createPassword,
204
225
  };
@@ -338,9 +338,7 @@ const findPackagesActivationErrors = (
338
338
  let isCurrentTemplateActivated = true;
339
339
  let hasDeactivatedModules = false;
340
340
 
341
- const {
342
- editorContent: { template },
343
- } = pageEditor?.editorContent;
341
+ const { template } = pageEditor?.editorContent;
344
342
 
345
343
  if (template && !isGlobal) {
346
344
  const mainContentModules = template?.mainContent?.modules;
@@ -24,7 +24,7 @@ function handleRequest(callback: any, responseActions: any, loadingActions: any[
24
24
  }
25
25
  })
26
26
 
27
- if (result) handleSuccess(response.data);
27
+ if (result) await handleSuccess(response.data);
28
28
 
29
29
  loadingActions.map(action => dispatch(action(false)));
30
30
 
@@ -4,10 +4,11 @@ import { areEquals } from "@ax/helpers";
4
4
  import { ICheck } from "@ax/types";
5
5
 
6
6
  const useBulkSelection = (itemIDs: any[]) => {
7
- const selectedItemsInitialState: { all: any[]; notPublished: any[]; published: any[] } = {
7
+ const selectedItemsInitialState: { all: any[]; notPublished: any[]; published: any[]; drafts: any[] } = {
8
8
  all: [],
9
9
  notPublished: [],
10
10
  published: [],
11
+ drafts: [],
11
12
  };
12
13
 
13
14
  const [items, setItems] = useState<any[]>([]);
@@ -1,4 +1,5 @@
1
- import { useState } from "react";
1
+ import { useEffect, useLayoutEffect, useState } from "react";
2
+ import { useWindowSize } from "./window";
2
3
 
3
4
  const useCategoryColors = (): any => {
4
5
  const fixedColors = [
@@ -38,4 +39,28 @@ const useCategoryColors = (): any => {
38
39
  };
39
40
  };
40
41
 
41
- export { useCategoryColors };
42
+ const useAdaptiveText = (
43
+ containerRef: React.RefObject<HTMLDivElement>,
44
+ fullText: string,
45
+ padding = 0
46
+ ): { text: string; width: number } => {
47
+ const [output, setTitle] = useState({ text: "", width: 0 });
48
+
49
+ const [width] = useWindowSize();
50
+
51
+ useEffect(() => {
52
+ setTitle({ text: "", width: 0 });
53
+ }, [width]);
54
+
55
+ useLayoutEffect(() => {
56
+ if (containerRef.current) {
57
+ const cellWidth = containerRef.current.offsetWidth;
58
+ const cellPadding = padding * 2;
59
+ setTitle({ text: fullText, width: cellWidth - cellPadding });
60
+ }
61
+ }, [containerRef, fullText, output.width, padding]);
62
+
63
+ return output;
64
+ };
65
+
66
+ export { useCategoryColors, useAdaptiveText };
@@ -2,7 +2,8 @@ import { useBulkSelection } from "./bulk";
2
2
  import { useDebounce, useEqualStructured, useIsDirty, usePrevious } from "./forms";
3
3
  import { useHandleClickOutside, useModal, useToast } from "./modals";
4
4
  import { useURLSearchParam } from "./location";
5
- import { useCategoryColors } from "./content";
5
+ import { useCategoryColors, useAdaptiveText } from "./content";
6
+ import { useWindowSize } from "./window";
6
7
  import { useOnMessageReceivedFromIframe, useOnMessageReceivedFromOutside } from "./iframe";
7
8
 
8
9
  export {
@@ -16,6 +17,8 @@ export {
16
17
  useBulkSelection,
17
18
  useURLSearchParam,
18
19
  useCategoryColors,
20
+ useWindowSize,
21
+ useAdaptiveText,
19
22
  useOnMessageReceivedFromIframe,
20
23
  useOnMessageReceivedFromOutside,
21
24
  };
@@ -0,0 +1,16 @@
1
+ import { useLayoutEffect, useState } from "react";
2
+
3
+ const useWindowSize = (): number[] => {
4
+ const [size, setSize] = useState([0, 0]);
5
+ useLayoutEffect(() => {
6
+ const updateSize = () => {
7
+ setSize([window.innerWidth, window.innerHeight]);
8
+ };
9
+ window.addEventListener("resize", updateSize);
10
+ updateSize();
11
+ return () => window.removeEventListener("resize", updateSize);
12
+ }, []);
13
+ return size;
14
+ };
15
+
16
+ export { useWindowSize };
@@ -40,6 +40,7 @@ const NavMenu = (props: IProps) => {
40
40
 
41
41
  const sitesPath = "/sites/";
42
42
  const isSite = location.pathname.includes(sitesPath) && location.pathname.length > sitesPath.length;
43
+ const siteSelector = location.pathname === "/sites";
43
44
 
44
45
  const goToPublishedSite = () => {
45
46
  const language = siteLanguages.find((l) => l.id === lang.id);
@@ -49,7 +50,7 @@ const NavMenu = (props: IProps) => {
49
50
  };
50
51
 
51
52
  const goToSites = () => {
52
- isSite && setHistoryPush("/sites");
53
+ !siteSelector && setHistoryPush("/sites");
53
54
  };
54
55
 
55
56
  const goToProfile = () => {
@@ -125,7 +126,7 @@ const NavMenu = (props: IProps) => {
125
126
  return (
126
127
  <NavProvider value={{ state, toggleSubmenu }}>
127
128
  <S.NavWrapper type={config.type} isOpened={isOpened}>
128
- <S.Home type={config.type} onClick={goToSites} isSite={isSite} isOpened={isOpened}>
129
+ <S.Home type={config.type} onClick={goToSites} isSite={isSite} isOpened={isOpened} siteSelector={siteSelector}>
129
130
  {isSite && (
130
131
  <S.GoBack>
131
132
  <Icon name="LeftArrow" />
@@ -14,7 +14,7 @@ export const GoBack = styled.div`
14
14
  width: auto;
15
15
  `;
16
16
 
17
- export const Home = styled.div<{ type: string; isSite: boolean; isOpened: boolean }>`
17
+ export const Home = styled.div<{ type: string; isSite: boolean; isOpened: boolean; siteSelector: boolean }>`
18
18
  padding: ${(p) => `${p.theme.spacing.xs} 12px`};
19
19
  background-color: ${(p) =>
20
20
  p.type === "multisite" ? p.theme.color.uiMainMenuBackground : p.theme.color.uiBarBackground};
@@ -28,7 +28,7 @@ export const Home = styled.div<{ type: string; isSite: boolean; isOpened: boolea
28
28
  & + ul {
29
29
  margin-top: ${(p) => p.theme.spacing.s};
30
30
  }
31
- cursor: ${(p) => (p.isSite ? "pointer" : "default")};
31
+ cursor: ${(p) => (p.siteSelector ? "default" : "pointer")};
32
32
 
33
33
  &:hover {
34
34
  & > ${NavLink} {
@@ -1,12 +1,11 @@
1
- import React, { memo, useState } from "react";
2
-
1
+ import React, { memo, useRef, useState } from "react";
3
2
  import { schemas } from "components";
3
+ import { useTheme } from "styled-components";
4
4
 
5
- import { useModal } from "@ax/hooks";
5
+ import { useAdaptiveText, useModal } from "@ax/hooks";
6
6
  import { getHumanLastModifiedDate, getTemplateDisplayName, slugify } from "@ax/helpers";
7
7
  import { IPage, ISite, ISavePageParams, ICheck, IColumn, IPageLanguage, IDataPack } from "@ax/types";
8
8
  import { pageStatus, ISetCurrentPageIDAction } from "@ax/containers/PageEditor/interfaces";
9
-
10
9
  import {
11
10
  CheckField,
12
11
  FieldsBehavior,
@@ -81,6 +80,12 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
81
80
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
82
81
  const { isOpen: isCopyOpen, toggleModal: toggleCopyModal } = useModal();
83
82
 
83
+ const nameCellRef = useRef<HTMLDivElement>(null);
84
+ const theme: any = useTheme();
85
+ const nameCellPadding = Number(theme.spacing.s.slice(0, -2));
86
+ const title = useAdaptiveText(nameCellRef, page.title, nameCellPadding);
87
+ const path = useAdaptiveText(nameCellRef, fullPath.page, nameCellPadding);
88
+
84
89
  const currentTemplateDataPacks = schemas.templates[templateId].dataPacks;
85
90
 
86
91
  const isCopyable = !currentTemplateDataPacks;
@@ -472,12 +477,15 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
472
477
  <S.CheckCell role="cell">
473
478
  <CheckField name="check" value={page.id} checked={isSelected} onChange={handleOnChange} />
474
479
  </S.CheckCell>
475
- <S.NameCell role="cell" onClick={goToPage}>
476
- <S.Title>
477
- {page.title}
478
- {isHome && <Icon name="home" />}
479
- </S.Title>
480
- <S.Slug>{fullPath && fullPath.page}</S.Slug>
480
+ <S.NameCell role="cell" onClick={goToPage} ref={nameCellRef}>
481
+ {isHome && (
482
+ <S.Home>
483
+ <Icon name="home" />
484
+ Home
485
+ </S.Home>
486
+ )}
487
+ <S.Title width={title.width}>{title.text}</S.Title>
488
+ {!isHome && <S.Slug width={path.width}>{path.text}</S.Slug>}
481
489
  </S.NameCell>
482
490
  {CategoryColumns}
483
491
  {activeColumns.includes("type") && (
@@ -24,14 +24,23 @@ const NameCell = styled(Cell)`
24
24
  }
25
25
  `;
26
26
 
27
- const Title = styled.div`
27
+ const Title = styled.div<{ width: number }>`
28
28
  ${(p) => p.theme.textStyle.uiL};
29
29
  color: ${(p) => p.theme.color.textHighEmphasis};
30
- display: flex;
31
- align-items: center;
30
+ display: inline;
31
+ overflow: hidden;
32
+ white-space: nowrap;
33
+ text-overflow: ellipsis;
34
+ width: ${(p) => `${p.width}px`};
32
35
  `;
33
36
 
34
- const Slug = styled.div``;
37
+ const Slug = styled.div<{ width: number }>`
38
+ display: inline;
39
+ overflow: hidden;
40
+ white-space: nowrap;
41
+ text-overflow: ellipsis;
42
+ width: ${(p) => `${p.width}px`};
43
+ `;
35
44
 
36
45
  const TypeCell = styled(Cell)`
37
46
  flex: 0 0 170px;
@@ -84,6 +93,18 @@ const Mark = styled.div`
84
93
  align-items: center;
85
94
  `;
86
95
 
96
+ const Home = styled.span`
97
+ ${(p) => p.theme.textStyle.uiXS};
98
+ width: 63px;
99
+ height: 20px;
100
+ margin-bottom: 8px;
101
+ background-color: ${(p) => p.theme.color.uiBackground03};
102
+ display: flex;
103
+ justify-content: center;
104
+ align-items: center;
105
+ padding: 2px 10px 2px 4px;
106
+ `;
107
+
87
108
  const Wrapper = styled.div`
88
109
  cursor: pointer;
89
110
  `;
@@ -191,4 +212,5 @@ export {
191
212
  ModalContent,
192
213
  GlobalCell,
193
214
  Mark,
215
+ Home,
194
216
  };
@@ -119,6 +119,10 @@ const Content = (props: IProps): JSX.Element => {
119
119
  query: currentFilterQuery,
120
120
  } = useFilterQuery(contentFilters);
121
121
  const { state: locationState } = useLocation<{ isFromEditor: boolean }>();
122
+ const [notification, setNotification] = useState<{
123
+ text: string;
124
+ subErrors?: { id: number; error: string }[];
125
+ } | null>(null);
122
126
 
123
127
  const currentFilter = getCurrentFilter(structuredData, filter);
124
128
  const checkFromPage = currentFilter ? currentFilter.fromPage : undefined;
@@ -169,6 +173,7 @@ const Content = (props: IProps): JSX.Element => {
169
173
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
170
174
  const [arePagesTranslated, setArePagesTranslated] = useState(false);
171
175
  const [templateInstanceError, setTemplateInstanceError] = useState({ error: false, templateName: "" });
176
+ const [pagesSelected, setPagesSelected] = useState<any[]>([]);
172
177
 
173
178
  const {
174
179
  resetBulkSelection,
@@ -302,11 +307,22 @@ const Content = (props: IProps): JSX.Element => {
302
307
 
303
308
  const bulkFilter = (bulkSelection: number[]) => filterByStatus(bulkSelection, currentSitePages);
304
309
 
305
- const handleAddToBulk = (item: ICheck) => addToBulkSelection(item, bulkFilter);
310
+ const handleAddToBulk = (item: ICheck) => {
311
+ const page = currentSitePages.find((page: IPage) => page.id === item.value);
312
+
313
+ item.isChecked && !pagesSelected.includes(item)
314
+ ? setPagesSelected((pagesSelected) => [...pagesSelected, page])
315
+ : setPagesSelected(pagesSelected.filter((page: IPage) => page.id !== item.value));
316
+
317
+ addToBulkSelection(item, bulkFilter);
318
+ };
306
319
 
307
320
  const handleSelectAll = () => selectAllItems(bulkFilter);
308
321
 
309
- const unselectAllItems = () => resetBulkSelection();
322
+ const unselectAllItems = () => {
323
+ resetBulkSelection();
324
+ setPagesSelected([]);
325
+ };
310
326
 
311
327
  const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());
312
328
 
@@ -345,11 +361,11 @@ const Content = (props: IProps): JSX.Element => {
345
361
  };
346
362
 
347
363
  const handleBulkDelete = async (pageIds: number[]) => {
348
- const globalPageIds = currentSitePages
364
+ const globalPageIds = pagesSelected
349
365
  .filter((page: IPage) => pageIds.includes(page.id) && page.origin === "GLOBAL")
350
366
  .map((page: IPage) => page.id);
351
367
 
352
- const filteredPageIds = currentSitePages
368
+ const filteredPageIds = pagesSelected
353
369
  .filter((page: IPage) => pageIds.includes(page.id) && page.origin !== "GLOBAL")
354
370
  .map((page: IPage) => page.id);
355
371
 
@@ -385,7 +401,19 @@ const Content = (props: IProps): JSX.Element => {
385
401
  };
386
402
 
387
403
  const bulkPublishAction = async (isPublish: boolean) => {
388
- const { notPublished, published } = selectedItems;
404
+ const { notPublished, published, drafts } = selectedItems;
405
+
406
+ if (drafts && drafts.length > 0) {
407
+ const multiplePages = drafts.length > 1;
408
+ const getPage = (id: number) => currentSitePages.find((page) => page.id === id);
409
+ const getPageWarning = (pageId: number) =>
410
+ `The '${getPage(pageId)?.title}' page has a draft version. To publish it, you must do it from the editor.`;
411
+ const text = multiplePages
412
+ ? `There are ${drafts.length} pages that cannot be published.`
413
+ : getPageWarning(drafts[0]);
414
+ const subErrors = multiplePages && drafts.map((draft) => ({ id: draft, error: getPageWarning(draft) }));
415
+ setNotification({ text, ...(subErrors && { subErrors }) });
416
+ }
389
417
 
390
418
  const status = isPublish ? pageStatus.UPLOAD_PENDING : pageStatus.OFFLINE_PENDING;
391
419
 
@@ -713,6 +741,14 @@ const Content = (props: IProps): JSX.Element => {
713
741
  />
714
742
  )}
715
743
  {(!!currentSiteErrorPages.length || errors.length > 0) && <Notification type="error" text={errorPagesText} />}
744
+ {notification && (
745
+ <Notification
746
+ type="warning"
747
+ text={notification.text}
748
+ subErrors={notification.subErrors}
749
+ resetError={() => setNotification(null)}
750
+ />
751
+ )}
716
752
  <TableList
717
753
  tableHeader={Header}
718
754
  pagination={pagination}
@@ -103,18 +103,20 @@ const getCurrentFilter = (structuredData: any, filter: any) =>
103
103
  const filterByStatus = (bulkSelection: number[], currentSitePages: IPage[]): Record<string, number[]> => {
104
104
  const notPublishedItems: number[] = [];
105
105
  const publishedItems: number[] = [];
106
+ const draftItems: number[] = [];
106
107
 
107
108
  currentSitePages.forEach((page: IPage) => {
108
- const { id, liveStatus } = page;
109
+ const { id, liveStatus, haveDraftPage } = page;
109
110
  if (bulkSelection.includes(id)) {
110
111
  switch (liveStatus.status) {
111
112
  case pageStatus.OFFLINE_PENDING:
112
113
  case pageStatus.OFFLINE:
113
114
  case pageStatus.UPLOAD_PENDING:
114
115
  notPublishedItems.push(id);
116
+ haveDraftPage ? draftItems.push(id) : notPublishedItems.push(id);
115
117
  break;
116
118
  case pageStatus.PUBLISHED:
117
- publishedItems.push(id);
119
+ haveDraftPage ? draftItems.push(id) : publishedItems.push(id);
118
120
  break;
119
121
  }
120
122
  }
@@ -124,6 +126,7 @@ const filterByStatus = (bulkSelection: number[], currentSitePages: IPage[]): Rec
124
126
  all: bulkSelection,
125
127
  notPublished: notPublishedItems,
126
128
  published: publishedItems,
129
+ drafts: draftItems,
127
130
  };
128
131
 
129
132
  return filteredItems;
@@ -2,16 +2,15 @@ import React, { useState } from "react";
2
2
  import { useParams } from "react-router-dom";
3
3
  import { connect } from "react-redux";
4
4
 
5
- import { isReqOk } from "@ax/helpers";
6
- import { global } from "@ax/api";
7
5
  import { appActions } from "@ax/containers/App";
8
6
  import { usersActions } from "@ax/containers/Users";
9
7
  import { Button, FieldsBehavior, ErrorToast } from "@ax/components";
8
+ import { ICreatePasswordParams } from "@ax/types";
10
9
 
11
10
  import * as S from "./style";
12
11
 
13
12
  const CreatePass = (props: IProps) => {
14
- const { setHistoryPush, getUser } = props;
13
+ const { setHistoryPush, createPassword } = props;
15
14
 
16
15
  const { id, token } = useParams<{ id: string; token: string }>();
17
16
 
@@ -36,12 +35,8 @@ const CreatePass = (props: IProps) => {
36
35
  retypedPassword: state.pass2,
37
36
  };
38
37
 
39
- const response = await global.createPassword(id, params);
40
- if (isReqOk(response.status)) {
41
- const {
42
- data: { token },
43
- } = response;
44
- await getUser("me", token);
38
+ const passCreated = await createPassword(id, params);
39
+ if (passCreated) {
45
40
  setHistoryPush("/profile?init=true");
46
41
  }
47
42
  };
@@ -65,6 +60,7 @@ const CreatePass = (props: IProps) => {
65
60
  value={state.pass1}
66
61
  onChange={handlePass1Change}
67
62
  validators={{ mandatory: true }}
63
+ autoComplete="off"
68
64
  />
69
65
  <FieldsBehavior
70
66
  fieldType="TextField"
@@ -74,6 +70,7 @@ const CreatePass = (props: IProps) => {
74
70
  value={state.pass2}
75
71
  onChange={handlePass2Change}
76
72
  validators={{ isSamePass: state.pass1, mandatory: true }}
73
+ autoComplete="off"
77
74
  />
78
75
  <Button type="submit" disabled={!validPasswords}>
79
76
  Create and log in
@@ -86,12 +83,12 @@ const CreatePass = (props: IProps) => {
86
83
 
87
84
  interface IProps {
88
85
  setHistoryPush(path: string): any;
89
- getUser(id: string, token: string): any;
86
+ createPassword(id: string, params: ICreatePasswordParams): Promise<boolean>;
90
87
  }
91
88
 
92
89
  const mapDispatchToProps = {
93
90
  setHistoryPush: appActions.setHistoryPush,
94
- getUser: usersActions.getUser,
91
+ createPassword: usersActions.createPassword,
95
92
  };
96
93
 
97
94
  export default connect(null, mapDispatchToProps)(CreatePass);
@@ -30,11 +30,7 @@ const FramePreview = (props: IProps) => {
30
30
 
31
31
  useOnMessageReceivedFromOutside(setEditorContent, setSelectedContent);
32
32
 
33
- const {
34
- editorContent: { canonicalSite, language, pageLanguages },
35
- header,
36
- footer,
37
- } = content;
33
+ const { canonicalSite, language, pageLanguages, header, footer } = content;
38
34
 
39
35
  document.body.classList.add("preview");
40
36
 
@@ -87,7 +83,7 @@ const FramePreview = (props: IProps) => {
87
83
  selectEditorID={selectEditorID}
88
84
  siteID={siteID}
89
85
  isPage={true}
90
- content={content.editorContent}
86
+ content={content}
91
87
  header={currentSiteInfo && header}
92
88
  footer={currentSiteInfo && footer}
93
89
  languageID={language}
@@ -9,9 +9,7 @@ const PageBrowser = (props: IProps) => {
9
9
  const {
10
10
  socials,
11
11
  cloudinaryName,
12
- content: {
13
- editorContent: { path, slug, canonicalSite },
14
- },
12
+ content: { path, slug, canonicalSite },
15
13
  setSelectedContent,
16
14
  globalLangs,
17
15
  theme,
@@ -35,7 +33,7 @@ const PageBrowser = (props: IProps) => {
35
33
  return (
36
34
  <Browser
37
35
  isPage={true}
38
- content={props.content.editorContent}
36
+ content={props.content}
39
37
  socials={socials}
40
38
  url={url}
41
39
  theme={theme}