Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve snippet organization in SQL Editor #27881

Merged
merged 43 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift click to select a range
30bd0f3
Init changes for sql fodlers
joshenlim Jul 4, 2024
ff1aa0a
Merge branch 'master' into feat/sql-folders-new
joshenlim Jul 4, 2024
cfb9c6b
Merge master resolve conflicts
joshenlim Jul 4, 2024
67fac44
Added upsert logic in sql-editor-v2 valtio, hooked up with templates …
joshenlim Jul 4, 2024
a994bcf
Do up logic for creating new snippets by typing in /new or by clickin…
joshenlim Jul 4, 2024
61a57a9
Do up logic for updating and deleting snippets
joshenlim Jul 4, 2024
a49d1c3
Do up logic for favourites and shared snippets
joshenlim Jul 5, 2024
a839c50
Do up logic for favourites and shared snippets
joshenlim Jul 5, 2024
31b05e8
Fix
joshenlim Jul 5, 2024
36d4fda
Fix saving indicator, add empty states for favorites and shared snippets
joshenlim Jul 5, 2024
5754e44
Implement sorting
joshenlim Jul 5, 2024
d02f065
Some minor QOL improvements
joshenlim Jul 5, 2024
96cb9ab
Minor fix on empty state for private snippets
joshenlim Jul 5, 2024
57ad789
Add delete folder mutation
joshenlim Jul 5, 2024
47164d7
Implement create and update folder
joshenlim Jul 8, 2024
60da9f7
Fix reinstate with AI renaming for new snippets under folder
joshenlim Jul 8, 2024
f2f3ebc
Support controlled multi select behaviour in private snippets
joshenlim Jul 8, 2024
169f05b
Undo changes to tree-view-multi-select
joshenlim Jul 8, 2024
ff09863
Support bulk deletes
joshenlim Jul 8, 2024
a0b9e54
Support moving queries rendering queries in folders
joshenlim Jul 9, 2024
33cd45c
Support deleting folders and creating a new folder when moving a query
joshenlim Jul 9, 2024
0bf4769
Fix bug where renaming query removes content
joshenlim Jul 9, 2024
d198936
Add initial loading state in sql editor nav handle fallback if cann…
joshenlim Jul 9, 2024
34f55f7
Fix some spelling
joshenlim Jul 9, 2024
6c39eed
Fix TS issue in sql folders mutation keys
joshenlim Jul 9, 2024
0db5380
Fix toggling favorite
joshenlim Jul 9, 2024
ba0b1ec
Lint
joshenlim Jul 9, 2024
f29d3c4
Revert fallback behaviour in ]id] for now
joshenlim Jul 9, 2024
5b7d5f5
Fix favorites and shared snippets not showing
joshenlim Jul 9, 2024
44e7e7f
Fix moving currently opened snippet leads to loading
joshenlim Jul 12, 2024
ed8da76
Support bulk moving
joshenlim Jul 12, 2024
824810b
Improve multi select logic a little
joshenlim Jul 12, 2024
f4aaaf5
Merge master resolve conflicts
joshenlim Jul 12, 2024
3df8008
Nit lint
joshenlim Jul 12, 2024
6362073
Merge branch 'master' into feat/sql-folders-new
joshenlim Jul 15, 2024
8bc49ba
Reinstate AI retitling for untitled snippets when running query
joshenlim Jul 15, 2024
e6bf586
Remove hardcode in useAFlag
joshenlim Jul 15, 2024
d77384a
Support creating new snippet in a folder directly
joshenlim Jul 15, 2024
f88da3e
Fix sharing snippets that are within a folder
joshenlim Jul 15, 2024
2a9aa88
Fix sharing snippets within a folder
joshenlim Jul 15, 2024
a2f774f
Fix favorite
joshenlim Jul 15, 2024
b200b98
Add loading state when fetching folder contents
joshenlim Jul 16, 2024
d431629
Fix favoriting snippets in folders
joshenlim Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Do up logic for favourites and shared snippets
  • Loading branch information
joshenlim committed Jul 5, 2024
commit a839c50a24d903895177c094a716296db921ba07
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 7,6 @@ const pricingMetricBytes = [
]

export const formatUsage = (pricingMetric: PricingMetric, usage: number) => {
console.log({ pricingMetric, usage })
if (pricingMetricBytes.includes(pricingMetric)) {
return (usage / 1e9).toFixed(2).toLocaleString()
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 1,14 @@
import type { ModalProps } from '@ui/components/Modal/Modal'
import { snakeCase } from 'lodash'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { Button, CodeBlock, IconExternalLink, Modal, Tabs } from 'ui'

import type { ModalProps } from '@ui/components/Modal/Modal'
import TwoOptionToggle from 'components/ui/TwoOptionToggle'
import { useFlag } from 'hooks/ui/useFlag'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { Button, CodeBlock, Modal, Tabs } from 'ui'
import { Markdown } from '../Markdown'
import {
generateFileCliCommand,
Expand All @@ -19,8 22,11 @@ export interface DownloadSnippetModalProps extends ModalProps {

const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => {
const snap = useSqlEditorStateSnapshot()
const snippet = snap.snippets[id].snippet
const migrationName = snakeCase(snippet.name)
const snapV2 = useSqlEditorV2StateSnapshot()
const enableFolders = useFlag('sqlFolderOrganization')

const snippet = enableFolders ? snapV2.snippets[id]?.snippet : snap.snippets[id].snippet
const migrationName = snakeCase(snippet?.name)

const [selectedView, setSelectedView] = useState<'CLI' | 'NPM'>('CLI')

Expand Down Expand Up @@ -60,7 66,7 @@ const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => {
header={<p>Download snippet as local migration file via the Supabase CLI.</p>}
{...props}
>
<div className="flex flex-col items-start justify-between gap-4 relative">
<div className="flex flex-col items-start justify-between gap-4 relative pt-2">
<Tabs type="underlined" listClassNames="pl-5">
{SNIPPETS.map((snippet) => {
return (
Expand All @@ -75,7 81,7 @@ const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => {
/>
</div>
<TwoOptionToggle
width={75}
width={50}
options={['CLI', 'NPM']}
activeOption={selectedView}
borderOverride="border-muted"
Expand All @@ -100,7 106,7 @@ const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => {
<Modal.Content className="w-full flex items-center justify-between pt-0">
<p className="text-xs text-lighter">Run this command from your project directory</p>
<div className="flex justify-between items-center gap-x-2">
<Button asChild type="default" icon={<IconExternalLink size={14} strokeWidth={1.5} />}>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/cli/local-development#database-migrations"
target="_blank"
Expand All @@ -110,7 116,7 @@ const DownloadSnippetModal = ({ id, ...props }: DownloadSnippetModalProps) => {
</Link>
</Button>

<Button asChild type="default" icon={<IconExternalLink size={14} strokeWidth={1.5} />}>
<Button asChild type="default" icon={<ExternalLink size={14} strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/cli/local-development"
target="_blank"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 99,15 @@ const MonacoEditor = ({

// [Joshen] Also needs updating here
const debouncedSetSql = debounce((id, value) => {
snap.setSql(id, value)
if (enableFolders) snapV2.setSql(id, value)
else snap.setSql(id, value)
}, 1000)

function handleEditorChange(value: string | undefined) {
const snippetCheck = enableFolders ? snapV2.snippets[id] : snap.snippets[id]

if (id && value) {
if (snap.snippets[id]) {
if (snippetCheck) {
debouncedSetSql(id, value)
} else {
if (ref && profile !== undefined && project !== undefined) {
Expand Down
12 changes: 8 additions & 4 deletions apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 170,10 @@ const SQLEditor = () => {

const { mutate: execute, isLoading: isExecuting } = useExecuteSqlMutation({
onSuccess(data, vars) {
if (id) snap.addResult(id, data.result, vars.autoLimit)
if (id) {
if (enableFolders) snapV2.addResult(id, data.result, vars.autoLimit)
else snap.addResult(id, data.result, vars.autoLimit)
}

// Refetching instead of invalidating since invalidate doesn't work with `enabled` flag
refetchEntityDefinitions()
Expand Down Expand Up @@ -208,7 211,8 @@ const SQLEditor = () => {
}
}

snap.addResultError(id, error, vars.autoLimit)
if (enableFolders) snapV2.addResultError(id, error, vars.autoLimit)
else snap.addResultError(id, error, vars.autoLimit)
}
},
})
Expand Down Expand Up @@ -388,8 392,8 @@ const SQLEditor = () => {

const onDebug = useCallback(async () => {
try {
const snippet = snap.snippets[id]
const result = snap.results[id]?.[0]
const snippet = enableFolders ? snapV2.snippets[id] : snap.snippets[id]
const result = enableFolders ? snapV2.results[id]?.[0] : snap.results[id]?.[0]

const { solution, sql } = await debugSql({
sql: snippet.snippet.content.sql.replace(sqlAiDisclaimerComment, '').trim(),
Expand Down
16 changes: 10 additions & 6 deletions apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 55,7 @@ export const createSqlSnippetSkeletonV2 = ({
project_id,
name,
folder_id: undefined,
favorite: false,
inserted_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
content: {
Expand Down Expand Up @@ -95,20 96,23 @@ export function checkDestructiveQuery(sql: string) {
return destructiveSqlRegex.some((regex) => regex.test(removeCommentsFromSql(sql)))
}

export const generateMigrationCliCommand = (id: string, name: string, isNpx = false) => `
export const generateMigrationCliCommand = (id: string, name: string, isNpx = false) =>
`
${isNpx ? 'npx ' : ''}supabase snippets download ${id} |
${isNpx ? 'npx ' : ''}supabase migration new ${name}
`
`.trim()

export const generateSeedCliCommand = (id: string, isNpx = false) => `
export const generateSeedCliCommand = (id: string, isNpx = false) =>
`
${isNpx ? 'npx ' : ''}supabase snippets download ${id} >> \\
supabase/seed.sql
`
`.trim()

export const generateFileCliCommand = (id: string, name: string, isNpx = false) => `
export const generateFileCliCommand = (id: string, name: string, isNpx = false) =>
`
${isNpx ? 'npx ' : ''}supabase snippets download ${id} > \\
${name}.sql
`
`.trim()

export const compareAsModification = (sqlDiff: ContentDiff) => {
const formattedModified = sqlDiff.modified.replace(sqlAiDisclaimerComment, '').trim()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 8,17 @@ import { GridFooter } from 'components/ui/GridFooter'
import { useKeyboardShortcuts } from 'hooks/deprecated'
import { copyToClipboard } from 'lib/helpers'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { useFlag } from 'hooks/ui/useFlag'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'

const Results = ({ id, rows }: { id: string; rows: readonly any[] }) => {
const SQL_CONTEXT_EDITOR_ID = 'sql-context-menu-' id
const enableFolders = useFlag('sqlFolderOrganization')
const [cellPosition, setCellPosition] = useState<any>(undefined)

const snap = useSqlEditorStateSnapshot()
const results = snap.results[id]?.[0]
const snapV2 = useSqlEditorV2StateSnapshot()
const results = enableFolders ? snapV2.results[id]?.[0] : snap.results[id]?.[0]

const onCopyCell = () => {
if (cellPosition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 18,22 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from 'ui'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { useFlag } from 'hooks/ui/useFlag'

export type ResultsDropdownProps = {
id: string
}

const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
const { project } = useProjectContext()
const snap = useSqlEditorStateSnapshot()
const telemetryProps = useTelemetryProps()
const result = snap.results?.[id]?.[0] ?? undefined

const snap = useSqlEditorStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
const enableFolders = useFlag('sqlFolderOrganization')

const result = enableFolders ? snapV2.results?.[id]?.[0] : snap.results?.[id]?.[0] ?? undefined
const csvRef = useRef<CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }>(null)
const router = useRouter()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 36,9 @@ import {
cn,
} from 'ui'
import SavingIndicator from './SavingIndicator'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { useFlag } from 'hooks/ui/useFlag'
import { Snippet } from 'data/content/sql-folders-query'

const ROWS_PER_PAGE_OPTIONS = [
{ value: -1, label: 'No limit' },
Expand Down Expand Up @@ -64,16 67,24 @@ const UtilityActions = ({
const os = detectOS()
const client = useQueryClient()
const { project } = useProjectContext()

const snap = useSqlEditorStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
const enableFolders = useFlag('sqlFolderOrganization')

const [isAiOpen] = useLocalStorageQuery(LOCAL_STORAGE_KEYS.SQL_EDITOR_AI_OPEN, true)
const [intellisenseEnabled, setIntellisenseEnabled] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
true
)

const snippet = snap.snippets[id]
const isFavorite = snippet !== undefined ? snippet.snippet.content.favorite : false
const snippet = enableFolders ? snapV2.snippets[id] : snap.snippets[id]
const isFavorite =
snippet !== undefined
? enableFolders
? (snippet.snippet as Snippet).favorite
: snippet.snippet.content.favorite
: false

const toggleIntellisense = () => {
setIntellisenseEnabled(!intellisenseEnabled)
Expand All @@ -83,7 94,12 @@ const UtilityActions = ({
}

const addFavorite = async () => {
snap.addFavorite(id)
if (enableFolders) {
snapV2.addFavorite(id)
} else {
snap.addFavorite(id)
}

client.setQueryData<ContentData>(
contentKeys.list(project?.ref),
(oldData: ContentData | undefined) => {
Expand All @@ -106,7 122,12 @@ const UtilityActions = ({
}

const removeFavorite = async () => {
snap.removeFavorite(id)
if (enableFolders) {
snapV2.removeFavorite(id)
} else {
snap.removeFavorite(id)
}

client.setQueryData<ContentData>(
contentKeys.list(project?.ref),
(oldData: ContentData | undefined) => {
Expand Down Expand Up @@ -232,13 253,20 @@ const UtilityActions = ({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button type="default" iconRight={<ChevronDown size={14} />}>
{ROWS_PER_PAGE_OPTIONS.find((opt) => opt.value === snap.limit)?.label}
{
ROWS_PER_PAGE_OPTIONS.find(
(opt) => opt.value === (enableFolders ? snapV2.limit : snap.limit)
)?.label
}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-42">
<DropdownMenuRadioGroup
value={snap.limit.toString()}
onValueChange={(val) => snap.setLimit(Number(val))}
value={enableFolders ? snapV2.limit.toString() : snap.limit.toString()}
onValueChange={(val) => {
if (enableFolders) snapV2.setLimit(Number(val))
else snap.setLimit(Number(val))
}}
>
{ROWS_PER_PAGE_OPTIONS.map((option) => (
<DropdownMenuRadioItem key={option.label} value={option.value.toString()}>
Expand All @@ -251,7 279,13 @@ const UtilityActions = ({

<div className="flex items-center justify-between gap-x-2">
<div className="flex items-center">
<DatabaseSelector variant="connected-on-right" onSelectId={() => snap.resetResult(id)} />
<DatabaseSelector
variant="connected-on-right"
onSelectId={() => {
if (enableFolders) snapV2.resetResult(id)
else snap.resetResult(id)
}}
/>
<RoleImpersonationPopover serviceRoleLabel="postgres" variant="connected-on-both" />
<Button
onClick={() => executeQuery()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 10,9 @@ import { ChartConfig } from './ChartConfig'
import ResultsDropdown from './ResultsDropdown'
import UtilityActions from './UtilityActions'
import UtilityTabResults from './UtilityTabResults'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { useFlag } from 'hooks/ui/useFlag'
import { Snippet } from 'data/content/sql-folders-query'

export type UtilityPanelProps = {
id: string
Expand Down Expand Up @@ -41,11 44,14 @@ const UtilityPanel = ({
}: UtilityPanelProps) => {
const { ref } = useParams()
const queryClient = useQueryClient()

const snap = useSqlEditorStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
const enableFolders = useFlag('sqlFolderOrganization')

const snippet = snap.snippets[id]?.snippet
const queryKeys = contentKeys.list(ref)
const result = snap.results[id]?.[0]
const result = enableFolders ? snapV2.results[id]?.[0] : snap.results[id]?.[0]

const { mutate: upsertContent } = useContentUpsertMutation({
invalidateQueriesOnSuccess: false,
Expand All @@ -68,7 74,8 @@ const UtilityPanel = ({
},
}

snap.updateSnippet(id, newSnippet)
if (enableFolders) snapV2.updateSnippet(id, newSnippet as Snippet)
else snap.updateSnippet(id, newSnippet)
},
onError: async (err, newContent, context) => {
toast.error(`Failed to update chart. Please try again.`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 8,8 @@ import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { AiIconAnimation, Button } from 'ui'
import Results from './Results'
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
import { useFlag } from 'hooks/ui/useFlag'

export type UtilityTabResultsProps = {
id: string
Expand All @@ -25,11 27,14 @@ const UtilityTabResults = ({
onDebug,
}: UtilityTabResultsProps) => {
const { ref } = useParams()
const snap = useSqlEditorStateSnapshot()
const state = useDatabaseSelectorStateSnapshot()
const organization = useSelectedOrganization()

const result = snap.results[id]?.[0]
const snap = useSqlEditorStateSnapshot()
const snapV2 = useSqlEditorV2StateSnapshot()
const enableFolders = useFlag('sqlFolderOrganization')

const result = enableFolders ? snapV2.results[id]?.[0] : snap.results[id]?.[0]
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })

// Customers on HIPAA plans should not have access to Supabase AI
Expand Down
Loading