From d7c9df7c7d3672423bf7f59496c3da5b2be12dbc Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 4 Oct 2022 19:18:26 +0000 Subject: [PATCH 01/14] Start sketching out new design --- .../WorkspaceScheduleButton.tsx | 9 +-- .../WorkspaceScheduleLabel.tsx | 66 ++++++++++++++----- site/src/i18n/en/common.json | 5 ++ 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index e3ef8211d3ea1..c95cc75ad0cbd 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -6,6 +6,7 @@ import Tooltip from "@material-ui/core/Tooltip" import AddIcon from "@material-ui/icons/Add" import RemoveIcon from "@material-ui/icons/Remove" import ScheduleIcon from "@material-ui/icons/Schedule" +import { Maybe } from "components/Conditionals/Maybe" import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import duration from "dayjs/plugin/duration" @@ -70,10 +71,10 @@ export const WorkspaceScheduleButton: React.FC = ( return ( - {shouldDisplayScheduleLabel(workspace) && ( + - {canUpdateWorkspace && shouldDisplayPlusMinus(workspace) && ( + = ( - )} + - )} + <> + + ) +} diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index b26e3d7d696e1..fdb7ab3a597d6 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -18,6 +18,7 @@ import { useTranslation } from "react-i18next" import { Workspace } from "../../api/typesGenerated" import { isWorkspaceOn } from "../../util/workspace" import { WorkspaceSchedule } from "../WorkspaceSchedule/WorkspaceSchedule" +import { EditHours } from "./EditHours" import { WorkspaceScheduleLabel } from "./WorkspaceScheduleLabel" // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're @@ -28,12 +29,12 @@ dayjs.extend(duration) dayjs.extend(relativeTime) dayjs.extend(timezone) -export const shouldDisplayPlusMinus = (workspace: Workspace): boolean => { +export const canEditDeadline = (workspace: Workspace): boolean => { return isWorkspaceOn(workspace) && Boolean(workspace.latest_build.deadline) } export const shouldDisplayScheduleLabel = (workspace: Workspace): boolean => { - if (shouldDisplayPlusMinus(workspace)) { + if (canEditDeadline(workspace)) { return true } if (isWorkspaceOn(workspace)) { @@ -44,13 +45,15 @@ export const shouldDisplayScheduleLabel = (workspace: Workspace): boolean => { export interface WorkspaceScheduleButtonProps { workspace: Workspace - onDeadlinePlus: () => void - onDeadlineMinus: () => void + onDeadlinePlus: (hours: number) => void + onDeadlineMinus: (hours: number) => void deadlineMinusEnabled: () => boolean deadlinePlusEnabled: () => boolean canUpdateWorkspace: boolean } +export type EditMode = "add" | "subtract" | "off" + export const WorkspaceScheduleButton: React.FC< WorkspaceScheduleButtonProps > = ({ @@ -64,25 +67,38 @@ export const WorkspaceScheduleButton: React.FC< const { t } = useTranslation("workspacePage") const anchorRef = useRef(null) const [isOpen, setIsOpen] = useState(false) + const [editMode, setEditMode] = useState("off") const id = isOpen ? "schedule-popover" : undefined - const styles = useStyles() + const styles = useStyles(editMode) const onClose = () => { setIsOpen(false) } + const handleSubmitHours = (hours: number) => { + if (editMode === "add") { + onDeadlinePlus(hours) + } + if (editMode === "subtract") { + onDeadlineMinus(hours) + } + setEditMode("off") + } + return ( - + { + setEditMode("subtract") + }} > @@ -92,13 +108,18 @@ export const WorkspaceScheduleButton: React.FC< className={styles.iconButton} size="small" disabled={!deadlinePlusEnabled()} - onClick={onDeadlinePlus} + onClick={() => { + setEditMode("add") + }} > + + + diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx index 136a3084d26fb..319b5fd0738a7 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx @@ -1,37 +1,12 @@ import { makeStyles } from "@material-ui/core/styles" -import TextField from "@material-ui/core/TextField" import { ChooseOne, Cond } from "components/Conditionals/ChooseOne" import { Maybe } from "components/Conditionals/Maybe" -import { LoadingButton } from "components/LoadingButton/LoadingButton" import { useTranslation } from "react-i18next" import { Workspace } from "../../api/typesGenerated" import { combineClasses } from "../../util/combineClasses" import { autoStartDisplay, autoStopDisplay, isShuttingDown } from "../../util/schedule" import { isWorkspaceOn } from "../../util/workspace" -const AutoStopDisplay = ({ workspace }: { workspace: Workspace }): JSX.Element => { - const { t } = useTranslation("common") - const autoStopTime = autoStopDisplay(workspace) - return ( - - - <> - - - {t("schedule.submitUpdate")} - - - - - {autoStopTime} - - - ) -} - export const WorkspaceScheduleLabel: React.FC<{ workspace: Workspace }> = ({ workspace }) => { const styles = useStyles() const { t } = useTranslation("common") @@ -44,7 +19,7 @@ export const WorkspaceScheduleLabel: React.FC<{ workspace: Workspace }> = ({ wor {" "} - + {autoStopDisplay(workspace)} diff --git a/site/src/i18n/en/common.json b/site/src/i18n/en/common.json index 64db2c167f1f6..463a8176bf164 100644 --- a/site/src/i18n/en/common.json +++ b/site/src/i18n/en/common.json @@ -21,8 +21,7 @@ }, "schedule": { "autoStartLabel": "Starts at", - "autoStopLabel": "Stops at", - "submitUpdate": "Update" + "autoStopLabel": "Stops at" }, "ctas": { "dismissCta": "Dismiss", diff --git a/site/src/i18n/en/workspacePage.json b/site/src/i18n/en/workspacePage.json index 781e03121ef42..8f36e9b3d44e6 100644 --- a/site/src/i18n/en/workspacePage.json +++ b/site/src/i18n/en/workspacePage.json @@ -4,8 +4,10 @@ }, "workspaceScheduleButton": { "schedule": "Schedule", - "editDeadlineMinus": "Subtract one hour", - "editDeadlinePlus": "Add one hour" + "editDeadlineMinus": "Subtract hours", + "editDeadlinePlus": "Add hours", + "submitDeadline": "Set", + "hours": "Hours" }, "ctas": { "createWorkspaceCta": "Create new workspace", diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 300ec5ca92e74..551e0faff97b1 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -81,16 +81,16 @@ export const WorkspaceReadyPage = ({ }, }} scheduleProps={{ - onDeadlineMinus: () => { + onDeadlineMinus: (hours: number) => { bannerSend({ type: "DECREASE_DEADLINE", - hours: 1, + hours, }) }, - onDeadlinePlus: () => { + onDeadlinePlus: (hours: number) => { bannerSend({ type: "INCREASE_DEADLINE", - hours: 1, + hours, }) }, deadlineMinusEnabled: () => !bannerState.matches("atMinDeadline"), From 833210e58d5fbc7fd80eaffc2c8d2d601b1aa769 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 11 Oct 2022 22:46:01 +0000 Subject: [PATCH 03/14] Highlight chosen mode --- .../WorkspaceScheduleButton.tsx | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index fdb7ab3a597d6..503c18a1e3b27 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -1,7 +1,7 @@ import Button from "@material-ui/core/Button" import IconButton from "@material-ui/core/IconButton" import Popover from "@material-ui/core/Popover" -import { makeStyles } from "@material-ui/core/styles" +import { makeStyles, Theme } from "@material-ui/core/styles" import Tooltip from "@material-ui/core/Tooltip" import AddIcon from "@material-ui/icons/Add" import RemoveIcon from "@material-ui/icons/Remove" @@ -69,7 +69,7 @@ export const WorkspaceScheduleButton: React.FC< const [isOpen, setIsOpen] = useState(false) const [editMode, setEditMode] = useState("off") const id = isOpen ? "schedule-popover" : undefined - const styles = useStyles(editMode) + const styles = useStyles({ editMode }) const onClose = () => { setIsOpen(false) @@ -93,7 +93,7 @@ export const WorkspaceScheduleButton: React.FC< { @@ -105,7 +105,7 @@ export const WorkspaceScheduleButton: React.FC< { @@ -161,7 +161,11 @@ export const WorkspaceScheduleButton: React.FC< ) } -const useStyles = makeStyles((theme) => ({ +interface StyleProps { + editMode: EditMode +} + +const useStyles = makeStyles((theme) => ({ wrapper: { display: "inline-flex", alignItems: "center", @@ -212,8 +216,13 @@ const useStyles = makeStyles((theme) => ({ }, }, }, - iconButton: { + addButton: { + borderRadius: theme.shape.borderRadius, + backgroundColor: ({ editMode }) => editMode === "add" ? theme.palette.primary.main : "inherit" + }, + subtractButton: { borderRadius: theme.shape.borderRadius, + backgroundColor: ({ editMode }) => editMode === "subtract" ? theme.palette.primary.main : "inherit" }, popoverPaper: { padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing( From 799491711ac184560bf1dbf9a2382216ddd3f010 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 11 Oct 2022 22:46:23 +0000 Subject: [PATCH 04/14] Format --- .../WorkspaceScheduleButton/EditHours.tsx | 4 +- .../WorkspaceScheduleButton.tsx | 6 ++- .../WorkspaceScheduleLabel.tsx | 52 +++++++++++-------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/EditHours.tsx b/site/src/components/WorkspaceScheduleButton/EditHours.tsx index 1a2bed5471d20..11401b3270cc6 100644 --- a/site/src/components/WorkspaceScheduleButton/EditHours.tsx +++ b/site/src/components/WorkspaceScheduleButton/EditHours.tsx @@ -19,7 +19,9 @@ export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { onChange={(e) => setHours(parseInt(e.target.value))} type="number" /> - + ) } diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index 503c18a1e3b27..7bb01085d49a3 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -218,11 +218,13 @@ const useStyles = makeStyles((theme) => ({ }, addButton: { borderRadius: theme.shape.borderRadius, - backgroundColor: ({ editMode }) => editMode === "add" ? theme.palette.primary.main : "inherit" + backgroundColor: ({ editMode }) => + editMode === "add" ? theme.palette.primary.main : "inherit", }, subtractButton: { borderRadius: theme.shape.borderRadius, - backgroundColor: ({ editMode }) => editMode === "subtract" ? theme.palette.primary.main : "inherit" + backgroundColor: ({ editMode }) => + editMode === "subtract" ? theme.palette.primary.main : "inherit", }, popoverPaper: { padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing( diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx index 319b5fd0738a7..609abf9d96786 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleLabel.tsx @@ -4,33 +4,43 @@ import { Maybe } from "components/Conditionals/Maybe" import { useTranslation } from "react-i18next" import { Workspace } from "../../api/typesGenerated" import { combineClasses } from "../../util/combineClasses" -import { autoStartDisplay, autoStopDisplay, isShuttingDown } from "../../util/schedule" +import { + autoStartDisplay, + autoStopDisplay, + isShuttingDown, +} from "../../util/schedule" import { isWorkspaceOn } from "../../util/workspace" -export const WorkspaceScheduleLabel: React.FC<{ workspace: Workspace }> = ({ workspace }) => { +export const WorkspaceScheduleLabel: React.FC<{ workspace: Workspace }> = ({ + workspace, +}) => { const styles = useStyles() const { t } = useTranslation("common") - return - - - - {t("schedule.autoStopLabel")} - - {" "} - - {autoStopDisplay(workspace)} + return ( + + + + + {t("schedule.autoStopLabel")} + {" "} + {autoStopDisplay(workspace)} - - - - - {t("schedule.autoStartLabel")} - {" "} - {autoStartDisplay(workspace.autostart_schedule)} - - - + + + + {t("schedule.autoStartLabel")}{" "} + + {autoStartDisplay(workspace.autostart_schedule)} + + + + + ) } const useStyles = makeStyles((theme) => ({ From 2983a19a6d8821241957930544e8d87850ec32ab Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 11 Oct 2022 22:50:15 +0000 Subject: [PATCH 05/14] Set hours field width --- .../components/WorkspaceScheduleButton/EditHours.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/site/src/components/WorkspaceScheduleButton/EditHours.tsx b/site/src/components/WorkspaceScheduleButton/EditHours.tsx index 11401b3270cc6..bfb6712dfdc47 100644 --- a/site/src/components/WorkspaceScheduleButton/EditHours.tsx +++ b/site/src/components/WorkspaceScheduleButton/EditHours.tsx @@ -1,4 +1,5 @@ import Button from "@material-ui/core/Button" +import { makeStyles } from "@material-ui/core/styles" import TextField from "@material-ui/core/TextField" import { useState } from "react" import { useTranslation } from "react-i18next" @@ -10,9 +11,12 @@ interface EditHoursProps { export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { const { t } = useTranslation("workspacePage") const [hours, setHours] = useState(0) + const styles = useStyles() + return (
handleSubmit(hours)}> { ) } + +const useStyles = makeStyles(() => ({ + inputField: { + width: '70px' + } +})) From 8f872c32a1037185cd4a7acf539ff0f2bf32e189 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 12 Oct 2022 00:32:59 +0000 Subject: [PATCH 06/14] Alignment on desktop --- .../WorkspaceScheduleButton/EditHours.tsx | 16 ++++++++++-- .../WorkspaceScheduleButton.tsx | 25 +++++++++++-------- .../WorkspaceScheduleLabel.tsx | 3 ++- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/EditHours.tsx b/site/src/components/WorkspaceScheduleButton/EditHours.tsx index bfb6712dfdc47..fef10cc9247ba 100644 --- a/site/src/components/WorkspaceScheduleButton/EditHours.tsx +++ b/site/src/components/WorkspaceScheduleButton/EditHours.tsx @@ -1,6 +1,7 @@ import Button from "@material-ui/core/Button" import { makeStyles } from "@material-ui/core/styles" import TextField from "@material-ui/core/TextField" +import { Stack } from "components/Stack/Stack" import { useState } from "react" import { useTranslation } from "react-i18next" @@ -15,6 +16,7 @@ export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { return (
handleSubmit(hours)}> + { onChange={(e) => setHours(parseInt(e.target.value))} type="number" /> - +
) } const useStyles = makeStyles(() => ({ inputField: { - width: '70px' + width: '70px', + "& .MuiOutlinedInput-root": { + height: '30px' + } + }, + button: { + "&.MuiButton-root": { + minHeight: '30px', + height: '30px' + } } })) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index 7bb01085d49a3..c54f623cb2e51 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -7,6 +7,7 @@ import AddIcon from "@material-ui/icons/Add" import RemoveIcon from "@material-ui/icons/Remove" import ScheduleIcon from "@material-ui/icons/Schedule" import { Maybe } from "components/Conditionals/Maybe" +import { Stack } from "components/Stack/Stack" import dayjs from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" import duration from "dayjs/plugin/duration" @@ -76,11 +77,13 @@ export const WorkspaceScheduleButton: React.FC< } const handleSubmitHours = (hours: number) => { - if (editMode === "add") { - onDeadlinePlus(hours) - } - if (editMode === "subtract") { - onDeadlineMinus(hours) + if (hours !== 0) { + if (editMode === "add") { + onDeadlinePlus(hours) + } + if (editMode === "subtract") { + onDeadlineMinus(hours) + } } setEditMode("off") } @@ -88,7 +91,7 @@ export const WorkspaceScheduleButton: React.FC< return ( - + @@ -121,7 +124,7 @@ export const WorkspaceScheduleButton: React.FC< - +
<> From 30f2b2f7ba1e5d464b89a234203bf9cd4c7f10cd Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 12 Oct 2022 00:41:20 +0000 Subject: [PATCH 08/14] Make 1 the default change --- site/src/components/WorkspaceScheduleButton/EditHours.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/WorkspaceScheduleButton/EditHours.tsx b/site/src/components/WorkspaceScheduleButton/EditHours.tsx index 4c643cc6233d3..338853094a4c0 100644 --- a/site/src/components/WorkspaceScheduleButton/EditHours.tsx +++ b/site/src/components/WorkspaceScheduleButton/EditHours.tsx @@ -11,7 +11,7 @@ interface EditHoursProps { export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { const { t } = useTranslation("workspacePage") - const [hours, setHours] = useState(0) + const [hours, setHours] = useState(1) const styles = useStyles() return ( From 31567ce1a578f9f9cd9bce2d53d52dbe086e3ae8 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 12 Oct 2022 18:45:18 +0000 Subject: [PATCH 09/14] Add stepper max --- site/src/components/Workspace/Workspace.tsx | 4 ++++ .../WorkspaceScheduleButton/EditHours.tsx | 5 +++-- .../WorkspaceScheduleButton.test.tsx | 6 +++--- .../WorkspaceScheduleButton.tsx | 6 +++++- .../pages/WorkspacePage/WorkspaceReadyPage.tsx | 4 ++++ site/src/util/schedule.test.ts | 17 +++++++++++++++++ site/src/util/schedule.ts | 11 +++++++++++ 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index ba9b0a0dc240f..145858f6e24f2 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -38,6 +38,8 @@ export interface WorkspaceProps { onDeadlineMinus: (hours: number) => void deadlinePlusEnabled: () => boolean deadlineMinusEnabled: () => boolean + maxDeadlineIncrease: number + maxDeadlineDecrease: number } handleStart: () => void handleStop: () => void @@ -121,6 +123,8 @@ export const Workspace: FC> = ({ onDeadlinePlus={scheduleProps.onDeadlinePlus} deadlineMinusEnabled={scheduleProps.deadlineMinusEnabled} deadlinePlusEnabled={scheduleProps.deadlinePlusEnabled} + maxDeadlineDecrease={scheduleProps.maxDeadlineDecrease} + maxDeadlineIncrease={scheduleProps.maxDeadlineIncrease} canUpdateWorkspace={canUpdateWorkspace} /> void + max: number } -export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { +export const EditHours = ({ handleSubmit, max }: EditHoursProps): JSX.Element => { const { t } = useTranslation("workspacePage") const [hours, setHours] = useState(1) const styles = useStyles() @@ -19,7 +20,7 @@ export const EditHours = ({ handleSubmit }: EditHoursProps): JSX.Element => { setHours(parseInt(e.target.value))} diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.test.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.test.tsx index 8e0d4e53583f1..5ee401f7be56b 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.test.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.test.tsx @@ -2,7 +2,7 @@ import dayjs from "dayjs" import utc from "dayjs/plugin/utc" import * as TypesGen from "../../api/typesGenerated" import * as Mocks from "../../testHelpers/entities" -import { shouldDisplayPlusMinus } from "./WorkspaceScheduleButton" +import { canEditDeadline } from "./WorkspaceScheduleButton" dayjs.extend(utc) @@ -13,7 +13,7 @@ describe("WorkspaceScheduleButton", () => { const workspace: TypesGen.Workspace = Mocks.MockStoppedWorkspace // Then: shouldDisplayPlusMinus should be false - expect(shouldDisplayPlusMinus(workspace)).toBeFalsy() + expect(canEditDeadline(workspace)).toBeFalsy() }) it("should display if the workspace is running", () => { @@ -21,7 +21,7 @@ describe("WorkspaceScheduleButton", () => { const workspace: TypesGen.Workspace = Mocks.MockWorkspace // Then: shouldDisplayPlusMinus should be false - expect(shouldDisplayPlusMinus(workspace)).toBeTruthy() + expect(canEditDeadline(workspace)).toBeTruthy() }) }) }) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index c54f623cb2e51..3838bc5ed24d4 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -50,6 +50,8 @@ export interface WorkspaceScheduleButtonProps { onDeadlineMinus: (hours: number) => void deadlineMinusEnabled: () => boolean deadlinePlusEnabled: () => boolean + maxDeadlineIncrease: number + maxDeadlineDecrease: number canUpdateWorkspace: boolean } @@ -63,6 +65,8 @@ export const WorkspaceScheduleButton: React.FC< onDeadlineMinus, deadlinePlusEnabled, deadlineMinusEnabled, + maxDeadlineDecrease, + maxDeadlineIncrease, canUpdateWorkspace, }) => { const { t } = useTranslation("workspacePage") @@ -121,7 +125,7 @@ export const WorkspaceScheduleButton: React.FC<
- + diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 551e0faff97b1..60240ca964a77 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -4,6 +4,8 @@ import dayjs from "dayjs" import { useContext } from "react" import { Helmet } from "react-helmet-async" import { useTranslation } from "react-i18next" +import { getMaxDeadline, getMaxDeadlineChange, getMinDeadline } from "util/schedule" +//import { getMaxDeadlineDecrease, getMaxDeadlineIncrease } from "util/schedule" import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors" import { StateFrom } from "xstate" import { DeleteDialog } from "../../components/Dialogs/DeleteDialog/DeleteDialog" @@ -95,6 +97,8 @@ export const WorkspaceReadyPage = ({ }, deadlineMinusEnabled: () => !bannerState.matches("atMinDeadline"), deadlinePlusEnabled: () => !bannerState.matches("atMaxDeadline"), + maxDeadlineDecrease: getMaxDeadlineChange(bannerState.context.workspace.deadline, getMinDeadline()), + maxDeadlineIncrease: getMaxDeadlineChange(bannerState.context.workspace.deadline, getMaxDeadline(workspace, bannerState.context.template)) }} isUpdating={workspaceState.hasTag("updating")} workspace={workspace} diff --git a/site/src/util/schedule.test.ts b/site/src/util/schedule.test.ts index dc25996cc4517..9cd3dd1b50177 100644 --- a/site/src/util/schedule.test.ts +++ b/site/src/util/schedule.test.ts @@ -9,6 +9,7 @@ import { deadlineExtensionMin, extractTimezone, getMaxDeadline, + getMaxDeadlineChange, getMinDeadline, stripTimezone, } from "./schedule" @@ -124,3 +125,19 @@ describe("canReduceDeadline", () => { expect(canReduceDeadline(dayjs().add(100, "years"))).toBeTruthy() }) }) + +describe("getMaxDeadlineChange", () => { + it("should return the number of hours you can add before hitting the max deadline", () => { + const deadline = dayjs() + const maxDeadline = dayjs().add(1, "hour").add(40, "minutes") + // you can only add one hour even though the max is 1:40 away + expect(getMaxDeadlineChange(deadline, maxDeadline)).toEqual(1) + }) + + it("should return the number of hours you can subtract before hitting the min deadline", () => { + const deadline = dayjs().add(2, "hours").add(40, "minutes") + const minDeadline = dayjs() + // you can only subtract 2 hours even though the min is 2:40 less + expect(getMaxDeadlineChange(deadline, minDeadline)).toEqual(2) + }) +}) diff --git a/site/src/util/schedule.ts b/site/src/util/schedule.ts index 75e3f849718a8..8978f7f114b1f 100644 --- a/site/src/util/schedule.ts +++ b/site/src/util/schedule.ts @@ -167,3 +167,14 @@ export function canReduceDeadline(deadline: dayjs.Dayjs): boolean { export const getDeadline = (workspace: Workspace): dayjs.Dayjs => dayjs(workspace.latest_build.deadline).utc() + +/** + * Get number of hours you can add or subtract to the current deadline before hitting the max or min deadline. + * @param deadline + * @param workspace + * @param template + * @returns number, in hours + */ +export const getMaxDeadlineChange = (deadline: dayjs.Dayjs, extremeDeadline: dayjs.Dayjs): number => { + return Math.abs(deadline.diff(extremeDeadline, "hours")) +} From e49ff22304e76268028458f9f06c6c3aa17a7c3a Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 12 Oct 2022 18:53:39 +0000 Subject: [PATCH 10/14] Fix storybook --- .../WorkspaceScheduleButton.stories.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx index 043c53da948bc..5b270fa47673c 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx @@ -16,6 +16,12 @@ export default { canUpdateWorkspace: { defaultValue: true, }, + deadlineMinusEnabled: { + defaultValue: (): boolean => false + }, + deadlinePlusEnabled: { + defaultValue: (): boolean => false + } }, } @@ -64,6 +70,7 @@ WorkspaceOffShort.args = { export const WorkspaceOffLong = Template.bind({}) WorkspaceOffLong.args = { + deadlinePlusEnabled: () => true, workspace: { ...Mocks.MockWorkspace, From 666c8d8c3eb21e9fc8875bf155c7a883bf145e03 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Wed, 12 Oct 2022 19:24:20 +0000 Subject: [PATCH 11/14] Handle undefined deadline --- site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx | 7 ++++--- site/src/util/schedule.ts | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 60240ca964a77..4892b38f6b2e7 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -5,7 +5,6 @@ import { useContext } from "react" import { Helmet } from "react-helmet-async" import { useTranslation } from "react-i18next" import { getMaxDeadline, getMaxDeadlineChange, getMinDeadline } from "util/schedule" -//import { getMaxDeadlineDecrease, getMaxDeadlineIncrease } from "util/schedule" import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors" import { StateFrom } from "xstate" import { DeleteDialog } from "../../components/Dialogs/DeleteDialog/DeleteDialog" @@ -33,6 +32,7 @@ export const WorkspaceReadyPage = ({ const [bannerState, bannerSend] = useActor( workspaceState.children["scheduleBannerMachine"], ) + const deadline = bannerState.context.workspace.deadline const xServices = useContext(XServiceContext) const featureVisibility = useSelector( xServices.entitlementsXService, @@ -41,6 +41,7 @@ export const WorkspaceReadyPage = ({ const [buildInfoState] = useActor(xServices.buildInfoXService) const { workspace, + template, refreshWorkspaceWarning, builds, getBuildsError, @@ -97,8 +98,8 @@ export const WorkspaceReadyPage = ({ }, deadlineMinusEnabled: () => !bannerState.matches("atMinDeadline"), deadlinePlusEnabled: () => !bannerState.matches("atMaxDeadline"), - maxDeadlineDecrease: getMaxDeadlineChange(bannerState.context.workspace.deadline, getMinDeadline()), - maxDeadlineIncrease: getMaxDeadlineChange(bannerState.context.workspace.deadline, getMaxDeadline(workspace, bannerState.context.template)) + maxDeadlineDecrease: deadline ? getMaxDeadlineChange(deadline, getMinDeadline()) : 0, + maxDeadlineIncrease: (deadline && template) ? getMaxDeadlineChange(getMaxDeadline(workspace, template), deadline) : 0 }} isUpdating={workspaceState.hasTag("updating")} workspace={workspace} diff --git a/site/src/util/schedule.ts b/site/src/util/schedule.ts index 8978f7f114b1f..4b7ab3ad88f88 100644 --- a/site/src/util/schedule.ts +++ b/site/src/util/schedule.ts @@ -175,6 +175,6 @@ export const getDeadline = (workspace: Workspace): dayjs.Dayjs => * @param template * @returns number, in hours */ -export const getMaxDeadlineChange = (deadline: dayjs.Dayjs, extremeDeadline: dayjs.Dayjs): number => { - return Math.abs(deadline.diff(extremeDeadline, "hours")) -} +export const getMaxDeadlineChange = (deadline: dayjs.Dayjs, extremeDeadline: dayjs.Dayjs): number => ( + Math.abs(deadline.diff(extremeDeadline, "hours")) +) From 33eb64c1da2ab90139f115e0e7ffd46e64d54311 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Thu, 13 Oct 2022 16:20:39 +0000 Subject: [PATCH 12/14] Access deadline correctly --- site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 4892b38f6b2e7..6524e0a6a542d 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -32,7 +32,7 @@ export const WorkspaceReadyPage = ({ const [bannerState, bannerSend] = useActor( workspaceState.children["scheduleBannerMachine"], ) - const deadline = bannerState.context.workspace.deadline + const deadline = bannerState.context.deadline const xServices = useContext(XServiceContext) const featureVisibility = useSelector( xServices.entitlementsXService, From 24a7f2d9491f28bc03236a50789fc941167d4fc0 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Thu, 13 Oct 2022 16:21:03 +0000 Subject: [PATCH 13/14] Format --- .../WorkspaceScheduleButton/EditHours.tsx | 43 ++++++++++--------- .../WorkspaceScheduleButton.stories.tsx | 6 +-- .../WorkspaceScheduleButton.tsx | 22 ++++++++-- .../WorkspacePage/WorkspaceReadyPage.tsx | 18 ++++++-- site/src/util/schedule.ts | 7 +-- 5 files changed, 63 insertions(+), 33 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/EditHours.tsx b/site/src/components/WorkspaceScheduleButton/EditHours.tsx index 2288f6aa20e67..5bcd34a710054 100644 --- a/site/src/components/WorkspaceScheduleButton/EditHours.tsx +++ b/site/src/components/WorkspaceScheduleButton/EditHours.tsx @@ -10,7 +10,10 @@ interface EditHoursProps { max: number } -export const EditHours = ({ handleSubmit, max }: EditHoursProps): JSX.Element => { +export const EditHours = ({ + handleSubmit, + max, +}: EditHoursProps): JSX.Element => { const { t } = useTranslation("workspacePage") const [hours, setHours] = useState(1) const styles = useStyles() @@ -18,33 +21,33 @@ export const EditHours = ({ handleSubmit, max }: EditHoursProps): JSX.Element => return (
handleSubmit(hours)}> - setHours(parseInt(e.target.value))} - type="number" - /> - - + setHours(parseInt(e.target.value))} + type="number" + /> + + ) } const useStyles = makeStyles(() => ({ inputField: { - width: '70px', + width: "70px", "& .MuiOutlinedInput-root": { - height: '30px' - } + height: "30px", + }, }, button: { "&.MuiButton-root": { - minHeight: '30px', - height: '30px' - } - } + minHeight: "30px", + height: "30px", + }, + }, })) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx index 5b270fa47673c..033c9698ced01 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx @@ -17,11 +17,11 @@ export default { defaultValue: true, }, deadlineMinusEnabled: { - defaultValue: (): boolean => false + defaultValue: (): boolean => false, }, deadlinePlusEnabled: { - defaultValue: (): boolean => false - } + defaultValue: (): boolean => false, + }, }, } diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index 3838bc5ed24d4..55d60dcc60af3 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -95,7 +95,12 @@ export const WorkspaceScheduleButton: React.FC< return ( - + @@ -125,7 +130,12 @@ export const WorkspaceScheduleButton: React.FC< - + @@ -226,12 +236,16 @@ const useStyles = makeStyles((theme) => ({ addButton: { borderRadius: theme.shape.borderRadius, border: ({ editMode }) => - editMode === "add" ? `2px solid ${theme.palette.primary.main}` : "2px solid transparent", + editMode === "add" + ? `2px solid ${theme.palette.primary.main}` + : "2px solid transparent", }, subtractButton: { borderRadius: theme.shape.borderRadius, border: ({ editMode }) => - editMode === "subtract" ? `2px solid ${theme.palette.primary.main}` : "2px solid transparent" + editMode === "subtract" + ? `2px solid ${theme.palette.primary.main}` + : "2px solid transparent", }, popoverPaper: { padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing( diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index 6524e0a6a542d..b695523a31327 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -4,7 +4,11 @@ import dayjs from "dayjs" import { useContext } from "react" import { Helmet } from "react-helmet-async" import { useTranslation } from "react-i18next" -import { getMaxDeadline, getMaxDeadlineChange, getMinDeadline } from "util/schedule" +import { + getMaxDeadline, + getMaxDeadlineChange, + getMinDeadline, +} from "util/schedule" import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors" import { StateFrom } from "xstate" import { DeleteDialog } from "../../components/Dialogs/DeleteDialog/DeleteDialog" @@ -98,8 +102,16 @@ export const WorkspaceReadyPage = ({ }, deadlineMinusEnabled: () => !bannerState.matches("atMinDeadline"), deadlinePlusEnabled: () => !bannerState.matches("atMaxDeadline"), - maxDeadlineDecrease: deadline ? getMaxDeadlineChange(deadline, getMinDeadline()) : 0, - maxDeadlineIncrease: (deadline && template) ? getMaxDeadlineChange(getMaxDeadline(workspace, template), deadline) : 0 + maxDeadlineDecrease: deadline + ? getMaxDeadlineChange(deadline, getMinDeadline()) + : 0, + maxDeadlineIncrease: + deadline && template + ? getMaxDeadlineChange( + getMaxDeadline(workspace, template), + deadline, + ) + : 0, }} isUpdating={workspaceState.hasTag("updating")} workspace={workspace} diff --git a/site/src/util/schedule.ts b/site/src/util/schedule.ts index 4b7ab3ad88f88..c96ba02daa47f 100644 --- a/site/src/util/schedule.ts +++ b/site/src/util/schedule.ts @@ -175,6 +175,7 @@ export const getDeadline = (workspace: Workspace): dayjs.Dayjs => * @param template * @returns number, in hours */ -export const getMaxDeadlineChange = (deadline: dayjs.Dayjs, extremeDeadline: dayjs.Dayjs): number => ( - Math.abs(deadline.diff(extremeDeadline, "hours")) -) +export const getMaxDeadlineChange = ( + deadline: dayjs.Dayjs, + extremeDeadline: dayjs.Dayjs, +): number => Math.abs(deadline.diff(extremeDeadline, "hours")) From fbd8e3c20a50ad0d3de72102038443a3e2b09757 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Thu, 13 Oct 2022 16:44:02 +0000 Subject: [PATCH 14/14] Fix overflow on mobile --- .../WorkspaceScheduleButton.tsx | 88 +++++++++++-------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx index 55d60dcc60af3..b613786910ca6 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -101,42 +101,54 @@ export const WorkspaceScheduleButton: React.FC< direction="row" alignItems="center" > - - - - { - setEditMode("subtract") - }} - > - - - - - { - setEditMode("add") - }} - > - - - - - - - + + + + + { + setEditMode("subtract") + }} + > + + + + + { + setEditMode("add") + }} + > + + + + + + + + @@ -194,15 +206,13 @@ const useStyles = makeStyles((theme) => ({ }, }, label: { - borderRight: 0, - padding: "0 8px 0 16px", + padding: theme.spacing(0, 2), color: theme.palette.text.secondary, [theme.breakpoints.down("sm")]: { width: "100%", - display: "flex", - alignItems: "center", padding: theme.spacing(1.5, 2), + flexDirection: "column", }, }, actions: {