Skip to content

Commit d305787

Browse files
committed
Add increase popover
1 parent eeafd82 commit d305787

File tree

2 files changed

+212
-49
lines changed

2 files changed

+212
-49
lines changed

site/src/components/Workspace/Workspace.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
194194
workspace={workspace}
195195
quota_budget={quota_budget}
196196
handleUpdate={handleUpdate}
197+
maxDeadlineDecrease={scheduleProps.maxDeadlineDecrease}
198+
maxDeadlineIncrease={scheduleProps.maxDeadlineIncrease}
197199
/>
198200

199201
{failedBuildLogs && (
Lines changed: 210 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Link from "@material-ui/core/Link"
22
import { OutdatedHelpTooltip } from "components/Tooltips"
3-
import { FC } from "react"
3+
import { FC, useRef, useState } from "react"
44
import { Link as RouterLink } from "react-router-dom"
55
import { createDayString } from "util/createDayString"
66
import {
@@ -12,6 +12,14 @@ import { Workspace } from "../../api/typesGenerated"
1212
import { Stats, StatsItem } from "components/Stats/Stats"
1313
import upperFirst from "lodash/upperFirst"
1414
import { autostopDisplay } from "util/schedule"
15+
import IconButton from "@material-ui/core/IconButton"
16+
import RemoveIcon from "@material-ui/icons/RemoveOutlined"
17+
import { makeStyles } from "@material-ui/core/styles"
18+
import AddIcon from "@material-ui/icons/AddOutlined"
19+
import Popover from "@material-ui/core/Popover"
20+
import TextField from "@material-ui/core/TextField"
21+
import { Stack } from "components/Stack/Stack"
22+
import Button from "@material-ui/core/Button"
1523

1624
const Language = {
1725
workspaceDetails: "Workspace Details",
@@ -27,77 +35,164 @@ const Language = {
2735

2836
export interface WorkspaceStatsProps {
2937
workspace: Workspace
38+
maxDeadlineIncrease: number
39+
maxDeadlineDecrease: number
3040
quota_budget?: number
3141
handleUpdate: () => void
3242
}
3343

3444
export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
3545
workspace,
3646
quota_budget,
47+
maxDeadlineDecrease,
48+
maxDeadlineIncrease,
3749
handleUpdate,
3850
}) => {
3951
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(
4052
workspace.latest_build,
4153
)
4254
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
55+
const styles = useStyles()
56+
const deadlinePlusEnabled = maxDeadlineIncrease >= 1
57+
const deadlineMinusEnabled = maxDeadlineDecrease >= 1
58+
const addingButtonRef = useRef<HTMLButtonElement>(null)
59+
const [isAddingTime, setIsAddingTime] = useState(false)
4360

4461
return (
45-
<Stats aria-label={Language.workspaceDetails}>
46-
<StatsItem
47-
label={Language.templateLabel}
48-
value={
49-
<Link
50-
component={RouterLink}
51-
to={`/templates/${workspace.template_name}`}
52-
>
53-
{displayTemplateName}
54-
</Link>
55-
}
56-
/>
57-
<StatsItem
58-
label={Language.versionLabel}
59-
value={
60-
<>
62+
<>
63+
<Stats aria-label={Language.workspaceDetails}>
64+
<StatsItem
65+
label={Language.templateLabel}
66+
value={
6167
<Link
6268
component={RouterLink}
63-
to={`/templates/${workspace.template_name}/versions/${workspace.latest_build.template_version_name}`}
69+
to={`/templates/${workspace.template_name}`}
6470
>
65-
{workspace.latest_build.template_version_name}
71+
{displayTemplateName}
6672
</Link>
67-
68-
{workspace.outdated && (
69-
<OutdatedHelpTooltip
70-
onUpdateVersion={handleUpdate}
71-
ariaLabel="update version"
72-
/>
73-
)}
74-
</>
75-
}
76-
/>
77-
<StatsItem
78-
label={Language.lastBuiltLabel}
79-
value={
80-
<>
81-
{upperFirst(createDayString(workspace.latest_build.created_at))} by{" "}
82-
{initiatedBy}
83-
</>
84-
}
85-
/>
86-
{shouldDisplayScheduleLabel(workspace) && (
73+
}
74+
/>
8775
<StatsItem
88-
label={getScheduleLabel(workspace)}
89-
value={autostopDisplay(workspace)}
76+
label={Language.versionLabel}
77+
value={
78+
<>
79+
<Link
80+
component={RouterLink}
81+
to={`/templates/${workspace.template_name}/versions/${workspace.latest_build.template_version_name}`}
82+
>
83+
{workspace.latest_build.template_version_name}
84+
</Link>
85+
86+
{workspace.outdated && (
87+
<OutdatedHelpTooltip
88+
onUpdateVersion={handleUpdate}
89+
ariaLabel="update version"
90+
/>
91+
)}
92+
</>
93+
}
9094
/>
91-
)}
92-
{workspace.latest_build.daily_cost > 0 && (
9395
<StatsItem
94-
label={Language.costLabel}
95-
value={`${workspace.latest_build.daily_cost} ${
96-
quota_budget ? `/ ${quota_budget}` : ""
97-
}`}
96+
label={Language.lastBuiltLabel}
97+
value={
98+
<>
99+
{upperFirst(createDayString(workspace.latest_build.created_at))}{" "}
100+
by {initiatedBy}
101+
</>
102+
}
98103
/>
99-
)}
100-
</Stats>
104+
{shouldDisplayScheduleLabel(workspace) && (
105+
<StatsItem
106+
label={getScheduleLabel(workspace)}
107+
value={
108+
<span className={styles.scheduleValue}>
109+
<Link
110+
component={RouterLink}
111+
to="settings/schedule"
112+
title="Schedule settings"
113+
>
114+
{autostopDisplay(workspace)}
115+
</Link>
116+
<span className={styles.scheduleControls}>
117+
<IconButton
118+
disabled={!deadlineMinusEnabled}
119+
size="small"
120+
title="Subtract hours from deadline"
121+
className={styles.scheduleButton}
122+
>
123+
<RemoveIcon />
124+
</IconButton>
125+
<IconButton
126+
disabled={!deadlinePlusEnabled}
127+
size="small"
128+
title="Add hours to deadline"
129+
className={styles.scheduleButton}
130+
ref={addingButtonRef}
131+
onClick={() => setIsAddingTime(true)}
132+
>
133+
<AddIcon />
134+
</IconButton>
135+
</span>
136+
</span>
137+
}
138+
/>
139+
)}
140+
{workspace.latest_build.daily_cost > 0 && (
141+
<StatsItem
142+
label={Language.costLabel}
143+
value={`${workspace.latest_build.daily_cost} ${
144+
quota_budget ? `/ ${quota_budget}` : ""
145+
}`}
146+
/>
147+
)}
148+
</Stats>
149+
150+
<Popover
151+
id="schedule-add"
152+
classes={{ paper: styles.timePopoverPaper }}
153+
open={isAddingTime}
154+
anchorEl={addingButtonRef.current}
155+
onClose={() => setIsAddingTime(false)}
156+
anchorOrigin={{
157+
vertical: "bottom",
158+
horizontal: "right",
159+
}}
160+
transformOrigin={{
161+
vertical: "top",
162+
horizontal: "right",
163+
}}
164+
>
165+
<span className={styles.timePopoverTitle}>Add hours to deadline</span>
166+
<span className={styles.timePopoverDescription}>
167+
Delay the shutdown of this workspace for a few more hours. This is
168+
only applied once.
169+
</span>
170+
<form className={styles.timePopoverForm}>
171+
<TextField
172+
type="number"
173+
size="small"
174+
fullWidth
175+
className={styles.timePopoverField}
176+
InputProps={{ className: styles.timePopoverFieldInput }}
177+
inputProps={{
178+
min: 0,
179+
max: maxDeadlineIncrease,
180+
step: 1,
181+
defaultValue: 1,
182+
}}
183+
/>
184+
185+
<Button
186+
variant="outlined"
187+
size="small"
188+
className={styles.timePopoverButton}
189+
type="submit"
190+
>
191+
Apply
192+
</Button>
193+
</form>
194+
</Popover>
195+
</>
101196
)
102197
}
103198

@@ -118,3 +213,69 @@ export const shouldDisplayScheduleLabel = (workspace: Workspace): boolean => {
118213
const getScheduleLabel = (workspace: Workspace) => {
119214
return isWorkspaceOn(workspace) ? "Stops at" : "Starts at"
120215
}
216+
217+
const useStyles = makeStyles((theme) => ({
218+
scheduleValue: {
219+
display: "flex",
220+
alignItems: "center",
221+
gap: theme.spacing(1.5),
222+
},
223+
224+
scheduleControls: {
225+
display: "flex",
226+
alignItems: "center",
227+
gap: theme.spacing(0.5),
228+
},
229+
230+
scheduleButton: {
231+
border: `1px solid ${theme.palette.divider}`,
232+
borderRadius: 4,
233+
234+
"& svg.MuiSvgIcon-root": {
235+
width: theme.spacing(1.5),
236+
height: theme.spacing(1.5),
237+
},
238+
},
239+
240+
timePopoverPaper: {
241+
padding: theme.spacing(3),
242+
maxWidth: theme.spacing(36),
243+
marginTop: theme.spacing(1),
244+
borderRadius: 4,
245+
display: "flex",
246+
flexDirection: "column",
247+
gap: theme.spacing(1),
248+
},
249+
250+
timePopoverTitle: {
251+
fontWeight: 600,
252+
},
253+
254+
timePopoverDescription: {
255+
color: theme.palette.text.secondary,
256+
},
257+
258+
timePopoverForm: {
259+
display: "flex",
260+
alignItems: "center",
261+
gap: theme.spacing(1),
262+
padding: theme.spacing(1, 0),
263+
},
264+
265+
timePopoverField: {
266+
margin: 0,
267+
},
268+
269+
timePopoverFieldInput: {
270+
fontSize: 14,
271+
padding: theme.spacing(0),
272+
borderRadius: 4,
273+
},
274+
275+
timePopoverButton: {
276+
borderRadius: 4,
277+
paddingLeft: theme.spacing(2),
278+
paddingRight: theme.spacing(2),
279+
flexShrink: 0,
280+
},
281+
}))

0 commit comments

Comments
 (0)