refactor: Centralize PDF styling with COLORS and FONT_SIZES and enhance module content and dynamic title generation.

This commit is contained in:
2026-02-03 22:35:01 +01:00
parent 38f2b871b9
commit ce421eb8d2
13 changed files with 346 additions and 254 deletions

View File

@@ -1,23 +1,51 @@
'use client';
import * as React from 'react';
import {
Text as PDFText,
View as PDFView,
StyleSheet as PDFStyleSheet,
Image as PDFImage
} from '@react-pdf/renderer';
import { View as PDFView, Text as PDFText, StyleSheet, Link as PDFLink, Image as PDFImage, Font } from '@react-pdf/renderer';
export const pdfStyles = PDFStyleSheet.create({
// INDUSTRIAL DESIGN SYSTEM TOKENS
export const COLORS = {
CHARCOAL: '#0f172a', // Slate 900
TEXT_MAIN: '#334155', // Slate 700
TEXT_DIM: '#64748b', // Slate 500
TEXT_LIGHT: '#94a3b8', // Slate 400
DIVIDER: '#cbd5e1', // Slate 300
GRID: '#f1f5f9', // Slate 100
BLUEPRINT: '#e2e8f0', // Slate 200
WHITE: '#ffffff'
};
export const FONT_SIZES = {
H1: 24,
H2: 18,
H3: 12,
BODY: 9,
TINY: 7,
SUB: 8,
BLUEPRINT: 5
};
// Register a more technical font if possible, or use Helvetica with varying weights
// Note: helvetica-bold is standard in react-pdf
export const pdfStyles = StyleSheet.create({
page: {
paddingTop: 45, // DIN 5008
paddingLeft: 70, // ~25mm
paddingRight: 57, // ~20mm
paddingBottom: 80, // Safe buffer for absolute footer
backgroundColor: '#ffffff',
backgroundColor: COLORS.WHITE,
fontFamily: 'Helvetica',
fontSize: 10,
color: '#000000',
fontSize: FONT_SIZES.BODY,
color: COLORS.CHARCOAL,
},
titlePage: {
width: '100%',
height: '100%',
backgroundColor: COLORS.WHITE,
fontFamily: 'Helvetica',
color: COLORS.CHARCOAL,
padding: 0, // NO PADDING to prevent inner overflow page breaks
},
header: {
flexDirection: 'row',
@@ -31,13 +59,13 @@ export const pdfStyles = PDFStyleSheet.create({
marginTop: 45, // DIN 5008 positioning for window
},
senderLine: {
fontSize: 7,
fontSize: FONT_SIZES.TINY,
textDecoration: 'underline',
color: '#666666',
color: COLORS.TEXT_DIM,
marginBottom: 8,
},
recipientAddress: {
fontSize: 10,
fontSize: FONT_SIZES.BODY,
lineHeight: 1.4,
},
brandLogoContainer: {
@@ -47,14 +75,14 @@ export const pdfStyles = PDFStyleSheet.create({
brandIconContainer: {
width: 40,
height: 40,
backgroundColor: '#000000',
backgroundColor: '#0f172a',
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 12,
},
brandIconText: {
color: '#ffffff',
color: COLORS.WHITE,
fontSize: 20,
fontWeight: 'bold',
},
@@ -62,27 +90,27 @@ export const pdfStyles = PDFStyleSheet.create({
marginBottom: 24,
},
mainTitle: {
fontSize: 12,
fontSize: FONT_SIZES.H3,
fontWeight: 'bold',
marginBottom: 4,
color: '#000000',
color: COLORS.CHARCOAL,
textTransform: 'uppercase',
letterSpacing: 1,
},
subTitle: {
fontSize: 9,
color: '#666666',
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_DIM,
marginTop: 2,
},
section: {
marginBottom: 32,
},
sectionTitle: {
fontSize: 8,
fontSize: FONT_SIZES.SUB,
fontWeight: 'bold',
textTransform: 'uppercase',
letterSpacing: 1,
color: '#999999',
color: COLORS.TEXT_LIGHT,
marginBottom: 8,
},
footer: {
@@ -91,7 +119,7 @@ export const pdfStyles = PDFStyleSheet.create({
left: 70,
right: 57,
borderTopWidth: 1,
borderTopColor: '#f1f5f9',
borderTopColor: COLORS.GRID,
paddingTop: 16,
flexDirection: 'row',
justifyContent: 'space-between',
@@ -108,17 +136,17 @@ export const pdfStyles = PDFStyleSheet.create({
marginBottom: 8,
},
footerText: {
fontSize: 7,
color: '#94a3b8',
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_LIGHT,
lineHeight: 1.5,
},
footerLabel: {
fontWeight: 'bold',
color: '#64748b',
color: COLORS.TEXT_DIM,
},
pageNumber: {
fontSize: 7,
color: '#cbd5e1',
fontSize: FONT_SIZES.TINY,
color: COLORS.DIVIDER,
fontWeight: 'bold',
marginTop: 8,
textAlign: 'right',
@@ -128,14 +156,41 @@ export const pdfStyles = PDFStyleSheet.create({
left: 20,
width: 10,
borderTopWidth: 0.5,
borderTopColor: '#cbd5e1',
borderTopColor: COLORS.DIVIDER,
},
divider: {
width: '100%',
height: 1,
backgroundColor: '#f1f5f9',
backgroundColor: COLORS.DIVIDER,
marginVertical: 12,
},
blueprintGrid: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: -10,
},
gridLineH: {
width: '100%',
height: 0.5,
backgroundColor: COLORS.GRID,
position: 'absolute',
},
gridLineV: {
width: 0.5,
height: '100%',
backgroundColor: COLORS.GRID,
position: 'absolute',
},
technicalMarker: {
position: 'absolute',
fontSize: FONT_SIZES.BLUEPRINT,
color: COLORS.BLUEPRINT,
fontFamily: 'Helvetica',
letterSpacing: 1,
},
// Atoms
industrialListItem: {
flexDirection: 'row',
@@ -145,47 +200,47 @@ export const pdfStyles = PDFStyleSheet.create({
industrialBulletBox: {
width: 6,
height: 6,
backgroundColor: '#0f172a',
backgroundColor: COLORS.CHARCOAL,
marginRight: 8,
marginTop: 5,
},
industrialTitle: {
fontSize: 24,
fontSize: FONT_SIZES.H1,
fontWeight: 'bold',
color: '#0f172a',
color: COLORS.CHARCOAL,
marginBottom: 6,
letterSpacing: -0.5,
letterSpacing: 0, // Reset for clarity
},
industrialSubtitle: {
fontSize: 8,
fontSize: FONT_SIZES.SUB,
fontWeight: 'bold',
color: '#94a3b8',
color: COLORS.TEXT_LIGHT,
textTransform: 'uppercase',
marginBottom: 16,
letterSpacing: 2,
},
industrialTextLead: {
fontSize: 10,
color: '#334155',
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_MAIN,
lineHeight: 1.6,
marginBottom: 12,
},
industrialText: {
fontSize: 9,
color: '#64748b',
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_DIM,
lineHeight: 1.6,
marginBottom: 8,
},
industrialCard: {
padding: 16,
borderWidth: 1,
borderColor: '#e2e8f0',
borderColor: COLORS.BLUEPRINT,
marginBottom: 12,
},
industrialCardTitle: {
fontSize: 10,
fontSize: FONT_SIZES.BODY + 1, // 10
fontWeight: 'bold',
color: '#0f172a',
color: COLORS.CHARCOAL,
marginBottom: 4,
textTransform: 'uppercase',
letterSpacing: 0.5,
@@ -193,22 +248,30 @@ export const pdfStyles = PDFStyleSheet.create({
darkBox: {
marginTop: 32,
padding: 24,
backgroundColor: '#0f172a',
color: '#ffffff',
backgroundColor: COLORS.CHARCOAL,
color: COLORS.WHITE,
},
darkTitle: {
fontSize: 18,
fontSize: FONT_SIZES.H2,
fontWeight: 'bold',
color: '#ffffff',
color: COLORS.WHITE,
marginBottom: 8,
},
darkText: {
fontSize: 9,
color: '#94a3b8',
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_LIGHT,
lineHeight: 1.6,
},
});
const styles = pdfStyles;
export const BlueprintBackground = () => (
<PDFView style={styles.blueprintGrid} fixed>
{/* Clean background - grid lines removed per user request */}
</PDFView>
);
export const IndustrialListItem = ({ children }: { children: React.ReactNode }) => (
<PDFView style={pdfStyles.industrialListItem}>
<PDFView style={pdfStyles.industrialBulletBox} />
@@ -238,5 +301,5 @@ export const Header = ({ sender, recipient, icon, showAddress = true }: { sender
);
export const DocumentTitle = ({ title, subLines }: { title: string; subLines?: string[] }) => (
<PDFView style={pdfStyles.titleInfo}><PDFText style={pdfStyles.mainTitle}>{title}</PDFText>{subLines?.map((line, i) => (<PDFText key={i} style={[pdfStyles.subTitle, i === 1 ? { fontWeight: 'bold', color: '#000000' } : {}]}>{line}</PDFText>))}</PDFView>
<PDFView style={pdfStyles.titleInfo}><PDFText style={pdfStyles.mainTitle}>{title}</PDFText>{subLines?.map((line, i) => (<PDFText key={i} style={[pdfStyles.subTitle, i === 1 ? { fontWeight: 'bold', color: '#0f172a' } : {}]}>{line}</PDFText>))}</PDFView>
);