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:
BIN
src/assets/certificate/signature-patrik.png
Normal file
BIN
src/assets/certificate/signature-patrik.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -16,28 +16,56 @@ const TEMPLATES_DIR = path.join(process.cwd(), 'src', 'templates', 'certificates
|
|||||||
* Available certificate templates
|
* Available certificate templates
|
||||||
*/
|
*/
|
||||||
export const CERTIFICATE_TEMPLATES = {
|
export const CERTIFICATE_TEMPLATES = {
|
||||||
|
// AI certifikáty s dynamickým názvom kurzu a rôznymi lektormi
|
||||||
AIcertifikat: {
|
AIcertifikat: {
|
||||||
name: 'AI Certifikát',
|
name: 'AI Certifikát (Zdarílek)',
|
||||||
file: 'AIcertifikat.html',
|
file: 'AIcertifikat.html',
|
||||||
description: 'Osvedčenie o absolvovaní AI kurzu',
|
description: 'AI kurz - lektor Zdarílek + Gablasová',
|
||||||
background: 'background.jpeg',
|
background: 'background.jpeg',
|
||||||
},
|
},
|
||||||
ScrumGeneric: {
|
AIcertifikatGablas: {
|
||||||
name: 'Scrum (Gablas)',
|
name: 'AI Certifikát (Gablas)',
|
||||||
file: 'ScrumGeneric.html',
|
file: 'AIcertifikatGablas.html',
|
||||||
description: 'Scrum Master / Product Owner - lektor Gablas',
|
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',
|
background: 'background-blue.jpeg',
|
||||||
},
|
},
|
||||||
ITIL: {
|
ScrumProductOwner: {
|
||||||
name: 'ITIL (Husam)',
|
name: 'Scrum Product Owner',
|
||||||
file: 'ITIL.html',
|
file: 'ScrumProductOwner.html',
|
||||||
description: 'ITIL Foundation - lektor Husam',
|
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',
|
background: 'background-green.jpeg',
|
||||||
},
|
},
|
||||||
PRINCE2: {
|
// PRINCE2 certifikáty - fixný názov kurzu, podpisy v pozadí
|
||||||
name: 'PRINCE2 (Gablas)',
|
PRINCE2Foundation: {
|
||||||
file: 'PRINCE2.html',
|
name: 'PRINCE2® Foundation',
|
||||||
description: 'PRINCE2 Foundation/Practitioner - lektor Gablas',
|
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',
|
background: 'background-orange.jpeg',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -74,16 +102,20 @@ const loadAssets = async (templateName) => {
|
|||||||
const template = CERTIFICATE_TEMPLATES[templateName];
|
const template = CERTIFICATE_TEMPLATES[templateName];
|
||||||
const backgroundFile = template?.background || 'background.jpeg';
|
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, 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')),
|
||||||
|
fs.readFile(path.join(ASSETS_DIR, 'signature-gablas.png')),
|
||||||
|
fs.readFile(path.join(ASSETS_DIR, 'signature-patrik.png')),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundImage: `data:image/jpeg;base64,${background.toString('base64')}`,
|
backgroundImage: `data:image/jpeg;base64,${background.toString('base64')}`,
|
||||||
signatureGablasova: `data:image/png;base64,${signatureGablasova.toString('base64')}`,
|
signatureGablasova: `data:image/png;base64,${signatureGablasova.toString('base64')}`,
|
||||||
signatureZdarilek: `data:image/png;base64,${signatureZdarilek.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')}`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
163
src/templates/certificates/AIcertifikatGablas.html
Normal file
163
src/templates/certificates/AIcertifikatGablas.html
Normal 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>
|
||||||
163
src/templates/certificates/AIcertifikatPatrik.html
Normal file
163
src/templates/certificates/AIcertifikatPatrik.html
Normal 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>
|
||||||
@@ -7,27 +7,10 @@
|
|||||||
<style>
|
<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');
|
@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; }
|
||||||
margin: 0;
|
@page { size: A4 landscape; margin: 0; }
|
||||||
padding: 0;
|
html, body { width: 297mm; height: 210mm; overflow: hidden; }
|
||||||
box-sizing: border-box;
|
body { font-family: 'Open Sans', Arial, sans-serif; background: #fff; }
|
||||||
}
|
|
||||||
|
|
||||||
@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 {
|
.certificate {
|
||||||
width: 297mm;
|
width: 297mm;
|
||||||
@@ -39,7 +22,6 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main content - centered in the white area */
|
|
||||||
.main-content {
|
.main-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -58,7 +40,7 @@
|
|||||||
|
|
||||||
.course-title {
|
.course-title {
|
||||||
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
||||||
font-size: 24pt;
|
font-size: 28pt;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
margin-bottom: 4mm;
|
margin-bottom: 4mm;
|
||||||
@@ -77,7 +59,6 @@
|
|||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Issue date - above "Dátum" at bottom left */
|
|
||||||
.issue-date {
|
.issue-date {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 42mm;
|
bottom: 42mm;
|
||||||
@@ -99,13 +80,11 @@
|
|||||||
<div class="certificate">
|
<div class="certificate">
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<h3 class="participant-name">{{participantName}}</h3>
|
<h3 class="participant-name">{{participantName}}</h3>
|
||||||
<h2 class="course-title">{{courseTitle}}</h2>
|
<h2 class="course-title">ITIL® 4 Foundation</h2>
|
||||||
{{#if dateRange}}
|
{{#if dateRange}}
|
||||||
<p class="course-dates">{{dateRange}}</p>
|
<p class="course-dates">{{dateRange}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if courseHours}}
|
<p class="course-hours">v rozsahu 24 hodín</p>
|
||||||
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="issue-date">{{issueDate}}</div>
|
<div class="issue-date">{{issueDate}}</div>
|
||||||
@@ -7,27 +7,10 @@
|
|||||||
<style>
|
<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');
|
@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; }
|
||||||
margin: 0;
|
@page { size: A4 landscape; margin: 0; }
|
||||||
padding: 0;
|
html, body { width: 297mm; height: 210mm; overflow: hidden; }
|
||||||
box-sizing: border-box;
|
body { font-family: 'Open Sans', Arial, sans-serif; background: #fff; }
|
||||||
}
|
|
||||||
|
|
||||||
@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 {
|
.certificate {
|
||||||
width: 297mm;
|
width: 297mm;
|
||||||
@@ -39,7 +22,6 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main content - centered in the white area */
|
|
||||||
.main-content {
|
.main-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -58,7 +40,7 @@
|
|||||||
|
|
||||||
.course-title {
|
.course-title {
|
||||||
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
||||||
font-size: 24pt;
|
font-size: 28pt;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
margin-bottom: 4mm;
|
margin-bottom: 4mm;
|
||||||
@@ -77,7 +59,6 @@
|
|||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Issue date - above "Dátum" at bottom left */
|
|
||||||
.issue-date {
|
.issue-date {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 42mm;
|
bottom: 42mm;
|
||||||
@@ -99,13 +80,11 @@
|
|||||||
<div class="certificate">
|
<div class="certificate">
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<h3 class="participant-name">{{participantName}}</h3>
|
<h3 class="participant-name">{{participantName}}</h3>
|
||||||
<h2 class="course-title">{{courseTitle}}</h2>
|
<h2 class="course-title">PRINCE2® Foundation</h2>
|
||||||
{{#if dateRange}}
|
{{#if dateRange}}
|
||||||
<p class="course-dates">{{dateRange}}</p>
|
<p class="course-dates">{{dateRange}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if courseHours}}
|
<p class="course-hours">v rozsahu 16 hodín</p>
|
||||||
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="issue-date">{{issueDate}}</div>
|
<div class="issue-date">{{issueDate}}</div>
|
||||||
@@ -7,27 +7,10 @@
|
|||||||
<style>
|
<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');
|
@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; }
|
||||||
margin: 0;
|
@page { size: A4 landscape; margin: 0; }
|
||||||
padding: 0;
|
html, body { width: 297mm; height: 210mm; overflow: hidden; }
|
||||||
box-sizing: border-box;
|
body { font-family: 'Open Sans', Arial, sans-serif; background: #fff; }
|
||||||
}
|
|
||||||
|
|
||||||
@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 {
|
.certificate {
|
||||||
width: 297mm;
|
width: 297mm;
|
||||||
@@ -39,7 +22,6 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main content - centered in the white area */
|
|
||||||
.main-content {
|
.main-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -58,7 +40,7 @@
|
|||||||
|
|
||||||
.course-title {
|
.course-title {
|
||||||
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
font-family: 'Bahnschrift', 'Open Sans', Arial, sans-serif;
|
||||||
font-size: 24pt;
|
font-size: 28pt;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
margin-bottom: 4mm;
|
margin-bottom: 4mm;
|
||||||
@@ -77,7 +59,6 @@
|
|||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Issue date - above "Dátum" at bottom left */
|
|
||||||
.issue-date {
|
.issue-date {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 42mm;
|
bottom: 42mm;
|
||||||
@@ -99,13 +80,11 @@
|
|||||||
<div class="certificate">
|
<div class="certificate">
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<h3 class="participant-name">{{participantName}}</h3>
|
<h3 class="participant-name">{{participantName}}</h3>
|
||||||
<h2 class="course-title">{{courseTitle}}</h2>
|
<h2 class="course-title">PRINCE2® Practitioner</h2>
|
||||||
{{#if dateRange}}
|
{{#if dateRange}}
|
||||||
<p class="course-dates">{{dateRange}}</p>
|
<p class="course-dates">{{dateRange}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if courseHours}}
|
<p class="course-hours">v rozsahu 24 hodín</p>
|
||||||
<p class="course-hours">v rozsahu {{courseHours}} hodín</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="issue-date">{{issueDate}}</div>
|
<div class="issue-date">{{issueDate}}</div>
|
||||||
94
src/templates/certificates/ScrumMaster.html
Normal file
94
src/templates/certificates/ScrumMaster.html
Normal 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>
|
||||||
94
src/templates/certificates/ScrumProductOwner.html
Normal file
94
src/templates/certificates/ScrumProductOwner.html
Normal 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>
|
||||||
@@ -1,15 +1,49 @@
|
|||||||
Refactoring Smoke Test Checklist
|
Task 1: Changed "Todos" to "Úlohy" in Sidebar
|
||||||
=================================
|
|
||||||
|
|
||||||
1. [x] Start server (npm run dev) — does it boot without import errors?
|
Task 2: Fixed EventDetail panel width from w-max to w-80
|
||||||
2. [x] Login — check audit log entry exists
|
|
||||||
3. [x] Create a company — check audit log
|
Task 3: Chat Online Indicator:
|
||||||
4. [x] Add a note to the company
|
- Added sendHeartbeat() to userApi
|
||||||
5. [x] Add a reminder to the company
|
- Added heartbeat interval (30s) in AppDataContext
|
||||||
6. [x] Assign a user to the company
|
- Added green dot indicator in ChatList for users online < 60s
|
||||||
7. [x] Create a project, assign user
|
|
||||||
8. [x] Create a todo with assigned users — check push notification
|
Task 4: Updated search placeholder in CompaniesPage
|
||||||
9. [x] Start/stop/pause/resume a timer
|
|
||||||
10. [x] Generate a monthly timesheet XLSX — open and verify
|
Task 5: Calendar Notification Badge:
|
||||||
11. [x] Upload a file to a project (check size/type limits)
|
- Created CalendarCountContext.jsx
|
||||||
12. [x] AI Kurzy: create course, participant, registration
|
- Added getTodayCount() to eventApi
|
||||||
|
- Added badge to Calendar button in Sidebar
|
||||||
|
|
||||||
|
Task 6: Calendar Reminders Display:
|
||||||
|
- Added reminders state to useCalendarData
|
||||||
|
- Display reminders in CalendarGrid with orange styling and bell icon
|
||||||
|
|
||||||
|
Task 7: team_leader role access in Sidebar
|
||||||
|
|
||||||
|
Task 8: Changed "AI Kurzy" to "Všetky kurzy" in Sidebar
|
||||||
|
|
||||||
|
Task 9: User Flagging System:
|
||||||
|
- Added needsFollowup checkbox column to RegistrationsTable
|
||||||
|
- Added red ring around rows when needsFollowup is true
|
||||||
|
|
||||||
|
Task 10: Sidebar Reorganization with logical sections:
|
||||||
|
- Quick Access: Profil, Dashboard, Chat, Kalendár
|
||||||
|
- Work Management: Úlohy, Sledovanie času, Výkazy
|
||||||
|
- CRM: Firmy, Projekty, Kontakty
|
||||||
|
- Communication: Emaily, Doručené
|
||||||
|
- Services: Služby
|
||||||
|
- Admin: Používatelia, História akcií, Všetky kurzy
|
||||||
|
|
||||||
|
---
|
||||||
|
Next Steps:
|
||||||
|
|
||||||
|
1. Run npm run db:push in crm-server to apply database changes
|
||||||
|
2. Test all features
|
||||||
|
3. Commit changes when ready
|
||||||
|
|
||||||
|
- i cant see online users there is not any green indicator and should be there
|
||||||
|
- i wanted to search comapny by her title,number , email address or website linked to company
|
||||||
|
now when i type phone nuber for exasmle it not display any company
|
||||||
|
- badge was added to kalendar button in Sidebar but i want to show number for whole day todos,reminders,dostupnost not only meetings/events
|
||||||
|
- i cant see the new role team_leader why ?? i cant create user with that role or edit role to team team_leader
|
||||||
|
-
|
||||||
|
|||||||
Reference in New Issue
Block a user