import React, { useState } from 'react'; import { CheckCircle, AlertTriangle, Upload, ExternalLink, Clock, Shield, File, FileImage, FileCode, FileText, FileJson, FileVideo, FileAudio, FileArchive, FolderOpen, Copy, Check, Lock, Database, Table, } from 'lucide-react'; import { ToolViewProps } from './types'; import { formatTimestamp, getToolTitle, normalizeContentToString, extractToolData } from './utils'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { ScrollArea } from "@/components/ui/scroll-area"; import { LoadingState } from './shared/LoadingState'; import { cn } from '@/lib/utils'; import { toast } from 'sonner'; interface UploadData { file_path: string | null; bucket_name: string | null; custom_filename: string | null; } interface UploadResult { message?: string; storage_path?: string; file_size?: string; secure_url?: string; expires_at?: string; success?: boolean; } function extractUploadData(assistantContent: any, toolContent: any): { uploadData: UploadData; uploadResult: UploadResult | null; rawContent: string | null; } { let uploadData: UploadData = { file_path: null, bucket_name: null, custom_filename: null, }; let uploadResult: UploadResult | null = null; let rawContent: string | null = null; // Extract parameters from assistant content const assistantStr = normalizeContentToString(assistantContent); if (assistantStr) { try { const parsed = JSON.parse(assistantStr); if (parsed.parameters) { uploadData.file_path = parsed.parameters.file_path || null; uploadData.bucket_name = parsed.parameters.bucket_name || 'file-uploads'; uploadData.custom_filename = parsed.parameters.custom_filename || null; } } catch (e) { // Try regex extraction as fallback const filePathMatch = assistantStr.match(/file_path["']\s*:\s*["']([^"']+)["']/); const bucketMatch = assistantStr.match(/bucket_name["']\s*:\s*["']([^"']+)["']/); const filenameMatch = assistantStr.match(/custom_filename["']\s*:\s*["']([^"']+)["']/); if (filePathMatch) uploadData.file_path = filePathMatch[1]; if (bucketMatch) uploadData.bucket_name = bucketMatch[1]; if (filenameMatch) uploadData.custom_filename = filenameMatch[1]; } } // Extract result from tool content const toolStr = normalizeContentToString(toolContent); if (toolStr) { rawContent = toolStr; try { const parsed = JSON.parse(toolStr); // Handle nested tool_execution structure let resultData = null; if (parsed.tool_execution && parsed.tool_execution.result) { resultData = parsed.tool_execution.result; } else if (parsed.output) { resultData = parsed; } if (resultData) { // Parse the output message to extract structured data const output = resultData.output || ''; uploadResult = { message: output, success: resultData.success !== undefined ? resultData.success : true, }; // Extract structured data from the output message if (typeof output === 'string') { const storageMatch = output.match(/📁 Storage: ([^\n]+)/); const sizeMatch = output.match(/📏 Size: ([^\n]+)/); const urlMatch = output.match(/🔗 Secure Access URL: ([^\n]+)/); const expiresMatch = output.match(/⏰ URL expires: ([^\n]+)/); if (storageMatch) uploadResult.storage_path = storageMatch[1]; if (sizeMatch) uploadResult.file_size = sizeMatch[1]; if (urlMatch) uploadResult.secure_url = urlMatch[1]; if (expiresMatch) uploadResult.expires_at = expiresMatch[1]; } } } catch (e) { // If parsing fails, treat as plain text result uploadResult = { message: toolStr, success: !toolStr.toLowerCase().includes('error') && !toolStr.toLowerCase().includes('failed'), }; } } return { uploadData, uploadResult, rawContent }; } export function UploadFileToolView({ assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false, name = 'upload-file', }: ToolViewProps) { const [isCopyingUrl, setIsCopyingUrl] = useState(false); const [isCopyingPath, setIsCopyingPath] = useState(false); const { uploadData, uploadResult, rawContent } = extractUploadData(assistantContent, toolContent); const toolTitle = getToolTitle(name); const actualIsSuccess = uploadResult?.success !== undefined ? uploadResult.success : isSuccess; const copyToClipboard = async (text: string, type: 'url' | 'path') => { try { await navigator.clipboard.writeText(text); if (type === 'url') { setIsCopyingUrl(true); setTimeout(() => setIsCopyingUrl(false), 2000); } else { setIsCopyingPath(true); setTimeout(() => setIsCopyingPath(false), 2000); } toast.success(`${type === 'url' ? 'Secure URL' : 'Storage path'} copied to clipboard!`); } catch (err) { toast.error('Failed to copy to clipboard'); } }; const getFileName = (filePath: string | null) => { if (!filePath) return 'Unknown file'; return filePath.split('/').pop() || filePath; }; const getFileExtension = (filename: string) => { const ext = filename.split('.').pop()?.toLowerCase() || ''; return ext; }; const getFileIcon = (filename: string) => { const ext = getFileExtension(filename); if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext)) return FileImage; if (['js', 'ts', 'jsx', 'tsx', 'py', 'html', 'css', 'json'].includes(ext)) return FileCode; if (['txt', 'md', 'doc', 'docx', 'pdf'].includes(ext)) return FileText; if (['csv', 'xlsx', 'xls'].includes(ext)) return Table; if (['mp4', 'mov', 'avi', 'webm'].includes(ext)) return FileVideo; if (['mp3', 'wav', 'ogg', 'flac'].includes(ext)) return FileAudio; if (['zip', 'rar', 'tar', 'gz'].includes(ext)) return FileArchive; if (ext === 'json') return FileJson; return File; }; const formatFileSize = (sizeStr: string | undefined) => { if (!sizeStr) return 'Unknown size'; return sizeStr; }; const formatExpiryTime = (expiryStr: string | undefined) => { if (!expiryStr) return 'Unknown expiry'; try { const date = new Date(expiryStr); const now = new Date(); const diffHours = Math.round((date.getTime() - now.getTime()) / (1000 * 60 * 60)); return `${expiryStr} (${diffHours}h remaining)`; } catch { return expiryStr; } }; const fileName = getFileName(uploadData.file_path); const FileIcon = getFileIcon(fileName); return (
{toolTitle}
{!isStreaming && ( {actualIsSuccess ? ( ) : ( )} {actualIsSuccess ? 'Upload successful' : 'Upload failed'} )}
{isStreaming ? ( ) : (
{actualIsSuccess && uploadResult ? (
{uploadData.bucket_name || 'file-uploads'}
{formatFileSize(uploadResult.file_size)}
{uploadResult.secure_url && (
Secure Access URL Private
{uploadResult.secure_url}
🔐 Private
)}
) : (

Upload Failed

{uploadResult?.message || 'The file upload encountered an error.'}

{rawContent && (
Error Details
                          {rawContent}
                        
)}
)}
)}
); }