diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx index e2f219448..fd1f7da1d 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx @@ -5,6 +5,8 @@ import { Calendar as CalendarIcon, Clock, Edit, + Folder, + HardDrive, Loader2, Plus, RefreshCw, @@ -61,6 +63,13 @@ import { EnumConnectorName } from "@/contracts/enums/connector"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { useSearchSourceConnectors } from "@/hooks/use-search-source-connectors"; import { cn } from "@/lib/utils"; +import { authenticatedFetch } from "@/lib/auth-utils"; +import { GoogleDriveFolderTree } from "@/components/connectors/google-drive-folder-tree"; + +interface DriveFolder { + id: string; + name: string; +} export default function ConnectorsPage() { const t = useTranslations("connectors"); @@ -105,6 +114,13 @@ export default function ConnectorsPage() { const [customFrequency, setCustomFrequency] = useState(""); const [isSavingPeriodic, setIsSavingPeriodic] = useState(false); + // Google Drive folder selection state + const [driveFolderDialogOpen, setDriveFolderDialogOpen] = useState(false); + const [driveFolders, setDriveFolders] = useState([]); + const [selectedFolderId, setSelectedFolderId] = useState(""); + const [selectedFolderName, setSelectedFolderName] = useState(""); + const [isLoadingFolders, setIsLoadingFolders] = useState(false); + useEffect(() => { if (error) { toast.error(t("failed_load")); @@ -129,8 +145,78 @@ export default function ConnectorsPage() { // Handle opening date picker for indexing const handleOpenDatePicker = (connectorId: number) => { + // Check if this is a Google Drive connector + const connector = connectors.find((c) => c.id === connectorId); + if (connector?.connector_type === EnumConnectorName.GOOGLE_DRIVE_CONNECTOR) { + // Open folder selection dialog for Google Drive + handleOpenDriveFolderDialog(connectorId); + } else { + // Open date picker for other connectors + setSelectedConnectorForIndexing(connectorId); + setDatePickerOpen(true); + } + }; + + // Handle opening Google Drive folder selection dialog + const handleOpenDriveFolderDialog = async (connectorId: number) => { setSelectedConnectorForIndexing(connectorId); - setDatePickerOpen(true); + setDriveFolderDialogOpen(true); + setIsLoadingFolders(true); + + try { + const response = await authenticatedFetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/connectors/${connectorId}/google-drive/folders`, + { method: "GET" } + ); + + if (!response.ok) { + throw new Error("Failed to load folders"); + } + + const data = await response.json(); + setDriveFolders(data.folders || []); + } catch (error) { + console.error("Error loading folders:", error); + toast.error("Failed to load Google Drive folders"); + setDriveFolderDialogOpen(false); + } finally { + setIsLoadingFolders(false); + } + }; + + // Handle Google Drive folder indexing + const handleIndexDriveFolder = async () => { + if (selectedConnectorForIndexing === null || !selectedFolderId) { + toast.error("Please select a folder"); + return; + } + + setDriveFolderDialogOpen(false); + + try { + setIndexingConnectorId(selectedConnectorForIndexing); + const selectedFolder = driveFolders.find((f) => f.id === selectedFolderId); + const folderName = selectedFolder?.name || "Selected Folder"; + + // Call indexConnector with folder_id and folder_name as query params + await indexConnector( + selectedConnectorForIndexing, + searchSpaceId, + undefined, + undefined, + selectedFolderId, + folderName + ); + toast.success(t("indexing_started")); + } catch (error) { + console.error("Error indexing connector content:", error); + toast.error(error instanceof Error ? error.message : t("indexing_failed")); + } finally { + setIndexingConnectorId(null); + setSelectedConnectorForIndexing(null); + setSelectedFolderId(""); + setDriveFolders([]); + } }; // Handle connector indexing with dates @@ -361,39 +447,52 @@ export default function ConnectorsPage() { > {indexingConnectorId === connector.id ? ( + ) : connector.connector_type === EnumConnectorName.GOOGLE_DRIVE_CONNECTOR ? ( + ) : ( )} - {t("index_date_range")} + + {connector.connector_type === EnumConnectorName.GOOGLE_DRIVE_CONNECTOR + ? "Select folder to index" + : t("index_date_range")} + -

{t("index_date_range")}

-
- - - - - - - - -

{t("quick_index_auto")}

+

+ {connector.connector_type === EnumConnectorName.GOOGLE_DRIVE_CONNECTOR + ? "Select folder to index" + : t("index_date_range")} +

+ {/* Hide quick index button for Google Drive (requires folder selection) */} + {connector.connector_type !== EnumConnectorName.GOOGLE_DRIVE_CONNECTOR && ( + + + + + + +

{t("quick_index_auto")}

+
+
+
+ )} )} {connector.is_indexable && ( @@ -581,6 +680,72 @@ export default function ConnectorsPage() { + {/* Google Drive Folder Selection Dialog */} + + + + Select Google Drive Folder + + Browse and select a folder to index. Click folders to expand and see subfolders. + + +
+
+ + {selectedConnectorForIndexing && ( + { + setSelectedFolderId(folderId); + setSelectedFolderName(folderName); + }} + /> + )} +

+ Changes to files in this folder will be automatically detected and re-indexed. +

+
+ {selectedFolderId && selectedFolderName && ( +
+
+

Selected folder:

+

+ {selectedFolderName} +

+
+
+

What will be indexed:

+
    +
  • Google Docs, Sheets, Slides (as PDFs)
  • +
  • PDFs, Word, Excel, PowerPoint files
  • +
  • Text files, markdown, code files
  • +
  • Images (with OCR if enabled)
  • +
+
+
+ )} +
+ + + + +
+
+ {/* Periodic Indexing Configuration Dialog */}