Skip to content

Commit 81502c2

Browse files
authored
fix: make sure Coder plugin indicates when a workspace is being deleted (#82)
* fix: display when a workspace is being deleted * refactor: clean up code for clarity * fix: expose statuses more directly in markup * fix: add pending and failed states to output * fix: add more granularity to status logic
1 parent f2f0689 commit 81502c2

File tree

1 file changed

+75
-10
lines changed

1 file changed

+75
-10
lines changed

plugins/backstage-plugin-coder/src/components/CoderWorkspacesCard/WorkspacesListItem.tsx

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { type Theme, makeStyles } from '@material-ui/core';
99
import { useId } from '../../hooks/hookPolyfills';
1010

1111
import { useCoderAppConfig } from '../CoderProvider';
12-
import { isWorkspaceOnline } from '../../api';
12+
import { getWorkspaceAgentStatuses } from '../../api';
1313

14-
import type { Workspace } from '../../typesConstants';
14+
import type { Workspace, WorkspaceStatus } from '../../typesConstants';
1515
import { WorkspacesListIcon } from './WorkspacesListIcon';
1616
import { VisuallyHidden } from '../VisuallyHidden';
1717

@@ -24,7 +24,7 @@ type StyleKey =
2424
| 'button';
2525

2626
type UseStyleInputs = Readonly<{
27-
isOnline: boolean;
27+
isAvailable: boolean;
2828
}>;
2929

3030
const useStyles = makeStyles<Theme, UseStyleInputs, StyleKey>(theme => ({
@@ -84,7 +84,7 @@ const useStyles = makeStyles<Theme, UseStyleInputs, StyleKey>(theme => ({
8484
fontSize: '16px',
8585
},
8686

87-
onlineStatusLight: ({ isOnline }) => ({
87+
onlineStatusLight: ({ isAvailable }) => ({
8888
display: 'block',
8989
width: theme.spacing(1),
9090
height: theme.spacing(1),
@@ -93,8 +93,10 @@ const useStyles = makeStyles<Theme, UseStyleInputs, StyleKey>(theme => ({
9393
borderStyle: 'solid',
9494

9595
// Border color helps increase color contrast in light mode
96-
borderColor: isOnline ? 'hsl(130deg,100%,40%)' : theme.palette.common.black,
97-
backgroundColor: isOnline
96+
borderColor: isAvailable
97+
? 'hsl(130deg,100%,40%)'
98+
: theme.palette.common.black,
99+
backgroundColor: isAvailable
98100
? 'hsl(135deg,100%,77%)'
99101
: theme.palette.common.black,
100102
}),
@@ -142,8 +144,11 @@ export const WorkspacesListItem = ({
142144
const { accessUrl } = useCoderAppConfig().deployment;
143145
const anchorElementRef = useRef<HTMLAnchorElement>(null);
144146

145-
const isOnline = isWorkspaceOnline(workspace);
146-
const styles = useStyles({ isOnline });
147+
const availabilityStatus = getAvailabilityStatus(workspace);
148+
const styles = useStyles({
149+
isAvailable:
150+
availabilityStatus === 'online' || availabilityStatus === 'pending',
151+
});
147152

148153
const { name, owner_name, template_icon } = workspace;
149154
const onlineStatusId = `${hookId}-online-status`;
@@ -205,8 +210,15 @@ export const WorkspacesListItem = ({
205210
/>
206211

207212
<VisuallyHidden>Workspace is </VisuallyHidden>
208-
{isOnline ? 'Online' : 'Offline'}
209-
<VisuallyHidden>.</VisuallyHidden>
213+
{availabilityStatus === 'deleting' ||
214+
availabilityStatus === 'pending' ? (
215+
<>{toUppercase(availabilityStatus)}&hellip;</>
216+
) : (
217+
<>
218+
{toUppercase(availabilityStatus)}
219+
<VisuallyHidden>.</VisuallyHidden>
220+
</>
221+
)}
210222
</span>
211223
</div>
212224

@@ -226,6 +238,55 @@ export const WorkspacesListItem = ({
226238
);
227239
};
228240

241+
const deletingStatuses: readonly WorkspaceStatus[] = ['deleting', 'deleted'];
242+
const offlineStatuses: readonly WorkspaceStatus[] = [
243+
'stopped',
244+
'stopping',
245+
'pending',
246+
'canceling',
247+
'canceled',
248+
];
249+
250+
type AvailabilityStatus =
251+
| 'online'
252+
| 'offline'
253+
| 'pending'
254+
| 'failed'
255+
| 'deleting';
256+
257+
function getAvailabilityStatus(workspace: Workspace): AvailabilityStatus {
258+
const currentStatus = workspace.latest_build.status;
259+
260+
if (currentStatus === 'failed') {
261+
return 'failed';
262+
}
263+
264+
// When a workspace is being deleted, there is a good chance that the agents
265+
// will still show as connected/connecting. If this check isn't done before
266+
// looking at the agent statuses, a deleting workspace might show up as online
267+
if (deletingStatuses.includes(currentStatus)) {
268+
return 'deleting';
269+
}
270+
271+
if (offlineStatuses.includes(currentStatus)) {
272+
return 'offline';
273+
}
274+
275+
const uniqueStatuses = getWorkspaceAgentStatuses(workspace);
276+
const isPending =
277+
currentStatus === 'starting' ||
278+
uniqueStatuses.some(status => status === 'connecting');
279+
280+
if (isPending) {
281+
return 'pending';
282+
}
283+
284+
// .every will still make workspaces with no agents show as online
285+
return uniqueStatuses.every(status => status === 'connected')
286+
? 'online'
287+
: 'offline';
288+
}
289+
229290
function stopClickEventBubbling(event: MouseEvent | KeyboardEvent): void {
230291
const { nativeEvent } = event;
231292
const shouldStopBubbling =
@@ -236,3 +297,7 @@ function stopClickEventBubbling(event: MouseEvent | KeyboardEvent): void {
236297
event.stopPropagation();
237298
}
238299
}
300+
301+
function toUppercase(s: string): string {
302+
return s.slice(0, 1).toUpperCase() + s.slice(1).toLowerCase();
303+
}

0 commit comments

Comments
 (0)