feat: introduce new PDF layouts and modules, enhance shared UI components, and add wording guidelines.
This commit is contained in:
55
src/components/pdf/DINLayout.tsx
Normal file
55
src/components/pdf/DINLayout.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Page as PDFPage } from '@react-pdf/renderer';
|
||||
import { FoldingMarks, Header, Footer, pdfStyles } from './SharedUI';
|
||||
|
||||
interface DINLayoutProps {
|
||||
children: React.ReactNode;
|
||||
sender?: string;
|
||||
recipient?: {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
address?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
taxId?: string;
|
||||
};
|
||||
icon?: string;
|
||||
footerLogo?: string;
|
||||
companyData: any;
|
||||
bankData: any;
|
||||
showAddress?: boolean;
|
||||
showFooterDetails?: boolean;
|
||||
}
|
||||
|
||||
export const DINLayout = ({
|
||||
children,
|
||||
sender,
|
||||
recipient,
|
||||
icon,
|
||||
footerLogo,
|
||||
companyData,
|
||||
bankData,
|
||||
showAddress = true,
|
||||
showFooterDetails = true
|
||||
}: DINLayoutProps) => {
|
||||
return (
|
||||
<PDFPage size="A4" style={pdfStyles.page}>
|
||||
<FoldingMarks />
|
||||
<Header
|
||||
sender={sender}
|
||||
recipient={recipient}
|
||||
icon={icon}
|
||||
showAddress={showAddress}
|
||||
/>
|
||||
{children}
|
||||
<Footer
|
||||
logo={footerLogo}
|
||||
companyData={companyData}
|
||||
bankData={bankData}
|
||||
showDetails={showFooterDetails}
|
||||
/>
|
||||
</PDFPage>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ export const pdfStyles = PDFStyleSheet.create({
|
||||
paddingTop: 45, // DIN 5008
|
||||
paddingLeft: 70, // ~25mm
|
||||
paddingRight: 57, // ~20mm
|
||||
paddingBottom: 48,
|
||||
paddingBottom: 80, // Safe buffer for absolute footer
|
||||
backgroundColor: '#ffffff',
|
||||
fontFamily: 'Helvetica',
|
||||
fontSize: 10,
|
||||
@@ -129,13 +129,98 @@ export const pdfStyles = PDFStyleSheet.create({
|
||||
width: 10,
|
||||
borderTopWidth: 0.5,
|
||||
borderTopColor: '#cbd5e1',
|
||||
}
|
||||
},
|
||||
// Atoms
|
||||
industrialListItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: 6,
|
||||
},
|
||||
industrialBulletBox: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
backgroundColor: '#0f172a',
|
||||
marginRight: 8,
|
||||
marginTop: 5,
|
||||
},
|
||||
industrialTitle: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#0f172a',
|
||||
marginBottom: 6,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
industrialSubtitle: {
|
||||
fontSize: 8,
|
||||
fontWeight: 'bold',
|
||||
color: '#94a3b8',
|
||||
textTransform: 'uppercase',
|
||||
marginBottom: 16,
|
||||
letterSpacing: 2,
|
||||
},
|
||||
industrialTextLead: {
|
||||
fontSize: 10,
|
||||
color: '#334155',
|
||||
lineHeight: 1.6,
|
||||
marginBottom: 12,
|
||||
},
|
||||
industrialText: {
|
||||
fontSize: 9,
|
||||
color: '#64748b',
|
||||
lineHeight: 1.6,
|
||||
marginBottom: 8,
|
||||
},
|
||||
industrialCard: {
|
||||
padding: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: '#e2e8f0',
|
||||
marginBottom: 12,
|
||||
},
|
||||
industrialCardTitle: {
|
||||
fontSize: 10,
|
||||
fontWeight: 'bold',
|
||||
color: '#0f172a',
|
||||
marginBottom: 4,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
darkBox: {
|
||||
marginTop: 32,
|
||||
padding: 24,
|
||||
backgroundColor: '#0f172a',
|
||||
color: '#ffffff',
|
||||
},
|
||||
darkTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
marginBottom: 8,
|
||||
},
|
||||
darkText: {
|
||||
fontSize: 9,
|
||||
color: '#94a3b8',
|
||||
lineHeight: 1.6,
|
||||
},
|
||||
});
|
||||
|
||||
export const IndustrialListItem = ({ children }: { children: React.ReactNode }) => (
|
||||
<PDFView style={pdfStyles.industrialListItem}>
|
||||
<PDFView style={pdfStyles.industrialBulletBox} />
|
||||
{children}
|
||||
</PDFView>
|
||||
);
|
||||
|
||||
export const IndustrialCard = ({ title, children, style = {} }: { title: string; children: React.ReactNode; style?: any }) => (
|
||||
<PDFView style={[pdfStyles.industrialCard, style]}>
|
||||
<PDFText style={pdfStyles.industrialCardTitle}>{title}</PDFText>
|
||||
{children}
|
||||
</PDFView>
|
||||
);
|
||||
|
||||
export const FoldingMarks = () => (<><PDFView style={[pdfStyles.foldingMark, { top: 297.6 }]} fixed /><PDFView style={[pdfStyles.foldingMark, { top: 420.9, width: 15 }]} fixed /><PDFView style={[pdfStyles.foldingMark, { top: 595.3 }]} fixed /></>);
|
||||
|
||||
export const Footer = ({ logo, companyData, bankData, showDetails = true }: { logo?: string; companyData: any; bankData: any; showDetails?: boolean }) => (
|
||||
<PDFView style={pdfStyles.footer}><PDFView style={pdfStyles.footerColumn}>{logo ? (<PDFImage src={logo} style={pdfStyles.footerLogo} />) : (<PDFText style={{ fontSize: 12, fontWeight: 'bold', marginBottom: 8 }}>marc mintel</PDFText>)}</PDFView>{showDetails && (<><PDFView style={pdfStyles.footerColumn}><PDFText style={pdfStyles.footerText}><PDFText style={pdfStyles.footerLabel}>{companyData.name}</PDFText>{"\n"}{companyData.address1}{"\n"}{companyData.address2}{"\n"}UST: {companyData.ustId}</PDFText></PDFView><PDFView style={[pdfStyles.footerColumn, { alignItems: 'flex-end' }]}><PDFText style={[pdfStyles.footerText, { textAlign: 'right' }]}><PDFText style={pdfStyles.footerLabel}>{bankData.name}</PDFText>{"\n"}{bankData.bic}{"\n"}{bankData.iban}</PDFText><PDFText style={pdfStyles.pageNumber} render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`} fixed /></PDFView></>)}{!showDetails && (<PDFView style={[pdfStyles.footerColumn, { alignItems: 'flex-end' }]}><PDFText style={pdfStyles.pageNumber} render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`} fixed /></PDFView>)}</PDFView>
|
||||
export const Footer = ({ logo, companyData, bankData, showDetails = true, showPageNumber = true }: { logo?: string; companyData: any; bankData: any; showDetails?: boolean; showPageNumber?: boolean }) => (
|
||||
<PDFView style={pdfStyles.footer}><PDFView style={pdfStyles.footerColumn}>{logo ? (<PDFImage src={logo} style={pdfStyles.footerLogo} />) : (<PDFText style={{ fontSize: 12, fontWeight: 'bold', marginBottom: 8 }}>marc mintel</PDFText>)}</PDFView>{showDetails && (<><PDFView style={pdfStyles.footerColumn}><PDFText style={pdfStyles.footerText}><PDFText style={pdfStyles.footerLabel}>{companyData.name}</PDFText>{"\n"}{companyData.address1}{"\n"}{companyData.address2}{"\n"}UST: {companyData.ustId}</PDFText></PDFView><PDFView style={[pdfStyles.footerColumn, { alignItems: 'flex-end' }]}>{showPageNumber && <PDFText style={pdfStyles.pageNumber} render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`} fixed />}</PDFView></>)}{!showDetails && (<PDFView style={[pdfStyles.footerColumn, { alignItems: 'flex-end' }]}>{showPageNumber && <PDFText style={pdfStyles.pageNumber} render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`} fixed />}</PDFView>)}</PDFView>
|
||||
);
|
||||
|
||||
export const Header = ({ sender, recipient, icon, showAddress = true }: { sender?: string; recipient?: { title: string; subtitle?: string; email?: string; address?: string; phone?: string; taxId?: string }; icon?: string; showAddress?: boolean; }) => (
|
||||
|
||||
66
src/components/pdf/SimpleLayout.tsx
Normal file
66
src/components/pdf/SimpleLayout.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Page as PDFPage, View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||
import { Header, Footer, pdfStyles } from './SharedUI';
|
||||
|
||||
const simpleStyles = StyleSheet.create({
|
||||
industrialPage: {
|
||||
padding: 40,
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
industrialNumber: {
|
||||
fontSize: 60,
|
||||
fontWeight: 'bold',
|
||||
color: '#f1f5f9',
|
||||
position: 'absolute',
|
||||
top: -10,
|
||||
right: 0,
|
||||
zIndex: -1,
|
||||
},
|
||||
industrialSection: {
|
||||
marginTop: 32,
|
||||
paddingTop: 24,
|
||||
flexDirection: 'row',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
interface SimpleLayoutProps {
|
||||
children: React.ReactNode;
|
||||
pageNumber?: string;
|
||||
icon?: string;
|
||||
footerLogo?: string;
|
||||
companyData: any;
|
||||
bankData: any;
|
||||
showPageNumber?: boolean;
|
||||
}
|
||||
|
||||
export const SimpleLayout = ({
|
||||
children,
|
||||
pageNumber,
|
||||
icon,
|
||||
footerLogo,
|
||||
companyData,
|
||||
bankData,
|
||||
showPageNumber = true
|
||||
}: SimpleLayoutProps) => {
|
||||
return (
|
||||
<PDFPage size="A4" style={[pdfStyles.page, simpleStyles.industrialPage]}>
|
||||
<Header icon={icon} showAddress={false} />
|
||||
{pageNumber && <PDFText style={simpleStyles.industrialNumber}>{pageNumber}</PDFText>}
|
||||
<PDFView style={simpleStyles.industrialSection}>
|
||||
<PDFView style={{ width: '100%' }}>
|
||||
{children}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
<Footer
|
||||
logo={footerLogo}
|
||||
companyData={companyData}
|
||||
bankData={bankData}
|
||||
showDetails={false}
|
||||
showPageNumber={showPageNumber}
|
||||
/>
|
||||
</PDFPage>
|
||||
);
|
||||
};
|
||||
99
src/components/pdf/modules/BrandingModules.tsx
Normal file
99
src/components/pdf/modules/BrandingModules.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||
import { IndustrialListItem, IndustrialCard } from '../SharedUI';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
industrialTitle: { fontSize: 24, fontWeight: 'bold', color: '#0f172a', marginBottom: 6, letterSpacing: -0.5 },
|
||||
industrialSubtitle: { fontSize: 8, fontWeight: 'bold', color: '#94a3b8', textTransform: 'uppercase', marginBottom: 16, letterSpacing: 2 },
|
||||
industrialTextLead: { fontSize: 10, color: '#334155', lineHeight: 1.6, marginBottom: 12 },
|
||||
industrialText: { fontSize: 9, color: '#64748b', lineHeight: 1.6, marginBottom: 8 },
|
||||
industrialGrid2: { flexDirection: 'row', gap: 32 },
|
||||
industrialCol: { width: '48%' },
|
||||
darkBox: { marginTop: 32, padding: 24, backgroundColor: '#0f172a', color: '#ffffff' },
|
||||
darkTitle: { fontSize: 18, fontWeight: 'bold', color: '#ffffff', marginBottom: 8 },
|
||||
darkText: { fontSize: 9, color: '#94a3b8', lineHeight: 1.6 },
|
||||
industrialBulletBox: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
backgroundColor: '#0f172a',
|
||||
marginRight: 8,
|
||||
marginTop: 5,
|
||||
},
|
||||
});
|
||||
|
||||
export const AboutModule = () => (
|
||||
<>
|
||||
<PDFText style={styles.industrialTitle}>Über mich</PDFText>
|
||||
<PDFText style={styles.industrialSubtitle}>Direkt. Sauber. Verantwortlich.</PDFText>
|
||||
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
|
||||
<PDFView style={styles.industrialCol}>
|
||||
<PDFText style={styles.industrialTextLead}>Ich baue Websites und Systeme seit über 15 Jahren. Nicht weil ich Websites so liebe – sondern weil ich es hasse, wenn Dinge nicht funktionieren.</PDFText>
|
||||
<PDFText style={styles.industrialText}>In diesen Jahren habe ich Agenturen von innen gesehen, Konzerne erlebt und Startups aufgebaut. Ich habe gelernt, dass das Problem selten die Technik ist – sondern die Zuständigkeit.</PDFText>
|
||||
<PDFView style={{ marginTop: 16 }}>
|
||||
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Meine Arbeitsweise:</PDFText>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>schnell & stabil</PDFText></IndustrialListItem>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>"boring" (im besten Sinne)</PDFText></IndustrialListItem>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>erweiterbar & wartungsarm</PDFText></IndustrialListItem>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>unabhängig von Agenturen</PDFText></IndustrialListItem>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
<PDFView style={styles.industrialCol}>
|
||||
<IndustrialCard title="WAS SIE BEKOMMEN">
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>Eine Person. Eine Verantwortung.</PDFText></IndustrialListItem>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>Direkte Kommunikation ohne Stille Post.</PDFText></IndustrialListItem>
|
||||
<IndustrialListItem><PDFText style={styles.industrialText}>Code, der Ihnen gehört.</PDFText></IndustrialListItem>
|
||||
</IndustrialCard>
|
||||
<IndustrialCard title="WAS SIE NICHT BEKOMMEN" style={{ backgroundColor: '#ffffff', borderColor: '#cbd5e1' }}>
|
||||
<PDFText style={[styles.industrialText, { color: '#94a3b8', textDecoration: 'line-through' }]}>Projektmanager</PDFText>
|
||||
<PDFText style={[styles.industrialText, { color: '#94a3b8', textDecoration: 'line-through' }]}>Ticket-Systeme</PDFText>
|
||||
<PDFText style={[styles.industrialText, { color: '#94a3b8', textDecoration: 'line-through' }]}>CMS-Drama & Update-Angst</PDFText>
|
||||
</IndustrialCard>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
|
||||
export const CrossSellModule = ({ state }: any) => {
|
||||
const isWebsite = state.projectType === 'website';
|
||||
const title = isWebsite ? "Das könnte Sie auch interessieren" : "Websites & Ökosysteme";
|
||||
const subtitle = isWebsite ? "Kleine Helfer, die den Alltag entlasten" : "Digitale Visitenkarten mit Tiefe";
|
||||
|
||||
return (
|
||||
<>
|
||||
<PDFText style={styles.industrialTitle}>Das könnte Sie auch interessieren</PDFText>
|
||||
<PDFText style={styles.industrialSubtitle}>Kleine helfer, die den alltag entlasten</PDFText>
|
||||
<PDFView style={[styles.industrialGrid2, { marginTop: 12 }]}>
|
||||
{isWebsite ? (
|
||||
<>
|
||||
<PDFView style={styles.industrialCol}>
|
||||
<PDFText style={styles.industrialTextLead}>Zusätzlich zur digitalen Präsenz fressen in vielen Unternehmen wiederkehrende Aufgaben unzählige Stunden: Daten abtippen, Formulare ausfüllen, Angebote anpassen.</PDFText>
|
||||
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Das ist keine wertschöpfende Arbeit. Das ist Routine.</PDFText>
|
||||
<PDFView style={styles.darkBox}>
|
||||
<PDFText style={styles.darkTitle}>Der schnelle Check</PDFText>
|
||||
<PDFText style={styles.darkText}>Wenn bei Ihnen gerade etwas "von Hand gemacht wird" oder "ewig dauert" – ich sage Ihnen in 1-2 Tagen, ob und wie schnell man das sinnvoll digitalisieren kann.</PDFText>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
<PDFView style={styles.industrialCol}>
|
||||
<IndustrialCard title="PDF-GENERATOREN">
|
||||
<PDFText style={styles.industrialText}>Kurze Eingaben via Formular → fertiges Angebot/Bericht. Von 60min auf 5min reduziert.</PDFText>
|
||||
</IndustrialCard>
|
||||
<IndustrialCard title="EXCEL-AUTOMATISIERUNG">
|
||||
<PDFText style={styles.industrialText}>Verkaufszahlen, Lagerbestände oder Kundenlisten automatisch synchronisieren und auswerten.</PDFText>
|
||||
</IndustrialCard>
|
||||
<IndustrialCard title="KI-SCANNER">
|
||||
<PDFText style={styles.industrialText}>Rechnungen oder handschriftliche Notizen fotografieren → Daten landen direkt strukturiert in der Tabelle.</PDFText>
|
||||
</IndustrialCard>
|
||||
</PDFView>
|
||||
</>
|
||||
) : (
|
||||
<PDFView style={{ width: '100%' }}>
|
||||
<PDFText style={styles.industrialTextLead}>Neben Automatisierung baue ich komplette digitale Ökosysteme für Unternehmen, die Wert auf Qualität legen.</PDFText>
|
||||
<PDFText style={styles.industrialText}>Keine Baukästen. Keine langsamen WordPress-Themes. Sondern maßgeschneiderte Systeme, die technisch und optisch in der ersten Liga spielen.</PDFText>
|
||||
</PDFView>
|
||||
)}
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
42
src/components/pdf/modules/BriefingModule.tsx
Normal file
42
src/components/pdf/modules/BriefingModule.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||
import { DocumentTitle } from '../SharedUI';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
section: { marginBottom: 24 },
|
||||
sectionTitle: { fontSize: 10, fontWeight: 'bold', marginBottom: 8, color: '#0f172a' },
|
||||
configGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 6, marginTop: 6 },
|
||||
configItem: { width: '24%', marginBottom: 4 },
|
||||
configLabel: { fontSize: 5, color: '#94a3b8', textTransform: 'uppercase', marginBottom: 2 },
|
||||
configValue: { fontSize: 7, color: '#0f172a', fontWeight: 'bold' },
|
||||
visionText: { fontSize: 9, color: '#334155', lineHeight: 1.8, textAlign: 'justify' },
|
||||
});
|
||||
|
||||
export const BriefingModule = ({ state }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Projektdetails" />
|
||||
{state.briefingSummary && (
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={styles.sectionTitle}>Briefing Analyse</PDFText>
|
||||
<PDFText style={{ fontSize: 8, color: '#334155', lineHeight: 1.6, textAlign: 'justify' }}>{state.briefingSummary}</PDFText>
|
||||
</PDFView>
|
||||
)}
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={styles.sectionTitle}>Kern-Informationen</PDFText>
|
||||
<PDFView style={styles.configGrid}>
|
||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Ansprechpartner</PDFText><PDFText style={styles.configValue}>{state.personName || "Ihr Team"}</PDFText></PDFView>
|
||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Status Quo</PDFText><PDFText style={styles.configValue}>{state.statusQuo || (state.existingWebsite ? 'Relaunch' : 'Neuentwicklung')}</PDFText></PDFView>
|
||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Mitarbeiter</PDFText><PDFText style={styles.configValue}>{state.employeeCount || "—"}</PDFText></PDFView>
|
||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Deadline / Zeitplan</PDFText><PDFText style={styles.configValue}>{state.deadline || 'Flexibel'}</PDFText></PDFView>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
{state.designVision && (
|
||||
<PDFView style={[styles.section, { padding: 16, borderLeftWidth: 2, borderLeftColor: '#000000', backgroundColor: '#f9fafb' }]}>
|
||||
<PDFText style={[styles.sectionTitle, { color: '#000000' }]}>Strategische Vision</PDFText>
|
||||
<PDFText style={styles.visionText}>{state.designVision}</PDFText>
|
||||
</PDFView>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
100
src/components/pdf/modules/CommonModules.tsx
Normal file
100
src/components/pdf/modules/CommonModules.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, StyleSheet, Image as PDFImage } from '@react-pdf/renderer';
|
||||
import { DocumentTitle } from '../SharedUI';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
section: { marginBottom: 24 },
|
||||
pricingGrid: { marginTop: 24 },
|
||||
pricingRow: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: '#f1f5f9', paddingVertical: 10, alignItems: 'flex-start' },
|
||||
pricingTitle: { width: '30%', fontSize: 9, fontWeight: 'bold', color: '#0f172a' },
|
||||
pricingDesc: { width: '55%', fontSize: 8, color: '#64748b', lineHeight: 1.4 },
|
||||
pricingTag: { width: '15%', fontSize: 9, fontWeight: 'bold', textAlign: 'right' },
|
||||
configLabel: { fontSize: 5, color: '#94a3b8', textTransform: 'uppercase', marginBottom: 8 },
|
||||
});
|
||||
|
||||
const CHROME_ICON = '/Users/marcmintel/Projects/mintel.me/src/assets/browser/chrome.png'; // Fallback to a placeholder if not found
|
||||
const SAFARI_ICON = '/Users/marcmintel/Projects/mintel.me/src/assets/browser/safari.png';
|
||||
|
||||
export const techPageModule = ({ techDetails, headerIcon }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Technische Umsetzung" />
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 16 }}>Ich entwickle Websites als moderne, performante Websysteme.</PDFText>
|
||||
<PDFView style={styles.pricingGrid}>
|
||||
{techDetails?.map((item: any, i: number) => (
|
||||
<PDFView key={i} style={styles.pricingRow}>
|
||||
<PDFText style={[styles.pricingTitle, { width: '35%' }]}>{item.t}</PDFText>
|
||||
<PDFText style={[styles.pricingDesc, { width: '65%' }]}>{item.d}</PDFText>
|
||||
</PDFView>
|
||||
))}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
|
||||
export const TransparenzModule = ({ pricing }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Preis-Transparenz & Modell" />
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={{ fontSize: 10, fontWeight: 'bold', color: '#000000', marginBottom: 8 }}>Grundlage für kalkulierbare Investitionen</PDFText>
|
||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 24 }}>Digitaler Erfolg basiert auf Transparenz. Dieses Dokument dient als detaillierte Grundlage für Ihre Investitionsentscheidung. Es handelt sich um ein Festpreis-Modell für die beschriebenen Leistungen, um Ihnen maximale Planungssicherheit zu gewährleisten.</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={{ fontSize: 9, color: '#0f172a', fontWeight: 'bold', marginBottom: 8 }}>Warum dieser modulare Ansatz?</PDFText>
|
||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 20 }}>Meine Kalkulation basiert auf einem transparenten Festpreis-System. Sie zahlen nicht für Stunden, sondern für messbare Ergebnisse. Jeder Baustein ist darauf ausgelegt, Ihre digitale Präsenz zu stärken und den ROI Ihres Projekts zu maximieren.</PDFText>
|
||||
<PDFView style={styles.pricingGrid}>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>1. Das Fundament</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Infrastruktur, technisches SEO, Analytics und ein skalierbares Setup. Der Grundstein für eine Website, die technisch perfekt performt.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.BASE_WEBSITE?.toLocaleString('de-DE')} €</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>2. Individuelle Erlebnisse</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Pro Seite entwickeln wir ein UI/UX-Konzept, das Besucher führt und überzeugt. Keine Standard-Templates, sondern maßgeschneidertes Design.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.PAGE?.toLocaleString('de-DE')} € / Stk</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>3. Smarte Content-Systeme</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Automatisieren Sie Ihre Kommunikation. Ob Blog, Job-Board oder Case Studies – wir bauen Systeme, die sich einfach pflegen lassen.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.FEATURE?.toLocaleString('de-DE')} € / Stk</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>4. Funktionalität</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Suche, Filter oder interaktive Formulare. Wir reduzieren Reibung und machen die Nutzung für Ihre Kunden zum Erlebnis.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.FUNCTION?.toLocaleString('de-DE')} € / Stk</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>5. High-End Inszenierung</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Video-Integrationen, Mikro-Animationen und immersive Effekte. So heben Sie sich im Markt ab und wirken als Premium-Marke.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.COMPLEX_INTERACTION?.toLocaleString('de-DE')} € / Stk</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>6. Autonomie & Vernetzung</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Nahtlose Anbindung an Headless CMS oder Ihre Bestands-APIs (CRM, ERP). Maximale Freiheit bei voller Kontrolle.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>ab {pricing.API_INTEGRATION?.toLocaleString('de-DE')} €</PDFText>
|
||||
</PDFView>
|
||||
<PDFView style={styles.pricingRow}>
|
||||
<PDFText style={styles.pricingTitle}>7. Sorglos-Betrieb</PDFText>
|
||||
<PDFText style={styles.pricingDesc}>Full-Service Hosting, tägliche Backups und proaktive Sicherheitsupdates. Damit Sie sich auf Ihr Kerngeschäft konzentrieren können.</PDFText>
|
||||
<PDFText style={styles.pricingTag}>{pricing.HOSTING_MONTHLY?.toLocaleString('de-DE')} € / Mon</PDFText>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
|
||||
export const PrinciplesModule = ({ principles }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Meine Prinzipien" />
|
||||
<PDFView style={styles.pricingGrid}>
|
||||
{principles?.map((item: any, i: number) => (
|
||||
<PDFView key={i} style={styles.pricingRow}>
|
||||
<PDFText style={[styles.pricingTitle, { width: '35%' }]}>{item.t}</PDFText>
|
||||
<PDFText style={[styles.pricingDesc, { width: '65%' }]}>{item.d}</PDFText>
|
||||
</PDFView>
|
||||
))}
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
55
src/components/pdf/modules/EstimationModule.tsx
Normal file
55
src/components/pdf/modules/EstimationModule.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||
import { DocumentTitle } from '../SharedUI';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
table: { marginTop: 12 },
|
||||
tableHeader: { flexDirection: 'row', paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: '#000000', marginBottom: 12 },
|
||||
tableRow: { flexDirection: 'row', paddingVertical: 6, borderBottomWidth: 1, borderBottomColor: '#eeeeee', alignItems: 'flex-start' },
|
||||
colPos: { width: '8%' },
|
||||
colDesc: { width: '62%' },
|
||||
colQty: { width: '10%', textAlign: 'center' },
|
||||
colPrice: { width: '20%', textAlign: 'right' },
|
||||
headerText: { fontSize: 7, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 },
|
||||
posText: { fontSize: 8, color: '#999999' },
|
||||
itemTitle: { fontSize: 10, fontWeight: 'bold', marginBottom: 4 },
|
||||
itemDesc: { fontSize: 8, color: '#666666', lineHeight: 1.4 },
|
||||
priceText: { fontSize: 10, fontWeight: 'bold' },
|
||||
summaryContainer: { borderTopWidth: 1, borderTopColor: '#000000', paddingTop: 8 },
|
||||
summaryRow: { flexDirection: 'row', justifyContent: 'flex-end', paddingVertical: 4, alignItems: 'baseline' },
|
||||
summaryLabel: { fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1, fontWeight: 'bold', marginRight: 12 },
|
||||
summaryValue: { fontSize: 9, fontWeight: 'bold', width: 100, textAlign: 'right' },
|
||||
totalRow: { flexDirection: 'row', justifyContent: 'flex-end', paddingTop: 12, marginTop: 8, borderTopWidth: 2, borderTopColor: '#000000', alignItems: 'baseline' },
|
||||
});
|
||||
|
||||
export const EstimationModule = ({ state, positions, totalPrice, date }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Kostenschätzung" subLines={[`Datum: ${date}`, `Projekt: ${state.projectType === 'website' ? 'Website' : 'Web App'}`]} />
|
||||
<PDFView style={styles.table}>
|
||||
<PDFView style={styles.tableHeader}>
|
||||
<PDFText style={[styles.headerText, styles.colPos]}>Pos</PDFText>
|
||||
<PDFText style={[styles.headerText, styles.colDesc]}>Beschreibung</PDFText>
|
||||
<PDFText style={[styles.headerText, styles.colQty]}>Menge</PDFText>
|
||||
<PDFText style={[styles.headerText, styles.colPrice]}>Betrag</PDFText>
|
||||
</PDFView>
|
||||
{positions.map((item: any, i: number) => (
|
||||
<PDFView key={i} style={styles.tableRow} wrap={false}>
|
||||
<PDFText style={[styles.posText, styles.colPos]}>{item.pos.toString().padStart(2, '0')}</PDFText>
|
||||
<PDFView style={styles.colDesc}>
|
||||
<PDFText style={styles.itemTitle}>{item.title}</PDFText>
|
||||
<PDFText style={styles.itemDesc}>{state.positionDescriptions?.[item.title] || item.desc}</PDFText>
|
||||
</PDFView>
|
||||
<PDFText style={[styles.posText, styles.colQty]}>{item.qty}</PDFText>
|
||||
<PDFText style={[styles.priceText, styles.colPrice]}>{item.price > 0 ? `${item.price.toLocaleString('de-DE')} €` : 'n. A.'}</PDFText>
|
||||
</PDFView>
|
||||
))}
|
||||
</PDFView>
|
||||
<PDFView style={styles.summaryContainer} wrap={false}>
|
||||
<PDFView style={styles.summaryRow}><PDFText style={styles.summaryLabel}>Zwischensumme (Netto)</PDFText><PDFText style={styles.summaryValue}>{totalPrice.toLocaleString('de-DE')} €</PDFText></PDFView>
|
||||
<PDFView style={styles.summaryRow}><PDFText style={styles.summaryLabel}>zzgl. 19% MwSt.</PDFText><PDFText style={styles.summaryValue}>{(totalPrice * 0.19).toLocaleString('de-DE')} €</PDFText></PDFView>
|
||||
<PDFView style={styles.totalRow}><PDFText style={styles.summaryLabel}>Gesamtsumme (Brutto)</PDFText><PDFText style={[styles.summaryValue, { fontSize: 14 }]}>{(totalPrice * 1.19).toLocaleString('de-DE')} €</PDFText></PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
73
src/components/pdf/modules/FrontPageModule.tsx
Normal file
73
src/components/pdf/modules/FrontPageModule.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, Image as PDFImage, StyleSheet } from '@react-pdf/renderer';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
titlePage: {
|
||||
padding: 60,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '90%',
|
||||
},
|
||||
titleBrandIcon: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
backgroundColor: '#000000',
|
||||
borderRadius: 16,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 40,
|
||||
},
|
||||
brandIconText: {
|
||||
fontSize: 40,
|
||||
color: '#ffffff',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
titleProjectName: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#0f172a',
|
||||
marginBottom: 16,
|
||||
textAlign: 'center',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 2,
|
||||
},
|
||||
titleCustomerName: {
|
||||
fontSize: 14,
|
||||
color: '#64748b',
|
||||
marginBottom: 40,
|
||||
textAlign: 'center',
|
||||
},
|
||||
titleDocumentType: {
|
||||
fontSize: 10,
|
||||
color: '#94a3b8',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 4,
|
||||
marginBottom: 8,
|
||||
},
|
||||
titleDivider: {
|
||||
width: 40,
|
||||
height: 2,
|
||||
backgroundColor: '#000000',
|
||||
marginBottom: 40,
|
||||
},
|
||||
titleDate: {
|
||||
fontSize: 9,
|
||||
color: '#94a3b8',
|
||||
marginTop: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
export const FrontPageModule = ({ state, headerIcon, date }: any) => (
|
||||
<PDFView style={styles.titlePage}>
|
||||
<PDFView style={styles.titleBrandIcon}>
|
||||
{headerIcon ? <PDFImage src={headerIcon} style={{ width: 40, height: 40 }} /> : <PDFText style={styles.brandIconText}>M</PDFText>}
|
||||
</PDFView>
|
||||
<PDFText style={styles.titleDocumentType}>Kostenschätzung & Konzept</PDFText>
|
||||
<PDFText style={styles.titleProjectName}>{state.projectType === 'website' ? 'Digitale Präsenz' : 'Digitale Applikation'}</PDFText>
|
||||
<PDFView style={styles.titleDivider} />
|
||||
<PDFText style={styles.titleCustomerName}>für {state.companyName || "Ihre Projektanfrage"}</PDFText>
|
||||
<PDFText style={styles.titleDate}>{date} | Marc Mintel</PDFText>
|
||||
</PDFView>
|
||||
);
|
||||
52
src/components/pdf/modules/SitemapModule.tsx
Normal file
52
src/components/pdf/modules/SitemapModule.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||
import { DocumentTitle } from '../SharedUI';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
section: { marginBottom: 24 },
|
||||
sitemapTree: { marginTop: 20 },
|
||||
sitemapRootNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
|
||||
sitemapRootDot: { width: 6, height: 6, borderRadius: 3, backgroundColor: '#000000', marginRight: 10 },
|
||||
sitemapRootTitle: { fontSize: 10, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 },
|
||||
sitemapMainLine: { position: 'absolute', left: 2, top: 20, bottom: 0, width: 0.5, backgroundColor: '#cbd5e1' },
|
||||
sitemapBranch: { marginLeft: 20, marginBottom: 12, position: 'relative' },
|
||||
sitemapNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 4 },
|
||||
sitemapRootIcon: { width: 4, height: 4, backgroundColor: '#000000', marginRight: 8 },
|
||||
sitemapBranchTitle: { fontSize: 8, fontWeight: 'bold' },
|
||||
sitemapLeaf: { marginLeft: 12, borderLeftWidth: 0.5, borderLeftColor: '#cbd5e1', paddingLeft: 12, marginTop: 4 },
|
||||
sitemapLeafNode: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 6 },
|
||||
sitemapLeafPointer: { fontSize: 7, color: '#94a3b8', marginRight: 6 },
|
||||
sitemapLeafTitle: { fontSize: 7, fontWeight: 'bold' },
|
||||
sitemapLeafDesc: { fontSize: 6, color: '#64748b', lineHeight: 1.3, marginTop: 1 },
|
||||
});
|
||||
|
||||
export const SitemapModule = ({ state }: any) => (
|
||||
<>
|
||||
<DocumentTitle title="Seitenstruktur" />
|
||||
<PDFView style={styles.section}>
|
||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 16 }}>Die folgende Struktur bildet das Fundament für die Benutzerführung und Informationsarchitektur Ihres Projekts.</PDFText>
|
||||
<PDFView style={styles.sitemapTree}>
|
||||
<PDFView style={styles.sitemapRootNode}><PDFView style={styles.sitemapRootDot} /><PDFText style={styles.sitemapRootTitle}>{state.websiteTopic || 'Digitales Ökosystem'}</PDFText></PDFView>
|
||||
<PDFView style={styles.sitemapMainLine} />
|
||||
{state.sitemap?.map((cat: any, i: number) => (
|
||||
<PDFView key={i} style={styles.sitemapBranch}>
|
||||
<PDFView style={styles.sitemapNode}><PDFView style={styles.sitemapRootIcon} /><PDFText style={styles.sitemapBranchTitle}>{cat.category}</PDFText></PDFView>
|
||||
<PDFView style={styles.sitemapLeaf}>
|
||||
{cat.pages.map((p: any, j: number) => (
|
||||
<PDFView key={j} style={styles.sitemapLeafNode}>
|
||||
<PDFText style={styles.sitemapLeafPointer}>└─</PDFText>
|
||||
<PDFView style={{ flex: 1 }}>
|
||||
<PDFText style={styles.sitemapLeafTitle}>{p.title}</PDFText>
|
||||
{p.desc && <PDFText style={styles.sitemapLeafDesc}>{p.desc}</PDFText>}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
))}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
))}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
Reference in New Issue
Block a user