diff --git a/packages/api-server/api_server/models/tortoise_models/scheduled_task.py b/packages/api-server/api_server/models/tortoise_models/scheduled_task.py index 323d153e6..6b733feea 100644 --- a/packages/api-server/api_server/models/tortoise_models/scheduled_task.py +++ b/packages/api-server/api_server/models/tortoise_models/scheduled_task.py @@ -68,7 +68,9 @@ def to_job(self) -> Job: job = schedule.every() if self.until is not None: # schedule uses `datetime.now()`, which is tz naive - job = job.until(datetime.fromtimestamp(self.until.timestamp())) + # Assuming self.until is a datetime object with timezone information + # Convert the timestamp to datetime without changing the timezone + job = job.until(datetime.utcfromtimestamp(self.until.timestamp())) if self.period in ( ScheduledTaskSchedule.Period.Monday, diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 4ba1c1a3a..dec29e0dd 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -123,16 +123,17 @@ function toApiSchedule(taskRequest: TaskRequest, schedule: Schedule): PostSchedu const apiSchedules: PostScheduledTaskRequest['schedules'] = []; const date = new Date(start); const start_from = start.toISOString(); + const until = schedule.until?.toISOString(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); const at = `${hours}:${minutes}`; - schedule.days[0] && apiSchedules.push({ period: 'monday', start_from, at }); - schedule.days[1] && apiSchedules.push({ period: 'tuesday', start_from, at }); - schedule.days[2] && apiSchedules.push({ period: 'wednesday', start_from, at }); - schedule.days[3] && apiSchedules.push({ period: 'thursday', start_from, at }); - schedule.days[4] && apiSchedules.push({ period: 'friday', start_from, at }); - schedule.days[5] && apiSchedules.push({ period: 'saturday', start_from, at }); - schedule.days[6] && apiSchedules.push({ period: 'sunday', start_from, at }); + schedule.days[0] && apiSchedules.push({ period: 'monday', start_from, at, until }); + schedule.days[1] && apiSchedules.push({ period: 'tuesday', start_from, at, until }); + schedule.days[2] && apiSchedules.push({ period: 'wednesday', start_from, at, until }); + schedule.days[3] && apiSchedules.push({ period: 'thursday', start_from, at, until }); + schedule.days[4] && apiSchedules.push({ period: 'friday', start_from, at, until }); + schedule.days[5] && apiSchedules.push({ period: 'saturday', start_from, at, until }); + schedule.days[6] && apiSchedules.push({ period: 'sunday', start_from, at, until }); return { task_request: taskRequest, schedules: apiSchedules, diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 7d9e3cfbb..d77a0f43f 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -16,6 +16,9 @@ import { DialogProps, DialogTitle, Divider, + FormControl, + FormControlLabel, + FormHelperText, Grid, IconButton, List, @@ -24,6 +27,8 @@ import { ListItemSecondaryAction, ListItemText, MenuItem, + Radio, + RadioGroup, styled, TextField, Typography, @@ -652,6 +657,12 @@ export type RecurringDays = [boolean, boolean, boolean, boolean, boolean, boolea export interface Schedule { startOn: Date; days: RecurringDays; + until?: Date; +} + +enum ScheduleUntilValue { + NEVER = 'never', + ON = 'on', } interface DaySelectorSwitchProps { @@ -781,8 +792,28 @@ export function CreateTaskForm({ const [schedule, setSchedule] = React.useState({ startOn: new Date(), days: [true, true, true, true, true, true, true], + until: undefined, }); const [atTime, setAtTime] = React.useState(new Date()); + const [scheduleUntilValue, setScheduleUntilValue] = React.useState( + ScheduleUntilValue.NEVER, + ); + + const handleScheduleUntilValue = (event: React.ChangeEvent) => { + if (event.target.value === ScheduleUntilValue.ON) { + /** + * Since the time change is done in the onchange of the Datepicker, + * we need a defined time if the user saves the value without calling the onChange event of the datepicker + */ + const date = new Date(); + date.setHours(23); + date.setMinutes(59); + setSchedule((prev) => ({ ...prev, until: date })); + } else { + setSchedule((prev) => ({ ...prev, until: undefined })); + } + setScheduleUntilValue(event.target.value); + }; // schedule is not supported with batch upload const scheduleEnabled = taskRequests.length === 1; @@ -1246,6 +1277,46 @@ export function CreateTaskForm({ /> + + + Ends + + + } + label="Never" + /> + + + } label="On" /> + + + + date && + setSchedule((prev) => { + date.setHours(23); + date.setMinutes(59); + return { ...prev, until: date }; + }) + } + disabled={scheduleUntilValue !== ScheduleUntilValue.ON} + renderInput={(props) => } + /> + + + + )}