Skip to content

Commit c41a5be

Browse files
awaseemAli Waseem
andauthored
Feature: Duplicate functions from the studio dashboard (supabase#39821)
* added feature to duplicate functions * updated formatting for index file * removed unused ai assitant code --------- Co-authored-by: Ali Waseem <[email protected]>
1 parent f236876 commit c41a5be

File tree

5 files changed

+74
-69
lines changed

5 files changed

+74
-69
lines changed

apps/studio/components/interfaces/Database/Functions/CreateFunction/CreateFunctionHeader.tsx

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { X } from 'lucide-react'
22

33
import { SheetClose, SheetHeader, SheetTitle, cn } from 'ui'
44

5+
interface CreateFunctionHeaderProps {
6+
selectedFunction?: string
7+
isDuplicating?: boolean
8+
}
9+
510
export const CreateFunctionHeader = ({
611
selectedFunction,
7-
assistantVisible,
8-
setAssistantVisible,
9-
}: {
10-
selectedFunction?: string
11-
assistantVisible: boolean
12-
setAssistantVisible: (v: boolean) => void
13-
}) => {
12+
isDuplicating,
13+
}: CreateFunctionHeaderProps) => {
1414
return (
1515
<SheetHeader className="py-3 flex flex-row justify-between items-center border-b-0">
1616
<div className="flex flex-row gap-3 items-center max-w-[75%]">
@@ -27,33 +27,12 @@ export const CreateFunctionHeader = ({
2727
</SheetClose>
2828
<SheetTitle className="truncate">
2929
{selectedFunction !== undefined
30-
? `Edit '${selectedFunction}' function`
30+
? isDuplicating
31+
? `Duplicate function`
32+
: `Edit '${selectedFunction}' function`
3133
: 'Add a new function'}
3234
</SheetTitle>
3335
</div>
34-
{/* <Tooltip>
35-
<TooltipTrigger asChild>
36-
<button
37-
aria-expanded={assistantVisible}
38-
aria-controls="ai-chat-assistant"
39-
className={cn(
40-
!assistantVisible ? 'text-foreground-lighter' : 'text-light',
41-
'hover:text-foreground',
42-
'transition'
43-
)}
44-
onClick={() => setAssistantVisible(!assistantVisible)}
45-
>
46-
{!assistantVisible ? (
47-
<PanelLeftClose size={19} strokeWidth={1} />
48-
) : (
49-
<PanelRightClose size={19} strokeWidth={1} />
50-
)}
51-
</button>
52-
</TooltipTrigger>
53-
<TooltipContent side="left">
54-
{assistantVisible ? 'Hide' : 'Show'} tools
55-
</TooltipContent>
56-
</Tooltip> */}
5736
</SheetHeader>
5837
)
5938
}

apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ const FORM_ID = 'create-function-sidepanel'
5050

5151
interface CreateFunctionProps {
5252
func?: DatabaseFunction
53+
isDuplicating?: boolean
5354
visible: boolean
54-
setVisible: (value: boolean) => void
55+
onClose: () => void
5556
}
5657

5758
const FormSchema = z.object({
@@ -68,15 +69,13 @@ const FormSchema = z.object({
6869
.optional(),
6970
})
7071

71-
const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
72+
const CreateFunction = ({ func, visible, isDuplicating = false, onClose }: CreateFunctionProps) => {
7273
const { data: project } = useSelectedProjectQuery()
7374
const [isClosingPanel, setIsClosingPanel] = useState(false)
7475
const [advancedSettingsShown, setAdvancedSettingsShown] = useState(false)
75-
// For now, there's no AI assistant for functions
76-
const [assistantVisible, setAssistantVisible] = useState(false)
7776
const [focusedEditor, setFocusedEditor] = useState(false)
7877

79-
const isEditing = !!func?.id
78+
const isEditing = !isDuplicating && !!func?.id
8079

8180
const form = useForm<z.infer<typeof FormSchema>>({
8281
resolver: zodResolver(FormSchema),
@@ -89,7 +88,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
8988
useDatabaseFunctionUpdateMutation()
9089

9190
function isClosingSidePanel() {
92-
form.formState.isDirty ? setIsClosingPanel(true) : setVisible(!visible)
91+
form.formState.isDirty ? setIsClosingPanel(true) : onClose()
9392
}
9493

9594
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
@@ -111,7 +110,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
111110
{
112111
onSuccess: () => {
113112
toast.success(`Successfully updated function ${data.name}`)
114-
setVisible(!visible)
113+
onClose()
115114
},
116115
}
117116
)
@@ -125,7 +124,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
125124
{
126125
onSuccess: () => {
127126
toast.success(`Successfully created function ${data.name}`)
128-
setVisible(!visible)
127+
onClose()
129128
},
130129
}
131130
)
@@ -155,19 +154,11 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
155154
<Sheet open={visible} onOpenChange={() => isClosingSidePanel()}>
156155
<SheetContent
157156
showClose={false}
158-
size={assistantVisible ? 'lg' : 'default'}
159-
className={cn(
160-
// 'bg-surface-200',
161-
'p-0 flex flex-row gap-0',
162-
assistantVisible ? '!min-w-screen lg:!min-w-[1200px]' : '!min-w-screen lg:!min-w-[600px]'
163-
)}
157+
size={'default'}
158+
className={'p-0 flex flex-row gap-0 !min-w-screen lg:!min-w-[600px]'}
164159
>
165-
<div className={cn('flex flex-col grow w-full', assistantVisible && 'w-[60%]')}>
166-
<CreateFunctionHeader
167-
selectedFunction={func?.name}
168-
assistantVisible={assistantVisible}
169-
setAssistantVisible={setAssistantVisible}
170-
/>
160+
<div className="flex flex-col grow w-full">
161+
<CreateFunctionHeader selectedFunction={func?.name} isDuplicating={isDuplicating} />
171162
<Separator />
172163
<Form_Shadcn_ {...form}>
173164
<form
@@ -405,19 +396,14 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
405396
</Button>
406397
</SheetFooter>
407398
</div>
408-
{assistantVisible ? (
409-
<div className="border-l shadow-[rgba(0,0,0,0.13)_-4px_0px_6px_0px] z-10 w-[50%] bg-studio">
410-
{/* This is where the AI assistant would be added */}
411-
</div>
412-
) : null}
413399
<ConfirmationModal
414400
visible={isClosingPanel}
415401
title="Discard changes"
416402
confirmLabel="Discard"
417403
onCancel={() => setIsClosingPanel(false)}
418404
onConfirm={() => {
419405
setIsClosingPanel(false)
420-
setVisible(!visible)
406+
onClose()
421407
}}
422408
>
423409
<p className="text-sm text-foreground-light">

apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionList.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
22
import { includes, noop, sortBy } from 'lodash'
3-
import { Edit, Edit2, FileText, MoreVertical, Trash } from 'lucide-react'
3+
import { Copy, Edit, Edit2, FileText, MoreVertical, Trash } from 'lucide-react'
44
import { useRouter } from 'next/router'
55

66
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
@@ -23,6 +23,7 @@ interface FunctionListProps {
2323
schema: string
2424
filterString: string
2525
isLocked: boolean
26+
duplicateFunction: (fn: any) => void
2627
editFunction: (fn: any) => void
2728
deleteFunction: (fn: any) => void
2829
}
@@ -31,6 +32,7 @@ const FunctionList = ({
3132
schema,
3233
filterString,
3334
isLocked,
35+
duplicateFunction = noop,
3436
editFunction = noop,
3537
deleteFunction = noop,
3638
}: FunctionListProps) => {
@@ -132,6 +134,7 @@ const FunctionList = ({
132134
<p>Client API docs</p>
133135
</DropdownMenuItem>
134136
)}
137+
<DropdownMenuSeparator />
135138
<DropdownMenuItem className="space-x-2" onClick={() => editFunction(x)}>
136139
<Edit2 size={14} />
137140
<p>Edit function</p>
@@ -169,6 +172,13 @@ const FunctionList = ({
169172
<Edit size={14} />
170173
<p>Edit function with Assistant</p>
171174
</DropdownMenuItem>
175+
<DropdownMenuItem
176+
className="space-x-2"
177+
onClick={() => duplicateFunction(x)}
178+
>
179+
<Copy size={14} />
180+
<p>Duplicate function</p>
181+
</DropdownMenuItem>
172182
<DropdownMenuSeparator />
173183
<DropdownMenuItem className="space-x-2" onClick={() => deleteFunction(x)}>
174184
<Trash size={14} className="text-destructive" />

apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import FunctionList from './FunctionList'
3232

3333
interface FunctionsListProps {
3434
createFunction: () => void
35+
duplicateFunction: (fn: PostgresFunction) => void
3536
editFunction: (fn: PostgresFunction) => void
3637
deleteFunction: (fn: PostgresFunction) => void
3738
}
@@ -40,6 +41,7 @@ const FunctionsList = ({
4041
createFunction = noop,
4142
editFunction = noop,
4243
deleteFunction = noop,
44+
duplicateFunction = noop,
4345
}: FunctionsListProps) => {
4446
const router = useRouter()
4547
const { search } = useParams()
@@ -199,6 +201,7 @@ const FunctionsList = ({
199201
schema={selectedSchema}
200202
filterString={filterString}
201203
isLocked={isSchemaLocked}
204+
duplicateFunction={duplicateFunction}
202205
editFunction={editFunction}
203206
deleteFunction={deleteFunction}
204207
/>

apps/studio/pages/project/[ref]/database/functions.tsx

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
1919
const [selectedFunction, setSelectedFunction] = useState<DatabaseFunction | undefined>()
2020
const [showCreateFunctionForm, setShowCreateFunctionForm] = useState(false)
2121
const [showDeleteFunctionForm, setShowDeleteFunctionForm] = useState(false)
22+
const [isDuplicating, setIsDuplicating] = useState(false)
2223
const isInlineEditorEnabled = useIsInlineEditorEnabled()
2324

2425
// Local editor panel state
@@ -42,6 +43,23 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
4243
}
4344
}
4445

46+
const duplicateFunction = (fn: DatabaseFunction) => {
47+
setIsDuplicating(true)
48+
49+
const dupFn = {
50+
...fn,
51+
name: `${fn.name}_duplicate`,
52+
}
53+
54+
if (isInlineEditorEnabled) {
55+
setSelectedFunctionForEditor(dupFn)
56+
setEditorPanelOpen(true)
57+
} else {
58+
setSelectedFunction(dupFn)
59+
setShowCreateFunctionForm(true)
60+
}
61+
}
62+
4563
const editFunction = (fn: DatabaseFunction) => {
4664
if (isInlineEditorEnabled) {
4765
setSelectedFunctionForEditor(fn)
@@ -57,6 +75,12 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
5775
setShowDeleteFunctionForm(true)
5876
}
5977

78+
const resetEditorPanel = () => {
79+
setIsDuplicating(false)
80+
setEditorPanelOpen(false)
81+
setSelectedFunctionForEditor(undefined)
82+
}
83+
6084
if (isPermissionsLoaded && !canReadFunctions) {
6185
return <NoPermission isFullPage resourceText="view database functions" />
6286
}
@@ -72,6 +96,7 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
7296
/>
7397
<FunctionsList
7498
createFunction={createFunction}
99+
duplicateFunction={duplicateFunction}
75100
editFunction={editFunction}
76101
deleteFunction={deleteFunction}
77102
/>
@@ -81,7 +106,11 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
81106
<CreateFunction
82107
func={selectedFunction}
83108
visible={showCreateFunctionForm}
84-
setVisible={setShowCreateFunctionForm}
109+
onClose={() => {
110+
setShowCreateFunctionForm(false)
111+
setIsDuplicating(false)
112+
}}
113+
isDuplicating={isDuplicating}
85114
/>
86115
<DeleteFunction
87116
func={selectedFunction}
@@ -91,14 +120,8 @@ const DatabaseFunctionsPage: NextPageWithLayout = () => {
91120

92121
<EditorPanel
93122
open={editorPanelOpen}
94-
onRunSuccess={() => {
95-
setEditorPanelOpen(false)
96-
setSelectedFunctionForEditor(undefined)
97-
}}
98-
onClose={() => {
99-
setEditorPanelOpen(false)
100-
setSelectedFunctionForEditor(undefined)
101-
}}
123+
onRunSuccess={resetEditorPanel}
124+
onClose={resetEditorPanel}
102125
initialValue={
103126
selectedFunctionForEditor
104127
? selectedFunctionForEditor.complete_statement
@@ -113,12 +136,16 @@ $$;`
113136
}
114137
label={
115138
selectedFunctionForEditor
116-
? `Edit function "${selectedFunctionForEditor.name}"`
139+
? isDuplicating
140+
? `Duplicate function "${selectedFunctionForEditor.name}"`
141+
: `Edit function "${selectedFunctionForEditor.name}"`
117142
: 'Create new database function'
118143
}
119144
initialPrompt={
120145
selectedFunctionForEditor
121-
? `Update the database function "${selectedFunctionForEditor.name}" to...`
146+
? isDuplicating
147+
? `Duplicate the database function "${selectedFunctionForEditor.name}" to...`
148+
: `Update the database function "${selectedFunctionForEditor.name}" to...`
122149
: 'Create a new database function that...'
123150
}
124151
/>

0 commit comments

Comments
 (0)