From 8c3da2ed9532d51d4a69a3fd512bc3314d46393b Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Fri, 29 Nov 2024 20:31:02 +1000 Subject: [PATCH 1/4] More assistant refinements (#30730) * ai refinements * ui fixes * space * reduce dot height * Fix prop type issue * Assign allowHoverEffect to ai assistant button and panel * Lint * Some lints --------- Co-authored-by: Joshen Lim --- .../PolicyTableRow/PolicyTableRowHeader.tsx | 2 +- .../Functions/FunctionsList/FunctionsList.tsx | 4 +- .../Triggers/TriggersList/TriggersList.tsx | 4 +- .../layouts/AppLayout/AssistantButton.tsx | 7 +- .../ui/AIAssistantPanel/AIAssistant.tsx | 149 ++++++-------- .../ui/AIAssistantPanel/AIOnboarding.tsx | 14 +- .../ui/AIAssistantPanel/Message.tsx | 82 ++++---- .../ui/AIAssistantPanel/SqlSnippet.tsx | 10 +- apps/studio/components/ui/DotGrid.tsx | 6 +- apps/studio/pages/api/ai/sql/generate-v3.ts | 4 +- .../ai-icon-animation/ai-icon-animation.tsx | 187 ++++++++++-------- 11 files changed, 227 insertions(+), 242 deletions(-) diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRowHeader.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRowHeader.tsx index b4c4193ff36ae..88d820c5e2f5c 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRowHeader.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRowHeader.tsx @@ -137,7 +137,7 @@ const PolicyTableRowHeader = ({ }, }} > - + diff --git a/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx b/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx index 609998fd789dc..bc11bf2eccd7c 100644 --- a/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx +++ b/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx @@ -149,9 +149,7 @@ const FunctionsList = ({ type="default" disabled={!canCreateFunctions} className="px-1 pointer-events-auto" - icon={ - - } + icon={} onClick={() => setAiAssistantPanel({ open: true, diff --git a/apps/studio/components/interfaces/Database/Triggers/TriggersList/TriggersList.tsx b/apps/studio/components/interfaces/Database/Triggers/TriggersList/TriggersList.tsx index fd2af38317ebe..bca14055c723e 100644 --- a/apps/studio/components/interfaces/Database/Triggers/TriggersList/TriggersList.tsx +++ b/apps/studio/components/interfaces/Database/Triggers/TriggersList/TriggersList.tsx @@ -131,9 +131,7 @@ const TriggersList = ({ type="default" disabled={!canCreateTriggers} className="px-1 pointer-events-auto" - icon={ - - } + icon={} onClick={() => setAiAssistantPanel({ open: true, diff --git a/apps/studio/components/layouts/AppLayout/AssistantButton.tsx b/apps/studio/components/layouts/AppLayout/AssistantButton.tsx index 7ea54e9a94dc1..c5711c9c02a75 100644 --- a/apps/studio/components/layouts/AppLayout/AssistantButton.tsx +++ b/apps/studio/components/layouts/AppLayout/AssistantButton.tsx @@ -7,13 +7,14 @@ const AssistantButton = () => { return ( ) } diff --git a/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx b/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx index 81f71e9bd35ad..c2a99c0bd76b4 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AIAssistant.tsx @@ -1,7 +1,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { AnimatePresence, motion } from 'framer-motion' import { last } from 'lodash' -import { FileText } from 'lucide-react' +import { FileText, Info } from 'lucide-react' import { memo, useEffect, useMemo, useRef, useState } from 'react' import { toast } from 'sonner' @@ -43,6 +43,7 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import AIOnboarding from './AIOnboarding' import CollapsibleCodeBlock from './CollapsibleCodeBlock' import { Message } from './Message' +import DotGrid from '../DotGrid' const MemoizedMessage = memo( ({ message, isLoading }: { message: MessageType; isLoading: boolean }) => { @@ -298,15 +299,6 @@ export const AIAssistant = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, isInSQLEditor, snippetContent]) - if (isLoadingTables) { - return ( -
- {/* [Joshen] We could try play around with a custom loader for the assistant here */} - -
- ) - } - return ( <>
@@ -315,12 +307,24 @@ export const AIAssistant = ({ className={cn('flex-grow overflow-auto flex flex-col')} onScroll={handleScroll} > -
-
- +
+
+ + +
Assistant
+
+ + + + + + The Assistant is in Alpha and your prompts might be rate limited.{' '} + {includeSchemaMetadata + ? 'Project metadata is being shared to improve Assistant responses.' + : 'Project metadata is not being shared. Opt in to improve Assistant responses.'} + + -
{hasMessages ? 'New chat' : 'Assistant'}
-
{(hasMessages || suggestions || sqlSnippets) && (
+ {!hasMessages && ( +
+ +
+ )} {hasMessages ? ( - -
- {new Date(messages[0].createdAt || new Date()).toLocaleDateString('en-US', { - month: 'long', - day: 'numeric', - year: 'numeric', - hour: 'numeric', - minute: 'numeric', - })} -
+ {renderedMessages} {(last(messages)?.role === 'user' || last(messages)?.content?.length === 0) && ( - - Thinking -
- - . - - - . - - - . - -
-
+
+ + + Thinking +
+ + . + + + . + + + . + +
+
+
)}
@@ -417,48 +420,15 @@ export const AIAssistant = ({ ))}
+ ) : isLoadingTables ? ( +
+ {/* [Joshen] We could try play around with a custom loader for the assistant here */} + +
) : (tables ?? [])?.length > 0 ? ( ) : ( -
-
-
- -
- - - -
-
-
+

Welcome to Supabase!

This is the Supabase assistant which will help you create, debug and modify tables, @@ -590,11 +560,6 @@ export const AIAssistant = ({ } }} /> - {!hasMessages && ( -

- The Assistant is in Alpha and your prompts might be rate limited -

- )}
diff --git a/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx b/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx index 0d330085b60fe..cab0382de7d6e 100644 --- a/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx @@ -1,7 +1,6 @@ import { motion } from 'framer-motion' import { FileText, MessageCircleMore, WandSparkles } from 'lucide-react' -import DotGrid from 'components/ui/DotGrid' import { Button } from 'ui' import { InnerSideMenuCollapsible, @@ -20,10 +19,7 @@ export default function AIOnboarding({ setMessages, onSendMessage }: AIOnboardin } return ( -
-
- -
+
- +
+
) : (
@@ -82,18 +82,7 @@ export const CronjobsTab = () => { onChange={(e) => setSearchQuery(e.target.value)} /> - +
{filteredCronJobs.length === 0 ? (
{ setCreateCronJobSheetShown(job)} + onEditCronJob={(job) => { + setCronJobForEditing(job) + setCreateCronJobSheetShown(true) + }} onDeleteCronJob={(job) => setCronJobForDeletion(job)} /> )) @@ -136,11 +128,12 @@ export const CronjobsTab = () => { > { setIsClosingCreateCronJobSheet(false) - setCreateCronJobSheetShown(undefined) + setCronJobForEditing(undefined) + setCreateCronJobSheetShown(false) }} isClosing={isClosingCreateCronJobSheet} setIsClosing={setIsClosingCreateCronJobSheet} diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx new file mode 100644 index 0000000000000..82798c330a23c --- /dev/null +++ b/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx @@ -0,0 +1,44 @@ +import { UseFormReturn } from 'react-hook-form' + +import { + FormControl_Shadcn_, + FormDescription_Shadcn_, + FormField_Shadcn_, + FormItem_Shadcn_, + FormLabel_Shadcn_, + FormMessage_Shadcn_, + SheetSection, + TextArea_Shadcn_, +} from 'ui' +import { CreateCronJobForm } from './CreateCronJobSheet' + +interface HttpBodyFieldSectionProps { + form: UseFormReturn +} + +export const HttpBodyFieldSection = ({ form }: HttpBodyFieldSectionProps) => { + return ( + + ( + + HTTP Request Body + + + + + The content should match the content-type header. + + + + )} + /> + + ) +} diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/HttpHeaderFieldsSection.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/HttpHeaderFieldsSection.tsx index bfd46415d0b14..6a76aac83d752 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/HttpHeaderFieldsSection.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/HttpHeaderFieldsSection.tsx @@ -15,6 +15,7 @@ import { FormControl_Shadcn_, FormField_Shadcn_, FormItem_Shadcn_, + FormLabel_Shadcn_, FormMessage_Shadcn_, Input_Shadcn_, SheetSection, @@ -38,7 +39,7 @@ export const HTTPHeaderFieldsSection = ({ variant }: HTTPHeaderFieldsSectionProp return ( - HTTP Headers + HTTP Headers
{fields.map((field, index) => (
diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/HttpParameterFieldsSection.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/HttpParameterFieldsSection.tsx deleted file mode 100644 index d00cb14571dc2..0000000000000 --- a/apps/studio/components/interfaces/Integrations/CronJobs/HttpParameterFieldsSection.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Plus, Trash } from 'lucide-react' -import { useFieldArray } from 'react-hook-form' - -import { - Button, - FormControl_Shadcn_, - FormField_Shadcn_, - FormItem_Shadcn_, - FormMessage_Shadcn_, - Input_Shadcn_, - SheetSection, -} from 'ui' -import { CreateCronJobForm } from './CreateCronJobSheet' - -interface HTTPParameterFieldsSectionProps { - variant: 'edge_function' | 'http_request' -} - -export const HTTPParameterFieldsSection = ({ variant }: HTTPParameterFieldsSectionProps) => { - // gets the fields through form context - const { fields, append, remove } = useFieldArray({ - name: 'values.httpParameters', - }) - - return ( - - HTTP Parameters -
- {fields.map((field, index) => ( -
- ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - -
- ))} -
- -
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/HttpRequestSection.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/HttpRequestSection.tsx index 13871ab4d7ace..03d522eb6263a 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/HttpRequestSection.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/HttpRequestSection.tsx @@ -51,7 +51,9 @@ export const HttpRequestSection = ({ form }: HttpRequestSectionProps) => { name="values.endpoint" render={({ field: { ref, ...rest } }) => ( - + + + )} /> diff --git a/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx b/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx index cad9403f5ea95..7d84094de0a5d 100644 --- a/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx +++ b/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx @@ -113,7 +113,7 @@ const supabaseIntegrations: IntegrationDefinition[] = [ id: 'cron-jobs', type: 'postgres_extension' as const, requiredExtensions: ['pg_cron'], - name: `Cron Jobs`, + name: `Cron`, icon: ({ className, ...props } = {}) => ( ), @@ -129,8 +129,8 @@ const supabaseIntegrations: IntegrationDefinition[] = [ label: 'Overview', }, { - route: 'cron-jobs', - label: 'Cron Jobs', + route: 'jobs', + label: 'Jobs', hasChild: true, childIcon: ( @@ -157,7 +157,7 @@ const supabaseIntegrations: IntegrationDefinition[] = [ loading: Loading, } ) - case 'cron-jobs': + case 'jobs': return dynamic(() => import('../CronJobs/CronJobsTab').then((mod) => mod.CronjobsTab), { loading: Loading, }) diff --git a/apps/studio/static-data/integrations/cron-jobs/overview.md b/apps/studio/static-data/integrations/cron-jobs/overview.md index b9cfc653dfa2f..dfb3bbbf8336b 100644 --- a/apps/studio/static-data/integrations/cron-jobs/overview.md +++ b/apps/studio/static-data/integrations/cron-jobs/overview.md @@ -1 +1 @@ -Cron jobs uses the `pg_cron` Postgres extension, which is a simple cron-based job scheduler for Postgres that runs inside the database. It uses the same syntax as a regular cron, but it allows you to schedule Postgres SQL commands directly from the database. +Supabase Cron uses the `pg_cron` Postgres extension, which is a simple cron-based job scheduler for Postgres that runs inside the database. It uses the same syntax as a regular cron, but it allows you to schedule Postgres SQL commands directly from the database. From 9e5cb993299be191d87fb6e49798cec20d1458e1 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 29 Nov 2024 09:55:55 -0330 Subject: [PATCH 3/4] Chore/misc integration nudges (#30720) * Misc fixes * Add webhooks as a module * Tiny little things --------- Co-authored-by: Ivan Vasilov --- .../Integration/IntegrationOverviewTab.tsx | 4 +-- .../Landing/AvailableIntegrations.tsx | 32 +++++++++++-------- .../Integrations/Landing/IntegrationCard.tsx | 32 +++++++++++++++---- .../Landing/Integrations.constants.tsx | 6 ++-- .../[id]/[pageId]/[childId]/index.tsx | 2 +- .../integrations/[id]/[pageId]/index.tsx | 2 +- .../project/[ref]/integrations/[id]/index.tsx | 2 +- 7 files changed, 51 insertions(+), 29 deletions(-) diff --git a/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.tsx b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.tsx index c17d3158abb90..d86dbac261bfb 100644 --- a/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.tsx +++ b/apps/studio/components/interfaces/Integrations/Integration/IntegrationOverviewTab.tsx @@ -59,11 +59,11 @@ export const IntegrationOverviewTab = ({ src={`${router.basePath}/img/supabase-logo.svg`} className=" h-2.5 cursor-pointer rounded" /> - Database Extension + Postgres Module `\`${x}\``).join(', ')} + content={`This integration uses the ${integration.requiredExtensions.map((x) => `\`${x}\``).join(', ')} extension${integration.requiredExtensions.length > 1 ? 's' : ''} directly in your Postgres database. ${hasMissingExtensions ? `Install ${integration.requiredExtensions.length > 1 ? 'these' : 'this'} database extension${integration.requiredExtensions.length > 1 ? 's' : ''} to use ${integration.name} in your project.` : ''} `} diff --git a/apps/studio/components/interfaces/Integrations/Landing/AvailableIntegrations.tsx b/apps/studio/components/interfaces/Integrations/Landing/AvailableIntegrations.tsx index d00fd8f68ffa8..2b4ce16f35467 100644 --- a/apps/studio/components/interfaces/Integrations/Landing/AvailableIntegrations.tsx +++ b/apps/studio/components/interfaces/Integrations/Landing/AvailableIntegrations.tsx @@ -10,6 +10,11 @@ import { IntegrationCard, IntegrationLoadingCard } from './IntegrationCard' import { useInstalledIntegrations } from './useInstalledIntegrations' type IntegrationCategory = 'all' | 'wrapper' | 'postgres_extensions' | 'custom' +const CATEGORIES = [ + { key: 'all', label: 'All Integrations' }, + { key: 'wrapper', label: 'Wrappers' }, + { key: 'postgres_extension', label: 'Postgres Modules' }, +] as const export const AvailableIntegrations = () => { const [selectedCategory, setSelectedCategory] = useQueryState( @@ -29,10 +34,8 @@ export const AvailableIntegrations = () => { // available integrations for install const integrationsByCategory = selectedCategory === 'all' - ? availableIntegrations.filter((i) => !installedIds.includes(i.id)) - : availableIntegrations.filter( - (i) => !installedIds.includes(i.id) && i.type === selectedCategory - ) + ? availableIntegrations + : availableIntegrations.filter((i) => i.type === selectedCategory) const filteredIntegrations = ( search.length > 0 ? integrationsByCategory.filter((i) => i.name.toLowerCase().includes(search.toLowerCase())) @@ -47,23 +50,21 @@ export const AvailableIntegrations = () => { onValueChange={(value) => setSelectedCategory(value as IntegrationCategory)} > - {['all', 'wrapper', 'postgres_extension'].map((category) => ( + {CATEGORIES.map((category) => ( setSelectedCategory(category as IntegrationCategory)} + key={category.key} + value={category.key} + onClick={() => setSelectedCategory(category.key as IntegrationCategory)} className={cn( buttonVariants({ size: 'tiny', - type: selectedCategory === category ? 'default' : 'outline', + type: selectedCategory === category.key ? 'default' : 'outline', }), - selectedCategory === category ? 'text-foreground' : 'text-foreground-lighter', + selectedCategory === category.key ? 'text-foreground' : 'text-foreground-lighter', '!rounded-full px-3' )} > - {category === 'all' - ? 'All Integrations' - : category.replace('_', ' ').replace(/\b\w/g, (char) => char.toUpperCase())} + {category.label} ))} { error={error} /> )} - {isSuccess && filteredIntegrations.map((i) => )} + {isSuccess && + filteredIntegrations.map((i) => ( + + ))} {isSuccess && search.length > 0 && filteredIntegrations.length === 0 && ( { ) } -export const IntegrationCard = ({ id, beta, name, icon, description }: IntegrationCardProps) => { +export const IntegrationCard = ({ + id, + beta, + name, + icon, + description, + isInstalled, +}: IntegrationCardProps) => { const { project } = useProjectContext() return ( @@ -37,7 +47,7 @@ export const IntegrationCard = ({ id, beta, name, icon, description }: Integrati
{icon()}
-
+

{name}

@@ -49,9 +59,17 @@ export const IntegrationCard = ({ id, beta, name, icon, description }: Integrati

{description}

- - Official - +
+ + Official + + {isInstalled && ( +
+ + Installed +
+ )} +
diff --git a/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx b/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx index 7d84094de0a5d..f016a84e281ab 100644 --- a/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx +++ b/apps/studio/components/interfaces/Integrations/Landing/Integrations.constants.tsx @@ -226,8 +226,8 @@ const supabaseIntegrations: IntegrationDefinition[] = [ }, { id: 'webhooks', - type: 'custom' as const, - name: `Webhooks`, + type: 'postgres_extension' as const, + name: `Database Webhooks`, icon: ({ className, ...props } = {}) => ( ), @@ -276,7 +276,7 @@ const supabaseIntegrations: IntegrationDefinition[] = [ id: 'graphiql', type: 'postgres_extension' as const, requiredExtensions: ['pg_graphql'], - name: `GraphiQL`, + name: `GraphQL`, icon: ({ className, ...props } = {}) => ( { return
Integration not found
} - if (!Component) return
Component not found
+ if (!Component) return
Component not found
return } diff --git a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx index e25dc2980fead..99f17904eb3df 100644 --- a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx +++ b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx @@ -53,7 +53,7 @@ const IntegrationPage: NextPageWithLayout = () => { return
Integration not found
} - if (!Component) return
Component not found
+ if (!Component) return
Component not found
return } diff --git a/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx b/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx index e25dc2980fead..99f17904eb3df 100644 --- a/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx +++ b/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx @@ -53,7 +53,7 @@ const IntegrationPage: NextPageWithLayout = () => { return
Integration not found
} - if (!Component) return
Component not found
+ if (!Component) return
Component not found
return } From 4caed0103df9e0ebc0e10bc557c4ca3d20b55267 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Fri, 29 Nov 2024 16:57:21 +0100 Subject: [PATCH 4/4] fix: Fix a timing issue when deleting a branch (#30736) * Add a timeout for invalidating the branch list queries. Add an optimistic deletion from the query cache when deleting a branch. * Don't refetch the branches list if one of the branches is faulty. The branch will be updated via useBranchQuery. --- .../BranchManagement/BranchManagement.tsx | 20 +------------------ .../data/branches/branch-delete-mutation.ts | 14 ++++++++++++- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx index 74b5b4919395b..3928bea2096ec 100644 --- a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx +++ b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx @@ -78,25 +78,7 @@ const BranchManagement = () => { isLoading: isLoadingBranches, isError: isErrorBranches, isSuccess: isSuccessBranches, - } = useBranchesQuery( - { projectRef }, - { - refetchInterval(data) { - if ( - data?.some( - (branch) => - branch.status === 'CREATING_PROJECT' || - branch.status === 'RUNNING_MIGRATIONS' || - branch.status === 'MIGRATIONS_FAILED' - ) - ) { - return 1000 * 3 // 3 seconds - } - - return false - }, - } - ) + } = useBranchesQuery({ projectRef }) const [[mainBranch], previewBranchesUnsorted] = partition(branches, (branch) => branch.is_default) const previewBranches = previewBranchesUnsorted.sort((a, b) => new Date(a.updated_at) < new Date(b.updated_at) ? 1 : -1 diff --git a/apps/studio/data/branches/branch-delete-mutation.ts b/apps/studio/data/branches/branch-delete-mutation.ts index b6682c4f915e9..5a2a2815fe4ee 100644 --- a/apps/studio/data/branches/branch-delete-mutation.ts +++ b/apps/studio/data/branches/branch-delete-mutation.ts @@ -3,6 +3,7 @@ import { toast } from 'sonner' import { del, handleError } from 'data/fetchers' import type { ResponseError } from 'types' +import { BranchesData } from './branches-query' import { branchKeys } from './keys' export type BranchDeleteVariables = { @@ -35,7 +36,18 @@ export const useBranchDeleteMutation = ({ { async onSuccess(data, variables, context) { const { projectRef } = variables - await queryClient.invalidateQueries(branchKeys.list(projectRef)) + setTimeout(() => { + queryClient.invalidateQueries(branchKeys.list(projectRef)) + }, 5000) + + const branches: BranchesData | undefined = queryClient.getQueryData( + branchKeys.list(projectRef) + ) + if (branches) { + const updatedBranches = branches.filter((branch) => branch.id !== variables.id) + queryClient.setQueryData(branchKeys.list(projectRef), updatedBranches) + } + await onSuccess?.(data, variables, context) }, async onError(data, variables, context) {