mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Put switches in form, persist form state
This commit is contained in:
@@ -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")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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) {
|
||||
return true
|
||||
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 ![
|
||||
parent.sunday,
|
||||
value,
|
||||
parent.tuesday,
|
||||
parent.wednesday,
|
||||
parent.thursday,
|
||||
parent.friday,
|
||||
parent.saturday,
|
||||
].every((day) => day === false)
|
||||
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
|
||||
}
|
||||
}),
|
||||
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 {
|
||||
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
|
||||
}
|
||||
})
|
||||
.test("is-time-string", Language.errorTime, (value) => {
|
||||
if (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<WorkspaceScheduleFormProps> = ({
|
||||
submitScheduleError,
|
||||
autoStart,
|
||||
toggleAutoStart,
|
||||
autoStop,
|
||||
toggleAutoStop,
|
||||
initialValues,
|
||||
isLoading,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
initialTouched,
|
||||
}) => {
|
||||
const styles = useStyles()
|
||||
const initialValues = { ...autoStart.schedule, ttl: autoStop.ttl }
|
||||
|
||||
const form = useFormik<WorkspaceScheduleFormValues>({
|
||||
initialValues,
|
||||
enableReinitialize: true,
|
||||
onSubmit,
|
||||
validationSchema: () => getValidationSchema(autoStart.enabled, autoStop.enabled),
|
||||
validationSchema,
|
||||
initialTouched,
|
||||
})
|
||||
const formHelpers = getFormHelpersWithError<WorkspaceScheduleFormValues>(
|
||||
@@ -206,6 +201,27 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
{ 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 (
|
||||
<FullPageForm onCancel={onCancel} title={Language.formTitle}>
|
||||
<form onSubmit={form.handleSubmit} className={styles.form}>
|
||||
@@ -213,12 +229,18 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
{submitScheduleError && <ErrorSummary error={submitScheduleError} />}
|
||||
<Section title={Language.startSection}>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={autoStart.enabled} onChange={toggleAutoStart} />}
|
||||
control={
|
||||
<Switch
|
||||
name="autoStartEnabled"
|
||||
checked={form.values.autoStartEnabled}
|
||||
onChange={handleToggleAutoStart}
|
||||
/>
|
||||
}
|
||||
label={Language.startSwitch}
|
||||
/>
|
||||
<TextField
|
||||
{...formHelpers("startTime", Language.startTimeHelperText)}
|
||||
disabled={isLoading || !autoStart.enabled}
|
||||
disabled={isLoading || !form.values.autoStartEnabled}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
@@ -229,7 +251,7 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
|
||||
<TextField
|
||||
{...formHelpers("timezone")}
|
||||
disabled={isLoading || !autoStart.enabled}
|
||||
disabled={isLoading || !form.values.autoStartEnabled}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
@@ -255,7 +277,7 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
control={
|
||||
<Checkbox
|
||||
checked={checkbox.value}
|
||||
disabled={isLoading || !autoStart.enabled}
|
||||
disabled={isLoading || !form.values.autoStartEnabled}
|
||||
onChange={form.handleChange}
|
||||
name={checkbox.name}
|
||||
color="primary"
|
||||
@@ -275,12 +297,18 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
|
||||
<Section title={Language.stopSection}>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={autoStop.enabled} onChange={toggleAutoStop} />}
|
||||
control={
|
||||
<Switch
|
||||
name="autoStopEnabled"
|
||||
checked={form.values.autoStopEnabled}
|
||||
onChange={handleToggleAutoStop}
|
||||
/>
|
||||
}
|
||||
label={Language.stopSwitch}
|
||||
/>
|
||||
<TextField
|
||||
{...formHelpers("ttl", ttlShutdownAt(form.values.ttl), "ttl_ms")}
|
||||
disabled={isLoading || !autoStop.enabled}
|
||||
disabled={isLoading || !form.values.autoStopEnabled}
|
||||
inputProps={{ min: 0, step: 1 }}
|
||||
label={Language.ttlLabel}
|
||||
type="number"
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as TypesGen from "../../api/typesGenerated"
|
||||
import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm"
|
||||
|
||||
const validValues: WorkspaceScheduleFormValues = {
|
||||
autoStartEnabled: true,
|
||||
sunday: false,
|
||||
monday: true,
|
||||
tuesday: true,
|
||||
@@ -17,6 +18,7 @@ const validValues: WorkspaceScheduleFormValues = {
|
||||
saturday: false,
|
||||
startTime: "09:30",
|
||||
timezone: "Canada/Eastern",
|
||||
autoStopEnabled: true,
|
||||
ttl: 120,
|
||||
}
|
||||
|
||||
@@ -26,6 +28,7 @@ describe("WorkspaceSchedulePage", () => {
|
||||
[
|
||||
// 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)
|
||||
})
|
||||
|
||||
@@ -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 <Navigate to="/workspaces" />
|
||||
}
|
||||
@@ -137,10 +87,7 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
return (
|
||||
<WorkspaceScheduleForm
|
||||
submitScheduleError={submitScheduleError}
|
||||
autoStart={autoStart}
|
||||
toggleAutoStart={onToggleAutoStart}
|
||||
autoStop={autoStop}
|
||||
toggleAutoStop={onToggleAutoStop}
|
||||
initialValues={{ ...autoStart, ...autoStop }}
|
||||
isLoading={scheduleState.tags.has("loading")}
|
||||
onCancel={() => {
|
||||
navigate(`/@${username}/${workspaceName}`)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user