diff --git a/frontend/components/ui/card.tsx b/frontend/components/ui/card.tsx index d1ec83e7..92c8a189 100644 --- a/frontend/components/ui/card.tsx +++ b/frontend/components/ui/card.tsx @@ -9,7 +9,7 @@ const Card = React.forwardRef< ref={ref} className={cn( "rounded-xl border border-border bg-card text-card-foreground shadow-sm", - className, + className )} {...props} /> @@ -33,8 +33,8 @@ const CardTitle = React.forwardRef<

diff --git a/frontend/components/ui/select.tsx b/frontend/components/ui/select.tsx index 66665060..63874553 100644 --- a/frontend/components/ui/select.tsx +++ b/frontend/components/ui/select.tsx @@ -26,7 +26,7 @@ const SelectTrigger = React.forwardRef< span]:line-clamp-1", + "flex h-10 w-full items-center justify-between rounded-md border border-input px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:bg-muted [&>span]:line-clamp-1 disabled:border-none", className )} {...props} @@ -34,7 +34,7 @@ const SelectTrigger = React.forwardRef< {children} {props.disabled ? ( - + ) : ( )} diff --git a/frontend/src/app/settings/icons/google-drive-icon.tsx b/frontend/src/app/settings/icons/google-drive-icon.tsx new file mode 100644 index 00000000..0980e644 --- /dev/null +++ b/frontend/src/app/settings/icons/google-drive-icon.tsx @@ -0,0 +1,36 @@ +const GoogleDriveIcon = () => ( + + + + + + + + +); + +export default GoogleDriveIcon; diff --git a/frontend/src/app/settings/icons/one-drive-icon.tsx b/frontend/src/app/settings/icons/one-drive-icon.tsx new file mode 100644 index 00000000..30b77a65 --- /dev/null +++ b/frontend/src/app/settings/icons/one-drive-icon.tsx @@ -0,0 +1,164 @@ +const OneDriveIcon = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default OneDriveIcon; diff --git a/frontend/src/app/settings/icons/share-point-icon.tsx b/frontend/src/app/settings/icons/share-point-icon.tsx new file mode 100644 index 00000000..3f51dfae --- /dev/null +++ b/frontend/src/app/settings/icons/share-point-icon.tsx @@ -0,0 +1,211 @@ +const SharePointIcon = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +export default SharePointIcon; diff --git a/frontend/src/app/settings/page.tsx b/frontend/src/app/settings/page.tsx index 7530bcbb..6017ab5b 100644 --- a/frontend/src/app/settings/page.tsx +++ b/frontend/src/app/settings/page.tsx @@ -1,7 +1,8 @@ "use client"; -import { ArrowUpRight, Loader2, PlugZap, Plus, RefreshCw } from "lucide-react"; +import { ArrowUpRight, Loader2, Plus, Minus } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; +import Link from "next/link"; import { Suspense, useCallback, useEffect, useState } from "react"; import { useUpdateFlowSettingMutation } from "@/app/api/mutations/useUpdateFlowSettingMutation"; import { @@ -12,7 +13,6 @@ import { import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; import { ConfirmationDialog } from "@/components/confirmation-dialog"; import { ProtectedRoute } from "@/components/protected-route"; -import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, @@ -21,7 +21,6 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Checkbox } from "@/components/ui/checkbox"; import { Switch } from "@/components/ui/switch"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -44,6 +43,10 @@ import { getFallbackModels, type ModelProvider } from "./helpers/model-helpers"; import { ModelSelectItems } from "./helpers/model-select-item"; import { LabelWrapper } from "@/components/label-wrapper"; +import GoogleDriveIcon from "./icons/google-drive-icon"; +import OneDriveIcon from "./icons/one-drive-icon"; +import SharePointIcon from "./icons/share-point-icon"; + const { MAX_SYSTEM_PROMPT_CHARS } = UI_CONSTANTS; interface GoogleDriveFile { @@ -92,6 +95,33 @@ interface Connection { last_sync?: string; } +const DEFAULT_CONNECTORS: Connector[] = [ + { + id: "google_drive", + name: "Google Drive", + description: "Google Drive is not configured.", + icon: , + status: "not_connected", + type: "google_drive", + }, + { + id: "one_drive", + name: "OneDrive", + description: "OneDrive is not configured.", + icon: , + status: "not_connected", + type: "one_drive", + }, + { + id: "amazon_s3", + name: "SharePoint", + description: "SharePoint is not configured.", + icon: , + status: "not_connected", + type: "sharepoint", + }, +]; + function KnowledgeSourcesPage() { const { isAuthenticated, isNoAuthMode } = useAuth(); const { addTask, tasks } = useTask(); @@ -114,7 +144,8 @@ function KnowledgeSourcesPage() { const [chunkOverlap, setChunkOverlap] = useState(50); const [tableStructure, setTableStructure] = useState(false); const [ocr, setOcr] = useState(false); - const [pictureDescriptions, setPictureDescriptions] = useState(false); + const [pictureDescriptions, setPictureDescriptions] = + useState(false); // Fetch settings using React Query const { data: settings = {} } = useGetSettingsQuery({ @@ -164,7 +195,7 @@ function KnowledgeSourcesPage() { onSuccess: () => { console.log("Setting updated successfully"); }, - onError: error => { + onError: (error) => { console.error("Failed to update setting:", error.message); }, }); @@ -261,22 +292,20 @@ function KnowledgeSourcesPage() { updateFlowSettingMutation.mutate({ picture_descriptions: checked }); }; + console.log({ connectors }); + // Helper function to get connector icon const getConnectorIcon = useCallback((iconName: string) => { const iconMap: { [key: string]: React.ReactElement } = { - "google-drive": ( -
- G -
- ), + "google-drive": , sharepoint: (
SP
), onedrive: ( -
- OD +
+
), }; @@ -303,8 +332,8 @@ function KnowledgeSourcesPage() { // Initialize connectors list with metadata from backend const initialConnectors = connectorTypes - .filter(type => connectorsResult.connectors[type].available) // Only show available connectors - .map(type => ({ + .filter((type) => connectorsResult.connectors[type].available) // Only show available connectors + .map((type) => ({ id: type, name: connectorsResult.connectors[type].name, description: connectorsResult.connectors[type].description, @@ -312,7 +341,7 @@ function KnowledgeSourcesPage() { status: "not_connected" as const, type: type, })); - + console.log({ initialConnectors }); setConnectors(initialConnectors); // Check status for each connector type @@ -327,8 +356,8 @@ function KnowledgeSourcesPage() { ); const isConnected = activeConnection !== undefined; - setConnectors(prev => - prev.map(c => + setConnectors((prev) => + prev.map((c) => c.type === connectorType ? { ...c, @@ -347,7 +376,7 @@ function KnowledgeSourcesPage() { const handleConnect = async (connector: Connector) => { setIsConnecting(connector.id); - setSyncResults(prev => ({ ...prev, [connector.id]: null })); + setSyncResults((prev) => ({ ...prev, [connector.id]: null })); try { // Use the shared auth callback URL, same as connectors page @@ -453,34 +482,13 @@ function KnowledgeSourcesPage() { const getStatusBadge = (status: Connector["status"]) => { switch (status) { case "connected": - return ( - - Connected - - ); + return
; case "connecting": - return ( - - Connecting... - - ); + return
; case "error": - return Error; + return
; default: - return ( - - Not Connected - - ); + return
; } }; @@ -508,9 +516,9 @@ function KnowledgeSourcesPage() { // Watch for task completions and refresh stats useEffect(() => { // Find newly completed tasks by comparing with previous state - const newlyCompletedTasks = tasks.filter(task => { + const newlyCompletedTasks = tasks.filter((task) => { const wasCompleted = - prevTasks.find(prev => prev.task_id === task.task_id)?.status === + prevTasks.find((prev) => prev.task_id === task.task_id)?.status === "completed"; return task.status === "completed" && !wasCompleted; }); @@ -564,7 +572,7 @@ function KnowledgeSourcesPage() { fetch(`/api/reset-flow/retrieval`, { method: "POST", }) - .then(response => { + .then((response) => { if (response.ok) { return response.json(); } @@ -577,7 +585,7 @@ function KnowledgeSourcesPage() { handleModelChange(DEFAULT_AGENT_SETTINGS.llm_model); closeDialog(); // Close after successful completion }) - .catch(error => { + .catch((error) => { console.error("Error restoring retrieval flow:", error); closeDialog(); // Close even on error (could show error toast instead) }); @@ -587,7 +595,7 @@ function KnowledgeSourcesPage() { fetch(`/api/reset-flow/ingest`, { method: "POST", }) - .then(response => { + .then((response) => { if (response.ok) { return response.json(); } @@ -602,7 +610,7 @@ function KnowledgeSourcesPage() { setPictureDescriptions(false); closeDialog(); // Close after successful completion }) - .catch(error => { + .catch((error) => { console.error("Error restoring ingest flow:", error); closeDialog(); // Close even on error (could show error toast instead) }); @@ -613,7 +621,7 @@ function KnowledgeSourcesPage() { {/* Connectors Section */}
-

+

Cloud Connectors

@@ -700,73 +708,85 @@ function KnowledgeSourcesPage() { {/* Connectors Grid */}
- {connectors.map(connector => ( - - -
-
- {connector.icon} -
- + {DEFAULT_CONNECTORS.map((connector) => { + const actualConnector = connectors.find( + (c) => c.id === connector.id + ); + return ( + + +
+
+
+
+ {connector.icon} +
+
+ {connector.name} + {actualConnector && + getStatusBadge(actualConnector.status)} - - {connector.description} + + {actualConnector?.description + ? `${actualConnector.name} is configured.` + : connector.description}
- {getStatusBadge(connector.status)} -
- - - {connector.status === "connected" ? ( -
- + + + {actualConnector?.status === "connected" ? ( +
+ - {syncResults[connector.id] && ( -
-
- Processed: {syncResults[connector.id]?.processed || 0} + {syncResults[connector.id] && ( +
+
+ Processed:{" "} + {syncResults[connector.id]?.processed || 0} +
+
+ Added: {syncResults[connector.id]?.added || 0} +
+ {syncResults[connector.id]?.errors && ( +
+ Errors: {syncResults[connector.id]?.errors} +
+ )}
-
- Added: {syncResults[connector.id]?.added || 0} -
- {syncResults[connector.id]?.errors && ( -
Errors: {syncResults[connector.id]?.errors}
- )} -
- )} -
- ) : ( - - )} - - - ))} + )} +
+ ) : ( +
+

+ See our{" "} + + Cloud Connectors installation guide + {" "} + for more detail. +

+
+ )} +
+ + ); + })}
{/* Agent Behavior Section */} @@ -834,7 +854,7 @@ function KnowledgeSourcesPage() { } confirmText="Proceed" confirmIcon={} - onConfirm={closeDialog => + onConfirm={(closeDialog) => handleEditInLangflow("chat", closeDialog) } variant="warning" @@ -854,7 +874,8 @@ function KnowledgeSourcesPage() {