feat: Add 8 certificate templates with different courses and lecturers

Templates:
- AIcertifikat (Zdarílek), AIcertifikatGablas, AIcertifikatPatrik
- ScrumMaster, ScrumProductOwner (blue background)
- ITILFoundation (green background)
- PRINCE2Foundation, PRINCE2Practitioner (orange background)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-29 17:15:44 +01:00
parent f7d252ee7b
commit e923e42a65
10 changed files with 630 additions and 113 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -16,28 +16,56 @@ const TEMPLATES_DIR = path.join(process.cwd(), 'src', 'templates', 'certificates
* Available certificate templates
*/
export const CERTIFICATE_TEMPLATES = {
// AI certifikáty s dynamickým názvom kurzu a rôznymi lektormi
AIcertifikat: {
name: 'AI Certifikát',
name: 'AI Certifikát (Zdarílek)',
file: 'AIcertifikat.html',
description: 'Osvedčenie o absolvovaní AI kurzu',
description: 'AI kurz - lektor Zdarílek + Gablasová',
background: 'background.jpeg',
},
ScrumGeneric: {
name: 'Scrum (Gablas)',
file: 'ScrumGeneric.html',
description: 'Scrum Master / Product Owner - lektor Gablas',
AIcertifikatGablas: {
name: 'AI Certifikát (Gablas)',
file: 'AIcertifikatGablas.html',
description: 'AI kurz - lektor Gablas + Gablasová',
background: 'background.jpeg',
},
AIcertifikatPatrik: {
name: 'AI Certifikát (Patrik)',
file: 'AIcertifikatPatrik.html',
description: 'AI kurz - lektor Patrik + Gablasová',
background: 'background.jpeg',
},
// Scrum certifikáty - fixný názov kurzu, podpisy v pozadí
ScrumMaster: {
name: 'Scrum Master',
file: 'ScrumMaster.html',
description: 'Scrum Master - Gablas + Gablasová',
background: 'background-blue.jpeg',
},
ITIL: {
name: 'ITIL (Husam)',
file: 'ITIL.html',
description: 'ITIL Foundation - lektor Husam',
ScrumProductOwner: {
name: 'Scrum Product Owner',
file: 'ScrumProductOwner.html',
description: 'Scrum Product Owner - Gablas + Gablasová',
background: 'background-blue.jpeg',
},
// ITIL certifikát - fixný názov kurzu, podpisy v pozadí
ITILFoundation: {
name: 'ITIL® 4 Foundation',
file: 'ITILFoundation.html',
description: 'ITIL Foundation - Husam + Gablasová',
background: 'background-green.jpeg',
},
PRINCE2: {
name: 'PRINCE2 (Gablas)',
file: 'PRINCE2.html',
description: 'PRINCE2 Foundation/Practitioner - lektor Gablas',
// PRINCE2 certifikáty - fixný názov kurzu, podpisy v pozadí
PRINCE2Foundation: {
name: 'PRINCE2® Foundation',
file: 'PRINCE2Foundation.html',
description: 'PRINCE2 Foundation - Gablas + Gablasová',
background: 'background-orange.jpeg',
},
PRINCE2Practitioner: {
name: 'PRINCE2® Practitioner',
file: 'PRINCE2Practitioner.html',
description: 'PRINCE2 Practitioner - Gablas + Gablasová',
background: 'background-orange.jpeg',
},
};
@@ -74,16 +102,20 @@ 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, signatureGablas, signaturePatrik] = await Promise.all([
fs.readFile(path.join(ASSETS_DIR, backgroundFile)),
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-gablas.png')),
fs.readFile(path.join(ASSETS_DIR, 'signature-patrik.png')),
]);
return {
backgroundImage: `data:image/jpeg;base64,${background.toString('base64')}`,
signatureGablasova: `data:image/png;base64,${signatureGablasova.toString('base64')}`,
signatureZdarilek: `data:image/png;base64,${signatureZdarilek.toString('base64')}`,
signatureGablas: `data:image/png;base64,${signatureGablas.toString('base64')}`,
signaturePatrik: `data:image/png;base64,${signaturePatrik.toString('base64')}`,
};
};

View File

@@ -0,0 +1,163 @@
<!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=Playfair+Display:wght@400;500;600;700&family=Open+Sans:wght@300;400;500;600&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: 28mm;
left: 0;
right: 0;
text-align: center;
}
.main-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 52pt;
font-weight: 700;
letter-spacing: 14px;
color: #1a1a1a;
text-transform: uppercase;
margin-bottom: 2mm;
}
.subtitle {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 18pt;
font-weight: 400;
color: #333;
letter-spacing: 1px;
margin-bottom: 18mm;
}
.course-section { margin-top: 5mm; }
.course-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 32pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 3mm;
}
.course-modules {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 13pt;
font-weight: 500;
color: #333;
line-height: 1.5;
margin-bottom: 2mm;
}
.course-dates {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 12pt;
font-weight: 400;
color: #444;
}
.participant-name {
font-family: 'Playfair Display', Georgia, serif;
font-size: 36pt;
font-weight: 600;
color: #1a1a1a;
margin-top: 14mm;
}
.footer-section {
position: absolute;
bottom: 28mm;
left: 25mm;
right: 25mm;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.date-location {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 11pt;
color: #333;
}
.signatures { display: flex; gap: 25mm; }
.signature { text-align: center; min-width: 55mm; }
.signature-image { height: 18mm; margin-bottom: 1mm; object-fit: contain; }
.signature-line { width: 55mm; height: 0.4mm; background: #333; margin: 0 auto 2mm auto; }
.signature-name { font-family: 'Open Sans', Arial, sans-serif; font-size: 10pt; font-weight: 600; color: #1a1a1a; }
.signature-title { font-family: 'Open Sans', Arial, sans-serif; font-size: 9pt; font-weight: 400; color: #555; }
.certificate-id {
position: absolute;
bottom: 20mm;
right: 25mm;
font-family: 'Open Sans', Arial, sans-serif;
font-size: 7pt;
color: #999;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div class="certificate">
<div class="content">
<h1 class="main-title">O S V E D Č E N I E</h1>
<p class="subtitle">o absolvovaní kurzu</p>
<div class="course-section">
<h2 class="course-title">{{courseTitle}}</h2>
{{#if courseModules}}
<p class="course-modules">{{courseModules}}</p>
{{/if}}
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
</div>
<h3 class="participant-name">{{participantName}}</h3>
</div>
<div class="footer-section">
<div class="date-location">
V Bratislave {{issueDate}}
</div>
<div class="signatures">
<div class="signature">
<img src="{{signatureGablas}}" alt="Podpis" class="signature-image" />
<div class="signature-line"></div>
<p class="signature-name">Ing. Branislav Gablas, PhD.</p>
<p class="signature-title">lektor</p>
</div>
<div class="signature">
<img src="{{signatureGablasova}}" alt="Podpis" class="signature-image" />
<div class="signature-line"></div>
<p class="signature-name">Ing. Jana Gablasová</p>
<p class="signature-title">konateľ</p>
</div>
</div>
</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,163 @@
<!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=Playfair+Display:wght@400;500;600;700&family=Open+Sans:wght@300;400;500;600&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: 28mm;
left: 0;
right: 0;
text-align: center;
}
.main-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 52pt;
font-weight: 700;
letter-spacing: 14px;
color: #1a1a1a;
text-transform: uppercase;
margin-bottom: 2mm;
}
.subtitle {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 18pt;
font-weight: 400;
color: #333;
letter-spacing: 1px;
margin-bottom: 18mm;
}
.course-section { margin-top: 5mm; }
.course-title {
font-family: 'Playfair Display', Georgia, serif;
font-size: 32pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 3mm;
}
.course-modules {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 13pt;
font-weight: 500;
color: #333;
line-height: 1.5;
margin-bottom: 2mm;
}
.course-dates {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 12pt;
font-weight: 400;
color: #444;
}
.participant-name {
font-family: 'Playfair Display', Georgia, serif;
font-size: 36pt;
font-weight: 600;
color: #1a1a1a;
margin-top: 14mm;
}
.footer-section {
position: absolute;
bottom: 28mm;
left: 25mm;
right: 25mm;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.date-location {
font-family: 'Open Sans', Arial, sans-serif;
font-size: 11pt;
color: #333;
}
.signatures { display: flex; gap: 25mm; }
.signature { text-align: center; min-width: 55mm; }
.signature-image { height: 18mm; margin-bottom: 1mm; object-fit: contain; }
.signature-line { width: 55mm; height: 0.4mm; background: #333; margin: 0 auto 2mm auto; }
.signature-name { font-family: 'Open Sans', Arial, sans-serif; font-size: 10pt; font-weight: 600; color: #1a1a1a; }
.signature-title { font-family: 'Open Sans', Arial, sans-serif; font-size: 9pt; font-weight: 400; color: #555; }
.certificate-id {
position: absolute;
bottom: 20mm;
right: 25mm;
font-family: 'Open Sans', Arial, sans-serif;
font-size: 7pt;
color: #999;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div class="certificate">
<div class="content">
<h1 class="main-title">O S V E D Č E N I E</h1>
<p class="subtitle">o absolvovaní kurzu</p>
<div class="course-section">
<h2 class="course-title">{{courseTitle}}</h2>
{{#if courseModules}}
<p class="course-modules">{{courseModules}}</p>
{{/if}}
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
</div>
<h3 class="participant-name">{{participantName}}</h3>
</div>
<div class="footer-section">
<div class="date-location">
V Bratislave {{issueDate}}
</div>
<div class="signatures">
<div class="signature">
<img src="{{signaturePatrik}}" alt="Podpis" class="signature-image" />
<div class="signature-line"></div>
<p class="signature-name">Mgr. Patrik Gablík</p>
<p class="signature-title">lektor</p>
</div>
<div class="signature">
<img src="{{signatureGablasova}}" alt="Podpis" class="signature-image" />
<div class="signature-line"></div>
<p class="signature-name">Ing. Jana Gablasová</p>
<p class="signature-title">konateľ</p>
</div>
</div>
</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>

View File

@@ -7,27 +7,10 @@
<style>
@import url('https://fonts.googleapis.com/css2?family=Bahnschrift:wght@400;600;700&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;
}
* { 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;
@@ -39,7 +22,6 @@
background-repeat: no-repeat;
}
/* Main content - centered in the white area */
.main-content {
position: absolute;
top: 50%;
@@ -58,7 +40,7 @@
.course-title {
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
font-size: 24pt;
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4mm;
@@ -77,7 +59,6 @@
color: #1a1a1a;
}
/* Issue date - above "Dátum" at bottom left */
.issue-date {
position: absolute;
bottom: 42mm;
@@ -99,13 +80,11 @@
<div class="certificate">
<div class="main-content">
<h3 class="participant-name">{{participantName}}</h3>
<h2 class="course-title">{{courseTitle}}</h2>
<h2 class="course-title">ITIL® 4 Foundation</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
<p class="course-hours">v rozsahu 24 hodín</p>
</div>
<div class="issue-date">{{issueDate}}</div>

View File

@@ -7,27 +7,10 @@
<style>
@import url('https://fonts.googleapis.com/css2?family=Bahnschrift:wght@400;600;700&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;
}
* { 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;
@@ -39,7 +22,6 @@
background-repeat: no-repeat;
}
/* Main content - centered in the white area */
.main-content {
position: absolute;
top: 50%;
@@ -58,7 +40,7 @@
.course-title {
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
font-size: 24pt;
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4mm;
@@ -77,7 +59,6 @@
color: #1a1a1a;
}
/* Issue date - above "Dátum" at bottom left */
.issue-date {
position: absolute;
bottom: 42mm;
@@ -99,13 +80,11 @@
<div class="certificate">
<div class="main-content">
<h3 class="participant-name">{{participantName}}</h3>
<h2 class="course-title">{{courseTitle}}</h2>
<h2 class="course-title">PRINCE2® Foundation</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
<p class="course-hours">v rozsahu 16 hodín</p>
</div>
<div class="issue-date">{{issueDate}}</div>

View File

@@ -7,27 +7,10 @@
<style>
@import url('https://fonts.googleapis.com/css2?family=Bahnschrift:wght@400;600;700&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;
}
* { 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;
@@ -39,7 +22,6 @@
background-repeat: no-repeat;
}
/* Main content - centered in the white area */
.main-content {
position: absolute;
top: 50%;
@@ -58,7 +40,7 @@
.course-title {
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
font-size: 24pt;
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4mm;
@@ -77,7 +59,6 @@
color: #1a1a1a;
}
/* Issue date - above "Dátum" at bottom left */
.issue-date {
position: absolute;
bottom: 42mm;
@@ -99,13 +80,11 @@
<div class="certificate">
<div class="main-content">
<h3 class="participant-name">{{participantName}}</h3>
<h2 class="course-title">{{courseTitle}}</h2>
<h2 class="course-title">PRINCE2® Practitioner</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
{{#if courseHours}}
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
{{/if}}
<p class="course-hours">v rozsahu 24 hodín</p>
</div>
<div class="issue-date">{{issueDate}}</div>

View File

@@ -0,0 +1,94 @@
<!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=Bahnschrift:wght@400;600;700&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;
}
.main-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
margin-top: -15mm;
}
.participant-name {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8mm;
}
.course-title {
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4mm;
}
.course-dates {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 2mm;
}
.course-hours {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
}
.issue-date {
position: absolute;
bottom: 42mm;
left: 28mm;
font-size: 12pt;
color: #333;
}
.certificate-id {
position: absolute;
bottom: 10mm;
right: 18mm;
font-size: 7pt;
color: #666;
}
</style>
</head>
<body>
<div class="certificate">
<div class="main-content">
<h3 class="participant-name">{{participantName}}</h3>
<h2 class="course-title">Scrum Master</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
<p class="course-hours">v rozsahu 16 hodín</p>
</div>
<div class="issue-date">{{issueDate}}</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!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=Bahnschrift:wght@400;600;700&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;
}
.main-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
margin-top: -15mm;
}
.participant-name {
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8mm;
}
.course-title {
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
font-size: 28pt;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 4mm;
}
.course-dates {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 2mm;
}
.course-hours {
font-size: 14pt;
font-weight: 600;
color: #1a1a1a;
}
.issue-date {
position: absolute;
bottom: 42mm;
left: 28mm;
font-size: 12pt;
color: #333;
}
.certificate-id {
position: absolute;
bottom: 10mm;
right: 18mm;
font-size: 7pt;
color: #666;
}
</style>
</head>
<body>
<div class="certificate">
<div class="main-content">
<h3 class="participant-name">{{participantName}}</h3>
<h2 class="course-title">Scrum Product Owner</h2>
{{#if dateRange}}
<p class="course-dates">{{dateRange}}</p>
{{/if}}
<p class="course-hours">v rozsahu 16 hodín</p>
</div>
<div class="issue-date">{{issueDate}}</div>
<div class="certificate-id">ID: {{certificateId}}</div>
</div>
</body>
</html>