diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index a9849063b6034..145858f6e24f2 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -34,10 +34,12 @@ export interface WorkspaceProps { onExtend: () => void } scheduleProps: { - onDeadlinePlus: () => void - onDeadlineMinus: () => void + onDeadlinePlus: (hours: number) => void + 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, + max, +}: EditHoursProps): JSX.Element => { + const { t } = useTranslation("workspacePage") + const [hours, setHours] = useState(1) + const styles = useStyles() + + return ( +
handleSubmit(hours)}> + + setHours(parseInt(e.target.value))} + type="number" + /> + + +
+ ) +} + +const useStyles = makeStyles(() => ({ + inputField: { + width: "70px", + "& .MuiOutlinedInput-root": { + height: "30px", + }, + }, + button: { + "&.MuiButton-root": { + minHeight: "30px", + height: "30px", + }, + }, +})) diff --git a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.stories.tsx index 043c53da948bc..033c9698ced01 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, 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 66e3df5e3075e..b613786910ca6 100644 --- a/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx +++ b/site/src/components/WorkspaceScheduleButton/WorkspaceScheduleButton.tsx @@ -1,11 +1,13 @@ 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" 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" @@ -17,6 +19,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 @@ -27,12 +30,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)) { @@ -43,13 +46,17 @@ 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 + maxDeadlineIncrease: number + maxDeadlineDecrease: number canUpdateWorkspace: boolean } +export type EditMode = "add" | "subtract" | "off" + export const WorkspaceScheduleButton: React.FC< WorkspaceScheduleButtonProps > = ({ @@ -58,49 +65,93 @@ export const WorkspaceScheduleButton: React.FC< onDeadlineMinus, deadlinePlusEnabled, deadlineMinusEnabled, + maxDeadlineDecrease, + maxDeadlineIncrease, canUpdateWorkspace, }) => { 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 (hours !== 0) { + if (editMode === "add") { + onDeadlinePlus(hours) + } + if (editMode === "subtract") { + onDeadlineMinus(hours) + } + } + setEditMode("off") + } + return ( - {shouldDisplayScheduleLabel(workspace) && ( - - - {canUpdateWorkspace && shouldDisplayPlusMinus(workspace) && ( - - - - - - - - - - - - - )} - - )} + + + + + + + { + setEditMode("subtract") + }} + > + + + + + { + setEditMode("add") + }} + > + + + + + + + + + + + + <>