'use client' import { useState, useCallback } from 'react' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { FileDown, Loader2 } from 'lucide-react' import { toast } from 'sonner' interface PdfReportProps { roundId: string sections: string[] } function buildReportHtml(reportData: Record): string { const parts: string[] = [] parts.push(` Round Report - ${String(reportData.roundName || 'Report')} `) parts.push(`
`) parts.push(`

${escapeHtml(String(reportData.roundName || 'Round Report'))}

`) parts.push(`

${escapeHtml(String(reportData.programName || ''))}

`) parts.push(`

Generated on ${new Date().toLocaleString()}

`) const summary = reportData.summary as Record | undefined if (summary) { parts.push(`

Summary

`) parts.push(statCard(summary.totalProjects, 'Projects')) parts.push(statCard(summary.totalEvaluations, 'Evaluations')) parts.push(statCard(summary.averageScore != null ? Number(summary.averageScore).toFixed(1) : '--', 'Avg Score')) parts.push(statCard(summary.completionRate != null ? Number(summary.completionRate).toFixed(0) + '%' : '--', 'Completion')) parts.push(`
`) } const rankings = reportData.rankings as Array> | undefined if (rankings && rankings.length > 0) { parts.push(`

Project Rankings

`) for (const p of rankings) { parts.push(``) } parts.push(`
#ProjectTeamAvg ScoreEvaluations
${escapeHtml(String(p.rank ?? ''))} ${escapeHtml(String(p.title ?? ''))} ${escapeHtml(String(p.team ?? ''))} ${Number(p.avgScore ?? 0).toFixed(2)} ${String(p.evalCount ?? 0)}
`) } const jurorStats = reportData.jurorStats as Array> | undefined if (jurorStats && jurorStats.length > 0) { parts.push(`

Juror Statistics

`) for (const j of jurorStats) { parts.push(``) } parts.push(`
JurorAssignedCompletedCompletion %Avg Score Given
${escapeHtml(String(j.name ?? ''))} ${String(j.assigned ?? 0)} ${String(j.completed ?? 0)} ${Number(j.completionRate ?? 0).toFixed(0)}% ${Number(j.avgScore ?? 0).toFixed(2)}
`) } parts.push(``) return parts.join('') } function escapeHtml(str: string): string { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') } function statCard(value: unknown, label: string): string { return `
${escapeHtml(String(value ?? 0))}
${escapeHtml(label)}
` } export function PdfReportGenerator({ roundId, sections }: PdfReportProps) { const [generating, setGenerating] = useState(false) const { refetch } = trpc.export.getReportData.useQuery( { roundId, sections }, { enabled: false } ) const handleGenerate = useCallback(async () => { setGenerating(true) try { const result = await refetch() if (!result.data) { toast.error('Failed to fetch report data') return } const html = buildReportHtml(result.data as Record) const blob = new Blob([html], { type: 'text/html;charset=utf-8' }) const url = URL.createObjectURL(blob) const newWindow = window.open(url, '_blank') if (!newWindow) { toast.error('Pop-up blocked. Please allow pop-ups and try again.') URL.revokeObjectURL(url) return } // Clean up after a delay setTimeout(() => URL.revokeObjectURL(url), 5000) toast.success('Report generated. Use the Print button or Ctrl+P to save as PDF.') } catch { toast.error('Failed to generate report') } finally { setGenerating(false) } }, [refetch]) return ( ) }