@griddo/ax 10.6.8 → 10.6.11

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 (70) hide show
  1. package/package.json +2 -2
  2. package/src/components/Fields/CheckField/index.tsx +16 -4
  3. package/src/components/Fields/DateField/index.tsx +9 -3
  4. package/src/components/Fields/TimeField/index.tsx +8 -2
  5. package/src/components/Icon/components/CancelEvent.js +10 -0
  6. package/src/components/Icon/svgs/cancel-event.svg +3 -0
  7. package/src/components/MainWrapper/AppBar/index.tsx +8 -1
  8. package/src/components/MainWrapper/AppBar/style.tsx +9 -1
  9. package/src/components/MainWrapper/index.tsx +1 -0
  10. package/src/components/Modal/index.tsx +17 -1
  11. package/src/components/ScheduleModal/index.tsx +100 -0
  12. package/src/components/ScheduleModal/style.tsx +19 -0
  13. package/src/components/TableFilters/LiveFilter/index.tsx +1 -1
  14. package/src/components/index.tsx +3 -0
  15. package/src/containers/PageEditor/actions.tsx +21 -1
  16. package/src/containers/PageEditor/interfaces.tsx +1 -0
  17. package/src/forms/validators.tsx +3 -2
  18. package/src/helpers/dates.tsx +6 -0
  19. package/src/helpers/index.tsx +2 -0
  20. package/src/hooks/bulk.tsx +5 -1
  21. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/index.tsx +17 -12
  22. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +5 -1
  23. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +3 -1
  24. package/src/modules/Categories/CategoriesList/index.tsx +3 -0
  25. package/src/modules/Content/BulkHeader/TableHeader/index.tsx +15 -9
  26. package/src/modules/Content/BulkHeader/index.tsx +3 -0
  27. package/src/modules/Content/PageItem/index.tsx +10 -4
  28. package/src/modules/Content/index.tsx +4 -0
  29. package/src/modules/Content/utils.tsx +13 -10
  30. package/src/modules/FileDrive/BulkListHeader/TableHeader/index.tsx +15 -11
  31. package/src/modules/FileDrive/BulkListHeader/index.tsx +4 -1
  32. package/src/modules/FileDrive/ListItem/index.tsx +3 -1
  33. package/src/modules/FileDrive/index.tsx +6 -3
  34. package/src/modules/GlobalEditor/index.tsx +83 -3
  35. package/src/modules/GlobalEditor/style.tsx +12 -1
  36. package/src/modules/Navigation/Defaults/BulkHeader/TableHeader/index.tsx +18 -12
  37. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +11 -3
  38. package/src/modules/Navigation/Defaults/Item/index.tsx +9 -2
  39. package/src/modules/Navigation/Defaults/index.tsx +4 -0
  40. package/src/modules/PageEditor/index.tsx +82 -2
  41. package/src/modules/PageEditor/style.tsx +9 -1
  42. package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +17 -10
  43. package/src/modules/Redirects/BulkHeader/index.tsx +4 -0
  44. package/src/modules/Redirects/RedirectItem/index.tsx +8 -1
  45. package/src/modules/Redirects/index.tsx +3 -0
  46. package/src/modules/Settings/Integrations/BulkHeader/TableHeader/index.tsx +16 -11
  47. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +5 -1
  48. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +3 -1
  49. package/src/modules/Settings/Integrations/index.tsx +3 -0
  50. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +15 -10
  51. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +3 -0
  52. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +14 -3
  53. package/src/modules/Sites/SitesList/index.tsx +3 -0
  54. package/src/modules/StructuredData/Form/index.tsx +72 -6
  55. package/src/modules/StructuredData/Form/style.tsx +17 -1
  56. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +15 -9
  57. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +3 -0
  58. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +10 -3
  59. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +13 -4
  60. package/src/modules/StructuredData/StructuredDataList/atoms.tsx +1 -1
  61. package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -0
  62. package/src/modules/Users/Roles/BulkHeader/TableHeader/index.tsx +24 -10
  63. package/src/modules/Users/Roles/BulkHeader/index.tsx +4 -0
  64. package/src/modules/Users/Roles/RoleItem/index.tsx +3 -2
  65. package/src/modules/Users/Roles/index.tsx +12 -2
  66. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +17 -10
  67. package/src/modules/Users/UserList/BulkHeader/index.tsx +4 -0
  68. package/src/modules/Users/UserList/UserItem/index.tsx +3 -1
  69. package/src/modules/Users/UserList/index.tsx +24 -7
  70. package/src/types/index.tsx +2 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "10.6.8",
4
+ "version": "10.6.11",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -233,5 +233,5 @@
233
233
  "publishConfig": {
234
234
  "access": "public"
235
235
  },
236
- "gitHead": "a622dc1b913fd26805bbbeea654314a0438630c5"
236
+ "gitHead": "18bf6b513f974ad898ecc8a13b73d4285a1025dc"
237
237
  }
@@ -18,18 +18,29 @@ const CheckField = (props: ICheckFieldProps): JSX.Element => {
18
18
  className,
19
19
  inversed = false,
20
20
  light = false,
21
+ setHoverCheck,
21
22
  } = props;
22
23
 
23
24
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
24
25
  const isChecked = e.target.checked;
25
- if (onChange) {
26
- onChange({ value, isChecked });
27
- }
26
+ onChange && onChange({ value, isChecked });
28
27
  };
29
28
 
29
+ const handleMouseOver = () => setHoverCheck && setHoverCheck(true);
30
+
31
+ const handleMouseLeave = () => setHoverCheck && setHoverCheck(false);
32
+
30
33
  return (
31
34
  <S.Wrapper className={className}>
32
- <S.Label data-testid="check-field-label" disabled={disabled} inversed={inversed} hasText={!!title}>
35
+ <S.Label
36
+ data-testid="check-field-label"
37
+ disabled={disabled}
38
+ inversed={inversed}
39
+ hasText={!!title}
40
+ onMouseOver={handleMouseOver}
41
+ onFocus={handleMouseOver}
42
+ onMouseLeave={handleMouseLeave}
43
+ >
33
44
  {icon ? (
34
45
  <S.IconLabelWrapper data-testid="check-field-icon-label">
35
46
  <S.IconWrapper>
@@ -76,6 +87,7 @@ export interface ICheckFieldProps {
76
87
  inversed?: boolean;
77
88
  className?: string;
78
89
  light?: boolean;
90
+ setHoverCheck?: (state: boolean) => void;
79
91
  }
80
92
 
81
93
  export default CheckField;
@@ -29,6 +29,7 @@ const DateField = (props: IDateFieldProps): JSX.Element => {
29
29
  disabled,
30
30
  handleValidation,
31
31
  validators,
32
+ editorID,
32
33
  } = props;
33
34
 
34
35
  const currentDate = new Date();
@@ -50,15 +51,19 @@ const DateField = (props: IDateFieldProps): JSX.Element => {
50
51
 
51
52
  const dateFormat = "dd/MM/yyyy";
52
53
 
53
- const minDate = futureDate ? initialDate.start : null;
54
- const maxDate = pastDate ? initialDate.start : null;
54
+ const minDate = futureDate ? currentDate : null;
55
+ const maxDate = pastDate ? currentDate : null;
55
56
 
56
57
  const [dates, setDates] = useState(initialDate);
57
58
 
59
+ useEffect(() => {
60
+ setDates(initialDate);
61
+ // eslint-disable-next-line react-hooks/exhaustive-deps
62
+ }, [editorID]);
63
+
58
64
  useEffect(() => {
59
65
  const { start, end } = initialDate;
60
66
  handleOnChange([start, end]);
61
-
62
67
  // eslint-disable-next-line react-hooks/exhaustive-deps
63
68
  }, []);
64
69
 
@@ -133,6 +138,7 @@ export interface IDateFieldProps {
133
138
  disabled?: boolean;
134
139
  handleValidation?: (value: string, validators?: Record<string, unknown>) => void;
135
140
  validators?: Record<string, unknown>;
141
+ editorID: number;
136
142
  }
137
143
 
138
144
  export default DateField;
@@ -1,17 +1,22 @@
1
- import React, { memo, useState } from "react";
1
+ import React, { memo, useEffect, useState } from "react";
2
2
  import { Select, Icon } from "@ax/components";
3
3
  import HourInput from "./HourInput";
4
4
 
5
5
  import * as S from "./style";
6
6
 
7
7
  const TimeField = (props: IProps) => {
8
- const { value, error, onChange, disabled } = props;
8
+ const { value, error, onChange, disabled, editorID } = props;
9
9
 
10
10
  const safeValue = !value || !value.length ? "00:00 am" : value;
11
11
  const splitValue = safeValue.split(" ");
12
12
  const initValue = { time: splitValue[0], meridian: splitValue[1] };
13
13
  const [state, setState] = useState(initValue);
14
14
 
15
+ useEffect(() => {
16
+ setState(initValue);
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ }, [editorID]);
19
+
15
20
  const handleChange = (newValue: string) => {
16
21
  setState({ ...state, time: newValue });
17
22
  onChange(`${newValue} ${state.meridian}`);
@@ -53,6 +58,7 @@ interface IProps {
53
58
  error?: boolean;
54
59
  onChange: (value: string) => void;
55
60
  disabled?: boolean;
61
+ editorID: number;
56
62
  }
57
63
 
58
64
  export default memo(TimeField);
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ const SvgCancelEvent = (props) => (
3
+ <svg xmlns="http://www.w3.org/2000/svg" width={24} height={24} fill="none" {...props}>
4
+ <path
5
+ fill="#5057FF"
6
+ d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2Zm0 16H5V9h14v10ZM5 7V5h14v2H5Zm3.23 9.41 1.06 1.06 2.44-2.44 2.44 2.44 1.06-1.06-2.44-2.44 2.44-2.44-1.06-1.06-2.44 2.44-2.44-2.44-1.06 1.06 2.44 2.44-2.44 2.44Z"
7
+ />
8
+ </svg>
9
+ );
10
+ export default SvgCancelEvent;
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M19 3H18V1H16V3H8V1H6V3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 19H5V9H19V19ZM5 7V5H19V7H5ZM8.23 16.41L9.29 17.47L11.73 15.03L14.17 17.47L15.23 16.41L12.79 13.97L15.23 11.53L14.17 10.47L11.73 12.91L9.29 10.47L8.23 11.53L10.67 13.97L8.23 16.41Z" fill="#5057FF"/>
3
+ </svg>
@@ -2,7 +2,7 @@ import React, { useState } from "react";
2
2
  import { withRouter, RouteComponentProps } from "react-router-dom";
3
3
 
4
4
  import { IErrorItem } from "@ax/types";
5
- import { trimText } from "@ax/helpers";
5
+ import { getScheduleFormatDate, trimText } from "@ax/helpers";
6
6
  import { useModal } from "@ax/hooks";
7
7
  import {
8
8
  Button,
@@ -52,6 +52,7 @@ const AppBar = (props: IProps): JSX.Element => {
52
52
  searchValue,
53
53
  isDirty,
54
54
  exportAction,
55
+ scheduledPublication,
55
56
  } = props;
56
57
 
57
58
  const publishedTooltip: any = {
@@ -60,9 +61,11 @@ const AppBar = (props: IProps): JSX.Element => {
60
61
  offline: "Offline",
61
62
  "offline-pending": "Offline pending",
62
63
  modified: "Live & Modified",
64
+ scheduled: "Scheduled publication",
63
65
  };
64
66
 
65
67
  const fixedClass = fixedAppBar ? "fixed" : "";
68
+ const isScheduledPub = !!scheduledPublication && pageStatus === "scheduled";
66
69
 
67
70
  const { isOpen, toggleModal } = useModal();
68
71
  const [langSelected, setLangSelected] = useState(language);
@@ -268,6 +271,9 @@ const AppBar = (props: IProps): JSX.Element => {
268
271
  <Tooltip content={publishedTooltip[pageStatus]} bottom>
269
272
  <PageStatus />
270
273
  </Tooltip>
274
+ {isScheduledPub && (
275
+ <S.ScheduledDate inversed={inversed}>{getScheduleFormatDate(scheduledPublication)}</S.ScheduledDate>
276
+ )}
271
277
  </S.IconStatusWrapper>
272
278
  {(rightButton || rightLineButton) && <S.Separator />}
273
279
  </>
@@ -348,6 +354,7 @@ export interface IAppBarProps {
348
354
  searchValue?: string;
349
355
  isDirty?: boolean;
350
356
  exportAction?(formats: (number | string)[]): void;
357
+ scheduledPublication?: string | null;
351
358
  }
352
359
 
353
360
  type IProps = IAppBarProps & RouteComponentProps;
@@ -53,7 +53,6 @@ const Subtitle = styled.span`
53
53
 
54
54
  const ActionText = styled.span`
55
55
  padding-left: ${(p) => p.theme.spacing.xs};
56
- margin-left: ${(p) => p.theme.spacing.xxs};
57
56
  `;
58
57
 
59
58
  const ActionItem = styled.li`
@@ -130,10 +129,12 @@ const Separator = styled.div`
130
129
  `;
131
130
 
132
131
  const IconStatusWrapper = styled.div<{ last: boolean }>`
132
+ display: flex;
133
133
  position: relative;
134
134
  height: ${(p) => p.theme.spacing.m};
135
135
  cursor: pointer;
136
136
  margin-right: ${(p) => (p.last ? p.theme.spacing.xs : "0")};
137
+ align-items: center;
137
138
  `;
138
139
 
139
140
  const Header = styled.section<{ inversed?: boolean }>`
@@ -220,6 +221,12 @@ const ModalContent = styled.div`
220
221
 
221
222
  const StatusBtn = styled.span``;
222
223
 
224
+ const ScheduledDate = styled.div<{ inversed?: boolean }>`
225
+ ${(p) => p.theme.textStyle.uiS};
226
+ color: ${(p) => (p.inversed ? p.theme.color.textHighEmphasisInverse : p.theme.color.interactive01)};
227
+ margin-left: ${(p) => p.theme.spacing.xs};
228
+ `;
229
+
223
230
  export {
224
231
  ActionMenu,
225
232
  Header,
@@ -244,4 +251,5 @@ export {
244
251
  StatusBtn,
245
252
  HelpText,
246
253
  ModalContent,
254
+ ScheduledDate,
247
255
  };
@@ -57,6 +57,7 @@ export interface IWrapperProps {
57
57
  searchValue?: string;
58
58
  isDirty?: boolean;
59
59
  exportAction?(formats: (number | string)[]): void;
60
+ scheduledPublication?: string | null;
60
61
  }
61
62
 
62
63
  export default MainWrapper;
@@ -1,4 +1,4 @@
1
- import React, { memo } from "react";
1
+ import React, { memo, useEffect } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
  import { IconAction } from "@ax/components";
4
4
  import Button from "../Button";
@@ -19,6 +19,22 @@ const Modal = (props: IModalProps): JSX.Element | null => {
19
19
  isChild,
20
20
  } = props;
21
21
 
22
+ useEffect(() => {
23
+ const handleKeyPress = (event: KeyboardEvent) => {
24
+ if (event.key === "Escape") {
25
+ hide();
26
+ }
27
+ };
28
+
29
+ if (isOpen) {
30
+ document.addEventListener("keydown", handleKeyPress);
31
+ }
32
+
33
+ return () => {
34
+ document.removeEventListener("keydown", handleKeyPress);
35
+ };
36
+ }, [isOpen, hide]);
37
+
22
38
  const titleContent = title ? <S.Title>{title}</S.Title> : "";
23
39
 
24
40
  const mainActionButton = mainAction ? (
@@ -0,0 +1,100 @@
1
+ import React, { useState } from "react";
2
+
3
+ import { IModal } from "@ax/types";
4
+ import { Modal, FieldsBehavior } from "@ax/components";
5
+ import { stringToDate } from "@ax/helpers";
6
+
7
+ import * as S from "./style";
8
+
9
+ const ScheduleModal = (props: IScheduleModal): JSX.Element => {
10
+ const { isOpen, toggleModal, mainModalAction, secondaryModalAction, scheduleDate, setScheduleDate } = props;
11
+
12
+ const [error, setError] = useState(false);
13
+
14
+ const handleDateChange = (dateVal: string) => {
15
+ setScheduleDate({ ...scheduleDate, date: dateVal });
16
+ const now = new Date();
17
+ now.setHours(0, 0, 0, 0);
18
+ const date = stringToDate(dateVal);
19
+ if (date >= now) {
20
+ setError(false);
21
+ } else {
22
+ setError(true);
23
+ }
24
+ };
25
+
26
+ const handleTimeChange = (time: string) => setScheduleDate({ ...scheduleDate, time });
27
+
28
+ const modalAction: any = { ...mainModalAction, disabled: error };
29
+
30
+ return (
31
+ <Modal
32
+ isOpen={isOpen}
33
+ hide={toggleModal}
34
+ title="Schedule publication"
35
+ secondaryAction={secondaryModalAction}
36
+ mainAction={modalAction}
37
+ size="M"
38
+ height={330}
39
+ overflow="visible"
40
+ >
41
+ <S.ModalContent>
42
+ <p>
43
+ You can schedule the publication of your page at a <strong>specific date and time</strong>.{" "}
44
+ <strong>The selected time is approximate</strong> due to our structural publication process.
45
+ </p>
46
+ <S.ModalFields>
47
+ <FieldsBehavior
48
+ title="Date"
49
+ name="date"
50
+ fieldType="DateField"
51
+ value={scheduleDate.date}
52
+ onChange={handleDateChange}
53
+ validators={{ dateFormat: "futureDate" }}
54
+ mandatory={true}
55
+ futureDate={true}
56
+ />
57
+ <FieldsBehavior
58
+ title="Time"
59
+ name="time"
60
+ fieldType="TimeField"
61
+ value={scheduleDate.time}
62
+ onChange={handleTimeChange}
63
+ helptext="12 hour time format"
64
+ mandatory={true}
65
+ />
66
+ </S.ModalFields>
67
+ </S.ModalContent>
68
+ </Modal>
69
+ );
70
+ };
71
+
72
+ const CancelScheduleModal = (props: IModal): JSX.Element => {
73
+ const { isOpen, toggleModal, mainModalAction, secondaryModalAction } = props;
74
+
75
+ return (
76
+ <Modal
77
+ isOpen={isOpen}
78
+ hide={toggleModal}
79
+ title="Cancel schedule publication"
80
+ secondaryAction={secondaryModalAction}
81
+ mainAction={mainModalAction}
82
+ size="S"
83
+ height={240}
84
+ >
85
+ <S.ModalContent>
86
+ <p>
87
+ Are you sure you want to cancel the scheduled publication? Once canceled,{" "}
88
+ <strong>the page will not be published at the selected time</strong>.
89
+ </p>
90
+ </S.ModalContent>
91
+ </Modal>
92
+ );
93
+ };
94
+
95
+ interface IScheduleModal extends IModal {
96
+ scheduleDate: { date: string; time: string };
97
+ setScheduleDate: (scheduleDate: { date: string; time: string }) => void;
98
+ }
99
+
100
+ export { ScheduleModal, CancelScheduleModal };
@@ -0,0 +1,19 @@
1
+ import styled from "styled-components";
2
+
3
+ const ModalContent = styled.div`
4
+ padding: ${(p) => p.theme.spacing.m};
5
+
6
+ p {
7
+ margin-bottom: ${(p) => p.theme.spacing.m};
8
+ }
9
+ `;
10
+
11
+ const ModalFields = styled.div`
12
+ display: flex;
13
+ width: 100%;
14
+ & > div:nth-child(odd) {
15
+ margin-right: ${(p) => p.theme.spacing.m};
16
+ }
17
+ `;
18
+
19
+ export { ModalContent, ModalFields };
@@ -24,7 +24,7 @@ const LiveFilter = (props: ILiveFilterProps): JSX.Element => {
24
24
  const parsedValue = Array.isArray(value) && value.length ? value.map((value) => value.value) : initialState;
25
25
  const [selectedValue, setSelectedValue] = useState(parsedValue);
26
26
  const [options, setOptions] = useState(filters);
27
- const basicStatus = ["offline", "active"];
27
+ const basicStatus = ["offline", "active", "scheduled"];
28
28
 
29
29
  useEffect(() => {
30
30
  const parsedValue = Array.isArray(value) && value.length ? value.map((value) => value.value) : initialState;
@@ -99,6 +99,7 @@ import Pagination from "./Pagination";
99
99
  import ProgressBar from "./ProgressBar";
100
100
  import ReorderArrows from "./ReorderArrows";
101
101
  import ResizePanel from "./ResizePanel";
102
+ import { ScheduleModal, CancelScheduleModal } from "./ScheduleModal";
102
103
  import SearchField from "./SearchField";
103
104
  import SearchTagsBar from "./SearchTagsBar";
104
105
  import SideModal from "./SideModal";
@@ -212,6 +213,8 @@ export {
212
213
  ProgressBar,
213
214
  ReorderArrows,
214
215
  ResizePanel,
216
+ ScheduleModal,
217
+ CancelScheduleModal,
215
218
  SearchField,
216
219
  SearchTagsBar,
217
220
  SideModal,
@@ -653,7 +653,7 @@ function duplicatePage(pageID: number, data: any, siteID?: number): (dispatch: D
653
653
  handleSuccess: (data: any) => {
654
654
  if (!siteID) {
655
655
  dispatch(setCurrentPageID(data.id));
656
- dispatch(setCurrentPageStatus("offline"));
656
+ dispatch(setCurrentPageStatus(pageStatus.OFFLINE));
657
657
  }
658
658
  return true;
659
659
  },
@@ -1460,6 +1460,25 @@ function setIsTranslated(isTranslated: boolean): (dispatch: Dispatch) => Promise
1460
1460
  };
1461
1461
  }
1462
1462
 
1463
+ function schedulePublication(date: string | null): (dispatch: Dispatch, getState: any) => Promise<boolean> {
1464
+ return async (dispatch, getState) => {
1465
+ try {
1466
+ const {
1467
+ pageEditor: { editorContent },
1468
+ } = getState();
1469
+
1470
+ const status = date ? pageStatus.SCHEDULED : pageStatus.OFFLINE;
1471
+ const updatedEditorContent = { ...editorContent, publicationScheduled: date };
1472
+
1473
+ dispatch(setEditorContent(updatedEditorContent));
1474
+ return await savePage(false, { status })(dispatch, getState);
1475
+ } catch (e) {
1476
+ console.log("Error", e);
1477
+ return false;
1478
+ }
1479
+ };
1480
+ }
1481
+
1463
1482
  export {
1464
1483
  setEditorContent,
1465
1484
  setTemplate,
@@ -1511,4 +1530,5 @@ export {
1511
1530
  getPageSummary,
1512
1531
  getPageTranslation,
1513
1532
  setIsTranslated,
1533
+ schedulePublication,
1514
1534
  };
@@ -151,6 +151,7 @@ export enum pageStatus {
151
151
  UPLOAD_PENDING = "upload-pending",
152
152
  OFFLINE_PENDING = "offline-pending",
153
153
  MODIFIED = "modified",
154
+ SCHEDULED = "scheduled",
154
155
  }
155
156
 
156
157
  type EditorActionsCreators = ISetEditorContent;
@@ -105,14 +105,15 @@ const VALIDATORS = {
105
105
  return { isValid: true, errorCode: "" };
106
106
  }
107
107
  const now = new Date();
108
+ now.setHours(0, 0, 0, 0);
108
109
  const date = stringToDate(val);
109
110
  switch (type) {
110
111
  case "futureDate": {
111
- const isValid = isValidDate(val) && dateToString(date) >= dateToString(now);
112
+ const isValid = isValidDate(val) && date >= now;
112
113
  return { isValid, errorCode: "ERR010" };
113
114
  }
114
115
  case "pastDate": {
115
- const isValid = isValidDate(val) && dateToString(date) <= dateToString(now);
116
+ const isValid = isValidDate(val) && date <= now;
116
117
  return { isValid, errorCode: "ERR011" };
117
118
  }
118
119
  default:
@@ -79,6 +79,11 @@ const getDaysAgo = (date: Date): string => {
79
79
  return formatDistanceToNowStrict(dateObj, { addSuffix: true });
80
80
  };
81
81
 
82
+ const getScheduleFormatDate = (date: string): string => {
83
+ const dateObj = parse(date, "dd/MM/yyyy HH:mm:ss", new Date());
84
+ return dateToString(dateObj, "dd/MM/yy · HH:mm");
85
+ };
86
+
82
87
  export {
83
88
  dateToString,
84
89
  stringToDate,
@@ -91,4 +96,5 @@ export {
91
96
  getFormattedDateWithTimezone,
92
97
  getDaysAgo,
93
98
  getRange,
99
+ getScheduleFormatDate,
94
100
  };
@@ -90,6 +90,7 @@ import {
90
90
  getFormattedDateWithTimezone,
91
91
  getDaysAgo,
92
92
  getRange,
93
+ getScheduleFormatDate,
93
94
  } from "./dates";
94
95
 
95
96
  import { imageResizeCropAndCompress, compressImage } from "./imageResize";
@@ -165,6 +166,7 @@ export {
165
166
  getFormattedDateWithTimezone,
166
167
  getDaysAgo,
167
168
  getRange,
169
+ getScheduleFormatDate,
168
170
  imageResizeCropAndCompress,
169
171
  compressImage,
170
172
  validateEmail,
@@ -17,13 +17,14 @@ const useBulkSelection = (itemIDs: any[]) => {
17
17
  const [checkState, setCheckState] = useState<Record<string, boolean>>({
18
18
  isAllSelected: false,
19
19
  indeterminate: false,
20
+ hoverCheck: false,
20
21
  });
21
22
 
22
23
  useEffect(() => {
23
24
  const isAllSelected = areAllItemsSelected(items);
24
25
  const indeterminate = !isAllSelected && areItemsSelected(items);
25
26
 
26
- setCheckState({ isAllSelected, indeterminate });
27
+ setCheckState({ isAllSelected, indeterminate, hoverCheck: false });
27
28
 
28
29
  // eslint-disable-next-line
29
30
  }, [selectedItems, items]);
@@ -126,6 +127,8 @@ const useBulkSelection = (itemIDs: any[]) => {
126
127
  });
127
128
  };
128
129
 
130
+ const setHoverCheck = (value: boolean) => setCheckState((state) => ({ ...state, hoverCheck: value }));
131
+
129
132
  return {
130
133
  resetBulkSelection,
131
134
  selectedItems,
@@ -134,6 +137,7 @@ const useBulkSelection = (itemIDs: any[]) => {
134
137
  checkState,
135
138
  addToBulkSelection,
136
139
  selectAllItems,
140
+ setHoverCheck,
137
141
  };
138
142
  };
139
143
 
@@ -1,24 +1,27 @@
1
1
  import React from "react";
2
2
 
3
- import { CheckField, TableCounter, TranslationsFilter } from "@ax/components";
3
+ import { CheckField, TableCounter, Tooltip, TranslationsFilter } from "@ax/components";
4
4
  import { IQueryValue } from "@ax/types";
5
5
 
6
6
  import * as S from "./style";
7
7
 
8
- const TableHeader = (props: IProps) => {
9
- const { totalItems, selectAllItems, isScrolling, filterItems, filterValues } = props;
8
+ const TableHeader = (props: IProps): JSX.Element => {
9
+ const { totalItems, selectAllItems, isScrolling, filterItems, filterValues, setHoverCheck, checkState } = props;
10
10
  return (
11
11
  <S.TableHeader isScrolling={isScrolling}>
12
12
  <S.CheckHeader>
13
- <CheckField
14
- key="selectAll"
15
- name="selectAll"
16
- value="selectAll"
17
- onChange={selectAllItems}
18
- checked={false}
19
- disabled={false}
20
- error={false}
21
- />
13
+ <Tooltip content="Select All Items">
14
+ <CheckField
15
+ key="selectAll"
16
+ name="selectAll"
17
+ value="selectAll"
18
+ onChange={selectAllItems}
19
+ checked={checkState.isAllSelected || checkState.hoverCheck}
20
+ disabled={false}
21
+ error={false}
22
+ setHoverCheck={setHoverCheck}
23
+ />
24
+ </Tooltip>
22
25
  </S.CheckHeader>
23
26
  <S.NameHeader>Name</S.NameHeader>
24
27
  <S.CodeHeader>Code</S.CodeHeader>
@@ -39,6 +42,8 @@ interface IProps {
39
42
  selectAllItems: () => void;
40
43
  filterValues: Record<string, IQueryValue[]>;
41
44
  filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
45
+ setHoverCheck: (state: boolean) => void;
46
+ checkState: Record<string, boolean>;
42
47
  }
43
48
 
44
49
  export default TableHeader;
@@ -14,6 +14,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
14
14
  bulkActions,
15
15
  filterItems,
16
16
  filterValues,
17
+ setHoverCheck,
17
18
  } = props;
18
19
 
19
20
  return showBulk ? (
@@ -30,13 +31,15 @@ const BulkHeader = (props: IProps): JSX.Element => {
30
31
  isScrolling={isScrolling}
31
32
  filterItems={filterItems}
32
33
  filterValues={filterValues}
34
+ setHoverCheck={setHoverCheck}
35
+ checkState={checkState}
33
36
  />
34
37
  );
35
38
  };
36
39
 
37
40
  interface IProps {
38
41
  showBulk: boolean;
39
- checkState: any;
42
+ checkState: Record<string, boolean>;
40
43
  selectItems: () => void;
41
44
  selectAllItems: () => void;
42
45
  totalItems: number;
@@ -44,6 +47,7 @@ interface IProps {
44
47
  bulkActions: { icon: string; text: string; action: () => void }[];
45
48
  filterValues: Record<string, IQueryValue[]>;
46
49
  filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
50
+ setHoverCheck: (state: boolean) => void;
47
51
  }
48
52
 
49
53
  export default BulkHeader;
@@ -28,6 +28,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
28
28
  dragHandleProps,
29
29
  icon,
30
30
  deleteCategoryGroup,
31
+ hoverCheck,
31
32
  } = props;
32
33
 
33
34
  const { isOpen, toggleModal } = useModal();
@@ -193,7 +194,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
193
194
  <S.IconWrapper>{icon}</S.IconWrapper>
194
195
  </S.HandleCell>
195
196
  <S.CheckCell role="cell">
196
- <CheckField name="check" value={category.id} checked={isSelected} onChange={handleOnChange} />
197
+ <CheckField name="check" value={category.id} checked={isSelected || hoverCheck} onChange={handleOnChange} />
197
198
  </S.CheckCell>
198
199
  <S.NameCell role="cell" onClick={handleClick} clickable={allowedToEditTaxonomy} isGroup={isGroup}>
199
200
  {isGroup && (
@@ -263,6 +264,7 @@ interface IProps {
263
264
  currentSiteID: number | null;
264
265
  icon: JSX.Element;
265
266
  dragHandleProps?: any;
267
+ hoverCheck?: boolean;
266
268
  }
267
269
 
268
270
  interface IDispatchProps {