import { ChevronsUpDown, Lightbulb } from 'lucide-react' import dynamic from 'next/dynamic' import { useEffect, useState } from 'react' import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Button, cn } from 'ui' import { QueryPanelContainer, QueryPanelSection } from './QueryPanel' import { buildQueryExplanationPrompt } from './QueryPerformance.ai' import { QUERY_PERFORMANCE_COLUMNS } from './QueryPerformance.constants' import { QueryPerformanceRow } from './QueryPerformance.types' import { formatDuration } from './QueryPerformance.utils' import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { AiAssistantDropdown } from '@/components/ui/AiAssistantDropdown' import { formatSql } from '@/lib/formatSql' import { useTrack } from '@/lib/telemetry/track' import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state' import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' interface QueryDetailProps { selectedRow?: QueryPerformanceRow onClickViewSuggestion: () => void onClose?: () => void } // Load SqlMonacoBlock (monaco editor) client-side only (does not behave well server-side) const SqlMonacoBlock = dynamic( () => import('./SqlMonacoBlock').then(({ SqlMonacoBlock }) => SqlMonacoBlock), { ssr: false, } ) export const QueryDetail = ({ selectedRow, onClickViewSuggestion, onClose }: QueryDetailProps) => { // [Joshen] TODO implement this logic once the linter rules are in const isLinterWarning = false const report = QUERY_PERFORMANCE_COLUMNS const [query, setQuery] = useState(selectedRow?.['query']) const { openSidebar } = useSidebarManagerSnapshot() const aiSnap = useAiAssistantStateSnapshot() const track = useTrack() useEffect(() => { if (selectedRow !== undefined) { const formattedQuery = formatSql(selectedRow['query']) setQuery(formattedQuery) } }, [selectedRow]) const [isExpanded, setIsExpanded] = useState(false) const handleExplainQuery = () => { if (!selectedRow?.query) return const { query, prompt } = buildQueryExplanationPrompt(selectedRow) openSidebar(SIDEBAR_KEYS.AI_ASSISTANT) aiSnap.newChat({ sqlSnippets: [ { label: 'Query', content: query, }, ], initialMessage: prompt, }) track('query_performance_explain_with_ai_button_clicked') // Close the query detail panel since we need to see the AI assistant panel onClose?.() } const buildPromptForCopy = () => { if (!selectedRow?.query) return '' const { query, prompt } = buildQueryExplanationPrompt(selectedRow) return `${prompt}\n\nSQL Query:\n\`\`\`sql\n${query}\n\`\`\`` } return (

Query pattern

{isLinterWarning && ( Suggested optimization: Add an index Adding an index will help this query execute faster )}

Metadata

    {report .filter((x) => x.id !== 'query') .map((x) => { const rawValue = selectedRow?.[x.id] const isTime = x.name.includes('time') const formattedValue = isTime ? typeof rawValue === 'number' && !isNaN(rawValue) && isFinite(rawValue) ? `${Math.round(rawValue).toLocaleString()}ms` : 'n/a' : rawValue != null ? String(rawValue) : 'n/a' if (x.id === 'prop_total_time') { const percentage = selectedRow?.prop_total_time || 0 const totalTime = selectedRow?.total_time || 0 return (
  • {x.name}

    {percentage && totalTime ? (

    {percentage.toFixed(1)}% {' '} /{' '} {formatDuration(totalTime)}

    ) : (

    )}
  • ) } if (x.id == 'rows_read') { return (
  • {x.name}

    {typeof rawValue === 'number' && !isNaN(rawValue) && isFinite(rawValue) ? (

    {rawValue.toLocaleString()}

    ) : (

    )}
  • ) } const cacheHitRateToNumber = (value: number | string) => { if (typeof value === 'number') return value return parseFloat(value.toString().replace('%', '')) || 0 } if (x.id === 'cache_hit_rate') { return (
  • {x.name}

    {typeof rawValue === 'string' || typeof rawValue === 'number' ? (

    {cacheHitRateToNumber(rawValue).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} %

    ) : (

    )}
  • ) } return (
  • {x.name}

    {formattedValue}

  • ) })}
) }