diff --git a/surfsense_web/components/tool-ui/google-drive/create-file.tsx b/surfsense_web/components/tool-ui/google-drive/create-file.tsx index fb3fb65a0..794504b03 100644 --- a/surfsense_web/components/tool-ui/google-drive/create-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/create-file.tsx @@ -3,15 +3,14 @@ import { makeAssistantToolUI } from "@assistant-ui/react"; import { AlertTriangleIcon, - CheckIcon, + CornerDownLeftIcon, FileIcon, Loader2Icon, Pen, RefreshCwIcon, - XIcon, } from "lucide-react"; import { useParams } from "next/navigation"; -import { useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -22,8 +21,11 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Textarea } from "@/components/ui/textarea"; +import { PlateEditor } from "@/components/editor/plate-editor"; +import { Spinner } from "@/components/ui/spinner"; import { authenticatedFetch } from "@/lib/auth-utils"; +import { useSetAtom } from "jotai"; +import { openHitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom"; interface GoogleDriveAccount { id: number; @@ -121,14 +123,8 @@ function ApprovalCard({ const [decided, setDecided] = useState<"approve" | "reject" | "edit" | null>( interruptData.__decided__ ?? null ); - const [isEditing, setIsEditing] = useState(false); - const [editedName, setEditedName] = useState(args.name ?? ""); - const [editedContent, setEditedContent] = useState(args.content ?? ""); - const [committedArgs, setCommittedArgs] = useState<{ - name: string; - file_type: string; - content?: string | null; - } | null>(null); + const [isPanelOpen, setIsPanelOpen] = useState(false); + const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); const accounts = interruptData.context?.accounts ?? []; @@ -142,8 +138,8 @@ function ApprovalCard({ const [parentFolderId, setParentFolderId] = useState(""); const isNameValid = useMemo( - () => (isEditing ? editedName.trim().length > 0 : args.name?.trim().length > 0), - [isEditing, editedName, args.name] + () => args.name && typeof args.name === "string" && args.name.trim().length > 0, + [args.name] ); const canApprove = !!selectedAccountId && isNameValid; @@ -152,274 +148,219 @@ function ApprovalCard({ const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; const canEdit = allowedDecisions.includes("edit"); - function buildFinalArgs() { - return { - name: isEditing ? editedName : args.name, - file_type: selectedFileType, - content: isEditing ? editedContent || null : (args.content ?? null), - connector_id: selectedAccountId ? Number(selectedAccountId) : null, - parent_folder_id: parentFolderId.trim() || null, + const handleApprove = useCallback(() => { + if (decided || isPanelOpen || !canApprove) return; + if (!allowedDecisions.includes("approve")) return; + setDecided("approve"); + onDecision({ + type: "approve", + edited_action: { + name: interruptData.action_requests[0].name, + args: { + ...args, + file_type: selectedFileType, + connector_id: selectedAccountId ? Number(selectedAccountId) : null, + parent_folder_id: parentFolderId.trim() || null, + }, + }, + }); + }, [decided, isPanelOpen, canApprove, allowedDecisions, onDecision, interruptData, args, selectedFileType, selectedAccountId, parentFolderId]); + + useEffect(() => { + const handler = (e: KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey) { + handleApprove(); + } }; - } + window.addEventListener("keydown", handler); + return () => window.removeEventListener("keydown", handler); + }, [handleApprove]); return ( -
+
{/* Header */} -
-
- -
-
-

Create Google Drive File

-

- {isEditing ? "You can edit the arguments below" : "Requires your approval to proceed"} +

+
+

+ {decided === "reject" + ? "Google Drive File Rejected" + : decided === "approve" || decided === "edit" + ? "Google Drive File Approved" + : "Create Google Drive File"} +

+

+ {decided === "reject" + ? "File creation was cancelled" + : decided === "edit" + ? "File creation is in progress with your changes" + : decided === "approve" + ? "File creation is in progress" + : "Requires your approval to proceed"}

+ {!decided && canEdit && ( + + )}
{/* Context section */} {!decided && interruptData.context && ( -
- {interruptData.context.error ? ( -

{interruptData.context.error}

- ) : ( - <> - {accounts.length > 0 && ( -
-
- Google Drive Account * + <> +
+
+ {interruptData.context.error ? ( +

{interruptData.context.error}

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

+ Google Drive Account * +

+
- - + - {accounts.map((account) => ( - - {account.name} - - ))} + Google Doc + Google Sheet
- )} -
-
- File Type * +
+

+ Parent Folder ID (optional) +

+ setParentFolderId(e.target.value)} + placeholder="Leave blank to create at Drive root" + /> +

+ Paste a Google Drive folder ID to place the file in a specific folder. +

- -
- -
-
- Parent Folder ID (optional) -
- setParentFolderId(e.target.value)} - placeholder="Leave blank to create at Drive root" - /> -

- Paste a Google Drive folder ID to place the file in a specific folder. -

-
- - )} -
- )} - - {/* Display mode */} - {!isEditing && ( -
-
-

Name

-

{committedArgs?.name ?? args.name}

-
-
-

Type

-

- {FILE_TYPE_LABELS[committedArgs?.file_type ?? args.file_type] ?? - committedArgs?.file_type ?? - args.file_type} -

-
- {(committedArgs?.content ?? args.content) && ( -
-

Content

-

- {committedArgs?.content ?? args.content} -

-
- )} -
- )} - - {/* Edit mode */} - {isEditing && !decided && ( -
-
- - setEditedName(e.target.value)} - placeholder="Enter file name" - className={!isNameValid ? "border-destructive" : ""} - /> - {!isNameValid &&

Name is required

} -
-
- -