Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions site/src/components/Section/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const Section: SectionFC = ({
}) => {
const styles = useStyles({ layout })
return (
<div className={combineClasses([styles.root, className])}>
<section className={combineClasses([styles.root, className])}>
<div className={styles.inner}>
{(title || description) && (
<div className={styles.header}>
Expand All @@ -49,7 +49,7 @@ export const Section: SectionFC = ({
{alert && <div className={styles.alert}>{alert}</div>}
{children}
</div>
</div>
</section>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -24,56 +22,61 @@ export default {
onSubmit: {
action: "onSubmit",
},
toggleAutoStart: {
action: "toggleAutoStart",
},
toggleAutoStop: {
action: "toggleAutoStop",
},
},
}

const Template: Story<WorkspaceScheduleFormProps> = (args) => <WorkspaceScheduleForm {...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.",
validations: [{ field: "ttl_ms", detail: "Invalid time until shutdown." }],
}),
}

export const Loading = Template.bind({})
Loading.args = {
autoStart: { enabled: true, schedule: defaultSchedule() },
autoStop: { enabled: true, ttl: 2 },
isLoading: true,
}
162 changes: 85 additions & 77 deletions site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ 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"
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"
Expand Down Expand Up @@ -55,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
Expand Down Expand Up @@ -160,37 +167,23 @@ export const validationSchema = Yup.object({
.max(24 * 7 /* 7 days */),
})

export const defaultWorkspaceScheduleTTL = 8

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<WorkspaceScheduleFormProps> = ({
submitScheduleError,
initialValues = defaultWorkspaceSchedule(),
autoStart,
toggleAutoStart,
autoStop,
toggleAutoStop,
isLoading,
onCancel,
onSubmit,
initialTouched,
}) => {
const styles = useStyles()
const initialValues = { ...autoStart.schedule, ttl: autoStop.ttl }

const form = useFormik<WorkspaceScheduleFormValues>({
initialValues,
enableReinitialize: true,
onSubmit,
validationSchema,
initialTouched,
Expand All @@ -215,67 +208,82 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
<form onSubmit={form.handleSubmit} className={styles.form}>
<Stack>
{submitScheduleError && <ErrorSummary error={submitScheduleError} />}
<TextField
{...formHelpers("startTime", Language.startTimeHelperText)}
disabled={isLoading}
InputLabelProps={{
shrink: true,
}}
label={Language.startTimeLabel}
type="time"
/>
<Section title="Start">
<FormControlLabel
control={<Switch checked={autoStart.enabled} onChange={toggleAutoStart} />}
label="Auto-start"
/>
<TextField
{...formHelpers("startTime", Language.startTimeHelperText)}
disabled={isLoading || !autoStart.enabled}
InputLabelProps={{
shrink: true,
}}
label={Language.startTimeLabel}
type="time"
fullWidth
/>

<TextField
{...formHelpers("timezone")}
disabled={isLoading}
InputLabelProps={{
shrink: true,
}}
label={Language.timezoneLabel}
select
>
{zones.map((zone) => (
<MenuItem key={zone} value={zone}>
{zone}
</MenuItem>
))}
</TextField>
<TextField
{...formHelpers("timezone")}
disabled={isLoading || !autoStart.enabled}
InputLabelProps={{
shrink: true,
}}
label={Language.timezoneLabel}
select
fullWidth
>
{zones.map((zone) => (
<MenuItem key={zone} value={zone}>
{zone}
</MenuItem>
))}
</TextField>

<FormControl component="fieldset" error={Boolean(form.errors.monday)}>
<FormLabel className={styles.daysOfWeekLabel} component="legend">
{Language.daysOfWeekLabel}
</FormLabel>
<FormControl component="fieldset" error={Boolean(form.errors.monday)}>
<FormLabel className={styles.daysOfWeekLabel} component="legend">
{Language.daysOfWeekLabel}
</FormLabel>

<FormGroup>
{checkboxes.map((checkbox) => (
<FormControlLabel
control={
<Checkbox
checked={checkbox.value}
disabled={isLoading}
onChange={form.handleChange}
name={checkbox.name}
color="primary"
size="small"
disableRipple
/>
}
key={checkbox.name}
label={checkbox.label}
/>
))}
</FormGroup>
<FormGroup>
{checkboxes.map((checkbox) => (
<FormControlLabel
control={
<Checkbox
checked={checkbox.value}
disabled={isLoading || !autoStart.enabled}
onChange={form.handleChange}
name={checkbox.name}
color="primary"
size="small"
disableRipple
/>
}
key={checkbox.name}
label={checkbox.label}
/>
))}
</FormGroup>

{form.errors.monday && <FormHelperText>{Language.errorNoDayOfWeek}</FormHelperText>}
</FormControl>
{form.errors.monday && <FormHelperText>{Language.errorNoDayOfWeek}</FormHelperText>}
</FormControl>
</Section>

<TextField
{...formHelpers("ttl", ttlShutdownAt(form.values.ttl), "ttl_ms")}
disabled={isLoading}
inputProps={{ min: 0, step: 1 }}
label={Language.ttlLabel}
type="number"
/>
<Section title="Stop">
<FormControlLabel
control={<Switch checked={autoStop.enabled} onChange={toggleAutoStop} />}
label="Auto-stop"
/>
<TextField
{...formHelpers("ttl", ttlShutdownAt(form.values.ttl), "ttl_ms")}
disabled={isLoading || !autoStop.enabled}
inputProps={{ min: 0, step: 1 }}
label={Language.ttlLabel}
type="number"
fullWidth
/>
</Section>

<FormFooter onCancel={onCancel} isLoading={isLoading} />
</Stack>
Expand Down
Loading