|
|
@@ -1,9 +1,10 @@
|
|
|
-import { useState } from 'react'
|
|
|
+import { useState, Fragment } from 'react'
|
|
|
+import { useNavigate } from 'react-router-dom'
|
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
import { executionsApi } from '../api/workflows'
|
|
|
import { useExecutionListUpdates } from '../hooks/useExecutionListUpdates'
|
|
|
import { formatDistanceToNow } from 'date-fns'
|
|
|
-import { CheckCircle, XCircle, Clock, Ban, RefreshCw, ChevronLeft, ChevronRight } from 'lucide-react'
|
|
|
+import { CheckCircle, XCircle, Clock, Ban, RefreshCw, ChevronLeft, ChevronRight, AlertCircle } from 'lucide-react'
|
|
|
import clsx from 'clsx'
|
|
|
|
|
|
// Helper to safely format timestamps that might be invalid or too large
|
|
|
@@ -21,8 +22,19 @@ function safeFormatDistanceToNow(timestamp: number): string {
|
|
|
const PAGE_SIZE = 20
|
|
|
|
|
|
export default function ExecutionsPage() {
|
|
|
+ const navigate = useNavigate()
|
|
|
const [page, setPage] = useState(1)
|
|
|
|
|
|
+ // The workflow editor already renders an execution across the canvas with each
|
|
|
+ // node's input and output, so open it there rather than building a second
|
|
|
+ // viewer that would show less.
|
|
|
+ const openExecution = (execution: { id: string; workflowId: string }) => {
|
|
|
+ if (!execution.workflowId) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ navigate(`/workflows/${execution.workflowId}?execution=${encodeURIComponent(execution.id)}`)
|
|
|
+ }
|
|
|
+
|
|
|
const { data, isLoading, refetch } = useQuery({
|
|
|
queryKey: ['executions', page],
|
|
|
queryFn: () => executionsApi.list(undefined, undefined, page, PAGE_SIZE),
|
|
|
@@ -112,7 +124,12 @@ export default function ExecutionsPage() {
|
|
|
</thead>
|
|
|
<tbody className="divide-y divide-gray-200 dark:divide-slate-700">
|
|
|
{executions.map((execution: any) => (
|
|
|
- <tr key={execution.id} className="hover:bg-gray-50 dark:hover:bg-slate-700/50">
|
|
|
+ <Fragment key={execution.id}>
|
|
|
+ <tr
|
|
|
+ onClick={() => openExecution(execution)}
|
|
|
+ title="Open this execution in the workflow editor"
|
|
|
+ className="hover:bg-gray-50 dark:hover:bg-slate-700/50 cursor-pointer"
|
|
|
+ >
|
|
|
<td className="px-4 py-3">
|
|
|
<div className="flex items-center gap-2">
|
|
|
{getStatusIcon(execution.status)}
|
|
|
@@ -145,6 +162,20 @@ export default function ExecutionsPage() {
|
|
|
: '-'}
|
|
|
</td>
|
|
|
</tr>
|
|
|
+ {execution.error && (
|
|
|
+ <tr
|
|
|
+ onClick={() => openExecution(execution)}
|
|
|
+ className="cursor-pointer hover:bg-gray-50 dark:hover:bg-slate-700/50"
|
|
|
+ >
|
|
|
+ <td colSpan={5} className="px-4 pb-3 pt-0">
|
|
|
+ <div className="flex items-start gap-1.5 text-red-600 dark:text-red-400 text-xs">
|
|
|
+ <AlertCircle className="w-3.5 h-3.5 shrink-0 mt-0.5" />
|
|
|
+ <span className="break-all">{execution.error}</span>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ )}
|
|
|
+ </Fragment>
|
|
|
))}
|
|
|
</tbody>
|
|
|
</table>
|