Files
supabase/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx
T
Ivan VasilovandJordi Enric 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

132 lines
4.7 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { isNull, partition } from 'lodash'
import { AlertCircle, Search } from 'lucide-react'
import { useEffect, useState } from 'react'
import {
Card,
Input,
ShadowScrollArea,
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'
export const Extensions = () => {
const { filter } = useParams()
const { data: project } = useSelectedProjectQuery()
const [filterString, setFilterString] = useState<string>('')
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 (
<>
<div className="mb-4">
<Input
size="tiny"
placeholder="Search for an extension"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
className="w-52"
icon={<Search />}
/>
</div>
{isPermissionsLoaded && !canUpdateExtensions && (
<InformationBox
icon={<AlertCircle className="text-foreground-light" size={18} strokeWidth={2} />}
title="You need additional permissions to update database extensions"
/>
)}
{isLoading ? (
<GenericSkeletonLoader />
) : (
<Card>
<ShadowScrollArea stickyLastColumn>
<Table>
<TableHeader>
<TableRow>
<TableHead key="name">Name</TableHead>
<TableHead key="version">Version</TableHead>
<TableHead key="schema">Schema</TableHead>
<TableHead key="description">Description</TableHead>
<TableHead key="used-by">Used by</TableHead>
<TableHead key="links">Links</TableHead>
{/*
[Joshen] All these classes are just to make the last column sticky
I reckon we can pull these out into the Table component where we can declare
sticky columns via props, but we can do that if we start to have more tables
in the dashboard with sticky columns
*/}
<TableHead key="enabled" className="px-0">
<div className="bg-200! px-4 w-full h-full flex items-center border-l">
Enabled
</div>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{[...enabledExtensions, ...disabledExtensions].map((extension) => (
<ExtensionRow key={extension.name} extension={extension} />
))}
{extensions.length === 0 && (
<TableRow>
<TableCell colSpan={7}>
<NoSearchResults
className="border-none p-0! bg-transparent"
searchString={filterString}
onResetFilter={() => setFilterString('')}
/>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</ShadowScrollArea>
</Card>
)}
</>
)
}