11import { CreateWorkflowRequest , WorkflowType } from "@dafthunk/types" ;
2- import { formatDistanceToNow } from "date-fns" ;
3- import { AlertCircle , Clock , Logs , Plus , Target , Workflow } from "lucide-react" ;
2+ import { format } from "date-fns" ;
3+ import { AlertCircle , Clock , Logs , Plus , Target , Workflow , MoreHorizontal , Eye } from "lucide-react" ;
44import { useState } from "react" ;
55import { useNavigate } from "react-router" ;
66import { Link } from "react-router" ;
@@ -14,9 +14,24 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
1414import { DataTableCard } from "@/components/ui/data-table-card" ;
1515import { CreateWorkflowDialog } from "@/components/workflow/create-workflow-dialog" ;
1616import type { WorkflowExecutionStatus } from "@/components/workflow/workflow-types" ;
17+ import {
18+ DropdownMenu ,
19+ DropdownMenuContent ,
20+ DropdownMenuItem ,
21+ DropdownMenuTrigger ,
22+ } from "@/components/ui/dropdown-menu" ;
1723import { useDashboard } from "@/services/dashboard-service" ;
1824import { createWorkflow } from "@/services/workflow-service" ;
1925
26+ // Define a type for recent execution items, mirroring DashboardStats.recentExecutions
27+ interface RecentExecutionItem {
28+ id : string ;
29+ workflowName : string ;
30+ status : string ;
31+ startedAt : number ;
32+ endedAt ?: number ;
33+ }
34+
2035export function DashboardPage ( ) {
2136 const [ isCreateDialogOpen , setIsCreateDialogOpen ] = useState ( false ) ;
2237 const navigate = useNavigate ( ) ;
@@ -175,26 +190,97 @@ export function DashboardPage() {
175190 } ,
176191 {
177192 accessorKey : "status" ,
178- header : "Status" ,
193+ header : "Execution Status" ,
179194 cell : ( { row } ) => {
180- const badgeStatus = row . original
181- . status as WorkflowExecutionStatus ;
195+ const execution = row . original as RecentExecutionItem ;
196+ const badgeStatus = execution . status as WorkflowExecutionStatus ;
182197 return < ExecutionStatusBadge status = { badgeStatus } /> ;
183198 } ,
184199 } ,
185200 {
186201 accessorKey : "startedAt" ,
187- header : "Started" ,
188- cell : ( { row } ) => (
189- < span className = "text-right text-xs text-muted-foreground" >
190- { formatDistanceToNow ( row . original . startedAt , {
191- addSuffix : true ,
192- } ) }
193- </ span >
194- ) ,
202+ header : "Started At" ,
203+ cell : ( { row } ) => {
204+ const execution = row . original as RecentExecutionItem ;
205+ return (
206+ < span className = "text-xs text-muted-foreground" >
207+ { execution . startedAt ? format ( new Date ( execution . startedAt ) , "PPpp" ) : "-" }
208+ </ span >
209+ ) ;
210+ } ,
211+ } ,
212+ {
213+ accessorKey : "endedAt" ,
214+ header : "Ended At" ,
215+ cell : ( { row } ) => {
216+ const execution = row . original as RecentExecutionItem ;
217+ const formatted = execution . endedAt
218+ ? format ( new Date ( execution . endedAt ) , "PPpp" )
219+ : "-" ;
220+ return (
221+ < span className = "text-xs text-muted-foreground" >
222+ { formatted }
223+ </ span >
224+ ) ;
225+ } ,
226+ } ,
227+ {
228+ accessorKey : "duration" ,
229+ header : "Duration" ,
230+ cell : ( { row } ) => {
231+ const execution = row . original as RecentExecutionItem ;
232+ const { startedAt, endedAt } = execution ;
233+
234+ if ( startedAt && endedAt ) {
235+ const durationMs =
236+ new Date ( endedAt ) . getTime ( ) - new Date ( startedAt ) . getTime ( ) ;
237+ const seconds = Math . floor ( ( durationMs / 1000 ) % 60 ) ;
238+ const minutes = Math . floor ( ( durationMs / ( 1000 * 60 ) ) % 60 ) ;
239+
240+ let formattedDuration = "" ;
241+ if ( minutes > 0 ) {
242+ formattedDuration += `${ minutes } m ` ;
243+ }
244+ formattedDuration += `${ seconds } s` ;
245+ if ( formattedDuration . trim ( ) === "0s" && durationMs > 0 && durationMs < 1000 ) {
246+ formattedDuration = "<1s" ;
247+ } else if ( formattedDuration . trim ( ) === "0s" && durationMs === 0 ) {
248+ formattedDuration = "0s" ;
249+ }
250+
251+
252+ return < div > { formattedDuration . trim ( ) } </ div > ;
253+ }
254+ return < div > -</ div > ;
255+ } ,
256+ } ,
257+ {
258+ id : "actions" ,
259+ cell : ( { row } ) => {
260+ const execution = row . original as RecentExecutionItem ;
261+ return (
262+ < div className = "text-right" >
263+ < DropdownMenu >
264+ < DropdownMenuTrigger asChild >
265+ < Button variant = "ghost" className = "h-8 w-8 p-0" >
266+ < span className = "sr-only" > Open menu</ span >
267+ < MoreHorizontal className = "h-4 w-4" />
268+ </ Button >
269+ </ DropdownMenuTrigger >
270+ < DropdownMenuContent align = "end" >
271+ < DropdownMenuItem asChild >
272+ < Link to = { `/workflows/executions/${ execution . id } ` } >
273+ View
274+ </ Link >
275+ </ DropdownMenuItem >
276+ </ DropdownMenuContent >
277+ </ DropdownMenu >
278+ </ div >
279+ ) ;
280+ } ,
195281 } ,
196282 ] }
197- data = { dashboardStats . recentExecutions }
283+ data = { dashboardStats . recentExecutions as RecentExecutionItem [ ] }
198284 emptyState = { {
199285 title : "No executions" ,
200286 description : "There are no recent executions to display." ,
0 commit comments