Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 15 additions & 27 deletions frontend/src/components/certificates/CertificateTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import {
drawCryptoStamp,
truncateHash,
} from '@/lib/certificate-generator';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, forwardRef } from 'react';

interface Props {
data: CertificateData;
/** If true, renders at full resolution for download */
forExport?: boolean;
}

export default function CertificateTemplate({ data, forExport = false }: Props) {
// Forward ref so parent can capture the container for downloading
const CertificateTemplate = forwardRef<HTMLDivElement, Props>(function CertificateTemplate({ data, forExport = false }, ref) {
const stampRef = useRef<HTMLCanvasElement>(null);

// Draw the crypto stamp on the canvas overlay
Expand All @@ -39,6 +40,7 @@ export default function CertificateTemplate({ data, forExport = false }: Props)
return (
<div
id="certificate-template"
ref={ref}
className={`relative overflow-hidden bg-zinc-950 select-none ${
forExport ? 'h-[848px] w-[1200px]' : 'aspect-[1200/848] w-full'
}`}
Expand All @@ -65,21 +67,15 @@ export default function CertificateTemplate({ data, forExport = false }: Props)
<div className="text-center">
<div className="mb-2 flex items-center justify-center gap-3">
<div className="h-px w-8 bg-red-600/60" />
<span className="font-mono text-xs tracking-[0.3em] text-red-500 uppercase">
Web3 Student Lab
</span>
<span className="font-mono text-xs tracking-[0.3em] text-red-500 uppercase">Web3 Student Lab</span>
<div className="h-px w-8 bg-red-600/60" />
</div>
<p className="font-mono text-[10px] tracking-[0.4em] text-gray-500 uppercase">
Blockchain-Verified Certificate of Completion
</p>
<p className="font-mono text-[10px] tracking-[0.4em] text-gray-500 uppercase">Blockchain-Verified Certificate of Completion</p>
</div>

{/* Main body */}
<div className="flex flex-1 flex-col items-center justify-center gap-4 text-center">
<p className="font-sans text-sm tracking-[0.25em] text-gray-400 uppercase">
This certifies that
</p>
<p className="font-sans text-sm tracking-[0.25em] text-gray-400 uppercase">This certifies that</p>

{/* Recipient name */}
<div className="relative">
Expand All @@ -92,9 +88,7 @@ export default function CertificateTemplate({ data, forExport = false }: Props)
<div className="mt-2 h-px bg-gradient-to-r from-transparent via-red-600/60 to-transparent" />
</div>

<p className="font-sans text-sm tracking-[0.25em] text-gray-400 uppercase">
has successfully completed
</p>
<p className="font-sans text-sm tracking-[0.25em] text-gray-400 uppercase">has successfully completed</p>

{/* Course name */}
<h2
Expand All @@ -105,22 +99,16 @@ export default function CertificateTemplate({ data, forExport = false }: Props)
</h2>

{/* Issue date */}
<p className="mt-2 font-mono text-sm tracking-widest text-gray-500">
Issued on {formattedDate}
</p>
<p className="mt-2 font-mono text-sm tracking-widest text-gray-500">Issued on {formattedDate}</p>
</div>

{/* Footer row */}
<div className="flex w-full items-end justify-between">
{/* Instructor signature */}
<div className="min-w-[180px] text-center">
<div className="mb-2 h-px bg-white/20" />
<p className="text-sm font-bold tracking-wider text-white">
{data.instructorName || 'Instructor'}
</p>
<p className="font-mono text-[10px] tracking-widest text-gray-500 uppercase">
Course Instructor
</p>
<p className="text-sm font-bold tracking-wider text-white">{data.instructorName || 'Instructor'}</p>
<p className="font-mono text-[10px] tracking-widest text-gray-500 uppercase">Course Instructor</p>
</div>

{/* Crypto stamp canvas */}
Expand All @@ -143,15 +131,13 @@ export default function CertificateTemplate({ data, forExport = false }: Props)
<p className="font-mono text-sm font-bold tracking-wider text-white">
{data.certificateId ? data.certificateId.slice(0, 12).toUpperCase() : '—'}
</p>
<p className="font-mono text-[10px] tracking-widest text-gray-500 uppercase">
Certificate ID
</p>
<p className="font-mono text-[10px] tracking-widest text-gray-500 uppercase">Certificate ID</p>
</div>
</div>
</div>
</div>
);
}
});

function CornerOrnament({
position,
Expand All @@ -175,3 +161,5 @@ function CornerOrnament({
</div>
);
}

export default CertificateTemplate;