import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { isNull, partition } from 'lodash' import { AlertCircleIcon, SearchIcon } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { Card, InputGroup, InputGroupAddon, InputGroupInput, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { ExtensionRow } from './ExtensionRow' import { HIDDEN_EXTENSIONS, SEARCH_TERMS } from './Extensions.constants' import InformationBox from '@/components/ui/InformationBox' import { NoSearchResults } from '@/components/ui/NoSearchResults' import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { onSearchInputEscape } from '@/lib/keyboard' import { SHORTCUT_IDS } from '@/state/shortcuts/registry' import { useShortcut } from '@/state/shortcuts/useShortcut' export const Extensions = () => { const { filter } = useParams() const { data: project } = useSelectedProjectQuery() const [filterString, setFilterString] = useState('') const searchInputRef = useRef(null) useShortcut( SHORTCUT_IDS.LIST_PAGE_FOCUS_SEARCH, () => { searchInputRef.current?.focus() searchInputRef.current?.select() }, { label: 'Search extensions' } ) useShortcut(SHORTCUT_IDS.LIST_PAGE_RESET_FILTERS, () => { setFilterString('') }) const { data = [], isPending: isLoading } = useDatabaseExtensionsQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const visibleExtensions = data.filter((ext) => !HIDDEN_EXTENSIONS.includes(ext.name)) const extensions = filterString.length === 0 ? visibleExtensions : visibleExtensions.filter((ext) => { const nameMatchesSearch = ext.name.toLowerCase().includes(filterString.toLowerCase()) const searchTermsMatchesSearch = (SEARCH_TERMS[ext.name] || []).some((x) => x.includes(filterString.toLowerCase()) ) return nameMatchesSearch || searchTermsMatchesSearch }) const [enabledExtensions, disabledExtensions] = partition( extensions, (ext) => !isNull(ext.installed_version) ) const { can: canUpdateExtensions, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions( PermissionAction.TENANT_SQL_ADMIN_WRITE, 'extensions' ) useEffect(() => { if (filter !== undefined) setFilterString(filter as string) }, [filter]) return ( <>
setFilterString(e.target.value)} onKeyDown={onSearchInputEscape(filterString, setFilterString)} />
{isPermissionsLoaded && !canUpdateExtensions && ( } title="You need additional permissions to update database extensions" /> )} {isLoading ? ( ) : ( Name Version Schema Description Used by Links
Enabled
{[...enabledExtensions, ...disabledExtensions].map((extension) => ( ))} {extensions.length === 0 && ( setFilterString('')} /> )}
)} ) }