Chore/new sql (#37579)

* just redirect to new on sql empty

* clean up

* update template and quickstarts link

* Nit clean up

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Saxon FletcherandJoshen Lim authored and GitHub committed 2025-08-01 17:03:34 +08:00
1 parent 58d6b4b912
commit efaf4d5183
9 files changed
+110 -73

No files matched your search

@@ -32,6 +32,7 @@ export type MonacoEditorProps = {
startLineNumber: number
endLineNumber: number
}) => void
placeholder?: string
}
const MonacoEditor = ({
@@ -39,6 +40,7 @@ const MonacoEditor = ({
editorRef,
monacoRef,
autoFocus = true,
placeholder = '',
className,
executeQuery,
onHasSelection,
@@ -204,6 +206,7 @@ const MonacoEditor = ({
options={{
tabSize: 2,
fontSize: 13,
placeholder,
lineDecorationsWidth: 0,
readOnly: disableEdit,
minimap: { enabled: false },
@@ -718,6 +718,13 @@ export const SQLEditor = () => {
<div key={id} className="w-full h-full relative">
<MonacoEditor
autoFocus
placeholder={
!promptState.isOpen && !editorRef.current?.getValue()
? 'Hit ' +
(os === 'macos' ? 'CMD+K' : `CTRL+K`) +
' to generate query or just start typing'
: ''
}
id={id}
className={cn(isDiffOpen && 'hidden')}
editorRef={editorRef}
@@ -763,19 +770,6 @@ export const SQLEditor = () => {
endLineNumber={promptState.endLineNumber}
/>
)}
<AnimatePresence>
{!promptState.isOpen && !editorRef.current?.getValue() && (
<motion.p
initial={{ y: 5, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 5, opacity: 0 }}
className="text-foreground-lighter absolute bottom-4 left-4 z-10 font-mono text-xs flex items-center gap-1"
>
Hit {os === 'macos' ? <Command size={12} /> : `CTRL+`}K to edit with the
Assistant
</motion.p>
)}
</AnimatePresence>
</div>
</>
)}
@@ -8,7 +8,7 @@ import { toast } from 'sonner'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useProfile } from 'lib/profile'
import { getAppStateSnapshot } from 'state/app-state'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
@@ -28,15 +28,14 @@ import {
InnerSideBarFilterSortDropdown,
InnerSideBarFilterSortDropdownItem,
} from 'ui-patterns/InnerSideMenu'
import { SqlEditorMenuStaticLinks } from './SqlEditorMenuStaticLinks'
import { SearchList } from './SQLEditorNavV2/SearchList'
import { SQLEditorNav } from './SQLEditorNavV2/SQLEditorNav'
export const SQLEditorMenu = () => {
const router = useRouter()
const { profile } = useProfile()
const project = useSelectedProject()
const { ref } = useParams()
const { profile } = useProfile()
const { data: project } = useSelectedProjectQuery()
const snapV2 = useSqlEditorV2StateSnapshot()
const [search, setSearch] = useState('')
@@ -153,14 +152,7 @@ export const SQLEditorMenu = () => {
</DropdownMenu>
</div>
{showSearch ? (
<SearchList search={debouncedSearch} />
) : (
<>
<SqlEditorMenuStaticLinks />
<SQLEditorNav sort={sort} />
</>
)}
{showSearch ? <SearchList search={debouncedSearch} /> : <SQLEditorNav sort={sort} />}
</div>
<div className="p-4 border-t sticky bottom-0 bg-studio">
@@ -0,0 +1,61 @@
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { BookText } from 'lucide-react'
import { useRouter } from 'next/router'
import {
InnerSideMenuCollapsible,
InnerSideMenuCollapsibleContent,
InnerSideMenuCollapsibleTrigger,
} from 'ui-patterns'
import { InnerSideMenuDataItem } from 'ui-patterns/InnerSideMenu'
import { DEFAULT_SECTION_STATE, type SectionState } from './SQLEditorNav.constants'
const OPTIONS = ['templates', 'quickstarts'] as const
export function CommunitySnippetsSection() {
const { ref } = useParams()
const router = useRouter()
const [sectionVisibility, setSectionVisibility] = useLocalStorage<SectionState>(
LOCAL_STORAGE_KEYS.SQL_EDITOR_SECTION_STATE(ref ?? ''),
DEFAULT_SECTION_STATE
)
const { community: showCommunitySnippets } = sectionVisibility
function isPageActive(key: string): boolean {
return router.asPath === `/project/${ref}/sql/${key}`
}
return (
<InnerSideMenuCollapsible
className="px-0"
open={showCommunitySnippets}
onOpenChange={(value) => {
setSectionVisibility({
...(sectionVisibility ?? DEFAULT_SECTION_STATE),
community: value,
})
}}
>
<InnerSideMenuCollapsibleTrigger title="Community" />
<InnerSideMenuCollapsibleContent className="group-data-[state=open]:pt-2">
{OPTIONS.map((pageId) => {
const active = isPageActive(pageId)
return (
<InnerSideMenuDataItem
key={pageId}
title={pageId === 'templates' ? 'Templates' : 'Quickstarts'}
isActive={active}
isOpened={false}
href={`/project/${ref}/sql/${pageId}`}
className="capitalize"
>
<BookText size={16} className="text-foreground-muted" />
{pageId}
</InnerSideMenuDataItem>
)
})}
</InnerSideMenuCollapsibleContent>
</InnerSideMenuCollapsible>
)
}
@@ -0,0 +1,13 @@
export type SectionState = {
shared: boolean
favorite: boolean
private: boolean
community: boolean
}
export const DEFAULT_SECTION_STATE: SectionState = {
shared: false,
favorite: false,
private: true,
community: true,
}
@@ -20,7 +20,7 @@ import { useSQLSnippetFoldersDeleteMutation } from 'data/content/sql-folders-del
import { Snippet, SnippetFolder, useSQLSnippetFoldersQuery } from 'data/content/sql-folders-query'
import { useSqlSnippetsQuery } from 'data/content/sql-snippets-query'
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useProfile } from 'lib/profile'
import uuidv4 from 'lib/uuid'
import {
@@ -30,7 +30,7 @@ import {
} from 'state/sql-editor-v2'
import { createTabId, useTabsStateSnapshot } from 'state/tabs'
import { SqlSnippets } from 'types'
import { Separator, TreeView } from 'ui'
import { TreeView } from 'ui'
import {
InnerSideBarEmptyPanel,
InnerSideMenuCollapsible,
@@ -39,7 +39,9 @@ import {
InnerSideMenuSeparator,
} from 'ui-patterns'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { CommunitySnippetsSection } from './CommunitySnippetsSection'
import SQLEditorLoadingSnippets from './SQLEditorLoadingSnippets'
import { DEFAULT_SECTION_STATE, type SectionState } from './SQLEditorNav.constants'
import { formatFolderResponseForTreeView, getLastItemIds, ROOT_NODE } from './SQLEditorNav.utils'
import { SQLEditorTreeViewItem } from './SQLEditorTreeViewItem'
@@ -47,18 +49,14 @@ interface SQLEditorNavProps {
sort?: 'inserted_at' | 'name'
}
type SectionState = { shared: boolean; favorite: boolean; private: boolean }
const DEFAULT_SECTION_STATE: SectionState = { shared: false, favorite: false, private: true }
export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => {
const router = useRouter()
const { ref: projectRef, id } = useParams()
const { profile } = useProfile()
const project = useSelectedProject()
const snapV2 = useSqlEditorV2StateSnapshot()
const { data: project } = useSelectedProjectQuery()
const tabs = useTabsStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
const [sectionVisibility, setSectionVisibility] = useLocalStorage<SectionState>(
LOCAL_STORAGE_KEYS.SQL_EDITOR_SECTION_STATE(projectRef ?? ''),
@@ -545,6 +543,8 @@ export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => {
return (
<>
<InnerSideMenuSeparator />
<InnerSideMenuCollapsible
className="px-0"
open={showSharedSnippets}
@@ -833,7 +833,11 @@ export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => {
</InnerSideMenuCollapsibleContent>
</InnerSideMenuCollapsible>
<Separator />
<InnerSideMenuSeparator />
<CommunitySnippetsSection />
<InnerSideMenuSeparator />
<RenameQueryModal
snippet={selectedSnippetToRename}
@@ -1,34 +0,0 @@
import { useParams } from 'common'
import { useRouter } from 'next/router'
import { InnerSideMenuDataItem } from 'ui-patterns/InnerSideMenu'
const OPTIONS = ['templates', 'quickstarts'] as const
export function SqlEditorMenuStaticLinks() {
const { ref } = useParams()
const router = useRouter()
function isPageActive(key: string): boolean {
return router.asPath === `/project/${ref}/sql/${key}`
}
return (
<div>
{OPTIONS.map((pageId) => {
const active = isPageActive(pageId)
return (
<InnerSideMenuDataItem
key={pageId}
title="Templates"
isActive={active}
isOpened={false}
href={`/project/${ref}/sql/${pageId}`}
className="capitalize"
>
{pageId}
</InnerSideMenuDataItem>
)
})}
</div>
)
}
@@ -8,6 +8,7 @@ import { EntityTypeIcon } from 'components/ui/EntityTypeIcon'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useTabsStateSnapshot, type Tab } from 'state/tabs'
import { cn, TabsTrigger_Shadcn_ } from 'ui'
import { useEditorType } from '../editors/EditorsLayout.hooks'
/**
* Individual draggable tab component that handles:
@@ -28,8 +29,9 @@ export const SortableTab = ({
openTabs: Tab[]
onClose: (id: string) => void
}) => {
const { selectedSchema: currentSchema } = useQuerySchemaState()
const editor = useEditorType()
const tabs = useTabsStateSnapshot()
const { selectedSchema: currentSchema } = useQuerySchemaState()
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: tab.id,
})
@@ -44,8 +46,8 @@ export const SortableTab = ({
const shouldShowSchema = useMemo(() => {
// For both table and schema tabs, show schema if:
// Any tab has a different schema than the current schema parameter
return openTabs.some((t) => t.metadata?.schema !== currentSchema)
}, [openTabs, currentSchema])
return openTabs.some((t) => editor === 'table' && t.metadata?.schema !== currentSchema)
}, [openTabs, currentSchema, editor])
// Create a motion version of TabsTrigger while preserving all functionality
// const MotionTabsTrigger = motion(TabsTrigger_Shadcn_)
@@ -6,7 +6,6 @@ import DefaultLayout from 'components/layouts/DefaultLayout'
import { EditorBaseLayout } from 'components/layouts/editors/EditorBaseLayout'
import SQLEditorLayout from 'components/layouts/SQLEditorLayout/SQLEditorLayout'
import { SQLEditorMenu } from 'components/layouts/SQLEditorLayout/SQLEditorMenu'
import { NewTab } from 'components/layouts/Tabs/NewTab'
import { useAppStateSnapshot } from 'state/app-state'
import { useTabsStateSnapshot } from 'state/tabs'
import type { NextPageWithLayout } from 'types'
@@ -26,10 +25,13 @@ const TableEditorPage: NextPageWithLayout = () => {
} else if (lastTabId) {
const lastTab = store.tabsMap[lastTabId]
if (lastTab) router.push(`/project/${projectRef}/sql/${lastTab.id.replace('sql-', '')}`)
} else {
router.push(`/project/${projectRef}/sql/new`)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <NewTab />
return null
}
TableEditorPage.getLayout = (page) => (