diff --git a/lib/KLZLogoVector.tsx b/lib/KLZLogoVector.tsx new file mode 100644 index 00000000..5555e3e8 --- /dev/null +++ b/lib/KLZLogoVector.tsx @@ -0,0 +1,153 @@ +import React from 'react'; +import { Svg, G, Path } from '@react-pdf/renderer'; + +export const KLZLogoVector = ({ width, color }: { width: number; color: string }) => { + // Original viewBox was 0 0 295 99, so height is width * 99 / 295 + const height = width * (99 / 295); + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/lib/pdf-business-card.tsx b/lib/pdf-business-card.tsx index 67993760..ef8d29b5 100644 --- a/lib/pdf-business-card.tsx +++ b/lib/pdf-business-card.tsx @@ -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 = ({ - person, - logoFullPath, - truckImagePath, - qrCodeDataUrl, -}) => { +// A simple technical grid background for the industrial aesthetic +const IndustrialGrid = () => ( + + {Array.from({ length: 10 }).map((_, i) => ( + + ))} + {Array.from({ length: 15 }).map((_, i) => ( + + ))} + +); + +export const PDFBusinessCard: React.FC = ({ person, qrCodeDataUrl }) => { return ( - {/* FRONT - 91x61mm with Bleed */} + {/* FRONT - Dark Navy Industrial */} - - + + + + + + + + + + + INDUSTRIAL CABLE SOLUTIONS - {/* BACK - 91x61mm with Bleed */} - + {/* BACK - Clean White with Green Accents */} + + + - + + + {person.name} - {person.role} + {person.role.toUpperCase()} @@ -184,7 +270,9 @@ export const PDFBusinessCard: React.FC = ({ - + + + diff --git a/scripts/gen-logo-vector.ts b/scripts/gen-logo-vector.ts new file mode 100644 index 00000000..a5af22b3 --- /dev/null +++ b/scripts/gen-logo-vector.ts @@ -0,0 +1,21 @@ +import * as fs from 'fs'; + +const svgContent = fs.readFileSync('public/logo-white.svg', 'utf8'); +const dMatches = [...svgContent.matchAll(/d="([^"]+)"/g)].map((m) => m[1]); + +let tsx = `import React from 'react';\nimport { Svg, G, Path } from '@react-pdf/renderer';\n\n`; +tsx += `export const KLZLogoVector = ({ width, color }: { width: number, color: string }) => {\n`; +tsx += ` // Original viewBox was 0 0 295 99, so height is width * 99 / 295\n`; +tsx += ` const height = width * (99 / 295);\n`; +tsx += ` return (\n`; +tsx += ` \n`; +tsx += ` \n`; + +for (const d of dMatches) { + tsx += ` \n`; +} + +tsx += ` \n \n );\n};\n`; + +fs.writeFileSync('lib/KLZLogoVector.tsx', tsx); +console.log('Generated KLZLogoVector.tsx');