'use client' import { useState } from 'react' import { useSession } from 'next-auth/react' import { trpc } from '@/lib/trpc/client' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import { RequirementUploadList } from '@/components/shared/requirement-upload-slot' import { FilePreview, isOfficeFile } from '@/components/shared/file-viewer' import { FileText, Upload, AlertTriangle, Clock, Video, File, Download, Eye, X, Loader2, } from 'lucide-react' import { toast } from 'sonner' const fileTypeIcons: Record = { EXEC_SUMMARY: FileText, BUSINESS_PLAN: FileText, PRESENTATION: FileText, VIDEO_PITCH: Video, VIDEO: Video, OTHER: File, SUPPORTING_DOC: File, } const fileTypeLabels: Record = { EXEC_SUMMARY: 'Executive Summary', BUSINESS_PLAN: 'Business Plan', PRESENTATION: 'Presentation', VIDEO_PITCH: 'Video Pitch', VIDEO: 'Video', OTHER: 'Other Document', SUPPORTING_DOC: 'Supporting Document', } function FileRow({ file }: { file: { id: string; fileName: string; fileType: string; createdAt: string | Date; isLate?: boolean; bucket?: string; objectKey?: string; mimeType?: string } }) { const [showPreview, setShowPreview] = useState(false) const Icon = fileTypeIcons[file.fileType] || File const mimeType = file.mimeType || '' const canPreview = mimeType.startsWith('video/') || mimeType === 'application/pdf' || mimeType.startsWith('image/') || isOfficeFile(mimeType, file.fileName) const { data: previewData, isLoading: isLoadingPreview } = trpc.file.getDownloadUrl.useQuery( { bucket: file.bucket!, objectKey: file.objectKey!, purpose: 'preview' as const }, { enabled: showPreview && !!file.bucket && !!file.objectKey, staleTime: 10 * 60 * 1000 } ) return (

{file.fileName}

{file.isLate && ( Late )}

{fileTypeLabels[file.fileType] || file.fileType} {' - '} {new Date(file.createdAt).toLocaleDateString()}

{file.bucket && file.objectKey && (
{canPreview && ( )}
)}
{showPreview && (
{isLoadingPreview ? (
) : previewData?.url ? ( ) : (
Failed to load preview
)}
)}
) } function DownloadButton({ bucket, objectKey, fileName }: { bucket: string; objectKey: string; fileName: string }) { const [downloading, setDownloading] = useState(false) const { refetch } = trpc.file.getDownloadUrl.useQuery( { bucket, objectKey, forDownload: true, fileName, purpose: 'download' as const }, { enabled: false } ) const handleDownload = async () => { setDownloading(true) try { const result = await refetch() if (result.data?.url) { window.location.href = result.data.url } } catch { toast.error('Failed to download file') } finally { setTimeout(() => setDownloading(false), 1000) } } return ( ) } export default function ApplicantDocumentsPage() { const { status: sessionStatus } = useSession() const isAuthenticated = sessionStatus === 'authenticated' const { data, isLoading } = trpc.applicant.getMyDashboard.useQuery(undefined, { enabled: isAuthenticated, }) if (isLoading) { return (
) } if (!data?.project) { return (

Documents

No Project

Submit a project first to upload documents.

) } const { project, openRounds, isRejected } = data return (
{/* Header */}

Documents

Upload and manage documents for your project: {project.title}

{/* Rejected banner */} {isRejected && (

Your project was not selected to advance. Documents are view-only.

)} {/* Per-round upload sections */} {!isRejected && openRounds.length > 0 && (
{openRounds.map((round: { id: string; name: string; windowCloseAt?: string | Date | null }) => { const now = new Date() const hasDeadline = !!round.windowCloseAt const deadlinePassed = hasDeadline && now > new Date(round.windowCloseAt!) const isLate = deadlinePassed return (
{round.name} Upload documents for this round
{isLate && ( Late submission )} {hasDeadline && !deadlinePassed && ( Due {new Date(round.windowCloseAt!).toLocaleDateString()} )}
) })}
)} {/* Uploaded files list */} All Uploaded Documents All files associated with your project {project.files.length === 0 ? (

No documents uploaded yet

) : (
{project.files.map((file) => { const fileRecord = file as typeof file & { isLate?: boolean; bucket?: string; objectKey?: string; mimeType?: string } return ( ) })}
)}
{/* No open rounds message */} {openRounds.length === 0 && project.files.length === 0 && (

No rounds are currently open for document submissions.

)}
) }