Skip to content

Commit 5e48e1a

Browse files
committed
actually, redo a lot of this
1 parent a8eff21 commit 5e48e1a

File tree

4 files changed

+148
-159
lines changed

4 files changed

+148
-159
lines changed

site/src/pages/WorkspacePage/ActivityStatus.tsx

Lines changed: 0 additions & 56 deletions
This file was deleted.

site/src/pages/WorkspacePage/WorkspaceScheduleControls.test.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { MockTemplate, MockWorkspace } from "testHelpers/entities";
1212
import { server } from "testHelpers/server";
1313
import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";
1414
import { WorkspaceScheduleControls } from "./WorkspaceScheduleControls";
15-
import { getWorkspaceActivityStatus } from "modules/workspaces/activity";
1615

1716
const Wrapper: FC = () => {
1817
const { data: workspace } = useQuery(
@@ -26,7 +25,6 @@ const Wrapper: FC = () => {
2625
return (
2726
<WorkspaceScheduleControls
2827
workspace={workspace}
29-
status={getWorkspaceActivityStatus(workspace)}
3028
template={MockTemplate}
3129
canUpdateSchedule
3230
/>

site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx

Lines changed: 145 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ScheduleOutlined from "@mui/icons-material/ScheduleOutlined";
77
import Tooltip from "@mui/material/Tooltip";
88
import { visuallyHidden } from "@mui/utils";
99
import dayjs, { type Dayjs } from "dayjs";
10-
import { forwardRef, type FC, useRef } from "react";
10+
import { forwardRef, type FC, useRef, useState, ReactNode } from "react";
1111
import { useMutation, useQueryClient } from "react-query";
1212
import { Link as RouterLink } from "react-router-dom";
1313
import { useTime } from "hooks/useTime";
@@ -28,50 +28,96 @@ import {
2828
} from "api/queries/workspaces";
2929
import { TopbarData, TopbarIcon } from "components/FullPageLayout/Topbar";
3030
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
31-
import type { WorkspaceActivityStatus } from "modules/workspaces/activity";
31+
import {
32+
getWorkspaceActivityStatus,
33+
type WorkspaceActivityStatus,
34+
} from "modules/workspaces/activity";
35+
import { Pill } from "components/Pill/Pill";
3236

33-
export interface WorkspaceScheduleProps {
34-
status: WorkspaceActivityStatus;
37+
export interface WorkspaceScheduleContainerProps {
38+
children?: ReactNode;
39+
onClickIcon?: () => void;
40+
}
41+
42+
export const WorkspaceScheduleContainer: FC<
43+
WorkspaceScheduleContainerProps
44+
> = ({ children, onClickIcon }) => {
45+
const icon = (
46+
<TopbarIcon>
47+
<ScheduleOutlined aria-label="Schedule" />
48+
</TopbarIcon>
49+
);
50+
51+
return (
52+
<TopbarData>
53+
<Tooltip title="Schedule">
54+
{onClickIcon ? (
55+
<span
56+
onClick={onClickIcon}
57+
css={{
58+
background: "transparent",
59+
border: 0,
60+
padding: 0,
61+
fontSize: "inherit",
62+
lineHeight: "inherit",
63+
}}
64+
>
65+
{icon}
66+
</span>
67+
) : (
68+
icon
69+
)}
70+
</Tooltip>
71+
{children}
72+
</TopbarData>
73+
);
74+
};
75+
76+
interface WorkspaceScheduleControlsProps {
3577
workspace: Workspace;
3678
template: Template;
37-
canUpdateWorkspace: boolean;
79+
canUpdateSchedule: boolean;
3880
}
3981

40-
export const WorkspaceSchedule: FC<WorkspaceScheduleProps> = ({
41-
status,
82+
export const WorkspaceScheduleControls: FC<WorkspaceScheduleControlsProps> = ({
4283
workspace,
4384
template,
44-
canUpdateWorkspace,
85+
canUpdateSchedule,
4586
}) => {
46-
if (!shouldDisplayScheduleControls(workspace, status)) {
87+
const activityStatus = useTime(() => getWorkspaceActivityStatus(workspace));
88+
89+
if (!shouldDisplayScheduleControls(workspace)) {
4790
return null;
4891
}
4992

5093
return (
51-
<TopbarData>
52-
<TopbarIcon>
53-
<Tooltip title="Schedule">
54-
<ScheduleOutlined aria-label="Schedule" />
55-
</Tooltip>
56-
</TopbarIcon>
57-
<WorkspaceScheduleControls
58-
workspace={workspace}
59-
status={status}
60-
template={template}
61-
canUpdateSchedule={canUpdateWorkspace}
62-
/>
63-
</TopbarData>
94+
<div css={styles.scheduleValue} data-testid="schedule-controls">
95+
{isWorkspaceOn(workspace) ? (
96+
<AutoStopDisplay
97+
workspace={workspace}
98+
status={activityStatus}
99+
template={template}
100+
canUpdateSchedule={canUpdateSchedule}
101+
/>
102+
) : (
103+
<WorkspaceScheduleContainer>
104+
<ScheduleSettingsLink>
105+
Starts at {autostartDisplay(workspace.autostart_schedule)}
106+
</ScheduleSettingsLink>
107+
</WorkspaceScheduleContainer>
108+
)}
109+
</div>
64110
);
65111
};
66112

67-
export interface WorkspaceScheduleControlsProps {
113+
interface AutoStopDisplayProps {
68114
workspace: Workspace;
69115
status: WorkspaceActivityStatus;
70116
template: Template;
71117
canUpdateSchedule: boolean;
72118
}
73119

74-
export const WorkspaceScheduleControls: FC<WorkspaceScheduleControlsProps> = ({
120+
const AutoStopDisplay: FC<AutoStopDisplayProps> = ({
75121
workspace,
76122
status,
77123
template,
@@ -128,69 +174,35 @@ export const WorkspaceScheduleControls: FC<WorkspaceScheduleControlsProps> = ({
128174
}, 500);
129175
};
130176

131-
return (
132-
<div css={styles.scheduleValue} data-testid="schedule-controls">
133-
{isWorkspaceOn(workspace) ? (
134-
<AutoStopDisplay
135-
workspace={workspace}
136-
status={status}
137-
template={template}
138-
/>
139-
) : (
140-
<ScheduleSettingsLink>
141-
Starts at {autostartDisplay(workspace.autostart_schedule)}
142-
</ScheduleSettingsLink>
143-
)}
144-
145-
{canUpdateSchedule && canEditDeadline(workspace) && (
146-
<div css={styles.scheduleControls}>
147-
<Tooltip title="Subtract 1 hour from deadline">
148-
<IconButton
149-
disabled={!deadlineMinusEnabled}
150-
size="small"
151-
css={styles.scheduleButton}
152-
onClick={() => {
153-
handleDeadlineChange(deadline.subtract(1, "h"));
154-
}}
155-
>
156-
<RemoveIcon />
157-
<span style={visuallyHidden}>Subtract 1 hour</span>
158-
</IconButton>
159-
</Tooltip>
160-
<Tooltip title="Add 1 hour to deadline">
161-
<IconButton
162-
disabled={!deadlinePlusEnabled}
163-
size="small"
164-
css={styles.scheduleButton}
165-
onClick={() => {
166-
handleDeadlineChange(deadline.add(1, "h"));
167-
}}
168-
>
169-
<AddIcon />
170-
<span style={visuallyHidden}>Add 1 hour</span>
171-
</IconButton>
172-
</Tooltip>
173-
</div>
174-
)}
175-
</div>
176-
);
177-
};
178-
179-
interface AutoStopDisplayProps {
180-
workspace: Workspace;
181-
status: WorkspaceActivityStatus;
182-
template: Template;
183-
}
184-
185-
const AutoStopDisplay: FC<AutoStopDisplayProps> = ({
186-
workspace,
187-
status,
188-
template,
189-
}) => {
190177
const { message, tooltip, danger } = useTime(() =>
191178
autostopDisplay(workspace, status, template),
192179
);
193180

181+
const [showControlsAnyway, setShowControlsAnyway] = useState(false);
182+
let onClickScheduleIcon: (() => void) | undefined;
183+
let activity: ReactNode = null;
184+
185+
if (status === "connected") {
186+
console.log("peepeepoopoo");
187+
onClickScheduleIcon = () => setShowControlsAnyway((it) => !it);
188+
activity = <Pill type="active">Connected</Pill>;
189+
190+
const now = dayjs();
191+
const noRequiredStopSoon =
192+
!workspace.latest_build.max_deadline ||
193+
dayjs(workspace.latest_build.max_deadline).isAfter(now.add(2, "hour"));
194+
195+
// User has shown controls manually, or we should warn about a nearby required stop
196+
if (!showControlsAnyway && noRequiredStopSoon) {
197+
return (
198+
<>
199+
{activity}
200+
<WorkspaceScheduleContainer onClickIcon={onClickScheduleIcon} />
201+
</>
202+
);
203+
}
204+
}
205+
194206
const display = (
195207
<ScheduleSettingsLink
196208
data-testid="schedule-controls-autostop"
@@ -205,11 +217,58 @@ const AutoStopDisplay: FC<AutoStopDisplayProps> = ({
205217
</ScheduleSettingsLink>
206218
);
207219

220+
const controls = canUpdateSchedule && canEditDeadline(workspace) && (
221+
<div css={styles.scheduleControls}>
222+
<Tooltip title="Subtract 1 hour from deadline">
223+
<IconButton
224+
disabled={!deadlineMinusEnabled}
225+
size="small"
226+
css={styles.scheduleButton}
227+
onClick={() => {
228+
handleDeadlineChange(deadline.subtract(1, "h"));
229+
}}
230+
>
231+
<RemoveIcon />
232+
<span style={visuallyHidden}>Subtract 1 hour</span>
233+
</IconButton>
234+
</Tooltip>
235+
<Tooltip title="Add 1 hour to deadline">
236+
<IconButton
237+
disabled={!deadlinePlusEnabled}
238+
size="small"
239+
css={styles.scheduleButton}
240+
onClick={() => {
241+
handleDeadlineChange(deadline.add(1, "h"));
242+
}}
243+
>
244+
<AddIcon />
245+
<span style={visuallyHidden}>Add 1 hour</span>
246+
</IconButton>
247+
</Tooltip>
248+
</div>
249+
);
250+
208251
if (tooltip) {
209-
return <Tooltip title={tooltip}>{display}</Tooltip>;
252+
return (
253+
<>
254+
{activity}
255+
<WorkspaceScheduleContainer onClickIcon={onClickScheduleIcon}>
256+
<Tooltip title={tooltip}>{display}</Tooltip>
257+
{controls}
258+
</WorkspaceScheduleContainer>
259+
</>
260+
);
210261
}
211262

212-
return display;
263+
return (
264+
<>
265+
{activity}
266+
<WorkspaceScheduleContainer onClickIcon={onClickScheduleIcon}>
267+
{display}
268+
{controls}
269+
</WorkspaceScheduleContainer>
270+
</>
271+
);
213272
};
214273

215274
const ScheduleSettingsLink = forwardRef<HTMLAnchorElement, LinkProps>(
@@ -245,16 +304,10 @@ export const canEditDeadline = (workspace: Workspace): boolean => {
245304

246305
export const shouldDisplayScheduleControls = (
247306
workspace: Workspace,
248-
status: WorkspaceActivityStatus,
249307
): boolean => {
250-
const now = dayjs();
251308
const willAutoStop = isWorkspaceOn(workspace) && hasDeadline(workspace);
252309
const willAutoStart = !isWorkspaceOn(workspace) && hasAutoStart(workspace);
253-
const noRequiredStopSoon =
254-
status === "connected" &&
255-
(!workspace.latest_build.max_deadline ||
256-
dayjs(workspace.latest_build.max_deadline).isAfter(now.add(2, "hour")));
257-
return (willAutoStop && !noRequiredStopSoon) || willAutoStart;
310+
return willAutoStop || willAutoStart;
258311
};
259312

260313
const styles = {

0 commit comments

Comments
 (0)