mirror of
https://github.com/supabase/supabase.git
synced 2026-10-13 05:09:12 +03:00
chore(studio): fix param on load
This commit is contained in:
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 =
|
||||
|
||||
+153
-15
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user