Skip to content

Commit b7e0b42

Browse files
authored
improvement(dashboard): cleanup execution dashboard UI, fix logs trace, and improve performance (#1651)
* improvement(dashboard): cleanup execution dashboard UI, fix logs trace, and improve perforamnce * cleanup * cleaned up * ack PR comments
1 parent c6ef578 commit b7e0b42

File tree

15 files changed

+634
-582
lines changed

15 files changed

+634
-582
lines changed

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/controls.tsx

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { ReactNode } from 'react'
2-
import { Loader2, Play, RefreshCw, Search, Square } from 'lucide-react'
2+
import { Loader2, RefreshCw, Search } from 'lucide-react'
33
import { Button } from '@/components/ui/button'
44
import { Input } from '@/components/ui/input'
55
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
66
import { cn } from '@/lib/utils'
7+
import { soehne } from '@/app/fonts/soehne/soehne'
78
import Timeline from '@/app/workspace/[workspaceId]/logs/components/filters/components/timeline'
89

910
export function Controls({
@@ -32,7 +33,12 @@ export function Controls({
3233
onExport?: () => void
3334
}) {
3435
return (
35-
<div className='mb-8 flex flex-col items-stretch justify-between gap-4 sm:flex-row sm:items-start'>
36+
<div
37+
className={cn(
38+
'mb-8 flex flex-col items-stretch justify-between gap-4 sm:flex-row sm:items-start',
39+
soehne.className
40+
)}
41+
>
3642
{searchComponent ? (
3743
searchComponent
3844
) : (
@@ -87,34 +93,32 @@ export function Controls({
8793
<TooltipContent>{isRefetching ? 'Refreshing...' : 'Refresh'}</TooltipContent>
8894
</Tooltip>
8995

90-
{showExport && viewMode !== 'dashboard' && (
91-
<Tooltip>
92-
<TooltipTrigger asChild>
93-
<Button
94-
variant='ghost'
95-
size='icon'
96-
onClick={onExport}
97-
className='h-9 rounded-[11px] hover:bg-secondary'
98-
aria-label='Export CSV'
96+
<Tooltip>
97+
<TooltipTrigger asChild>
98+
<Button
99+
variant='ghost'
100+
size='icon'
101+
onClick={onExport}
102+
className='h-9 rounded-[11px] hover:bg-secondary'
103+
aria-label='Export CSV'
104+
>
105+
<svg
106+
xmlns='http://www.w3.org/2000/svg'
107+
viewBox='0 0 24 24'
108+
fill='none'
109+
stroke='currentColor'
110+
strokeWidth='2'
111+
className='h-5 w-5'
99112
>
100-
<svg
101-
xmlns='http://www.w3.org/2000/svg'
102-
viewBox='0 0 24 24'
103-
fill='none'
104-
stroke='currentColor'
105-
strokeWidth='2'
106-
className='h-5 w-5'
107-
>
108-
<path d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' />
109-
<polyline points='7 10 12 15 17 10' />
110-
<line x1='12' y1='15' x2='12' y2='3' />
111-
</svg>
112-
<span className='sr-only'>Export CSV</span>
113-
</Button>
114-
</TooltipTrigger>
115-
<TooltipContent>Export CSV</TooltipContent>
116-
</Tooltip>
117-
)}
113+
<path d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' />
114+
<polyline points='7 10 12 15 17 10' />
115+
<line x1='12' y1='15' x2='12' y2='3' />
116+
</svg>
117+
<span className='sr-only'>Export CSV</span>
118+
</Button>
119+
</TooltipTrigger>
120+
<TooltipContent>Export CSV</TooltipContent>
121+
</Tooltip>
118122

119123
<div className='inline-flex h-9 items-center rounded-[11px] border bg-card p-1 shadow-sm'>
120124
<Button
@@ -123,21 +127,13 @@ export function Controls({
123127
onClick={() => setLive((v) => !v)}
124128
className={cn(
125129
'h-7 rounded-[8px] px-3 font-normal text-xs',
126-
live ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground'
130+
live
131+
? 'bg-[var(--brand-primary-hex)] text-white shadow-[0_0_0_0_var(--brand-primary-hex)] hover:bg-[var(--brand-primary-hover-hex)] hover:text-white hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)]'
132+
: 'text-muted-foreground hover:text-foreground'
127133
)}
128134
aria-pressed={live}
129135
>
130-
{live ? (
131-
<>
132-
<Square className='mr-1.5 h-3 w-3 fill-current' />
133-
Live
134-
</>
135-
) : (
136-
<>
137-
<Play className='mr-1.5 h-3 w-3' />
138-
Live
139-
</>
140-
)}
136+
Live
141137
</Button>
142138
</div>
143139

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/kpis.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,28 @@ export interface AggregateMetrics {
88

99
export function KPIs({ aggregate }: { aggregate: AggregateMetrics }) {
1010
return (
11-
<div className='mb-5 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4'>
11+
<div className='mb-2 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4'>
1212
<div className='rounded-[12px] border bg-card p-4 shadow-sm'>
1313
<div className='text-muted-foreground text-xs'>Total executions</div>
14-
<div className='mt-1 font-semibold text-[22px] leading-6'>
14+
<div className='mt-1 font-[440] text-[22px] leading-6'>
1515
{aggregate.totalExecutions.toLocaleString()}
1616
</div>
1717
</div>
1818
<div className='rounded-[12px] border bg-card p-4 shadow-sm'>
1919
<div className='text-muted-foreground text-xs'>Success rate</div>
20-
<div className='mt-1 font-semibold text-[22px] leading-6'>
20+
<div className='mt-1 font-[440] text-[22px] leading-6'>
2121
{aggregate.successRate.toFixed(1)}%
2222
</div>
2323
</div>
2424
<div className='rounded-[12px] border bg-card p-4 shadow-sm'>
2525
<div className='text-muted-foreground text-xs'>Failed executions</div>
26-
<div className='mt-1 font-semibold text-[22px] leading-6'>
26+
<div className='mt-1 font-[440] text-[22px] leading-6'>
2727
{aggregate.failedExecutions.toLocaleString()}
2828
</div>
2929
</div>
3030
<div className='rounded-[12px] border bg-card p-4 shadow-sm'>
3131
<div className='text-muted-foreground text-xs'>Active workflows</div>
32-
<div className='mt-1 font-semibold text-[22px] leading-6'>{aggregate.activeWorkflows}</div>
32+
<div className='mt-1 font-[440] text-[22px] leading-6'>{aggregate.activeWorkflows}</div>
3333
</div>
3434
</div>
3535
)

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/line-chart.tsx

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@ export function LineChart({
1717
color: string
1818
unit?: string
1919
}) {
20-
// Responsive sizing: chart fills its container width
2120
const containerRef = useRef<HTMLDivElement | null>(null)
2221
const [containerWidth, setContainerWidth] = useState<number>(420)
2322
const width = containerWidth
24-
const height = 176
25-
// Add a touch more space below the axis so curves never visually clip it
26-
const padding = { top: 18, right: 18, bottom: 32, left: 42 }
27-
// Observe container width for responsiveness
23+
const height = 166
24+
const padding = { top: 16, right: 28, bottom: 26, left: 26 }
2825
useEffect(() => {
2926
if (!containerRef.current) return
3027
const element = containerRef.current
@@ -36,7 +33,6 @@ export function LineChart({
3633
}
3734
})
3835
ro.observe(element)
39-
// Initialize once immediately
4036
const rect = element.getBoundingClientRect()
4137
if (rect?.width) setContainerWidth(Math.max(280, Math.floor(rect.width)))
4238
return () => ro.disconnect()
@@ -67,22 +63,26 @@ export function LineChart({
6763
)
6864
}
6965

70-
// Ensure nice padding on the y-domain so the line never hugs the axes
7166
const rawMax = Math.max(...data.map((d) => d.value), 1)
7267
const rawMin = Math.min(...data.map((d) => d.value), 0)
7368
const paddedMax = rawMax === 0 ? 1 : rawMax * 1.1
74-
const paddedMin = Math.min(0, rawMin) // never below zero for our metrics
75-
const maxValue = Math.ceil(paddedMax)
76-
const minValue = Math.floor(paddedMin)
69+
const paddedMin = Math.min(0, rawMin)
70+
const unitSuffixPre = (unit || '').trim().toLowerCase()
71+
let maxValue = Math.ceil(paddedMax)
72+
let minValue = Math.floor(paddedMin)
73+
if (unitSuffixPre === 'ms') {
74+
maxValue = Math.max(1000, Math.ceil(paddedMax / 1000) * 1000)
75+
minValue = 0
76+
}
7777
const valueRange = maxValue - minValue || 1
7878

7979
const yMin = padding.top + 3
8080
const yMax = padding.top + chartHeight - 3
8181

8282
const scaledPoints = data.map((d, i) => {
83-
const x = padding.left + (i / (data.length - 1 || 1)) * chartWidth
83+
const usableW = Math.max(1, chartWidth - 8)
84+
const x = padding.left + (i / (data.length - 1 || 1)) * usableW
8485
const rawY = padding.top + chartHeight - ((d.value - minValue) / valueRange) * chartHeight
85-
// keep the line safely within the plotting area to avoid clipping behind the x-axis
8686
const y = Math.max(yMin, Math.min(yMax, rawY))
8787
return { x, y }
8888
})
@@ -101,7 +101,6 @@ export function LineChart({
101101
let cp1y = p1.y + ((p2.y - p0.y) / 6) * tension
102102
const cp2x = p2.x - ((p3.x - p1.x) / 6) * tension
103103
let cp2y = p2.y - ((p3.y - p1.y) / 6) * tension
104-
// Clamp control points vertically to avoid bezier overshoot below the axis
105104
cp1y = Math.max(yMin, Math.min(yMax, cp1y))
106105
cp2y = Math.max(yMin, Math.min(yMax, cp2y))
107106
d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`
@@ -110,14 +109,17 @@ export function LineChart({
110109
})()
111110

112111
return (
113-
<div ref={containerRef} className='w-full rounded-[11px] border bg-card p-4 shadow-sm'>
112+
<div
113+
ref={containerRef}
114+
className='w-full overflow-hidden rounded-[11px] border bg-card p-4 shadow-sm'
115+
>
114116
<h4 className='mb-3 font-medium text-foreground text-sm'>{label}</h4>
115117
<TooltipProvider delayDuration={0}>
116118
<div className='relative' style={{ width, height }}>
117119
<svg
118120
width={width}
119121
height={height}
120-
className='overflow-visible'
122+
className='overflow-hidden'
121123
onMouseMove={(e) => {
122124
if (scaledPoints.length === 0) return
123125
const rect = (e.currentTarget as SVGSVGElement).getBoundingClientRect()
@@ -138,7 +140,7 @@ export function LineChart({
138140
<rect
139141
x={padding.left}
140142
y={yMin}
141-
width={chartWidth}
143+
width={Math.max(1, chartWidth - 8)}
142144
height={chartHeight - (yMin - padding.top) * 2}
143145
rx='2'
144146
/>
@@ -156,7 +158,7 @@ export function LineChart({
156158

157159
{[0.25, 0.5, 0.75].map((p) => (
158160
<line
159-
key={p}
161+
key={`${label}-grid-${p}`}
160162
x1={padding.left}
161163
y1={padding.top + chartHeight * p}
162164
x2={width - padding.right}
@@ -189,7 +191,6 @@ export function LineChart({
189191
style={{ mixBlendMode: isDark ? 'screen' : 'normal' }}
190192
/>
191193
) : (
192-
// Single-point series: show a dot so the value doesn't "disappear"
193194
<circle cx={scaledPoints[0].x} cy={scaledPoints[0].y} r='3' fill={color} />
194195
)}
195196

@@ -215,9 +216,10 @@ export function LineChart({
215216

216217
{(() => {
217218
if (data.length < 2) return null
219+
const usableW = Math.max(1, chartWidth - 8)
218220
const idx = [0, Math.floor(data.length / 2), data.length - 1]
219221
return idx.map((i) => {
220-
const x = padding.left + (i / (data.length - 1 || 1)) * chartWidth
222+
const x = padding.left + (i / (data.length - 1 || 1)) * usableW
221223
const tsSource = data[i]?.timestamp
222224
if (!tsSource) return null
223225
const ts = new Date(tsSource)
@@ -226,10 +228,10 @@ export function LineChart({
226228
: ts.toLocaleString('en-US', { month: 'short', day: 'numeric' })
227229
return (
228230
<text
229-
key={i}
231+
key={`${label}-x-axis-${i}`}
230232
x={x}
231233
y={height - padding.bottom + 14}
232-
fontSize='10'
234+
fontSize='9'
233235
textAnchor='middle'
234236
fill='hsl(var(--muted-foreground))'
235237
>
@@ -239,26 +241,41 @@ export function LineChart({
239241
})
240242
})()}
241243

242-
<text
243-
x={padding.left - 10}
244-
y={padding.top}
245-
textAnchor='end'
246-
fontSize='10'
247-
fill='hsl(var(--muted-foreground))'
248-
>
249-
{maxValue}
250-
{unit}
251-
</text>
252-
<text
253-
x={padding.left - 10}
254-
y={height - padding.bottom}
255-
textAnchor='end'
256-
fontSize='10'
257-
fill='hsl(var(--muted-foreground))'
258-
>
259-
{minValue}
260-
{unit}
261-
</text>
244+
{(() => {
245+
const unitSuffix = (unit || '').trim()
246+
const showInTicks = unitSuffix === '%'
247+
const fmtCompact = (v: number) =>
248+
new Intl.NumberFormat('en-US', {
249+
notation: 'compact',
250+
maximumFractionDigits: 1,
251+
})
252+
.format(v)
253+
.toLowerCase()
254+
return (
255+
<>
256+
<text
257+
x={padding.left - 8}
258+
y={padding.top}
259+
textAnchor='end'
260+
fontSize='9'
261+
fill='hsl(var(--muted-foreground))'
262+
>
263+
{fmtCompact(maxValue)}
264+
{showInTicks ? unit : ''}
265+
</text>
266+
<text
267+
x={padding.left - 8}
268+
y={height - padding.bottom}
269+
textAnchor='end'
270+
fontSize='9'
271+
fill='hsl(var(--muted-foreground))'
272+
>
273+
{fmtCompact(minValue)}
274+
{showInTicks ? unit : ''}
275+
</text>
276+
</>
277+
)
278+
})()}
262279

263280
<line
264281
x1={padding.left}
@@ -283,7 +300,7 @@ export function LineChart({
283300
} else if (u.toLowerCase().includes('ms')) {
284301
formatted = `${Math.round(val)}ms`
285302
} else if (u.toLowerCase().includes('exec')) {
286-
formatted = `${Math.round(val)}${u}` // keep label like " execs"
303+
formatted = `${Math.round(val)}${u}`
287304
} else {
288305
formatted = `${Math.round(val)}${u}`
289306
}

0 commit comments

Comments
 (0)