mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-01 20:03:30 +02:00
feat: streamline Composio connector configurations and enhance UI interactions
- Refactored Composio connector configuration components to improve modularity and maintainability. - Simplified the ComposioCalendarConfig, ComposioGmailConfig, and ComposioDriveConfig components by removing unnecessary state management and UI elements. - Added functionality to remove selected folders and files in the Google Drive and Composio Drive configurations, enhancing user experience. - Updated connector display names for better clarity in the UI. - Improved the overall structure of the connector edit view for better readability and usability.
This commit is contained in:
parent
1343fabeee
commit
12f45e1bd3
8 changed files with 88 additions and 390 deletions
|
|
@ -7,7 +7,7 @@ import type { FC } from "react";
|
|||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
||||
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Tabs, TabsContent } from "@/components/ui/tabs";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
import { useConnectorsElectric } from "@/hooks/use-connectors-electric";
|
||||
|
|
@ -185,6 +185,7 @@ export const ConnectorIndicator: FC = () => {
|
|||
</TooltipIconButton>
|
||||
|
||||
<DialogContent className="max-w-3xl w-[95vw] sm:w-full h-[75vh] sm:h-[85vh] flex flex-col p-0 gap-0 overflow-hidden border border-border bg-muted text-foreground [&>button]:right-4 sm:[&>button]:right-12 [&>button]:top-6 sm:[&>button]:top-10 [&>button]:opacity-80 hover:[&>button]:opacity-100 [&>button_svg]:size-5">
|
||||
<DialogTitle className="sr-only">Manage Connectors</DialogTitle>
|
||||
{/* YouTube Crawler View - shown when adding YouTube videos */}
|
||||
{isYouTubeView && searchSpaceId ? (
|
||||
<YouTubeCrawlerView searchSpaceId={searchSpaceId} onBack={handleBackFromYouTube} />
|
||||
|
|
|
|||
|
|
@ -1,17 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { Calendar, Clock } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
|
||||
interface ComposioCalendarConfigProps {
|
||||
|
|
@ -20,201 +9,7 @@ interface ComposioCalendarConfigProps {
|
|||
onNameChange?: (name: string) => void;
|
||||
}
|
||||
|
||||
interface CalendarIndexingOptions {
|
||||
max_events: number;
|
||||
include_recurring: boolean;
|
||||
include_past_events: boolean;
|
||||
days_ahead: number;
|
||||
}
|
||||
|
||||
const DEFAULT_CALENDAR_OPTIONS: CalendarIndexingOptions = {
|
||||
max_events: 500,
|
||||
include_recurring: true,
|
||||
include_past_events: true,
|
||||
days_ahead: 365,
|
||||
};
|
||||
|
||||
export const ComposioCalendarConfig: FC<ComposioCalendarConfigProps> = ({ connector, onConfigChange }) => {
|
||||
const isIndexable = connector.config?.is_indexable as boolean;
|
||||
|
||||
// Initialize with existing options from connector config
|
||||
const existingOptions =
|
||||
(connector.config?.calendar_options as CalendarIndexingOptions | undefined) || DEFAULT_CALENDAR_OPTIONS;
|
||||
|
||||
const [calendarOptions, setCalendarOptions] = useState<CalendarIndexingOptions>(existingOptions);
|
||||
|
||||
// Update options when connector config changes
|
||||
useEffect(() => {
|
||||
const options =
|
||||
(connector.config?.calendar_options as CalendarIndexingOptions | undefined) ||
|
||||
DEFAULT_CALENDAR_OPTIONS;
|
||||
setCalendarOptions(options);
|
||||
}, [connector.config]);
|
||||
|
||||
const updateConfig = (options: CalendarIndexingOptions) => {
|
||||
if (onConfigChange) {
|
||||
onConfigChange({
|
||||
...connector.config,
|
||||
calendar_options: options,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleOptionChange = (key: keyof CalendarIndexingOptions, value: number | boolean) => {
|
||||
const newOptions = { ...calendarOptions, [key]: value };
|
||||
setCalendarOptions(newOptions);
|
||||
updateConfig(newOptions);
|
||||
};
|
||||
|
||||
// Only show configuration if the connector is indexable
|
||||
if (!isIndexable) {
|
||||
return <div className="space-y-6" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Calendar Indexing Options */}
|
||||
<div className="rounded-xl border border-border bg-slate-400/5 dark:bg-white/5 p-3 sm:p-6 space-y-4">
|
||||
<div className="space-y-1 sm:space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="size-4 text-blue-500" />
|
||||
<h3 className="font-medium text-sm sm:text-base">Calendar Indexing Options</h3>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||
Configure how events are indexed from your Google Calendar.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Max events to index */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="max-events" className="text-sm font-medium">
|
||||
Max events to index
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Maximum number of events to index per sync
|
||||
</p>
|
||||
</div>
|
||||
<Select
|
||||
value={calendarOptions.max_events.toString()}
|
||||
onValueChange={(value) =>
|
||||
handleOptionChange("max_events", parseInt(value, 10))
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="max-events"
|
||||
className="w-[140px] bg-slate-400/5 dark:bg-slate-400/5 border-slate-400/20 text-xs sm:text-sm"
|
||||
>
|
||||
<SelectValue placeholder="Select limit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="z-[100]">
|
||||
<SelectItem value="100" className="text-xs sm:text-sm">
|
||||
100 events
|
||||
</SelectItem>
|
||||
<SelectItem value="250" className="text-xs sm:text-sm">
|
||||
250 events
|
||||
</SelectItem>
|
||||
<SelectItem value="500" className="text-xs sm:text-sm">
|
||||
500 events
|
||||
</SelectItem>
|
||||
<SelectItem value="1000" className="text-xs sm:text-sm">
|
||||
1000 events
|
||||
</SelectItem>
|
||||
<SelectItem value="2500" className="text-xs sm:text-sm">
|
||||
2500 events
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Days ahead */}
|
||||
<div className="space-y-2 pt-2 border-t border-slate-400/20">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Clock className="size-3.5 text-muted-foreground" />
|
||||
<Label htmlFor="days-ahead" className="text-sm font-medium">
|
||||
Future events range
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
How far ahead to index future events
|
||||
</p>
|
||||
</div>
|
||||
<Select
|
||||
value={calendarOptions.days_ahead.toString()}
|
||||
onValueChange={(value) =>
|
||||
handleOptionChange("days_ahead", parseInt(value, 10))
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="days-ahead"
|
||||
className="w-[140px] bg-slate-400/5 dark:bg-slate-400/5 border-slate-400/20 text-xs sm:text-sm"
|
||||
>
|
||||
<SelectValue placeholder="Select range" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="z-[100]">
|
||||
<SelectItem value="30" className="text-xs sm:text-sm">
|
||||
30 days
|
||||
</SelectItem>
|
||||
<SelectItem value="90" className="text-xs sm:text-sm">
|
||||
90 days
|
||||
</SelectItem>
|
||||
<SelectItem value="180" className="text-xs sm:text-sm">
|
||||
180 days
|
||||
</SelectItem>
|
||||
<SelectItem value="365" className="text-xs sm:text-sm">
|
||||
1 year
|
||||
</SelectItem>
|
||||
<SelectItem value="730" className="text-xs sm:text-sm">
|
||||
2 years
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Include recurring events toggle */}
|
||||
<div className="flex items-center justify-between pt-2 border-t border-slate-400/20">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="include-recurring" className="text-sm font-medium">
|
||||
Include recurring events
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Index individual instances of recurring events
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="include-recurring"
|
||||
checked={calendarOptions.include_recurring}
|
||||
onCheckedChange={(checked) =>
|
||||
handleOptionChange("include_recurring", checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Include past events toggle */}
|
||||
<div className="flex items-center justify-between pt-2 border-t border-slate-400/20">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="include-past" className="text-sm font-medium">
|
||||
Include past events
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Index events from before the selected date range
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="include-past"
|
||||
checked={calendarOptions.include_past_events}
|
||||
onCheckedChange={(checked) =>
|
||||
handleOptionChange("include_past_events", checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export const ComposioCalendarConfig: FC<ComposioCalendarConfigProps> = () => {
|
||||
return <div className="space-y-6" />;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { File, FileSpreadsheet, FileText, FolderClosed, Image, Presentation } from "lucide-react";
|
||||
import { File, FileSpreadsheet, FileText, FolderClosed, Image, Presentation, X } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ComposioDriveFolderTree } from "@/components/connectors/composio-drive-folder-tree";
|
||||
|
|
@ -143,6 +143,18 @@ export const ComposioDriveConfig: FC<ComposioDriveConfigProps> = ({ connector, o
|
|||
updateConfig(selectedFolders, selectedFiles, newOptions);
|
||||
};
|
||||
|
||||
const handleRemoveFolder = (folderId: string) => {
|
||||
const newFolders = selectedFolders.filter((folder) => folder.id !== folderId);
|
||||
setSelectedFolders(newFolders);
|
||||
updateConfig(newFolders, selectedFiles, indexingOptions);
|
||||
};
|
||||
|
||||
const handleRemoveFile = (fileId: string) => {
|
||||
const newFiles = selectedFiles.filter((file) => file.id !== fileId);
|
||||
setSelectedFiles(newFiles);
|
||||
updateConfig(selectedFolders, newFiles, indexingOptions);
|
||||
};
|
||||
|
||||
const totalSelected = selectedFolders.length + selectedFiles.length;
|
||||
|
||||
// Only show configuration if the connector is indexable
|
||||
|
|
@ -176,29 +188,45 @@ export const ComposioDriveConfig: FC<ComposioDriveConfigProps> = ({ connector, o
|
|||
`${selectedFiles.length} file${selectedFiles.length > 1 ? "s" : ""}`
|
||||
);
|
||||
}
|
||||
return parts.length > 0 ? `(${parts.join(" ")})` : "";
|
||||
return parts.length > 0 ? `(${parts.join(", ")})` : "";
|
||||
})()}
|
||||
</p>
|
||||
<div className="max-h-20 sm:max-h-24 overflow-y-auto space-y-1">
|
||||
{selectedFolders.map((folder) => (
|
||||
<p
|
||||
<div
|
||||
key={folder.id}
|
||||
className="text-xs sm:text-sm text-muted-foreground truncate flex items-center gap-1.5"
|
||||
title={folder.name}
|
||||
>
|
||||
<FolderClosed className="size-3.5 shrink-0 text-gray-500" />
|
||||
{folder.name}
|
||||
</p>
|
||||
<span className="flex-1 truncate">{folder.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveFolder(folder.id)}
|
||||
className="shrink-0 p-0.5 hover:bg-muted-foreground/20 rounded transition-colors"
|
||||
aria-label={`Remove ${folder.name}`}
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{selectedFiles.map((file) => (
|
||||
<p
|
||||
<div
|
||||
key={file.id}
|
||||
className="text-xs sm:text-sm text-muted-foreground truncate flex items-center gap-1.5"
|
||||
title={file.name}
|
||||
>
|
||||
{getFileIconFromName(file.name)}
|
||||
{file.name}
|
||||
</p>
|
||||
<span className="flex-1 truncate">{file.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveFile(file.id)}
|
||||
className="shrink-0 p-0.5 hover:bg-muted-foreground/20 rounded transition-colors"
|
||||
aria-label={`Remove ${file.name}`}
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { Mail, Tag } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
|
||||
interface ComposioGmailConfigProps {
|
||||
|
|
@ -20,155 +9,7 @@ interface ComposioGmailConfigProps {
|
|||
onNameChange?: (name: string) => void;
|
||||
}
|
||||
|
||||
interface GmailIndexingOptions {
|
||||
max_emails: number;
|
||||
label_filter: string;
|
||||
search_query: string;
|
||||
}
|
||||
|
||||
const DEFAULT_GMAIL_OPTIONS: GmailIndexingOptions = {
|
||||
max_emails: 500,
|
||||
label_filter: "",
|
||||
search_query: "",
|
||||
};
|
||||
|
||||
export const ComposioGmailConfig: FC<ComposioGmailConfigProps> = ({ connector, onConfigChange }) => {
|
||||
const isIndexable = connector.config?.is_indexable as boolean;
|
||||
|
||||
// Initialize with existing options from connector config
|
||||
const existingOptions =
|
||||
(connector.config?.gmail_options as GmailIndexingOptions | undefined) || DEFAULT_GMAIL_OPTIONS;
|
||||
|
||||
const [gmailOptions, setGmailOptions] = useState<GmailIndexingOptions>(existingOptions);
|
||||
|
||||
// Update options when connector config changes
|
||||
useEffect(() => {
|
||||
const options =
|
||||
(connector.config?.gmail_options as GmailIndexingOptions | undefined) ||
|
||||
DEFAULT_GMAIL_OPTIONS;
|
||||
setGmailOptions(options);
|
||||
}, [connector.config]);
|
||||
|
||||
const updateConfig = (options: GmailIndexingOptions) => {
|
||||
if (onConfigChange) {
|
||||
onConfigChange({
|
||||
...connector.config,
|
||||
gmail_options: options,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleOptionChange = (key: keyof GmailIndexingOptions, value: number | string) => {
|
||||
const newOptions = { ...gmailOptions, [key]: value };
|
||||
setGmailOptions(newOptions);
|
||||
updateConfig(newOptions);
|
||||
};
|
||||
|
||||
// Only show configuration if the connector is indexable
|
||||
if (!isIndexable) {
|
||||
return <div className="space-y-6" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Gmail Indexing Options */}
|
||||
<div className="rounded-xl border border-border bg-slate-400/5 dark:bg-white/5 p-3 sm:p-6 space-y-4">
|
||||
<div className="space-y-1 sm:space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Mail className="size-4 text-red-500" />
|
||||
<h3 className="font-medium text-sm sm:text-base">Gmail Indexing Options</h3>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||
Configure how emails are indexed from your Gmail account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Max emails to index */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="max-emails" className="text-sm font-medium">
|
||||
Max emails to index
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Maximum number of emails to index per sync
|
||||
</p>
|
||||
</div>
|
||||
<Select
|
||||
value={gmailOptions.max_emails.toString()}
|
||||
onValueChange={(value) =>
|
||||
handleOptionChange("max_emails", parseInt(value, 10))
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="max-emails"
|
||||
className="w-[140px] bg-slate-400/5 dark:bg-slate-400/5 border-slate-400/20 text-xs sm:text-sm"
|
||||
>
|
||||
<SelectValue placeholder="Select limit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="z-[100]">
|
||||
<SelectItem value="100" className="text-xs sm:text-sm">
|
||||
100 emails
|
||||
</SelectItem>
|
||||
<SelectItem value="250" className="text-xs sm:text-sm">
|
||||
250 emails
|
||||
</SelectItem>
|
||||
<SelectItem value="500" className="text-xs sm:text-sm">
|
||||
500 emails
|
||||
</SelectItem>
|
||||
<SelectItem value="1000" className="text-xs sm:text-sm">
|
||||
1000 emails
|
||||
</SelectItem>
|
||||
<SelectItem value="2500" className="text-xs sm:text-sm">
|
||||
2500 emails
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Label filter */}
|
||||
<div className="space-y-2 pt-2 border-t border-slate-400/20">
|
||||
<div className="space-y-0.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Tag className="size-3.5 text-muted-foreground" />
|
||||
<Label htmlFor="label-filter" className="text-sm font-medium">
|
||||
Label filter (optional)
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Only index emails with this label (e.g., "INBOX", "IMPORTANT", "work")
|
||||
</p>
|
||||
</div>
|
||||
<Input
|
||||
id="label-filter"
|
||||
value={gmailOptions.label_filter}
|
||||
onChange={(e) => handleOptionChange("label_filter", e.target.value)}
|
||||
placeholder="Enter label name..."
|
||||
className="bg-slate-400/5 dark:bg-slate-400/5 border-slate-400/20 text-xs sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Search query */}
|
||||
<div className="space-y-2 pt-2 border-t border-slate-400/20">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="search-query" className="text-sm font-medium">
|
||||
Search query (optional)
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Gmail search query to filter emails (e.g., "from:boss@company.com", "has:attachment")
|
||||
</p>
|
||||
</div>
|
||||
<Input
|
||||
id="search-query"
|
||||
value={gmailOptions.search_query}
|
||||
onChange={(e) => handleOptionChange("search_query", e.target.value)}
|
||||
placeholder="Enter Gmail search query..."
|
||||
className="bg-slate-400/5 dark:bg-slate-400/5 border-slate-400/20 text-xs sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export const ComposioGmailConfig: FC<ComposioGmailConfigProps> = () => {
|
||||
return <div className="space-y-6" />;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { File, FileSpreadsheet, FileText, FolderClosed, Image, Presentation } from "lucide-react";
|
||||
import { File, FileSpreadsheet, FileText, FolderClosed, Image, Presentation, X } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { GoogleDriveFolderTree } from "@/components/connectors/google-drive-folder-tree";
|
||||
|
|
@ -135,6 +135,18 @@ export const GoogleDriveConfig: FC<ConnectorConfigProps> = ({ connector, onConfi
|
|||
updateConfig(selectedFolders, selectedFiles, newOptions);
|
||||
};
|
||||
|
||||
const handleRemoveFolder = (folderId: string) => {
|
||||
const newFolders = selectedFolders.filter((folder) => folder.id !== folderId);
|
||||
setSelectedFolders(newFolders);
|
||||
updateConfig(newFolders, selectedFiles, indexingOptions);
|
||||
};
|
||||
|
||||
const handleRemoveFile = (fileId: string) => {
|
||||
const newFiles = selectedFiles.filter((file) => file.id !== fileId);
|
||||
setSelectedFiles(newFiles);
|
||||
updateConfig(selectedFolders, newFiles, indexingOptions);
|
||||
};
|
||||
|
||||
const totalSelected = selectedFolders.length + selectedFiles.length;
|
||||
|
||||
return (
|
||||
|
|
@ -161,29 +173,45 @@ export const GoogleDriveConfig: FC<ConnectorConfigProps> = ({ connector, onConfi
|
|||
if (selectedFiles.length > 0) {
|
||||
parts.push(`${selectedFiles.length} file${selectedFiles.length > 1 ? "s" : ""}`);
|
||||
}
|
||||
return parts.length > 0 ? `(${parts.join(" ")})` : "";
|
||||
return parts.length > 0 ? `(${parts.join(", ")})` : "";
|
||||
})()}
|
||||
</p>
|
||||
<div className="max-h-20 sm:max-h-24 overflow-y-auto space-y-1">
|
||||
{selectedFolders.map((folder) => (
|
||||
<p
|
||||
<div
|
||||
key={folder.id}
|
||||
className="text-xs sm:text-sm text-muted-foreground truncate flex items-center gap-1.5"
|
||||
title={folder.name}
|
||||
>
|
||||
<FolderClosed className="size-3.5 shrink-0 text-gray-500" />
|
||||
{folder.name}
|
||||
</p>
|
||||
<span className="flex-1 truncate">{folder.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveFolder(folder.id)}
|
||||
className="shrink-0 p-0.5 hover:bg-muted-foreground/20 rounded transition-colors"
|
||||
aria-label={`Remove ${folder.name}`}
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{selectedFiles.map((file) => (
|
||||
<p
|
||||
<div
|
||||
key={file.id}
|
||||
className="text-xs sm:text-sm text-muted-foreground truncate flex items-center gap-1.5"
|
||||
title={file.name}
|
||||
>
|
||||
{getFileIconFromName(file.name)}
|
||||
{file.name}
|
||||
</p>
|
||||
<span className="flex-1 truncate">{file.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveFile(file.id)}
|
||||
className="shrink-0 p-0.5 hover:bg-muted-foreground/20 rounded transition-colors"
|
||||
aria-label={`Remove ${file.name}`}
|
||||
>
|
||||
<X className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { cn } from "@/lib/utils";
|
|||
import { DateRangeSelector } from "../../components/date-range-selector";
|
||||
import { PeriodicSyncConfig } from "../../components/periodic-sync-config";
|
||||
import { getConnectorConfigComponent } from "../index";
|
||||
import { getConnectorDisplayName } from "../../tabs/all-connectors-tab";
|
||||
|
||||
interface ConnectorEditViewProps {
|
||||
connector: SearchSourceConnector;
|
||||
|
|
@ -151,7 +152,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight text-wrap whitespace-normal wrap-break-word">
|
||||
{connector.name}
|
||||
{getConnectorDisplayName(connector.name)}
|
||||
</h2>
|
||||
<p className="text-xs sm:text-base text-muted-foreground mt-1">
|
||||
Manage your connector settings and sync configuration
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { connectorsApiService } from "@/lib/apis/connectors-api.service";
|
|||
import { cn } from "@/lib/utils";
|
||||
import { COMPOSIO_CONNECTORS, OAUTH_CONNECTORS } from "../constants/connector-constants";
|
||||
import { getDocumentCountForConnector } from "../utils/connector-document-mapping";
|
||||
import { getConnectorDisplayName } from "./all-connectors-tab";
|
||||
|
||||
interface ActiveConnectorsTabProps {
|
||||
searchQuery: string;
|
||||
|
|
@ -263,8 +264,8 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
|
|||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-[14px] font-semibold leading-tight">
|
||||
{connector.name}
|
||||
<p className="text-[14px] font-semibold leading-tight truncate">
|
||||
{getConnectorDisplayName(connector.name)}
|
||||
</p>
|
||||
</div>
|
||||
{isIndexing ? (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue