diff --git a/cmd/web-ui/static/app.js b/cmd/web-ui/static/app.js index c2291d7..f9d2a3d 100644 --- a/cmd/web-ui/static/app.js +++ b/cmd/web-ui/static/app.js @@ -1137,6 +1137,11 @@ async function loadTimeseries() { try { + // Destroy chart so it's recreated with new window scale + if (dashboardChart) { + dashboardChart.destroy(); + dashboardChart = null; + } const data = await api('/v1/stats/timeseries?window=' + dashboardState.window); if (!isCurrentPath('/')) return; dashboardState.timeseries = data; @@ -1146,28 +1151,35 @@ } } + function buildChartData() { + const ts = dashboardState.timeseries; + if (!ts || !ts.series || ts.series.length === 0) return null; + return [ + ts.series.map(b => Math.floor(new Date(b.ts).getTime() / 1000)), + ts.series.map(b => b.runs), + ts.series.map(b => b.tools), + ts.series.map(b => b.errors), + ]; + } + function renderTimeseriesChart() { const container = document.getElementById('dash-chart'); if (!container || !dashboardState.timeseries) return; - const ts = dashboardState.timeseries; - if (!ts.series || ts.series.length === 0) { + const data = buildChartData(); + if (!data) { container.innerHTML = '

No data for this window

'; return; } + // If chart already exists, just update the data if (dashboardChart) { - dashboardChart.destroy(); - dashboardChart = null; + dashboardChart.setData(data); + return; } container.innerHTML = ''; - const timestamps = ts.series.map(b => Math.floor(new Date(b.ts).getTime() / 1000)); - const runs = ts.series.map(b => b.runs); - const tools = ts.series.map(b => b.tools); - const errors = ts.series.map(b => b.errors); - const width = container.clientWidth || 600; const height = 200; @@ -1217,7 +1229,7 @@ ], }; - dashboardChart = new uPlot(opts, [timestamps, runs, tools, errors], container); + dashboardChart = new uPlot(opts, data, container); if (dashboardResizeObserver) { dashboardResizeObserver.disconnect(); diff --git a/internal/store/postgres/stats.go b/internal/store/postgres/stats.go index 107ce23..77943f3 100644 --- a/internal/store/postgres/stats.go +++ b/internal/store/postgres/stats.go @@ -36,23 +36,21 @@ func (d *DB) GetSummary(ctx context.Context) (*Summary, error) { now := time.Now() midnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - // Active sessions: sessions that started today but have not ended today + // Active sessions: sessions with a session.start but no session.end (ever) activeQ := ` SELECT COUNT(DISTINCT session_id) FROM events WHERE type = 'session.start' - AND ts >= $1 AND session_id IS NOT NULL AND session_id NOT IN ( SELECT DISTINCT session_id FROM events WHERE type = 'session.end' - AND ts >= $1 AND session_id IS NOT NULL ) ` var activeSessions int - if err := d.sql.QueryRowContext(ctx, activeQ, midnight).Scan(&activeSessions); err != nil { + if err := d.sql.QueryRowContext(ctx, activeQ).Scan(&activeSessions); err != nil { return nil, err }