diff --git a/surfsense_backend/app/agents/new_chat/tools/onedrive/create_file.py b/surfsense_backend/app/agents/new_chat/tools/onedrive/create_file.py index 0dee72d3f..a712c9a45 100644 --- a/surfsense_backend/app/agents/new_chat/tools/onedrive/create_file.py +++ b/surfsense_backend/app/agents/new_chat/tools/onedrive/create_file.py @@ -109,7 +109,32 @@ def create_create_onedrive_file_tool( "connector_type": "onedrive", } - context = {"accounts": accounts} + parent_folders: dict[int, list[dict[str, str]]] = {} + for acc in accounts: + cid = acc["id"] + if acc.get("auth_expired"): + parent_folders[cid] = [] + continue + try: + client = OneDriveClient(session=db_session, connector_id=cid) + items, err = await client.list_children("root") + if err: + logger.warning("Failed to list folders for connector %s: %s", cid, err) + parent_folders[cid] = [] + else: + parent_folders[cid] = [ + {"folder_id": item["id"], "name": item["name"]} + for item in items + if item.get("folder") is not None and item.get("id") and item.get("name") + ] + except Exception: + logger.warning("Error fetching folders for connector %s", cid, exc_info=True) + parent_folders[cid] = [] + + context: dict[str, Any] = { + "accounts": accounts, + "parent_folders": parent_folders, + } approval = interrupt( { diff --git a/surfsense_web/components/tool-ui/onedrive/create-file.tsx b/surfsense_web/components/tool-ui/onedrive/create-file.tsx index 5af1c3d94..c75be7f7f 100644 --- a/surfsense_web/components/tool-ui/onedrive/create-file.tsx +++ b/surfsense_web/components/tool-ui/onedrive/create-file.tsx @@ -35,6 +35,7 @@ interface InterruptResult { }>; context?: { accounts?: OneDriveAccount[]; + parent_folders?: Record>; error?: string; }; } @@ -107,6 +108,7 @@ function ApprovalCard({ const accounts = interruptData.context?.accounts ?? []; const validAccounts = accounts.filter((a) => !a.auth_expired); + const expiredAccounts = accounts.filter((a) => a.auth_expired); const defaultAccountId = useMemo(() => { if (validAccounts.length === 1) return String(validAccounts[0].id); @@ -114,6 +116,18 @@ function ApprovalCard({ }, [validAccounts]); const [selectedAccountId, setSelectedAccountId] = useState(defaultAccountId); + const [parentFolderId, setParentFolderId] = useState("__root__"); + + const parentFolders = interruptData.context?.parent_folders ?? {}; + const availableParentFolders = useMemo(() => { + if (!selectedAccountId) return []; + return parentFolders[Number(selectedAccountId)] ?? []; + }, [selectedAccountId, parentFolders]); + + const handleAccountChange = useCallback((value: string) => { + setSelectedAccountId(value); + setParentFolderId("__root__"); + }, []); const isNameValid = useMemo(() => { const name = pendingEdits?.name ?? args.name; @@ -138,10 +152,23 @@ function ApprovalCard({ ...args, ...(pendingEdits && { name: pendingEdits.name, content: pendingEdits.content }), connector_id: selectedAccountId ? Number(selectedAccountId) : null, + parent_folder_id: parentFolderId === "__root__" ? null : parentFolderId, }, }, }); - }, [phase, isPanelOpen, canApprove, allowedDecisions, pendingEdits, setProcessing, onDecision, interruptData, args, selectedAccountId]); + }, [ + phase, + setProcessing, + isPanelOpen, + canApprove, + allowedDecisions, + onDecision, + interruptData, + args, + selectedAccountId, + parentFolderId, + pendingEdits, + ]); useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -153,56 +180,134 @@ function ApprovalCard({ return (
+ {/* Header */}

- {phase === "rejected" ? "OneDrive File Rejected" : phase === "processing" || phase === "complete" ? "OneDrive File Approved" : "Create OneDrive File"} + {phase === "rejected" + ? "Word Document Rejected" + : phase === "processing" || phase === "complete" + ? "Word Document Approved" + : "Create Word Document"}

{phase === "processing" ? ( - + ) : phase === "complete" ? ( -

{pendingEdits ? "File created with your changes" : "File created"}

+

+ {pendingEdits ? "File created with your changes" : "File created"} +

) : phase === "rejected" ? (

File creation was cancelled

) : ( -

Requires your approval to proceed

+

+ Requires your approval to proceed +

)}
{phase === "pending" && canEdit && ( - )}
+ {/* Context section — pickers in pending */} {phase === "pending" && interruptData.context && ( <>
{interruptData.context.error ? (

{interruptData.context.error}

- ) : accounts.length > 0 ? ( -
-

OneDrive Account *

- -
- ) : null} + ) : ( + <> + {accounts.length > 0 && ( +
+

+ OneDrive Account * +

+ +
+ )} + +
+

+ File Type +

+ +
+ + {selectedAccountId && ( +
+

Parent Folder

+ + {availableParentFolders.length === 0 && ( +

+ No folders found. File will be created at OneDrive root. +

+ )} +
+ )} + + )}
)}