This commit is contained in:
1 parent
0acd1b4da6
commit
194291bd70
1 file changed
+168
@@ -0,0 +1,168 @@
|
||||
import { jsPDF } from "jspdf";
|
||||
|
||||
export interface YearPdfMonth {
|
||||
title: string;
|
||||
vacation: string;
|
||||
overtime: string;
|
||||
}
|
||||
|
||||
export interface YearPdfVacation {
|
||||
range: string;
|
||||
days: string;
|
||||
}
|
||||
|
||||
export interface YearPdfOvertime {
|
||||
date: string;
|
||||
detail: string;
|
||||
}
|
||||
|
||||
export interface YearPdfData {
|
||||
year: number;
|
||||
createdAt: string;
|
||||
entitlement: string;
|
||||
used: string;
|
||||
remaining: string;
|
||||
months: YearPdfMonth[];
|
||||
vacations: YearPdfVacation[];
|
||||
overtimeEntries: YearPdfOvertime[];
|
||||
}
|
||||
|
||||
const HARBOR: [number, number, number] = [24, 56, 75];
|
||||
const SEA: [number, number, number] = [22, 131, 123];
|
||||
const MUTED: [number, number, number] = [90, 109, 118];
|
||||
const LIGHT_LINE: [number, number, number] = [214, 225, 229];
|
||||
const WHITE: [number, number, number] = [255, 255, 255];
|
||||
const BLACK: [number, number, number] = [0, 0, 0];
|
||||
const LEFT = 20;
|
||||
const RIGHT = 190;
|
||||
const TOP = 20;
|
||||
const BOTTOM = 282;
|
||||
const LINE = 8;
|
||||
|
||||
interface Layout {
|
||||
doc: jsPDF;
|
||||
data: YearPdfData;
|
||||
page: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export function downloadYearPdf(data: YearPdfData): void {
|
||||
const layout: Layout = { doc: new jsPDF(), data, page: 1, y: 0 };
|
||||
writeHeader(layout);
|
||||
writeBalances(layout);
|
||||
writeMonthTable(layout);
|
||||
writeSection(
|
||||
layout,
|
||||
"Urlaubsbuchungen",
|
||||
data.vacations.map((entry) => `${entry.range} · ${entry.days}`),
|
||||
);
|
||||
writeSection(
|
||||
layout,
|
||||
"Überstundenbuchungen",
|
||||
data.overtimeEntries.map((entry) => `${entry.date} · ${entry.detail}`),
|
||||
);
|
||||
footer(layout);
|
||||
layout.doc.save(`jahresuebersicht-${data.year}.pdf`);
|
||||
}
|
||||
|
||||
function footer(layout: Layout): void {
|
||||
const { doc, data, page } = layout;
|
||||
doc.setFontSize(9);
|
||||
doc.setTextColor(...MUTED);
|
||||
doc.text(`Stand: ${data.createdAt} · Seite ${page}`, LEFT, 292);
|
||||
doc.setTextColor(...BLACK);
|
||||
}
|
||||
|
||||
function breakPage(layout: Layout): void {
|
||||
footer(layout);
|
||||
layout.doc.addPage();
|
||||
layout.page += 1;
|
||||
layout.y = TOP;
|
||||
}
|
||||
|
||||
function writeHeader(layout: Layout): void {
|
||||
const { doc, data } = layout;
|
||||
doc.setFillColor(...HARBOR);
|
||||
doc.rect(0, 0, 210, 30, "F");
|
||||
doc.setTextColor(...WHITE);
|
||||
doc.setFontSize(18);
|
||||
doc.text(`Jahresübersicht ${data.year}`, LEFT, 18);
|
||||
doc.setTextColor(...BLACK);
|
||||
layout.y = 42;
|
||||
}
|
||||
|
||||
function writeBalances(layout: Layout): void {
|
||||
const { doc, data } = layout;
|
||||
let { y } = layout;
|
||||
doc.setFontSize(11);
|
||||
doc.setDrawColor(...LIGHT_LINE);
|
||||
doc.line(LEFT, y - 5, RIGHT, y - 5);
|
||||
doc.text(`Anspruch: ${data.entitlement}`, LEFT, y);
|
||||
doc.text(`Verbraucht: ${data.used}`, 80, y);
|
||||
doc.setTextColor(...SEA);
|
||||
doc.text(`Rest: ${data.remaining}`, 145, y);
|
||||
doc.setTextColor(...BLACK);
|
||||
layout.y = y + LINE * 2;
|
||||
}
|
||||
|
||||
function writeMonthTable(layout: Layout): void {
|
||||
const { doc, data } = layout;
|
||||
let { y } = layout;
|
||||
doc.setFontSize(12);
|
||||
doc.text("Monat", LEFT, y);
|
||||
doc.text("Urlaub", 90, y);
|
||||
doc.text("Überstunden", 140, y);
|
||||
y += 2;
|
||||
doc.setDrawColor(...HARBOR);
|
||||
doc.line(LEFT, y, RIGHT, y);
|
||||
y += LINE;
|
||||
doc.setFontSize(11);
|
||||
doc.setDrawColor(...LIGHT_LINE);
|
||||
for (const month of data.months) {
|
||||
if (y > BOTTOM) {
|
||||
layout.y = y;
|
||||
breakPage(layout);
|
||||
y = TOP;
|
||||
}
|
||||
doc.text(month.title, LEFT, y);
|
||||
doc.text(month.vacation, 90, y);
|
||||
doc.text(month.overtime, 140, y);
|
||||
y += 2;
|
||||
doc.line(LEFT, y, RIGHT, y);
|
||||
y += LINE - 2;
|
||||
}
|
||||
layout.y = y + LINE;
|
||||
}
|
||||
|
||||
function writeSection(
|
||||
layout: Layout,
|
||||
title: string,
|
||||
lines: string[],
|
||||
): void {
|
||||
if (lines.length === 0) {
|
||||
return;
|
||||
}
|
||||
const { doc } = layout;
|
||||
let { y } = layout;
|
||||
if (y > BOTTOM - 20) {
|
||||
layout.y = y;
|
||||
breakPage(layout);
|
||||
y = TOP;
|
||||
}
|
||||
doc.setFontSize(13);
|
||||
doc.text(title, LEFT, y);
|
||||
y += LINE;
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(...MUTED);
|
||||
for (const line of lines) {
|
||||
if (y > BOTTOM) {
|
||||
layout.y = y;
|
||||
breakPage(layout);
|
||||
y = TOP;
|
||||
}
|
||||
doc.text(`• ${line}`, LEFT, y);
|
||||
y += LINE - 2;
|
||||
}
|
||||
doc.setTextColor(...BLACK);
|
||||
layout.y = y + LINE;
|
||||
}
|
||||
Reference in new issue
Block a user