@@ -7,7 +7,7 @@ import ScheduleOutlined from "@mui/icons-material/ScheduleOutlined";
7
7
import Tooltip from "@mui/material/Tooltip" ;
8
8
import { visuallyHidden } from "@mui/utils" ;
9
9
import dayjs , { type Dayjs } from "dayjs" ;
10
- import { forwardRef , type FC , useRef } from "react" ;
10
+ import { forwardRef , type FC , useRef , useState , ReactNode } from "react" ;
11
11
import { useMutation , useQueryClient } from "react-query" ;
12
12
import { Link as RouterLink } from "react-router-dom" ;
13
13
import { useTime } from "hooks/useTime" ;
@@ -28,50 +28,96 @@ import {
28
28
} from "api/queries/workspaces" ;
29
29
import { TopbarData , TopbarIcon } from "components/FullPageLayout/Topbar" ;
30
30
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" ;
32
36
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 {
35
77
workspace : Workspace ;
36
78
template : Template ;
37
- canUpdateWorkspace : boolean ;
79
+ canUpdateSchedule : boolean ;
38
80
}
39
81
40
- export const WorkspaceSchedule : FC < WorkspaceScheduleProps > = ( {
41
- status,
82
+ export const WorkspaceScheduleControls : FC < WorkspaceScheduleControlsProps > = ( {
42
83
workspace,
43
84
template,
44
- canUpdateWorkspace ,
85
+ canUpdateSchedule ,
45
86
} ) => {
46
- if ( ! shouldDisplayScheduleControls ( workspace , status ) ) {
87
+ const activityStatus = useTime ( ( ) => getWorkspaceActivityStatus ( workspace ) ) ;
88
+
89
+ if ( ! shouldDisplayScheduleControls ( workspace ) ) {
47
90
return null ;
48
91
}
49
92
50
93
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 >
64
110
) ;
65
111
} ;
66
112
67
- export interface WorkspaceScheduleControlsProps {
113
+ interface AutoStopDisplayProps {
68
114
workspace : Workspace ;
69
115
status : WorkspaceActivityStatus ;
70
116
template : Template ;
71
117
canUpdateSchedule : boolean ;
72
118
}
73
119
74
- export const WorkspaceScheduleControls : FC < WorkspaceScheduleControlsProps > = ( {
120
+ const AutoStopDisplay : FC < AutoStopDisplayProps > = ( {
75
121
workspace,
76
122
status,
77
123
template,
@@ -128,69 +174,35 @@ export const WorkspaceScheduleControls: FC<WorkspaceScheduleControlsProps> = ({
128
174
} , 500 ) ;
129
175
} ;
130
176
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
- } ) => {
190
177
const { message, tooltip, danger } = useTime ( ( ) =>
191
178
autostopDisplay ( workspace , status , template ) ,
192
179
) ;
193
180
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
+
194
206
const display = (
195
207
< ScheduleSettingsLink
196
208
data-testid = "schedule-controls-autostop"
@@ -205,11 +217,58 @@ const AutoStopDisplay: FC<AutoStopDisplayProps> = ({
205
217
</ ScheduleSettingsLink >
206
218
) ;
207
219
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
+
208
251
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
+ ) ;
210
261
}
211
262
212
- return display ;
263
+ return (
264
+ < >
265
+ { activity }
266
+ < WorkspaceScheduleContainer onClickIcon = { onClickScheduleIcon } >
267
+ { display }
268
+ { controls }
269
+ </ WorkspaceScheduleContainer >
270
+ </ >
271
+ ) ;
213
272
} ;
214
273
215
274
const ScheduleSettingsLink = forwardRef < HTMLAnchorElement , LinkProps > (
@@ -245,16 +304,10 @@ export const canEditDeadline = (workspace: Workspace): boolean => {
245
304
246
305
export const shouldDisplayScheduleControls = (
247
306
workspace : Workspace ,
248
- status : WorkspaceActivityStatus ,
249
307
) : boolean => {
250
- const now = dayjs ( ) ;
251
308
const willAutoStop = isWorkspaceOn ( workspace ) && hasDeadline ( workspace ) ;
252
309
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 ;
258
311
} ;
259
312
260
313
const styles = {
0 commit comments