mirror of
https://github.com/supabase/supabase.git
synced 2026-06-20 09:32:15 +08:00
Feature: Duplicate functions from the studio dashboard (#39821)
* added feature to duplicate functions * updated formatting for index file * removed unused ai assitant code --------- Co-authored-by: Ali Waseem <awaseem@Alis-MacBook-Pro.local>
This commit is contained in:
@@ -2,15 +2,15 @@ import { X } from 'lucide-react'
|
||||
|
||||
import { SheetClose, SheetHeader, SheetTitle, cn } from 'ui'
|
||||
|
||||
interface CreateFunctionHeaderProps {
|
||||
selectedFunction?: string
|
||||
isDuplicating?: boolean
|
||||
}
|
||||
|
||||
export const CreateFunctionHeader = ({
|
||||
selectedFunction,
|
||||
assistantVisible,
|
||||
setAssistantVisible,
|
||||
}: {
|
||||
selectedFunction?: string
|
||||
assistantVisible: boolean
|
||||
setAssistantVisible: (v: boolean) => void
|
||||
}) => {
|
||||
isDuplicating,
|
||||
}: CreateFunctionHeaderProps) => {
|
||||
return (
|
||||
<SheetHeader className="py-3 flex flex-row justify-between items-center border-b-0">
|
||||
<div className="flex flex-row gap-3 items-center max-w-[75%]">
|
||||
@@ -27,33 +27,12 @@ export const CreateFunctionHeader = ({
|
||||
</SheetClose>
|
||||
<SheetTitle className="truncate">
|
||||
{selectedFunction !== undefined
|
||||
? `Edit '${selectedFunction}' function`
|
||||
? isDuplicating
|
||||
? `Duplicate function`
|
||||
: `Edit '${selectedFunction}' function`
|
||||
: 'Add a new function'}
|
||||
</SheetTitle>
|
||||
</div>
|
||||
{/* <Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
aria-expanded={assistantVisible}
|
||||
aria-controls="ai-chat-assistant"
|
||||
className={cn(
|
||||
!assistantVisible ? 'text-foreground-lighter' : 'text-light',
|
||||
'hover:text-foreground',
|
||||
'transition'
|
||||
)}
|
||||
onClick={() => setAssistantVisible(!assistantVisible)}
|
||||
>
|
||||
{!assistantVisible ? (
|
||||
<PanelLeftClose size={19} strokeWidth={1} />
|
||||
) : (
|
||||
<PanelRightClose size={19} strokeWidth={1} />
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left">
|
||||
{assistantVisible ? 'Hide' : 'Show'} tools
|
||||
</TooltipContent>
|
||||
</Tooltip> */}
|
||||
</SheetHeader>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,8 +50,9 @@ const FORM_ID = 'create-function-sidepanel'
|
||||
|
||||
interface CreateFunctionProps {
|
||||
func?: DatabaseFunction
|
||||
isDuplicating?: boolean
|
||||
visible: boolean
|
||||
setVisible: (value: boolean) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const FormSchema = z.object({
|
||||
@@ -68,15 +69,13 @@ const FormSchema = z.object({
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
const CreateFunction = ({ func, visible, isDuplicating = false, onClose }: CreateFunctionProps) => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const [isClosingPanel, setIsClosingPanel] = useState(false)
|
||||
const [advancedSettingsShown, setAdvancedSettingsShown] = useState(false)
|
||||
// For now, there's no AI assistant for functions
|
||||
const [assistantVisible, setAssistantVisible] = useState(false)
|
||||
const [focusedEditor, setFocusedEditor] = useState(false)
|
||||
|
||||
const isEditing = !!func?.id
|
||||
const isEditing = !isDuplicating && !!func?.id
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
@@ -89,7 +88,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
useDatabaseFunctionUpdateMutation()
|
||||
|
||||
function isClosingSidePanel() {
|
||||
form.formState.isDirty ? setIsClosingPanel(true) : setVisible(!visible)
|
||||
form.formState.isDirty ? setIsClosingPanel(true) : onClose()
|
||||
}
|
||||
|
||||
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
|
||||
@@ -111,7 +110,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success(`Successfully updated function ${data.name}`)
|
||||
setVisible(!visible)
|
||||
onClose()
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -125,7 +124,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success(`Successfully created function ${data.name}`)
|
||||
setVisible(!visible)
|
||||
onClose()
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -155,19 +154,11 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
<Sheet open={visible} onOpenChange={() => isClosingSidePanel()}>
|
||||
<SheetContent
|
||||
showClose={false}
|
||||
size={assistantVisible ? 'lg' : 'default'}
|
||||
className={cn(
|
||||
// 'bg-surface-200',
|
||||
'p-0 flex flex-row gap-0',
|
||||
assistantVisible ? '!min-w-screen lg:!min-w-[1200px]' : '!min-w-screen lg:!min-w-[600px]'
|
||||
)}
|
||||
size={'default'}
|
||||
className={'p-0 flex flex-row gap-0 !min-w-screen lg:!min-w-[600px]'}
|
||||
>
|
||||
<div className={cn('flex flex-col grow w-full', assistantVisible && 'w-[60%]')}>
|
||||
<CreateFunctionHeader
|
||||
selectedFunction={func?.name}
|
||||
assistantVisible={assistantVisible}
|
||||
setAssistantVisible={setAssistantVisible}
|
||||
/>
|
||||
<div className="flex flex-col grow w-full">
|
||||
<CreateFunctionHeader selectedFunction={func?.name} isDuplicating={isDuplicating} />
|
||||
<Separator />
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form
|
||||
@@ -405,11 +396,6 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</div>
|
||||
{assistantVisible ? (
|
||||
<div className="border-l shadow-[rgba(0,0,0,0.13)_-4px_0px_6px_0px] z-10 w-[50%] bg-studio">
|
||||
{/* This is where the AI assistant would be added */}
|
||||
</div>
|
||||
) : null}
|
||||
<ConfirmationModal
|
||||
visible={isClosingPanel}
|
||||
title="Discard changes"
|
||||
@@ -417,7 +403,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
onCancel={() => setIsClosingPanel(false)}
|
||||
onConfirm={() => {
|
||||
setIsClosingPanel(false)
|
||||
setVisible(!visible)
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
<p className="text-sm text-foreground-light">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { includes, noop, sortBy } from 'lodash'
|
||||
import { Edit, Edit2, FileText, MoreVertical, Trash } from 'lucide-react'
|
||||
import { Copy, Edit, Edit2, FileText, MoreVertical, Trash } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
@@ -23,6 +23,7 @@ interface FunctionListProps {
|
||||
schema: string
|
||||
filterString: string
|
||||
isLocked: boolean
|
||||
duplicateFunction: (fn: any) => void
|
||||
editFunction: (fn: any) => void
|
||||
deleteFunction: (fn: any) => void
|
||||
}
|
||||
@@ -31,6 +32,7 @@ const FunctionList = ({
|
||||
schema,
|
||||
filterString,
|
||||
isLocked,
|
||||
duplicateFunction = noop,
|
||||
editFunction = noop,
|
||||
deleteFunction = noop,
|
||||
}: FunctionListProps) => {
|
||||
@@ -132,6 +134,7 @@ const FunctionList = ({
|
||||
<p>Client API docs</p>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="space-x-2" onClick={() => editFunction(x)}>
|
||||
<Edit2 size={14} />
|
||||
<p>Edit function</p>
|
||||
@@ -169,6 +172,13 @@ const FunctionList = ({
|
||||
<Edit size={14} />
|
||||
<p>Edit function with Assistant</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="space-x-2"
|
||||
onClick={() => duplicateFunction(x)}
|
||||
>
|
||||
<Copy size={14} />
|
||||
<p>Duplicate function</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="space-x-2" onClick={() => deleteFunction(x)}>
|
||||
<Trash size={14} className="text-destructive" />
|
||||
|
||||
@@ -32,6 +32,7 @@ import FunctionList from './FunctionList'
|
||||
|
||||
interface FunctionsListProps {
|
||||
createFunction: () => void
|
||||
duplicateFunction: (fn: PostgresFunction) => void
|
||||
editFunction: (fn: PostgresFunction) => void
|
||||
deleteFunction: (fn: PostgresFunction) => void
|
||||
}
|
||||
@@ -40,6 +41,7 @@ const FunctionsList = ({
|
||||
createFunction = noop,
|
||||
editFunction = noop,
|
||||
deleteFunction = noop,
|
||||
duplicateFunction = noop,
|
||||
}: FunctionsListProps) => {
|
||||
const router = useRouter()
|
||||
const { search } = useParams()
|
||||
@@ -199,6 +201,7 @@ const FunctionsList = ({
|
||||
schema={selectedSchema}
|
||||
filterString={filterString}
|
||||
isLocked={isSchemaLocked}
|
||||
duplicateFunction={duplicateFunction}
|
||||
editFunction={editFunction}
|
||||
deleteFunction={deleteFunction}
|
||||
/>
|
||||
|
||||
@@ -19,6 +19,7 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
const [selectedFunction, setSelectedFunction] = useState<DatabaseFunction | undefined>()
|
||||
const [showCreateFunctionForm, setShowCreateFunctionForm] = useState(false)
|
||||
const [showDeleteFunctionForm, setShowDeleteFunctionForm] = useState(false)
|
||||
const [isDuplicating, setIsDuplicating] = useState(false)
|
||||
const isInlineEditorEnabled = useIsInlineEditorEnabled()
|
||||
|
||||
// Local editor panel state
|
||||
@@ -42,6 +43,23 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateFunction = (fn: DatabaseFunction) => {
|
||||
setIsDuplicating(true)
|
||||
|
||||
const dupFn = {
|
||||
...fn,
|
||||
name: `${fn.name}_duplicate`,
|
||||
}
|
||||
|
||||
if (isInlineEditorEnabled) {
|
||||
setSelectedFunctionForEditor(dupFn)
|
||||
setEditorPanelOpen(true)
|
||||
} else {
|
||||
setSelectedFunction(dupFn)
|
||||
setShowCreateFunctionForm(true)
|
||||
}
|
||||
}
|
||||
|
||||
const editFunction = (fn: DatabaseFunction) => {
|
||||
if (isInlineEditorEnabled) {
|
||||
setSelectedFunctionForEditor(fn)
|
||||
@@ -57,6 +75,12 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
setShowDeleteFunctionForm(true)
|
||||
}
|
||||
|
||||
const resetEditorPanel = () => {
|
||||
setIsDuplicating(false)
|
||||
setEditorPanelOpen(false)
|
||||
setSelectedFunctionForEditor(undefined)
|
||||
}
|
||||
|
||||
if (isPermissionsLoaded && !canReadFunctions) {
|
||||
return <NoPermission isFullPage resourceText="view database functions" />
|
||||
}
|
||||
@@ -72,6 +96,7 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
/>
|
||||
<FunctionsList
|
||||
createFunction={createFunction}
|
||||
duplicateFunction={duplicateFunction}
|
||||
editFunction={editFunction}
|
||||
deleteFunction={deleteFunction}
|
||||
/>
|
||||
@@ -81,7 +106,11 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
<CreateFunction
|
||||
func={selectedFunction}
|
||||
visible={showCreateFunctionForm}
|
||||
setVisible={setShowCreateFunctionForm}
|
||||
onClose={() => {
|
||||
setShowCreateFunctionForm(false)
|
||||
setIsDuplicating(false)
|
||||
}}
|
||||
isDuplicating={isDuplicating}
|
||||
/>
|
||||
<DeleteFunction
|
||||
func={selectedFunction}
|
||||
@@ -91,14 +120,8 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
|
||||
|
||||
<EditorPanel
|
||||
open={editorPanelOpen}
|
||||
onRunSuccess={() => {
|
||||
setEditorPanelOpen(false)
|
||||
setSelectedFunctionForEditor(undefined)
|
||||
}}
|
||||
onClose={() => {
|
||||
setEditorPanelOpen(false)
|
||||
setSelectedFunctionForEditor(undefined)
|
||||
}}
|
||||
onRunSuccess={resetEditorPanel}
|
||||
onClose={resetEditorPanel}
|
||||
initialValue={
|
||||
selectedFunctionForEditor
|
||||
? selectedFunctionForEditor.complete_statement
|
||||
@@ -113,12 +136,16 @@ $$;`
|
||||
}
|
||||
label={
|
||||
selectedFunctionForEditor
|
||||
? `Edit function "${selectedFunctionForEditor.name}"`
|
||||
? isDuplicating
|
||||
? `Duplicate function "${selectedFunctionForEditor.name}"`
|
||||
: `Edit function "${selectedFunctionForEditor.name}"`
|
||||
: 'Create new database function'
|
||||
}
|
||||
initialPrompt={
|
||||
selectedFunctionForEditor
|
||||
? `Update the database function "${selectedFunctionForEditor.name}" to...`
|
||||
? isDuplicating
|
||||
? `Duplicate the database function "${selectedFunctionForEditor.name}" to...`
|
||||
: `Update the database function "${selectedFunctionForEditor.name}" to...`
|
||||
: 'Create a new database function that...'
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user