"use client" import type React from "react" import { useState, useRef, useCallback } from "react" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Alert, AlertDescription } from "@/components/ui/alert" import { Progress } from "@/components/ui/progress" import { Upload, X, AlertCircle, Eye, RotateCcw, FileImage, Cloud, Loader2 } from "lucide-react" import { SupabaseImageService, ImageCompressionService, type SupabaseImageFile, type BatchUploadResult, } from "@/lib/supabase-image-utils" import { soundManager } from "@/lib/sound-effects" interface SupabaseImageUploadProps { images: SupabaseImageFile[] onImagesChange: (images: SupabaseImageFile[]) => void disabled?: boolean className?: string maxFiles?: number } export default function SupabaseImageUpload({ images, onImagesChange, disabled = false, className = "", maxFiles = 10, }: SupabaseImageUploadProps) { const [dragActive, setDragActive] = useState(false) const [uploading, setUploading] = useState(false) const [uploadProgress, setUploadProgress] = useState(0) const [errors, setErrors] = useState([]) const [uploadStats, setUploadStats] = useState<{ total: 0; completed: 0; failed: 0 }>({ total: 0, completed: 0, failed: 0, }) const fileInputRef = useRef(null) const handleFiles = useCallback( async (files: FileList) => { if (disabled) return setUploading(true) setErrors([]) setUploadProgress(0) const fileArray = Array.from(files) const remainingSlots = maxFiles - images.length if (fileArray.length > remainingSlots) { setErrors([`最多只能再上傳 ${remainingSlots} 張圖片`]) setUploading(false) return } try { // 初始化統計 setUploadStats({ total: fileArray.length, completed: 0, failed: 0 }) // 先壓縮圖片 setUploadProgress(10) const compressedFiles = await ImageCompressionService.compressImages(fileArray) setUploadProgress(20) // 批量上傳到 Supabase Storage const uploadResult: BatchUploadResult = await SupabaseImageService.uploadImages(compressedFiles) // 更新統計 setUploadStats({ total: uploadResult.total, completed: uploadResult.successful.length, failed: uploadResult.failed.length, }) // 處理上傳結果 if (uploadResult.successful.length > 0) { onImagesChange([...images, ...uploadResult.successful]) await soundManager.play("success") } // 處理錯誤 if (uploadResult.failed.length > 0) { const errorMessages = uploadResult.failed.map((failure) => `${failure.file.name}: ${failure.error}`) setErrors(errorMessages) } setUploadProgress(100) } catch (error) { console.error("Upload process error:", error) setErrors([`上傳過程中發生錯誤: ${error}`]) } finally { setTimeout(() => { setUploading(false) setUploadProgress(0) setUploadStats({ total: 0, completed: 0, failed: 0 }) }, 2000) } }, [images, onImagesChange, disabled, maxFiles], ) const handleDrag = useCallback((e: React.DragEvent) => { e.preventDefault() e.stopPropagation() }, []) const handleDragIn = useCallback((e: React.DragEvent) => { e.preventDefault() e.stopPropagation() if (e.dataTransfer.items && e.dataTransfer.items.length > 0) { setDragActive(true) } }, []) const handleDragOut = useCallback((e: React.DragEvent) => { e.preventDefault() e.stopPropagation() setDragActive(false) }, []) const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault() e.stopPropagation() setDragActive(false) if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { handleFiles(e.dataTransfer.files) } }, [handleFiles], ) const handleFileInput = useCallback( (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { handleFiles(e.target.files) } e.target.value = "" }, [handleFiles], ) const removeImage = useCallback( async (imageId: string) => { const imageToRemove = images.find((img) => img.id === imageId) if (imageToRemove) { // 從 Supabase Storage 刪除圖片 const deleteResult = await SupabaseImageService.deleteImage(imageToRemove.storage_path) if (!deleteResult.success) { console.warn("Failed to delete image from storage:", deleteResult.error) // 即使刪除失敗,也從列表中移除(避免阻塞用戶操作) } onImagesChange(images.filter((img) => img.id !== imageId)) await soundManager.play("click") } }, [images, onImagesChange], ) const clearAllImages = useCallback(async () => { if (images.length === 0) return const storagePaths = images.map((img) => img.storage_path) const deleteResult = await SupabaseImageService.deleteImages(storagePaths) if (!deleteResult.success) { console.warn("Failed to delete some images from storage:", deleteResult.error) } onImagesChange([]) setErrors([]) await soundManager.play("click") }, [images, onImagesChange]) const formatFileSize = (bytes: number): string => { if (bytes === 0) return "0 Bytes" const k = 1024 const sizes = ["Bytes", "KB", "MB", "GB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i] } return (
{/* 上傳區域 */} !disabled && !uploading && fileInputRef.current?.click()} >
{uploading ? ( ) : (
)}

{uploading ? "正在上傳到雲端..." : dragActive ? "放開以上傳圖片" : "上傳相關圖片到雲端"}

{uploading ? "圖片將安全存儲在 Supabase 雲端" : "拖拽圖片到此處或點擊選擇檔案"}

雲端存儲 支援 JPG、PNG、WebP、GIF 單檔最大 5MB 最多 {maxFiles} 張
{/* 上傳進度 */} {uploading && (
{uploadStats.completed}/{uploadStats.total} 完成 {uploadProgress}%
{uploadStats.failed > 0 && (
{uploadStats.failed} 個檔案上傳失敗
)}
)}
{/* 錯誤訊息 */} {errors.length > 0 && (
{errors.map((error, index) => (
• {error}
))}
)} {/* 已上傳的圖片 */} {images.length > 0 && (

雲端圖片 ({images.length}/{maxFiles})

Supabase
{images.length > 1 && ( )}
{images.map((image) => (
{image.name} { const target = e.target as HTMLImageElement target.src = "/placeholder.svg?height=200&width=200&text=圖片載入失敗" }} /> {/* 懸停覆蓋層 */}
{/* 雲端標識 */}
{/* 檔案資訊 */}
{image.name}
{formatFileSize(image.size)} 雲端
))}
)}
) }