'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 { FileText, Video, File, Download, ExternalLink, Play, FileImage, Loader2, AlertCircle, X, } from 'lucide-react' import { cn } from '@/lib/utils' 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 } interface FileViewerProps { files: ProjectFile[] 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, 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 {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' return (

{file.fileName}

{getFileTypeLabel(file.fileType)} {formatFileSize(file.size)}
{canPreview && ( )}
{/* Preview area */} {showPreview && (
{isLoadingUrl ? (
) : urlData?.url ? ( ) : (
Failed to load preview
)}
)}
) } 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 (