@@ -13,6 +13,11 @@ import { AvatarData } from "components/Avatar/AvatarData";
13
13
import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton" ;
14
14
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip" ;
15
15
import { Stack } from "components/Stack/Stack" ;
16
+ import {
17
+ StatusIndicator ,
18
+ StatusIndicatorDot ,
19
+ type StatusIndicatorProps ,
20
+ } from "components/StatusIndicator/StatusIndicator" ;
16
21
import {
17
22
Table ,
18
23
TableBody ,
@@ -25,19 +30,26 @@ import {
25
30
TableLoaderSkeleton ,
26
31
TableRowSkeleton ,
27
32
} from "components/TableLoader/TableLoader" ;
33
+ import dayjs from "dayjs" ;
34
+ import relativeTime from "dayjs/plugin/relativeTime" ;
28
35
import { useClickableTableRow } from "hooks/useClickableTableRow" ;
29
36
import { useDashboard } from "modules/dashboard/useDashboard" ;
30
37
import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus" ;
31
38
import { WorkspaceDormantBadge } from "modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge" ;
32
39
import { WorkspaceOutdatedTooltip } from "modules/workspaces/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip" ;
33
- import { WorkspaceStatusBadge } from "modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge" ;
34
- import { LastUsed } from "pages/WorkspacesPage/LastUsed" ;
35
40
import { type FC , type ReactNode , useMemo } from "react" ;
36
41
import { useNavigate } from "react-router-dom" ;
37
42
import { cn } from "utils/cn" ;
38
- import { getDisplayWorkspaceTemplateName } from "utils/workspace" ;
43
+ import {
44
+ type DisplayWorkspaceStatusType ,
45
+ getDisplayWorkspaceStatus ,
46
+ getDisplayWorkspaceTemplateName ,
47
+ lastUsedMessage ,
48
+ } from "utils/workspace" ;
39
49
import { WorkspacesEmpty } from "./WorkspacesEmpty" ;
40
50
51
+ dayjs . extend ( relativeTime ) ;
52
+
41
53
export interface WorkspacesTableProps {
42
54
workspaces ?: readonly Workspace [ ] ;
43
55
checkedWorkspaces : readonly Workspace [ ] ;
@@ -125,8 +137,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
125
137
</ TableHead >
126
138
{ hasAppStatus && < TableHead className = "w-2/6" > Activity</ TableHead > }
127
139
< TableHead className = "w-2/6" > Template</ TableHead >
128
- < TableHead className = "w-1/6" > Last used</ TableHead >
129
- < TableHead className = "w-1/6" > Status</ TableHead >
140
+ < TableHead className = "w-2/6" > Status</ TableHead >
130
141
< TableHead className = "w-0" />
131
142
</ TableRow >
132
143
</ TableHeader >
@@ -248,26 +259,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
248
259
/>
249
260
</ TableCell >
250
261
251
- < TableCell >
252
- < LastUsed lastUsedAt = { workspace . last_used_at } />
253
- </ TableCell >
254
-
255
- < TableCell >
256
- < div className = "flex items-center gap-2" >
257
- < WorkspaceStatusBadge workspace = { workspace } />
258
- { workspace . latest_build . status === "running" &&
259
- ! workspace . health . healthy && (
260
- < InfoTooltip
261
- type = "warning"
262
- title = "Workspace is unhealthy"
263
- message = "Your workspace is running but some agents are unhealthy."
264
- />
265
- ) }
266
- { workspace . dormant_at && (
267
- < WorkspaceDormantBadge workspace = { workspace } />
268
- ) }
269
- </ div >
270
- </ TableCell >
262
+ < WorkspaceStatusCell workspace = { workspace } />
271
263
272
264
< TableCell >
273
265
< div className = "flex pl-4" >
@@ -345,14 +337,11 @@ const TableLoader: FC<TableLoaderProps> = ({ canCheckWorkspaces }) => {
345
337
< TableCell className = "w-2/6" >
346
338
< AvatarDataSkeleton />
347
339
</ TableCell >
348
- < TableCell className = "w-1/6" >
349
- < Skeleton variant = "text" width = "75%" />
350
- </ TableCell >
351
- < TableCell className = "w-1/6" >
352
- < Skeleton variant = "text" width = "75%" />
340
+ < TableCell className = "w-2/6" >
341
+ < Skeleton variant = "text" width = "50%" />
353
342
</ TableCell >
354
343
< TableCell className = "w-0" >
355
- < Skeleton variant = "text" width = "75 %" />
344
+ < Skeleton variant = "text" width = "25 %" />
356
345
</ TableCell >
357
346
</ TableRowSkeleton >
358
347
</ TableLoaderSkeleton >
@@ -362,3 +351,51 @@ const TableLoader: FC<TableLoaderProps> = ({ canCheckWorkspaces }) => {
362
351
const cantBeChecked = ( workspace : Workspace ) => {
363
352
return [ "deleting" , "pending" ] . includes ( workspace . latest_build . status ) ;
364
353
} ;
354
+
355
+ type WorkspaceStatusCellProps = {
356
+ workspace : Workspace ;
357
+ } ;
358
+
359
+ const variantByStatusType : Record <
360
+ DisplayWorkspaceStatusType ,
361
+ StatusIndicatorProps [ "variant" ]
362
+ > = {
363
+ active : "pending" ,
364
2851
td>+ inactive : "inactive" ,
365
+ success : "success" ,
366
+ error : "failed" ,
367
+ danger : "warning" ,
368
+ warning : "warning" ,
369
+ } ;
370
+
371
+ const WorkspaceStatusCell : FC < WorkspaceStatusCellProps > = ( { workspace } ) => {
372
+ const { text, type } = getDisplayWorkspaceStatus (
373
+ workspace . latest_build . status ,
374
+ workspace . latest_build . job ,
375
+ ) ;
376
+
377
+ return (
378
+ < TableCell >
379
+ < div className = "flex flex-col" >
380
+ < StatusIndicator variant = { variantByStatusType [ type ] } >
381
+ < StatusIndicatorDot />
382
+ { text }
383
+ { workspace . latest_build . status === "running" &&
384
+ ! workspace . health . healthy && (
385
+ < InfoTooltip
386
+ type = "warning"
387
+ title = "Workspace is unhealthy"
388
+ message = "Your workspace is running but some agents are unhealthy."
389
+ />
390
+ ) }
391
+ { workspace . dormant_at && (
392
+ < WorkspaceDormantBadge workspace = { workspace } />
393
+ ) }
394
+ </ StatusIndicator >
395
+ < span className = "text-xs font-medium text-content-secondary ml-6" >
396
+ { lastUsedMessage ( workspace . last_used_at ) }
397
+ </ span >
398
+ </ div >
399
+ </ TableCell >
400
+ ) ;
401
+ } ;
0 commit comments