@griddo/ax 10.6.9 → 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.
- package/package.json +2 -2
- package/src/components/Fields/CheckField/index.tsx +16 -4
- package/src/components/Fields/DateField/index.tsx +9 -3
- package/src/components/Fields/TimeField/index.tsx +8 -2
- package/src/components/Icon/components/CancelEvent.js +10 -0
- package/src/components/Icon/svgs/cancel-event.svg +3 -0
- package/src/components/MainWrapper/AppBar/index.tsx +8 -1
- package/src/components/MainWrapper/AppBar/style.tsx +9 -1
- package/src/components/MainWrapper/index.tsx +1 -0
- package/src/components/Modal/index.tsx +17 -1
- package/src/components/ScheduleModal/index.tsx +100 -0
- package/src/components/ScheduleModal/style.tsx +19 -0
- package/src/components/TableFilters/LiveFilter/index.tsx +1 -1
- package/src/components/index.tsx +3 -0
- package/src/containers/PageEditor/actions.tsx +21 -1
- package/src/containers/PageEditor/interfaces.tsx +1 -0
- package/src/forms/validators.tsx +3 -2
- package/src/helpers/dates.tsx +6 -0
- package/src/helpers/index.tsx +2 -0
- package/src/hooks/bulk.tsx +5 -1
- package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/index.tsx +17 -12
- package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +5 -1
- package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +3 -1
- package/src/modules/Categories/CategoriesList/index.tsx +3 -0
- package/src/modules/Content/BulkHeader/TableHeader/index.tsx +15 -9
- package/src/modules/Content/BulkHeader/index.tsx +3 -0
- package/src/modules/Content/PageItem/index.tsx +10 -4
- package/src/modules/Content/index.tsx +4 -0
- package/src/modules/Content/utils.tsx +13 -10
- package/src/modules/FileDrive/BulkListHeader/TableHeader/index.tsx +15 -11
- package/src/modules/FileDrive/BulkListHeader/index.tsx +4 -1
- package/src/modules/FileDrive/ListItem/index.tsx +3 -1
- package/src/modules/FileDrive/index.tsx +6 -3
- package/src/modules/GlobalEditor/index.tsx +83 -3
- package/src/modules/GlobalEditor/style.tsx +12 -1
- package/src/modules/Navigation/Defaults/BulkHeader/TableHeader/index.tsx +18 -12
- package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +11 -3
- package/src/modules/Navigation/Defaults/Item/index.tsx +9 -2
- package/src/modules/Navigation/Defaults/index.tsx +4 -0
- package/src/modules/PageEditor/index.tsx +82 -2
- package/src/modules/PageEditor/style.tsx +9 -1
- package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +17 -10
- package/src/modules/Redirects/BulkHeader/index.tsx +4 -0
- package/src/modules/Redirects/RedirectItem/index.tsx +8 -1
- package/src/modules/Redirects/index.tsx +3 -0
- package/src/modules/Settings/Integrations/BulkHeader/TableHeader/index.tsx +16 -11
- package/src/modules/Settings/Integrations/BulkHeader/index.tsx +5 -1
- package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +3 -1
- package/src/modules/Settings/Integrations/index.tsx +3 -0
- package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +15 -10
- package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +3 -0
- package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +14 -3
- package/src/modules/Sites/SitesList/index.tsx +3 -0
- package/src/modules/StructuredData/Form/index.tsx +72 -6
- package/src/modules/StructuredData/Form/style.tsx +17 -1
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +15 -9
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +3 -0
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +10 -3
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +13 -4
- package/src/modules/StructuredData/StructuredDataList/atoms.tsx +1 -1
- package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -0
- package/src/modules/Users/Roles/BulkHeader/TableHeader/index.tsx +24 -10
- package/src/modules/Users/Roles/BulkHeader/index.tsx +4 -0
- package/src/modules/Users/Roles/RoleItem/index.tsx +3 -2
- package/src/modules/Users/Roles/index.tsx +12 -2
- package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +17 -10
- package/src/modules/Users/UserList/BulkHeader/index.tsx +4 -0
- package/src/modules/Users/UserList/UserItem/index.tsx +3 -1
- package/src/modules/Users/UserList/index.tsx +24 -7
- 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.
|
|
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": "
|
|
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
|
-
|
|
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
|
|
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 ?
|
|
54
|
-
const maxDate = pastDate ?
|
|
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
|
};
|
|
@@ -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;
|
package/src/components/index.tsx
CHANGED
|
@@ -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(
|
|
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
|
};
|
package/src/forms/validators.tsx
CHANGED
|
@@ -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) &&
|
|
112
|
+
const isValid = isValidDate(val) && date >= now;
|
|
112
113
|
return { isValid, errorCode: "ERR010" };
|
|
113
114
|
}
|
|
114
115
|
case "pastDate": {
|
|
115
|
-
const isValid = isValidDate(val) &&
|
|
116
|
+
const isValid = isValidDate(val) && date <= now;
|
|
116
117
|
return { isValid, errorCode: "ERR011" };
|
|
117
118
|
}
|
|
118
119
|
default:
|
package/src/helpers/dates.tsx
CHANGED
|
@@ -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
|
};
|
package/src/helpers/index.tsx
CHANGED
|
@@ -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,
|
package/src/hooks/bulk.tsx
CHANGED
|
@@ -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
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
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 {
|