@@ -22,7 +22,7 @@ const breathingAnimation = `
22
22
}
23
23
` ;
24
24
25
- // Format time difference in a human-readable way
25
+ // Format time difference in a concise way (e.g., "5s", "3m 45s", "2h 15m")
26
26
function formatTimeDifference (
27
27
startDate : string | null ,
28
28
endDate : string | null ,
@@ -36,27 +36,27 @@ function formatTimeDifference(
36
36
const diffSec = Math . floor ( diffMs / 1000 ) ;
37
37
38
38
if ( diffSec < 1 ) {
39
- return 'less than a second ' ;
39
+ return '< 1s ' ;
40
40
}
41
41
42
42
if ( diffSec < 60 ) {
43
- return `${ diffSec } second ${ diffSec !== 1 ? 's' : '' } ` ;
43
+ return `${ diffSec } s ` ;
44
44
}
45
45
46
46
const minutes = Math . floor ( diffSec / 60 ) ;
47
47
const seconds = diffSec % 60 ;
48
48
49
49
if ( minutes < 60 ) {
50
- return ` ${ minutes } minute ${ minutes !== 1 ? 's' : '' } ${ seconds } second ${ seconds !== 1 ? 's' : '' } `;
50
+ return seconds > 0 ? ` ${ minutes } m ${ seconds } s` : ` ${ minutes } m `;
51
51
}
52
52
53
53
const hours = Math . floor ( minutes / 60 ) ;
54
54
const remainingMinutes = minutes % 60 ;
55
55
56
- return ` ${ hours } hour ${ hours !== 1 ? 's' : '' } ${ remainingMinutes } minute ${ remainingMinutes !== 1 ? 's' : '' } `;
56
+ return remainingMinutes > 0 ? ` ${ hours } h ${ remainingMinutes } m` : ` ${ hours } h `;
57
57
}
58
58
59
- // Format relative time (e.g., "3 seconds ago")
59
+ // Format relative time in a concise way (e.g., "3s ago", "5m ago")
60
60
function formatRelativeTime (
61
61
date : string | null ,
62
62
now : Date = new Date ( ) ,
@@ -69,27 +69,27 @@ function formatRelativeTime(
69
69
70
70
// Handle case where time difference is negative (server/client time mismatch)
71
71
if ( diffSec < 1 ) {
72
- return 'just now ' ;
72
+ return '0s ' ;
73
73
}
74
74
75
75
if ( diffSec < 60 ) {
76
- return `${ diffSec } second ${ diffSec !== 1 ? 's' : '' } ago ` ;
76
+ return `${ diffSec } s ` ;
77
77
}
78
78
79
79
const minutes = Math . floor ( diffSec / 60 ) ;
80
80
81
81
if ( minutes < 60 ) {
82
- return `${ minutes } minute ${ minutes !== 1 ? 's' : '' } ago ` ;
82
+ return `${ minutes } m ` ;
83
83
}
84
84
85
85
const hours = Math . floor ( minutes / 60 ) ;
86
86
87
87
if ( hours < 24 ) {
88
- return `${ hours } hour ${ hours !== 1 ? 's' : '' } ago ` ;
88
+ return `${ hours } h ` ;
89
89
}
90
90
91
91
const days = Math . floor ( hours / 24 ) ;
92
- return `${ days } day ${ days !== 1 ? 's' : '' } ago ` ;
92
+ return `${ days } d ` ;
93
93
}
94
94
95
95
interface FlowRunDetailsProps {
@@ -164,14 +164,14 @@ export default function FlowRunDetails({
164
164
{ runData . status === 'started' ? 'running' : runData . status }
165
165
</ span >
166
166
</ div >
167
- < div className = "text-xs text-muted-foreground " >
167
+ < div className = "text-xs" >
168
168
{ runData . status === 'started' && runData . started_at && (
169
- < span >
169
+ < span className = "text-yellow-600/80" >
170
170
Running for { formatTimeDifference ( runData . started_at , null ) }
171
171
</ span >
172
172
) }
173
173
{ runData . status === 'completed' && runData . completed_at && (
174
- < span >
174
+ < span className = "text-green-600/80" >
175
175
Took{ ' ' }
176
176
{ formatTimeDifference (
177
177
runData . started_at ,
@@ -180,8 +180,8 @@ export default function FlowRunDetails({
180
180
</ span >
181
181
) }
182
182
{ runData . status === 'failed' && runData . failed_at && (
183
- < span >
184
- Failed after
183
+ < span className = "text-red-600/80" >
184
+ Failed after{ ' ' }
185
185
{ formatTimeDifference ( runData . started_at , runData . failed_at ) }
186
186
</ span >
187
187
) }
@@ -230,8 +230,10 @@ export default function FlowRunDetails({
230
230
// Find the corresponding step tasks for this step
231
231
const stepTasks = runData . step_tasks
232
232
?. filter ( ( task ) => task . step_slug === step . step_slug )
233
- . sort ( ( a , b ) => ( a . step_index || 0 ) - ( b . step_index || 0 ) ) ;
234
-
233
+ . sort (
234
+ ( a , b ) => ( a . step_index || 0 ) - ( b . step_index || 0 ) ,
235
+ ) ;
236
+
235
237
// Get the completed task with output
236
238
const stepTask = stepTasks ?. find (
237
239
( task ) => task . status === 'completed' ,
@@ -240,33 +242,37 @@ export default function FlowRunDetails({
240
242
return (
241
243
< Collapsible
242
244
key = { index }
243
- className = { `mb-1 rounded-lg border ${
244
- ( ( ) => {
245
- // Get the pre-sorted step tasks from above
246
- const latestTask = stepTasks && stepTasks . length > 0
247
- ? stepTasks . sort ( ( a , b ) =>
248
- ( b . attempts_count || 0 ) - ( a . attempts_count || 0 )
249
- ) [ 0 ]
245
+ className = { `mb-1 rounded-lg border ${ ( ( ) => {
246
+ // Get the pre-sorted step tasks from above
247
+ const latestTask =
248
+ stepTasks && stepTasks . length > 0
249
+ ? stepTasks . sort (
250
+ ( a , b ) =>
251
+ ( b . attempts_count || 0 ) -
252
+ ( a . attempts_count || 0 ) ,
253
+ ) [ 0 ]
250
254
: null ;
251
-
252
- // Check if this is a retry (attempts_count > 1)
253
- const isRetrying = latestTask && latestTask . attempts_count > 1 && step . status === 'started' ;
254
-
255
- if ( step . status === 'completed' ) {
256
- return 'bg-green-500/5 border-green-500/30' ;
257
- } else if ( isRetrying ) {
258
- return 'bg-red-500/5 border-red-500/30 animate-pulse' ;
259
- } else if ( step . status === 'started' ) {
260
- return 'bg-yellow-500/5 border-yellow-500/30' ;
261
- } else if ( step . status === 'failed' ) {
262
- return 'bg-red-500/5 border-red-500/30' ;
263
- } else if ( step . status === 'created' ) {
264
- return 'bg-blue-500/5 border-blue-500/30' ;
265
- } else {
266
- return 'bg-gray-500/5 border-gray-500/30' ;
267
- }
268
- } ) ( )
269
- } `}
255
+
256
+ // Check if this is a retry (attempts_count > 1)
257
+ const isRetrying =
258
+ latestTask &&
259
+ latestTask . attempts_count > 1 &&
260
+ step . status === 'started' ;
261
+
262
+ if ( step . status === 'completed' ) {
263
+ return 'bg-green-500/5 border-green-500/30' ;
264
+ } else if ( isRetrying ) {
265
+ return 'bg-red-500/5 border-red-500/30 animate-pulse' ;
266
+ } else if ( step . status === 'started' ) {
267
+ return 'bg-yellow-500/5 border-yellow-500/30' ;
268
+ } else if ( step . status === 'failed' ) {
269
+ return 'bg-red-500/5 border-red-500/30' ;
270
+ } else if ( step . status === 'created' ) {
271
+ return 'bg-blue-500/5 border-blue-500/30' ;
272
+ } else {
273
+ return 'bg-gray-500/5 border-gray-500/30' ;
274
+ }
275
+ } ) ( ) } `}
270
276
>
271
277
< CollapsibleTrigger className = "flex items-center justify-between w-full p-2 text-left" >
272
278
< div >
@@ -276,7 +282,7 @@ export default function FlowRunDetails({
276
282
</ div >
277
283
< div className = "flex items-center" >
278
284
{ step . status === 'started' && step . started_at && (
279
- < span className = "text-xs text-muted-foreground mr-2" >
285
+ < span className = "text-xs text-yellow-600/80 mr-2" >
280
286
{ formatRelativeTime (
281
287
step . started_at ,
282
288
currentTime ,
@@ -286,7 +292,7 @@ export default function FlowRunDetails({
286
292
{ step . status === 'completed' &&
287
293
step . started_at &&
288
294
step . completed_at && (
289
- < span className = "text-xs text-muted-foreground mr-2" >
295
+ < span className = "text-xs text-green-600/80 mr-2" >
290
296
{ formatTimeDifference (
291
297
step . started_at ,
292
298
step . completed_at ,
@@ -296,7 +302,7 @@ export default function FlowRunDetails({
296
302
{ step . status === 'failed' &&
297
303
step . started_at &&
298
304
step . failed_at && (
299
- < span className = "text-xs text-muted-foreground mr-2" >
305
+ < span className = "text-xs text-red-600/80 mr-2" >
300
306
Failed after{ ' ' }
301
307
{ formatTimeDifference (
302
308
step . started_at ,
@@ -306,15 +312,21 @@ export default function FlowRunDetails({
306
312
) }
307
313
{ ( ( ) => {
308
314
// Use the pre-sorted step tasks from above
309
- const latestTask = stepTasks && stepTasks . length > 0
310
- ? stepTasks . sort ( ( a , b ) =>
311
- ( b . attempts_count || 0 ) - ( a . attempts_count || 0 )
312
- ) [ 0 ]
313
- : null ;
314
-
315
+ const latestTask =
316
+ stepTasks && stepTasks . length > 0
317
+ ? stepTasks . sort (
318
+ ( a , b ) =>
319
+ ( b . attempts_count || 0 ) -
320
+ ( a . attempts_count || 0 ) ,
321
+ ) [ 0 ]
322
+ : null ;
323
+
315
324
// Check if this is a retry (attempts_count > 1)
316
- const isRetrying = latestTask && latestTask . attempts_count > 1 && step . status === 'started' ;
317
-
325
+ const isRetrying =
326
+ latestTask &&
327
+ latestTask . attempts_count > 1 &&
328
+ step . status === 'started' ;
329
+
318
330
return (
319
331
< span
320
332
className = { `inline-block w-2 h-2 rounded-full mr-1 ${
@@ -336,15 +348,21 @@ export default function FlowRunDetails({
336
348
< span className = "capitalize text-xs" >
337
349
{ ( ( ) => {
338
350
// Use the pre-sorted step tasks from above
339
- const latestTask = stepTasks && stepTasks . length > 0
340
- ? stepTasks . sort ( ( a , b ) =>
341
- ( b . attempts_count || 0 ) - ( a . attempts_count || 0 )
342
- ) [ 0 ]
343
- : null ;
344
-
351
+ const latestTask =
352
+ stepTasks && stepTasks . length > 0
353
+ ? stepTasks . sort (
354
+ ( a , b ) =>
355
+ ( b . attempts_count || 0 ) -
356
+ ( a . attempts_count || 0 ) ,
357
+ ) [ 0 ]
358
+ : null ;
359
+
345
360
// Check if this is a retry (attempts_count > 1)
346
- const isRetrying = latestTask && latestTask . attempts_count > 1 && step . status === 'started' ;
347
-
361
+ const isRetrying =
362
+ latestTask &&
363
+ latestTask . attempts_count > 1 &&
364
+ step . status === 'started' ;
365
+
348
366
if ( isRetrying ) {
349
367
return `retrying (retry ${ latestTask . attempts_count - 1 } )` ;
350
368
} else if ( step . status === 'created' ) {
0 commit comments