import { useState, useCallback, useEffect } from 'react' import { PluginElementRenderProps, useYooptaEditor, Elements, useYooptaReadOnly, } from '@yoopta/editor' import { Play, ChevronDown, ChevronUp, Table2, BarChart3, Loader2, Pencil } from 'lucide-react' import { useMutation } from '@tanstack/react-query' import { useParams } from 'common' import { executeSql } from 'data/sql/execute-sql-query' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import Results from 'components/interfaces/SQLEditor/UtilityPanel/Results' import { Button, cn, Input_Shadcn_ } from 'ui' import { BlockViewConfiguration } from 'components/ui/QueryBlock/BlockViewConfiguration' import { DEFAULT_CHART_CONFIG, type ChartConfig } from 'components/ui/QueryBlock/QueryBlock.types' import { SQLQueryChart } from './SQLQueryChart' export interface SQLQueryElementProps { sql: string label: string chartConfig: ChartConfig results?: any[] error?: string } export const SQLQueryElement = ({ element, children, attributes, blockId, }: PluginElementRenderProps) => { const editor = useYooptaEditor() const isReadOnly = useYooptaReadOnly() const { ref: projectRef } = useParams() const state = useDatabaseSelectorStateSnapshot() const props = element.props as SQLQueryElementProps const { sql = '', label = 'Untitled Query', chartConfig = DEFAULT_CHART_CONFIG } = props const [localSql, setLocalSql] = useState(sql) const [localLabel, setLocalLabel] = useState(label) const [localChartConfig, setLocalChartConfig] = useState(chartConfig) const [showSql, setShowSql] = useState(true) const [isEditingLabel, setIsEditingLabel] = useState(false) const [results, setResults] = useState(props.results) const [error, setError] = useState(props.error) // Sync local state with element props useEffect(() => { setLocalSql(sql) setLocalLabel(label) setLocalChartConfig(chartConfig) setResults(props.results) setError(props.error) }, [sql, label, chartConfig, props.results, props.error]) const { mutate: executeQuery, isPending: isExecuting } = useMutation({ mutationFn: async (query: string) => { const result = await executeSql({ projectRef: projectRef!, connectionString: state.selectedDatabaseId, sql: query, }) return result }, onSuccess: (data) => { const resultRows = Array.isArray(data.result) ? data.result : [] setResults(resultRows) setError(undefined) // Update element with results Elements.updateElement(editor, blockId, { type: 'sql-query', props: { ...props, sql: localSql, label: localLabel, results: resultRows, error: undefined, }, }) }, onError: (err: any) => { const errorMessage = err?.message || 'An error occurred while executing the query' setError(errorMessage) setResults(undefined) // Update element with error Elements.updateElement(editor, blockId, { type: 'sql-query', props: { ...props, sql: localSql, label: localLabel, results: undefined, error: errorMessage, }, }) }, }) const handleRunQuery = useCallback(() => { if (!localSql.trim() || !projectRef) return executeQuery(localSql) }, [localSql, projectRef, executeQuery]) const handleSqlChange = useCallback( (e: React.ChangeEvent) => { const newSql = e.target.value setLocalSql(newSql) // Debounced update to element Elements.updateElement(editor, blockId, { type: 'sql-query', props: { ...props, sql: newSql, }, }) }, [editor, blockId, props] ) const handleLabelChange = useCallback((e: React.ChangeEvent) => { const newLabel = e.target.value setLocalLabel(newLabel) }, []) const handleLabelBlur = useCallback(() => { setIsEditingLabel(false) Elements.updateElement(editor, blockId, { type: 'sql-query', props: { ...props, label: localLabel, }, }) }, [editor, blockId, props, localLabel]) const handleChartConfigChange = useCallback( (config: Partial) => { const newConfig = { ...localChartConfig, ...config } setLocalChartConfig(newConfig) Elements.updateElement(editor, blockId, { type: 'sql-query', props: { ...props, chartConfig: newConfig, }, }) }, [editor, blockId, props, localChartConfig] ) const hasResults = Array.isArray(results) && results.length > 0 const view = localChartConfig.view || 'table' return (
{/* Header */}
{isEditingLabel && !isReadOnly ? ( { if (e.key === 'Enter') { handleLabelBlur() } }} className="h-6 text-sm font-medium max-w-[200px]" autoFocus /> ) : ( )} {!isReadOnly && !isEditingLabel && ( )}
{/* SQL Editor */} {showSql && (