From 54bf130ed51418a3012674e14c4a0cb837bd5734 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Fri, 29 Jul 2022 19:59:20 +0000 Subject: [PATCH 01/26] Add elements --- site/src/components/Section/Section.tsx | 4 ++-- .../WorkspaceScheduleForm.tsx | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/site/src/components/Section/Section.tsx b/site/src/components/Section/Section.tsx index 9e2b993ed38d7..7cdb7efa85d72 100644 --- a/site/src/components/Section/Section.tsx +++ b/site/src/components/Section/Section.tsx @@ -30,7 +30,7 @@ export const Section: SectionFC = ({ }) => { const styles = useStyles({ layout }) return ( -
+
{(title || description) && (
@@ -49,7 +49,7 @@ export const Section: SectionFC = ({ {alert &&
{alert}
} {children}
-
+
) } diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 6eb500550ff38..dbc88319cf3df 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -6,8 +6,10 @@ import FormHelperText from "@material-ui/core/FormHelperText" import FormLabel from "@material-ui/core/FormLabel" import MenuItem from "@material-ui/core/MenuItem" import makeStyles from "@material-ui/core/styles/makeStyles" +import Switch from "@material-ui/core/Switch" import TextField from "@material-ui/core/TextField" import { ErrorSummary } from "components/ErrorSummary/ErrorSummary" +import { Section } from "components/Section/Section" import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import duration from "dayjs/plugin/duration" @@ -215,6 +217,11 @@ export const WorkspaceScheduleForm: FC = ({
{submitScheduleError && } +
+ } + label="Auto-start" + /> = ({ {form.errors.monday && {Language.errorNoDayOfWeek}} +
+
+ } + label="Auto-Stop" + /> = ({ label={Language.ttlLabel} type="number" /> +
From ecc113705f27be0eaffac8e1958ce47927888193 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Fri, 29 Jul 2022 21:01:29 +0000 Subject: [PATCH 02/26] Add Loading story --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index cb24e1316dc5e..811a616c4791f 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -77,3 +77,6 @@ WithError.args = { validations: [{ field: "ttl_ms", detail: "Invalid time until shutdown." }], }), } + +export const Loading = Template.bind({}) +Loading.args = { isLoading: true } From 67050760018851326509d8ff388e2056c47d45a4 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Fri, 29 Jul 2022 21:52:55 +0000 Subject: [PATCH 03/26] Make form show empty values when manual --- .../WorkspaceScheduleForm.tsx | 17 ++++++++ .../WorkspaceSchedulePage.test.tsx | 39 ++++++++++++++----- .../WorkspaceSchedulePage.tsx | 7 ++-- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index dbc88319cf3df..273ba64319a5c 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -164,6 +164,23 @@ export const validationSchema = Yup.object({ 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(), diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx index 1f440e18eea5c..61ac5ddfc7f0d 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx @@ -166,15 +166,15 @@ describe("WorkspaceSchedulePage", () => { }, { sunday: false, - monday: true, - tuesday: true, - wednesday: true, - thursday: true, - friday: true, + monday: false, + tuesday: false, + wednesday: false, + thursday: false, + friday: false, saturday: false, - startTime: "09:30", + startTime: "", timezone: "", - ttl: 8, + ttl: 0, }, ], @@ -185,6 +185,27 @@ describe("WorkspaceSchedulePage", () => { autostart_schedule: "", ttl_ms: 7_200_000, }, + { + sunday: false, + monday: false, + tuesday: false, + wednesday: false, + thursday: false, + friday: false, + saturday: false, + startTime: "", + timezone: "", + ttl: 2, + }, + ], + + // start schedule only + [ + { + ...Mocks.MockWorkspace, + autostart_schedule: "CRON_TZ=UTC 30 9 * * 1-5", + ttl_ms: undefined, + }, { sunday: false, monday: true, @@ -194,8 +215,8 @@ describe("WorkspaceSchedulePage", () => { friday: true, saturday: false, startTime: "09:30", - timezone: "", - ttl: 2, + timezone: "UTC", + ttl: 0, }, ], diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 3e74c1e17a6ad..0477fc9347b07 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -9,8 +9,7 @@ import * as TypesGen from "../../api/typesGenerated" import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary" import { FullScreenLoader } from "../../components/Loader/FullScreenLoader" import { - defaultWorkspaceSchedule, - defaultWorkspaceScheduleTTL, + emptyWorkspaceSchedule, WorkspaceScheduleForm, WorkspaceScheduleFormValues, } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" @@ -111,10 +110,10 @@ export const workspaceToInitialValues = ( const schedule = workspace.autostart_schedule const ttlHours = workspace.ttl_ms ? Math.round(workspace.ttl_ms / (1000 * 60 * 60)) - : defaultWorkspaceScheduleTTL + : 0 if (!schedule) { - return defaultWorkspaceSchedule(ttlHours, defaultTimeZone) + return emptyWorkspaceSchedule(ttlHours, defaultTimeZone) } const timezone = extractTimezone(schedule, defaultTimeZone) From 91259c4fc7b7d05f78155b11b993aeaebf1d205e Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 20:54:38 +0000 Subject: [PATCH 04/26] 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 273ba64319a5c..96924398dfa82 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 0000000000000..d39d261ef7711 --- /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 0000000000000..48459eeffb4e7 --- /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 } +) From 70228bd14b71ed60641e6f81982add66170caedd Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 20:55:55 +0000 Subject: [PATCH 05/26] Fix style --- .../components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 96924398dfa82..8186cf352559d 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -221,6 +221,7 @@ export const WorkspaceScheduleForm: FC = ({ }} label={Language.startTimeLabel} type="time" + fullWidth /> = ({ }} label={Language.timezoneLabel} select + fullWidth > {zones.map((zone) => ( @@ -279,6 +281,7 @@ export const WorkspaceScheduleForm: FC = ({ inputProps={{ min: 0, step: 1 }} label={Language.ttlLabel} type="number" + fullWidth />
From 52b26bf421de05ac6a2b025e401ddfd979f541ab Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 20:56:15 +0000 Subject: [PATCH 06/26] Format --- .../WorkspaceScheduleForm.tsx | 134 +++++++++--------- .../WorkspaceSchedulePage.tsx | 17 +-- site/src/pages/WorkspacesPage/schedule.ts | 5 +- site/src/pages/WorkspacesPage/ttl.ts | 3 +- 4 files changed, 79 insertions(+), 80 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 8186cf352559d..b63ed4edfbb37 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -209,80 +209,80 @@ export const WorkspaceScheduleForm: FC = ({ {submitScheduleError && }
- } - label="Auto-start" - /> - + } + label="Auto-start" + /> + - - {zones.map((zone) => ( - - {zone} - - ))} - + + {zones.map((zone) => ( + + {zone} + + ))} + - - - {Language.daysOfWeekLabel} - + + + {Language.daysOfWeekLabel} + - - {checkboxes.map((checkbox) => ( - - } - key={checkbox.name} - label={checkbox.label} - /> - ))} - + + {checkboxes.map((checkbox) => ( + + } + key={checkbox.name} + label={checkbox.label} + /> + ))} + - {form.errors.monday && {Language.errorNoDayOfWeek}} - + {form.errors.monday && {Language.errorNoDayOfWeek}} +
- } - label="Auto-Stop" - /> - + } + label="Auto-Stop" + /> +
diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 70bf14848caf2..bc3a113b94ef5 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,6 +1,6 @@ import { useMachine, useSelector } from "@xstate/react" import { defaultSchedule, emptySchedule, scheduleToAutoStart } from "pages/WorkspacesPage/schedule" -import { ttlMsToAutoStop, emptyTTL, defaultTTL } from "pages/WorkspacesPage/ttl" +import { defaultTTL, emptyTTL, ttlMsToAutoStop } from "pages/WorkspacesPage/ttl" import React, { useContext, useEffect, useState } from "react" import { useNavigate, useParams } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" @@ -116,7 +116,8 @@ 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 getAutoStart = (workspace?: TypesGen.Workspace) => + scheduleToAutoStart(workspace?.autostart_schedule) const getAutoStop = (workspace?: TypesGen.Workspace) => ttlMsToAutoStop(workspace?.ttl_ms) const [autoStart, setAutoStart] = useState(getAutoStart(workspace)) @@ -131,20 +132,20 @@ export const WorkspaceSchedulePage: React.FC = () => { if (autoStart.enabled) { setAutoStart({ enabled: false, - schedule: emptySchedule + schedule: emptySchedule, }) } else { if (workspace?.autostart_schedule) { // repopulate saved schedule setAutoStart({ enabled: true, - schedule: getAutoStart(workspace).schedule + schedule: getAutoStart(workspace).schedule, }) } else { // populate with defaults setAutoStart({ enabled: true, - schedule: defaultSchedule() + schedule: defaultSchedule(), }) } } @@ -154,20 +155,20 @@ export const WorkspaceSchedulePage: React.FC = () => { if (autoStop.enabled) { setAutoStop({ enabled: false, - ttl: emptyTTL + ttl: emptyTTL, }) } else { if (workspace?.ttl_ms) { // repopulate saved ttl setAutoStop({ enabled: true, - ttl: getAutoStop(workspace).ttl + ttl: getAutoStop(workspace).ttl, }) } else { // set default setAutoStop({ enabled: true, - ttl: defaultTTL + ttl: defaultTTL, }) } } diff --git a/site/src/pages/WorkspacesPage/schedule.ts b/site/src/pages/WorkspacesPage/schedule.ts index d39d261ef7711..6518872cb68f9 100644 --- a/site/src/pages/WorkspacesPage/schedule.ts +++ b/site/src/pages/WorkspacesPage/schedule.ts @@ -40,8 +40,7 @@ export const emptySchedule = { timezone: "", } -export const defaultSchedule = ( -): AutoStartSchedule => ({ +export const defaultSchedule = (): AutoStartSchedule => ({ sunday: false, monday: true, tuesday: true, @@ -85,7 +84,7 @@ export const scheduleToAutoStart = (schedule?: string): AutoStart => { if (schedule) { return { enabled: true, - schedule: transformSchedule(schedule) + 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 index 48459eeffb4e7..4035ab09edeb9 100644 --- a/site/src/pages/WorkspacesPage/ttl.ts +++ b/site/src/pages/WorkspacesPage/ttl.ts @@ -9,6 +9,5 @@ export const defaultTTL = 8 const msToHours = (ms: number) => Math.round(ms / (1000 * 60 * 60)) -export const ttlMsToAutoStop = (ttl_ms?: number): AutoStop => ( +export const ttlMsToAutoStop = (ttl_ms?: number): AutoStop => ttl_ms ? { enabled: true, ttl: msToHours(ttl_ms) } : { enabled: false, ttl: 0 } -) From b22425865dbe967f88ba2e55eba04758caecd33f Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 21:14:58 +0000 Subject: [PATCH 07/26] Update unit tests --- .../WorkspaceSchedulePage.test.tsx | 167 +++++++----------- 1 file changed, 63 insertions(+), 104 deletions(-) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx index 61ac5ddfc7f0d..29f564fce6274 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx @@ -1,11 +1,8 @@ +import { AutoStart, scheduleToAutoStart } from "pages/WorkspacesPage/schedule" +import { AutoStop, ttlMsToAutoStop } from "pages/WorkspacesPage/ttl" import * as TypesGen from "../../api/typesGenerated" import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" -import * as Mocks from "../../testHelpers/entities" -import { - formValuesToAutoStartRequest, - formValuesToTTLRequest, - workspaceToInitialValues, -} from "./WorkspaceSchedulePage" +import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "./WorkspaceSchedulePage" const validValues: WorkspaceScheduleFormValues = { sunday: false, @@ -155,117 +152,79 @@ describe("WorkspaceSchedulePage", () => { }) }) - describe("workspaceToInitialValues", () => { - it.each<[TypesGen.Workspace, WorkspaceScheduleFormValues]>([ + describe("scheduleToAutoStart", () => { + it.each<[string | undefined, AutoStart]>([ // Empty case [ - { - ...Mocks.MockWorkspace, - autostart_schedule: undefined, - ttl_ms: undefined, - }, - { - sunday: false, - monday: false, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - startTime: "", - timezone: "", - ttl: 0, + undefined, + { + enabled: false, + schedule: { + sunday: false, + monday: false, + tuesday: false, + wednesday: false, + thursday: false, + friday: false, + saturday: false, + startTime: "", + timezone: "", + }, }, ], - // ttl-only case (2 hours) + // Basic case: 9:30 1-5 UTC [ - { - ...Mocks.MockWorkspace, - autostart_schedule: "", - ttl_ms: 7_200_000, - }, - { - sunday: false, - monday: false, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - startTime: "", - timezone: "", - ttl: 2, + "CRON_TZ=UTC 30 9 * * 1-5", + { + enabled: true, + schedule: { + sunday: false, + monday: true, + tuesday: true, + wednesday: true, + thursday: true, + friday: true, + saturday: false, + startTime: "09:30", + timezone: "UTC", + }, }, ], - // start schedule only + // Complex case: 4:20 1 3-4 6 Canada/Eastern [ - { - ...Mocks.MockWorkspace, - autostart_schedule: "CRON_TZ=UTC 30 9 * * 1-5", - ttl_ms: undefined, - }, - { - sunday: false, - monday: true, - tuesday: true, - wednesday: true, - thursday: true, - friday: true, - saturday: false, - startTime: "09:30", - timezone: "UTC", - ttl: 0, - }, - ], - - // Basic case: 9:30 1-5 UTC running for 2 hours - // - // NOTE: We have to set CRON_TZ here because otherwise this test will - // flake based off of where it runs! - [ - { - ...Mocks.MockWorkspace, - autostart_schedule: "CRON_TZ=UTC 30 9 * * 1-5", - ttl_ms: 7_200_000, - }, - { - sunday: false, - monday: true, - tuesday: true, - wednesday: true, - thursday: true, - friday: true, - saturday: false, - startTime: "09:30", - timezone: "UTC", - ttl: 2, + "CRON_TZ=Canada/Eastern 20 16 * * 1,3-4,6", + { + enabled: true, + schedule: { + sunday: false, + monday: true, + tuesday: false, + wednesday: true, + thursday: true, + friday: false, + saturday: true, + startTime: "16:20", + timezone: "Canada/Eastern", + }, }, ], + ])(`scheduleToAutoStart(%p) returns %p`, (schedule, autoStart) => { + expect(scheduleToAutoStart(schedule)).toEqual(autoStart) + }) + }) - // Complex case: 4:20 1 3-4 6 Canada/Eastern for 8 hours - [ - { - ...Mocks.MockWorkspace, - autostart_schedule: "CRON_TZ=Canada/Eastern 20 16 * * 1,3-4,6", - ttl_ms: 28_800_000, - }, - { - sunday: false, - monday: true, - tuesday: false, - wednesday: true, - thursday: true, - friday: false, - saturday: true, - startTime: "16:20", - timezone: "Canada/Eastern", - ttl: 8, - }, - ], - ])(`workspaceToInitialValues(%p) returns %p`, (workspace, formValues) => { - expect(workspaceToInitialValues(workspace)).toEqual(formValues) + describe("ttlMsToAutoStop", () => { + it.each<[number | undefined, AutoStop]>([ + // empty case + [undefined, { enabled: false, ttl: 0 }], + // zero + [0, { enabled: false, ttl: 0 }], + // basic case + [28_800_000, { enabled: true, ttl: 8 }], + ])(`ttlMsToAutoStop(%p) returns %p`, (ttlMs, autoStop) => { + expect(ttlMsToAutoStop(ttlMs)).toEqual(autoStop) }) }) }) From 4f68bb16e00c94b1a4a3944450f5e896eedbd736 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 1 Aug 2022 22:25:49 +0000 Subject: [PATCH 08/26] Tweaks --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 2 +- .../WorkspaceSchedulePage/WorkspaceSchedulePage.tsx | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index b63ed4edfbb37..d9699ff56d8e4 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -273,7 +273,7 @@ export const WorkspaceScheduleForm: FC = ({
} - label="Auto-Stop" + label="Auto-stop" /> { } if (!username || !workspaceName) { - navigate("/workspaces") - return null + return } if ( @@ -228,12 +227,10 @@ export const WorkspaceSchedulePage: React.FC = () => { } if (scheduleState.matches("submitSuccess")) { - navigate(`/@${username}/${workspaceName}`) - return + return } // Theoretically impossible - log and bail console.error("WorkspaceSchedulePage: unknown state :: ", scheduleState) - navigate("/") - return null + return } From ff48adf8a1daef0c490982999894f77d3e35dc5f Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 2 Aug 2022 14:43:21 +0000 Subject: [PATCH 09/26] Update storybook --- .../WorkspaceScheduleForm.stories.tsx | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index 811a616c4791f..e6e2e9613717a 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -3,12 +3,10 @@ import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" +import { defaultSchedule, emptySchedule } from "pages/WorkspacesPage/schedule" +import { emptyTTL } from "pages/WorkspacesPage/ttl" import { makeMockApiError } from "testHelpers/entities" -import { - defaultWorkspaceSchedule, - WorkspaceScheduleForm, - WorkspaceScheduleFormProps, -} from "./WorkspaceScheduleForm" +import { WorkspaceScheduleForm, WorkspaceScheduleFormProps } from "./WorkspaceScheduleForm" dayjs.extend(advancedFormat) dayjs.extend(utc) @@ -24,53 +22,51 @@ export default { onSubmit: { action: "onSubmit", }, + toggleAutoStart: { + action: "toggleAutoStart", + }, + toggleAutoStop: { + action: "toggleAutoStop", + }, }, } const Template: Story = (args) => -export const WorkspaceWillNotShutDown = Template.bind({}) -WorkspaceWillNotShutDown.args = { - initialValues: { - ...defaultWorkspaceSchedule(5), - ttl: 0, - }, +export const AllDisabled = Template.bind({}) +AllDisabled.args = { + autoStart: { enabled: false, schedule: emptySchedule }, + autoStop: { enabled: false, ttl: emptyTTL }, } -export const WorkspaceWillShutdownInAnHour = Template.bind({}) -WorkspaceWillShutdownInAnHour.args = { - initialValues: { - ...defaultWorkspaceSchedule(5), - ttl: 1, - }, +export const AutoStart = Template.bind({}) +AutoStart.args = { + autoStart: { enabled: true, schedule: defaultSchedule() }, + autoStop: { enabled: false, ttl: emptyTTL }, } export const WorkspaceWillShutdownInTwoHours = Template.bind({}) WorkspaceWillShutdownInTwoHours.args = { - initialValues: { - ...defaultWorkspaceSchedule(2), - ttl: 2, - }, + autoStart: { enabled: true, schedule: defaultSchedule() }, + autoStop: { enabled: true, ttl: 2 }, } export const WorkspaceWillShutdownInADay = Template.bind({}) WorkspaceWillShutdownInADay.args = { - initialValues: { - ...defaultWorkspaceSchedule(2), - ttl: 24, - }, + autoStart: { enabled: true, schedule: defaultSchedule() }, + autoStop: { enabled: true, ttl: 24 }, } export const WorkspaceWillShutdownInTwoDays = Template.bind({}) WorkspaceWillShutdownInTwoDays.args = { - initialValues: { - ...defaultWorkspaceSchedule(2), - ttl: 48, - }, + autoStart: { enabled: true, schedule: defaultSchedule() }, + autoStop: { enabled: true, ttl: 48 }, } export const WithError = Template.bind({}) WithError.args = { + autoStart: { enabled: false, schedule: emptySchedule }, + autoStop: { enabled: true, ttl: 100 }, initialTouched: { ttl: true }, submitScheduleError: makeMockApiError({ message: "Something went wrong.", @@ -79,4 +75,8 @@ WithError.args = { } export const Loading = Template.bind({}) -Loading.args = { isLoading: true } +Loading.args = { + autoStart: { enabled: true, schedule: defaultSchedule() }, + autoStop: { enabled: true, ttl: 2 }, + isLoading: true, +} From bbaee7d04384f94724b2739f1cca0b59b174ddaa Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 18:21:00 +0000 Subject: [PATCH 10/26] Move util files --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx | 4 ++-- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 4 ++-- .../WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx | 4 ++-- .../src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx | 4 ++-- .../{WorkspacesPage => WorkspaceSchedulePage}/schedule.ts | 0 .../pages/{WorkspacesPage => WorkspaceSchedulePage}/ttl.ts | 0 6 files changed, 8 insertions(+), 8 deletions(-) rename site/src/pages/{WorkspacesPage => WorkspaceSchedulePage}/schedule.ts (100%) rename site/src/pages/{WorkspacesPage => WorkspaceSchedulePage}/ttl.ts (100%) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index e6e2e9613717a..e351f71c8cc74 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -3,8 +3,8 @@ import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" -import { defaultSchedule, emptySchedule } from "pages/WorkspacesPage/schedule" -import { emptyTTL } from "pages/WorkspacesPage/ttl" +import { defaultSchedule, emptySchedule } from "pages/WorkspaceSchedulePage/schedule" +import { emptyTTL } from "pages/WorkspaceSchedulePage/ttl" import { makeMockApiError } from "testHelpers/entities" import { WorkspaceScheduleForm, WorkspaceScheduleFormProps } from "./WorkspaceScheduleForm" diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index d9699ff56d8e4..3cbcc4603503f 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -17,8 +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 { AutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { AutoStop } from "pages/WorkspaceSchedulePage/ttl" import { FC } from "react" import * as Yup from "yup" import { getFormHelpersWithError } from "../../util/formUtils" diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx index 29f564fce6274..5d7aff3c95ed4 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx @@ -1,5 +1,5 @@ -import { AutoStart, scheduleToAutoStart } from "pages/WorkspacesPage/schedule" -import { AutoStop, ttlMsToAutoStop } from "pages/WorkspacesPage/ttl" +import { AutoStart, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import * as TypesGen from "../../api/typesGenerated" import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "./WorkspaceSchedulePage" diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 6e807a7d76276..ef63bd4b84e8e 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,6 +1,6 @@ import { useMachine, useSelector } from "@xstate/react" -import { defaultSchedule, emptySchedule, scheduleToAutoStart } from "pages/WorkspacesPage/schedule" -import { defaultTTL, emptyTTL, ttlMsToAutoStop } from "pages/WorkspacesPage/ttl" +import { defaultSchedule, emptySchedule, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { defaultTTL, emptyTTL, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useContext, useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" diff --git a/site/src/pages/WorkspacesPage/schedule.ts b/site/src/pages/WorkspaceSchedulePage/schedule.ts similarity index 100% rename from site/src/pages/WorkspacesPage/schedule.ts rename to site/src/pages/WorkspaceSchedulePage/schedule.ts diff --git a/site/src/pages/WorkspacesPage/ttl.ts b/site/src/pages/WorkspaceSchedulePage/ttl.ts similarity index 100% rename from site/src/pages/WorkspacesPage/ttl.ts rename to site/src/pages/WorkspaceSchedulePage/ttl.ts From f251efa758f9f3172dec0e4440378c2aff22c534 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 18:24:40 +0000 Subject: [PATCH 11/26] Pull out more util functions --- .../WorkspaceSchedulePage.tsx | 84 ++----------------- .../WorkspaceSchedulePage/formToRequest.ts | 74 ++++++++++++++++ 2 files changed, 81 insertions(+), 77 deletions(-) create mode 100644 site/src/pages/WorkspaceSchedulePage/formToRequest.ts diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index ef63bd4b84e8e..a59b4b0804ebe 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,19 +1,21 @@ import { useMachine, useSelector } from "@xstate/react" -import { defaultSchedule, emptySchedule, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { + defaultSchedule, + emptySchedule, + scheduleToAutoStart, +} from "pages/WorkspaceSchedulePage/schedule" import { defaultTTL, emptyTTL, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useContext, useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary" import { FullScreenLoader } from "../../components/Loader/FullScreenLoader" -import { - WorkspaceScheduleForm, - WorkspaceScheduleFormValues, -} from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" +import { WorkspaceScheduleForm } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" import { firstOrItem } from "../../util/array" import { selectUser } from "../../xServices/auth/authSelectors" import { XServiceContext } from "../../xServices/StateContext" import { workspaceSchedule } from "../../xServices/workspaceSchedule/workspaceScheduleXService" +import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "./formToRequest" const Language = { forbiddenError: "You don't have permissions to update the schedule for this workspace.", @@ -21,78 +23,6 @@ const Language = { checkPermissionsError: "Failed to fetch permissions.", } -export const formValuesToAutoStartRequest = ( - values: WorkspaceScheduleFormValues, -): TypesGen.UpdateWorkspaceAutostartRequest => { - if (!values.startTime) { - return { - schedule: "", - } - } - - const [HH, mm] = values.startTime.split(":") - - // Note: Space after CRON_TZ if timezone is defined - const preparedTZ = values.timezone ? `CRON_TZ=${values.timezone} ` : "" - - const makeCronString = (dow: string) => `${preparedTZ}${mm} ${HH} * * ${dow}` - - const days = [ - values.sunday, - values.monday, - values.tuesday, - values.wednesday, - values.thursday, - values.friday, - values.saturday, - ] - - const isEveryDay = days.every((day) => day) - - const isMonThroughFri = - !values.sunday && - values.monday && - values.tuesday && - values.wednesday && - values.thursday && - values.friday && - !values.saturday && - !values.sunday - - // Handle special cases, falling through to comma-separation - if (isEveryDay) { - return { - schedule: makeCronString("*"), - } - } else if (isMonThroughFri) { - return { - schedule: makeCronString("1-5"), - } - } else { - const dow = days.reduce((previous, current, idx) => { - if (!current) { - return previous - } else { - const prefix = previous ? "," : "" - return previous + prefix + idx - } - }, "") - - return { - schedule: makeCronString(dow), - } - } -} - -export const formValuesToTTLRequest = ( - values: WorkspaceScheduleFormValues, -): TypesGen.UpdateWorkspaceTTLRequest => { - return { - // minutes to nanoseconds - ttl_ms: values.ttl ? values.ttl * 60 * 60 * 1000 : undefined, - } -} - export const WorkspaceSchedulePage: React.FC = () => { const { username: usernameQueryParam, workspace: workspaceQueryParam } = useParams() const navigate = useNavigate() diff --git a/site/src/pages/WorkspaceSchedulePage/formToRequest.ts b/site/src/pages/WorkspaceSchedulePage/formToRequest.ts new file mode 100644 index 0000000000000..88dafe245d5b9 --- /dev/null +++ b/site/src/pages/WorkspaceSchedulePage/formToRequest.ts @@ -0,0 +1,74 @@ +import * as TypesGen from "api/typesGenerated" +import { WorkspaceScheduleFormValues } from "components/WorkspaceScheduleForm/WorkspaceScheduleForm" + +export const formValuesToAutoStartRequest = ( + values: WorkspaceScheduleFormValues, +): TypesGen.UpdateWorkspaceAutostartRequest => { + if (!values.startTime) { + return { + schedule: "", + } + } + + const [HH, mm] = values.startTime.split(":") + + // Note: Space after CRON_TZ if timezone is defined + const preparedTZ = values.timezone ? `CRON_TZ=${values.timezone} ` : "" + + const makeCronString = (dow: string) => `${preparedTZ}${mm} ${HH} * * ${dow}` + + const days = [ + values.sunday, + values.monday, + values.tuesday, + values.wednesday, + values.thursday, + values.friday, + values.saturday, + ] + + const isEveryDay = days.every((day) => day) + + const isMonThroughFri = + !values.sunday && + values.monday && + values.tuesday && + values.wednesday && + values.thursday && + values.friday && + !values.saturday && + !values.sunday + + // Handle special cases, falling through to comma-separation + if (isEveryDay) { + return { + schedule: makeCronString("*"), + } + } else if (isMonThroughFri) { + return { + schedule: makeCronString("1-5"), + } + } else { + const dow = days.reduce((previous, current, idx) => { + if (!current) { + return previous + } else { + const prefix = previous ? "," : "" + return previous + prefix + idx + } + }, "") + + return { + schedule: makeCronString(dow), + } + } +} + +export const formValuesToTTLRequest = ( + values: WorkspaceScheduleFormValues, +): TypesGen.UpdateWorkspaceTTLRequest => { + return { + // minutes to nanoseconds + ttl_ms: values.ttl ? values.ttl * 60 * 60 * 1000 : undefined, + } +} From eab6e5fc9ce5d53d48201a7dc89d05d1f210ebe3 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 18:27:44 +0000 Subject: [PATCH 12/26] Pull out strings --- .../WorkspaceScheduleForm.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 3cbcc4603503f..c9f3e01789d1c 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -55,6 +55,11 @@ export const Language = { ttlCausesShutdownHelperText: "Your workspace will shut down", ttlCausesShutdownAfterStart: "after start", ttlCausesNoShutdownHelperText: "Your workspace will not automatically shut down.", + formTitle: "Workspace schedule", + startSection: "Start", + startSwitch: "Auto-start", + stopSection: "Stop", + stopSwitch: "Auto-stop" } export interface WorkspaceScheduleFormProps { @@ -204,14 +209,14 @@ export const WorkspaceScheduleForm: FC = ({ ] return ( - + {submitScheduleError && } -
+
} - label="Auto-start" + label={Language.startSwitch} /> = ({
-
+
} - label="Auto-stop" + label={Language.stopSwitch} /> Date: Mon, 8 Aug 2022 18:47:34 +0000 Subject: [PATCH 13/26] Add border to section --- site/src/components/Section/Section.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/components/Section/Section.tsx b/site/src/components/Section/Section.tsx index 7cdb7efa85d72..40e68161b75b6 100644 --- a/site/src/components/Section/Section.tsx +++ b/site/src/components/Section/Section.tsx @@ -63,6 +63,7 @@ const useStyles = makeStyles((theme) => ({ marginBottom: theme.spacing(1), padding: theme.spacing(6), borderRadius: theme.shape.borderRadius, + border: `1px solid ${theme.palette.divider}`, [theme.breakpoints.down("sm")]: { padding: theme.spacing(4, 3, 4, 3), From a35bf20160f6d40c4f7e64c2c3da8fe05e78d34f Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 18:49:03 +0000 Subject: [PATCH 14/26] Make min ttl 1 --- .../components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index c9f3e01789d1c..4073e496b0d1c 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -168,7 +168,7 @@ export const validationSchema = Yup.object({ }), ttl: Yup.number() .integer() - .min(0) + .min(1) .max(24 * 7 /* 7 days */), }) From 750f08bee1a732a586a5cc6b547ce5df2c91c366 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 18:49:32 +0000 Subject: [PATCH 15/26] Format --- .../components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 4073e496b0d1c..583ef04cef6cd 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -59,7 +59,7 @@ export const Language = { startSection: "Start", startSwitch: "Auto-start", stopSection: "Stop", - stopSwitch: "Auto-stop" + stopSwitch: "Auto-stop", } export interface WorkspaceScheduleFormProps { From 3dfa41baf4f6f19e46cbd3f320b5c5abf38a758c Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 19:59:36 +0000 Subject: [PATCH 16/26] Fix import --- .../pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx index 5d7aff3c95ed4..66923e03db0d7 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx @@ -2,7 +2,7 @@ import { AutoStart, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/sche import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import * as TypesGen from "../../api/typesGenerated" import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" -import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "./WorkspaceSchedulePage" +import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "pages/WorkspaceSchedulePage/formToRequest" const validValues: WorkspaceScheduleFormValues = { sunday: false, From 848732a2851582f532fd0371c98a6c85a31a9a84 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 8 Aug 2022 22:51:23 +0000 Subject: [PATCH 17/26] Fix validation for falsey values --- .../WorkspaceScheduleForm.test.ts | 28 ++++++------- .../WorkspaceScheduleForm.tsx | 42 +++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts index 0b08446f0fcc8..c3b2cf1f40e69 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts @@ -1,7 +1,7 @@ import { Language, ttlShutdownAt, - validationSchema, + getValidationSchema, WorkspaceScheduleFormValues, } from "./WorkspaceScheduleForm" import { zones } from "./zones" @@ -35,7 +35,7 @@ describe("validationSchema", () => { timezone: "", ttl: 0, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).not.toThrow() }) @@ -44,7 +44,7 @@ describe("validationSchema", () => { ...valid, ttl: -1, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrow() }) @@ -59,7 +59,7 @@ describe("validationSchema", () => { friday: false, saturday: false, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorNoDayOfWeek) }) @@ -75,7 +75,7 @@ describe("validationSchema", () => { saturday: false, startTime: "", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorNoTime) }) @@ -84,7 +84,7 @@ describe("validationSchema", () => { ...valid, startTime: "16:20", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).not.toThrow() }) @@ -93,7 +93,7 @@ describe("validationSchema", () => { ...valid, startTime: "9:30", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -102,7 +102,7 @@ describe("validationSchema", () => { ...valid, startTime: "09:5", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -111,7 +111,7 @@ describe("validationSchema", () => { ...valid, startTime: "24:01", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -120,7 +120,7 @@ describe("validationSchema", () => { ...valid, startTime: "09:60", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -129,7 +129,7 @@ describe("validationSchema", () => { ...valid, timezone: "Canada/North", } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError(Language.errorTimezone) }) @@ -138,7 +138,7 @@ describe("validationSchema", () => { ...valid, timezone: zone, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).not.toThrow() }) @@ -147,7 +147,7 @@ describe("validationSchema", () => { ...valid, ttl: 24 * 7, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).not.toThrowError() }) @@ -156,7 +156,7 @@ describe("validationSchema", () => { ...valid, ttl: 24 * 7 + 1, } - const validate = () => validationSchema.validateSync(values) + const validate = () => getValidationSchema(true, true).validateSync(values) expect(validate).toThrowError("ttl must be less than or equal to 168") }) }) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 583ef04cef6cd..8fb8f4cf7c2ef 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -21,6 +21,7 @@ import { AutoStart } from "pages/WorkspaceSchedulePage/schedule" import { AutoStop } from "pages/WorkspaceSchedulePage/ttl" import { FC } from "react" import * as Yup from "yup" +import { OptionalObjectSchema } from "yup/lib/object" import { getFormHelpersWithError } from "../../util/formUtils" import { FormFooter } from "../FormFooter/FormFooter" import { FullPageForm } from "../FullPageForm/FullPageForm" @@ -36,10 +37,11 @@ dayjs.extend(relativeTime) dayjs.extend(timezone) export const Language = { - errorNoDayOfWeek: "Must set at least one day of week if start time is set", - errorNoTime: "Start time is required when days of the week are selected", + errorNoDayOfWeek: "Must set at least one day of week if auto-start is enabled", + errorNoTime: "Start time is required when auto-start is enabled", errorTime: "Time must be in HH:mm format (24 hours)", errorTimezone: "Invalid timezone", + errorNoStop: "Time until shutdown must be greater than zero when auto-stop is enabled", daysOfWeekLabel: "Days of Week", daySundayLabel: "Sunday", dayMondayLabel: "Monday", @@ -89,12 +91,13 @@ export interface WorkspaceScheduleFormValues { ttl: number } -export const validationSchema = Yup.object({ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const getValidationSchema = (autoStartEnabled: boolean, autoStopEnabled: boolean) => (Yup.object({ sunday: Yup.boolean(), monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { const parent = this.parent as WorkspaceScheduleFormValues - if (!parent.startTime) { + if (!autoStartEnabled) { return true } else { return ![ @@ -116,20 +119,8 @@ export const validationSchema = Yup.object({ startTime: Yup.string() .ensure() - .test("required-if-day-selected", Language.errorNoTime, function (value) { - const parent = this.parent as WorkspaceScheduleFormValues - - const isDaySelected = [ - parent.sunday, - parent.monday, - parent.tuesday, - parent.wednesday, - parent.thursday, - parent.friday, - parent.saturday, - ].some((day) => day) - - if (isDaySelected) { + .test("required-if-auto-start", Language.errorNoTime, function (value) { + if (autoStartEnabled) { return value !== "" } else { return true @@ -168,9 +159,16 @@ export const validationSchema = Yup.object({ }), ttl: Yup.number() .integer() - .min(1) - .max(24 * 7 /* 7 days */), -}) + .min(0) + .max(24 * 7 /* 7 days */) + .test("positive-if-auto-stop", Language.errorNoStop, (value) => { + if (autoStopEnabled) { + return !!value + } else { + return true + } + }), +})) export const WorkspaceScheduleForm: FC = ({ submitScheduleError, @@ -190,7 +188,7 @@ export const WorkspaceScheduleForm: FC = ({ initialValues, enableReinitialize: true, onSubmit, - validationSchema, + validationSchema: () => getValidationSchema(autoStart.enabled, autoStop.enabled), initialTouched, }) const formHelpers = getFormHelpersWithError( From bfbabe121300d06d49c7120c87c2d8dc4b21dc9b Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 9 Aug 2022 18:24:28 +0000 Subject: [PATCH 18/26] Format and fix tests --- .../WorkspaceScheduleForm.test.ts | 14 +- .../WorkspaceScheduleForm.tsx | 138 +++++++++--------- .../WorkspaceSchedulePage.test.tsx | 5 +- 3 files changed, 80 insertions(+), 77 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts index c3b2cf1f40e69..49d97a3c7c5dd 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts @@ -1,7 +1,7 @@ import { + getValidationSchema, Language, ttlShutdownAt, - getValidationSchema, WorkspaceScheduleFormValues, } from "./WorkspaceScheduleForm" import { zones } from "./zones" @@ -21,7 +21,7 @@ const valid: WorkspaceScheduleFormValues = { } describe("validationSchema", () => { - it("allows everything to be falsy", () => { + it("allows everything to be falsy when switches are off", () => { const values: WorkspaceScheduleFormValues = { sunday: false, monday: false, @@ -35,7 +35,7 @@ describe("validationSchema", () => { timezone: "", ttl: 0, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => getValidationSchema(false, false).validateSync(values) expect(validate).not.toThrow() }) @@ -48,7 +48,7 @@ describe("validationSchema", () => { expect(validate).toThrow() }) - it("disallows all days-of-week to be false when startTime is set", () => { + it("disallows all days-of-week to be false when auto-start is enabled", () => { const values: WorkspaceScheduleFormValues = { ...valid, sunday: false, @@ -59,11 +59,11 @@ describe("validationSchema", () => { friday: false, saturday: false, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => getValidationSchema(true, false).validateSync(values) expect(validate).toThrowError(Language.errorNoDayOfWeek) }) - it("disallows empty startTime when at least one day is set", () => { + it("disallows empty startTime when auto-start is enabled", () => { const values: WorkspaceScheduleFormValues = { ...valid, sunday: false, @@ -75,7 +75,7 @@ describe("validationSchema", () => { saturday: false, startTime: "", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => getValidationSchema(true, false).validateSync(values) expect(validate).toThrowError(Language.errorNoTime) }) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 8fb8f4cf7c2ef..3fda342cad163 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -21,7 +21,6 @@ import { AutoStart } from "pages/WorkspaceSchedulePage/schedule" import { AutoStop } from "pages/WorkspaceSchedulePage/ttl" import { FC } from "react" import * as Yup from "yup" -import { OptionalObjectSchema } from "yup/lib/object" import { getFormHelpersWithError } from "../../util/formUtils" import { FormFooter } from "../FormFooter/FormFooter" import { FullPageForm } from "../FullPageForm/FullPageForm" @@ -92,83 +91,84 @@ export interface WorkspaceScheduleFormValues { } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export const getValidationSchema = (autoStartEnabled: boolean, autoStopEnabled: boolean) => (Yup.object({ - sunday: Yup.boolean(), - monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { - const parent = this.parent as WorkspaceScheduleFormValues - - if (!autoStartEnabled) { - return true - } else { - return ![ - parent.sunday, - value, - parent.tuesday, - parent.wednesday, - parent.thursday, - parent.friday, - parent.saturday, - ].every((day) => day === false) - } - }), - tuesday: Yup.boolean(), - wednesday: Yup.boolean(), - thursday: Yup.boolean(), - friday: Yup.boolean(), - saturday: Yup.boolean(), +export const getValidationSchema = (autoStartEnabled: boolean, autoStopEnabled: boolean) => + Yup.object({ + sunday: Yup.boolean(), + monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues - startTime: Yup.string() - .ensure() - .test("required-if-auto-start", Language.errorNoTime, function (value) { - if (autoStartEnabled) { - return value !== "" - } else { + if (!autoStartEnabled) { return true - } - }) - .test("is-time-string", Language.errorTime, (value) => { - if (value === "") { - return true - } else if (!/^[0-9][0-9]:[0-9][0-9]$/.test(value)) { - return false } else { - const parts = value.split(":") - const HH = Number(parts[0]) - const mm = Number(parts[1]) - return HH >= 0 && HH <= 23 && mm >= 0 && mm <= 59 + return ![ + parent.sunday, + value, + parent.tuesday, + parent.wednesday, + parent.thursday, + parent.friday, + parent.saturday, + ].every((day) => day === false) } }), - timezone: Yup.string() - .ensure() - .test("is-timezone", Language.errorTimezone, function (value) { - const parent = this.parent as WorkspaceScheduleFormValues + tuesday: Yup.boolean(), + wednesday: Yup.boolean(), + thursday: Yup.boolean(), + friday: Yup.boolean(), + saturday: Yup.boolean(), - if (!parent.startTime) { - return true - } else { - // Unfortunately, there's not a good API on dayjs at this time for - // evaluating a timezone. Attempt to parse today in the supplied timezone - // and return as valid if the function doesn't throw. - try { - dayjs.tz(dayjs(), value) + startTime: Yup.string() + .ensure() + .test("required-if-auto-start", Language.errorNoTime, function (value) { + if (autoStartEnabled) { + return value !== "" + } else { return true - } catch (e) { + } + }) + .test("is-time-string", Language.errorTime, (value) => { + if (value === "") { + return true + } else if (!/^[0-9][0-9]:[0-9][0-9]$/.test(value)) { return false + } else { + const parts = value.split(":") + const HH = Number(parts[0]) + const mm = Number(parts[1]) + return HH >= 0 && HH <= 23 && mm >= 0 && mm <= 59 } - } - }), - ttl: Yup.number() - .integer() - .min(0) - .max(24 * 7 /* 7 days */) - .test("positive-if-auto-stop", Language.errorNoStop, (value) => { - if (autoStopEnabled) { - return !!value - } else { - return true - } - }), -})) + }), + timezone: Yup.string() + .ensure() + .test("is-timezone", Language.errorTimezone, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues + + if (!parent.startTime) { + return true + } else { + // Unfortunately, there's not a good API on dayjs at this time for + // evaluating a timezone. Attempt to parse today in the supplied timezone + // and return as valid if the function doesn't throw. + try { + dayjs.tz(dayjs(), value) + return true + } catch (e) { + return false + } + } + }), + ttl: Yup.number() + .integer() + .min(0) + .max(24 * 7 /* 7 days */) + .test("positive-if-auto-stop", Language.errorNoStop, (value) => { + if (autoStopEnabled) { + return !!value + } else { + return true + } + }), + }) export const WorkspaceScheduleForm: FC = ({ submitScheduleError, diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx index 66923e03db0d7..365d2540a9a8b 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx @@ -1,8 +1,11 @@ +import { + formValuesToAutoStartRequest, + formValuesToTTLRequest, +} from "pages/WorkspaceSchedulePage/formToRequest" import { AutoStart, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import * as TypesGen from "../../api/typesGenerated" import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" -import { formValuesToAutoStartRequest, formValuesToTTLRequest } from "pages/WorkspaceSchedulePage/formToRequest" const validValues: WorkspaceScheduleFormValues = { sunday: false, From d1420de03f0fd12e54ed8a50eed10c6a10d42a93 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 19:44:32 +0000 Subject: [PATCH 19/26] Put switches in form, persist form state --- .../WorkspaceScheduleForm.test.ts | 36 +-- .../WorkspaceScheduleForm.tsx | 208 ++++++++++-------- .../WorkspaceSchedulePage.test.tsx | 84 +++---- .../WorkspaceSchedulePage.tsx | 59 +---- .../WorkspaceSchedulePage/formToRequest.ts | 4 +- .../pages/WorkspaceSchedulePage/schedule.ts | 13 +- site/src/pages/WorkspaceSchedulePage/ttl.ts | 4 +- 7 files changed, 196 insertions(+), 212 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts index 49d97a3c7c5dd..101635a13cd00 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts @@ -1,12 +1,13 @@ import { - getValidationSchema, Language, ttlShutdownAt, + validationSchema, WorkspaceScheduleFormValues, } from "./WorkspaceScheduleForm" import { zones } from "./zones" const valid: WorkspaceScheduleFormValues = { + autoStartEnabled: true, sunday: false, monday: true, tuesday: true, @@ -14,15 +15,17 @@ const valid: WorkspaceScheduleFormValues = { thursday: true, friday: true, saturday: false, - startTime: "09:30", timezone: "Canada/Eastern", + + autoStopEnabled: true, ttl: 120, } describe("validationSchema", () => { it("allows everything to be falsy when switches are off", () => { const values: WorkspaceScheduleFormValues = { + autoStartEnabled: false, sunday: false, monday: false, tuesday: false, @@ -30,12 +33,13 @@ describe("validationSchema", () => { thursday: false, friday: false, saturday: false, - startTime: "", timezone: "", + + autoStopEnabled: false, ttl: 0, } - const validate = () => getValidationSchema(false, false).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrow() }) @@ -44,7 +48,7 @@ describe("validationSchema", () => { ...valid, ttl: -1, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrow() }) @@ -59,7 +63,7 @@ describe("validationSchema", () => { friday: false, saturday: false, } - const validate = () => getValidationSchema(true, false).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorNoDayOfWeek) }) @@ -75,7 +79,7 @@ describe("validationSchema", () => { saturday: false, startTime: "", } - const validate = () => getValidationSchema(true, false).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorNoTime) }) @@ -84,7 +88,7 @@ describe("validationSchema", () => { ...valid, startTime: "16:20", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrow() }) @@ -93,7 +97,7 @@ describe("validationSchema", () => { ...valid, startTime: "9:30", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -102,7 +106,7 @@ describe("validationSchema", () => { ...valid, startTime: "09:5", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -111,7 +115,7 @@ describe("validationSchema", () => { ...valid, startTime: "24:01", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -120,7 +124,7 @@ describe("validationSchema", () => { ...valid, startTime: "09:60", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTime) }) @@ -129,7 +133,7 @@ describe("validationSchema", () => { ...valid, timezone: "Canada/North", } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTimezone) }) @@ -138,7 +142,7 @@ describe("validationSchema", () => { ...valid, timezone: zone, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrow() }) @@ -147,7 +151,7 @@ describe("validationSchema", () => { ...valid, ttl: 24 * 7, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrowError() }) @@ -156,7 +160,7 @@ describe("validationSchema", () => { ...valid, ttl: 24 * 7 + 1, } - const validate = () => getValidationSchema(true, true).validateSync(values) + const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError("ttl must be less than or equal to 168") }) }) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 3fda342cad163..e80cfad8901c3 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -17,9 +17,9 @@ 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/WorkspaceSchedulePage/schedule" -import { AutoStop } from "pages/WorkspaceSchedulePage/ttl" -import { FC } from "react" +import { defaultSchedule } from "pages/WorkspaceSchedulePage/schedule" +import { defaultTTL } from "pages/WorkspaceSchedulePage/ttl" +import { ChangeEvent, FC } from "react" import * as Yup from "yup" import { getFormHelpersWithError } from "../../util/formUtils" import { FormFooter } from "../FormFooter/FormFooter" @@ -65,10 +65,7 @@ export const Language = { export interface WorkspaceScheduleFormProps { submitScheduleError?: Error | unknown - autoStart: AutoStart - toggleAutoStart: () => void - autoStop: AutoStop - toggleAutoStop: () => void + initialValues: WorkspaceScheduleFormValues isLoading: boolean onCancel: () => void onSubmit: (values: WorkspaceScheduleFormValues) => void @@ -77,6 +74,7 @@ export interface WorkspaceScheduleFormProps { } export interface WorkspaceScheduleFormValues { + autoStartEnabled: boolean sunday: boolean monday: boolean tuesday: boolean @@ -84,111 +82,108 @@ export interface WorkspaceScheduleFormValues { thursday: boolean friday: boolean saturday: boolean - startTime: string timezone: string + + autoStopEnabled: boolean ttl: number } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export const getValidationSchema = (autoStartEnabled: boolean, autoStopEnabled: boolean) => - Yup.object({ - sunday: Yup.boolean(), - monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { - const parent = this.parent as WorkspaceScheduleFormValues +export const validationSchema = Yup.object({ + sunday: Yup.boolean(), + monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues - if (!autoStartEnabled) { + if (parent.autoStartEnabled) { + return true + } else { + return ![ + parent.sunday, + value, + parent.tuesday, + parent.wednesday, + parent.thursday, + parent.friday, + parent.saturday, + ].every((day) => day === false) + } + }), + tuesday: Yup.boolean(), + wednesday: Yup.boolean(), + thursday: Yup.boolean(), + friday: Yup.boolean(), + saturday: Yup.boolean(), + + startTime: Yup.string() + .ensure() + .test("required-if-auto-start", Language.errorNoTime, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues + if (parent.autoStartEnabled) { + return value !== "" + } else { + return true + } + }) + .test("is-time-string", Language.errorTime, (value) => { + if (value === "") { return true + } else if (!/^[0-9][0-9]:[0-9][0-9]$/.test(value)) { + return false } else { - return ![ - parent.sunday, - value, - parent.tuesday, - parent.wednesday, - parent.thursday, - parent.friday, - parent.saturday, - ].every((day) => day === false) + const parts = value.split(":") + const HH = Number(parts[0]) + const mm = Number(parts[1]) + return HH >= 0 && HH <= 23 && mm >= 0 && mm <= 59 } }), - tuesday: Yup.boolean(), - wednesday: Yup.boolean(), - thursday: Yup.boolean(), - friday: Yup.boolean(), - saturday: Yup.boolean(), + timezone: Yup.string() + .ensure() + .test("is-timezone", Language.errorTimezone, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues - startTime: Yup.string() - .ensure() - .test("required-if-auto-start", Language.errorNoTime, function (value) { - if (autoStartEnabled) { - return value !== "" - } else { - return true - } - }) - .test("is-time-string", Language.errorTime, (value) => { - if (value === "") { + if (!parent.startTime) { + return true + } else { + // Unfortunately, there's not a good API on dayjs at this time for + // evaluating a timezone. Attempt to parse today in the supplied timezone + // and return as valid if the function doesn't throw. + try { + dayjs.tz(dayjs(), value) return true - } else if (!/^[0-9][0-9]:[0-9][0-9]$/.test(value)) { + } catch (e) { return false - } else { - const parts = value.split(":") - const HH = Number(parts[0]) - const mm = Number(parts[1]) - return HH >= 0 && HH <= 23 && mm >= 0 && mm <= 59 - } - }), - timezone: Yup.string() - .ensure() - .test("is-timezone", Language.errorTimezone, function (value) { - const parent = this.parent as WorkspaceScheduleFormValues - - if (!parent.startTime) { - return true - } else { - // Unfortunately, there's not a good API on dayjs at this time for - // evaluating a timezone. Attempt to parse today in the supplied timezone - // and return as valid if the function doesn't throw. - try { - dayjs.tz(dayjs(), value) - return true - } catch (e) { - return false - } } - }), - ttl: Yup.number() - .integer() - .min(0) - .max(24 * 7 /* 7 days */) - .test("positive-if-auto-stop", Language.errorNoStop, (value) => { - if (autoStopEnabled) { - return !!value - } else { - return true - } - }), - }) + } + }), + ttl: Yup.number() + .integer() + .min(0) + .max(24 * 7 /* 7 days */) + .test("positive-if-auto-stop", Language.errorNoStop, function (value) { + const parent = this.parent as WorkspaceScheduleFormValues + if (parent.autoStopEnabled) { + return !!value + } else { + return true + } + }), +}) export const WorkspaceScheduleForm: FC = ({ submitScheduleError, - autoStart, - toggleAutoStart, - autoStop, - toggleAutoStop, + initialValues, isLoading, onCancel, onSubmit, initialTouched, }) => { const styles = useStyles() - const initialValues = { ...autoStart.schedule, ttl: autoStop.ttl } const form = useFormik({ initialValues, - enableReinitialize: true, onSubmit, - validationSchema: () => getValidationSchema(autoStart.enabled, autoStop.enabled), + validationSchema, initialTouched, }) const formHelpers = getFormHelpersWithError( @@ -206,6 +201,27 @@ export const WorkspaceScheduleForm: FC = ({ { value: form.values.saturday, name: "saturday", label: Language.daySaturdayLabel }, ] + const handleToggleAutoStart = async (e: ChangeEvent) => { + form.handleChange(e) + // if enabling from empty values, fill with defaults + if (!form.values.autoStartEnabled && !form.values.startTime) { + const defaults = defaultSchedule() + checkboxes.forEach(async ({ name }) => { + await form.setFieldValue(name, defaults[name]) + }) + await form.setFieldValue("startTime", defaults.startTime) + await form.setFieldValue("timezone", defaults.timezone) + } + } + + const handleToggleAutoStop = async (e: ChangeEvent) => { + form.handleChange(e) + // if enabling from empty values, fill with defaults + if (!form.values.autoStopEnabled && !form.values.ttl) { + await form.setFieldValue("ttl", defaultTTL) + } + } + return ( @@ -213,12 +229,18 @@ export const WorkspaceScheduleForm: FC = ({ {submitScheduleError && }
} + control={ + + } label={Language.startSwitch} /> = ({ = ({ control={ = ({
} + control={ + + } label={Language.stopSwitch} /> { [ // Empty case { + autoStartEnabled: false, sunday: false, monday: false, tuesday: false, @@ -35,6 +38,7 @@ describe("WorkspaceSchedulePage", () => { saturday: false, startTime: "", timezone: "", + autoStopEnabled: false, ttl: 0, }, { @@ -44,6 +48,7 @@ describe("WorkspaceSchedulePage", () => { [ // Single day { + autoStartEnabled: true, sunday: true, monday: false, tuesday: false, @@ -53,6 +58,7 @@ describe("WorkspaceSchedulePage", () => { saturday: false, startTime: "16:20", timezone: "Canada/Eastern", + autoStopEnabled: true, ttl: 120, }, { @@ -62,6 +68,7 @@ describe("WorkspaceSchedulePage", () => { [ // Standard 1-5 case { + autoStartEnabled: true, sunday: false, monday: true, tuesday: true, @@ -71,6 +78,7 @@ describe("WorkspaceSchedulePage", () => { saturday: false, startTime: "09:30", timezone: "America/Central", + autoStopEnabled: true, ttl: 120, }, { @@ -80,6 +88,7 @@ describe("WorkspaceSchedulePage", () => { [ // Everyday { + autoStartEnabled: true, sunday: true, monday: true, tuesday: true, @@ -89,6 +98,7 @@ describe("WorkspaceSchedulePage", () => { saturday: true, startTime: "09:00", timezone: "", + autoStopEnabled: true, ttl: 60 * 8, }, { @@ -98,6 +108,7 @@ describe("WorkspaceSchedulePage", () => { [ // Mon, Wed, Fri Evenings { + autoStartEnabled: true, sunday: false, monday: true, tuesday: false, @@ -107,6 +118,7 @@ describe("WorkspaceSchedulePage", () => { saturday: false, startTime: "16:20", timezone: "", + autoStopEnabled: true, ttl: 60 * 3, }, { @@ -161,18 +173,16 @@ describe("WorkspaceSchedulePage", () => { [ undefined, { - enabled: false, - schedule: { - sunday: false, - monday: false, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - startTime: "", - timezone: "", - }, + autoStartEnabled: false, + sunday: false, + monday: false, + tuesday: false, + wednesday: false, + thursday: false, + friday: false, + saturday: false, + startTime: "", + timezone: "", }, ], @@ -180,18 +190,16 @@ describe("WorkspaceSchedulePage", () => { [ "CRON_TZ=UTC 30 9 * * 1-5", { - enabled: true, - schedule: { - sunday: false, - monday: true, - tuesday: true, - wednesday: true, - thursday: true, - friday: true, - saturday: false, - startTime: "09:30", - timezone: "UTC", - }, + autoStartEnabled: true, + sunday: false, + monday: true, + tuesday: true, + wednesday: true, + thursday: true, + friday: true, + saturday: false, + startTime: "09:30", + timezone: "UTC", }, ], @@ -199,18 +207,16 @@ describe("WorkspaceSchedulePage", () => { [ "CRON_TZ=Canada/Eastern 20 16 * * 1,3-4,6", { - enabled: true, - schedule: { - sunday: false, - monday: true, - tuesday: false, - wednesday: true, - thursday: true, - friday: false, - saturday: true, - startTime: "16:20", - timezone: "Canada/Eastern", - }, + autoStartEnabled: true, + sunday: false, + monday: true, + tuesday: false, + wednesday: true, + thursday: true, + friday: false, + saturday: true, + startTime: "16:20", + timezone: "Canada/Eastern", }, ], ])(`scheduleToAutoStart(%p) returns %p`, (schedule, autoStart) => { @@ -221,11 +227,11 @@ describe("WorkspaceSchedulePage", () => { describe("ttlMsToAutoStop", () => { it.each<[number | undefined, AutoStop]>([ // empty case - [undefined, { enabled: false, ttl: 0 }], + [undefined, { autoStopEnabled: false, ttl: 0 }], // zero - [0, { enabled: false, ttl: 0 }], + [0, { autoStopEnabled: false, ttl: 0 }], // basic case - [28_800_000, { enabled: true, ttl: 8 }], + [28_800_000, { autoStopEnabled: true, ttl: 8 }], ])(`ttlMsToAutoStop(%p) returns %p`, (ttlMs, autoStop) => { expect(ttlMsToAutoStop(ttlMs)).toEqual(autoStop) }) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index a59b4b0804ebe..b20be6b5ded83 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,10 +1,6 @@ import { useMachine, useSelector } from "@xstate/react" -import { - defaultSchedule, - emptySchedule, - scheduleToAutoStart, -} from "pages/WorkspaceSchedulePage/schedule" -import { defaultTTL, emptyTTL, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" +import { scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useContext, useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" @@ -58,52 +54,6 @@ export const WorkspaceSchedulePage: React.FC = () => { 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) { return } @@ -137,10 +87,7 @@ export const WorkspaceSchedulePage: React.FC = () => { return ( { navigate(`/@${username}/${workspaceName}`) diff --git a/site/src/pages/WorkspaceSchedulePage/formToRequest.ts b/site/src/pages/WorkspaceSchedulePage/formToRequest.ts index 88dafe245d5b9..8802139c769d6 100644 --- a/site/src/pages/WorkspaceSchedulePage/formToRequest.ts +++ b/site/src/pages/WorkspaceSchedulePage/formToRequest.ts @@ -4,7 +4,7 @@ import { WorkspaceScheduleFormValues } from "components/WorkspaceScheduleForm/Wo export const formValuesToAutoStartRequest = ( values: WorkspaceScheduleFormValues, ): TypesGen.UpdateWorkspaceAutostartRequest => { - if (!values.startTime) { + if (!values.autoStartEnabled || !values.startTime) { return { schedule: "", } @@ -69,6 +69,6 @@ export const formValuesToTTLRequest = ( ): TypesGen.UpdateWorkspaceTTLRequest => { return { // minutes to nanoseconds - ttl_ms: values.ttl ? values.ttl * 60 * 60 * 1000 : undefined, + ttl_ms: values.autoStopEnabled && values.ttl ? values.ttl * 60 * 60 * 1000 : undefined, } } diff --git a/site/src/pages/WorkspaceSchedulePage/schedule.ts b/site/src/pages/WorkspaceSchedulePage/schedule.ts index 6518872cb68f9..ef08da81e9193 100644 --- a/site/src/pages/WorkspaceSchedulePage/schedule.ts +++ b/site/src/pages/WorkspaceSchedulePage/schedule.ts @@ -22,10 +22,9 @@ export interface AutoStartSchedule { timezone: string } -export interface AutoStart { - enabled: boolean - schedule: AutoStartSchedule -} +export type AutoStart = { + autoStartEnabled: boolean +} & AutoStartSchedule export const emptySchedule = { sunday: false, @@ -83,10 +82,10 @@ const transformSchedule = (schedule: string) => { export const scheduleToAutoStart = (schedule?: string): AutoStart => { if (schedule) { return { - enabled: true, - schedule: transformSchedule(schedule), + autoStartEnabled: true, + ...transformSchedule(schedule), } } else { - return { enabled: false, schedule: emptySchedule } + return { autoStartEnabled: false, ...emptySchedule } } } diff --git a/site/src/pages/WorkspaceSchedulePage/ttl.ts b/site/src/pages/WorkspaceSchedulePage/ttl.ts index 4035ab09edeb9..0d82563b64ff8 100644 --- a/site/src/pages/WorkspaceSchedulePage/ttl.ts +++ b/site/src/pages/WorkspaceSchedulePage/ttl.ts @@ -1,5 +1,5 @@ export interface AutoStop { - enabled: boolean + autoStopEnabled: boolean ttl: number } @@ -10,4 +10,4 @@ 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 } + ttl_ms ? { autoStopEnabled: true, ttl: msToHours(ttl_ms) } : { autoStopEnabled: false, ttl: 0 } From 0577ea9d4543ac94b3ee6bd3c701ddd72f86d9a4 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 19:55:52 +0000 Subject: [PATCH 20/26] Fix bug --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index e80cfad8901c3..3751cc7cf9c6c 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -17,7 +17,7 @@ import relativeTime from "dayjs/plugin/relativeTime" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { FormikTouched, useFormik } from "formik" -import { defaultSchedule } from "pages/WorkspaceSchedulePage/schedule" +import { AutoStartSchedule, defaultSchedule } from "pages/WorkspaceSchedulePage/schedule" import { defaultTTL } from "pages/WorkspaceSchedulePage/ttl" import { ChangeEvent, FC } from "react" import * as Yup from "yup" @@ -95,7 +95,7 @@ export const validationSchema = Yup.object({ monday: Yup.boolean().test("at-least-one-day", Language.errorNoDayOfWeek, function (value) { const parent = this.parent as WorkspaceScheduleFormValues - if (parent.autoStartEnabled) { + if (!parent.autoStartEnabled) { return true } else { return ![ @@ -207,7 +207,7 @@ export const WorkspaceScheduleForm: FC = ({ if (!form.values.autoStartEnabled && !form.values.startTime) { const defaults = defaultSchedule() checkboxes.forEach(async ({ name }) => { - await form.setFieldValue(name, defaults[name]) + await form.setFieldValue(name, defaults[name as keyof AutoStartSchedule]) }) await form.setFieldValue("startTime", defaults.startTime) await form.setFieldValue("timezone", defaults.timezone) From a6271ca6c4f83d149c283021fd6831d4ea771a3c Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 20:03:48 +0000 Subject: [PATCH 21/26] Remove helper text when disabled --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 3751cc7cf9c6c..784e0a8b1ed03 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -239,7 +239,8 @@ export const WorkspaceScheduleForm: FC = ({ label={Language.startSwitch} /> = ({ label={Language.stopSwitch} /> Date: Wed, 10 Aug 2022 20:11:10 +0000 Subject: [PATCH 22/26] Fix storybook --- .../WorkspaceScheduleForm.stories.tsx | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index e351f71c8cc74..6d52575719ed6 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -4,7 +4,7 @@ import advancedFormat from "dayjs/plugin/advancedFormat" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { defaultSchedule, emptySchedule } from "pages/WorkspaceSchedulePage/schedule" -import { emptyTTL } from "pages/WorkspaceSchedulePage/ttl" +import { defaultTTL, emptyTTL } from "pages/WorkspaceSchedulePage/ttl" import { makeMockApiError } from "testHelpers/entities" import { WorkspaceScheduleForm, WorkspaceScheduleFormProps } from "./WorkspaceScheduleForm" @@ -22,51 +22,56 @@ export default { onSubmit: { action: "onSubmit", }, - toggleAutoStart: { - action: "toggleAutoStart", - }, - toggleAutoStop: { - action: "toggleAutoStop", - }, }, } const Template: Story = (args) => +const defaultInitialValues = { + autoStartEnabled: true, + ...defaultSchedule(), + autoStopEnabled: true, + ttl: defaultTTL +} + export const AllDisabled = Template.bind({}) AllDisabled.args = { - autoStart: { enabled: false, schedule: emptySchedule }, - autoStop: { enabled: false, ttl: emptyTTL }, + initialValues: { + autoStartEnabled: false, + ...emptySchedule, + autoStopEnabled: false, + ttl: emptyTTL + } } export const AutoStart = Template.bind({}) AutoStart.args = { - autoStart: { enabled: true, schedule: defaultSchedule() }, - autoStop: { enabled: false, ttl: emptyTTL }, + initialValues: { + autoStartEnabled: true, + ...defaultSchedule(), + autoStopEnabled: false, + ttl: emptyTTL + }, } export const WorkspaceWillShutdownInTwoHours = Template.bind({}) WorkspaceWillShutdownInTwoHours.args = { - autoStart: { enabled: true, schedule: defaultSchedule() }, - autoStop: { enabled: true, ttl: 2 }, + initialValues: { ...defaultInitialValues, ttl: 2 } } export const WorkspaceWillShutdownInADay = Template.bind({}) WorkspaceWillShutdownInADay.args = { - autoStart: { enabled: true, schedule: defaultSchedule() }, - autoStop: { enabled: true, ttl: 24 }, + initialValues: { ...defaultInitialValues, ttl: 24 } } export const WorkspaceWillShutdownInTwoDays = Template.bind({}) WorkspaceWillShutdownInTwoDays.args = { - autoStart: { enabled: true, schedule: defaultSchedule() }, - autoStop: { enabled: true, ttl: 48 }, + initialValues: { ...defaultInitialValues, ttl: 48 } } export const WithError = Template.bind({}) WithError.args = { - autoStart: { enabled: false, schedule: emptySchedule }, - autoStop: { enabled: true, ttl: 100 }, + initialValues: { ...defaultInitialValues, ttl: 100 }, initialTouched: { ttl: true }, submitScheduleError: makeMockApiError({ message: "Something went wrong.", @@ -76,7 +81,6 @@ WithError.args = { export const Loading = Template.bind({}) Loading.args = { - autoStart: { enabled: true, schedule: defaultSchedule() }, - autoStop: { enabled: true, ttl: 2 }, + initialValues: defaultInitialValues, isLoading: true, } From e8ae5ba27bb269c62979a3e5a825cb14bda0076f Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 20:11:37 +0000 Subject: [PATCH 23/26] Revert "Remove helper text when disabled" This reverts commit a6271ca6c4f83d149c283021fd6831d4ea771a3c. --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 784e0a8b1ed03..3751cc7cf9c6c 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -239,8 +239,7 @@ export const WorkspaceScheduleForm: FC = ({ label={Language.startSwitch} /> = ({ label={Language.stopSwitch} /> Date: Wed, 10 Aug 2022 20:15:58 +0000 Subject: [PATCH 24/26] Format --- .../WorkspaceScheduleForm.stories.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index 6d52575719ed6..7d1957bcdff8d 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -31,7 +31,7 @@ const defaultInitialValues = { autoStartEnabled: true, ...defaultSchedule(), autoStopEnabled: true, - ttl: defaultTTL + ttl: defaultTTL, } export const AllDisabled = Template.bind({}) @@ -40,8 +40,8 @@ AllDisabled.args = { autoStartEnabled: false, ...emptySchedule, autoStopEnabled: false, - ttl: emptyTTL - } + ttl: emptyTTL, + }, } export const AutoStart = Template.bind({}) @@ -50,23 +50,23 @@ AutoStart.args = { autoStartEnabled: true, ...defaultSchedule(), autoStopEnabled: false, - ttl: emptyTTL + ttl: emptyTTL, }, } export const WorkspaceWillShutdownInTwoHours = Template.bind({}) WorkspaceWillShutdownInTwoHours.args = { - initialValues: { ...defaultInitialValues, ttl: 2 } + initialValues: { ...defaultInitialValues, ttl: 2 }, } export const WorkspaceWillShutdownInADay = Template.bind({}) WorkspaceWillShutdownInADay.args = { - initialValues: { ...defaultInitialValues, ttl: 24 } + initialValues: { ...defaultInitialValues, ttl: 24 }, } export const WorkspaceWillShutdownInTwoDays = Template.bind({}) WorkspaceWillShutdownInTwoDays.args = { - initialValues: { ...defaultInitialValues, ttl: 48 } + initialValues: { ...defaultInitialValues, ttl: 48 }, } export const WithError = Template.bind({}) From a17dc2e3f37918cf81fba107559eac43cc1026ee Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 21:41:50 +0000 Subject: [PATCH 25/26] Use nicer function to set values --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 3751cc7cf9c6c..621d19729191e 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -205,12 +205,7 @@ export const WorkspaceScheduleForm: FC = ({ form.handleChange(e) // if enabling from empty values, fill with defaults if (!form.values.autoStartEnabled && !form.values.startTime) { - const defaults = defaultSchedule() - checkboxes.forEach(async ({ name }) => { - await form.setFieldValue(name, defaults[name as keyof AutoStartSchedule]) - }) - await form.setFieldValue("startTime", defaults.startTime) - await form.setFieldValue("timezone", defaults.timezone) + await form.setValues({ ...form.values, autoStartEnabled: true, ...defaultSchedule() }) } } From bf22caad199dd85dfbb8c024dbd8249e7b933bbb Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 10 Aug 2022 21:51:14 +0000 Subject: [PATCH 26/26] Format --- .../components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 621d19729191e..94c378c7f7e95 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -17,7 +17,7 @@ import relativeTime from "dayjs/plugin/relativeTime" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { FormikTouched, useFormik } from "formik" -import { AutoStartSchedule, defaultSchedule } from "pages/WorkspaceSchedulePage/schedule" +import { defaultSchedule } from "pages/WorkspaceSchedulePage/schedule" import { defaultTTL } from "pages/WorkspaceSchedulePage/ttl" import { ChangeEvent, FC } from "react" import * as Yup from "yup" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy