feat: update certificate design with SVG template and Poppins font

- Changed certificate background from PNG to SVG for better quality
- Added Poppins font import to index.html
- Updated certificate layout with Team Name and Person Name in blue (#59bef5)
- Repositioned QR code to fit in designated white box placeholder
- Removed generated copyright and date overlays (now part of SVG template)
- Adjusted text positioning for better alignment with new design
- Increased canvas scale to 4x and wait time to 1500ms for sharper rendering

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Maulana Sodiqin
2025-12-13 23:41:56 +07:00
co-authored by Claude
parent 782cce820b
commit 3ad828e759
4 changed files with 190 additions and 98 deletions
+3
View File
@@ -18,6 +18,9 @@
type="image/svg+xml"
href="/images/imphnen-logo-simple.svg"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/src/index.css" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://hackathon.imphnen.dev/" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 263 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 506 KiB

@@ -115,19 +115,22 @@ const CertificatePage: FC = (): ReactElement => {
setIsGenerating(true);
try {
// Wait a bit for fonts and images to load
await new Promise((resolve) => setTimeout(resolve, 500));
// Wait longer for fonts and images to load properly
await new Promise((resolve) => setTimeout(resolve, 1500));
const canvas = await html2canvas(certificateRef.current, {
scale: 2,
scale: 4,
useCORS: true,
backgroundColor: '#ffffff',
logging: false,
width: 1000,
height: (1000 * 2480) / 3508,
allowTaint: true,
imageTimeout: 0,
removeContainer: true,
});
const imageUrl = canvas.toDataURL('image/png');
const imageUrl = canvas.toDataURL('image/png', 1.0);
setCertificateImage(imageUrl);
setShowTemplate(false);
} catch (error) {
@@ -315,57 +318,30 @@ const CertificatePage: FC = (): ReactElement => {
id="certificate-template"
style={{
position: 'relative',
backgroundImage: 'url(/images/blank_cert.png)',
backgroundImage: 'url(/images/blank_cert.svg)',
backgroundSize: 'cover',
backgroundPosition: 'center',
width: '1000px',
height: `${(1000 * 2480) / 3508}px`,
}}
>
{/* User Name (from session) */}
{/* Team Name - positioned in middle between "Diberikan Kepada" and "Telah Berpartisipasi" */}
<div
style={{
position: 'absolute',
top: '40%',
left: '50%',
transform: 'translateX(-50%)',
width: '80%',
}}
>
<h3
ref={userNameRef}
style={{
fontWeight: 'bold',
color: '#111827',
textAlign: 'center',
fontSize: userNameFontSize,
lineHeight: '1.2',
wordBreak: 'break-word',
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
margin: 0,
}}
>
{certificateName || 'N/A'}
</h3>
</div>
{/* Team Name */}
<div
style={{
position: 'absolute',
top: '46%',
left: '50%',
transform: 'translateX(-50%)',
width: '70%',
top: '41%',
left: '3.5%',
width: '55%',
}}
>
<h3
ref={teamNameRef}
style={{
fontWeight: '600',
color: '#1f2937',
textAlign: 'center',
fontSize: teamNameFontSize,
fontFamily: 'Poppins, sans-serif',
fontWeight: 'bold',
color: '#59bef5',
textAlign: 'left',
fontSize: '32px',
lineHeight: '1.2',
wordBreak: 'break-word',
margin: 0,
@@ -375,82 +351,53 @@ const CertificatePage: FC = (): ReactElement => {
</h3>
</div>
{/* Participation Text */}
{/* User Name - positioned below team name */}
<div
style={{
position: 'absolute',
top: '60%',
left: '50%',
transform: 'translateX(-50%)',
width: '70%',
top: '45%',
left: '3.5%',
width: '55%',
}}
>
<p
<h3
ref={userNameRef}
style={{
textAlign: 'center',
color: '#374151',
fontSize: '18px',
fontWeight: '500',
fontFamily: 'Poppins, sans-serif',
fontWeight: 'bold',
color: '#59bef5',
textAlign: 'left',
fontSize: '40px',
lineHeight: '1.2',
wordBreak: 'break-word',
margin: 0,
}}
>
<span style={{ fontWeight: 'bold' }}>
Peserta Hackathon IMPHNEN x KOLOSAL AI
</span>
</p>
{certificateName || 'N/A'}
</h3>
</div>
{/* QR Code */}
{/* QR Code - positioned in the white box area */}
<div
style={{
position: 'absolute',
bottom: '8%',
left: '8%',
top: '33%',
right: '9.3%',
width: '190px',
height: '190px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{qrCodeUrl && (
<div
style={{
backgroundColor: '#ffffff',
padding: '8px',
borderRadius: '4px',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
}}
>
<img
src={qrCodeUrl}
alt="Certificate QR Code"
style={{ width: '96px', height: '96px' }}
/>
</div>
<img
src={qrCodeUrl}
alt="Certificate QR Code"
style={{ width: '190px', height: '190px', display: 'block' }}
/>
)}
</div>
{/* Date */}
<div
style={{
position: 'absolute',
bottom: '8%',
right: '8%',
}}
>
<p
style={{
fontSize: '14px',
color: '#374151',
margin: 0,
}}
>
{new Date().toLocaleDateString(
'id-ID',
{
day: 'numeric',
month: 'long',
year: 'numeric',
}
)}
</p>
</div>
</div>
</div>