From 91259c4fc7b7d05f78155b11b993aeaebf1d205e Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 20:54:38 +0000 Subject: [PATCH] Make form depend on switches --- .../WorkspaceScheduleForm.tsx | 62 +++------ .../WorkspaceSchedulePage.tsx | 118 ++++++++++-------- site/src/pages/WorkspacesPage/schedule.ts | 93 ++++++++++++++ site/src/pages/WorkspacesPage/ttl.ts | 14 +++ 4 files changed, 189 insertions(+), 98 deletions(-) create mode 100644 site/src/pages/WorkspacesPage/schedule.ts create mode 100644 site/src/pages/WorkspacesPage/ttl.ts diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 273ba64319..96924398df 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -17,6 +17,8 @@ import relativeTime from "dayjs/plugin/relativeTime" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { FormikTouched, useFormik } from "formik" +import { AutoStart } from "pages/WorkspacesPage/schedule" +import { AutoStop } from "pages/WorkspacesPage/ttl" import { FC } from "react" import * as Yup from "yup" import { getFormHelpersWithError } from "../../util/formUtils" @@ -57,7 +59,10 @@ export const Language = { export interface WorkspaceScheduleFormProps { submitScheduleError?: Error | unknown - initialValues?: WorkspaceScheduleFormValues + autoStart: AutoStart + toggleAutoStart: () => void + autoStop: AutoStop + toggleAutoStop: () => void isLoading: boolean onCancel: () => void onSubmit: (values: WorkspaceScheduleFormValues) => void @@ -162,54 +167,23 @@ export const validationSchema = Yup.object({ .max(24 * 7 /* 7 days */), }) -export const defaultWorkspaceScheduleTTL = 8 - -export const emptyWorkspaceSchedule = ( - ttl = 0, - timezone = dayjs.tz.guess(), -): WorkspaceScheduleFormValues => ({ - sunday: false, - monday: false, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - - startTime: "", - timezone, - ttl, -}) - -export const defaultWorkspaceSchedule = ( - ttl = defaultWorkspaceScheduleTTL, - timezone = dayjs.tz.guess(), -): WorkspaceScheduleFormValues => ({ - sunday: false, - monday: true, - tuesday: true, - wednesday: true, - thursday: true, - friday: true, - saturday: false, - - startTime: "09:30", - timezone, - ttl, -}) - export const WorkspaceScheduleForm: FC = ({ submitScheduleError, - initialValues = defaultWorkspaceSchedule(), + autoStart, + toggleAutoStart, + autoStop, + toggleAutoStop, isLoading, onCancel, onSubmit, initialTouched, }) => { const styles = useStyles() + const initialValues = { ...autoStart.schedule, ttl: autoStop.ttl } const form = useFormik({ initialValues, + enableReinitialize: true, onSubmit, validationSchema, initialTouched, @@ -236,12 +210,12 @@ export const WorkspaceScheduleForm: FC = ({ {submitScheduleError && }
} + control={} label="Auto-start" /> = ({ = ({ control={ = ({
} + control={} label="Auto-Stop" /> { - const schedule = workspace.autostart_schedule - const ttlHours = workspace.ttl_ms - ? Math.round(workspace.ttl_ms / (1000 * 60 * 60)) - : 0 - - if (!schedule) { - return emptyWorkspaceSchedule(ttlHours, defaultTimeZone) - } - - const timezone = extractTimezone(schedule, defaultTimeZone) - - const expression = cronParser.parseExpression(stripTimezone(schedule)) - - const HH = expression.fields.hour.join("").padStart(2, "0") - const mm = expression.fields.minute.join("").padStart(2, "0") - - const weeklyFlags = [false, false, false, false, false, false, false] - - for (const day of expression.fields.dayOfWeek) { - weeklyFlags[day % 7] = true - } - - return { - sunday: weeklyFlags[0], - monday: weeklyFlags[1], - tuesday: weeklyFlags[2], - wednesday: weeklyFlags[3], - thursday: weeklyFlags[4], - friday: weeklyFlags[5], - saturday: weeklyFlags[6], - startTime: `${HH}:${mm}`, - timezone, - ttl: ttlHours, - } -} - export const WorkspaceSchedulePage: React.FC = () => { const { username: usernameQueryParam, workspace: workspaceQueryParam } = useParams() const navigate = useNavigate() @@ -166,6 +116,63 @@ export const WorkspaceSchedulePage: React.FC = () => { username && workspaceName && scheduleSend({ type: "GET_WORKSPACE", username, workspaceName }) }, [username, workspaceName, scheduleSend]) + const getAutoStart = (workspace?: TypesGen.Workspace) => scheduleToAutoStart(workspace?.autostart_schedule) + const getAutoStop = (workspace?: TypesGen.Workspace) => ttlMsToAutoStop(workspace?.ttl_ms) + + const [autoStart, setAutoStart] = useState(getAutoStart(workspace)) + const [autoStop, setAutoStop] = useState(getAutoStop(workspace)) + + useEffect(() => { + setAutoStart(getAutoStart(workspace)) + setAutoStop(getAutoStop(workspace)) + }, [workspace]) + + const onToggleAutoStart = () => { + if (autoStart.enabled) { + setAutoStart({ + enabled: false, + schedule: emptySchedule + }) + } else { + if (workspace?.autostart_schedule) { + // repopulate saved schedule + setAutoStart({ + enabled: true, + schedule: getAutoStart(workspace).schedule + }) + } else { + // populate with defaults + setAutoStart({ + enabled: true, + schedule: defaultSchedule() + }) + } + } + } + + const onToggleAutoStop = () => { + if (autoStop.enabled) { + setAutoStop({ + enabled: false, + ttl: emptyTTL + }) + } else { + if (workspace?.ttl_ms) { + // repopulate saved ttl + setAutoStop({ + enabled: true, + ttl: getAutoStop(workspace).ttl + }) + } else { + // set default + setAutoStop({ + enabled: true, + ttl: defaultTTL + }) + } + } + } + if (!username || !workspaceName) { navigate("/workspaces") return null @@ -200,7 +207,10 @@ export const WorkspaceSchedulePage: React.FC = () => { return ( { navigate(`/@${username}/${workspaceName}`) diff --git a/site/src/pages/WorkspacesPage/schedule.ts b/site/src/pages/WorkspacesPage/schedule.ts new file mode 100644 index 0000000000..d39d261ef7 --- /dev/null +++ b/site/src/pages/WorkspacesPage/schedule.ts @@ -0,0 +1,93 @@ +import * as cronParser from "cron-parser" +import dayjs from "dayjs" +import timezone from "dayjs/plugin/timezone" +import utc from "dayjs/plugin/utc" +import { extractTimezone, stripTimezone } from "../../util/schedule" + +// REMARK: timezone plugin depends on UTC +// +// SEE: https://day.js.org/docs/en/timezone/timezone +dayjs.extend(utc) +dayjs.extend(timezone) + +export interface AutoStartSchedule { + sunday: boolean + monday: boolean + tuesday: boolean + wednesday: boolean + thursday: boolean + friday: boolean + saturday: boolean + startTime: string + timezone: string +} + +export interface AutoStart { + enabled: boolean + schedule: AutoStartSchedule +} + +export const emptySchedule = { + sunday: false, + monday: false, + tuesday: false, + wednesday: false, + thursday: false, + friday: false, + saturday: false, + + startTime: "", + timezone: "", +} + +export const defaultSchedule = ( +): AutoStartSchedule => ({ + sunday: false, + monday: true, + tuesday: true, + wednesday: true, + thursday: true, + friday: true, + saturday: false, + + startTime: "09:30", + timezone: dayjs.tz.guess(), +}) + +const transformSchedule = (schedule: string) => { + const timezone = extractTimezone(schedule, dayjs.tz.guess()) + + const expression = cronParser.parseExpression(stripTimezone(schedule)) + + const HH = expression.fields.hour.join("").padStart(2, "0") + const mm = expression.fields.minute.join("").padStart(2, "0") + + const weeklyFlags = [false, false, false, false, false, false, false] + + for (const day of expression.fields.dayOfWeek) { + weeklyFlags[day % 7] = true + } + + return { + sunday: weeklyFlags[0], + monday: weeklyFlags[1], + tuesday: weeklyFlags[2], + wednesday: weeklyFlags[3], + thursday: weeklyFlags[4], + friday: weeklyFlags[5], + saturday: weeklyFlags[6], + startTime: `${HH}:${mm}`, + timezone, + } +} + +export const scheduleToAutoStart = (schedule?: string): AutoStart => { + if (schedule) { + return { + enabled: true, + schedule: transformSchedule(schedule) + } + } else { + return { enabled: false, schedule: emptySchedule } + } +} diff --git a/site/src/pages/WorkspacesPage/ttl.ts b/site/src/pages/WorkspacesPage/ttl.ts new file mode 100644 index 0000000000..48459eeffb --- /dev/null +++ b/site/src/pages/WorkspacesPage/ttl.ts @@ -0,0 +1,14 @@ +export interface AutoStop { + enabled: boolean + ttl: number +} + +export const emptyTTL = 0 + +export const defaultTTL = 8 + +const msToHours = (ms: number) => Math.round(ms / (1000 * 60 * 60)) + +export const ttlMsToAutoStop = (ttl_ms?: number): AutoStop => ( + ttl_ms ? { enabled: true, ttl: msToHours(ttl_ms) } : { enabled: false, ttl: 0 } +)