'use client'; import { useState, useEffect, useRef, useCallback, DragEvent, ChangeEvent } from 'react'; import { useApiKey } from '@/components/shared/ApiKeyWidget/apikey-context'; import { Section } from '@/components/shared/Section'; import { CodeExamplesWidget } from '@/components/demo/CodeExamplesWidget'; import { SelectedFileCodePreview } from '@/components/demo/SelectedFileCodePreview'; import { ImageMetadataBar } from '@/components/shared/ImageMetadataBar'; import { ImageCard } from '@/components/shared/ImageCard'; import { calculateAspectRatio } from '@/utils/imageUtils'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; const UPLOAD_HISTORY_KEY = 'banatie_upload_history'; const ALLOWED_FILE_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/webp']; const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB interface UploadResponse { success: boolean; message: string; data?: { filename: string; originalName: string; path: string; url: string; size: number; contentType: string; uploadedAt: string; }; error?: string; } interface UploadHistoryItem { id: string; timestamp: Date; filename: string; originalName: string; url: string; size: number; contentType: string; durationMs: number; width?: number; height?: number; aspectRatio?: string; downloadMs?: number; } export default function DemoUploadPage() { // API Key from context const { apiKey, apiKeyValidated, focus } = useApiKey(); // Upload State const [selectedFile, setSelectedFile] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(''); const [validationError, setValidationError] = useState(''); const [dragActive, setDragActive] = useState(false); const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number; aspectRatio: string; } | null>(null); // History State const [uploadHistory, setUploadHistory] = useState([]); // Copy Feedback State const [codeCopied, setCodeCopied] = useState(false); // Refs const fileInputRef = useRef(null); // Load upload history from sessionStorage useEffect(() => { const storedHistory = sessionStorage.getItem(UPLOAD_HISTORY_KEY); if (storedHistory) { try { const parsed = JSON.parse(storedHistory); setUploadHistory( parsed.map((item: UploadHistoryItem) => ({ ...item, timestamp: new Date(item.timestamp), })), ); } catch (error) { console.error('Failed to parse upload history:', error); } } }, []); // Save upload history to sessionStorage useEffect(() => { if (uploadHistory.length > 0) { sessionStorage.setItem(UPLOAD_HISTORY_KEY, JSON.stringify(uploadHistory)); } }, [uploadHistory]); const validateFile = (file: File): string | null => { if (!ALLOWED_FILE_TYPES.includes(file.type)) { return `Invalid file type. Allowed: PNG, JPEG, JPG, WebP`; } if (file.size > MAX_FILE_SIZE) { return `File too large. Maximum size: ${MAX_FILE_SIZE / (1024 * 1024)}MB`; } return null; }; const handleFileSelect = (file: File) => { setValidationError(''); setUploadError(''); const error = validateFile(file); if (error) { setValidationError(error); setSelectedFile(null); setPreviewUrl(null); setImageDimensions(null); return; } setSelectedFile(file); const reader = new FileReader(); reader.onloadend = () => { const dataUrl = reader.result as string; setPreviewUrl(dataUrl); // Extract image dimensions const img = new Image(); img.onload = () => { const aspectRatio = calculateAspectRatio(img.width, img.height); setImageDimensions({ width: img.width, height: img.height, aspectRatio, }); }; img.src = dataUrl; }; reader.readAsDataURL(file); }; const handleFileInputChange = (e: ChangeEvent) => { const file = e.target.files?.[0]; if (file) { handleFileSelect(file); } }; const handleDragEnter = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(true); }; const handleDragLeave = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); }; const handleDragOver = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; const handleDrop = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); const file = e.dataTransfer.files?.[0]; if (file) { handleFileSelect(file); } }; const handleUpload = async () => { if (!selectedFile) return; setUploading(true); setUploadError(''); const startTime = Date.now(); try { const formData = new FormData(); formData.append('file', selectedFile); const response = await fetch(`${API_BASE_URL}/api/upload`, { method: 'POST', headers: { 'X-API-Key': apiKey, }, body: formData, }); const result: UploadResponse = await response.json(); if (result.success && result.data) { const endTime = Date.now(); const durationMs = endTime - startTime; const historyItem: UploadHistoryItem = { id: Date.now().toString(), timestamp: new Date(), filename: result.data.filename, originalName: result.data.originalName, url: result.data.url, size: result.data.size, contentType: result.data.contentType, durationMs, width: imageDimensions?.width, height: imageDimensions?.height, aspectRatio: imageDimensions?.aspectRatio, }; setUploadHistory((prev) => [historyItem, ...prev]); setSelectedFile(null); setPreviewUrl(null); setImageDimensions(null); if (fileInputRef.current) { fileInputRef.current.value = ''; } } else { setUploadError(result.error || 'Upload failed'); } } catch (error) { setUploadError(error instanceof Error ? error.message : 'Failed to upload file'); } finally { setUploading(false); } }; const handleDownloadMeasured = useCallback((itemId: string, downloadMs: number) => { setUploadHistory((prev) => prev.map((item) => { // Only update if this item doesn't have downloadMs yet (prevent re-measuring) if (item.id === itemId && item.downloadMs === undefined) { return { ...item, downloadMs }; } return item; }) ); }, []); const generateUploadCodeExamples = (item: UploadHistoryItem, key: string, baseUrl: string) => { const fileName = item.originalName; return { curl: `# Navigate to your images folder cd /your/images/folder # Upload the file curl -X POST "${baseUrl}/api/upload" \\ -H "X-API-Key: ${key}" \\ -F "file=@${fileName}"`, fetch: `// Set your images folder path const imagePath = '/your/images/folder'; const fileName = '${fileName}'; // For Node.js with fs module: const fs = require('fs'); const FormData = require('form-data'); const formData = new FormData(); formData.append('file', fs.createReadStream(\`\${imagePath}/\${fileName}\`)); fetch('${baseUrl}/api/upload', { method: 'POST', headers: { 'X-API-Key': '${key}' }, body: formData }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));`, rest: `# From your images folder: /your/images/folder POST ${baseUrl}/api/upload Headers: X-API-Key: ${key} Content-Type: multipart/form-data Body (form-data): file: @${fileName}`, }; }; const handleCopyCode = (code: string) => { navigator.clipboard.writeText(code); setCodeCopied(true); setTimeout(() => setCodeCopied(false), 2000); }; return (

File Upload Workbench

Developer tool for testing file upload API

{/* API Key Required Notice - Only show when not validated */} {!apiKeyValidated && (

API Key Required

Enter your API key to use this workbench

)}

Upload File

{ if (apiKeyValidated && !uploading) { fileInputRef.current?.click(); } }} > {!selectedFile ? (

Drag and drop your image here, or click to browse

PNG, JPEG, JPG, WebP up to 5MB

) : (
{previewUrl && ( Preview )}

{selectedFile.name}

{imageDimensions && ( )}
)}
{validationError && (

{validationError}

)} {selectedFile && apiKeyValidated && !validationError && (
)}
{uploading ? 'Uploading...' : selectedFile ? 'Ready to upload' : 'No file selected'}
{uploadError && (

{uploadError}

)}
{uploadHistory.length > 0 && (

Upload History

{uploadHistory.map((item) => (
{/* Column 1: Image Card */}
handleDownloadMeasured(item.id, downloadMs)} />
{/* Columns 2-3: API Code Examples Widget */}
))}
)}
); }