Skip to content

feat: form for editing ws schedule #1634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 20, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 7 additions & 4 deletions site/src/components/Stack/Stack.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { makeStyles } from "@material-ui/core/styles"
import React from "react"
import { combineClasses } from "../../util/combineClasses"

type Direction = "column" | "row"

interface StyleProps {
spacing: number
direction: Direction
spacing: number
}

const useStyles = makeStyles((theme) => ({
Expand All @@ -17,11 +18,13 @@ const useStyles = makeStyles((theme) => ({
}))

export interface StackProps {
spacing?: number
className?: string
direction?: Direction
spacing?: number
}

export const Stack: React.FC<StackProps> = ({ children, spacing = 2, direction = "column" }) => {
export const Stack: React.FC<StackProps> = ({ children, className, direction = "column", spacing = 2 }) => {
const styles = useStyles({ spacing, direction })
return <div className={styles.stack}>{children}</div>

return <div className={combineClasses([styles.stack, className])}>{children}</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { action } from "@storybook/addon-actions"
import { Story } from "@storybook/react"
import React from "react"
import { WorkspaceScheduleForm, WorkspaceScheduleFormProps } from "./WorkspaceScheduleForm"

export default {
title: "components/WorkspaceScheduleForm",
component: WorkspaceScheduleForm,
}

const Template: Story<WorkspaceScheduleFormProps> = (args) => <WorkspaceScheduleForm {...args} />

export const Example = Template.bind({})
Example.args = {
onCancel: () => action("onCancel"),
onSubmit: () => {
action("onSubmit")
return Promise.resolve()
},
}
149 changes: 149 additions & 0 deletions site/src/components/WorkspaceStats/WorkspaceScheduleForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import Checkbox from "@material-ui/core/Checkbox"
import FormControl from "@material-ui/core/FormControl"
import FormControlLabel from "@material-ui/core/FormControlLabel"
import FormGroup from "@material-ui/core/FormGroup"
import FormLabel from "@material-ui/core/FormLabel"
import makeStyles from "@material-ui/core/styles/makeStyles"
import TextField from "@material-ui/core/TextField"
import { useFormik } from "formik"
import React from "react"
import { getFormHelpers } from "../../util/formUtils"
import { FormFooter } from "../FormFooter/FormFooter"
import { FullPageForm } from "../FullPageForm/FullPageForm"
import { Stack } from "../Stack/Stack"

export const Language = {
daysOfWeekLabel: "Days of Week",
daySundayLabel: "Sunday",
dayMondayLabel: "Monday",
dayTuesdayLabel: "Tuesday",
dayWednesdayLabel: "Wednesday",
dayThursdayLabel: "Thursday",
dayFridayLabel: "Friday",
daySaturdayLabel: "Saturday",
startTimeLabel: "Start time",
ttlLabel: "Runtime (minutes)",
ttlHelperText: "Your workspace will automatically shutdown after the runtime.",
}

export interface WorkspaceScheduleFormProps {
onCancel: () => void
onSubmit: (values: WorkspaceScheduleFormValues) => Promise<void>
}

export interface WorkspaceScheduleFormValues {
sunday: boolean
monday: boolean
tuesday: boolean
wednesday: boolean
thursday: boolean
friday: boolean
saturday: boolean

startTime: string
ttl: number
}

export const WorkspaceScheduleForm: React.FC<WorkspaceScheduleFormProps> = ({ onCancel, onSubmit }) => {
const styles = useStyles()

const form = useFormik<WorkspaceScheduleFormValues>({
initialValues: {
sunday: false,
monday: true,
tuesday: true,
wednesday: true,
thursday: true,
friday: true,
saturday: false,
Comment on lines +84 to +91
Copy link
Member

@johnstcn johnstcn May 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking): I'm not sure how this is going to be translated into a CRON string, but the below are all valid representations:

MON-FRI
MON,TUE,THU,SUN
1-3
2,3,4,5

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parent controlling the form will map the startTime and each of these into an acceptable cron string.

It will likely do , format for simplicity. We will test each of these cases both with code and in crontab.guru.

However, this is all to say - that's a follow-up to the form, that doesn't need to know anything about cron. The code that maps between the form and API is dependency injected via onSubmit, which allows us to test and maintain it in isolation, should something about our cron format change (which is nice).


startTime: "",
ttl: 0,
},
onSubmit,
})
const formHelpers = getFormHelpers<WorkspaceScheduleFormValues>(form)

return (
<FullPageForm onCancel={onCancel} title="Workspace Schedule">
<form className={styles.form} onSubmit={form.handleSubmit}>
<Stack className={styles.stack}>
<TextField
{...formHelpers("startTime")}
InputLabelProps={{
shrink: true,
}}
label={Language.startTimeLabel}
type="time"
variant="standard"
/>

<FormControl component="fieldset">
<FormLabel className={styles.daysOfWeekLabel} component="legend">
{Language.daysOfWeekLabel}
</FormLabel>

<FormGroup>
<FormControlLabel
control={<Checkbox checked={form.values.sunday} onChange={form.handleChange} name="sunday" />}
label={Language.daySundayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.monday} onChange={form.handleChange} name="monday" />}
label={Language.dayMondayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.tuesday} onChange={form.handleChange} name="tuesday" />}
label={Language.dayTuesdayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.wednesday} onChange={form.handleChange} name="wednesday" />}
label={Language.dayWednesdayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.thursday} onChange={form.handleChange} name="thursday" />}
label={Language.dayThursdayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.friday} onChange={form.handleChange} name="friday" />}
label={Language.dayFridayLabel}
/>
<FormControlLabel
control={<Checkbox checked={form.values.saturday} onChange={form.handleChange} name="saturday" />}
label={Language.daySaturdayLabel}
/>
</FormGroup>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: I can create a map for these as a refactor...will do in follow-up/later.

</FormControl>

<TextField
{...formHelpers("ttl", Language.ttlHelperText)}
inputProps={{ min: 0, step: 30 }}
label={Language.ttlLabel}
type="number"
variant="standard"
/>

<FormFooter onCancel={onCancel} isLoading={form.isSubmitting} />
</Stack>
</form>
</FullPageForm>
)
}

const useStyles = makeStyles({
form: {
display: "flex",
justifyContent: "center",
},
stack: {
// REMARK: 360 is 'arbitrary' in that it gives the helper text enough room
// to render on one line. If we change the text, we might want to
// adjust these. Without constraining the width, the date picker
// and number inputs aren't visually appealing or maximally usable.
maxWidth: 360,
minWidth: 360,
},
daysOfWeekLabel: {
fontSize: 12,
},
})