import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useIndexAdvisorReport, useApplyIndexRecommendation } from "@/hooks/use-index-advisor"; import { RecommendationCard } from "./RecommendationCard"; import { toast } from "sonner"; import { Loader2, Gauge, Search, AlertTriangle } from "lucide-react"; import type { IndexAdvisorReport } from "@/types"; interface Props { connectionId: string; } export function IndexAdvisorPanel({ connectionId }: Props) { const [report, setReport] = useState(null); const [appliedDdls, setAppliedDdls] = useState>(new Set()); const [applyingDdl, setApplyingDdl] = useState(null); const reportMutation = useIndexAdvisorReport(); const applyMutation = useApplyIndexRecommendation(); const handleAnalyze = () => { reportMutation.mutate(connectionId, { onSuccess: (data) => { setReport(data); setAppliedDdls(new Set()); }, onError: (err) => toast.error("Analysis failed", { description: String(err) }), }); }; const handleApply = async (ddl: string) => { if (!confirm("Apply this index change? This will modify the database schema.")) return; setApplyingDdl(ddl); try { await applyMutation.mutateAsync({ connectionId, ddl }); setAppliedDdls((prev) => new Set(prev).add(ddl)); toast.success("Index change applied"); } catch (err) { toast.error("Failed to apply", { description: String(err) }); } finally { setApplyingDdl(null); } }; return (
{/* Header */}

Index Advisor

{/* Content */}
{!report ? (
Click Analyze to scan your database for index optimization opportunities.
) : (
Recommendations {report.recommendations.length > 0 && ( {report.recommendations.length} )} Table Stats Index Stats Slow Queries {!report.has_pg_stat_statements && ( )}
{report.recommendations.length === 0 ? (
No recommendations found. Your indexes look good!
) : ( report.recommendations.map((rec, i) => ( )) )}
{report.table_stats.map((ts) => { const ratio = ts.seq_scan + ts.idx_scan > 0 ? ts.seq_scan / (ts.seq_scan + ts.idx_scan) : 0; return ( ); })}
Table Seq Scans Idx Scans Rows Table Size Index Size
{ts.schema}.{ts.table} 0.8 && ts.n_live_tup > 1000 ? "text-destructive font-medium" : ""}`}> {ts.seq_scan.toLocaleString()} {ts.idx_scan.toLocaleString()} {ts.n_live_tup.toLocaleString()} {ts.table_size} {ts.index_size}
{report.index_stats.map((is) => ( ))}
Index Table Scans Size Definition
{is.index_name} {is.schema}.{is.table} {is.idx_scan.toLocaleString()} {is.index_size} {is.definition}
{!report.has_pg_stat_statements ? (
pg_stat_statements extension is not installed

Enable it with: CREATE EXTENSION pg_stat_statements;

) : report.slow_queries.length === 0 ? (
No slow queries found.
) : (
{report.slow_queries.map((sq, i) => ( ))}
Query Calls Mean (ms) Total (ms) Rows
{sq.query.substring(0, 150)} {sq.calls.toLocaleString()} {sq.mean_time_ms.toFixed(1)} {sq.total_time_ms.toFixed(0)} {sq.rows.toLocaleString()}
)}
)}
); }