UtilityPanel.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // @ts-nocheck
  2. import { useParams } from 'common'
  3. import { toast } from 'sonner'
  4. import { Tabs_Shadcn_, TabsContent_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_ } from 'ui'
  5. import { ChartConfig } from './ChartConfig'
  6. import { UtilityActions } from './UtilityActions'
  7. import { UtilityTabExplain } from './UtilityTabExplain'
  8. import { UtilityTabResults } from './UtilityTabResults'
  9. import { DownloadResultsButton } from '@/components/ui/DownloadResultsButton'
  10. import { useContentUpsertMutation } from '@/data/content/content-upsert-mutation'
  11. import { Snippet } from '@/data/content/sql-folders-query'
  12. import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
  13. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  14. import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2'
  15. export type UtilityPanelProps = {
  16. id: string
  17. isExecuting?: boolean
  18. isExplainExecuting?: boolean
  19. isDebugging?: boolean
  20. isDisabled?: boolean
  21. hasSelection: boolean
  22. prettifyQuery: () => void
  23. executeQuery: () => void
  24. executeExplainQuery: () => void
  25. onDebug: () => void
  26. buildDebugPrompt: () => string
  27. activeTab?: string
  28. onActiveTabChange?: (tab: string) => void
  29. }
  30. const DEFAULT_CHART_CONFIG: ChartConfig = {
  31. type: 'bar',
  32. cumulative: false,
  33. xKey: '',
  34. yKey: '',
  35. showLabels: false,
  36. showGrid: false,
  37. }
  38. export const UtilityPanel = ({
  39. id,
  40. isExecuting,
  41. isExplainExecuting,
  42. isDebugging,
  43. isDisabled,
  44. hasSelection,
  45. prettifyQuery,
  46. executeQuery,
  47. executeExplainQuery,
  48. onDebug,
  49. buildDebugPrompt,
  50. activeTab = 'results',
  51. onActiveTabChange,
  52. }: UtilityPanelProps) => {
  53. const { ref } = useParams()
  54. const { data: org } = useSelectedOrganizationQuery()
  55. const snapV2 = useSqlEditorV2StateSnapshot()
  56. const snippet = snapV2.snippets[id]?.snippet
  57. const result = snapV2.results[id]?.[0]
  58. const handleTabChange = (tab: string) => {
  59. // When switching to the explain tab, trigger the explain query
  60. if (tab === 'explain') {
  61. executeExplainQuery()
  62. }
  63. onActiveTabChange?.(tab)
  64. }
  65. const { mutate: sendEvent } = useSendEventMutation()
  66. const { mutate: upsertContent } = useContentUpsertMutation({
  67. invalidateQueriesOnSuccess: false,
  68. // Optimistic update to the cache
  69. onMutate: async (newContentSnippet) => {
  70. const { payload } = newContentSnippet
  71. // No need to update the cache for non-SQL content
  72. if (payload.type !== 'sql') return
  73. if (!('chart' in payload.content)) return
  74. const newSnippet = {
  75. ...snippet,
  76. content: {
  77. ...snippet.content,
  78. chart: payload.content.chart,
  79. },
  80. }
  81. snapV2.updateSnippet({ id, snippet: newSnippet as unknown as Snippet })
  82. },
  83. onError: async (_err, _newContent, _context) => {
  84. toast.error(`Failed to update chart. Please try again.`)
  85. },
  86. })
  87. function getChartConfig() {
  88. if (!snippet || snippet.type !== 'sql') {
  89. return DEFAULT_CHART_CONFIG
  90. }
  91. if (!snippet.content?.chart) {
  92. return DEFAULT_CHART_CONFIG
  93. }
  94. return snippet.content.chart
  95. }
  96. const chartConfig = getChartConfig()
  97. function onConfigChange(config: ChartConfig) {
  98. if (!ref || !snippet?.id) return
  99. upsertContent({
  100. projectRef: ref,
  101. payload: {
  102. ...snippet,
  103. id: snippet.id,
  104. description: snippet.description || '',
  105. project_id: snippet.project_id || 0,
  106. content: {
  107. ...snippet.content,
  108. content_id: id,
  109. chart: config,
  110. },
  111. },
  112. })
  113. }
  114. return (
  115. <Tabs_Shadcn_
  116. value={activeTab}
  117. onValueChange={handleTabChange}
  118. className="w-full h-full flex flex-col"
  119. >
  120. <TabsList_Shadcn_ className="flex justify-between gap-2 px-4 overflow-x-auto min-h-[42px]">
  121. <div className="flex items-center gap-4">
  122. <TabsTrigger_Shadcn_ className="py-3 text-xs" value="results">
  123. <span className="translate-y-px">Results</span>
  124. </TabsTrigger_Shadcn_>
  125. <TabsTrigger_Shadcn_ className="py-3 text-xs" value="explain">
  126. <span className="translate-y-px">Explain</span>
  127. </TabsTrigger_Shadcn_>
  128. <TabsTrigger_Shadcn_ className="py-3 text-xs" value="chart">
  129. <span className="translate-y-px">Chart</span>
  130. </TabsTrigger_Shadcn_>
  131. {result?.rows && (
  132. <DownloadResultsButton
  133. type="text"
  134. results={result.rows as any[]}
  135. fileName={`Briven Snippet ${snippet.name}`}
  136. onDownloadAsCSV={() =>
  137. sendEvent({
  138. action: 'sql_editor_result_download_csv_clicked',
  139. groups: { project: ref ?? '', organization: org?.slug ?? '' },
  140. })
  141. }
  142. onCopyAsMarkdown={() => {
  143. sendEvent({
  144. action: 'sql_editor_result_copy_markdown_clicked',
  145. groups: { project: ref ?? '', organization: org?.slug ?? '' },
  146. })
  147. }}
  148. onCopyAsJSON={() => {
  149. sendEvent({
  150. action: 'sql_editor_result_copy_json_clicked',
  151. groups: { project: ref ?? '', organization: org?.slug ?? '' },
  152. })
  153. }}
  154. onCopyAsCSV={() => {
  155. sendEvent({
  156. action: 'sql_editor_result_copy_csv_clicked',
  157. groups: { project: ref ?? '', organization: org?.slug ?? '' },
  158. })
  159. }}
  160. />
  161. )}
  162. </div>
  163. <UtilityActions
  164. id={id}
  165. isExecuting={isExecuting}
  166. isDisabled={isDisabled}
  167. hasSelection={hasSelection}
  168. prettifyQuery={prettifyQuery}
  169. executeQuery={executeQuery}
  170. />
  171. </TabsList_Shadcn_>
  172. <TabsContent_Shadcn_ asChild value="results" className="mt-0 grow">
  173. <UtilityTabResults
  174. id={id}
  175. isExecuting={isExecuting}
  176. isDisabled={isDisabled}
  177. onDebug={onDebug}
  178. buildDebugPrompt={buildDebugPrompt}
  179. isDebugging={isDebugging}
  180. />
  181. </TabsContent_Shadcn_>
  182. <TabsContent_Shadcn_ asChild value="explain" className="mt-0 grow">
  183. <UtilityTabExplain id={id} isExecuting={isExplainExecuting} />
  184. </TabsContent_Shadcn_>
  185. <TabsContent_Shadcn_ asChild value="chart" className="mt-0 grow">
  186. <ChartConfig results={result} config={chartConfig} onConfigChange={onConfigChange} />
  187. </TabsContent_Shadcn_>
  188. </Tabs_Shadcn_>
  189. )
  190. }