From 1eb0fb3593458f2a064aea14af237d8a99f4a5f9 Mon Sep 17 00:00:00 2001
From: Brent O'Neill
Date: Fri, 3 Oct 2025 15:00:53 -0600
Subject: [PATCH] design sweep for ingest for connector page
---
frontend/src/app/upload/[provider]/page.tsx | 659 +++++++++---------
.../components/cloud-picker/picker-header.tsx | 8 +-
2 files changed, 332 insertions(+), 335 deletions(-)
diff --git a/frontend/src/app/upload/[provider]/page.tsx b/frontend/src/app/upload/[provider]/page.tsx
index 7c72ec3d..8e0a306c 100644
--- a/frontend/src/app/upload/[provider]/page.tsx
+++ b/frontend/src/app/upload/[provider]/page.tsx
@@ -12,367 +12,370 @@ import { useTask } from "@/contexts/task-context";
// CloudFile interface is now imported from the unified cloud picker
interface CloudConnector {
- id: string;
- name: string;
- description: string;
- status: "not_connected" | "connecting" | "connected" | "error";
- type: string;
- connectionId?: string;
- clientId: string;
- hasAccessToken: boolean;
- accessTokenError?: string;
+ id: string;
+ name: string;
+ description: string;
+ status: "not_connected" | "connecting" | "connected" | "error";
+ type: string;
+ connectionId?: string;
+ clientId: string;
+ hasAccessToken: boolean;
+ accessTokenError?: string;
}
export default function UploadProviderPage() {
- const params = useParams();
- const router = useRouter();
- const provider = params.provider as string;
- const { addTask, tasks } = useTask();
+ const params = useParams();
+ const router = useRouter();
+ const provider = params.provider as string;
+ const { addTask, tasks } = useTask();
- const [connector, setConnector] = useState(null);
- const [isLoading, setIsLoading] = useState(true);
- const [error, setError] = useState(null);
- const [accessToken, setAccessToken] = useState(null);
- const [selectedFiles, setSelectedFiles] = useState([]);
- const [isIngesting, setIsIngesting] = useState(false);
- const [currentSyncTaskId, setCurrentSyncTaskId] = useState(
- null,
- );
- const [ingestSettings, setIngestSettings] = useState({
- chunkSize: 1000,
- chunkOverlap: 200,
- ocr: false,
- pictureDescriptions: false,
- embeddingModel: "text-embedding-3-small",
- });
+ const [connector, setConnector] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [accessToken, setAccessToken] = useState(null);
+ const [selectedFiles, setSelectedFiles] = useState([]);
+ const [isIngesting, setIsIngesting] = useState(false);
+ const [currentSyncTaskId, setCurrentSyncTaskId] = useState(
+ null
+ );
+ const [ingestSettings, setIngestSettings] = useState({
+ chunkSize: 1000,
+ chunkOverlap: 200,
+ ocr: false,
+ pictureDescriptions: false,
+ embeddingModel: "text-embedding-3-small",
+ });
- useEffect(() => {
- const fetchConnectorInfo = async () => {
- setIsLoading(true);
- setError(null);
+ useEffect(() => {
+ const fetchConnectorInfo = async () => {
+ setIsLoading(true);
+ setError(null);
- try {
- // Fetch available connectors to validate the provider
- const connectorsResponse = await fetch("/api/connectors");
- if (!connectorsResponse.ok) {
- throw new Error("Failed to load connectors");
- }
+ try {
+ // Fetch available connectors to validate the provider
+ const connectorsResponse = await fetch("/api/connectors");
+ if (!connectorsResponse.ok) {
+ throw new Error("Failed to load connectors");
+ }
- const connectorsResult = await connectorsResponse.json();
- const providerInfo = connectorsResult.connectors[provider];
+ const connectorsResult = await connectorsResponse.json();
+ const providerInfo = connectorsResult.connectors[provider];
- if (!providerInfo || !providerInfo.available) {
- setError(
- `Cloud provider "${provider}" is not available or configured.`,
- );
- return;
- }
+ if (!providerInfo || !providerInfo.available) {
+ setError(
+ `Cloud provider "${provider}" is not available or configured.`
+ );
+ return;
+ }
- // Check connector status
- const statusResponse = await fetch(
- `/api/connectors/${provider}/status`,
- );
- if (!statusResponse.ok) {
- throw new Error(`Failed to check ${provider} status`);
- }
+ // Check connector status
+ const statusResponse = await fetch(
+ `/api/connectors/${provider}/status`
+ );
+ if (!statusResponse.ok) {
+ throw new Error(`Failed to check ${provider} status`);
+ }
- const statusData = await statusResponse.json();
- const connections = statusData.connections || [];
- const activeConnection = connections.find(
- (conn: { is_active: boolean; connection_id: string }) =>
- conn.is_active,
- );
- const isConnected = activeConnection !== undefined;
+ const statusData = await statusResponse.json();
+ const connections = statusData.connections || [];
+ const activeConnection = connections.find(
+ (conn: { is_active: boolean; connection_id: string }) =>
+ conn.is_active
+ );
+ const isConnected = activeConnection !== undefined;
- let hasAccessToken = false;
- let accessTokenError: string | undefined;
+ let hasAccessToken = false;
+ let accessTokenError: string | undefined;
- // Try to get access token for connected connectors
- if (isConnected && activeConnection) {
- try {
- const tokenResponse = await fetch(
- `/api/connectors/${provider}/token?connection_id=${activeConnection.connection_id}`,
- );
- if (tokenResponse.ok) {
- const tokenData = await tokenResponse.json();
- if (tokenData.access_token) {
- hasAccessToken = true;
- setAccessToken(tokenData.access_token);
- }
- } else {
- const errorData = await tokenResponse
- .json()
- .catch(() => ({ error: "Token unavailable" }));
- accessTokenError = errorData.error || "Access token unavailable";
- }
- } catch {
- accessTokenError = "Failed to fetch access token";
- }
- }
+ // Try to get access token for connected connectors
+ if (isConnected && activeConnection) {
+ try {
+ const tokenResponse = await fetch(
+ `/api/connectors/${provider}/token?connection_id=${activeConnection.connection_id}`
+ );
+ if (tokenResponse.ok) {
+ const tokenData = await tokenResponse.json();
+ if (tokenData.access_token) {
+ hasAccessToken = true;
+ setAccessToken(tokenData.access_token);
+ }
+ } else {
+ const errorData = await tokenResponse
+ .json()
+ .catch(() => ({ error: "Token unavailable" }));
+ accessTokenError = errorData.error || "Access token unavailable";
+ }
+ } catch {
+ accessTokenError = "Failed to fetch access token";
+ }
+ }
- setConnector({
- id: provider,
- name: providerInfo.name,
- description: providerInfo.description,
- status: isConnected ? "connected" : "not_connected",
- type: provider,
- connectionId: activeConnection?.connection_id,
- clientId: activeConnection?.client_id,
- hasAccessToken,
- accessTokenError,
- });
- } catch (error) {
- console.error("Failed to load connector info:", error);
- setError(
- error instanceof Error
- ? error.message
- : "Failed to load connector information",
- );
- } finally {
- setIsLoading(false);
- }
- };
+ setConnector({
+ id: provider,
+ name: providerInfo.name,
+ description: providerInfo.description,
+ status: isConnected ? "connected" : "not_connected",
+ type: provider,
+ connectionId: activeConnection?.connection_id,
+ clientId: activeConnection?.client_id,
+ hasAccessToken,
+ accessTokenError,
+ });
+ } catch (error) {
+ console.error("Failed to load connector info:", error);
+ setError(
+ error instanceof Error
+ ? error.message
+ : "Failed to load connector information"
+ );
+ } finally {
+ setIsLoading(false);
+ }
+ };
- if (provider) {
- fetchConnectorInfo();
- }
- }, [provider]);
+ if (provider) {
+ fetchConnectorInfo();
+ }
+ }, [provider]);
- // Watch for sync task completion and redirect
- useEffect(() => {
- if (!currentSyncTaskId) return;
+ // Watch for sync task completion and redirect
+ useEffect(() => {
+ if (!currentSyncTaskId) return;
- const currentTask = tasks.find(
- (task) => task.task_id === currentSyncTaskId,
- );
+ const currentTask = tasks.find(
+ (task) => task.task_id === currentSyncTaskId
+ );
- if (currentTask && currentTask.status === "completed") {
- // Task completed successfully, show toast and redirect
- setIsIngesting(false);
- setTimeout(() => {
- router.push("/knowledge");
- }, 2000); // 2 second delay to let user see toast
- } else if (currentTask && currentTask.status === "failed") {
- // Task failed, clear the tracking but don't redirect
- setIsIngesting(false);
- setCurrentSyncTaskId(null);
- }
- }, [tasks, currentSyncTaskId, router]);
+ if (currentTask && currentTask.status === "completed") {
+ // Task completed successfully, show toast and redirect
+ setIsIngesting(false);
+ setTimeout(() => {
+ router.push("/knowledge");
+ }, 2000); // 2 second delay to let user see toast
+ } else if (currentTask && currentTask.status === "failed") {
+ // Task failed, clear the tracking but don't redirect
+ setIsIngesting(false);
+ setCurrentSyncTaskId(null);
+ }
+ }, [tasks, currentSyncTaskId, router]);
- const handleFileSelected = (files: CloudFile[]) => {
- setSelectedFiles(files);
- console.log(`Selected ${files.length} files from ${provider}:`, files);
- // You can add additional handling here like triggering sync, etc.
- };
+ const handleFileSelected = (files: CloudFile[]) => {
+ setSelectedFiles(files);
+ console.log(`Selected ${files.length} files from ${provider}:`, files);
+ // You can add additional handling here like triggering sync, etc.
+ };
- const handleSync = async (connector: CloudConnector) => {
- if (!connector.connectionId || selectedFiles.length === 0) return;
+ const handleSync = async (connector: CloudConnector) => {
+ if (!connector.connectionId || selectedFiles.length === 0) return;
- setIsIngesting(true);
+ setIsIngesting(true);
- try {
- const syncBody: {
- connection_id: string;
- max_files?: number;
- selected_files?: string[];
- settings?: IngestSettings;
- } = {
- connection_id: connector.connectionId,
- selected_files: selectedFiles.map((file) => file.id),
- settings: ingestSettings,
- };
+ try {
+ const syncBody: {
+ connection_id: string;
+ max_files?: number;
+ selected_files?: string[];
+ settings?: IngestSettings;
+ } = {
+ connection_id: connector.connectionId,
+ selected_files: selectedFiles.map((file) => file.id),
+ settings: ingestSettings,
+ };
- const response = await fetch(`/api/connectors/${connector.type}/sync`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(syncBody),
- });
+ const response = await fetch(`/api/connectors/${connector.type}/sync`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(syncBody),
+ });
- const result = await response.json();
+ const result = await response.json();
- if (response.status === 201) {
- const taskIds = result.task_ids;
- if (taskIds && taskIds.length > 0) {
- const taskId = taskIds[0]; // Use the first task ID
- addTask(taskId);
- setCurrentSyncTaskId(taskId);
- }
- } else {
- console.error("Sync failed:", result.error);
- }
- } catch (error) {
- console.error("Sync error:", error);
- setIsIngesting(false);
- }
- };
+ if (response.status === 201) {
+ const taskIds = result.task_ids;
+ if (taskIds && taskIds.length > 0) {
+ const taskId = taskIds[0]; // Use the first task ID
+ addTask(taskId);
+ setCurrentSyncTaskId(taskId);
+ }
+ } else {
+ console.error("Sync failed:", result.error);
+ }
+ } catch (error) {
+ console.error("Sync error:", error);
+ setIsIngesting(false);
+ }
+ };
- const getProviderDisplayName = () => {
- const nameMap: { [key: string]: string } = {
- google_drive: "Google Drive",
- onedrive: "OneDrive",
- sharepoint: "SharePoint",
- };
- return nameMap[provider] || provider;
- };
+ const getProviderDisplayName = () => {
+ const nameMap: { [key: string]: string } = {
+ google_drive: "Google Drive",
+ onedrive: "OneDrive",
+ sharepoint: "SharePoint",
+ };
+ return nameMap[provider] || provider;
+ };
- if (isLoading) {
- return (
-
-
-
-
-
Loading {getProviderDisplayName()} connector...
-
-
-
- );
- }
+ if (isLoading) {
+ return (
+
+
+
+
+
Loading {getProviderDisplayName()} connector...
+
+
+
+ );
+ }
- if (error || !connector) {
- return (
-
-
-
-
+ if (error || !connector) {
+ return (
+
+
+
+
-
-
-
-
- Provider Not Available
-
-
{error}
-
-
-
-
- );
- }
+
+
+
+
+ Provider Not Available
+
+
{error}
+
+
+
+
+ );
+ }
- if (connector.status !== "connected") {
- return (
-
-
-
-
+ if (connector.status !== "connected") {
+ return (
+
+
+
+
-
-
-
-
- {connector.name} Not Connected
-
-
- You need to connect your {connector.name} account before you can
- select files.
-
-
-
-
-
- );
- }
+
+
+
+
+ {connector.name} Not Connected
+
+
+ You need to connect your {connector.name} account before you can
+ select files.
+
+
+
+
+
+ );
+ }
- if (!connector.hasAccessToken) {
- return (
-
-
-
-
+ if (!connector.hasAccessToken) {
+ return (
+
+
+
+
-
-
-
-
- Access Token Required
-
-
- {connector.accessTokenError ||
- `Unable to get access token for ${connector.name}. Try reconnecting your account.`}
-
-
-
-
-
- );
- }
+
+
+
+
+ Access Token Required
+
+
+ {connector.accessTokenError ||
+ `Unable to get access token for ${connector.name}. Try reconnecting your account.`}
+
+
+
+
+
+ );
+ }
- return (
-
-
-
-
- Add from {getProviderDisplayName()}
-
-
+ return (
+
+
+
+
+ Add from {getProviderDisplayName()}
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
- );
+
+
+
+
+
+
+
+ );
}
diff --git a/frontend/src/components/cloud-picker/picker-header.tsx b/frontend/src/components/cloud-picker/picker-header.tsx
index 54407aa7..e0d9cfa4 100644
--- a/frontend/src/components/cloud-picker/picker-header.tsx
+++ b/frontend/src/components/cloud-picker/picker-header.tsx
@@ -51,19 +51,13 @@ export const PickerHeader = ({
Select files from {getProviderName(provider)} to ingest.
-
- csv, json, pdf,{" "}
-
+16 more{" "}
-
150 MB max
-
);