'use client' import { useState } from 'react' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { FileText, Video, File, Download, ExternalLink, Play, FileImage, Loader2, AlertCircle, X, History, PackageOpen, } from 'lucide-react' import { cn } from '@/lib/utils' import { toast } from 'sonner' interface ProjectFile { id: string fileType: 'EXEC_SUMMARY' | 'PRESENTATION' | 'VIDEO' | 'OTHER' | 'BUSINESS_PLAN' | 'VIDEO_PITCH' | 'SUPPORTING_DOC' fileName: string mimeType: string size: number bucket: string objectKey: string version?: number } interface FileViewerProps { files: ProjectFile[] projectId?: string className?: string } function 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 parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } function getFileIcon(fileType: string, mimeType: string) { if (mimeType.startsWith('video/')) return Video if (mimeType.startsWith('image/')) return FileImage if (mimeType === 'application/pdf') return FileText if (fileType === 'EXEC_SUMMARY' || fileType === 'PRESENTATION') return FileText if (fileType === 'VIDEO') return Video return File } function getFileTypeLabel(fileType: string) { switch (fileType) { case 'EXEC_SUMMARY': return 'Executive Summary' case 'PRESENTATION': return 'Presentation' case 'VIDEO': return 'Video' case 'BUSINESS_PLAN': return 'Business Plan' case 'VIDEO_PITCH': return 'Video Pitch' case 'SUPPORTING_DOC': return 'Supporting Document' default: return 'Attachment' } } export function FileViewer({ files, projectId, className }: FileViewerProps) { if (files.length === 0) { return (

No files attached

This project has no files uploaded yet

) } // Sort files by type order const sortOrder = ['EXEC_SUMMARY', 'BUSINESS_PLAN', 'PRESENTATION', 'VIDEO', 'VIDEO_PITCH', 'SUPPORTING_DOC', 'OTHER'] const sortedFiles = [...files].sort( (a, b) => sortOrder.indexOf(a.fileType) - sortOrder.indexOf(b.fileType) ) return ( Project Files {projectId && files.length > 1 && ( f.id)} /> )} {sortedFiles.map((file) => ( ))} ) } function FileItem({ file }: { file: ProjectFile }) { const [showPreview, setShowPreview] = useState(false) const Icon = getFileIcon(file.fileType, file.mimeType) const { data: urlData, isLoading: isLoadingUrl } = trpc.file.getDownloadUrl.useQuery( { bucket: file.bucket, objectKey: file.objectKey }, { enabled: showPreview } ) const canPreview = file.mimeType.startsWith('video/') || file.mimeType === 'application/pdf' || file.mimeType.startsWith('image/') return (

{file.fileName}

{file.version != null && file.version > 1 && ( v{file.version} )}
{getFileTypeLabel(file.fileType)} {formatFileSize(file.size)}
{file.version != null && file.version > 1 && ( )} {canPreview && ( )}
{/* Preview area */} {showPreview && (
{isLoadingUrl ? (
) : urlData?.url ? ( ) : (
Failed to load preview
)}
)}
) } function VersionHistoryButton({ fileId }: { fileId: string }) { const [open, setOpen] = useState(false) const { data: versions, isLoading } = trpc.file.getVersionHistory.useQuery( { fileId }, { enabled: open } ) return ( Version History
{isLoading ? (
{[1, 2, 3].map((i) => ( ))}
) : versions && (versions as Array>).length > 0 ? ( (versions as Array>).map((v) => (

{String(v.fileName)}

v{String(v.version)}
{formatFileSize(Number(v.size))} {v.createdAt ? new Date(String(v.createdAt)).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }) : ''}
)) ) : (

No version history available

)}
) } function VersionDownloadButton({ bucket, objectKey }: { bucket: string; objectKey: string }) { const [downloading, setDownloading] = useState(false) const { refetch } = trpc.file.getDownloadUrl.useQuery( { bucket, objectKey }, { enabled: false } ) const handleDownload = async () => { setDownloading(true) try { const result = await refetch() if (result.data?.url) { window.open(result.data.url, '_blank') } } catch { toast.error('Failed to get download URL') } finally { setDownloading(false) } } return ( ) } function BulkDownloadButton({ projectId, fileIds }: { projectId: string; fileIds: string[] }) { const [downloading, setDownloading] = useState(false) const { refetch } = trpc.file.getBulkDownloadUrls.useQuery( { projectId, fileIds }, { enabled: false } ) const handleBulkDownload = async () => { setDownloading(true) try { const result = await refetch() if (result.data && Array.isArray(result.data)) { // Open each download URL with a small delay to avoid popup blocking for (let i = 0; i < result.data.length; i++) { const item = result.data[i] as { downloadUrl: string } if (item.downloadUrl) { // Use link element to trigger download without popup const link = document.createElement('a') link.href = item.downloadUrl link.target = '_blank' link.rel = 'noopener noreferrer' document.body.appendChild(link) link.click() document.body.removeChild(link) // Small delay between downloads if (i < result.data.length - 1) { await new Promise((resolve) => setTimeout(resolve, 300)) } } } toast.success(`Downloading ${result.data.length} files`) } } catch { toast.error('Failed to download files') } finally { setDownloading(false) } } return ( ) } function FileDownloadButton({ file }: { file: ProjectFile }) { const [downloading, setDownloading] = useState(false) const { refetch } = trpc.file.getDownloadUrl.useQuery( { bucket: file.bucket, objectKey: file.objectKey }, { enabled: false } ) const handleDownload = async () => { setDownloading(true) try { const result = await refetch() if (result.data?.url) { // Open in new tab for download window.open(result.data.url, '_blank') } } catch (error) { console.error('Failed to get download URL:', error) } finally { setDownloading(false) } } return ( ) } function FilePreview({ file, url }: { file: ProjectFile; url: string }) { if (file.mimeType.startsWith('video/')) { return ( ) } if (file.mimeType === 'application/pdf') { return (