Initial commit: IT Nexus Web-App

This commit is contained in:
2026-06-01 20:49:07 +02:00
commit 8023765e6c
387 changed files with 106900 additions and 0 deletions

View File

@@ -0,0 +1,345 @@
const PDFDocument = require('pdfkit');
const fs = require('fs');
const path = require('path');
class PDFGenerator {
constructor() {
this.doc = new PDFDocument({
size: 'A4',
margins: {
top: 50,
bottom: 50,
left: 50,
right: 50
}
});
this.currentY = 50;
}
/**
* Add Cereda Systems header with logo
*/
addHeader(title, subtitle = '') {
// Company name
this.doc
.fontSize(20)
.fillColor('#3fa3a3')
.font('Helvetica-Bold')
.text('Cereda Systems GmbH', { align: 'center' });
this.currentY = this.doc.y + 10;
// Title
this.doc
.fontSize(16)
.fillColor('#1e293b')
.font('Helvetica-Bold')
.text(title, { align: 'center' });
this.currentY = this.doc.y + 5;
// Subtitle
if (subtitle) {
this.doc
.fontSize(10)
.fillColor('#64748b')
.font('Helvetica')
.text(subtitle, { align: 'center' });
this.currentY = this.doc.y + 5;
}
// Horizontal line
this.doc
.moveTo(50, this.currentY + 10)
.lineTo(545, this.currentY + 10)
.strokeColor('#3fa3a3')
.lineWidth(2)
.stroke();
this.currentY += 30;
this.doc.y = this.currentY;
return this;
}
/**
* Add section title
*/
addSectionTitle(title) {
this.currentY = this.doc.y + 10;
this.doc
.fontSize(14)
.fillColor('#1e293b')
.font('Helvetica-Bold')
.text(title, 50, this.currentY);
this.currentY = this.doc.y + 10;
this.doc.y = this.currentY;
return this;
}
/**
* Add text paragraph
*/
addText(text, options = {}) {
const defaultOptions = {
fontSize: 10,
color: '#334155',
font: 'Helvetica',
align: 'left'
};
const opts = { ...defaultOptions, ...options };
this.doc
.fontSize(opts.fontSize)
.fillColor(opts.color)
.font(opts.font)
.text(text, {
align: opts.align,
continued: opts.continued || false
});
this.currentY = this.doc.y + 5;
return this;
}
/**
* Add key-value pair
*/
addKeyValue(key, value, options = {}) {
const x = options.x || 50;
const y = this.currentY;
this.doc
.fontSize(10)
.fillColor('#64748b')
.font('Helvetica-Bold')
.text(`${key}:`, x, y, { continued: true, width: 150 })
.fillColor('#1e293b')
.font('Helvetica')
.text(` ${value || '-'}`);
this.currentY = this.doc.y + 5;
return this;
}
/**
* Add table
*/
addTable(headers, rows, options = {}) {
const startX = options.x || 50;
const startY = this.currentY;
const columnWidth = options.columnWidth || 120;
const rowHeight = options.rowHeight || 25;
// Draw header
this.doc
.fontSize(9)
.fillColor('#ffffff')
.font('Helvetica-Bold');
headers.forEach((header, index) => {
const x = startX + (index * columnWidth);
// Header background
this.doc
.rect(x, startY, columnWidth, rowHeight)
.fillAndStroke('#3fa3a3', '#3fa3a3');
// Header text
this.doc
.fillColor('#ffffff')
.text(header, x + 5, startY + 8, {
width: columnWidth - 10,
ellipsis: true
});
});
// Draw rows
rows.forEach((row, rowIndex) => {
const y = startY + rowHeight + (rowIndex * rowHeight);
this.doc
.fontSize(9)
.fillColor('#1e293b')
.font('Helvetica');
row.forEach((cell, colIndex) => {
const x = startX + (colIndex * columnWidth);
// Row background (alternating)
this.doc
.rect(x, y, columnWidth, rowHeight)
.fillAndStroke(rowIndex % 2 === 0 ? '#f8fafc' : '#ffffff', '#e2e8f0');
// Cell text
this.doc
.fillColor('#1e293b')
.text(cell || '-', x + 5, y + 8, {
width: columnWidth - 10,
ellipsis: true
});
});
});
this.currentY = startY + rowHeight + (rows.length * rowHeight) + 20;
this.doc.y = this.currentY;
return this;
}
/**
* Add checklist
*/
addChecklist(items, checkedItems = {}) {
this.currentY = this.doc.y + 10;
items.forEach((item) => {
const isChecked = checkedItems[item.key] || false;
const checkbox = isChecked ? '[X]' : '[ ]';
this.doc
.fontSize(10)
.fillColor(isChecked ? '#10b981' : '#64748b')
.font('Helvetica-Bold')
.text(checkbox, 50, this.currentY, { continued: true })
.font('Helvetica')
.fillColor('#1e293b')
.text(' ' + item.label);
this.currentY = this.doc.y + 8;
});
this.currentY += 10;
this.doc.y = this.currentY;
return this;
}
/**
* Add signature fields
*/
addSignatureFields(fields) {
this.currentY = this.doc.y + 20;
const fieldWidth = 200;
const spacing = (545 - 50 - (fields.length * fieldWidth)) / (fields.length - 1);
fields.forEach((field, index) => {
const x = 50 + (index * (fieldWidth + spacing));
// Signature line
this.doc
.moveTo(x, this.currentY + 40)
.lineTo(x + fieldWidth, this.currentY + 40)
.strokeColor('#94a3b8')
.lineWidth(1)
.stroke();
// Field label
this.doc
.fontSize(9)
.fillColor('#64748b')
.font('Helvetica')
.text(field.label, x, this.currentY + 45, {
width: fieldWidth,
align: 'center'
});
// Date field
this.doc
.fontSize(8)
.fillColor('#94a3b8')
.text('Datum: _______________', x, this.currentY + 60, {
width: fieldWidth,
align: 'center'
});
});
this.currentY += 100;
this.doc.y = this.currentY;
return this;
}
/**
* Add footer
*/
addFooter(text) {
const bottomY = 800;
// Horizontal line
this.doc
.moveTo(50, bottomY - 20)
.lineTo(545, bottomY - 20)
.strokeColor('#e2e8f0')
.lineWidth(1)
.stroke();
// Footer text
this.doc
.fontSize(8)
.fillColor('#94a3b8')
.font('Helvetica')
.text(text, 50, bottomY - 10, {
align: 'center'
});
return this;
}
/**
* Add page break
*/
addPageBreak() {
this.doc.addPage();
this.currentY = 50;
this.doc.y = this.currentY;
return this;
}
/**
* Save PDF to file
*/
async saveToFile(filePath) {
return new Promise((resolve, reject) => {
// Ensure directory exists
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const stream = fs.createWriteStream(filePath);
this.doc.pipe(stream);
this.doc.end();
stream.on('finish', () => resolve(filePath));
stream.on('error', reject);
});
}
/**
* Get PDF as buffer
*/
async getBuffer() {
return new Promise((resolve, reject) => {
const buffers = [];
this.doc.on('data', buffers.push.bind(buffers));
this.doc.on('end', () => resolve(Buffer.concat(buffers)));
this.doc.on('error', reject);
this.doc.end();
});
}
}
module.exports = PDFGenerator;