Files
supabase/apps/studio/components/interfaces/Database/Extensions/Extensions.tsx
Joshen Lim 12d92aed99 Assistant V2 (#30523)
* start

* added panels

* remove stuff

* fixes and refinements

* clean up

* remove old assistant panel

* resizable assistant kinda

* use icon

* Add missing package

* remove canvas

* add suggestions

* updated empty state if no tables exist

* fix table condition

* Implement diffing if using assistant in sql editor

* Reinstate old assistant in SQL editor if feature preview is off

* pane size adjustment

* assistant button corners

* Add SQL snippet content to assistant if opening assistant in sql editor

* Add the necessary checks for opt in and hipaa

* revert adding snippet to assistant when opening assistant in sql editor

* Add cmd i shortcut

* Add admonitions for when disablePrompt is toggled on, and if no api key is set. Add footer note RE rate limitation

* Bump ai package in packages

* some fixes for backwards compability depending on feature preview toggled

* Rename feature preview property for new assistant

* Smol fix

* Prevent SQL snippet from running until message is finished

* only loading last message

* fix z-index

* save chat state to global state

* add debug to failed ai queries

* Add basic contextual invalidation

* Add explain code action to SQL editor

* Add link to abort ongoing queries from SqlSnippet

* Update feature preview content

* Fix

* Fix

* Fix

* Te4st

* Fix tests

* ONly show ai button within a project

* Fix PH tracking

* Beef up a bit more event tracking

* Rough fix to padding when assistant is open

* A bit more telemetry stuff

* Update prompts

* fix rls editing via assistant

* Update generate-v3.ts

prompt to get auth schema too

* Add policy satement to assistant when editing

* Address all comments

* fixc

* Fix SqlSnippet not taking full width on larger viewports

* Adjust max width

---------

Co-authored-by: Saxon Fletcher <saxonafletcher@gmail.com>
2024-11-25 18:50:56 +08:00

129 lines
4.6 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { isNull, partition } from 'lodash'
import { AlertCircle, Search } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import InformationBox from 'components/ui/InformationBox'
import NoSearchResults from 'components/ui/NoSearchResults'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
import { Input } from 'ui'
import ExtensionCard from './ExtensionCard'
import ExtensionCardSkeleton from './ExtensionCardSkeleton'
import { HIDDEN_EXTENSIONS, SEARCH_TERMS } from './Extensions.constants'
const Extensions = () => {
const { filter } = useParams()
const { project } = useProjectContext()
const [filterString, setFilterString] = useState<string>('')
const { data, isLoading } = useDatabaseExtensionsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const extensions =
filterString.length === 0
? data ?? []
: (data ?? []).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 extensionsWithoutHidden = extensions.filter((ext) => !HIDDEN_EXTENSIONS.includes(ext.name))
const [enabledExtensions, disabledExtensions] = partition(
extensionsWithoutHidden,
(ext) => !isNull(ext.installed_version)
)
const canUpdateExtensions = useCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
'extensions'
)
const isPermissionsLoaded = usePermissionsLoaded()
useEffect(() => {
if (filter !== undefined) setFilterString(filter as string)
}, [filter])
return (
<>
<div className="mb-4">
<div className="flex items-center justify-between">
<Input
size="tiny"
placeholder="Search for an extension"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
className="w-52"
icon={<Search size={14} />}
/>
<DocsButton href="https://supabase.com/docs/guides/database/extensions" />
</div>
</div>
{isPermissionsLoaded && !canUpdateExtensions && (
<InformationBox
icon={<AlertCircle className="text-foreground-light" size={18} strokeWidth={2} />}
title="You need additional permissions to update database extensions"
/>
)}
{isLoading ? (
<div className="my-8 w-full space-y-12">
<div className="space-y-4">
<ShimmeringLoader className="h-[28px] w-40" />
<div className="mb-4 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
{Array.from({ length: 6 }).map((_, index) => (
<ExtensionCardSkeleton key={index} index={index} />
))}
</div>
</div>
</div>
) : (
<>
{extensions.length === 0 && (
<NoSearchResults
searchString={filterString}
onResetFilter={() => setFilterString('')}
/>
)}
<div className="my-8 w-full space-y-12">
{enabledExtensions.length > 0 && (
<div className="space-y-4">
<h4 className="text-lg">Enabled extensions</h4>
<div className="mb-4 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
{enabledExtensions.map((extension) => (
<ExtensionCard key={extension.name} extension={extension} />
))}
</div>
</div>
)}
{disabledExtensions.length > 0 && (
<div className="space-y-4">
<h4 className="text-lg">Available extensions</h4>
<div className="mb-4 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
{disabledExtensions.map((extension) => (
<ExtensionCard key={extension.name} extension={extension} />
))}
</div>
</div>
)}
</div>
</>
)}
</>
)
}
export default Extensions