"use client"; import { makeAssistantToolUI } from "@assistant-ui/react"; import { AlertTriangleIcon, CornerDownLeftIcon, FileIcon, Loader2Icon, Pen, RefreshCwIcon, } from "lucide-react"; import { useParams } from "next/navigation"; import { useCallback, useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; 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; name: string; } interface InterruptResult { __interrupt__: true; __decided__?: "approve" | "reject" | "edit"; action_requests: Array<{ name: string; args: Record; }>; review_configs: Array<{ action_name: string; allowed_decisions: Array<"approve" | "edit" | "reject">; }>; context?: { accounts?: GoogleDriveAccount[]; supported_types?: string[]; error?: string; }; } interface SuccessResult { status: "success"; file_id: string; name: string; web_view_link?: string; message?: string; } interface ErrorResult { status: "error"; message: string; } interface InsufficientPermissionsResult { status: "insufficient_permissions"; connector_id: number; message: string; } type CreateGoogleDriveFileResult = | InterruptResult | SuccessResult | ErrorResult | InsufficientPermissionsResult; function isInterruptResult(result: unknown): result is InterruptResult { return ( typeof result === "object" && result !== null && "__interrupt__" in result && (result as InterruptResult).__interrupt__ === true ); } function isErrorResult(result: unknown): result is ErrorResult { return ( typeof result === "object" && result !== null && "status" in result && (result as ErrorResult).status === "error" ); } function isInsufficientPermissionsResult(result: unknown): result is InsufficientPermissionsResult { return ( typeof result === "object" && result !== null && "status" in result && (result as InsufficientPermissionsResult).status === "insufficient_permissions" ); } const FILE_TYPE_LABELS: Record = { google_doc: "Google Doc", google_sheet: "Google Sheet", }; function ApprovalCard({ args, interruptData, onDecision, }: { args: { name: string; file_type: string; content?: string }; interruptData: InterruptResult; onDecision: (decision: { type: "approve" | "reject" | "edit"; message?: string; edited_action?: { name: string; args: Record }; }) => void; }) { const [decided, setDecided] = useState<"approve" | "reject" | "edit" | null>( interruptData.__decided__ ?? null ); const [isPanelOpen, setIsPanelOpen] = useState(false); const openHitlEditPanel = useSetAtom(openHitlEditPanelAtom); const accounts = interruptData.context?.accounts ?? []; const defaultAccountId = useMemo(() => { if (accounts.length === 1) return String(accounts[0].id); return ""; }, [accounts]); const [selectedAccountId, setSelectedAccountId] = useState(defaultAccountId); const [selectedFileType, setSelectedFileType] = useState(args.file_type ?? "google_doc"); const [parentFolderId, setParentFolderId] = useState(""); const isNameValid = useMemo( () => args.name && typeof args.name === "string" && args.name.trim().length > 0, [args.name] ); const canApprove = !!selectedAccountId && isNameValid; const reviewConfig = interruptData.review_configs[0]; const allowedDecisions = reviewConfig?.allowed_decisions ?? ["approve", "reject"]; const canEdit = allowedDecisions.includes("edit"); 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 */}

{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 *

)}

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.

)}
)} {/* Content preview */}
{args.name != null && (

{args.name}

)} {args.file_type && (

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

)} {args.content != null && (
)}
{/* Action buttons - only shown when pending */} {!decided && ( <>
{allowedDecisions.includes("approve") && ( )} {allowedDecisions.includes("reject") && ( )}
)}
); } function InsufficientPermissionsCard({ result }: { result: InsufficientPermissionsResult }) { const params = useParams(); const searchSpaceId = params.search_space_id as string; const [loading, setLoading] = useState(false); async function handleReauth() { setLoading(true); try { const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; const url = new URL(`${backendUrl}/api/v1/auth/google/drive/connector/reauth`); url.searchParams.set("connector_id", String(result.connector_id)); url.searchParams.set("space_id", searchSpaceId); url.searchParams.set("return_url", window.location.pathname); const response = await authenticatedFetch(url.toString()); if (!response.ok) { const data = await response.json().catch(() => ({})); toast.error(data.detail ?? "Failed to initiate re-authentication. Please try again."); return; } const data = await response.json(); if (data.auth_url) { window.location.href = data.auth_url; } } catch { toast.error("Failed to initiate re-authentication. Please try again."); } finally { setLoading(false); } } return (

Additional permissions required

{result.message}

); } function ErrorCard({ result }: { result: ErrorResult }) { return (

Failed to create Google Drive file

{result.message}

); } function SuccessCard({ result }: { result: SuccessResult }) { return (

{result.message || "Google Drive file created successfully"}

{result.name}
{result.web_view_link && ( )}
); } export const CreateGoogleDriveFileToolUI = makeAssistantToolUI< { name: string; file_type: string; content?: string }, CreateGoogleDriveFileResult >({ toolName: "create_google_drive_file", render: function CreateGoogleDriveFileUI({ args, result, status }) { if (status.type === "running") { return (

Preparing Google Drive file...

); } if (!result) return null; if (isInterruptResult(result)) { return ( { window.dispatchEvent( new CustomEvent("hitl-decision", { detail: { decisions: [decision] } }) ); }} /> ); } if ( typeof result === "object" && result !== null && "status" in result && (result as { status: string }).status === "rejected" ) { return null; } if (isInsufficientPermissionsResult(result)) return ; if (isErrorResult(result)) return ; return ; }, });