Files
MOPC-Portal/src/components/mentor/file-promotion-panel.tsx

192 lines
7.0 KiB
TypeScript
Raw Normal View History

'use client'
import { useState } from 'react'
import { trpc } from '@/lib/trpc/client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Skeleton } from '@/components/ui/skeleton'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { FileText, Upload, CheckCircle2, ArrowUp } from 'lucide-react'
import { toast } from 'sonner'
interface FilePromotionPanelProps {
mentorAssignmentId: string
}
function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
export function FilePromotionPanel({ mentorAssignmentId }: FilePromotionPanelProps) {
const [selectedSlot, setSelectedSlot] = useState<string>('')
const utils = trpc.useUtils()
// Mock workspace files - in real implementation, would fetch from workspaceGetFiles
const workspaceFiles: any[] = [] // Placeholder
const promoteMutation = trpc.mentor.workspacePromoteFile.useMutation({
onSuccess: () => {
toast.success('File promoted successfully')
setSelectedSlot('')
},
onError: (err) => toast.error(err.message),
})
const handlePromote = (mentorFileId: string) => {
if (!selectedSlot) {
toast.error('Please select a file requirement slot')
return
}
promoteMutation.mutate({
roundId: '', // Would need to get this from context
mentorFileId,
slotKey: selectedSlot,
})
}
const isLoading = false // Placeholder
if (isLoading) {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-48" />
</CardHeader>
<CardContent>
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<Skeleton key={i} className="h-20" />
))}
</div>
</CardContent>
</Card>
)
}
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Upload className="h-5 w-5" />
File Promotion
</CardTitle>
<CardDescription>
Promote workspace files to official submission windows
</CardDescription>
</CardHeader>
<CardContent>
{/* Slot selector */}
<div className="mb-6">
<label className="text-sm font-medium mb-2 block">
Target File Requirement
</label>
<Select value={selectedSlot} onValueChange={setSelectedSlot}>
<SelectTrigger>
<SelectValue placeholder="Select a file requirement..." />
</SelectTrigger>
<SelectContent>
{/* Mock slots - in real implementation, would fetch available requirements */}
<SelectItem value="pitch_deck">Pitch Deck</SelectItem>
<SelectItem value="business_plan">Business Plan</SelectItem>
<SelectItem value="presentation">Presentation</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground mt-1">
Select which file requirement to promote files to
</p>
</div>
{/* Workspace files list */}
<div className="space-y-3">
{workspaceFiles.length === 0 ? (
<div className="text-center py-12 border border-dashed rounded-lg">
<FileText className="h-12 w-12 text-muted-foreground/50 mx-auto mb-3" />
<p className="text-sm text-muted-foreground">No workspace files available</p>
<p className="text-xs text-muted-foreground mt-1">
Files shared in the workspace will appear here
</p>
</div>
) : (
workspaceFiles.map((file: any) => {
const isPromoted = file.promotedToWindow
return (
<Card key={file.id}>
<CardContent className="p-4">
<div className="flex items-start gap-4">
<div className="rounded-lg bg-brand-blue/10 p-3 shrink-0">
<FileText className="h-5 w-5 text-brand-blue" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2 mb-2">
<div className="flex-1 min-w-0">
<p className="font-medium text-sm truncate" title={file.filename}>
{file.filename}
</p>
<div className="flex items-center gap-2 mt-1">
<Badge variant="outline" className="text-xs">
{file.mimeType?.split('/')[1]?.toUpperCase() || 'FILE'}
</Badge>
{file.size && (
<span className="text-xs text-muted-foreground">
{formatFileSize(file.size)}
</span>
)}
</div>
</div>
{isPromoted ? (
<Badge variant="default" className="shrink-0 bg-emerald-50 text-emerald-700 border-emerald-200">
<CheckCircle2 className="mr-1 h-3 w-3" />
Promoted
</Badge>
) : (
<Button
size="sm"
onClick={() => handlePromote(file.id)}
disabled={!selectedSlot || promoteMutation.isPending}
className="shrink-0"
>
<ArrowUp className="mr-1 h-3 w-3" />
Promote
</Button>
)}
</div>
{isPromoted && file.promotedToWindow && (
<p className="text-xs text-muted-foreground">
Promoted to: {file.promotedToWindow.name}
</p>
)}
</div>
</div>
</CardContent>
</Card>
)
})
)}
</div>
{workspaceFiles.length > 0 && (
<div className="mt-4 p-3 bg-muted/50 rounded-lg">
<p className="text-sm text-muted-foreground">
<strong>Note:</strong> Promoting a file will make it visible to jurors in the selected
submission window. This action can help teams submit refined versions of their work.
</p>
</div>
)}
</CardContent>
</Card>
)
}