// @ts-nocheck import { useQuery } from '@tanstack/react-query' import { parseAsString, parseAsStringEnum, useQueryState } from 'nuqs' import { useMemo } from 'react' import { Button, Card, ShadowScrollArea, Table, TableBody, TableHeader } from 'ui' import { EmptyStatePresentational, PageContainer, PageHeader, PageHeaderDescription, PageHeaderMeta, PageHeaderSummary, PageHeaderTitle, } from 'ui-patterns' import { EXCLUDED_CATEGORY_SLUGS, FEATURED_INTEGRATION_IDS, formatCategoryLabel, getMarketplaceSource, getMarketplaceType, INTEGRATION_TYPES, MARKETPLACE_SOURCES, type MarketplaceIntegrationType, type MarketplaceSource, } from './Marketplace.constants' import { MarketplaceCard } from './MarketplaceCard' import { MarketplaceFeaturedHero } from './MarketplaceFeaturedHero' import { MarketplaceFilterBar, type ViewMode } from './MarketplaceFilterBar' import { MarketplaceListHeader, MarketplaceListRow } from './MarketplaceListRow' import { IntegrationLoadingCard } from '@/components/interfaces/Integrations/Landing/IntegrationCard' import { useAvailableIntegrations } from '@/components/interfaces/Integrations/Landing/useAvailableIntegrations' import { useInstalledIntegrations } from '@/components/interfaces/Integrations/Landing/useInstalledIntegrations' import { AlertError } from '@/components/ui/AlertError' import { DocsButton } from '@/components/ui/DocsButton' import { useMarketplaceCategoriesQuery } from '@/data/marketplace/integration-categories-query' import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' import { DOCS_URL } from '@/lib/constants' const MARKETPLACE_VIEW_MODE_STORAGE_KEY = 'briven.marketplace.viewMode' export const MarketplaceIndex = () => { const [search, setSearch] = useQueryState( 'search', parseAsString.withDefault('').withOptions({ clearOnDefault: true }) ) const [category, setCategory] = useQueryState( 'category', parseAsString.withOptions({ clearOnDefault: true }) ) const [type, setType] = useQueryState( 'type', parseAsStringEnum([ 'oauth', 'postgres_extension', 'template', 'wrapper', ]).withOptions({ clearOnDefault: true }) ) const [source, setSource] = useQueryState( 'source', parseAsStringEnum(['Official', 'Partner', 'Community']).withOptions({ clearOnDefault: true, }) ) const [viewMode, setViewMode] = useLocalStorageQuery( MARKETPLACE_VIEW_MODE_STORAGE_KEY, 'grid' ) const { data: availableIntegrations, error, isPending: isLoadingAvailable, isError, isSuccess: isSuccessAvailable, } = useAvailableIntegrations() const { installedIntegrations, isLoading: isLoadingInstalled, isSuccess: isSuccessInstalled, } = useInstalledIntegrations() const installedIds = installedIntegrations.map((i) => i.id) const isLoading = isLoadingAvailable || isLoadingInstalled const isSuccess = isSuccessAvailable && isSuccessInstalled const { data: marketplaceCategories = [] } = useQuery(useMarketplaceCategoriesQuery()) const categoryOptions = useMemo( () => marketplaceCategories .filter( (c): c is { slug: string; name: string } & typeof c => !!c.slug && !!c.name && !EXCLUDED_CATEGORY_SLUGS.has(c.slug) ) .map((c) => ({ slug: c.slug, name: c.name })), [marketplaceCategories] ) const typeCounts = useMemo(() => { const all = availableIntegrations ?? [] return Object.fromEntries( INTEGRATION_TYPES.map(({ key }) => [ key, all.filter((i) => getMarketplaceType(i) === key).length, ]) ) as Record }, [availableIntegrations]) const sourceCounts = useMemo(() => { const all = availableIntegrations ?? [] return Object.fromEntries( MARKETPLACE_SOURCES.map(({ key }) => [ key, all.filter((i) => getMarketplaceSource(i) === key).length, ]) ) as Record }, [availableIntegrations]) const categoryCounts = useMemo(() => { const all = availableIntegrations ?? [] const counts: Record = {} for (const { slug } of categoryOptions) { counts[slug] = all.filter((i) => i.categories?.includes(slug)).length } return counts }, [availableIntegrations, categoryOptions]) const hasActiveFilter = !!(category || type || source) const hasSearchOrFilter = hasActiveFilter || search.length > 0 const filtered = useMemo(() => { let result = availableIntegrations ?? [] if (category) { result = result.filter((i) => i.categories?.includes(category)) } if (type) { result = result.filter((i) => getMarketplaceType(i) === type) } if (source) { result = result.filter((i) => getMarketplaceSource(i) === source) } if (search.length > 0) { const needle = search.toLowerCase() result = result.filter((i) => i.name.toLowerCase().includes(needle)) } return [...result].sort((a, b) => { const aInstalled = installedIds.includes(a.id) const bInstalled = installedIds.includes(b.id) if (aInstalled && !bInstalled) return -1 if (!aInstalled && bInstalled) return 1 return a.name.localeCompare(b.name) }) }, [availableIntegrations, category, type, source, search, installedIds]) const featured = useMemo(() => { if (hasSearchOrFilter) return [] const byId = new Map((availableIntegrations ?? []).map((i) => [i.id, i])) return FEATURED_INTEGRATION_IDS.map((id) => byId.get(id)).filter( (i): i is NonNullable => !!i ) }, [availableIntegrations, hasSearchOrFilter]) const clearAll = () => { setCategory(null) setType(null) setSource(null) setSearch('') } const activeFilters = [category, type, source].filter(Boolean) const pageTitle = useMemo(() => { if (activeFilters.length !== 1) return 'Integrations Marketplace' if (category) return `Integrations Marketplace: ${formatCategoryLabel(category, categoryOptions)}` if (type) return `Integrations Marketplace: ${INTEGRATION_TYPES.find((t) => t.key === type)?.label ?? type}s` if (source) return `Integrations Marketplace: ${MARKETPLACE_SOURCES.find((s) => s.key === source)?.label ?? source}` return 'Integrations Marketplace' }, [activeFilters.length, category, type, source, categoryOptions]) return ( <> {pageTitle} Explore native and third-party integrations to add functionality to your Briven project.
{isLoading && (
{Array.from({ length: 8 }).map((_, idx) => ( ))}
)} {isError && ( )} {isSuccess && ( <> {featured.length > 0 && ( )} setSearch(v)} category={category} onCategoryChange={(v) => setCategory(v)} categoryOptions={categoryOptions} categoryCounts={categoryCounts} type={type} onTypeChange={(v) => setType(v)} typeCounts={typeCounts} source={source} onSourceChange={(v) => setSource(v)} sourceCounts={sourceCounts} viewMode={viewMode} onViewModeChange={(v) => setViewMode(v)} hasActiveFilter={hasActiveFilter} onClearFilters={clearAll} /> {filtered.length === 0 && ( )} {filtered.length > 0 && (viewMode === 'grid' ? (
{filtered.map((integration) => ( ))}
) : ( {filtered.map((integration) => ( ))}
))} )}
) }