Skip to content

Commit 03b4a6c

Browse files
feat(ui): merge Slow Queries and Workload Analysis into Performance
One sidebar destination with Query Trends, By Customer, Workload, and Settings. If no slow-query log source is attached, the page shows a Configure slow queries empty state that opens SlowQuerySourceModal. Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
1 parent 3883011 commit 03b4a6c

12 files changed

Lines changed: 205 additions & 88 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ backend/
9393
src/ # Frontend (React)
9494
components/ # UI components
9595
tabs/ # 40+ specialized tabs
96+
sections/ # Top-level sidebar destinations (Agent, Dashboards, Brain,
97+
# Performance = Slow Queries + Workload, Editor, Docs)
9698
lib/
9799
api/client.js # Centralized API layer (axios, 25+ modules)
98100
stores/ # Zustand stores (dashboard, connection, chat, UI)

src/components/layout/AppSidebar.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useEffect, useRef } from 'react'
2-
import { BookOpen, Brain, Code2, Database, Settings, PanelLeftClose, PanelLeftOpen, LogOut, User, ChevronDown, Check, Newspaper, Gauge, Activity, MessageSquare, LayoutDashboard } from 'lucide-react'
2+
import { BookOpen, Brain, Code2, Database, Settings, PanelLeftClose, PanelLeftOpen, LogOut, User, ChevronDown, Check, Newspaper, Gauge, MessageSquare, LayoutDashboard } from 'lucide-react'
33
import { useActiveSection, useSetActiveSection } from '@/lib/stores/useNavStore'
44
import { useConnectionManager } from '@/lib/hooks/useConnectionManager'
55
import { AGENTS_ENABLED, canAccessHomeSection, getConnectionAccessBadge, getConnectionAccessLabel } from '@/lib/features'
@@ -14,8 +14,7 @@ const NAV_ITEMS = [
1414
{ id: 'digest', label: 'Digest', icon: Newspaper },
1515
...(AGENTS_ENABLED ? [{ id: 'brain', label: 'Agents', icon: Brain }] : []),
1616
{ id: 'company-knowledge', label: 'Brain', icon: Brain },
17-
{ id: 'slow-queries', label: 'Slow Queries', icon: Gauge },
18-
{ id: 'workload-analysis', label: 'Workload Analysis', icon: Activity },
17+
{ id: 'performance', label: 'Performance', icon: Gauge },
1918
{ id: 'editor', label: 'Editor', icon: Code2 },
2019
{ id: 'docs', label: 'Docs', icon: BookOpen },
2120
]

src/components/onboarding/StepQueryLogs.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const SOURCES = [
2727
id: 'other',
2828
icon: Zap,
2929
title: 'Other (Datadog, ELK…)',
30-
sub: 'Configure after setup in the Slow Queries tab.',
30+
sub: 'Configure after setup in the Performance tab.',
3131
badge: 'Coming soon',
3232
disabled: true,
3333
},
Lines changed: 93 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,137 @@
11
import { useState } from 'react'
2-
import { LineChart, Settings, Users } from 'lucide-react'
2+
import { Activity, FileText, LineChart, Settings, Users } from 'lucide-react'
33
import { useConnectionManager } from '@/lib/hooks/useConnectionManager'
4+
import { useSlowLogSourceConfig } from '@/lib/hooks/queries'
45
import QueryTrendsTab from '@/components/tabs/Performance/QueryTrendsTab'
56
import CustomerExplorer from '@/components/tabs/Performance/CustomerExplorer'
67
import SlowQuerySettingsPanel from '@/components/tabs/Performance/SlowQuerySettingsPanel'
8+
import WorkloadAnalysisPanel from '@/components/tabs/Performance/WorkloadAnalysisPanel'
9+
import SlowQuerySourceModal from '@/components/SlowQuerySourceModal'
10+
import { HelpTooltip } from '@/components/tabs/Brain/components/HelpTooltip'
711
import sectionStyles from './TopLevelSection.module.css'
812
import styles from './SlowQueriesSection.module.css'
913

1014
const TABS = [
1115
{ id: 'trends', label: 'Query Trends', icon: LineChart },
1216
{ id: 'customers', label: 'By Customer', icon: Users },
17+
{ id: 'workload', label: 'Workload', icon: Activity },
1318
{ id: 'settings', label: 'Settings', icon: Settings },
1419
]
1520

21+
const LOG_SOURCE_HELP = {
22+
title: 'Slow query log',
23+
description:
24+
'Query trends, per-customer load, and workload analysis all read from ingested slow-query logs. Attach CloudWatch, S3, Azure, GCP, Datadog, Elasticsearch, or a file upload before those views can run.',
25+
}
26+
1627
/**
17-
* Top-level "Slow Queries" section — the 30-day slow-query analytics surface,
18-
* promoted out of the Brain tab strip into its own sidebar destination.
28+
* Combined Performance section — Slow Queries + Workload Analysis.
1929
*
20-
* Two sub-views: the query trends/timeline/regressions, and the settings
21-
* panel where the tenant column for per-customer attribution is configured.
30+
* Both surfaces need a slow-query log source. If none is attached, the page
31+
* is a single empty state whose CTA opens SlowQuerySourceModal.
2232
*/
2333
export default function SlowQueriesSection() {
24-
const { connectionId } = useConnectionManager()
34+
const { connectionId, selectedConnection } = useConnectionManager()
2535
const [tab, setTab] = useState('trends')
36+
const [logSourceModalOpen, setLogSourceModalOpen] = useState(false)
37+
const logSourceQ = useSlowLogSourceConfig(connectionId)
38+
const hasLogSource = Boolean(logSourceQ.data?.id)
2639

2740
return (
2841
<div className={sectionStyles.page}>
2942
<div className={sectionStyles.header}>
30-
<div className={sectionStyles.eyebrow}>Slow Queries</div>
31-
<h1 className={sectionStyles.title}>Slow query analytics</h1>
43+
<div className={sectionStyles.eyebrow}>Performance</div>
44+
<h1 className={sectionStyles.title}>Slow queries &amp; workload</h1>
3245
<p className={sectionStyles.subtitle}>
33-
Per-query performance trends over the last 30 days, regression detection,
34-
and per-customer breakdown.
46+
Per-query trends, regressions, customer attribution, and a holistic
47+
workload report — all from the same slow-query log.
3548
</p>
3649
</div>
3750

3851
{!connectionId ? (
3952
<div className={styles.empty}>
40-
Select a database connection to see slow query analytics.
53+
Select a database connection to see performance analytics.
54+
</div>
55+
) : logSourceQ.isLoading ? (
56+
<div className={styles.empty}>Checking slow query log source…</div>
57+
) : logSourceQ.isError ? (
58+
<div className={styles.empty}>
59+
Could not load the slow query log configuration for this connection.
60+
</div>
61+
) : !hasLogSource ? (
62+
<div className={styles.setupCard}>
63+
<FileText size={32} className={styles.setupIcon} />
64+
<h2 className={styles.setupTitle}>
65+
<HelpTooltip content={LOG_SOURCE_HELP}>
66+
<span>Configure slow queries</span>
67+
</HelpTooltip>
68+
</h2>
69+
<p className={styles.setupCopy}>
70+
Attach a slow-query log source to unlock query trends, per-customer
71+
breakdown, and workload analysis. DeepSQL pulls from CloudWatch, S3,
72+
Azure Blob, GCP, Datadog, Elasticsearch, or a file you upload.
73+
</p>
74+
<button
75+
type="button"
76+
className={styles.setupCta}
77+
onClick={() => setLogSourceModalOpen(true)}
78+
>
79+
<FileText size={14} />
80+
Configure slow queries
81+
</button>
4182
</div>
4283
) : (
4384
<>
44-
<div className={styles.tabBar} role="tablist" aria-label="Slow query views">
45-
{TABS.map((t) => {
46-
const Icon = t.icon
47-
const active = tab === t.id
48-
return (
49-
<button
50-
key={t.id}
51-
type="button"
52-
role="tab"
53-
aria-selected={active}
54-
className={`${styles.tabButton} ${active ? styles.tabButtonActive : ''}`}
55-
onClick={() => setTab(t.id)}
56-
>
57-
<Icon size={14} />
58-
<span>{t.label}</span>
59-
</button>
60-
)
61-
})}
85+
<div className={styles.toolbar}>
86+
<div className={styles.tabBar} role="tablist" aria-label="Performance views">
87+
{TABS.map((t) => {
88+
const Icon = t.icon
89+
const active = tab === t.id
90+
return (
91+
<button
92+
key={t.id}
93+
type="button"
94+
role="tab"
95+
aria-selected={active}
96+
className={`${styles.tabButton} ${active ? styles.tabButtonActive : ''}`}
97+
onClick={() => setTab(t.id)}
98+
>
99+
<Icon size={14} />
100+
<span>{t.label}</span>
101+
</button>
102+
)
103+
})}
104+
</div>
105+
<button
106+
type="button"
107+
className={styles.logSourceBtn}
108+
onClick={() => setLogSourceModalOpen(true)}
109+
>
110+
<FileText size={13} />
111+
Log source
112+
</button>
62113
</div>
63114

64115
<div className={styles.content}>
65116
{tab === 'trends' && <QueryTrendsTab connectionId={connectionId} />}
66117
{tab === 'customers' && <CustomerExplorer connectionId={connectionId} />}
118+
{tab === 'workload' && <WorkloadAnalysisPanel connectionId={connectionId} />}
67119
{tab === 'settings' && <SlowQuerySettingsPanel connectionId={connectionId} />}
68120
</div>
69121
</>
70122
)}
123+
124+
{logSourceModalOpen && connectionId && (
125+
<SlowQuerySourceModal
126+
connectionId={connectionId}
127+
connectionName={selectedConnection?.connectionName}
128+
dbType={selectedConnection?.dbType || 'mysql'}
129+
onClose={() => {
130+
setLogSourceModalOpen(false)
131+
logSourceQ.refetch()
132+
}}
133+
/>
134+
)}
71135
</div>
72136
)
73137
}

src/components/sections/SlowQueriesSection.module.css

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
width: fit-content;
99
}
1010

11+
.toolbar {
12+
display: flex;
13+
align-items: center;
14+
justify-content: space-between;
15+
gap: 12px;
16+
flex-wrap: wrap;
17+
}
18+
1119
.tabButton {
1220
display: inline-flex;
1321
align-items: center;
@@ -45,3 +53,77 @@
4553
font-size: 13px;
4654
color: #9ca3af;
4755
}
56+
57+
.setupCard {
58+
display: flex;
59+
flex-direction: column;
60+
align-items: center;
61+
gap: 8px;
62+
max-width: 560px;
63+
margin: 24px auto 0;
64+
padding: 40px 28px;
65+
border: 1px solid #e5e7eb;
66+
border-radius: 14px;
67+
background: #fff;
68+
text-align: center;
69+
}
70+
71+
.setupIcon {
72+
opacity: 0.4;
73+
margin-bottom: 4px;
74+
}
75+
76+
.setupTitle {
77+
margin: 0;
78+
font-size: 18px;
79+
font-weight: 650;
80+
letter-spacing: -0.02em;
81+
color: #111827;
82+
}
83+
84+
.setupCopy {
85+
margin: 0 0 10px;
86+
font-size: 14px;
87+
line-height: 1.55;
88+
color: #6b7280;
89+
}
90+
91+
.setupCta {
92+
display: inline-flex;
93+
align-items: center;
94+
gap: 8px;
95+
margin-top: 6px;
96+
padding: 10px 16px;
97+
border: 1px solid #111827;
98+
border-radius: 10px;
99+
background: #111827;
100+
color: #fff;
101+
font: inherit;
102+
font-size: 13px;
103+
font-weight: 600;
104+
cursor: pointer;
105+
}
106+
107+
.setupCta:hover {
108+
opacity: 0.88;
109+
}
110+
111+
.logSourceBtn {
112+
display: inline-flex;
113+
align-items: center;
114+
gap: 6px;
115+
padding: 8px 12px;
116+
border: 1px solid #e5e7eb;
117+
border-radius: 10px;
118+
background: #fff;
119+
color: #374151;
120+
font: inherit;
121+
font-size: 13px;
122+
font-weight: 600;
123+
cursor: pointer;
124+
}
125+
126+
.logSourceBtn:hover {
127+
border-color: #d1d5db;
128+
color: #111827;
129+
}
Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,6 @@
1-
import { useConnectionManager } from '@/lib/hooks/useConnectionManager'
2-
import WorkloadAnalysisPanel from '@/components/tabs/Performance/WorkloadAnalysisPanel'
3-
import sectionStyles from './TopLevelSection.module.css'
4-
import styles from './SlowQueriesSection.module.css'
1+
export { default } from './SlowQueriesSection'
52

63
/**
7-
* Top-level "Workload Analysis" section — the holistic, on-demand report that
8-
* composes index recommendations, query rewrites, pre-aggregations, and index
9-
* cleanup over the most-impactful queries. Promoted out of the Slow Queries
10-
* tab strip into its own sidebar destination (sits right below Slow Queries).
4+
* @deprecated Workload Analysis now lives as a sub-tab of Performance
5+
* (`SlowQueriesSection`). Kept as a re-export so old imports keep working.
116
*/
12-
export default function WorkloadAnalysisSection() {
13-
const { connectionId } = useConnectionManager()
14-
15-
return (
16-
<div className={sectionStyles.page}>
17-
<div className={sectionStyles.header}>
18-
<div className={sectionStyles.eyebrow}>Workload Analysis</div>
19-
<h1 className={sectionStyles.title}>Holistic workload analysis</h1>
20-
<p className={sectionStyles.subtitle}>
21-
One holistic pass over your most-impactful queries — index
22-
recommendations, query rewrites, pre-aggregations, and index cleanup,
23-
composed into a single report.
24-
</p>
25-
</div>
26-
27-
{!connectionId ? (
28-
<div className={styles.empty}>
29-
Select a database connection to analyze its workload.
30-
</div>
31-
) : (
32-
<div className={styles.content}>
33-
<WorkloadAnalysisPanel connectionId={connectionId} />
34-
</div>
35-
)}
36-
</div>
37-
)
38-
}

src/components/tabs/Brain/utils/helpText.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ export const QUERY_INTELLIGENCE = {
738738
panel: {
739739
title: 'Query Intelligence',
740740
description: 'ML-based learning system that builds pattern knowledge over time for smarter query optimization.',
741-
differentiation: 'Unlike the Slow Queries tab (operational monitoring for immediate issues), Query Intelligence focuses on long-term pattern recognition and cardinality accuracy learning.',
741+
differentiation: 'Unlike the Performance tab (operational monitoring for immediate issues), Query Intelligence focuses on long-term pattern recognition and cardinality accuracy learning.',
742742
},
743743

744744
// Buttons
@@ -795,7 +795,7 @@ export const QUERY_INTELLIGENCE = {
795795
recommendations: {
796796
title: 'Recommendations',
797797
description: 'Actionable database maintenance commands to improve cardinality estimation accuracy.',
798-
differentiation: 'These focus on optimizer statistics maintenance (ANALYZE TABLE, histograms). Query rewrites and index suggestions are in Database Advisor → Slow Queries.',
798+
differentiation: 'These focus on optimizer statistics maintenance (ANALYZE TABLE, histograms). Query rewrites and index suggestions are in Performance → Workload.',
799799
impact: 'Accurate cardinality estimates lead to better query plans, reducing execution time for complex queries.',
800800
},
801801
analyzeTable: {

src/components/tabs/Core/ExplainAnalysisPanel.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export default function ExplainAnalysisPanel({ analysis }) {
363363
)}
364364
{indexBottleneck && (
365365
<div className={styles.bannerNote}>
366-
Note: index recommendations are workload-weighted across all queries — see Workload Analysis,
366+
Note: index recommendations are workload-weighted across all queries — see Performance → Workload,
367367
not this single query.
368368
</div>
369369
)}

src/components/tabs/Performance/WorkloadAnalysisPanel.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
CheckCircle2,
2828
} from "lucide-react";
2929
import { workloadAnalysisAPI } from "@/lib/api/client";
30+
import { HelpTooltip } from "@/components/tabs/Brain/components/HelpTooltip";
3031
import styles from "./WorkloadAnalysisPanel.module.css";
3132

3233
const fmtMs = (v) => {
@@ -341,14 +342,16 @@ export default function WorkloadAnalysisPanel({ connectionId }) {
341342
<span className={styles.reasonBadge}>{REASON_LABEL[rec.candidateReason] || rec.candidateReason}</span>
342343
)}
343344
{rec.fingerprint && (
344-
<span
345-
className={styles.fingerprint}
346-
title="Query fingerprint — look this up in the Slow Queries tab to see its historical performance"
347-
>
348-
<span className={styles.fingerprintLabel}>fingerprint</span>
349-
<code>{rec.fingerprint}</code>
350-
<CopyButton text={rec.fingerprint} />
351-
</span>
345+
<HelpTooltip content={{
346+
title: 'Query fingerprint',
347+
description: 'Look this up under Performance → Query Trends to see historical performance.',
348+
}}>
349+
<span className={styles.fingerprint}>
350+
<span className={styles.fingerprintLabel}>fingerprint</span>
351+
<code>{rec.fingerprint}</code>
352+
<CopyButton text={rec.fingerprint} />
353+
</span>
354+
</HelpTooltip>
352355
)}
353356
{rec.estimatedImprovementPct != null && (
354357
<span className={styles.improve}>~{Math.round(rec.estimatedImprovementPct)}% faster</span>

0 commit comments

Comments
 (0)