import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import jsPDF from 'jspdf'; import { Award, Calendar, Download, FileImage, FileText } from 'lucide-react'; import { useRef, useState } from 'react'; import { toast } from 'sonner'; interface DynamicCertificateProps { template: CertificateTemplate; courseName: string; studentName: string; completionDate: string; } const DynamicCertificate = ({ template, courseName, studentName, completionDate }: DynamicCertificateProps) => { const [downloadFormat, setDownloadFormat] = useState('png'); const certificateRef = useRef(null); const dimensions = { width: 900, height: 600 }; const { template_data } = template; const handleDownloadCertificate = async () => { if (!certificateRef.current) return; if (downloadFormat === 'pdf') { await downloadAsPDF(); } else { await downloadAsPNG(); } }; const loadImage = (src: string): Promise => { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); }; const downloadAsPNG = async () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return; canvas.width = dimensions.width; canvas.height = dimensions.height; // Load logo first if it exists let logoImage: HTMLImageElement | null = null; if (template.logo_path) { try { logoImage = await loadImage(template.logo_path); } catch (error) { console.error('Failed to load logo:', error); } } await drawCertificate(ctx, dimensions, logoImage); canvas.toBlob((blob) => { if (!blob) return; const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${studentName}_${courseName}_Certificate.png`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success('Certificate saved as PNG!'); }, 'image/png'); }; const downloadAsPDF = async () => { const isLandscape = dimensions.width > dimensions.height; const pdf = new jsPDF({ orientation: isLandscape ? 'landscape' : 'portrait', unit: 'px', format: [dimensions.width, dimensions.height], }); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return; canvas.width = dimensions.width; canvas.height = dimensions.height; // Load logo first if it exists let logoImage: HTMLImageElement | null = null; if (template.logo_path) { try { logoImage = await loadImage(template.logo_path); } catch (error) { console.error('Failed to load logo:', error); } } await drawCertificate(ctx, dimensions, logoImage); const imgData = canvas.toDataURL('image/png'); pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height); pdf.save(`${studentName}_${courseName}_Certificate.pdf`); toast.success('Certificate saved as PDF!'); }; const wrapText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number) => { const words = text.split(' '); let line = ''; let testLine = ''; const lines = []; for (let n = 0; n < words.length; n++) { testLine += `${words[n]} `; const metrics = ctx.measureText(testLine); const testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { lines.push({ text: line.trim(), width: ctx.measureText(line).width }); testLine = `${words[n]} `; line = `${words[n]} `; } else { line = testLine; } } lines.push({ text: line.trim(), width: ctx.measureText(line).width }); let currentY = y; lines.forEach((lineObj) => { ctx.fillText(lineObj.text, x, currentY); currentY += lineHeight; }); return currentY; }; const drawCertificate = async ( ctx: CanvasRenderingContext2D, dimensions: { width: number; height: number }, logoImage: HTMLImageElement | null = null, ) => { // Background ctx.fillStyle = template_data.backgroundColor; ctx.fillRect(0, 0, dimensions.width, dimensions.height); // Outer border ctx.strokeStyle = template_data.borderColor; ctx.lineWidth = 8; ctx.strokeRect(20, 20, dimensions.width - 40, dimensions.height - 40); // Inner border ctx.strokeStyle = template_data.primaryColor; ctx.lineWidth = 2; ctx.strokeRect(40, 40, dimensions.width - 80, dimensions.height - 80); // Set text align ctx.textAlign = 'center'; let currentY = 100; // Draw logo if exists if (logoImage) { const logoSize = 80; const logoX = (dimensions.width - logoSize) / 2; const logoY = 60; ctx.drawImage(logoImage, logoX, logoY, logoSize, logoSize); currentY = logoY + logoSize + 30; // Position text below logo } // Title ctx.font = `bold 42px ${template_data.fontFamily}`; ctx.fillStyle = template_data.primaryColor; ctx.fillText(template_data.titleText, dimensions.width / 2, currentY); currentY += 20; // Decorative line under title ctx.strokeStyle = template_data.borderColor; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(dimensions.width / 2 - 150, currentY); ctx.lineTo(dimensions.width / 2 + 150, currentY); ctx.stroke(); currentY += 50; // Description text ctx.font = `22px ${template_data.fontFamily}`; ctx.fillStyle = template_data.secondaryColor; currentY = wrapText(ctx, template_data.descriptionText, dimensions.width / 2, currentY, dimensions.width - 100, 30); currentY += 50; // Student name ctx.font = `bold 36px ${template_data.fontFamily}`; ctx.fillStyle = template_data.primaryColor; ctx.fillText(studentName, dimensions.width / 2, currentY); // Underline for student name const nameWidth = ctx.measureText(studentName).width; ctx.strokeStyle = template_data.borderColor; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo((dimensions.width - nameWidth) / 2 - 20, currentY + 10); ctx.lineTo((dimensions.width + nameWidth) / 2 + 20, currentY + 10); ctx.stroke(); currentY += 60; // Completion text ctx.font = `22px ${template_data.fontFamily}`; ctx.fillStyle = template_data.secondaryColor; ctx.fillText(template_data.completionText, dimensions.width / 2, currentY); currentY += 50; // Course name ctx.font = `bold 28px ${template_data.fontFamily}`; ctx.fillStyle = template_data.primaryColor; ctx.fillText(courseName, dimensions.width / 2, currentY); currentY += 60; // Completion date ctx.font = `18px ${template_data.fontFamily}`; ctx.fillStyle = template_data.secondaryColor; ctx.fillText(`Completed on: ${completionDate}`, dimensions.width / 2, currentY); currentY += 60; // Footer ctx.font = `16px ${template_data.fontFamily}`; ctx.fillStyle = template_data.secondaryColor; ctx.fillText(template_data.footerText, dimensions.width / 2, dimensions.height - 60); }; return (
{/* Inner decorative border */}
{/* Logo or Award Icon */} {template.logo_path ? (
Logo
) : ( )} {/* Title */}

{template_data.titleText}

{/* Description */}

{template_data.descriptionText}

{/* Student Name */}

{studentName}

{/* Completion Text */}

{template_data.completionText}

{/* Course Name */}

{courseName}

{/* Completion Date */}

Completed on: {completionDate}

{/* Footer */}

{template_data.footerText}

); }; export default DynamicCertificate;