feat: Add certificate templates for Scrum, ITIL, and PRINCE2 courses

- Add 3 new HTML templates: ScrumGeneric, ITIL, PRINCE2
- Add 3 background images: blue (Scrum), green (ITIL), orange (PRINCE2)
- Extract and add signatures: Gablas, Husam
- Update certificate.service.js to support multiple templates with different backgrounds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-29 14:22:03 +01:00
parent 12acd68156
commit f2af7ffe22
9 changed files with 391 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -20,13 +20,26 @@ export const CERTIFICATE_TEMPLATES = {
name: 'AI Certifikát', name: 'AI Certifikát',
file: 'AIcertifikat.html', file: 'AIcertifikat.html',
description: 'Osvedčenie o absolvovaní AI kurzu', description: 'Osvedčenie o absolvovaní AI kurzu',
background: 'background.jpeg',
},
ScrumGeneric: {
name: 'Scrum (Gablas)',
file: 'ScrumGeneric.html',
description: 'Scrum Master / Product Owner - lektor Gablas',
background: 'background-blue.jpeg',
},
ITIL: {
name: 'ITIL (Husam)',
file: 'ITIL.html',
description: 'ITIL Foundation - lektor Husam',
background: 'background-green.jpeg',
},
PRINCE2: {
name: 'PRINCE2 (Gablas)',
file: 'PRINCE2.html',
description: 'PRINCE2 Foundation/Practitioner - lektor Gablas',
background: 'background-orange.jpeg',
}, },
// Add more templates here in the future
// participation: {
// name: 'Potvrdenie o účasti',
// file: 'participation.html',
// description: 'Potvrdenie o účasti na školení',
// },
}; };
/** /**
@@ -57,9 +70,12 @@ const generateCertificateId = (registraciaId) => {
/** /**
* Load assets as base64 for embedding in HTML * Load assets as base64 for embedding in HTML
*/ */
const loadAssets = async () => { const loadAssets = async (templateName) => {
const template = CERTIFICATE_TEMPLATES[templateName];
const backgroundFile = template?.background || 'background.jpeg';
const [background, signatureGablasova, signatureZdarilek] = await Promise.all([ const [background, signatureGablasova, signatureZdarilek] = await Promise.all([
fs.readFile(path.join(ASSETS_DIR, 'background.jpeg')), fs.readFile(path.join(ASSETS_DIR, backgroundFile)),
fs.readFile(path.join(ASSETS_DIR, 'signature-gablasova.png')), fs.readFile(path.join(ASSETS_DIR, 'signature-gablasova.png')),
fs.readFile(path.join(ASSETS_DIR, 'signature-zdarilek.png')), fs.readFile(path.join(ASSETS_DIR, 'signature-zdarilek.png')),
]); ]);
@@ -196,7 +212,7 @@ export const generateCertificate = async (registraciaId, templateName = 'AIcerti
// Load template and assets // Load template and assets
const [templateHtml, assets] = await Promise.all([ const [templateHtml, assets] = await Promise.all([
loadTemplate(templateName), loadTemplate(templateName),
loadAssets(), loadAssets(templateName),
]); ]);
// Generate certificate data // Generate certificate data

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="sk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Osvedčenie - {{participantName}}</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@page {
size: A4 landscape;
margin: 0;
}
html, body {
width: 297mm;
height: 210mm;
overflow: hidden;
}
body {
font-family: 'Open Sans', Arial, sans-serif;
background: #fff;
}
.certificate {
width: 297mm;
height: 210mm;
position: relative;
background-image: url('{{backgroundImage}}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.content {
position: absolute;
top: 42mm;
left: 15mm;
right: 15mm;
}
.course-title {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8mm;
}
.course-dates {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 3mm;
}
.course-hours {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
}
.participant-section {
position: absolute;
top: 58%;
left: 0;
right: 0;
text-align: center;
}
.participant-name {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
}
.issue-date {
position: absolute;
bottom: 32mm;
left: 20mm;
font-size: 11pt;
color: #333;
}
.certificate-id {
position: absolute;
bottom: 8mm;
right: 15mm;
font-size: 7pt;
color: #666;
}
</style>
</head>
<body>
<div class="certificate">
<div class="content">
<h2 class="course-title">{{courseTitle}}</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
</div>
<div class="participant-section">
<h3 class="participant-name">{{participantName}}</h3>
</div>
<div class="issue-date">{{issueDate}}</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="sk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Osvedčenie - {{participantName}}</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@page {
size: A4 landscape;
margin: 0;
}
html, body {
width: 297mm;
height: 210mm;
overflow: hidden;
}
body {
font-family: 'Open Sans', Arial, sans-serif;
background: #fff;
}
.certificate {
width: 297mm;
height: 210mm;
position: relative;
background-image: url('{{backgroundImage}}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.content {
position: absolute;
top: 42mm;
left: 15mm;
right: 15mm;
}
.course-title {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8mm;
}
.course-dates {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 3mm;
}
.course-hours {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
}
.participant-section {
position: absolute;
top: 58%;
left: 0;
right: 0;
text-align: center;
}
.participant-name {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
}
.issue-date {
position: absolute;
bottom: 32mm;
left: 20mm;
font-size: 11pt;
color: #333;
}
.certificate-id {
position: absolute;
bottom: 8mm;
right: 15mm;
font-size: 7pt;
color: #666;
}
</style>
</head>
<body>
<div class="certificate">
<div class="content">
<h2 class="course-title">{{courseTitle}}</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
</div>
<div class="participant-section">
<h3 class="participant-name">{{participantName}}</h3>
</div>
<div class="issue-date">{{issueDate}}</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="sk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Osvedčenie - {{participantName}}</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
@page {
size: A4 landscape;
margin: 0;
}
html, body {
width: 297mm;
height: 210mm;
overflow: hidden;
}
body {
font-family: 'Open Sans', Arial, sans-serif;
background: #fff;
}
.certificate {
width: 297mm;
height: 210mm;
position: relative;
background-image: url('{{backgroundImage}}');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.content {
position: absolute;
top: 42mm;
left: 15mm;
right: 15mm;
}
.course-title {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8mm;
}
.course-dates {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 3mm;
}
.course-hours {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
}
.participant-section {
position: absolute;
top: 58%;
left: 0;
right: 0;
text-align: center;
}
.participant-name {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
}
.participant-birthdate {
font-size: 12pt;
color: #333;
margin-top: 2mm;
}
.issue-date {
position: absolute;
bottom: 32mm;
left: 20mm;
font-size: 11pt;
color: #333;
}
.certificate-id {
position: absolute;
bottom: 8mm;
right: 15mm;
font-size: 7pt;
color: #666;
}
</style>
</head>
<body>
<div class="certificate">
<div class="content">
<h2 class="course-title">{{courseTitle}}</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
</div>
<div class="participant-section">
<h3 class="participant-name">{{participantName}}</h3>
</div>
<div class="issue-date">{{issueDate}}</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>