diff --git a/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx b/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx index a98c4ac0a2b..f4316b09b04 100644 --- a/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/MonacoEditor.tsx @@ -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 }, diff --git a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx index 0efecee1d43..708e72eb52b 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx @@ -718,6 +718,13 @@ export const SQLEditor = () => {
{ endLineNumber={promptState.endLineNumber} /> )} - - {!promptState.isOpen && !editorRef.current?.getValue() && ( - - Hit {os === 'macos' ? : `CTRL+`}K to edit with the - Assistant - - )} -
)} diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx index f8722f68179..901e6ed8701 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx @@ -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 = () => { - {showSearch ? ( - - ) : ( - <> - - - - )} + {showSearch ? : }
diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/CommunitySnippetsSection.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/CommunitySnippetsSection.tsx new file mode 100644 index 00000000000..5466d8c7b2f --- /dev/null +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/CommunitySnippetsSection.tsx @@ -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( + 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 ( + { + setSectionVisibility({ + ...(sectionVisibility ?? DEFAULT_SECTION_STATE), + community: value, + }) + }} + > + + + {OPTIONS.map((pageId) => { + const active = isPageActive(pageId) + return ( + + + {pageId} + + ) + })} + + + ) +} diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.constants.ts b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.constants.ts new file mode 100644 index 00000000000..72f80c44725 --- /dev/null +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.constants.ts @@ -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, +} diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx index 55b8dee51f7..3a189539e11 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx @@ -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( LOCAL_STORAGE_KEYS.SQL_EDITOR_SECTION_STATE(projectRef ?? ''), @@ -545,6 +543,8 @@ export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => { return ( <> + + { - + + + + + - {OPTIONS.map((pageId) => { - const active = isPageActive(pageId) - return ( - - {pageId} - - ) - })} -
- ) -} diff --git a/apps/studio/components/layouts/Tabs/SortableTab.tsx b/apps/studio/components/layouts/Tabs/SortableTab.tsx index 11cab9c7095..b763b61a6ff 100644 --- a/apps/studio/components/layouts/Tabs/SortableTab.tsx +++ b/apps/studio/components/layouts/Tabs/SortableTab.tsx @@ -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_) diff --git a/apps/studio/pages/project/[ref]/sql/index.tsx b/apps/studio/pages/project/[ref]/sql/index.tsx index b76303b8010..893eee8c440 100644 --- a/apps/studio/pages/project/[ref]/sql/index.tsx +++ b/apps/studio/pages/project/[ref]/sql/index.tsx @@ -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 + return null } TableEditorPage.getLayout = (page) => (