refactor: industrial vector redesign of business cards
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 54s
Build & Deploy / 🏗️ Build (push) Successful in 1m55s
Build & Deploy / 🚀 Deploy (push) Successful in 14s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s

- Uses raw SVG paths for the logo instead of PNG

- Implements Dark Navy, Grid, Accent Green project styling

- Respects draft layout rules.
This commit is contained in:
2026-06-09 11:28:27 +02:00
parent 1f07999d48
commit 2171f471f2
3 changed files with 329 additions and 67 deletions

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Document, Page, Text, View, StyleSheet, Font, Image } from '@react-pdf/renderer';
import { Document, Page, Text, View, StyleSheet, Svg, Line, Image } from '@react-pdf/renderer';
import { KLZLogoVector } from './KLZLogoVector';
export interface PersonData {
name: string;
@@ -10,112 +11,147 @@ export interface PersonData {
interface PDFBusinessCardProps {
person: PersonData;
logoWhitePath: string;
logoBluePath: string;
logoFullPath: string;
truckImagePath: string;
qrCodeDataUrl: string;
}
// Mintel Aesthetics Colors
const COLORS = {
navy: '#001a4d',
navyLight: '#002870',
green: '#82ed20',
white: '#ffffff',
grayText: '#8899aa',
darkText: '#000000',
lightGray: '#f0f0f0',
};
const styles = StyleSheet.create({
page: {
width: '85mm',
height: '55mm',
backgroundColor: '#ffffff',
position: 'relative',
fontFamily: 'Helvetica',
},
pageBleed: {
// 85x55mm + 3mm bleed on each side = 91x61mm
width: '91mm',
height: '61mm',
backgroundColor: '#ffffff',
backgroundColor: COLORS.navy,
position: 'relative',
fontFamily: 'Helvetica',
overflow: 'hidden',
},
// FRONT PAGE
pageBleedBack: {
width: '91mm',
height: '61mm',
backgroundColor: COLORS.white,
position: 'relative',
fontFamily: 'Helvetica',
overflow: 'hidden',
},
// -- FRONT --
frontContainer: {
flex: 1,
position: 'relative',
padding: '8mm',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
frontLogo: {
frontLogoPos: {
position: 'absolute',
top: '5mm',
right: '5mm',
width: '25mm',
top: '8mm',
right: '8mm',
},
truckImage: {
frontGraphicContainer: {
position: 'absolute',
bottom: '-2mm',
left: '-5mm',
width: '100mm', // cover bleed width
height: '50mm',
objectFit: 'contain',
bottom: '-3mm',
left: '-3mm',
right: '-3mm',
height: '30mm',
backgroundColor: COLORS.navyLight,
borderTopWidth: 2,
borderTopColor: COLORS.green,
},
// BACK PAGE
frontTagline: {
position: 'absolute',
bottom: '8mm',
left: '8mm',
color: COLORS.green,
fontSize: 10,
fontWeight: 'bold',
letterSpacing: 1.5,
},
gridLine: {
position: 'absolute',
backgroundColor: 'rgba(255,255,255,0.05)',
},
// -- BACK --
backContainer: {
flex: 1,
position: 'relative',
padding: '6mm 6mm',
},
backLogo: {
width: '20mm',
marginBottom: '8mm',
marginBottom: '6mm',
},
backMiddle: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '8mm',
width: '100%',
},
nameSection: {
flexDirection: 'column',
width: '35%',
},
nameText: {
fontSize: 16,
fontWeight: 700,
color: '#000000',
fontSize: 14,
fontWeight: 'bold',
color: COLORS.navy,
marginBottom: 2,
},
roleText: {
fontSize: 8,
fontWeight: 400,
color: '#333333',
fontSize: 7.5,
fontWeight: 'normal',
color: COLORS.grayText,
letterSpacing: 0.5,
},
contactSection: {
flexDirection: 'column',
width: '45%',
paddingLeft: '2mm',
borderLeftWidth: 1,
borderLeftColor: COLORS.lightGray,
},
contactRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 3,
marginBottom: 4,
},
iconCircle: {
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: '#82ed20',
justifyContent: 'center',
alignItems: 'center',
marginRight: 4,
width: 10,
height: 10,
borderRadius: 5,
backgroundColor: COLORS.green,
marginRight: 5,
},
contactText: {
fontSize: 6.5,
fontWeight: 500,
color: '#000000',
color: COLORS.navy,
},
qrSection: {
width: '20%',
alignItems: 'flex-end',
},
qrCodeWrapper: {
width: '16mm',
height: '16mm',
padding: 2,
backgroundColor: COLORS.white,
borderWidth: 1,
borderColor: COLORS.lightGray,
},
qrCode: {
width: '18mm',
height: '18mm',
width: '100%',
height: '100%',
},
footer: {
position: 'absolute',
@@ -123,49 +159,99 @@ const styles = StyleSheet.create({
left: '6mm',
right: '6mm',
flexDirection: 'row',
justifyContent: 'center',
justifyContent: 'flex-start',
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: COLORS.lightGray,
paddingTop: '2mm',
},
footerText: {
fontSize: 6.5,
fontWeight: 500,
color: '#000000',
fontSize: 6,
color: COLORS.grayText,
letterSpacing: 0.5,
},
pipe: {
fontSize: 6.5,
fontWeight: 500,
color: '#82ed20',
marginHorizontal: 3,
fontSize: 6,
color: COLORS.green,
marginHorizontal: 4,
fontWeight: 'bold',
},
greenBar: {
position: 'absolute',
top: 0,
left: 0,
width: '3mm',
height: '61mm',
backgroundColor: COLORS.green,
},
});
export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
person,
logoFullPath,
truckImagePath,
qrCodeDataUrl,
}) => {
// A simple technical grid background for the industrial aesthetic
const IndustrialGrid = () => (
<Svg
viewBox="0 0 910 610"
style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, opacity: 0.1 }}
>
{Array.from({ length: 10 }).map((_, i) => (
<Line
key={`h-${i}`}
x1="0"
y1={i * 61}
x2="910"
y2={i * 61}
stroke={COLORS.white}
strokeWidth={1}
/>
))}
{Array.from({ length: 15 }).map((_, i) => (
<Line
key={`v-${i}`}
x1={i * 61}
y1="0"
x2={i * 61}
y2="610"
stroke={COLORS.white}
strokeWidth={1}
/>
))}
</Svg>
);
export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({ person, qrCodeDataUrl }) => {
return (
<Document
title={`Business Card - ${person.name}`}
author="KLZ Cables"
creator="KLZ React-PDF Engine"
>
{/* FRONT - 91x61mm with Bleed */}
{/* FRONT - Dark Navy Industrial */}
<Page size={[258, 173]} style={styles.pageBleed}>
<Image src={truckImagePath} style={styles.truckImage} />
<Image src={logoFullPath} style={styles.frontLogo} />
<IndustrialGrid />
<View style={styles.frontLogoPos}>
<KLZLogoVector width={100} color={COLORS.white} />
</View>
<View style={styles.frontGraphicContainer}>
<IndustrialGrid />
</View>
<Text style={styles.frontTagline}>INDUSTRIAL CABLE SOLUTIONS</Text>
</Page>
{/* BACK - 91x61mm with Bleed */}
<Page size={[258, 173]} style={styles.pageBleed}>
{/* BACK - Clean White with Green Accents */}
<Page size={[258, 173]} style={styles.pageBleedBack}>
<View style={styles.greenBar} />
<View style={styles.backContainer}>
<Image src={logoFullPath} style={styles.backLogo} />
<View style={styles.backLogo}>
<KLZLogoVector width={80} color={COLORS.navy} />
</View>
<View style={styles.backMiddle}>
<View style={styles.nameSection}>
<Text style={styles.nameText}>{person.name}</Text>
<Text style={styles.roleText}>{person.role}</Text>
<Text style={styles.roleText}>{person.role.toUpperCase()}</Text>
</View>
<View style={styles.contactSection}>
@@ -184,7 +270,9 @@ export const PDFBusinessCard: React.FC<PDFBusinessCardProps> = ({
</View>
<View style={styles.qrSection}>
<Image src={qrCodeDataUrl} style={styles.qrCode} />
<View style={styles.qrCodeWrapper}>
<Image src={qrCodeDataUrl} style={styles.qrCode} />
</View>
</View>
</View>