feat(web): segregate connectors into knowledge base and tools/live sources

This commit is contained in:
CREDO23 2026-06-02 16:37:54 +02:00
parent af192a8405
commit ef60af90cf
2 changed files with 76 additions and 80 deletions

View file

@ -243,6 +243,38 @@ export function getConnectorTitle(connectorType: string): string {
); );
} }
/**
* Primary way a user interacts with a connector.
* Drives the two top-level groupings in the connector catalog UI.
*/
export type ConnectorCategory = "knowledge_base" | "tools_live";
export const CONNECTOR_CATEGORY_LABELS: Record<ConnectorCategory, string> = {
knowledge_base: "Knowledge Base",
tools_live: "Tools & Live Sources",
};
const KNOWLEDGE_BASE_CONNECTOR_TYPES = new Set<string>([
EnumConnectorName.GOOGLE_DRIVE_CONNECTOR,
EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR,
EnumConnectorName.ONEDRIVE_CONNECTOR,
EnumConnectorName.DROPBOX_CONNECTOR,
EnumConnectorName.NOTION_CONNECTOR,
EnumConnectorName.CONFLUENCE_CONNECTOR,
EnumConnectorName.YOUTUBE_CONNECTOR,
EnumConnectorName.WEBCRAWLER_CONNECTOR,
EnumConnectorName.BOOKSTACK_CONNECTOR,
EnumConnectorName.GITHUB_CONNECTOR,
EnumConnectorName.ELASTICSEARCH_CONNECTOR,
EnumConnectorName.CIRCLEBACK_CONNECTOR,
EnumConnectorName.OBSIDIAN_CONNECTOR,
]);
/** Unmapped connectors surface under Tools & Live Sources. */
export function getConnectorCategory(connectorType: string): ConnectorCategory {
return KNOWLEDGE_BASE_CONNECTOR_TYPES.has(connectorType) ? "knowledge_base" : "tools_live";
}
// Composio Toolkits (available integrations via Composio) // Composio Toolkits (available integrations via Composio)
export const COMPOSIO_TOOLKITS = [ export const COMPOSIO_TOOLKITS = [
{ {

View file

@ -9,7 +9,10 @@ import { isSelfHosted } from "@/lib/env-config";
import { ConnectorCard } from "../components/connector-card"; import { ConnectorCard } from "../components/connector-card";
import { import {
COMPOSIO_CONNECTORS, COMPOSIO_CONNECTORS,
CONNECTOR_CATEGORY_LABELS,
type ConnectorCategory,
CRAWLERS, CRAWLERS,
getConnectorCategory,
OAUTH_CONNECTORS, OAUTH_CONNECTORS,
OTHER_CONNECTORS, OTHER_CONNECTORS,
} from "../constants/connector-constants"; } from "../constants/connector-constants";
@ -20,19 +23,6 @@ type ComposioConnector = (typeof COMPOSIO_CONNECTORS)[number];
type OtherConnector = (typeof OTHER_CONNECTORS)[number]; type OtherConnector = (typeof OTHER_CONNECTORS)[number];
type CrawlerConnector = (typeof CRAWLERS)[number]; type CrawlerConnector = (typeof CRAWLERS)[number];
const DOCUMENT_FILE_CONNECTOR_TYPES = new Set<string>([
EnumConnectorName.GOOGLE_DRIVE_CONNECTOR,
EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR,
EnumConnectorName.ONEDRIVE_CONNECTOR,
EnumConnectorName.DROPBOX_CONNECTOR,
]);
const OTHER_DOCUMENT_CONNECTOR_TYPES = new Set<string>([
EnumConnectorName.YOUTUBE_CONNECTOR,
EnumConnectorName.NOTION_CONNECTOR,
EnumConnectorName.AIRTABLE_CONNECTOR,
]);
/** /**
* Extract the display name from a full connector name. * Extract the display name from a full connector name.
* Full names are in format "Base Name - identifier" (e.g., "Gmail - john@example.com"). * Full names are in format "Base Name - identifier" (e.g., "Gmail - john@example.com").
@ -106,45 +96,23 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
c.description.toLowerCase().includes(searchQuery.toLowerCase()) c.description.toLowerCase().includes(searchQuery.toLowerCase())
); );
const nativeGoogleDriveConnectors = filteredOAuth.filter( const inCategory =
(c) => c.connectorType === EnumConnectorName.GOOGLE_DRIVE_CONNECTOR (category: ConnectorCategory) =>
); <T extends { connectorType?: string }>(connector: T): boolean =>
const composioGoogleDriveConnectors = filteredComposio.filter( !!connector.connectorType && getConnectorCategory(connector.connectorType) === category;
(c) => c.connectorType === EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR
);
const fileStorageConnectors = filteredOAuth.filter(
(c) =>
c.connectorType === EnumConnectorName.ONEDRIVE_CONNECTOR ||
c.connectorType === EnumConnectorName.DROPBOX_CONNECTOR
);
const otherDocumentYouTubeConnectors = filteredCrawlers.filter( const knowledgeBase = {
(c) => c.connectorType === EnumConnectorName.YOUTUBE_CONNECTOR oauth: filteredOAuth.filter(inCategory("knowledge_base")),
); composio: filteredComposio.filter(inCategory("knowledge_base")),
const otherDocumentNotionConnectors = filteredOAuth.filter( other: filteredOther.filter(inCategory("knowledge_base")),
(c) => c.connectorType === EnumConnectorName.NOTION_CONNECTOR crawlers: filteredCrawlers.filter(inCategory("knowledge_base")),
); };
const otherDocumentAirtableConnectors = filteredOAuth.filter( const toolsLive = {
(c) => c.connectorType === EnumConnectorName.AIRTABLE_CONNECTOR oauth: filteredOAuth.filter(inCategory("tools_live")),
); composio: filteredComposio.filter(inCategory("tools_live")),
other: filteredOther.filter(inCategory("tools_live")),
const moreIntegrationsComposio = filteredComposio.filter( crawlers: filteredCrawlers.filter(inCategory("tools_live")),
(c) => };
!DOCUMENT_FILE_CONNECTOR_TYPES.has(c.connectorType) &&
!OTHER_DOCUMENT_CONNECTOR_TYPES.has(c.connectorType)
);
const moreIntegrationsOAuth = filteredOAuth.filter(
(c) =>
!DOCUMENT_FILE_CONNECTOR_TYPES.has(c.connectorType) &&
!OTHER_DOCUMENT_CONNECTOR_TYPES.has(c.connectorType)
);
const moreIntegrationsOther = filteredOther;
const moreIntegrationsCrawlers = filteredCrawlers.filter(
(c) =>
!c.connectorType ||
(!DOCUMENT_FILE_CONNECTOR_TYPES.has(c.connectorType) &&
!OTHER_DOCUMENT_CONNECTOR_TYPES.has(c.connectorType))
);
const renderOAuthCard = (connector: OAuthConnector | ComposioConnector) => { const renderOAuthCard = (connector: OAuthConnector | ComposioConnector) => {
const isConnected = connectedTypes.has(connector.connectorType); const isConnected = connectedTypes.has(connector.connectorType);
@ -275,20 +243,18 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
); );
}; };
const hasDocumentFileConnectors = const hasKnowledgeBase =
nativeGoogleDriveConnectors.length > 0 || knowledgeBase.oauth.length > 0 ||
composioGoogleDriveConnectors.length > 0 || knowledgeBase.composio.length > 0 ||
fileStorageConnectors.length > 0; knowledgeBase.other.length > 0 ||
const hasMoreIntegrations = knowledgeBase.crawlers.length > 0;
otherDocumentYouTubeConnectors.length > 0 || const hasToolsLive =
otherDocumentNotionConnectors.length > 0 || toolsLive.oauth.length > 0 ||
otherDocumentAirtableConnectors.length > 0 || toolsLive.composio.length > 0 ||
moreIntegrationsComposio.length > 0 || toolsLive.other.length > 0 ||
moreIntegrationsOAuth.length > 0 || toolsLive.crawlers.length > 0;
moreIntegrationsOther.length > 0 ||
moreIntegrationsCrawlers.length > 0;
const hasAnyResults = hasDocumentFileConnectors || hasMoreIntegrations; const hasAnyResults = hasKnowledgeBase || hasToolsLive;
if (!hasAnyResults && searchQuery) { if (!hasAnyResults && searchQuery) {
return ( return (
@ -302,36 +268,34 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
return ( return (
<div className="space-y-8"> <div className="space-y-8">
{/* File Storage Integrations */} {hasKnowledgeBase && (
{hasDocumentFileConnectors && (
<section> <section>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<h3 className="text-sm font-semibold text-muted-foreground"> <h3 className="text-sm font-semibold text-muted-foreground">
File Storage Integrations {CONNECTOR_CATEGORY_LABELS.knowledge_base}
</h3> </h3>
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{nativeGoogleDriveConnectors.map(renderOAuthCard)} {knowledgeBase.oauth.map(renderOAuthCard)}
{composioGoogleDriveConnectors.map(renderOAuthCard)} {knowledgeBase.composio.map(renderOAuthCard)}
{fileStorageConnectors.map(renderOAuthCard)} {knowledgeBase.crawlers.map(renderCrawlerCard)}
{knowledgeBase.other.map(renderOtherCard)}
</div> </div>
</section> </section>
)} )}
{/* More Integrations */} {hasToolsLive && (
{hasMoreIntegrations && (
<section> <section>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<h3 className="text-sm font-semibold text-muted-foreground">More Integrations</h3> <h3 className="text-sm font-semibold text-muted-foreground">
{CONNECTOR_CATEGORY_LABELS.tools_live}
</h3>
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{otherDocumentYouTubeConnectors.map(renderCrawlerCard)} {toolsLive.oauth.map(renderOAuthCard)}
{otherDocumentNotionConnectors.map(renderOAuthCard)} {toolsLive.composio.map(renderOAuthCard)}
{otherDocumentAirtableConnectors.map(renderOAuthCard)} {toolsLive.crawlers.map(renderCrawlerCard)}
{moreIntegrationsComposio.map(renderOAuthCard)} {toolsLive.other.map(renderOtherCard)}
{moreIntegrationsOAuth.map(renderOAuthCard)}
{moreIntegrationsOther.map(renderOtherCard)}
{moreIntegrationsCrawlers.map(renderCrawlerCard)}
</div> </div>
</section> </section>
)} )}