feat(pdf): implement premium industrial layout and integrate brand logo
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 1m18s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-04-20 19:32:58 +02:00
parent c3068f23e7
commit 666bcf0b9d
2 changed files with 105 additions and 69 deletions

View File

@@ -1,5 +1,5 @@
import * as React from 'react'; import { Document, Page, View, Text, StyleSheet, Font, Link, Image } from '@react-pdf/renderer';
import { Document, Page, View, Text, StyleSheet, Font, Link } from '@react-pdf/renderer'; import path from 'path';
// Standard fonts like Helvetica are built-in to PDF and don't require registration // Standard fonts like Helvetica are built-in to PDF and don't require registration
// unless we want to use specific TTF files. Using built-in Helvetica for maximum stability. // unless we want to use specific TTF files. Using built-in Helvetica for maximum stability.
@@ -24,67 +24,65 @@ const MARGIN = 72;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
page: { page: {
color: C.gray900, color: C.gray900,
lineHeight: 1.5, lineHeight: 1.6,
backgroundColor: C.white, backgroundColor: C.white,
paddingTop: 0, paddingTop: 0,
paddingBottom: 100, paddingBottom: 80,
fontFamily: 'Helvetica', fontFamily: 'Helvetica',
}, },
// Hero-style header // Premium Header Layout
hero: { hero: {
backgroundColor: C.white, backgroundColor: C.offWhite,
paddingTop: 24, paddingTop: 40,
paddingBottom: 20, paddingBottom: 32,
paddingHorizontal: MARGIN, paddingHorizontal: MARGIN,
marginBottom: 20, marginBottom: 40,
position: 'relative',
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: C.gray200, borderBottomColor: C.gray200,
position: 'relative',
}, },
header: { headerTop: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
marginBottom: 16, marginBottom: 32,
}, },
logoText: { logo: {
fontSize: 24, width: 60,
fontWeight: 700, height: 'auto',
color: C.navyDeep,
letterSpacing: 1,
textTransform: 'uppercase',
}, },
docTitle: { docType: {
fontSize: 10, fontSize: 9,
fontWeight: 700, fontWeight: 700,
color: C.navy, color: C.navy,
letterSpacing: 2, letterSpacing: 2,
textTransform: 'uppercase', textTransform: 'uppercase',
opacity: 0.6,
}, },
productHero: { headerTitleArea: {
marginTop: 0, marginTop: 0,
}, },
pageTitle: { pageTitle: {
fontSize: 24, fontSize: 28,
fontWeight: 700, fontWeight: 700,
color: C.navyDeep, color: C.navyDeep,
marginBottom: 0, marginBottom: 4,
textTransform: 'uppercase', textTransform: 'uppercase',
letterSpacing: -0.5, letterSpacing: -0.5,
}, },
accentBar: { accentBar: {
width: 30, width: 40,
height: 3, height: 4,
backgroundColor: C.accent, backgroundColor: C.accent,
marginTop: 8, marginTop: 12,
borderRadius: 1.5, borderRadius: 2,
}, },
// Content Area // Content Area
@@ -92,89 +90,103 @@ const styles = StyleSheet.create({
paddingHorizontal: MARGIN, paddingHorizontal: MARGIN,
}, },
// Lexical Elements // Lexical Elements with high-fidelity formatting
paragraph: { paragraph: {
fontSize: 10, fontSize: 10,
color: C.gray600, color: C.gray600,
lineHeight: 1.7, lineHeight: 1.7,
marginBottom: 12, marginBottom: 14,
textAlign: 'justify',
}, },
heading1: { heading1: {
fontSize: 16, fontSize: 14,
fontWeight: 700, fontWeight: 700,
color: C.navyDeep, color: C.navyDeep,
marginTop: 20, marginTop: 24,
marginBottom: 12,
textTransform: 'uppercase',
letterSpacing: 0.8,
borderLeftWidth: 3,
borderLeftColor: C.accent,
paddingLeft: 12,
},
heading2: {
fontSize: 11,
fontWeight: 700,
color: C.navyDeep,
marginTop: 18,
marginBottom: 10, marginBottom: 10,
textTransform: 'uppercase', textTransform: 'uppercase',
letterSpacing: 0.5, letterSpacing: 0.5,
}, },
heading2: {
fontSize: 12,
fontWeight: 700,
color: C.navyDeep,
marginTop: 16,
marginBottom: 8,
},
heading3: { heading3: {
fontSize: 10, fontSize: 10,
fontWeight: 700, fontWeight: 700,
color: C.navyDeep, color: C.navyDeep,
marginTop: 12, marginTop: 14,
marginBottom: 6, marginBottom: 8,
}, },
list: { list: {
marginBottom: 12, marginBottom: 16,
marginLeft: 8, marginLeft: 4,
}, },
listItem: { listItem: {
flexDirection: 'row', flexDirection: 'row',
marginBottom: 4, marginBottom: 6,
}, },
listItemBullet: { listItemBullet: {
width: 12, width: 20,
fontSize: 10, fontSize: 10,
color: C.accent, color: C.gray400,
fontWeight: 700, fontWeight: 700,
}, },
listItemContent: { listItemContent: {
flex: 1, flex: 1,
fontSize: 10, fontSize: 10,
color: C.gray600, color: C.gray600,
lineHeight: 1.7, lineHeight: 1.7,
textAlign: 'justify',
}, },
link: { link: {
color: C.accent, color: C.navy,
textDecoration: 'none', textDecoration: 'underline',
}, },
textBold: { textBold: {
fontWeight: 700, fontWeight: 700,
fontFamily: 'Helvetica-Bold', fontFamily: 'Helvetica-Bold',
color: C.navyDeep, color: C.navyDeep,
}, },
textItalic: { textItalic: {
fontStyle: 'italic', fontStyle: 'italic',
}, },
// Footer — matches brochure style // Industrial Footer
footer: { footer: {
position: 'absolute', position: 'absolute',
bottom: 40, bottom: 30,
left: MARGIN, left: MARGIN,
right: MARGIN, right: MARGIN,
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'flex-end',
paddingTop: 24, paddingTop: 16,
borderTopWidth: 1, borderTopWidth: 0.5,
borderTopColor: C.gray200, borderTopColor: C.gray200,
}, },
footerText: { footerInfo: {
fontSize: 8, flexDirection: 'column',
color: C.gray400, gap: 2,
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: 1,
}, },
footerBrand: { footerBrand: {
@@ -183,6 +195,20 @@ const styles = StyleSheet.create({
color: C.navyDeep, color: C.navyDeep,
textTransform: 'uppercase', textTransform: 'uppercase',
letterSpacing: 1, letterSpacing: 1,
marginBottom: 2,
},
footerText: {
fontSize: 7,
color: C.gray400,
fontWeight: 500,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
pageNum: {
fontSize: 8,
color: C.gray400,
}, },
}); });
@@ -291,19 +317,21 @@ export const PDFPage: React.FC<PDFPageProps> = ({ page, locale = 'de' }) => {
day: 'numeric', day: 'numeric',
}); });
const logoPath = path.join(process.cwd(), 'public/logo.png');
return ( return (
<Document> <Document>
<Page size="A4" style={styles.page}> <Page size="A4" style={styles.page}>
{/* Hero Header */} {/* Improved Hero Header */}
<View style={styles.hero} fixed> <View style={styles.hero} fixed>
<View style={styles.header}> <View style={styles.headerTop}>
<View> <Image src={logoPath} style={styles.logo} />
<Text style={styles.logoText}>KLZ</Text> <Text style={styles.docType}>
</View> {locale === 'en' ? 'Official Document' : 'Offizielles Dokument'}
<Text style={styles.docTitle}>{locale === 'en' ? 'Document' : 'Dokument'}</Text> </Text>
</View> </View>
<View style={styles.productHero}> <View style={styles.headerTitleArea}>
<Text style={styles.pageTitle}>{page.title}</Text> <Text style={styles.pageTitle}>{page.title}</Text>
<View style={styles.accentBar} /> <View style={styles.accentBar} />
</View> </View>
@@ -317,10 +345,18 @@ export const PDFPage: React.FC<PDFPageProps> = ({ page, locale = 'de' }) => {
</View> </View>
</View> </View>
{/* Minimal footer */} {/* Industrial footer with page numbers */}
<View style={styles.footer} fixed> <View style={styles.footer} fixed>
<Text style={styles.footerBrand}>KLZ CABLES</Text> <View style={styles.footerInfo}>
<Text style={styles.footerText}>{dateStr}</Text> <Text style={styles.footerBrand}>KLZ VERTRIEBS GMBH</Text>
<Text style={styles.footerText}>
RAIFFEISENSTRASSE 22 73630 REMSHALDEN {dateStr}
</Text>
</View>
<Text
style={styles.pageNum}
render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`}
/>
</View> </View>
</Page> </Page>
</Document> </Document>

View File

@@ -139,7 +139,7 @@
"prepare": "husky", "prepare": "husky",
"preinstall": "npx only-allow pnpm" "preinstall": "npx only-allow pnpm"
}, },
"version": "2.3.18-rc.1", "version": "2.3.18-rc.2",
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",