Allow filtering the list of available edge functions when creating/editing a cron job (#43138)

## Problem

Finding a specific edge function is cumbersome when you have many of
then.

## Solution

Use a combobox instead of a select to allow filtering

## How to test

- Enable the cron extension
- Enable the pg_net extension
- Create a few edge functions 
- Create a new cron job and select _Supabase edge functions_

You should be able to:
- select/unselect an edge function
- filter the list to reduce the number of edge functions displayed
This commit is contained in:
Gildas Garcia authored and GitHub committed 2026-02-25 08:05:33 +01:00
1 parent 487c74f174
commit 65dfd228ad
1 file changed
+99 -43
@@ -1,18 +1,29 @@
import Link from 'next/link'
import { UseFormReturn } from 'react-hook-form'
import { useParams } from 'common/hooks'
import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useEffect, useMemo } from 'react'
import { Check, ChevronsUpDown } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useId, useMemo, useState } from 'react'
import { UseFormReturn } from 'react-hook-form'
import {
Button,
cn,
Command_Shadcn_,
CommandEmpty_Shadcn_,
CommandGroup_Shadcn_,
CommandInput_Shadcn_,
CommandItem_Shadcn_,
CommandList_Shadcn_,
FormControl_Shadcn_,
FormField_Shadcn_,
FormItem_Shadcn_,
FormLabel_Shadcn_,
FormMessage_Shadcn_,
Input,
Popover_Shadcn_,
PopoverContent_Shadcn_,
PopoverTrigger_Shadcn_,
ScrollArea,
Select_Shadcn_,
SelectContent_Shadcn_,
SelectItem_Shadcn_,
@@ -21,6 +32,7 @@ import {
SheetSection,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { CreateCronJobForm } from './CreateCronJobSheet/CreateCronJobSheet.constants'
interface HTTPRequestFieldsProps {
@@ -41,19 +53,22 @@ export const EdgeFunctionSection = ({ form }: HTTPRequestFieldsProps) => {
isSuccess,
isPending: isLoading,
} = useEdgeFunctionsQuery({ projectRef: ref })
const edgeFunctions = useMemo(() => functions ?? [], [functions])
const [open, setOpen] = useState(false)
const listboxId = useId()
const edgeFunctions = useMemo(
() =>
functions?.map((fn) => ({
...fn,
url: buildFunctionUrl(fn.slug, selectedProject?.ref || '', selectedProject?.restUrl),
})) ?? [],
[functions, selectedProject]
)
// Only set a default value if the field is empty
useEffect(() => {
if (isSuccess && edgeFunctions.length > 0 && !form.getValues('values.edgeFunctionName')) {
const fn = edgeFunctions[0]
const functionUrl = buildFunctionUrl(
fn.slug,
selectedProject?.ref || '',
selectedProject?.restUrl
)
form.setValue('values.edgeFunctionName', functionUrl)
form.setValue('values.edgeFunctionName', fn.url)
}
}, [edgeFunctions, form, isSuccess, selectedProject?.ref, selectedProject?.restUrl])
@@ -84,47 +99,88 @@ export const EdgeFunctionSection = ({ form }: HTTPRequestFieldsProps) => {
{edgeFunctions.length === 0 ? (
<div className="space-y-1">
<p className="text-sm text-foreground-light">Select which edge function to trigger</p>
<div className="px-4 py-4 border rounded bg-surface-300 border-strong flex items-center justify-between space-x-4">
<p className="text-sm">No edge functions created yet</p>
<Button asChild>
<Link href={`/project/${ref}/functions`}>Create an edge function</Link>
{isLoading ? (
<Button type="default" className="justify-start" block size="small" loading>
Loading edge functions...
</Button>
</div>
) : (
<div className="px-4 py-4 border rounded bg-surface-300 border-strong flex items-center justify-between space-x-4">
<p className="text-sm">No edge functions created yet</p>
<Button asChild>
<Link href={`/project/${ref}/functions`}>Create an edge function</Link>
</Button>
</div>
)}
</div>
) : edgeFunctions.length > 0 ? (
<FormField_Shadcn_
control={form.control}
name="values.edgeFunctionName"
render={({ field }) => {
const selectedFunction = edgeFunctions.find((fn) => fn.url === field.value)
return (
<FormItem_Shadcn_>
<FormLabel_Shadcn_>Edge Function</FormLabel_Shadcn_>
<Select_Shadcn_
onValueChange={field.onChange}
value={field.value}
disabled={isLoading}
>
<FormControl_Shadcn_>
<SelectTrigger_Shadcn_>
<SelectValue_Shadcn_ placeholder="Select which edge function to trigger" />
</SelectTrigger_Shadcn_>
</FormControl_Shadcn_>
<SelectContent_Shadcn_>
{edgeFunctions.map((fn) => {
const functionUrl = buildFunctionUrl(
fn.slug,
selectedProject?.ref || '',
selectedProject?.restUrl
)
return (
<SelectItem_Shadcn_ key={fn.id} value={functionUrl}>
{fn.name}
</SelectItem_Shadcn_>
)
})}
</SelectContent_Shadcn_>
</Select_Shadcn_>
<Popover_Shadcn_ open={open} onOpenChange={setOpen}>
<PopoverTrigger_Shadcn_ asChild>
<FormControl_Shadcn_>
<Button
type="default"
role="combobox"
aria-expanded={open}
aria-controls={listboxId}
className={cn(
'w-full justify-between',
!field.value && 'text-muted-foreground'
)}
size="small"
iconRight={
<ChevronsUpDown
className="ml-2 h-4 w-4 shrink-0 opacity-50"
strokeWidth={1}
/>
}
>
{selectedFunction
? selectedFunction.name
: 'Select which edge function to trigger'}
</Button>
</FormControl_Shadcn_>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ id={listboxId} className="p-0" sameWidthAsTrigger>
<Command_Shadcn_>
<CommandInput_Shadcn_ placeholder="Search edge functions..." />
<CommandList_Shadcn_>
<CommandEmpty_Shadcn_>No edge function found.</CommandEmpty_Shadcn_>
<CommandGroup_Shadcn_>
<ScrollArea className={edgeFunctions.length > 7 ? 'h-[210px]' : ''}>
{edgeFunctions.map((fn) => {
return (
<CommandItem_Shadcn_
value={fn.name}
key={fn.id}
onSelect={() => {
field.onChange(fn.url === field.value ? '' : fn.url)
setOpen(false)
}}
>
<Check
className={cn(
'mr-2 h-4 w-4',
fn.url === field.value ? 'opacity-100' : 'opacity-0'
)}
/>
{fn.name}
</CommandItem_Shadcn_>
)
})}
</ScrollArea>
</CommandGroup_Shadcn_>
</CommandList_Shadcn_>
</Command_Shadcn_>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
<FormMessage_Shadcn_ />
</FormItem_Shadcn_>
)