Skip to content

Commit 8daa1ea

Browse files
author
Zabilsya
committed
[DOP-22993] add transfer schedule
1 parent 2aed439 commit 8daa1ea

File tree

27 files changed

+590
-9
lines changed

27 files changed

+590
-9
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"clsx": "2.1.1",
2626
"dayjs": "1.11.13",
2727
"dotenv-webpack": "8.1.0",
28+
"rc-picker": "4.9.2",
2829
"react": "18.2.0",
2930
"react-dom": "18.2.0",
3031
"react-error-boundary": "4.0.13",

src/features/transfer/MutateTransferForm/components/TransferSchedule/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Form, Input, Switch } from 'antd';
1+
import { CronSelect } from '@shared/ui';
2+
import { Form, Switch } from 'antd';
23
import React, { useState } from 'react';
34

45
export const TransferSchedule = () => {
@@ -16,7 +17,7 @@ export const TransferSchedule = () => {
1617
</Form.Item>
1718
{isScheduled && (
1819
<Form.Item label="Schedule" name="schedule" rules={[{ required: true }]}>
19-
<Input size="large" />
20+
<CronSelect />
2021
</Form.Item>
2122
)}
2223
</>

src/features/transfer/TransferDetailInfo/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { Descriptions } from 'antd';
33
import { Link } from 'react-router-dom';
4+
import { CronService } from '@shared/services';
45

56
import { TransferDetailInfoProps } from './types';
67
import classes from './styles.module.less';
@@ -32,12 +33,11 @@ export const TransferDetailInfo = ({
3233
<Descriptions.Item label="Queue" span={3}>
3334
<Link to={`/queues/${queue.id}`}>{queue.name}</Link>
3435
</Descriptions.Item>
35-
<Descriptions.Item label="Is scheduled" span={3}>
36-
{transfer.is_scheduled ? 'Yes' : 'No'}
37-
</Descriptions.Item>
38-
<Descriptions.Item label="Schedule" span={3}>
39-
{transfer.schedule}
40-
</Descriptions.Item>
36+
{transfer.is_scheduled && (
37+
<Descriptions.Item label="Schedule" span={3}>
38+
{new CronService(transfer.schedule).getSchedule()}
39+
</Descriptions.Item>
40+
)}
4141
<Descriptions.Item label="Strategy params" span={3}>
4242
{transfer.strategy_params.type}
4343
</Descriptions.Item>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { CronSegmentKey, CronSegmentValue, DayOfWeekName } from './types';
2+
3+
export const CRON_VALUE_DEFAULT = new Map<CronSegmentKey, CronSegmentValue>([
4+
['minute', new Date().getMinutes()],
5+
['hour', new Date().getHours()],
6+
['date', null],
7+
['day', null],
8+
]);
9+
10+
export const DAYS_OF_WEEK = Object.values(DayOfWeekName);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { getOrdinalNumber } from '@shared/utils';
2+
3+
import { CRON_VALUE_DEFAULT, DAYS_OF_WEEK } from './constants';
4+
import { CronSegmentKey, CronSegmentValue } from './types';
5+
6+
/** Class for convenient handling cron settings */
7+
export class CronService {
8+
private initialValueLength = 5;
9+
10+
private value: Map<CronSegmentKey, CronSegmentValue>;
11+
12+
constructor(initialValue?: string) {
13+
this.value = this.transformInitialValueToMap(initialValue);
14+
}
15+
16+
private transformInitialValueToMap(initialValue?: string) {
17+
const splittedValue = initialValue?.split(' ');
18+
if (splittedValue?.length !== this.initialValueLength) {
19+
return CRON_VALUE_DEFAULT;
20+
}
21+
22+
const cronValue = splittedValue.map((segment) => {
23+
const parsedValue = parseInt(segment);
24+
if (Number.isInteger(parsedValue)) {
25+
return parsedValue;
26+
}
27+
return null;
28+
});
29+
30+
return new Map<CronSegmentKey, CronSegmentValue>([
31+
['minute', cronValue[0]],
32+
['hour', cronValue[1]],
33+
['date', cronValue[2]],
34+
['day', cronValue[4]],
35+
]);
36+
}
37+
38+
getMinute(): number {
39+
return this.value.get('minute')!;
40+
}
41+
42+
getHour(): number {
43+
return this.value.get('hour')!;
44+
}
45+
46+
getMonthDay(): CronSegmentValue {
47+
return this.value.get('date') ?? null;
48+
}
49+
50+
getWeekDay(): CronSegmentValue {
51+
return this.value.get('day') ?? null;
52+
}
53+
54+
getTime() {
55+
return `${this.getHour()}:${this.getMinute()}`;
56+
}
57+
58+
setMinute(value: number) {
59+
if (value < 0 || value > 59) {
60+
this.value.set('minute', new Date().getMinutes());
61+
} else {
62+
this.value.set('minute', value);
63+
}
64+
}
65+
66+
setHour(value: number) {
67+
if (value < 0 || value > 23) {
68+
this.value.set('hour', new Date().getHours());
69+
} else {
70+
this.value.set('hour', value);
71+
}
72+
}
73+
74+
setMonthDay(value: CronSegmentValue) {
75+
if (value === null) {
76+
this.value.set('date', null);
77+
return;
78+
}
79+
if (value < 1 || value > 31) {
80+
this.value.set('date', new Date().getDate());
81+
} else {
82+
this.value.set('date', value);
83+
}
84+
this.setWeekDay(null);
85+
}
86+
87+
setWeekDay(value: CronSegmentValue) {
88+
if (value === null) {
89+
this.value.set('day', null);
90+
return;
91+
}
92+
if (value < 0 || value > 6) {
93+
this.value.set('day', new Date().getDay());
94+
} else {
95+
this.value.set('day', value);
96+
}
97+
this.setMonthDay(null);
98+
}
99+
100+
toString() {
101+
const minute = this.getMinute();
102+
const hour = this.getHour();
103+
const date = this.getMonthDay() ?? '*';
104+
const day = this.getWeekDay() ?? '*';
105+
return `${minute} ${hour} ${date} * ${day}`;
106+
}
107+
108+
getSchedule() {
109+
const time = this.getTime();
110+
const day = this.getWeekDay();
111+
const date = this.getMonthDay();
112+
113+
let schedule = 'Every ';
114+
115+
if (day === null && !date) {
116+
schedule += 'day ';
117+
} else if (day !== null && !date) {
118+
schedule += 'week ';
119+
} else {
120+
schedule += 'month ';
121+
}
122+
123+
if (day !== null) {
124+
schedule += `on ${DAYS_OF_WEEK[day]} `;
125+
} else if (date) {
126+
schedule += `${getOrdinalNumber(date)} `;
127+
}
128+
129+
schedule += `at ${time}`;
130+
131+
return schedule;
132+
}
133+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './cronService';
2+
export * from './types';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export type CronSegmentValue = number | null;
2+
3+
export type CronSegmentKey = 'date' | 'day' | 'hour' | 'minute';
4+
5+
export enum Period {
6+
DAY = 'day',
7+
WEEK = 'week',
8+
MONTH = 'month',
9+
}
10+
11+
export enum DayOfWeek {
12+
SUNDAY,
13+
MONDAY,
14+
TUESDAY,
15+
WEDNESDAY,
16+
THURSDAY,
17+
FRIDAY,
18+
SATURDAY,
19+
}
20+
21+
export enum DayOfWeekName {
22+
SUNDAY = 'Sunday',
23+
MONDAY = 'Monday',
24+
TUESDAY = 'Tuesday',
25+
WEDNESDAY = 'Wednesday',
26+
THURSDAY = 'Thursday',
27+
FRIDAY = 'Friday',
28+
SATURDAY = 'Saturday',
29+
}

src/shared/services/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './cronService';

src/shared/ui/Calendar/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Dayjs } from 'dayjs';
2+
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
3+
import generateCalendar from 'antd/es/calendar/generateCalendar';
4+
5+
export const Calendar = generateCalendar<Dayjs>(dayjsGenerateConfig);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import { Period } from '@shared/services';
3+
import { Select } from 'antd';
4+
5+
import classes from '../../styles.module.less';
6+
import { DAYS_OF_MONTH_SELECT_OPTIONS, DAYS_OF_WEEK_SELECT_OPTIONS } from '../../constants';
7+
8+
import { DynamicSelectProps } from './types';
9+
10+
export const DynamicSelect = ({
11+
periodSelectValue,
12+
weekDay,
13+
monthDay,
14+
onChangeWeekDay,
15+
onChangeMonthDay,
16+
}: DynamicSelectProps) => {
17+
switch (periodSelectValue) {
18+
case Period.WEEK:
19+
return (
20+
<Select
21+
className={classes.day}
22+
size="large"
23+
onChange={onChangeWeekDay}
24+
options={DAYS_OF_WEEK_SELECT_OPTIONS}
25+
value={weekDay}
26+
/>
27+
);
28+
case Period.MONTH:
29+
return (
30+
<Select
31+
className={classes.date}
32+
size="large"
33+
onChange={onChangeMonthDay}
34+
options={DAYS_OF_MONTH_SELECT_OPTIONS}
35+
value={monthDay}
36+
/>
37+
);
38+
default:
39+
return null;
40+
}
41+
};

0 commit comments

Comments
 (0)