mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: ws schedule timezone select (#2032)
Resolves: #1959 Summary: The package tzdata is used to create a meaningful select-list for timezone in the workspace schedule form. Impact: Improved UX. Furthermore, we guess your timezone if the form is being initialized from scratch.
This commit is contained in:
@ -48,6 +48,7 @@
|
||||
"react-router-dom": "6.3.0",
|
||||
"sourcemapped-stacktrace": "1.1.11",
|
||||
"swr": "1.2.2",
|
||||
"tzdata": "1.0.30",
|
||||
"uuid": "8.3.2",
|
||||
"xstate": "4.32.1",
|
||||
"xterm": "4.18.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Language, validationSchema, WorkspaceScheduleFormValues } from "./WorkspaceScheduleForm"
|
||||
import { zones } from "./zones"
|
||||
|
||||
const valid: WorkspaceScheduleFormValues = {
|
||||
sunday: false,
|
||||
@ -127,6 +128,15 @@ describe("validationSchema", () => {
|
||||
expect(validate).toThrowError(Language.errorTimezone)
|
||||
})
|
||||
|
||||
it.each<[string]>(zones.map((zone) => [zone]))(`validation passes for tz=%p`, (zone) => {
|
||||
const values: WorkspaceScheduleFormValues = {
|
||||
...valid,
|
||||
timezone: zone,
|
||||
}
|
||||
const validate = () => validationSchema.validateSync(values)
|
||||
expect(validate).not.toThrow()
|
||||
})
|
||||
|
||||
it("allows a ttl of 7 days", () => {
|
||||
const values: WorkspaceScheduleFormValues = {
|
||||
...valid,
|
||||
|
@ -4,7 +4,7 @@ import FormControlLabel from "@material-ui/core/FormControlLabel"
|
||||
import FormGroup from "@material-ui/core/FormGroup"
|
||||
import FormHelperText from "@material-ui/core/FormHelperText"
|
||||
import FormLabel from "@material-ui/core/FormLabel"
|
||||
import Link from "@material-ui/core/Link"
|
||||
import MenuItem from "@material-ui/core/MenuItem"
|
||||
import makeStyles from "@material-ui/core/styles/makeStyles"
|
||||
import TextField from "@material-ui/core/TextField"
|
||||
import dayjs from "dayjs"
|
||||
@ -18,6 +18,7 @@ import { getFormHelpers } from "../../util/formUtils"
|
||||
import { FormFooter } from "../FormFooter/FormFooter"
|
||||
import { FullPageForm } from "../FullPageForm/FullPageForm"
|
||||
import { Stack } from "../Stack/Stack"
|
||||
import { zones } from "./zones"
|
||||
|
||||
// REMARK: timezone plugin depends on UTC
|
||||
//
|
||||
@ -203,21 +204,20 @@ export const WorkspaceScheduleForm: FC<WorkspaceScheduleFormProps> = ({
|
||||
/>
|
||||
|
||||
<TextField
|
||||
{...formHelpers(
|
||||
"timezone",
|
||||
<>
|
||||
Timezone must be a valid name from the{" "}
|
||||
<Link href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List" target="_blank">
|
||||
timezone database
|
||||
</Link>
|
||||
</>,
|
||||
)}
|
||||
{...formHelpers("timezone")}
|
||||
disabled={isLoading}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
label={Language.timezoneLabel}
|
||||
/>
|
||||
select
|
||||
>
|
||||
{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">
|
||||
|
3
site/src/components/WorkspaceScheduleForm/zones.ts
Normal file
3
site/src/components/WorkspaceScheduleForm/zones.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import tzData from "tzdata"
|
||||
|
||||
export const zones: string[] = Object.keys(tzData.zones)
|
@ -92,7 +92,10 @@ export const formValuesToTTLRequest = (values: WorkspaceScheduleFormValues): Typ
|
||||
}
|
||||
}
|
||||
|
||||
export const workspaceToInitialValues = (workspace: TypesGen.Workspace): WorkspaceScheduleFormValues => {
|
||||
export const workspaceToInitialValues = (
|
||||
workspace: TypesGen.Workspace,
|
||||
defaultTimeZone = "",
|
||||
): WorkspaceScheduleFormValues => {
|
||||
const schedule = workspace.autostart_schedule
|
||||
const ttlHours = workspace.ttl_ms ? Math.round(workspace.ttl_ms / (1000 * 60 * 60)) : 0
|
||||
|
||||
@ -106,12 +109,12 @@ export const workspaceToInitialValues = (workspace: TypesGen.Workspace): Workspa
|
||||
friday: false,
|
||||
saturday: false,
|
||||
startTime: "",
|
||||
timezone: "",
|
||||
timezone: defaultTimeZone,
|
||||
ttl: ttlHours,
|
||||
}
|
||||
}
|
||||
|
||||
const timezone = extractTimezone(schedule, dayjs.tz.guess())
|
||||
const timezone = extractTimezone(schedule, defaultTimeZone)
|
||||
|
||||
const expression = cronParser.parseExpression(stripTimezone(schedule))
|
||||
|
||||
@ -162,7 +165,7 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
return (
|
||||
<WorkspaceScheduleForm
|
||||
fieldErrors={formErrors}
|
||||
initialValues={workspaceToInitialValues(workspace)}
|
||||
initialValues={workspaceToInitialValues(workspace, dayjs.tz.guess())}
|
||||
isLoading={scheduleState.tags.has("loading")}
|
||||
onCancel={() => {
|
||||
navigate(`/workspaces/${workspaceId}`)
|
||||
|
@ -13270,6 +13270,11 @@ typescript@4.6.4:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
|
||||
integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
|
||||
|
||||
tzdata@1.0.30:
|
||||
version "1.0.30"
|
||||
resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.30.tgz#d9d5a4b4b5e1ed95f6255f98c0564c4256316f52"
|
||||
integrity sha512-/0yogZsIRUVhGIEGZahL+Nnl9gpMD6jtQ9MlVtPVofFwhaqa+cFTgRy1desTAKqdmIJjS6CL+i6F/mnetrLaxw==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.15.1"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.1.tgz#9403dc6fa5695a6172a91bc983ea39f0f7c9086d"
|
||||
|
Reference in New Issue
Block a user