瀏覽代碼

fix(webui): keep an execution's error inside its own row

The error was rendered as a separate table row, and tbody's divide-y drew a
border above it, so a failed execution read as two rows: one with no error and
one with no execution. Overriding the divider would have meant fighting the
specificity of .divide-y > * + *.

The error belongs to exactly one execution, so it now sits inside that row,
beneath the workflow name that identifies it. Cells are top-aligned so a
two-line row stays readable, and the message wraps rather than stretching the
column.
fszontagh 1 月之前
父節點
當前提交
9f440bd866
共有 1 個文件被更改,包括 19 次插入 和 22 次删除
  1. 19 22
      webui/src/pages/ExecutionsPage.tsx

+ 19 - 22
webui/src/pages/ExecutionsPage.tsx

@@ -1,4 +1,4 @@
-import { useState, Fragment } from 'react'
+import { useState } from 'react'
 import { useNavigate } from 'react-router-dom'
 import { useQuery } from '@tanstack/react-query'
 import { executionsApi } from '../api/workflows'
@@ -124,13 +124,13 @@ export default function ExecutionsPage() {
               </thead>
               <tbody className="divide-y divide-gray-200 dark:divide-slate-700">
                 {executions.map((execution: any) => (
-                  <Fragment key={execution.id}>
                   <tr
+                    key={execution.id}
                     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">
+                    <td className="px-4 py-3 align-top">
                       <div className="flex items-center gap-2">
                         {getStatusIcon(execution.status)}
                         <span
@@ -143,18 +143,29 @@ export default function ExecutionsPage() {
                         </span>
                       </div>
                     </td>
-                    <td className="px-4 py-3">
-                      <span className="font-medium text-gray-900 dark:text-gray-100">{execution.workflowName || execution.workflowId}</span>
+                    <td className="px-4 py-3 align-top">
+                      <span className="font-medium text-gray-900 dark:text-gray-100">
+                        {execution.workflowName || execution.workflowId}
+                      </span>
+                      {execution.error && (
+                        <div
+                          className="mt-1 flex items-start gap-1.5 text-red-600 dark:text-red-400 text-xs max-w-xl"
+                          title={execution.error}
+                        >
+                          <AlertCircle className="w-3.5 h-3.5 shrink-0 mt-0.5" />
+                          <span className="break-words">{execution.error}</span>
+                        </div>
+                      )}
                     </td>
-                    <td className="px-4 py-3 text-gray-600 dark:text-gray-400">
+                    <td className="px-4 py-3 align-top text-gray-600 dark:text-gray-400">
                       {execution.triggerType}
                     </td>
-                    <td className="px-4 py-3 text-gray-500 dark:text-gray-400 text-sm">
+                    <td className="px-4 py-3 align-top text-gray-500 dark:text-gray-400 text-sm">
                       {execution.startedAt
                         ? safeFormatDistanceToNow(execution.startedAt)
                         : '-'}
                     </td>
-                    <td className="px-4 py-3 text-gray-500 dark:text-gray-400 text-sm">
+                    <td className="px-4 py-3 align-top text-gray-500 dark:text-gray-400 text-sm">
                       {execution.finishedAt && execution.startedAt
                         ? `${((execution.finishedAt - execution.startedAt) / 1000).toFixed(2)}s`
                         : execution.status === 'running'
@@ -162,20 +173,6 @@ 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>