- )}
- {folderIds.length > 0 && (
-
- )}
- {triggers.length > 0 && (
-
- )}
+ {/* Top section pinned */}
+
+ {/* Time Range Display */}
+
+
+
+ {getDateRange()}
+
+ {(workflowIds.length > 0 || folderIds.length > 0 || triggers.length > 0) && (
+
+ Filters:
+ {workflowIds.length > 0 && (
+
+ {workflowIds.length} workflow{workflowIds.length !== 1 ? 's' : ''}
+
+ )}
+ {folderIds.length > 0 && (
+
+ {folderIds.length} folder{folderIds.length !== 1 ? 's' : ''}
+
+ )}
+ {triggers.length > 0 && (
+
+ {triggers.length} trigger{triggers.length !== 1 ? 's' : ''}
+
+ )}
+
+ )}
+
+
+ {/* Time Controls */}
+
- {/* Time Controls */}
-
-
-
-
+ {/* KPIs */}
+
+
+
+
- {/* KPIs */}
-
-
-
-
- {/* Details section below the entire bars component - always visible */}
- {(() => {
- if (expandedWorkflowId) {
- const wf = executions.find((w) => w.workflowId === expandedWorkflowId)
- if (!wf) return null
- const total = wf.segments.reduce((s, x) => s + (x.totalExecutions || 0), 0)
- const success = wf.segments.reduce((s, x) => s + (x.successfulExecutions || 0), 0)
- const failures = Math.max(total - success, 0)
- const rate = total > 0 ? (success / total) * 100 : 100
-
- // Prepare filtered logs for details
- const details = workflowDetails[expandedWorkflowId]
- let logsToDisplay = details?.logs || []
- if (details && selectedSegmentIndices.length > 0) {
- const totalMs = endTime.getTime() - getStartTime().getTime()
- const segMs = totalMs / BAR_COUNT
-
- const windows = selectedSegmentIndices
- .map((idx) => wf.segments[idx])
- .filter(Boolean)
- .map((s) => {
- const start = new Date(s.timestamp).getTime()
- const end = start + segMs
- return { start, end }
- })
-
- const inAnyWindow = (t: number) =>
- windows.some((w) => t >= w.start && t < w.end)
-
- logsToDisplay = details.allLogs
- .filter((log) => inAnyWindow(new Date(log.startedAt).getTime()))
- .map((log) => ({
- // Ensure workflow name is visible in the table for multi-select
- ...log,
- workflowName: (log as any).workflowName || wf.workflowName,
- }))
-
- const minStart = new Date(Math.min(...windows.map((w) => w.start)))
- const maxEnd = new Date(Math.max(...windows.map((w) => w.end)))
-
- let filteredErrorRates = (details.errorRates || []).filter((p: any) =>
- inAnyWindow(new Date(p.timestamp).getTime())
- )
- let filteredDurations = (
- Array.isArray((details as any).durations) ? (details as any).durations : []
- ).filter((p: any) => inAnyWindow(new Date(p.timestamp).getTime()))
- let filteredExecutionCounts = (details.executionCounts || []).filter((p: any) =>
- inAnyWindow(new Date(p.timestamp).getTime())
+ {/* Details section in its own scroll area */}
+
+ {(() => {
+ if (expandedWorkflowId) {
+ const wf = executions.find((w) => w.workflowId === expandedWorkflowId)
+ if (!wf) return null
+ const total = wf.segments.reduce((s, x) => s + (x.totalExecutions || 0), 0)
+ const success = wf.segments.reduce(
+ (s, x) => s + (x.successfulExecutions || 0),
+ 0
)
+ const failures = Math.max(total - success, 0)
+ const rate = total > 0 ? (success / total) * 100 : 100
- if (filteredErrorRates.length === 0 || filteredExecutionCounts.length === 0) {
- const series = buildSeriesFromLogs(logsToDisplay, minStart, maxEnd, 8)
- filteredErrorRates = series.errorRates
- filteredExecutionCounts = series.executionCounts
- filteredDurations = series.durations
+ const details = workflowDetails[expandedWorkflowId]
+ let logsToDisplay = (details?.logs || []).map((log) => ({
+ ...log,
+ workflowName: (log as any).workflowName || wf.workflowName,
+ }))
+ if (details && selectedSegmentIndices.length > 0) {
+ const totalMs = endTime.getTime() - getStartTime().getTime()
+ const segMs = totalMs / Math.max(1, segmentCount)
+
+ const windows = selectedSegmentIndices
+ .map((idx) => wf.segments[idx])
+ .filter(Boolean)
+ .map((s) => {
+ const start = new Date(s.timestamp).getTime()
+ const end = start + segMs
+ return { start, end }
+ })
+
+ const inAnyWindow = (t: number) =>
+ windows.some((w) => t >= w.start && t < w.end)
+
+ logsToDisplay = details.allLogs
+ .filter((log) => inAnyWindow(new Date(log.startedAt).getTime()))
+ .map((log) => ({
+ ...log,
+ workflowName: (log as any).workflowName || wf.workflowName,
+ }))
+
+ const minStart = new Date(Math.min(...windows.map((w) => w.start)))
+ const maxEnd = new Date(Math.max(...windows.map((w) => w.end)))
+
+ let filteredErrorRates = (details.errorRates || []).filter((p: any) =>
+ inAnyWindow(new Date(p.timestamp).getTime())
+ )
+ let filteredDurations = (
+ Array.isArray((details as any).durations) ? (details as any).durations : []
+ ).filter((p: any) => inAnyWindow(new Date(p.timestamp).getTime()))
+ let filteredExecutionCounts = (details.executionCounts || []).filter(
+ (p: any) => inAnyWindow(new Date(p.timestamp).getTime())
+ )
+
+ if (filteredErrorRates.length === 0 || filteredExecutionCounts.length === 0) {
+ const series = buildSeriesFromLogs(logsToDisplay, minStart, maxEnd, 8)
+ filteredErrorRates = series.errorRates
+ filteredExecutionCounts = series.executionCounts
+ filteredDurations = series.durations
+ }
+
+ ;(details as any).__filtered = {
+ errorRates: filteredErrorRates,
+ durations: filteredDurations,
+ executionCounts: filteredExecutionCounts,
+ }
}
- ;(details as any).__filtered = {
- errorRates: filteredErrorRates,
- durations: filteredDurations,
- executionCounts: filteredExecutionCounts,
- }
+ const detailsWithFilteredLogs = details
+ ? {
+ ...details,
+ logs: logsToDisplay,
+ errorRates:
+ (details as any).__filtered?.errorRates ||
+ details.errorRates ||
+ buildSeriesFromLogs(
+ logsToDisplay,
+ new Date(
+ wf.segments[0]?.timestamp ||
+ logsToDisplay[0]?.startedAt ||
+ new Date().toISOString()
+ ),
+ endTime,
+ 8
+ ).errorRates,
+ durations:
+ (details as any).__filtered?.durations ||
+ (details as any).durations ||
+ buildSeriesFromLogs(
+ logsToDisplay,
+ new Date(
+ wf.segments[0]?.timestamp ||
+ logsToDisplay[0]?.startedAt ||
+ new Date().toISOString()
+ ),
+ endTime,
+ 8
+ ).durations,
+ executionCounts:
+ (details as any).__filtered?.executionCounts ||
+ details.executionCounts ||
+ buildSeriesFromLogs(
+ logsToDisplay,
+ new Date(
+ wf.segments[0]?.timestamp ||
+ logsToDisplay[0]?.startedAt ||
+ new Date().toISOString()
+ ),
+ endTime,
+ 8
+ ).executionCounts,
+ }
+ : undefined
+
+ const selectedSegment =
+ selectedSegmentIndices.length === 1
+ ? wf.segments[selectedSegmentIndices[0]]
+ : null
+
+ return (
+ {
+ setSelectedSegmentIndices([])
+ setLastAnchorIndex(null)
+ }}
+ formatCost={formatCost}
+ />
+ )
}
- const detailsWithFilteredLogs = details
- ? {
- ...details,
- logs: logsToDisplay,
- errorRates:
- (details as any).__filtered?.errorRates ||
- details.errorRates ||
- buildSeriesFromLogs(
- logsToDisplay,
- new Date(
- wf.segments[0]?.timestamp ||
- logsToDisplay[0]?.startedAt ||
- new Date().toISOString()
- ),
- endTime,
- 8
- ).errorRates,
- durations:
- (details as any).__filtered?.durations ||
- (details as any).durations ||
- buildSeriesFromLogs(
- logsToDisplay,
- new Date(
- wf.segments[0]?.timestamp ||
- logsToDisplay[0]?.startedAt ||
- new Date().toISOString()
- ),
- endTime,
- 8
- ).durations,
- executionCounts:
- (details as any).__filtered?.executionCounts ||
- details.executionCounts ||
- buildSeriesFromLogs(
- logsToDisplay,
- new Date(
- wf.segments[0]?.timestamp ||
- logsToDisplay[0]?.startedAt ||
- new Date().toISOString()
- ),
- endTime,
- 8
- ).executionCounts,
- }
- : undefined
-
- const selectedSegment =
- selectedSegmentIndices.length === 1
- ? wf.segments[selectedSegmentIndices[0]]
- : null
+ // Aggregate view for all workflows
+ if (!globalDetails) return null
+ const totals = aggregateSegments.reduce(
+ (acc, s) => {
+ acc.total += s.totalExecutions
+ acc.success += s.successfulExecutions
+ return acc
+ },
+ { total: 0, success: 0 }
+ )
+ const failures = Math.max(totals.total - totals.success, 0)
+ const rate = totals.total > 0 ? (totals.success / totals.total) * 100 : 100
return (
{
setSelectedSegmentIndices([])
setLastAnchorIndex(null)
@@ -855,38 +901,8 @@ export default function ExecutionsDashboard() {
formatCost={formatCost}
/>
)
- }
-
- // Aggregate view for all workflows
- if (!globalDetails) return null
- const totals = aggregateSegments.reduce(
- (acc, s) => {
- acc.total += s.totalExecutions
- acc.success += s.successfulExecutions
- return acc
- },
- { total: 0, success: 0 }
- )
- const failures = Math.max(totals.total - totals.success, 0)
- const rate = totals.total > 0 ? (totals.success / totals.total) * 100 : 100
-
- return (
- {
- setSelectedSegmentIndices([])
- setLastAnchorIndex(null)
- }}
- formatCost={formatCost}
- />
- )
- })()}
+ })()}
+
>
)}