chore(studio): fix param on load

This commit is contained in:
Francesco Sansalvadore committed 2025-11-06 18:26:18 +01:00
1 parent 57b1755cb9
commit 2dcdb1fda1
4 files changed
+200 -158

No files matched your search

@@ -1,6 +1,7 @@
import { Edit, MoreVertical, Search, Trash } from 'lucide-react'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { toast } from 'sonner'
import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
@@ -45,7 +46,6 @@ export const EnumeratedTypes = () => {
'edit',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const [selectedTypeToDelete, setSelectedTypeToDelete] = useQueryState(
'delete',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
@@ -55,6 +55,33 @@ export const EnumeratedTypes = () => {
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const typeToEdit = data?.find((type: EnumeratedType) => type.id.toString() === selectedTypeToEdit)
const showTypeToEdit = selectedTypeToEdit !== '' && !!typeToEdit
const typeToEditNotFound = selectedTypeToEdit !== '' && !typeToEdit
const typeToDelete = data?.find(
(type: EnumeratedType) => type.id.toString() === selectedTypeToDelete
)
const showTypeToDelete = selectedTypeToDelete !== '' && !!typeToDelete
const typeToDeleteNotFound = selectedTypeToDelete !== '' && !typeToDelete
// Error handling if edit panel is open and type is not found
useEffect(() => {
if (!isLoading && typeToEditNotFound) {
toast.error('Enumerated Type not found')
setSelectedTypeToEdit('')
}
}, [selectedTypeToEdit, typeToEdit, isLoading])
// Error handling if delete panel is open and type is not found
useEffect(() => {
if (!isLoading && typeToDeleteNotFound) {
toast.error('Enumerated Type not found')
setSelectedTypeToDelete('')
}
}, [selectedTypeToDelete, typeToDelete])
const enumeratedTypes = (data ?? []).filter((type) => type.enums.length > 0)
const filteredEnumeratedTypes =
search.length > 0
@@ -193,14 +220,14 @@ export const EnumeratedTypes = () => {
/>
<EditEnumeratedTypeSidePanel
visible={!!selectedTypeToEdit}
selectedEnumeratedType={data?.find((type) => type.id.toString() === selectedTypeToEdit)}
visible={showTypeToEdit}
selectedEnumeratedType={typeToEdit}
onClose={() => setSelectedTypeToEdit('')}
/>
<DeleteEnumeratedTypeModal
visible={!!selectedTypeToDelete}
selectedEnumeratedType={data?.find((type) => type.id.toString() === selectedTypeToDelete)}
visible={showTypeToDelete}
selectedEnumeratedType={typeToDelete}
onClose={() => setSelectedTypeToDelete('')}
/>
</div>
@@ -6,7 +6,6 @@ import { useRouter } from 'next/router'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { useDatabaseFunctionsQuery } from 'data/database-functions/database-functions-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
@@ -21,6 +20,7 @@ import {
TableCell,
TableRow,
} from 'ui'
import type { DatabaseFunction } from 'data/database-functions/database-functions-query'
interface FunctionListProps {
schema: string
@@ -31,6 +31,7 @@ interface FunctionListProps {
duplicateFunction: (fn: any) => void
editFunction: (fn: any) => void
deleteFunction: (fn: any) => void
functions: DatabaseFunction[]
}
const FunctionList = ({
@@ -42,17 +43,13 @@ const FunctionList = ({
duplicateFunction = noop,
editFunction = noop,
deleteFunction = noop,
functions,
}: FunctionListProps) => {
const router = useRouter()
const { data: selectedProject } = useSelectedProjectQuery()
const aiSnap = useAiAssistantStateSnapshot()
const { openSidebar } = useSidebarManagerSnapshot()
const { data: functions } = useDatabaseFunctionsQuery({
projectRef: selectedProject?.ref,
connectionString: selectedProject?.connectionString,
})
const filteredFunctions = (functions ?? []).filter((x) => {
const matchesName = includes(x.name.toLowerCase(), filterString.toLowerCase())
const matchesReturnType =
@@ -1,9 +1,9 @@
import type { PostgresFunction } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { noop } from 'lodash'
import { useEffect, useState } from 'react'
import { Search } from 'lucide-react'
import { useRouter } from 'next/router'
import { parseAsJson, useQueryState } from 'nuqs'
import { parseAsJson, parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { toast } from 'sonner'
import { useParams } from 'common'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
@@ -36,26 +36,77 @@ import {
} from 'components/interfaces/Reports/v2/ReportsSelectFilter'
import { ProtectedSchemaWarning } from '../../ProtectedSchemaWarning'
import FunctionList from './FunctionList'
import type { DatabaseFunction } from 'data/database-functions/database-functions-query'
interface FunctionsListProps {
createFunction: () => void
duplicateFunction: (fn: PostgresFunction) => void
editFunction: (fn: PostgresFunction) => void
deleteFunction: (fn: PostgresFunction) => void
}
import { useIsInlineEditorEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { CreateFunction } from 'components/interfaces/Database/Functions/CreateFunction'
import { DeleteFunction } from 'components/interfaces/Database/Functions/DeleteFunction'
import { useEditorPanelStateSnapshot } from 'state/editor-panel-state'
const FunctionsList = ({
createFunction = noop,
editFunction = noop,
deleteFunction = noop,
duplicateFunction = noop,
}: FunctionsListProps) => {
const FunctionsList = () => {
const router = useRouter()
const { search } = useParams()
const { data: project } = useSelectedProjectQuery()
const aiSnap = useAiAssistantStateSnapshot()
const { openSidebar } = useSidebarManagerSnapshot()
const { selectedSchema, setSelectedSchema } = useQuerySchemaState()
const isInlineEditorEnabled = useIsInlineEditorEnabled()
const {
setValue: setEditorPanelValue,
setTemplates: setEditorPanelTemplates,
setInitialPrompt: setEditorPanelInitialPrompt,
} = useEditorPanelStateSnapshot()
const createFunction = () => {
setSelectedFunctionIdToDuplicate('')
if (isInlineEditorEnabled) {
setEditorPanelInitialPrompt('Create a new database function that...')
setEditorPanelValue(`create function function_name()
returns void
language plpgsql
as $$
begin
-- Write your function logic here
end;
$$;`)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
setShowCreateFunctionForm(true)
}
}
const duplicateFunction = (fn: DatabaseFunction) => {
const dupFn = {
...fn,
name: `${fn.name}_duplicate`,
}
if (isInlineEditorEnabled) {
setEditorPanelInitialPrompt('Create new database function that...')
setEditorPanelValue(dupFn.complete_statement)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
!selectedFunctionIdToDuplicate && setSelectedFunctionIdToDuplicate(dupFn.id.toString())
functionToDuplicate = dupFn
}
}
const editFunction = (fn: DatabaseFunction) => {
setSelectedFunctionIdToDuplicate('')
if (isInlineEditorEnabled) {
setEditorPanelValue(fn.complete_statement)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
setSelectedFunctionToEdit(fn.id.toString())
}
}
const deleteFunction = (fn: any) => {
setSelectedFunctionToDelete(fn.id.toString())
}
const filterString = search ?? ''
@@ -114,6 +165,68 @@ const FunctionsList = ({
...(hasInvoker ? [{ label: 'Invoker', value: 'invoker' }] : []),
]
const [showCreateFunctionForm, setShowCreateFunctionForm] = useQueryState(
'new',
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
)
const [selectedFunctionToEdit, setSelectedFunctionToEdit] = useQueryState(
'edit',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const [selectedFunctionIdToDuplicate, setSelectedFunctionIdToDuplicate] = useQueryState(
'duplicate',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const [selectedFunctionToDelete, setSelectedFunctionToDelete] = useQueryState(
'delete',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const functionToEdit = functions?.find((fn) => fn.id.toString() === selectedFunctionToEdit)
const showFunctionToEdit = selectedFunctionToEdit !== '' && !!functionToEdit
const functionToEditNotFound = selectedFunctionToEdit !== '' && !functionToEdit
let functionToDuplicate = functions?.find(
(fn) => fn.id.toString() === selectedFunctionIdToDuplicate
)
const showFunctionToDuplicate =
!isLoading && selectedFunctionIdToDuplicate !== '' && !!functionToDuplicate
const functionToDelete = functions?.find((fn) => fn.id.toString() === selectedFunctionToDelete)
const showFunctionToDelete = selectedFunctionToDelete !== '' && !!functionToDelete
const functionToDeleteNotFound = selectedFunctionToDelete !== '' && !functionToDelete
// Error handling if edit panel is open and function is not found
useEffect(() => {
if (!isLoading && functionToEditNotFound) {
toast.error('Database Function not found')
setSelectedFunctionToEdit('')
}
}, [selectedFunctionToEdit, functionToEdit, isLoading])
// Error handling if duplicate panel is open and function is not found
useEffect(() => {
if (selectedFunctionIdToDuplicate !== '') {
const fnToDuplicate = functions?.find(
(fn) => fn.id.toString() === selectedFunctionIdToDuplicate
)
if (fnToDuplicate) {
duplicateFunction(fnToDuplicate)
} else {
toast.error('Database Function not found')
setSelectedFunctionIdToDuplicate('')
}
}
}, [functionToDuplicate, selectedFunctionToDelete, isLoading])
// Error handling if delete panel is open and function is not found
useEffect(() => {
if (!isLoading && functionToDeleteNotFound) {
toast.error('Database Function not found')
setSelectedFunctionToDelete('')
}
}, [selectedFunctionToDelete, functionToDelete, isLoading])
if (isLoading) return <GenericSkeletonLoader />
if (isError) return <AlertError error={error} subject="Failed to retrieve database functions" />
@@ -252,12 +365,37 @@ const FunctionsList = ({
duplicateFunction={duplicateFunction}
editFunction={editFunction}
deleteFunction={deleteFunction}
functions={functions ?? []}
/>
</TableBody>
</Table>
</Card>
</div>
)}
<CreateFunction
visible={showCreateFunctionForm}
onClose={() => {
setShowCreateFunctionForm(false)
}}
/>
<CreateFunction
func={functionToEdit || functionToDuplicate}
visible={showFunctionToEdit || showFunctionToDuplicate}
onClose={() => {
setSelectedFunctionToEdit('')
setSelectedFunctionIdToDuplicate('')
functionToDuplicate = undefined
}}
isDuplicating={selectedFunctionIdToDuplicate !== '' && !!functionToDuplicate}
/>
<DeleteFunction
func={functionToDelete}
visible={showFunctionToDelete}
setVisible={setSelectedFunctionToDelete}
/>
</>
)
}
@@ -1,157 +1,37 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useState } from 'react'
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { useIsInlineEditorEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { CreateFunction } from 'components/interfaces/Database/Functions/CreateFunction'
import { DeleteFunction } from 'components/interfaces/Database/Functions/DeleteFunction'
import FunctionsList from 'components/interfaces/Database/Functions/FunctionsList/FunctionsList'
import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
import DefaultLayout from 'components/layouts/DefaultLayout'
import { SIDEBAR_KEYS } from 'components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import NoPermission from 'components/ui/NoPermission'
import { DatabaseFunction } from 'data/database-functions/database-functions-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { DOCS_URL } from 'lib/constants'
import { useEditorPanelStateSnapshot } from 'state/editor-panel-state'
import { useSidebarManagerSnapshot } from 'state/sidebar-manager-state'
import type { NextPageWithLayout } from 'types'
const DatabaseFunctionsPage: NextPageWithLayout = () => {
const [selectedFunction, setSelectedFunction] = useState<DatabaseFunction | undefined>()
const [showCreateFunctionForm, setShowCreateFunctionForm] = useQueryState(
'new',
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
)
const [showEditFunctionForm, setShowEditFunctionForm] = useQueryState(
'edit',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const [showDeleteFunctionForm, setShowDeleteFunctionForm] = useQueryState(
'delete',
parseAsString.withDefault('').withOptions({ history: 'push', clearOnDefault: true })
)
const [isDuplicating, setIsDuplicating] = useState(false)
const isInlineEditorEnabled = useIsInlineEditorEnabled()
const { openSidebar } = useSidebarManagerSnapshot()
const {
setValue: setEditorPanelValue,
setTemplates: setEditorPanelTemplates,
setInitialPrompt: setEditorPanelInitialPrompt,
} = useEditorPanelStateSnapshot()
const { can: canReadFunctions, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_READ,
'functions'
)
const createFunction = () => {
setIsDuplicating(false)
if (isInlineEditorEnabled) {
setEditorPanelInitialPrompt('Create a new database function that...')
setEditorPanelValue(`create function function_name()
returns void
language plpgsql
as $$
begin
-- Write your function logic here
end;
$$;`)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
setSelectedFunction(undefined)
setShowCreateFunctionForm(true)
}
}
const duplicateFunction = (fn: DatabaseFunction) => {
setIsDuplicating(true)
const dupFn = {
...fn,
name: `${fn.name}_duplicate`,
}
if (isInlineEditorEnabled) {
setEditorPanelInitialPrompt('Create new database function that...')
setEditorPanelValue(dupFn.complete_statement)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
setSelectedFunction(dupFn)
setShowEditFunctionForm(dupFn.id.toString())
}
}
const editFunction = (fn: DatabaseFunction) => {
setIsDuplicating(false)
if (isInlineEditorEnabled) {
setEditorPanelValue(fn.complete_statement)
setEditorPanelTemplates([])
openSidebar(SIDEBAR_KEYS.EDITOR_PANEL)
} else {
setSelectedFunction(fn)
setShowEditFunctionForm(fn.id.toString())
}
}
const deleteFunction = (fn: any) => {
setSelectedFunction(fn)
setShowDeleteFunctionForm(fn.id.toString())
}
if (isPermissionsLoaded && !canReadFunctions) {
return <NoPermission isFullPage resourceText="view database functions" />
}
return (
<>
<ScaffoldContainer>
<ScaffoldSection>
<div className="col-span-12">
<FormHeader
title="Database Functions"
docsUrl={`${DOCS_URL}/guides/database/functions`}
/>
<FunctionsList
createFunction={createFunction}
duplicateFunction={duplicateFunction}
editFunction={editFunction}
deleteFunction={deleteFunction}
/>
</div>
</ScaffoldSection>
</ScaffoldContainer>
<CreateFunction
func={selectedFunction}
visible={showCreateFunctionForm}
onClose={() => {
setShowCreateFunctionForm(false)
setIsDuplicating(false)
}}
isDuplicating={isDuplicating}
/>
<CreateFunction
func={selectedFunction}
visible={!!showEditFunctionForm}
onClose={() => {
setShowEditFunctionForm('')
setIsDuplicating(false)
}}
isDuplicating={isDuplicating}
/>
<DeleteFunction
func={selectedFunction}
visible={!!showDeleteFunctionForm}
setVisible={setShowDeleteFunctionForm}
/>
</>
<ScaffoldContainer>
<ScaffoldSection>
<div className="col-span-12">
<FormHeader
title="Database Functions"
docsUrl={`${DOCS_URL}/guides/database/functions`}
/>
<FunctionsList />
</div>
</ScaffoldSection>
</ScaffoldContainer>
)
}