| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- import { Search } from 'lucide-react'
- import { parseAsString, useQueryState } from 'nuqs'
- import { buttonVariants, cn, Tabs_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_ } from 'ui'
- import { Admonition } from 'ui-patterns/admonition'
- import { Input } from 'ui-patterns/DataInputs/Input'
- import { IntegrationCard, IntegrationLoadingCard } from './IntegrationCard'
- import { useAvailableIntegrations } from './useAvailableIntegrations'
- import { useInstalledIntegrations } from './useInstalledIntegrations'
- import AlertError from '@/components/ui/AlertError'
- import { NoSearchResults } from '@/components/ui/NoSearchResults'
- import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
- 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 { integrationsWrappers } = useIsFeatureEnabled(['integrations:wrappers'])
- const [selectedCategory, setSelectedCategory] = useQueryState(
- 'category',
- parseAsString.withDefault('all').withOptions({ clearOnDefault: true })
- )
- const [search, setSearch] = useQueryState(
- 'search',
- parseAsString.withDefault('').withOptions({ clearOnDefault: true })
- )
- const { data: allIntegrations = [] } = useAvailableIntegrations()
- const { installedIntegrations, error, isError, isLoading, isSuccess } = useInstalledIntegrations()
- const installedIds = installedIntegrations.map((i) => i.id)
- // available integrations for install
- const availableIntegrations = integrationsWrappers
- ? allIntegrations
- : allIntegrations.filter((x) => !x.id.endsWith('_wrapper'))
- const integrationsByCategory =
- selectedCategory === 'all'
- ? availableIntegrations
- : availableIntegrations.filter((i) => i.type === selectedCategory)
- const filteredIntegrations = (
- search.length > 0
- ? integrationsByCategory.filter((i) => i.name.toLowerCase().includes(search.toLowerCase()))
- : integrationsByCategory
- ).sort((a, b) => a.name.localeCompare(b.name))
- return (
- <>
- <Tabs_Shadcn_
- className="mt-4"
- value={selectedCategory}
- onValueChange={(value) => setSelectedCategory(value as IntegrationCategory)}
- >
- <TabsList_Shadcn_ className="px-4 md:px-10 gap-2 border-b-0 border-t pt-5">
- {CATEGORIES.map((category) => (
- <TabsTrigger_Shadcn_
- key={category.key}
- value={category.key}
- onClick={() => setSelectedCategory(category.key as IntegrationCategory)}
- className={cn(
- buttonVariants({
- size: 'tiny',
- type: selectedCategory === category.key ? 'default' : 'outline',
- }),
- selectedCategory === category.key ? 'text-foreground' : 'text-foreground-lighter',
- 'rounded-full! px-3'
- )}
- >
- {category.label}
- </TabsTrigger_Shadcn_>
- ))}
- <Input
- value={search}
- onChange={(e) => {
- setSearch(e.target.value)
- setSelectedCategory('all')
- }}
- containerClassName="group w-40 ml-5"
- icon={
- <Search
- size={14}
- className="transition text-foreground-lighter group-hover:text-foreground"
- />
- }
- iconContainerClassName="p-0"
- className="pl-7 rounded-none border-0! border-transparent bg-transparent shadow-none! ring-0! ring-offset-0!"
- placeholder="Search..."
- />
- </TabsList_Shadcn_>
- </Tabs_Shadcn_>
- <div className="p-4 md:p-10 md:py-8 flex flex-col gap-y-5">
- <div className="grid xl:grid-cols-3 2xl:grid-cols-4 gap-x-4 gap-y-3">
- {isLoading &&
- Array.from({ length: 3 }).map((_, idx) => (
- <IntegrationLoadingCard key={`integration-loading-${idx}`} />
- ))}
- {isError && (
- <AlertError
- className="xl:col-span-3 2xl:col-span-4"
- subject="Failed to retrieve available integrations"
- error={error}
- />
- )}
- {isSuccess &&
- filteredIntegrations.map((i) => (
- <IntegrationCard key={i.id} {...i} isInstalled={installedIds.includes(i.id)} />
- ))}
- {isSuccess && search.length > 0 && filteredIntegrations.length === 0 && (
- <NoSearchResults
- className="xl:col-span-3 2xl:col-span-4"
- searchString={search}
- onResetFilter={() => setSearch('')}
- />
- )}
- {isSuccess &&
- selectedCategory !== 'all' &&
- search.length === 0 &&
- filteredIntegrations.length === 0 && (
- <Admonition
- showIcon={false}
- className="xl:col-span-3 2xl:col-span-4"
- type="default"
- title="All integrations in this category are currently in use"
- description="Manage your installed integrations in the section above"
- />
- )}
- </div>
- </div>
- </>
- )
- }
|