|
1 | 1 | import { useState } from 'react' |
2 | | -import { LineChart, Settings, Users } from 'lucide-react' |
| 2 | +import { Activity, FileText, LineChart, Settings, Users } from 'lucide-react' |
3 | 3 | import { useConnectionManager } from '@/lib/hooks/useConnectionManager' |
| 4 | +import { useSlowLogSourceConfig } from '@/lib/hooks/queries' |
4 | 5 | import QueryTrendsTab from '@/components/tabs/Performance/QueryTrendsTab' |
5 | 6 | import CustomerExplorer from '@/components/tabs/Performance/CustomerExplorer' |
6 | 7 | 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' |
7 | 11 | import sectionStyles from './TopLevelSection.module.css' |
8 | 12 | import styles from './SlowQueriesSection.module.css' |
9 | 13 |
|
10 | 14 | const TABS = [ |
11 | 15 | { id: 'trends', label: 'Query Trends', icon: LineChart }, |
12 | 16 | { id: 'customers', label: 'By Customer', icon: Users }, |
| 17 | + { id: 'workload', label: 'Workload', icon: Activity }, |
13 | 18 | { id: 'settings', label: 'Settings', icon: Settings }, |
14 | 19 | ] |
15 | 20 |
|
| 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 | + |
16 | 27 | /** |
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. |
19 | 29 | * |
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. |
22 | 32 | */ |
23 | 33 | export default function SlowQueriesSection() { |
24 | | - const { connectionId } = useConnectionManager() |
| 34 | + const { connectionId, selectedConnection } = useConnectionManager() |
25 | 35 | const [tab, setTab] = useState('trends') |
| 36 | + const [logSourceModalOpen, setLogSourceModalOpen] = useState(false) |
| 37 | + const logSourceQ = useSlowLogSourceConfig(connectionId) |
| 38 | + const hasLogSource = Boolean(logSourceQ.data?.id) |
26 | 39 |
|
27 | 40 | return ( |
28 | 41 | <div className={sectionStyles.page}> |
29 | 42 | <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 & workload</h1> |
32 | 45 | <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. |
35 | 48 | </p> |
36 | 49 | </div> |
37 | 50 |
|
38 | 51 | {!connectionId ? ( |
39 | 52 | <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> |
41 | 82 | </div> |
42 | 83 | ) : ( |
43 | 84 | <> |
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> |
62 | 113 | </div> |
63 | 114 |
|
64 | 115 | <div className={styles.content}> |
65 | 116 | {tab === 'trends' && <QueryTrendsTab connectionId={connectionId} />} |
66 | 117 | {tab === 'customers' && <CustomerExplorer connectionId={connectionId} />} |
| 118 | + {tab === 'workload' && <WorkloadAnalysisPanel connectionId={connectionId} />} |
67 | 119 | {tab === 'settings' && <SlowQuerySettingsPanel connectionId={connectionId} />} |
68 | 120 | </div> |
69 | 121 | </> |
70 | 122 | )} |
| 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 | + )} |
71 | 135 | </div> |
72 | 136 | ) |
73 | 137 | } |
0 commit comments