mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-01 11:56:25 +02:00
refactor: streamline connector management UI and enhance document handling
- Updated the ConnectorIndicator component to accurately reflect active connectors and their document counts. - Improved the display of standalone document types in the ActiveConnectorsTab, allowing users to view all documents easily. - Enhanced the ConnectorCard to show last indexed dates and formatted document counts for better clarity. - Adjusted tooltip and aria-labels for accessibility and consistency across attachment upload components. - Preserved newlines in URL input for webcrawler configuration to ensure proper backend handling.
This commit is contained in:
parent
0e93d8420f
commit
543daa0434
12 changed files with 239 additions and 116 deletions
|
|
@ -337,12 +337,12 @@ export const ComposerAddAttachment: FC = () => {
|
|||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<TooltipIconButton
|
||||
tooltip="Upload documents or add attachment"
|
||||
tooltip="Upload"
|
||||
side="bottom"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="aui-composer-add-attachment size-[34px] rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
|
||||
aria-label="Upload documents or add attachment"
|
||||
aria-label="Upload"
|
||||
>
|
||||
<PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
|
||||
</TooltipIconButton>
|
||||
|
|
@ -350,7 +350,7 @@ export const ComposerAddAttachment: FC = () => {
|
|||
<DropdownMenuContent align="start" className="w-48 bg-background border-border">
|
||||
<DropdownMenuItem onSelect={handleChatAttachment} className="cursor-pointer">
|
||||
<Paperclip className="size-4" />
|
||||
<span>Add attachment(s)</span>
|
||||
<span>Add attachment</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleFileUpload} className="cursor-pointer">
|
||||
<Upload className="size-4" />
|
||||
|
|
|
|||
|
|
@ -38,11 +38,10 @@ const ConnectorIndicator: FC = () => {
|
|||
? Object.entries(documentTypeCounts).filter(([_, count]) => count > 0)
|
||||
: [];
|
||||
|
||||
const nonIndexableConnectors = connectors.filter((connector) => !connector.is_indexable);
|
||||
|
||||
const hasConnectors = nonIndexableConnectors.length > 0;
|
||||
// Count only active connectors (matching what's shown in the Active tab)
|
||||
const activeConnectorsCount = connectors.length;
|
||||
const hasConnectors = activeConnectorsCount > 0;
|
||||
const hasSources = hasConnectors || activeDocumentTypes.length > 0;
|
||||
const totalSourceCount = nonIndexableConnectors.length + activeDocumentTypes.length;
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
// Clear any pending close timeout
|
||||
|
|
@ -76,7 +75,7 @@ const ConnectorIndicator: FC = () => {
|
|||
"text-muted-foreground"
|
||||
)}
|
||||
aria-label={
|
||||
hasSources ? `View ${totalSourceCount} connected sources` : "Add your first connector"
|
||||
hasConnectors ? `View ${activeConnectorsCount} active connectors` : "Add your first connector"
|
||||
}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
|
|
@ -86,9 +85,9 @@ const ConnectorIndicator: FC = () => {
|
|||
) : (
|
||||
<>
|
||||
<Plug2 className="size-4" />
|
||||
{totalSourceCount > 0 && (
|
||||
{activeConnectorsCount > 0 && (
|
||||
<span className="absolute -top-0.5 -right-0.5 flex items-center justify-center min-w-[16px] h-4 px-1 text-[10px] font-medium rounded-full bg-primary text-primary-foreground shadow-sm">
|
||||
{totalSourceCount > 99 ? "99+" : totalSourceCount}
|
||||
{activeConnectorsCount > 99 ? "99+" : activeConnectorsCount}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -104,35 +103,50 @@ const ConnectorIndicator: FC = () => {
|
|||
>
|
||||
{hasSources ? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-xs font-medium text-muted-foreground">Connected Sources</p>
|
||||
<span className="text-xs font-medium bg-muted px-1.5 py-0.5 rounded">
|
||||
{totalSourceCount}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{activeDocumentTypes.map(([docType, count]) => (
|
||||
<div
|
||||
key={docType}
|
||||
className="flex items-center gap-1.5 rounded-md bg-muted/80 px-2.5 py-1.5 text-xs border border-border/50"
|
||||
>
|
||||
{getConnectorIcon(docType, "size-3.5")}
|
||||
<span className="truncate max-w-[100px]">{getDocumentTypeLabel(docType)}</span>
|
||||
<span className="flex items-center justify-center min-w-[18px] h-[18px] px-1 text-[10px] font-medium rounded-full bg-primary/10 text-primary">
|
||||
{count > 999 ? "999+" : count}
|
||||
</span>
|
||||
{activeConnectorsCount > 0 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-xs font-medium text-muted-foreground">Active Connectors</p>
|
||||
<span className="text-xs font-medium bg-muted px-1.5 py-0.5 rounded">
|
||||
{activeConnectorsCount}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{activeConnectorsCount > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{connectors.map((connector) => (
|
||||
<div
|
||||
key={`connector-${connector.id}`}
|
||||
className="flex items-center gap-1.5 rounded-md bg-muted/80 px-2.5 py-1.5 text-xs border border-border/50"
|
||||
>
|
||||
{getConnectorIcon(connector.connector_type, "size-3.5")}
|
||||
<span className="truncate max-w-[100px]">{connector.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{activeDocumentTypes.length > 0 && (
|
||||
<>
|
||||
{activeConnectorsCount > 0 && (
|
||||
<div className="pt-2 border-t border-border/50">
|
||||
<p className="text-xs font-medium text-muted-foreground mb-2">Documents</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{activeDocumentTypes.map(([docType, count]) => (
|
||||
<div
|
||||
key={docType}
|
||||
className="flex items-center gap-1.5 rounded-md bg-muted/80 px-2.5 py-1.5 text-xs border border-border/50"
|
||||
>
|
||||
{getConnectorIcon(docType, "size-3.5")}
|
||||
<span className="truncate max-w-[100px]">{getDocumentTypeLabel(docType)}</span>
|
||||
<span className="flex items-center justify-center min-w-[18px] h-[18px] px-1 text-[10px] font-medium rounded-full bg-primary/10 text-primary">
|
||||
{count > 999 ? "999+" : count}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
{nonIndexableConnectors.map((connector) => (
|
||||
<div
|
||||
key={`connector-${connector.id}`}
|
||||
className="flex items-center gap-1.5 rounded-md bg-muted/80 px-2.5 py-1.5 text-xs border border-border/50"
|
||||
>
|
||||
{getConnectorIcon(connector.connector_type, "size-3.5")}
|
||||
<span className="truncate max-w-[100px]">{connector.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="pt-1 border-t border-border/50">
|
||||
<Link
|
||||
href={`/dashboard/${searchSpaceId}/connectors/add`}
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ export const ConnectorIndicator: FC = () => {
|
|||
const hasConnectors = connectors.length > 0;
|
||||
const hasSources = hasConnectors || activeDocumentTypes.length > 0;
|
||||
const totalSourceCount = connectors.length + activeDocumentTypes.length;
|
||||
const activeConnectorsCount = connectors.length; // Only actual connectors, not document types
|
||||
|
||||
// Check which connectors are already connected
|
||||
const connectedTypes = new Set(
|
||||
|
|
@ -170,7 +171,7 @@ export const ConnectorIndicator: FC = () => {
|
|||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<TooltipIconButton
|
||||
tooltip={hasSources ? `Manage ${totalSourceCount} sources` : "Connect your data"}
|
||||
tooltip={hasConnectors ? `Manage ${activeConnectorsCount} connectors` : "Connect your data"}
|
||||
side="bottom"
|
||||
className={cn(
|
||||
"size-[34px] rounded-full p-1 flex items-center justify-center transition-colors relative",
|
||||
|
|
@ -179,7 +180,7 @@ export const ConnectorIndicator: FC = () => {
|
|||
"border-0 ring-0 focus:ring-0 shadow-none focus:shadow-none"
|
||||
)}
|
||||
aria-label={
|
||||
hasSources ? `View ${totalSourceCount} connected sources` : "Add your first connector"
|
||||
hasConnectors ? `View ${activeConnectorsCount} connectors` : "Add your first connector"
|
||||
}
|
||||
onClick={() => handleOpenChange(true)}
|
||||
>
|
||||
|
|
@ -188,9 +189,9 @@ export const ConnectorIndicator: FC = () => {
|
|||
) : (
|
||||
<>
|
||||
<Cable className="size-4 stroke-[1.5px]" />
|
||||
{totalSourceCount > 0 && (
|
||||
{activeConnectorsCount > 0 && (
|
||||
<span className="absolute -top-0.5 right-0 flex items-center justify-center min-w-[16px] h-4 px-1 text-[10px] font-medium rounded-full bg-primary text-primary-foreground shadow-sm">
|
||||
{totalSourceCount > 99 ? "99+" : totalSourceCount}
|
||||
{activeConnectorsCount > 99 ? "99+" : activeConnectorsCount}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -259,7 +260,7 @@ export const ConnectorIndicator: FC = () => {
|
|||
{/* Header */}
|
||||
<ConnectorDialogHeader
|
||||
activeTab={activeTab}
|
||||
totalSourceCount={totalSourceCount}
|
||||
totalSourceCount={activeConnectorsCount}
|
||||
searchQuery={searchQuery}
|
||||
onTabChange={handleTabChange}
|
||||
onSearchChange={setSearchQuery}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { IconBrandYoutube } from "@tabler/icons-react";
|
||||
import { FileText, Loader2 } from "lucide-react";
|
||||
import { type FC } from "react";
|
||||
import { format } from "date-fns";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||
import type { LogActiveTask } from "@/contracts/types/log.types";
|
||||
|
|
@ -16,6 +17,7 @@ interface ConnectorCardProps {
|
|||
isConnected?: boolean;
|
||||
isConnecting?: boolean;
|
||||
documentCount?: number;
|
||||
lastIndexedAt?: string | null;
|
||||
isIndexing?: boolean;
|
||||
activeTask?: LogActiveTask;
|
||||
onConnect?: () => void;
|
||||
|
|
@ -33,6 +35,20 @@ function extractIndexedCount(message: string | undefined): number | null {
|
|||
return match ? parseInt(match[1], 10) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format document count (e.g., "1.2k docs", "500 docs", "1.5M docs")
|
||||
*/
|
||||
function formatDocumentCount(count: number | undefined): string {
|
||||
if (count === undefined || count === 0) return "0 docs";
|
||||
if (count < 1000) return `${count} docs`;
|
||||
if (count < 1000000) {
|
||||
const k = (count / 1000).toFixed(1);
|
||||
return `${k.replace(/\.0$/, "")}k docs`;
|
||||
}
|
||||
const m = (count / 1000000).toFixed(1);
|
||||
return `${m.replace(/\.0$/, "")}M docs`;
|
||||
}
|
||||
|
||||
export const ConnectorCard: FC<ConnectorCardProps> = ({
|
||||
id,
|
||||
title,
|
||||
|
|
@ -41,6 +57,7 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
|
|||
isConnected = false,
|
||||
isConnecting = false,
|
||||
documentCount,
|
||||
lastIndexedAt,
|
||||
isIndexing = false,
|
||||
activeTask,
|
||||
onConnect,
|
||||
|
|
@ -70,18 +87,16 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
|
|||
}
|
||||
|
||||
if (isConnected) {
|
||||
if (documentCount !== undefined && documentCount > 0) {
|
||||
// Show last indexed date for connected connectors
|
||||
if (lastIndexedAt) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<FileText className="size-3 flex-shrink-0" />
|
||||
<span className="whitespace-nowrap">
|
||||
{documentCount.toLocaleString()} document{documentCount !== 1 ? "s" : ""}
|
||||
</span>
|
||||
<span className="whitespace-nowrap">
|
||||
Last indexed: {format(new Date(lastIndexedAt), "MMM d, yyyy")}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
// Fallback for connected but no documents yet
|
||||
return <span className="whitespace-nowrap">No documents indexed</span>;
|
||||
// Fallback for connected but never indexed
|
||||
return <span className="whitespace-nowrap">Never indexed</span>;
|
||||
}
|
||||
|
||||
return description;
|
||||
|
|
@ -105,6 +120,11 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
|
|||
<div className="text-[11px] text-muted-foreground mt-1">
|
||||
{getStatusContent()}
|
||||
</div>
|
||||
{isConnected && documentCount !== undefined && (
|
||||
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||
{formatDocumentCount(documentCount)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
|
|
@ -123,6 +143,8 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
|
|||
"Syncing..."
|
||||
) : isConnected ? (
|
||||
"Manage"
|
||||
) : id === "youtube-crawler" ? (
|
||||
"Add"
|
||||
) : connectorType ? (
|
||||
"Connect"
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -43,9 +43,11 @@ export const WebcrawlerConfig: FC<ConnectorConfigProps> = ({
|
|||
const handleUrlsChange = (value: string) => {
|
||||
setInitialUrls(value);
|
||||
if (onConfigChange) {
|
||||
// Preserve newlines for multi-line URL input
|
||||
// Backend will handle trimming individual URLs when splitting by newline
|
||||
onConfigChange({
|
||||
...connector.config,
|
||||
INITIAL_URLS: value.trim() || undefined,
|
||||
INITIAL_URLS: value || undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ interface ConnectorEditViewProps {
|
|||
onSave: () => void;
|
||||
onDisconnect: () => void;
|
||||
onBack: () => void;
|
||||
onConfigChange?: (config: Record<string, any>) => void;
|
||||
onConfigChange?: (config: Record<string, unknown>) => void;
|
||||
onNameChange?: (name: string) => void;
|
||||
}
|
||||
|
||||
|
|
@ -201,35 +201,37 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
</div>
|
||||
|
||||
{/* Fixed Footer - Action buttons */}
|
||||
<div className="flex-shrink-0 flex items-center justify-between px-6 sm:px-12 py-6 bg-muted border-t border-border">
|
||||
<div className="flex-shrink-0 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3 sm:gap-0 px-6 sm:px-12 py-4 sm:py-6 bg-muted border-t border-border">
|
||||
{showDisconnectConfirm ? (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs sm:text-sm text-muted-foreground">Are you sure?</span>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={handleDisconnectConfirm}
|
||||
disabled={isDisconnecting}
|
||||
className="text-xs sm:text-sm"
|
||||
>
|
||||
{isDisconnecting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Disconnecting...
|
||||
</>
|
||||
) : (
|
||||
"Confirm Disconnect"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleDisconnectCancel}
|
||||
disabled={isDisconnecting}
|
||||
className="text-xs sm:text-sm"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3 flex-1 sm:flex-initial">
|
||||
<span className="text-xs sm:text-sm text-muted-foreground sm:whitespace-nowrap">Are you sure?</span>
|
||||
<div className="flex items-center gap-2 sm:gap-3">
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={handleDisconnectConfirm}
|
||||
disabled={isDisconnecting}
|
||||
className="text-xs sm:text-sm flex-1 sm:flex-initial"
|
||||
>
|
||||
{isDisconnecting ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Disconnecting...
|
||||
</>
|
||||
) : (
|
||||
"Confirm Disconnect"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleDisconnectCancel}
|
||||
disabled={isDisconnecting}
|
||||
className="text-xs sm:text-sm flex-1 sm:flex-initial"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
|
|
@ -237,13 +239,17 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
size="sm"
|
||||
onClick={handleDisconnectClick}
|
||||
disabled={isSaving || isDisconnecting}
|
||||
className="text-xs sm:text-sm"
|
||||
className="text-xs sm:text-sm flex-1 sm:flex-initial"
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Disconnect
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onSave} disabled={isSaving || isDisconnecting} className="text-xs sm:text-sm">
|
||||
<Button
|
||||
onClick={onSave}
|
||||
disabled={isSaving || isDisconnecting}
|
||||
className="text-xs sm:text-sm flex-1 sm:flex-initial"
|
||||
>
|
||||
{isSaving ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
"use client";
|
||||
|
||||
import { format } from "date-fns";
|
||||
import { Cable, FileText, Loader2 } from "lucide-react";
|
||||
import { ArrowRight, Cable, Loader2 } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { getDocumentTypeLabel } from "@/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentTypeIcon";
|
||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
import type { LogSummary, LogActiveTask } from "@/contracts/types/log.types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getDocumentCountForConnector } from "../utils/connector-document-mapping";
|
||||
import {
|
||||
TabsContent,
|
||||
} from "@/components/ui/tabs";
|
||||
|
|
@ -31,46 +33,75 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
|||
connectors,
|
||||
indexingConnectorIds,
|
||||
logsSummary,
|
||||
searchSpaceId,
|
||||
onTabChange,
|
||||
onManage,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
const handleViewAllDocuments = () => {
|
||||
router.push(`/dashboard/${searchSpaceId}/documents`);
|
||||
};
|
||||
|
||||
// Convert activeDocumentTypes array to Record for utility function
|
||||
const documentTypeCounts = activeDocumentTypes.reduce(
|
||||
(acc, [docType, count]) => {
|
||||
acc[docType] = count;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
);
|
||||
|
||||
// Format document count (e.g., "1.2k docs", "500 docs", "1.5M docs")
|
||||
const formatDocumentCount = (count: number | undefined): string => {
|
||||
if (count === undefined || count === 0) return "0 docs";
|
||||
if (count < 1000) return `${count} docs`;
|
||||
if (count < 1000000) {
|
||||
const k = (count / 1000).toFixed(1);
|
||||
return `${k.replace(/\.0$/, "")}k docs`;
|
||||
}
|
||||
const m = (count / 1000000).toFixed(1);
|
||||
return `${m.replace(/\.0$/, "")}M docs`;
|
||||
};
|
||||
|
||||
// Document types that should be shown as cards (not from connectors)
|
||||
// These are: EXTENSION (browser extension), FILE (uploaded files), NOTE (editor notes),
|
||||
// YOUTUBE_VIDEO (YouTube videos), and CRAWLED_URL (web pages - shown separately even though it can come from WEBCRAWLER_CONNECTOR)
|
||||
const standaloneDocumentTypes = ["EXTENSION", "FILE", "NOTE", "YOUTUBE_VIDEO", "CRAWLED_URL"];
|
||||
|
||||
// Filter to only show standalone document types that have documents (count > 0)
|
||||
const standaloneDocuments = activeDocumentTypes
|
||||
.filter(([docType, count]) =>
|
||||
standaloneDocumentTypes.includes(docType) && count > 0
|
||||
)
|
||||
.map(([docType, count]) => ({
|
||||
type: docType,
|
||||
count,
|
||||
label: getDocumentTypeLabel(docType),
|
||||
}));
|
||||
|
||||
return (
|
||||
<TabsContent value="active" className="m-0">
|
||||
{hasSources ? (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground">
|
||||
Currently Active
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{activeDocumentTypes.map(([docType, count]) => (
|
||||
<div
|
||||
key={docType}
|
||||
className="flex items-center gap-4 p-4 rounded-xl bg-slate-400/5 dark:bg-white/5 hover:bg-slate-400/10 dark:hover:bg-white/10 border border-border transition-all"
|
||||
>
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-slate-400/5 dark:bg-white/5 border border-slate-400/5 dark:border-white/5">
|
||||
{getConnectorIcon(docType, "size-6")}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[14px] font-semibold leading-tight">
|
||||
{getDocumentTypeLabel(docType)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted-foreground mt-1 inline-flex items-center gap-1.5">
|
||||
<FileText className="size-3 flex-shrink-0" />
|
||||
<span className="whitespace-nowrap">
|
||||
{(count as number).toLocaleString()} document{count !== 1 ? "s" : ""}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
{/* Active Connectors Section */}
|
||||
{connectors.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground">
|
||||
Active Connectors
|
||||
</h3>
|
||||
</div>
|
||||
))}
|
||||
{connectors.map((connector) => {
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{connectors.map((connector) => {
|
||||
const isIndexing = indexingConnectorIds.has(connector.id);
|
||||
const activeTask = logsSummary?.active_tasks?.find(
|
||||
(task: LogActiveTask) => task.connector_id === connector.id
|
||||
);
|
||||
const documentCount = getDocumentCountForConnector(
|
||||
connector.connector_type,
|
||||
documentTypeCounts
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -113,6 +144,9 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
|||
: "Never indexed"}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||
{formatDocumentCount(documentCount)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
|
@ -126,7 +160,47 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Standalone Documents Section */}
|
||||
{standaloneDocuments.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground">
|
||||
Documents
|
||||
</h3>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleViewAllDocuments}
|
||||
className="h-7 text-xs text-muted-foreground hover:text-foreground gap-1.5"
|
||||
>
|
||||
View all documents
|
||||
<ArrowRight className="size-3" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{standaloneDocuments.map((doc) => (
|
||||
<div
|
||||
key={doc.type}
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full border border-border bg-slate-400/5 dark:bg-white/5 hover:bg-slate-400/10 dark:hover:bg-white/10 transition-all"
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
{getConnectorIcon(doc.type, "size-3.5")}
|
||||
</div>
|
||||
<span className="text-[12px] font-medium">
|
||||
{doc.label}
|
||||
</span>
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{formatDocumentCount(doc.count)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-center">
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
isConnected={isConnected}
|
||||
isConnecting={isConnecting}
|
||||
documentCount={documentCount}
|
||||
lastIndexedAt={actualConnector?.last_indexed_at}
|
||||
isIndexing={isIndexing}
|
||||
activeTask={activeTask}
|
||||
onConnect={() => onConnectOAuth(connector)}
|
||||
|
|
@ -162,6 +163,7 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
isConnected={isConnected}
|
||||
isConnecting={isConnecting}
|
||||
documentCount={documentCount}
|
||||
lastIndexedAt={actualConnector?.last_indexed_at}
|
||||
isIndexing={isIndexing}
|
||||
activeTask={activeTask}
|
||||
onConnect={handleConnect}
|
||||
|
|
@ -230,6 +232,7 @@ export const AllConnectorsTab: FC<AllConnectorsTabProps> = ({
|
|||
isConnected={isConnected}
|
||||
isConnecting={isConnecting}
|
||||
documentCount={documentCount}
|
||||
lastIndexedAt={actualConnector?.last_indexed_at}
|
||||
isIndexing={isIndexing}
|
||||
activeTask={activeTask}
|
||||
onConnect={handleConnect}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue