Files
supabase/apps/studio/components/grid/context/TableIndexAdvisorContext.tsx
Ivan Vasilov 56de26fe22 chore: Migrate the monorepo to use Tailwind v4 (#45318)
This PR migrates the whole monorepo to use Tailwind v4:
- Removed `@tailwindcss/container-queries` plugin since it's included by
default in v4,
- Bump all instances of Tailwind to v4. Made minimal changes to the
shared config to remove non-supported features (`alpha` mentions),
- Migrate all apps to be compatible with v4 configs,
- Fix the `typography.css` import in 3 apps,
- Add missing rules which were included by default in v3,
- Run `pnpm dlx @tailwindcss/upgrade` on all apps, which renames a lot
of classes
- Rename all misnamed classes according to
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities in all
apps.

---------

Co-authored-by: Jordi Enric <jordi.err@gmail.com>
2026-04-30 10:53:24 +00:00

158 lines
5.2 KiB
TypeScript

import { useQueryClient } from '@tanstack/react-query'
import { createContext, PropsWithChildren, useCallback, useContext, useState } from 'react'
import { Sheet, SheetContent, SheetHeader, SheetTitle } from 'ui'
import { useIndexAdvisorStatus } from '@/components/interfaces/QueryPerformance/hooks/useIsIndexAdvisorStatus'
import { QueryIndexes } from '@/components/interfaces/QueryPerformance/QueryIndexes'
import { databaseKeys } from '@/data/database/keys'
import {
cleanIndexColumnName,
IndexAdvisorSuggestion,
TableIndexAdvisorData,
useTableIndexAdvisorQuery,
} from '@/data/database/table-index-advisor-query'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
interface TableIndexAdvisorContextValue {
isLoading: boolean
isAvailable: boolean
isEnabled: boolean
columnsWithSuggestions: string[]
suggestions: TableIndexAdvisorData['suggestions']
openSheet: (columnName: string) => void
getSuggestionsForColumn: (columnName: string) => IndexAdvisorSuggestion[]
invalidate: () => Promise<void>
}
const TableIndexAdvisorContext = createContext<TableIndexAdvisorContextValue>({
isLoading: false,
isAvailable: false,
isEnabled: false,
columnsWithSuggestions: [],
suggestions: [],
openSheet: () => {},
getSuggestionsForColumn: () => [],
invalidate: async () => {},
})
interface TableIndexAdvisorProviderProps {
schema: string
table: string
}
export function TableIndexAdvisorProvider({
children,
schema,
table,
}: PropsWithChildren<TableIndexAdvisorProviderProps>) {
const { data: project } = useSelectedProjectQuery()
const { isIndexAdvisorAvailable, isIndexAdvisorEnabled } = useIndexAdvisorStatus()
const queryClient = useQueryClient()
const [isSheetOpen, setIsSheetOpen] = useState(false)
const [selectedColumn, setSelectedColumn] = useState<string | undefined>(undefined)
const { data, isLoading } = useTableIndexAdvisorQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
schema,
table,
},
{
enabled: isIndexAdvisorEnabled && !!schema && !!table,
}
)
const openSheet = useCallback((columnName: string) => {
setSelectedColumn(columnName)
setIsSheetOpen(true)
}, [])
const closeSheet = useCallback(() => {
setIsSheetOpen(false)
setSelectedColumn(undefined)
}, [])
const getSuggestionsForColumn = useCallback(
(columnName: string): IndexAdvisorSuggestion[] => {
if (!data?.suggestions) return []
// Filter suggestions that include this column in their index statements
return data.suggestions.filter((suggestion) =>
suggestion.index_statements.some((stmt) => {
const match = stmt.match(/USING\s+\w+\s*\(([^)]+)\)/i)
if (match) {
const columns = match[1].split(',').map((c) => cleanIndexColumnName(c))
return columns.includes(columnName)
}
return false
})
)
},
[data?.suggestions]
)
const invalidate = useCallback(async () => {
if (project?.ref && schema && table) {
await queryClient.invalidateQueries({
queryKey: databaseKeys.tableIndexAdvisor(project.ref, schema, table),
})
}
}, [queryClient, project?.ref, schema, table])
// Get the first suggestion for the selected column to pass to QueryIndexes
const selectedSuggestion = selectedColumn ? getSuggestionsForColumn(selectedColumn)[0] : null
const prefetchedIndexAdvisorResult = selectedSuggestion
? {
errors: [],
index_statements: selectedSuggestion.index_statements,
startup_cost_before: selectedSuggestion.startup_cost_before,
startup_cost_after: selectedSuggestion.startup_cost_after,
total_cost_before: selectedSuggestion.total_cost_before,
total_cost_after: selectedSuggestion.total_cost_after,
}
: null
const value: TableIndexAdvisorContextValue = {
isLoading,
isAvailable: isIndexAdvisorAvailable,
isEnabled: isIndexAdvisorEnabled,
columnsWithSuggestions: data?.columnsWithSuggestions ?? [],
suggestions: data?.suggestions ?? [],
openSheet,
getSuggestionsForColumn,
invalidate,
}
return (
<TableIndexAdvisorContext.Provider value={value}>
{children}
<Sheet open={isSheetOpen} onOpenChange={(open) => !open && closeSheet()}>
<SheetContent className="flex flex-col gap-0 p-0 lg:w-[calc(100vw-802px)]! max-w-[700px]">
<SheetHeader className="border-b px-5 py-3">
<SheetTitle>Index Recommendation</SheetTitle>
</SheetHeader>
{selectedSuggestion && (
<QueryIndexes
selectedRow={{ query: selectedSuggestion.query }}
columnName={selectedColumn}
suggestedSelectQuery={selectedSuggestion.query}
prefetchedIndexAdvisorResult={prefetchedIndexAdvisorResult}
onClose={closeSheet}
/>
)}
</SheetContent>
</Sheet>
</TableIndexAdvisorContext.Provider>
)
}
export function useTableIndexAdvisor() {
return useContext(TableIndexAdvisorContext)
}
export function useColumnHasIndexSuggestion(columnName: string): boolean {
const { columnsWithSuggestions } = useTableIndexAdvisor()
return columnsWithSuggestions.includes(columnName)
}