QueryInsightsDetailSheet.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { Loader2 } from 'lucide-react'
  2. import {
  3. AiIconAnimation,
  4. Button,
  5. Sheet,
  6. SheetContent,
  7. SheetDescription,
  8. SheetTitle,
  9. Tabs_Shadcn_,
  10. TabsContent_Shadcn_,
  11. TabsList_Shadcn_,
  12. TabsTrigger_Shadcn_,
  13. } from 'ui'
  14. import { QueryDetail } from '../../QueryPerformance/QueryDetail'
  15. import { QueryIndexes } from '../../QueryPerformance/QueryIndexes'
  16. import { buildExplainOptimizationPrompt } from '../../QueryPerformance/QueryPerformance.ai'
  17. import type { ClassifiedQuery } from '../QueryInsightsHealth/QueryInsightsHealth.types'
  18. import { ExplainVisualizer } from '@/components/interfaces/ExplainVisualizer/ExplainVisualizer'
  19. import type { QueryPlanRow } from '@/components/interfaces/ExplainVisualizer/ExplainVisualizer.types'
  20. import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
  21. import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
  22. import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
  23. interface QueryInsightsDetailSheetProps {
  24. open: boolean
  25. onOpenChange: (open: boolean) => void
  26. activeSheetRow: ClassifiedQuery | undefined
  27. sheetView: 'details' | 'indexes' | 'explain'
  28. onSheetViewChange: (view: 'details' | 'indexes' | 'explain') => void
  29. onClose: () => void
  30. dataGridContainerRef: React.RefObject<HTMLDivElement | null>
  31. triageContainerRef: React.RefObject<HTMLDivElement | null>
  32. explainLoadingQuery: string | null
  33. explainResults: Record<string, QueryPlanRow[]>
  34. }
  35. export const QueryInsightsDetailSheet = ({
  36. open,
  37. onOpenChange,
  38. activeSheetRow,
  39. sheetView,
  40. onSheetViewChange,
  41. onClose,
  42. dataGridContainerRef,
  43. triageContainerRef,
  44. explainLoadingQuery,
  45. explainResults,
  46. }: QueryInsightsDetailSheetProps) => {
  47. const { openSidebar } = useSidebarManagerSnapshot()
  48. const aiSnap = useAiAssistantStateSnapshot()
  49. return (
  50. <Sheet open={open} onOpenChange={onOpenChange} modal={false}>
  51. <SheetTitle className="sr-only">Query details</SheetTitle>
  52. <SheetDescription className="sr-only">Query Insights Details &amp; Indexes</SheetDescription>
  53. <SheetContent
  54. side="right"
  55. className="flex flex-col h-full bg-studio border-l lg:w-[calc(100vw-802px)]! max-w-[700px] w-full"
  56. hasOverlay={false}
  57. onInteractOutside={(event) => {
  58. if (
  59. dataGridContainerRef.current?.contains(event.target as Node) ||
  60. triageContainerRef.current?.contains(event.target as Node)
  61. ) {
  62. event.preventDefault()
  63. }
  64. }}
  65. >
  66. <Tabs_Shadcn_
  67. value={sheetView}
  68. className="flex flex-col h-full"
  69. onValueChange={(v) => onSheetViewChange(v as 'details' | 'indexes' | 'explain')}
  70. >
  71. <div className="px-5 border-b">
  72. <TabsList_Shadcn_ className="px-0 flex gap-x-4 min-h-[46px] border-b-0 [&>button]:h-[47px]">
  73. <TabsTrigger_Shadcn_
  74. value="details"
  75. className="px-0 pb-0 data-[state=active]:bg-transparent shadow-none!"
  76. >
  77. Query details
  78. </TabsTrigger_Shadcn_>
  79. <TabsTrigger_Shadcn_
  80. value="indexes"
  81. className="px-0 pb-0 data-[state=active]:bg-transparent shadow-none!"
  82. >
  83. Indexes
  84. </TabsTrigger_Shadcn_>
  85. {activeSheetRow?.issueType !== 'error' && (
  86. <TabsTrigger_Shadcn_
  87. value="explain"
  88. className="px-0 pb-0 data-[state=active]:bg-transparent shadow-none!"
  89. >
  90. Explain
  91. </TabsTrigger_Shadcn_>
  92. )}
  93. </TabsList_Shadcn_>
  94. </div>
  95. <TabsContent_Shadcn_ value="details" className="mt-0 grow min-h-0 overflow-y-auto">
  96. {activeSheetRow && (
  97. <QueryDetail
  98. selectedRow={activeSheetRow}
  99. onClickViewSuggestion={() => onSheetViewChange('indexes')}
  100. onClose={onClose}
  101. />
  102. )}
  103. </TabsContent_Shadcn_>
  104. <TabsContent_Shadcn_ value="indexes" className="mt-0 grow min-h-0 overflow-y-auto">
  105. {activeSheetRow && <QueryIndexes selectedRow={activeSheetRow} />}
  106. </TabsContent_Shadcn_>
  107. <TabsContent_Shadcn_
  108. value="explain"
  109. className="mt-0 grow min-h-0 flex flex-col overflow-hidden"
  110. >
  111. {explainLoadingQuery ? (
  112. <div className="px-6 py-4 flex items-center gap-2 text-sm text-foreground-light">
  113. <Loader2 size={14} className="animate-spin" /> Running EXPLAIN ANALYZE...
  114. </div>
  115. ) : activeSheetRow && explainResults[activeSheetRow.query]?.length > 0 ? (
  116. <>
  117. <div className="flex items-center justify-between px-5 py-2 border-b shrink-0">
  118. <p className="text-xs text-foreground-lighter">EXPLAIN ANALYZE output</p>
  119. <Button
  120. type="default"
  121. size="tiny"
  122. icon={<AiIconAnimation size={14} />}
  123. onClick={() => {
  124. const rows = explainResults[activeSheetRow.query]
  125. const { query, prompt } = buildExplainOptimizationPrompt(
  126. activeSheetRow.query,
  127. rows,
  128. {
  129. mean_time: activeSheetRow.mean_time,
  130. calls: activeSheetRow.calls,
  131. total_time: activeSheetRow.total_time,
  132. }
  133. )
  134. openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
  135. aiSnap.newChat({
  136. sqlSnippets: [{ label: 'Query', content: query }],
  137. initialMessage: prompt,
  138. })
  139. }}
  140. >
  141. Optimize with AI
  142. </Button>
  143. </div>
  144. <div className="flex-1 min-h-0 overflow-y-auto">
  145. <ExplainVisualizer rows={explainResults[activeSheetRow.query]} />
  146. </div>
  147. </>
  148. ) : (
  149. <div className="px-6 py-4 text-sm text-foreground-lighter">
  150. No explain results available.
  151. </div>
  152. )}
  153. </TabsContent_Shadcn_>
  154. </Tabs_Shadcn_>
  155. </SheetContent>
  156. </Sheet>
  157. )
  158. }