'use client' import { useState, useCallback, type RefObject } from 'react' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { FileDown, Loader2 } from 'lucide-react' import { toast } from 'sonner' import { createReportDocument, addCoverPage, addPageBreak, addHeader, addSectionTitle, addStatCards, addTable, addChartImage, addAllPageFooters, savePdf, } from '@/lib/pdf-generator' interface ExportPdfButtonProps { roundId: string roundName?: string programName?: string chartRefs?: Record> variant?: 'default' | 'outline' | 'secondary' | 'ghost' size?: 'default' | 'sm' | 'lg' | 'icon' } export function ExportPdfButton({ roundId, roundName, programName, chartRefs, variant = 'outline', size = 'sm', }: ExportPdfButtonProps) { const [generating, setGenerating] = useState(false) const { refetch } = trpc.export.getReportData.useQuery( { roundId, sections: [] }, { enabled: false } ) const handleGenerate = useCallback(async () => { setGenerating(true) toast.info('Generating PDF report...') try { const result = await refetch() if (!result.data) { toast.error('Failed to fetch report data') return } const data = result.data as Record const rName = roundName || String(data.roundName || 'Report') const pName = programName || String(data.programName || '') // 1. Create document const doc = await createReportDocument() // 2. Cover page await addCoverPage(doc, { title: 'Round Report', subtitle: `${pName} ${data.programYear ? `(${data.programYear})` : ''}`.trim(), roundName: rName, programName: pName, }) // 3. Summary section const summary = data.summary as Record | undefined if (summary) { addPageBreak(doc) await addHeader(doc, rName) let y = addSectionTitle(doc, 'Summary', 28) y = addStatCards(doc, [ { label: 'Projects', value: String(summary.projectCount ?? 0) }, { label: 'Evaluations', value: String(summary.evaluationCount ?? 0) }, { label: 'Avg Score', value: summary.averageScore != null ? Number(summary.averageScore).toFixed(1) : '--', }, { label: 'Completion', value: summary.completionRate != null ? `${Number(summary.completionRate).toFixed(0)}%` : '--', }, ], y) // Capture chart images if refs provided if (chartRefs) { for (const [, ref] of Object.entries(chartRefs)) { if (ref.current) { try { y = await addChartImage(doc, ref.current, y, { maxHeight: 90 }) } catch { // Skip chart if capture fails } } } } } // 4. Rankings section const rankings = data.rankings as Array> | undefined if (rankings && rankings.length > 0) { addPageBreak(doc) await addHeader(doc, rName) let y = addSectionTitle(doc, 'Project Rankings', 28) const headers = ['#', 'Project', 'Team', 'Avg Score', 'Evaluations', 'Yes %'] const rows = rankings.map((r, i) => [ i + 1, String(r.title ?? ''), String(r.teamName ?? ''), r.averageScore != null ? Number(r.averageScore).toFixed(2) : '-', String(r.evaluationCount ?? 0), r.yesPercentage != null ? `${Number(r.yesPercentage).toFixed(0)}%` : '-', ]) y = addTable(doc, headers, rows, y) } // 5. Juror stats section const jurorStats = data.jurorStats as Array> | undefined if (jurorStats && jurorStats.length > 0) { addPageBreak(doc) await addHeader(doc, rName) let y = addSectionTitle(doc, 'Juror Statistics', 28) const headers = ['Juror', 'Assigned', 'Completed', 'Completion %', 'Avg Score'] const rows = jurorStats.map((j) => [ String(j.name ?? ''), String(j.assigned ?? 0), String(j.completed ?? 0), `${Number(j.completionRate ?? 0).toFixed(0)}%`, j.averageScore != null ? Number(j.averageScore).toFixed(2) : '-', ]) y = addTable(doc, headers, rows, y) } // 6. Criteria breakdown const criteriaBreakdown = data.criteriaBreakdown as Array> | undefined if (criteriaBreakdown && criteriaBreakdown.length > 0) { addPageBreak(doc) await addHeader(doc, rName) let y = addSectionTitle(doc, 'Criteria Breakdown', 28) const headers = ['Criterion', 'Avg Score', 'Responses'] const rows = criteriaBreakdown.map((c) => [ String(c.label ?? ''), c.averageScore != null ? Number(c.averageScore).toFixed(2) : '-', String(c.count ?? 0), ]) y = addTable(doc, headers, rows, y) } // 7. Footer on all pages addAllPageFooters(doc) // 8. Save const dateStr = new Date().toISOString().split('T')[0] savePdf(doc, `MOPC-Report-${rName.replace(/\s+/g, '-')}-${dateStr}.pdf`) toast.success('PDF report downloaded successfully') } catch (err) { console.error('PDF generation error:', err) toast.error('Failed to generate PDF report') } finally { setGenerating(false) } }, [refetch, roundName, programName, chartRefs]) return ( ) }