From 770b1c3ada8f0e2c14f6d32747785afaf6ae0b51 Mon Sep 17 00:00:00 2001 From: G r e y Date: Fri, 10 Jun 2022 16:34:37 +0000 Subject: [PATCH 1/4] fix: workspace schedule time displays Summary: Various time displays weren't quite right. Details: - Display date (not just time) of upcoming workspace stop in workspace page - Fix ttlShutdownAt for various cases + tests --> manual to non-manual --> unchanged/unmodified --> isBefore --> isSameOrBefore --> use the delta (off by _ error) --- .../WorkspaceSchedule/WorkspaceSchedule.tsx | 6 +- .../WorkspaceScheduleForm.test.ts | 88 ++++++++++++++++--- .../WorkspaceScheduleForm.tsx | 41 ++++++--- 3 files changed, 109 insertions(+), 26 deletions(-) diff --git a/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx b/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx index 1a5d35a499f11..5d5d1db960c5c 100644 --- a/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx +++ b/site/src/components/WorkspaceSchedule/WorkspaceSchedule.tsx @@ -17,8 +17,10 @@ import { stripTimezone } from "../../util/schedule" import { isWorkspaceOn } from "../../util/workspace" import { Stack } from "../Stack/Stack" -dayjs.extend(advancedFormat) +// REMARK: some plugins depend on utc, so it's listed first. Otherwise they're +// sorted alphabetically. dayjs.extend(utc) +dayjs.extend(advancedFormat) dayjs.extend(duration) dayjs.extend(relativeTime) dayjs.extend(timezone) @@ -50,7 +52,7 @@ export const Language = { if (now.isAfter(deadline)) { return "Workspace is shutting down" } else { - return deadline.tz(dayjs.tz.guess()).format("hh:mm A") + return deadline.tz(dayjs.tz.guess()).format("MMM D, YYYY h:mm A") } } else if (!ttl || ttl < 1) { // If the workspace is not on, and the ttl is 0 or undefined, then the diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts index 8fa25cc66abd3..3e37484b9c09b 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts @@ -160,31 +160,99 @@ describe("validationSchema", () => { }) describe("ttlShutdownAt", () => { - it.each<[dayjs.Dayjs, Workspace, string, number, string]>([ - [dayjs("2022-05-17T18:09:00Z"), Mocks.MockStoppedWorkspace, "America/Chicago", 1, Language.ttlHelperText], - [dayjs("2022-05-17T18:09:00Z"), Mocks.MockWorkspace, "America/Chicago", 0, Language.ttlCausesNoShutdownHelperText], + it.each<[string, dayjs.Dayjs, Workspace, string, number, string]>([ [ + "Workspace is stopped --> helper text", dayjs("2022-05-17T18:09:00Z"), - Mocks.MockWorkspace, + Mocks.MockStoppedWorkspace, "America/Chicago", 1, - `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} 01:39 PM CDT.`, + Language.ttlHelperText, ], [ - dayjs("2022-05-17T18:10:00Z"), + "TTL is not modified --> helper text", + dayjs("2022-05-17T16:09:00Z"), + { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + deadline: "2022-05-17T18:09:00Z", + }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 + }, + "America/Chicago", + 2, + Language.ttlHelperText, + ], + [ + "TTL becomes 0 --> manual helper text", + dayjs("2022-05-17T18:09:00Z"), Mocks.MockWorkspace, "America/Chicago", + 0, + Language.ttlCausesNoShutdownHelperText, + ], + [ + "Deadline of 18:09 becomes 17:09 at 17:09 --> immediate shutdown", + dayjs("2022-05-17T17:09:00Z"), + { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + deadline: "2022-05-17T18:09:00Z", + }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 + }, + "America/Chicago", + 1, + `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownImmediately} ⚠️`, + ], + [ + "Deadline of 18:09 becomes 17:09 at 16:39 --> display shutdown soon", + dayjs("2022-05-17T16:39:00Z"), + { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + deadline: "2022-05-17T18:09:00Z", + }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 + }, + "America/Chicago", 1, `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownSoon} ⚠️`, ], [ - dayjs("2022-05-17T18:40:00Z"), - Mocks.MockWorkspace, + "Deadline of 18:09 becomes 17:09 at 16:09 --> display 12:09 CDT", + dayjs("2022-05-17T16:09:00Z"), + { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + deadline: "2022-05-17T18:09:00Z", + }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 + }, "America/Chicago", 1, - `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownImmediately} ⚠️`, + `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} May 17, 2022 12:09 PM.`, + ], + [ + "Manual workspace gets new deadline of 18:09 at 17:09 --> display 1:09 CDT", + dayjs("2022-05-17T17:09:00Z"), + { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + deadline: "0001-01-01T00:00:00Z", + }, + ttl_ms: 0, + }, + "America/Chicago", + 1, + `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} May 17, 2022 1:09 PM.`, ], - ])("ttlShutdownAt(%p, %p, %p, %p) returns %p", (now, workspace, timezone, ttlHours, expected) => { + ])("%p", (_, now, workspace, timezone, ttlHours, expected) => { expect(ttlShutdownAt(now, workspace, timezone, ttlHours)).toEqual(expected) }) }) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index e5d5376596c94..999481413c42e 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -9,6 +9,7 @@ import makeStyles from "@material-ui/core/styles/makeStyles" import TextField from "@material-ui/core/TextField" import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" +import isSameOrBefore from "dayjs/plugin/isSameOrBefore" import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { useFormik } from "formik" @@ -23,11 +24,11 @@ import { FullPageForm } from "../FullPageForm/FullPageForm" import { Stack } from "../Stack/Stack" import { zones } from "./zones" -// REMARK: timezone plugin depends on UTC -// -// SEE: https://day.js.org/docs/en/timezone/timezone -dayjs.extend(advancedFormat) +// REMARK: some plugins depend on utc, so it's listed first. Otherwise they're +// sorted alphabetically. dayjs.extend(utc) +dayjs.extend(advancedFormat) +dayjs.extend(isSameOrBefore) dayjs.extend(timezone) export const Language = { @@ -282,19 +283,31 @@ export const WorkspaceScheduleForm: FC = ({ ) } -export const ttlShutdownAt = (now: dayjs.Dayjs, workspace: Workspace, tz: string, newTTL: number): string => { - const newDeadline = dayjs(workspace.latest_build.updated_at).add(newTTL, "hour") - if (!isWorkspaceOn(workspace)) { +export const ttlShutdownAt = (now: dayjs.Dayjs, workspace: Workspace, tz: string, formTTL: number): string => { + // a manual shutdown has a deadline of '"0001-01-01T00:00:00Z"' + // SEE: #1834 + const deadline = dayjs(workspace.latest_build.deadline).utc() + const hasDeadline = deadline.year() > 1 + const ttl = workspace.ttl_ms ? workspace.ttl_ms / (1000 * 60 * 60) : 0 + const delta = formTTL - ttl + + console.info("delta :: ", delta) + + if (delta === 0 || !isWorkspaceOn(workspace)) { return Language.ttlHelperText - } else if (newTTL === 0) { + } else if (formTTL === 0) { return Language.ttlCausesNoShutdownHelperText - } else if (newDeadline.isBefore(now)) { - return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownImmediately} ⚠️` - } else if (newDeadline.isBefore(now.add(30, "minute"))) { - return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownSoon} ⚠️` } else { - const newDeadlineString = newDeadline.tz(tz).format("hh:mm A z") - return `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} ${newDeadlineString}.` + const newDeadline = dayjs(hasDeadline ? deadline : now).add(delta, "hour") + if (newDeadline.isSameOrBefore(now)) { + return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownImmediately} ⚠️` + } else if (newDeadline.isSameOrBefore(now.add(30, "minute"))) { + return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownSoon} ⚠️` + } else { + return `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} ${newDeadline + .tz(tz) + .format("MMM D, YYYY h:mm A")}.` + } } } From 8a9950486f33155bdfe979a9b2c9e25178db01d1 Mon Sep 17 00:00:00 2001 From: G r e y Date: Fri, 10 Jun 2022 12:43:39 -0400 Subject: [PATCH 2/4] remove console.info --- .../components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 999481413c42e..7edbf4da53830 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -291,8 +291,6 @@ export const ttlShutdownAt = (now: dayjs.Dayjs, workspace: Workspace, tz: string const ttl = workspace.ttl_ms ? workspace.ttl_ms / (1000 * 60 * 60) : 0 const delta = formTTL - ttl - console.info("delta :: ", delta) - if (delta === 0 || !isWorkspaceOn(workspace)) { return Language.ttlHelperText } else if (formTTL === 0) { From 69974e378187eff18dcc8b3284a32547dd2e9b56 Mon Sep 17 00:00:00 2001 From: G r e y Date: Fri, 10 Jun 2022 12:46:27 -0400 Subject: [PATCH 3/4] pluralize units in dayjs.add --- .../WorkspaceScheduleForm/WorkspaceScheduleForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index 7edbf4da53830..efd2c78199e34 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -296,10 +296,10 @@ export const ttlShutdownAt = (now: dayjs.Dayjs, workspace: Workspace, tz: string } else if (formTTL === 0) { return Language.ttlCausesNoShutdownHelperText } else { - const newDeadline = dayjs(hasDeadline ? deadline : now).add(delta, "hour") + const newDeadline = dayjs(hasDeadline ? deadline : now).add(delta, "hours") if (newDeadline.isSameOrBefore(now)) { return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownImmediately} ⚠️` - } else if (newDeadline.isSameOrBefore(now.add(30, "minute"))) { + } else if (newDeadline.isSameOrBefore(now.add(30, "minutes"))) { return `⚠️ ${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownSoon} ⚠️` } else { return `${Language.ttlCausesShutdownHelperText} ${Language.ttlCausesShutdownAt} ${newDeadline From c70c01a12fd620def31a733c8e96ec8f2a77e442 Mon Sep 17 00:00:00 2001 From: G r e y Date: Fri, 10 Jun 2022 16:55:57 +0000 Subject: [PATCH 4/4] fixup! fix: workspace schedule time displays --- .../WorkspaceScheduleForm.stories.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx index c9ea6eafa8b9d..5c3126fc74248 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.stories.tsx @@ -22,7 +22,7 @@ export const WorkspaceNotRunning = Template.bind({}) WorkspaceNotRunning.args = { now: dayjs("2022-05-17T17:40:00Z"), initialValues: { - ...defaultWorkspaceSchedule(5, "asdfasdf"), + ...defaultWorkspaceSchedule(5), timezone: "UTC", }, workspace: { @@ -41,7 +41,7 @@ export const WorkspaceWillNotShutDown = Template.bind({}) WorkspaceWillNotShutDown.args = { now: dayjs("2022-05-17T17:40:00Z"), initialValues: { - ...defaultWorkspaceSchedule(5, "asdfasdf"), + ...defaultWorkspaceSchedule(5), timezone: "UTC", ttl: 0, }, @@ -60,7 +60,7 @@ export const WorkspaceWillShutdown = Template.bind({}) WorkspaceWillShutdown.args = { now: dayjs("2022-05-17T17:40:00Z"), initialValues: { - ...defaultWorkspaceSchedule(5, "asdfasdf"), + ...defaultWorkspaceSchedule(5), timezone: "UTC", }, workspace: { @@ -76,9 +76,9 @@ WorkspaceWillShutdown.args = { export const WorkspaceWillShutdownSoon = Template.bind({}) WorkspaceWillShutdownSoon.args = { - now: dayjs("2022-05-17T18:10:00Z"), + now: dayjs("2022-05-17T16:39:00Z"), initialValues: { - ...defaultWorkspaceSchedule(5, "asdfasdf"), + ...defaultWorkspaceSchedule(2), timezone: "UTC", ttl: 1, }, @@ -86,8 +86,9 @@ WorkspaceWillShutdownSoon.args = { ...Mocks.MockWorkspace, latest_build: { ...Mocks.MockWorkspaceBuild, - updated_at: "2022-05-17T17:39:00Z", + deadline: "2022-05-17T18:09:00Z", }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 }, onCancel: () => action("onCancel"), onSubmit: () => action("onSubmit"), @@ -95,9 +96,9 @@ WorkspaceWillShutdownSoon.args = { export const WorkspaceWillShutdownImmediately = Template.bind({}) WorkspaceWillShutdownImmediately.args = { - now: dayjs("2022-05-17T18:40:00Z"), + now: dayjs("2022-05-17T17:09:00Z"), initialValues: { - ...defaultWorkspaceSchedule(5, "asdfasdf"), + ...defaultWorkspaceSchedule(1), timezone: "UTC", ttl: 1, }, @@ -105,8 +106,9 @@ WorkspaceWillShutdownImmediately.args = { ...Mocks.MockWorkspace, latest_build: { ...Mocks.MockWorkspaceBuild, - updated_at: "2022-05-17T17:39:00Z", + deadline: "2022-05-17T18:09:00Z", }, + ttl_ms: 2 * 60 * 60 * 1000, // 2 hours = shuts off at 18:09 }, onCancel: () => action("onCancel"), onSubmit: () => action("onSubmit"), 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