'use client' import { useState, useCallback, useEffect } from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Search, Upload, Image as ImageIcon, Video, Music, File, FolderPlus, Loader2, Check, X, Eye } from 'lucide-react' import { useDropzone } from 'react-dropzone' import { toast } from 'sonner' import Image from 'next/image' // Helper function to determine content type from URL const getContentTypeFromUrl = (url: string): string | null => { // Try to extract extension from the pathname, looking for common image extensions // even if they're not at the very end (due to timestamps or other suffixes) const extensionMatch = url.match(/\.(jpg|jpeg|png|gif|webp|svg|mp4|webm|mp3|wav|pdf|doc|docx|txt)(?:-\d+)?(?:\?.*)?$/i) const extension = extensionMatch ? extensionMatch[1].toLowerCase() : null if (!extension) { // Fallback: check if the URL contains any image extension const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'] for (const ext of imageExtensions) { if (url.toLowerCase().includes(`.${ext}`)) { return getExtensionMimeType(ext) } } return null } return getExtensionMimeType(extension) } // Helper function to map extensions to MIME types const getExtensionMimeType = (extension: string): string | null => { const extensionMap: Record = { 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png', 'gif': 'image/gif', 'webp': 'image/webp', 'svg': 'image/svg+xml', 'mp4': 'video/mp4', 'webm': 'video/webm', 'mp3': 'audio/mpeg', 'wav': 'audio/wav', 'pdf': 'application/pdf', 'doc': 'application/msword', 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'txt': 'text/plain' } return extensionMap[extension] || null } export interface MediaFile { id: string name: string type: 'file' | 'folder' size?: number mimeType?: string url?: string path: string createdAt: string uploadedBy: { id: string name: string } } interface MediaSelectorProps { onSelect: (file: MediaFile) => void selectedUrl?: string allowedTypes?: string[] // e.g., ['image/*', 'video/*'] children: React.ReactNode } export default function MediaSelector({ onSelect, selectedUrl, allowedTypes = ['image/*', 'video/*', 'audio/*'], children }: MediaSelectorProps) { console.log('MediaSelector component rendered') const [isOpen, setIsOpen] = useState(false) const [files, setFiles] = useState([]) const [currentPath, setCurrentPath] = useState('/') const [searchTerm, setSearchTerm] = useState('') const [loading, setLoading] = useState(false) const [uploading, setUploading] = useState(false) const [selectedFile, setSelectedFile] = useState(null) const [previewUrl, setPreviewUrl] = useState(null) const fetchFiles = useCallback(async () => { console.log('fetchFiles called, isOpen:', isOpen, 'currentPath:', currentPath) if (!isOpen) { console.log('Dialog not open, skipping fetch') return } setLoading(true) try { // Use our blob storage API to list files const searchParams = new URLSearchParams() // Use 'media' prefix for all requests for consistency with upload folder structure const prefix = 'media' console.log('Fetching files with prefix:', prefix) searchParams.set('prefix', prefix) searchParams.set('limit', '100') const apiUrl = `/api/upload/files?${searchParams.toString()}` console.log('Making API request to:', apiUrl) const response = await fetch(apiUrl) console.log('API response status:', response.status) if (response.ok) { const data = await response.json() console.log('MediaSelector API response:', data) // Debug log if (data.success) { // Transform blob storage response to MediaFile format const blobs = data.data?.blobs || [] console.log('Blobs found:', blobs) // Debug log const transformedFiles: MediaFile[] = blobs.map((blob: any) => { const mimeType = getContentTypeFromUrl(blob.url) || 'application/octet-stream' console.log('Transforming blob:', { url: blob.url, pathname: blob.pathname, detectedMimeType: mimeType }) return { id: blob.pathname, name: blob.pathname.split('/').pop() || blob.pathname, type: 'file' as const, size: blob.size, mimeType, url: blob.url, path: blob.pathname, createdAt: blob.uploadedAt, uploadedBy: { id: 'system', name: 'System' } } }) console.log('Transformed files:', transformedFiles) console.log('Allowed types:', allowedTypes) // Filter files based on allowed types const filteredFiles = transformedFiles.filter((file: MediaFile) => { if (!file.mimeType) { console.log('File rejected - no mimeType:', file.name) return false } const isAllowed = allowedTypes.some(type => { if (type.endsWith('/*')) { const matches = file.mimeType!.startsWith(type.replace('/*', '/')) console.log(`Checking ${file.mimeType} against ${type}: ${matches}`) return matches } const matches = file.mimeType === type console.log(`Checking ${file.mimeType} against ${type}: ${matches}`) return matches }) console.log(`File ${file.name} allowed: ${isAllowed}`) return isAllowed }) console.log('Filtered files:', filteredFiles) setFiles(filteredFiles) } else { console.error('API request failed:', data) setFiles([]) } } else { console.error('Failed to fetch files, status:', response.status) setFiles([]) } } catch (error) { console.error('Error fetching files:', error) toast.error('Failed to load files') } finally { setLoading(false) } }, [currentPath, isOpen, allowedTypes]) useEffect(() => { console.log('MediaSelector mounted, isOpen:', isOpen) fetchFiles() }, [fetchFiles, isOpen]) const onDrop = useCallback(async (acceptedFiles: File[]) => { setUploading(true) const formData = new FormData() acceptedFiles.forEach((file) => { formData.append('files', file) }) // Set upload type and folder based on current path formData.append('type', 'general') if (currentPath !== '/') { formData.append('folder', currentPath.substring(1)) // Remove leading slash } else { formData.append('folder', 'media') } try { const response = await fetch('/api/upload/files', { method: 'POST', body: formData }) if (response.ok) { const data = await response.json() if (data.success) { toast.success(`Uploaded ${data.data.length} file(s) successfully`) fetchFiles() } else { console.error('Upload failed with data:', data) throw new Error(data.message || 'Upload failed') } } else { const errorData = await response.text() console.error('Upload failed with status:', response.status, 'Response:', errorData) throw new Error(`Upload failed with status ${response.status}: ${errorData}`) } } catch (error) { console.error('Upload error:', error) toast.error(`Failed to upload files: ${error instanceof Error ? error.message : 'Unknown error'}`) } finally { setUploading(false) } }, [currentPath, fetchFiles]) const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, multiple: true, accept: allowedTypes.reduce((acc, type) => { acc[type] = [] return acc }, {} as Record) }) const handleFileSelect = (file: MediaFile) => { if (file.type === 'folder') { setCurrentPath(currentPath === '/' ? `/${file.name}` : `${currentPath}/${file.name}`) setSelectedFile(null) setPreviewUrl(null) } else { setSelectedFile(file) setPreviewUrl(file.url || null) } } const handleConfirmSelection = () => { if (selectedFile) { onSelect(selectedFile) setIsOpen(false) setSelectedFile(null) setPreviewUrl(null) } } const navigateUp = () => { const pathParts = currentPath.split('/').filter(p => p) if (pathParts.length > 0) { pathParts.pop() setCurrentPath(pathParts.length === 0 ? '/' : '/' + pathParts.join('/')) } } const getFileIcon = (file: MediaFile) => { if (file.type === 'folder') return if (!file.mimeType) return if (file.mimeType.startsWith('image/')) return if (file.mimeType.startsWith('video/')) return