import { Book, Maximize2, X } from 'lucide-react' import { useRouter } from 'next/router' import { useState } from 'react' import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { createSqlSnippetSkeletonV2, suffixWithLimit, } from 'components/interfaces/SQLEditor/SQLEditor.utils' import Results from 'components/interfaces/SQLEditor/UtilityPanel/Results' import { SqlRunButton } from 'components/interfaces/SQLEditor/UtilityPanel/RunButton' import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { useExecuteSqlMutation } from 'data/sql/execute-sql-mutation' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { BASE_PATH } from 'lib/constants' import { uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' import { useEditorPanelStateSnapshot } from 'state/editor-panel-state' import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' import { Button, cn, CodeBlock, Command_Shadcn_, CommandEmpty_Shadcn_, CommandGroup_Shadcn_, CommandInput_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, HoverCard_Shadcn_, HoverCardContent_Shadcn_, HoverCardTrigger_Shadcn_, KeyboardShortcut, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, SQL_ICON, } from 'ui' import { Admonition } from 'ui-patterns' import { containsUnknownFunction, isReadOnlySelect } from '../AIAssistantPanel/AIAssistant.utils' import AIEditor from '../AIEditor' import { ButtonTooltip } from '../ButtonTooltip' import { SqlWarningAdmonition } from '../SqlWarningAdmonition' export const EditorPanel = () => { const { value, templates, results, error, initialPrompt, onChange, setValue, setTemplates, setResults, setError, } = useEditorPanelStateSnapshot() const { profile } = useProfile() const { closeSidebar } = useSidebarManagerSnapshot() const sqlEditorSnap = useSqlEditorV2StateSnapshot() const label = 'SQL Editor' const [isInlineEditorHotkeyEnabled] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.HOTKEY_SIDEBAR(SIDEBAR_KEYS.EDITOR_PANEL), true ) const [isAIAssistantHotkeyEnabled] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.HOTKEY_SIDEBAR(SIDEBAR_KEYS.AI_ASSISTANT), true ) const currentValue = value || '' const { ref } = useParams() const router = useRouter() const { data: project } = useSelectedProjectQuery() const { data: org } = useSelectedOrganizationQuery() const [showWarning, setShowWarning] = useState<'hasWriteOperation' | 'hasUnknownFunctions'>() const [showResults, setShowResults] = useState(true) const [isTemplatesOpen, setIsTemplatesOpen] = useState(false) const errorHeader = error?.formattedError?.split('\n')?.filter((x: string) => x.length > 0)?.[0] const errorContent = 'formattedError' in (error || {}) ? error?.formattedError ?.split('\n') ?.filter((x: string) => x.length > 0) ?.slice(1) ?? [] : [error?.message ?? ''] const { mutate: executeSql, isPending: isExecuting } = useExecuteSqlMutation({ onSuccess: async (res) => { setResults(res.result) setError(undefined) }, onError: (mutationError) => { setError(mutationError) setResults([]) }, }) const onExecuteSql = (skipValidation = false) => { setError(undefined) setShowWarning(undefined) setResults(undefined) if (currentValue.length === 0) return if (!skipValidation) { const isReadOnlySelectSQL = isReadOnlySelect(currentValue) if (!isReadOnlySelectSQL) { const hasUnknownFunctions = containsUnknownFunction(currentValue) setShowWarning(hasUnknownFunctions ? 'hasUnknownFunctions' : 'hasWriteOperation') return } } executeSql({ sql: suffixWithLimit(currentValue, 100), projectRef: project?.ref, connectionString: project?.connectionString, handleError: (executeError) => { throw executeError }, contextualInvalidation: true, }) } const handleChange = (value: string) => { setValue(value) onChange?.(value) } const onSelectTemplate = (content: string) => { handleChange(content) setIsTemplatesOpen(false) } const handleClosePanel = () => { closeSidebar(SIDEBAR_KEYS.EDITOR_PANEL) setTemplates([]) setError(undefined) setShowWarning(undefined) setShowResults(true) } return (
{label}
{templates.length > 0 && ( No templates found. {templates.map((template) => ( onSelectTemplate(template.content)} className="cursor-pointer" >

{template.name}

{template.description}

))}
)} } tooltip={{ content: { side: 'bottom', text: 'Expand to SQL editor', }, }} onClick={() => { if (!ref) return console.error('Project ref is required') if (!project) { console.error('Project is required') return } if (!profile) { console.error('Profile is required') return } const snippet = createSqlSnippetSkeletonV2({ id: uuidv4(), name: 'New query', sql: currentValue, owner_id: profile.id, project_id: project.id, }) sqlEditorSnap.addSnippet({ projectRef: ref, snippet }) sqlEditorSnap.addNeedsSaving(snippet.id) router.push(`/project/${ref}/sql/${snippet.id}`) handleClosePanel() }} /> } tooltip={{ content: { side: 'bottom', text: (
Close Editor {isInlineEditorHotkeyEnabled && }
), }, }} />
{error !== undefined && (
{errorContent.length > 0 ? ( errorContent.map((errorText: string, i: number) => (
                        {errorText}
                      
)) ) : (

{error?.error}

)}
} />
)} {showWarning && ( setShowWarning(undefined)} onConfirm={() => { setShowWarning(undefined) onExecuteSql(true) }} /> )} {results !== undefined && results.length > 0 && (
{showResults && (
)}
{results.length} rows{results.length >= 100 && ` (Limited to only 100 rows)`}
)} {results !== undefined && results.length === 0 && !error && (

Success. No rows returned.

)}
) } export default EditorPanel