refactor: estimation generation
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 32s
Build & Deploy / 🏗️ Build (push) Successful in 4m41s
Build & Deploy / 🧪 QA (push) Successful in 5m56s
Build & Deploy / 🚀 Deploy (push) Successful in 12s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
Build & Deploy / ⚡ PageSpeed (push) Successful in 55s

This commit is contained in:
2026-02-09 19:11:02 +01:00
parent f4ba861b4d
commit e6809a6d64
17 changed files with 3788 additions and 1805 deletions

View File

@@ -1,108 +1,218 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
import { IndustrialListItem, IndustrialCard, Divider, COLORS, FONT_SIZES } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
StyleSheet,
} from "@react-pdf/renderer";
import {
DocumentTitle,
IndustrialListItem,
IndustrialCard,
Divider,
COLORS,
FONT_SIZES,
} from "../SharedUI";
const styles = StyleSheet.create({
industrialTitle: { fontSize: FONT_SIZES.H1, fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 6, letterSpacing: -1 },
industrialSubtitle: { fontSize: FONT_SIZES.SUB, fontWeight: 'bold', color: COLORS.TEXT_LIGHT, marginBottom: 16, letterSpacing: 0.5 },
industrialTextLead: { fontSize: FONT_SIZES.H3, color: COLORS.TEXT_MAIN, lineHeight: 1.6, marginBottom: 16 },
industrialText: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 12 },
industrialGrid2: { flexDirection: 'row' },
industrialCol: { width: '46%' },
industrialBulletBox: {
width: 6,
height: 6,
backgroundColor: COLORS.DIVIDER,
marginRight: 8,
marginTop: 5,
},
industrialTextLead: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_MAIN,
lineHeight: 1.4,
marginBottom: 16,
},
industrialText: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_DIM,
lineHeight: 1.4,
marginBottom: 12,
},
industrialGrid2: { flexDirection: "row" },
industrialCol: { width: "46%" },
});
export const AboutModule = () => (
<>
<PDFText style={styles.industrialTitle}>Expertise & Profil</PDFText>
<PDFText style={styles.industrialSubtitle}>Entwicklung & Technischer Partner für den Mittelstand</PDFText>
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
<>
<DocumentTitle
title="Expertise & Profil"
subLines={["Entwicklung & Technischer Partner für den Mittelstand"]}
isHero={true}
/>
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
<PDFView style={{ marginTop: 24 }}>
<PDFText style={styles.industrialTextLead}>
Begleitung mittelständischer Unternehmen und Agenturen bei der Realisierung anspruchsvoller Web-Projekte. Als Senior Software Developer mit über 15 Jahren Erfahrung wird das gesamte technische Spektrum abgedeckt von der Architektur bis zum fertigen Produkt.
</PDFText>
<PDFView style={{ marginTop: 24 }}>
<PDFText style={styles.industrialTextLead}>
Begleitung mittelständischer Unternehmen und Agenturen bei der
Realisierung anspruchsvoller Web-Projekte. Als Senior Software Developer
mit over 15 Jahren Erfahrung wird das gesamte technische Spektrum
abgedeckt von der Architektur bis zum fertigen Produkt.
</PDFText>
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
<PDFView style={[styles.industrialCol, { marginRight: '8%' }]}>
<PDFText style={[styles.industrialText, { fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 8 }]}>Erfahrung & Substanz</PDFText>
<PDFText style={styles.industrialText}>
Der Werdegang umfasst alle Ebenen der Webentwicklung: von der Teamleitung in Kreativagenturen bis zur Softwareentwicklung für internationale Konzerne.
</PDFText>
<PDFText style={styles.industrialText}>
Die Kenntnis komplexer Enterprise-Systeme wird mit der Agilität kombiniert, die im Mittelstand gefordert ist. Dieses Wissen ermöglicht den Bau von Lösungen, die technologisch auf Augenhöhe mit Konzern-Standards sind, jedoch ohne unnötigen bürokratischen Overhead auskommen.
</PDFText>
</PDFView>
<PDFView style={styles.industrialCol}>
<PDFText style={[styles.industrialText, { fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 8 }]}>Fokus Einzelentwicklung</PDFText>
<PDFText style={styles.industrialText}>
Die Umsetzung erfolgt bewusst als spezialisierter Einzelentwickler. Dies garantiert maximale Geschwindigkeit, direkte Kommunikationswege und volle technologische Verantwortung.
</PDFText>
<PDFText style={styles.industrialText}>
Als direkter technischer Sparringspartner bleibt die Codebasis von der ersten bis zur letzten Zeile transparent und wartbar. Diese Unmittelbarkeit stellt sicher, dass Ergebnisse sowohl technisch sauber als auch wirtschaftlich sinnvoll realisiert werden.
</PDFText>
</PDFView>
</PDFView>
<PDFView style={{ marginTop: 32, paddingVertical: 16, borderTopWidth: 1, borderTopColor: COLORS.GRID }}>
<PDFText style={[styles.industrialText, { fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 4 }]}>Infrastruktur & Souveränität</PDFText>
<PDFText style={styles.industrialText}>
Es wird keine instabile Prototyp-Software geliefert, sondern produktionsreife Systeme, die technisch skalierbar bleiben. Die Codebasis folgt modernen Standards bei wachsenden Ansprüchen oder dem Wechsel zu einer Agentur kann der Quellcode jederzeit nahtlos übernommen und weitergeführt werden.
</PDFText>
</PDFView>
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
<PDFView style={[styles.industrialCol, { marginRight: "8%" }]}>
<PDFText
style={[
styles.industrialText,
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 8 },
]}
>
Erfahrung & Substanz
</PDFText>
<PDFText style={styles.industrialText}>
Der Werdegang umfasst alle Ebenen der Webentwicklung: von der
Teamleitung in Kreativagenturen bis zur Softwareentwicklung für
internationale Konzerne.
</PDFText>
<PDFText style={styles.industrialText}>
Die Kenntnis komplexer Enterprise-Systeme wird mit der Agilität
kombiniert, die im Mittelstand gefordert ist. Dieses Wissen
ermöglicht den Bau von Lösungen, die technologisch auf Augenhöhe mit
Konzern-Standards sind, jedoch ohne unnötigen bürokratischen
Overhead auskommen.
</PDFText>
</PDFView>
</>
<PDFView style={styles.industrialCol}>
<PDFText
style={[
styles.industrialText,
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 8 },
]}
>
Fokus Einzelentwicklung
</PDFText>
<PDFText style={styles.industrialText}>
Die Umsetzung erfolgt bewusst als spezialisierter Einzelentwickler.
Dies garantiert maximale Geschwindigkeit, direkte Kommunikationswege
und volle technologische Verantwortung.
</PDFText>
<PDFText style={styles.industrialText}>
Als direkter technischer Sparringspartner bleibt die Codebasis von
der ersten bis zur letzten Zeile transparent und wartbar. Diese
Unmittelbarkeit stellt sicher, dass Ergebnisse sowohl technisch
sauber als auch wirtschaftlich sinnvoll realisiert werden.
</PDFText>
</PDFView>
</PDFView>
<PDFView
style={{
marginTop: 32,
paddingVertical: 16,
borderTopWidth: 1,
borderTopColor: COLORS.GRID,
}}
>
<PDFText
style={[
styles.industrialText,
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 4 },
]}
>
Infrastruktur & Souveränität
</PDFText>
<PDFText style={styles.industrialText}>
Es wird keine instabile Prototyp-Software geliefert, sondern
produktionsreife Systeme, die technisch skalierbar bleiben. Die
Codebasis folgt modernen Standards bei wachsenden Ansprüchen oder
dem Wechsel zu einer Agentur kann der Quellcode jederzeit nahtlos
übernommen und weitergeführt werden.
</PDFText>
</PDFView>
</PDFView>
</>
);
export const CrossSellModule = ({ state }: any) => {
const isWebsite = state.projectType === 'website';
const title = isWebsite ? "Weitere Potenziale" : "Websites & Ökosysteme";
const subtitle = isWebsite ? "Automatisierung und Prozessoptimierung" : "Technische Infrastruktur ohne Kompromisse";
const isWebsite = state.projectType === "website";
const title = isWebsite ? "Weitere Potenziale" : "Websites & Ökosysteme";
const subtitle = isWebsite
? "Automatisierung und Prozessoptimierung"
: "Technische Infrastruktur ohne Kompromisse";
return (
<>
<PDFText style={styles.industrialTitle}>{title}</PDFText>
<PDFText style={styles.industrialSubtitle}>{subtitle}</PDFText>
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
<PDFView style={[styles.industrialGrid2, { marginTop: 16 }]} >
{isWebsite ? (
<>
<PDFView style={[styles.industrialCol, { marginRight: '8%' }]}>
<PDFText style={styles.industrialTextLead}>Über die klassische Webpräsenz hinaus werden maßgeschneiderte Lösungen zur Automatisierung von Routine-Prozessen angeboten. Dies ermöglicht eine signifikante Effizienzsteigerung im Tagesgeschäft.</PDFText>
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Keine Abos. Keine komplexen neuen Systeme. Gezielte Zeitersparnis.</PDFText>
<PDFView style={{ marginTop: 24, padding: 16, backgroundColor: '#f8fafc', borderLeftWidth: 2, borderLeftColor: COLORS.GRID }}>
<PDFText style={[styles.industrialText, { fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 4 }]}>Individuelle Analyse</PDFText>
<PDFText style={styles.industrialText}>Spezifische Prozesse werden auf technisches Automatisierungspotenzial untersucht. Das Ergebnis liefert Klarheit über die wirtschaftliche Sinnhaftigkeit einer Umsetzung.</PDFText>
</PDFView>
</PDFView>
<PDFView style={styles.industrialCol}>
<IndustrialCard title="DOKUMENT-AUTOMATION">
<PDFText style={styles.industrialText}>Erstellung von PDF-Angeboten, Berichten oder Protokollen in Sekunden statt Stunden.</PDFText>
</IndustrialCard>
<IndustrialCard title="EXCEL-LOGIK">
<PDFText style={styles.industrialText}>Intelligente Tabellen und automatisierte Auswertungen bestehender Datensätze.</PDFText>
</IndustrialCard>
<IndustrialCard title="KI-ASSISTENZ">
<PDFText style={styles.industrialText}>Effiziente Verarbeitung von analogen Dokumenten oder handschriftlichen Notizen mittels KI.</PDFText>
</IndustrialCard>
</PDFView>
</>
) : (
<PDFView style={{ width: '100%' }}>
<PDFText style={styles.industrialTextLead}>Bereitstellung einer stabilen technischen Basis ohne Abhängigkeiten von Baukasten-Systemen oder Agenturen.</PDFText>
<PDFText style={styles.industrialText}>Entwicklung performanter Frontends und skalierbarer Backends. Die Auslieferung erfolgt als kontrollierbarer und nachhaltiger Quellcode.</PDFText>
</PDFView>
)}
return (
<>
<DocumentTitle title={title} subLines={[subtitle]} isHero={true} />
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
<PDFView style={[styles.industrialGrid2, { marginTop: 16 }]}>
{isWebsite ? (
<>
<PDFView style={[styles.industrialCol, { marginRight: "8%" }]}>
<PDFText style={styles.industrialTextLead}>
Über die klassische Webpräsenz hinaus werden maßgeschneiderte
Lösungen zur Automatisierung von Routine-Prozessen angeboten.
Dies ermöglicht eine signifikante Effizienzsteigerung im
Tagesgeschäft.
</PDFText>
<PDFText style={[styles.industrialText, { fontWeight: "bold" }]}>
Keine Abos. Keine komplexen neuen Systeme. Gezielte
Zeitersparnis.
</PDFText>
<PDFView
style={{
marginTop: 24,
padding: 16,
backgroundColor: "#f8fafc",
borderLeftWidth: 2,
borderLeftColor: COLORS.GRID,
}}
>
<PDFText
style={[
styles.industrialText,
{
fontWeight: "bold",
color: COLORS.CHARCOAL,
marginBottom: 4,
},
]}
>
Individuelle Analyse
</PDFText>
<PDFText style={styles.industrialText}>
Spezifische Prozesse werden auf technisches
Automatisierungspotenzial untersucht. Das Ergebnis liefert
Klarheit über die wirtschaftliche Sinnhaftigkeit einer
Umsetzung.
</PDFText>
</PDFView>
</PDFView>
</>
);
<PDFView style={styles.industrialCol}>
<IndustrialCard title="DOKUMENT-AUTOMATION">
<PDFText style={styles.industrialText}>
Erstellung von PDF-Angeboten, Berichten oder Protokollen in
Sekunden statt Stunden.
</PDFText>
</IndustrialCard>
<IndustrialCard title="EXCEL-LOGIK">
<PDFText style={styles.industrialText}>
Intelligente Tabellen und automatisierte Auswertungen
bestehender Datensätze.
</PDFText>
</IndustrialCard>
<IndustrialCard title="KI-ASSISTENZ">
<PDFText style={styles.industrialText}>
Effiziente Verarbeitung von analogen Dokumenten oder
handschriftlichen Notizen mittels KI.
</PDFText>
</IndustrialCard>
</PDFView>
</>
) : (
<PDFView style={{ width: "100%" }}>
<PDFText style={styles.industrialTextLead}>
Bereitstellung einer stabilen technischen Basis ohne
Abhängigkeiten von Baukasten-Systemen oder Agenturen.
</PDFText>
<PDFText style={styles.industrialText}>
Entwicklung performanter Frontends und skalierbarer Backends. Die
Auslieferung erfolgt als kontrollierbarer und nachhaltiger
Quellcode.
</PDFText>
</PDFView>
)}
</PDFView>
</>
);
};

View File

@@ -1,29 +1,69 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
import { DocumentTitle, COLORS, FONT_SIZES } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
StyleSheet,
} from "@react-pdf/renderer";
import { DocumentTitle, COLORS, FONT_SIZES } from "../SharedUI";
const styles = StyleSheet.create({
section: { marginBottom: 24 },
sectionTitle: { fontSize: FONT_SIZES.BODY + 1, fontWeight: 'bold', marginBottom: 8, color: COLORS.CHARCOAL },
visionText: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_MAIN, lineHeight: 1.8, textAlign: 'justify' },
section: { marginBottom: 24 },
sectionTitle: {
fontSize: FONT_SIZES.LABEL,
fontWeight: "bold",
marginBottom: 8,
color: COLORS.CHARCOAL,
},
visionText: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_MAIN,
lineHeight: 1.4,
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: FONT_SIZES.BODY, color: COLORS.TEXT_MAIN, lineHeight: 1.6, textAlign: 'justify' }}>{state.briefingSummary}</PDFText>
</PDFView>
)}
{state.designVision && (
<PDFView style={[styles.section, { padding: 12, borderLeftWidth: 2, borderLeftColor: COLORS.DIVIDER, backgroundColor: COLORS.GRID }]}>
<PDFText style={[styles.sectionTitle, { color: COLORS.CHARCOAL, marginBottom: 4 }]}>Strategische Vision</PDFText>
<PDFText style={[styles.visionText, { lineHeight: 1.6 }]}>{state.designVision}</PDFText>
</PDFView>
)}
</>
<>
<DocumentTitle title="Projektdetails" isHero={true} />
{state.briefingSummary && (
<PDFView style={styles.section}>
<PDFText style={styles.sectionTitle}>Briefing Analyse</PDFText>
<PDFText
style={{
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_MAIN,
lineHeight: 1.6,
textAlign: "justify",
}}
>
{state.briefingSummary}
</PDFText>
</PDFView>
)}
{state.designVision && (
<PDFView
style={[
styles.section,
{
padding: 12,
borderLeftWidth: 2,
borderLeftColor: COLORS.DIVIDER,
backgroundColor: COLORS.GRID,
},
]}
>
<PDFText
style={[
styles.sectionTitle,
{ color: COLORS.CHARCOAL, marginBottom: 4 },
]}
>
Strategische Vision
</PDFText>
<PDFText style={styles.visionText}>{state.designVision}</PDFText>
</PDFView>
)}
</>
);

View File

@@ -1,106 +1,443 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, StyleSheet, Image as PDFImage } from '@react-pdf/renderer';
import { DocumentTitle, Divider, COLORS, FONT_SIZES } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
StyleSheet,
Image as PDFImage,
} from "@react-pdf/renderer";
import {
DocumentTitle,
Divider,
COLORS,
FONT_SIZES,
TechnicalSpec,
AsymmetryView,
} from "../SharedUI";
const styles = StyleSheet.create({
section: { marginBottom: 16 },
pricingGrid: { marginTop: 12 },
pricingRow: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: COLORS.DIVIDER, paddingVertical: 12, alignItems: 'flex-start' },
pricingTitle: { width: '30%', fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.CHARCOAL, paddingRight: 15 },
pricingDesc: { width: '55%', fontSize: FONT_SIZES.SUB, color: COLORS.TEXT_DIM, lineHeight: 1.5, paddingRight: 10 },
pricingTag: { width: '15%', fontSize: FONT_SIZES.BODY, fontWeight: 'bold', textAlign: 'right', color: COLORS.CHARCOAL },
configLabel: { fontSize: FONT_SIZES.BLUEPRINT, color: COLORS.TEXT_LIGHT, textTransform: 'uppercase', marginBottom: 8 },
section: { marginBottom: 24 },
moduleLabel: {
fontSize: FONT_SIZES.LABEL,
fontWeight: "bold",
color: COLORS.CHARCOAL,
letterSpacing: 0.5,
marginBottom: 4,
},
moduleDesc: {
fontSize: FONT_SIZES.SMALL,
color: COLORS.TEXT_DIM,
lineHeight: 1.5,
},
ledgerRow: {
paddingVertical: 14,
borderBottomWidth: 1,
borderBottomColor: COLORS.GRID,
flexDirection: "row",
alignItems: "flex-start",
},
ledgerPrice: {
fontSize: FONT_SIZES.BODY,
fontWeight: "bold",
color: COLORS.CHARCOAL,
},
ledgerUnit: {
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_LIGHT,
marginLeft: 2,
},
});
export const techPageModule = ({ techDetails, headerIcon }: any) => (
export const techPageModule = ({ techDetails }: any) => {
// Focus on the first 3 items as "Featured Specs", the rest as high-density grid
const featured = techDetails?.slice(0, 3) || [];
const rest = techDetails?.slice(3) || [];
return (
<>
<DocumentTitle title="Technische Umsetzung" />
<PDFView style={styles.section}>
<PDFView style={styles.pricingGrid}>
{techDetails?.map((item: any, i: number) => (
<PDFView key={i} style={styles.pricingRow}>
<PDFText style={[styles.pricingTitle, { width: '30%' }]}>{item.t}</PDFText>
<PDFText style={[styles.pricingDesc, { width: '70%', paddingRight: 0 }]}>{item.d}</PDFText>
</PDFView>
<DocumentTitle title="Technische Umsetzung" isHero={true} />
<PDFView style={styles.section}>
{/* FEATURED SPECS - Editorial focus */}
{featured.map((item: any, i: number) => (
<PDFView key={i} style={{ marginBottom: 24 }}>
<PDFText
style={[
styles.moduleLabel,
{ color: COLORS.BLUEPRINT, fontSize: FONT_SIZES.TINY },
]}
>
FOKUS_{i + 1}
</PDFText>
<PDFText
style={[
styles.moduleLabel,
{ fontSize: FONT_SIZES.HEADING, marginBottom: 8 },
]}
>
{item.t}
</PDFText>
<PDFText
style={[
styles.moduleDesc,
{ fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_MAIN },
]}
>
{item.d}
</PDFText>
</PDFView>
))}
<Divider style={{ marginVertical: 24, backgroundColor: COLORS.GRID }} />
{/* TECHNICAL GRID - High density */}
<PDFView style={{ flexDirection: "row", flexWrap: "wrap", gap: 12 }}>
{rest.map((item: any, i: number) => (
<PDFView
key={i}
style={{
width: "31%",
padding: 10,
backgroundColor: "#fdfdfd",
borderBottomWidth: 1,
borderBottomColor: COLORS.BLUEPRINT,
}}
>
<PDFText
style={[
styles.moduleLabel,
{
fontSize: FONT_SIZES.TINY,
marginBottom: 4,
color: COLORS.TEXT_LIGHT,
},
]}
>
{item.t.toUpperCase()}
</PDFText>
<PDFText
style={[styles.moduleDesc, { fontSize: FONT_SIZES.TINY }]}
>
{item.d}
</PDFText>
</PDFView>
))}
</PDFView>
</PDFView>
</>
);
};
export const MaintenanceModule = ({ maintenanceDetails }: any) => (
<>
<DocumentTitle
title="Monitoring, Security & techn. Support"
isHero={true}
/>
<PDFView style={styles.section}>
<PDFView style={{ flexDirection: "row", flexWrap: "wrap", gap: 16 }}>
{maintenanceDetails?.map((item: any, i: number) => (
<PDFView
key={i}
style={{
width: "48%",
padding: 16,
backgroundColor: COLORS.GRID,
borderLeftWidth: 2,
borderLeftColor: COLORS.DIVIDER,
}}
>
<PDFText
style={[
styles.moduleLabel,
{
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_LIGHT,
marginBottom: 8,
},
]}
>
{item.t.toUpperCase()}
</PDFText>
<PDFText
style={[
styles.moduleDesc,
{
fontSize: FONT_SIZES.SMALL,
color: COLORS.CHARCOAL,
lineHeight: 1.4,
},
]}
>
{item.d}
</PDFText>
</PDFView>
))}
</PDFView>
</PDFView>
</>
);
export const StandardsModule = ({ standardsDetails, principles }: any) => {
const independence = standardsDetails?.find(
(s: any) => s.t === "Unabhängigkeit",
);
const others =
standardsDetails?.filter((s: any) => s.t !== "Unabhängigkeit") || [];
return (
<>
<DocumentTitle title="Meine Standards" isHero={true} />
<PDFView style={styles.section}>
{/* FOCUS: UNABHÄNGIGKEIT & PRINCIPLES */}
<AsymmetryView
left={
<PDFView>
<PDFText
style={[
styles.moduleLabel,
{
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_LIGHT,
marginBottom: 12,
},
]}
>
MODERNE PRINZIPIEN
</PDFText>
{principles?.map((p: any, i: number) => (
<PDFView key={i} style={{ marginBottom: 12 }}>
<PDFText
style={[styles.moduleLabel, { fontSize: FONT_SIZES.TINY }]}
>
{p.t.toUpperCase()}
</PDFText>
<PDFText
style={[
styles.moduleDesc,
{ fontSize: FONT_SIZES.TINY, opacity: 0.8 },
]}
>
{p.d}
</PDFText>
</PDFView>
))}
</PDFView>
}
right={
<PDFView>
{/* HERO BOX: UNABHÄNGIGKEIT */}
{independence && (
<PDFView
style={{
backgroundColor: COLORS.CHARCOAL,
padding: 24,
marginBottom: 32,
borderLeftWidth: 4,
borderLeftColor: COLORS.TEXT_LIGHT,
}}
>
<PDFText
style={{
fontSize: FONT_SIZES.HEADING,
fontWeight: "bold",
color: COLORS.WHITE,
letterSpacing: 1,
marginBottom: 12,
}}
>
{independence.t.toUpperCase()}
</PDFText>
<PDFText
style={{
fontSize: FONT_SIZES.BODY,
color: COLORS.WHITE,
lineHeight: 1.4,
opacity: 0.9,
}}
>
{independence.d}
</PDFText>
</PDFView>
)}
{/* VERTICAL STACK OF OTHER STANDARDS */}
<PDFView>
{others.map((item: any, i: number) => (
<PDFView
key={i}
style={{
marginBottom: 16,
borderBottomWidth: 1,
borderBottomColor: COLORS.GRID,
paddingBottom: 12,
}}
>
<PDFText style={styles.moduleLabel}>{item.t}</PDFText>
<PDFText style={styles.moduleDesc}>{item.d}</PDFText>
</PDFView>
))}
</PDFView>
</PDFView>
</PDFView>
}
/>
</PDFView>
</>
);
);
};
export const TransparenzModule = ({ pricing }: any) => (
export const TransparenzModule = ({ pricing }: any) => {
const sorglosPrice = (pricing.HOSTING_MONTHLY || 250) * 12;
return (
<>
<DocumentTitle title="Preis-Transparenz & Modell" />
<PDFView style={styles.section}>
<PDFView style={styles.pricingGrid}>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Fundament</PDFText>
<PDFText style={styles.pricingDesc}>Setup, Infrastruktur, Hosting, SEO-Basics, Staging & Live-Umgebungen.</PDFText>
<PDFText style={styles.pricingTag}>{pricing.BASE_WEBSITE?.toLocaleString('de-DE')} </PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Seiten</PDFText>
<PDFText style={styles.pricingDesc}>Layout & Umsetzung individueller Seiten. Responsive Design / Cross-Browser.</PDFText>
<PDFText style={styles.pricingTag}>{pricing.PAGE?.toLocaleString('de-DE')} / Stk</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Features</PDFText>
<PDFText style={styles.pricingDesc}>Abgeschlossene Systeme (z. B. Blog, Jobs, Produkte) inkl. Datenstruktur.</PDFText>
<PDFText style={styles.pricingTag}>{pricing.FEATURE?.toLocaleString('de-DE')} / Stk</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Funktionen</PDFText>
<PDFText style={styles.pricingDesc}>Logik-Einheiten wie Filter, Suchen oder Kontakt-Schnittstellen.</PDFText>
<PDFText style={styles.pricingTag}>{pricing.FUNCTION?.toLocaleString('de-DE')} / Stk</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Schnittstellen</PDFText>
<PDFText style={styles.pricingDesc}>Anbindung externer Systeme (CRM, ERP, Payment) zur Synchronisation.</PDFText>
<PDFText style={styles.pricingTag}>ab {pricing.API_INTEGRATION?.toLocaleString('de-DE')} / Stk</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>CMS Setup</PDFText>
<PDFText style={styles.pricingDesc}>Konfiguration Headless CMS zur unabhängigen Datenpflege aller Module.</PDFText>
<PDFText style={pricing.CMS_SETUP ? styles.pricingTag : [styles.pricingTag, { color: COLORS.TEXT_LIGHT }]}>{pricing.CMS_SETUP?.toLocaleString('de-DE')} </PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Inszenierung</PDFText>
<PDFText style={styles.pricingDesc}>Interaktions-Mechanismen, Konfiguratoren oder visuelles Storytelling.</PDFText>
<PDFText style={styles.pricingTag}>ab {pricing.VISUAL_STAGING?.toLocaleString('de-DE')} </PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Sprachen</PDFText>
<PDFText style={styles.pricingDesc}>Skalierung der System-Architektur auf zusätzliche Sprachversionen.</PDFText>
<PDFText style={styles.pricingTag}>+20% / Sprache</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Initial-Pflege</PDFText>
<PDFText style={styles.pricingDesc}>Manuelle Aufbereitung & Übernahme von Datensätzen in das Zielsystem.</PDFText>
<PDFText style={styles.pricingTag}>{pricing.NEW_DATASET?.toLocaleString('de-DE')} / Stk</PDFText>
</PDFView>
<PDFView style={styles.pricingRow}>
<PDFText style={styles.pricingTitle}>Sorglos-Paket</PDFText>
<PDFText style={styles.pricingDesc}>Betrieb, Hosting, Updates & Monitoring gemäß AGB Punkt 7a.</PDFText>
<PDFText style={styles.pricingTag}>Inklusive 1 Jahr</PDFText>
</PDFView>
<DocumentTitle title="Preis-Transparenz & Modell" isHero={true} />
<PDFView style={styles.section}>
<PDFView style={{ borderTopWidth: 1, borderTopColor: COLORS.CHARCOAL }}>
{[
{
l: "Fundament",
d: "Bereitstellung der techn. Infrastruktur & System-Umgebung.",
p: pricing.BASE_WEBSITE,
},
{
l: "Einzelseiten",
d: "Individuelle Gestaltung, Layout & responsive Struktur.",
p: pricing.PAGE,
unit: "/ Stk",
},
{
l: "Core Features",
d: "Geschlossene Datensysteme mit eigener Datenstruktur.",
p: pricing.FEATURE,
unit: "/ Stk",
},
{
l: "Logik & Funktionen",
d: "Interaktive Funktions-Bausteine & Prozess-Logik.",
p: pricing.FUNCTION,
unit: "/ Stk",
},
{
l: "Schnittstellen",
d: "Synchronisation mit externen Zielsystemen.",
p: pricing.API_INTEGRATION,
unit: "/ Stk",
},
{
l: "Inhalts-Verwaltung",
d: "Schnittstelle zur eigenständigen Daten-Pflege (optional).",
p: pricing.CMS_CONNECTION_PER_FEATURE,
unit: "/ Stk",
},
{
l: "Sprachversionen",
d: "Skalierung der System-Architektur auf Zweit-Sprachen.",
p: "+20%",
isLang: true,
},
{
l: "Initial-Pflege",
d: "Konvertierung & Aufbereitung von Bestandsdaten.",
p: pricing.NEW_DATASET,
unit: "/ Stk",
},
{
l: "Sorglos Betrieb",
d: "Hosting, Instandhaltung, Security & techn. Support.",
p: sorglosPrice,
unit: "/ Jahr",
},
].map((item: any, i: number) => (
<PDFView key={i} style={styles.ledgerRow}>
<PDFView style={{ width: "25%" }}>
<PDFText style={styles.moduleLabel}>
{item.l.toUpperCase()}
</PDFText>
</PDFView>
<PDFView style={{ width: "50%" }}>
<PDFText style={styles.moduleDesc}>{item.d}</PDFText>
</PDFView>
<PDFView style={{ width: "25%", alignItems: "flex-end" }}>
<PDFText style={styles.ledgerPrice}>
{typeof item.p === "number"
? `${item.p.toLocaleString("de-DE")}`
: item.p}
{item.unit && (
<PDFText style={styles.ledgerUnit}> {item.unit}</PDFText>
)}
</PDFText>
{item.sub && (
<PDFText
style={[
styles.moduleDesc,
{
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_LIGHT,
marginTop: 2,
},
]}
>
{item.sub}
</PDFText>
)}
</PDFView>
</PDFView>
))}
</PDFView>
</PDFView>
</>
);
);
};
export const PrinciplesModule = ({ principles }: any) => (
<>
<DocumentTitle title="Prinzipien & Standards" />
<PDFView style={[styles.pricingGrid, { marginTop: 8 }]}>
{principles?.map((item: any, i: number) => (
<PDFView key={i} style={styles.pricingRow}>
<PDFText style={[styles.pricingTitle, { width: '30%' }]}>{item.t}</PDFText>
<PDFText style={[styles.pricingDesc, { width: '70%', paddingRight: 0 }]}>{item.d}</PDFText>
</PDFView>
))}
export const ClosingModule = () => (
<>
<DocumentTitle title="Abschluss & Kontakt" isHero={true} />
<PDFView style={styles.section}>
<PDFText
style={[
styles.moduleLabel,
{ fontSize: FONT_SIZES.HEADING, marginBottom: 12 },
]}
>
Vielen Dank für Ihr Interesse!
</PDFText>
<PDFText style={styles.moduleDesc}>
Die aufgeführten Positionen stellen eine detaillierte Schätzung auf
Basis unseres aktuellen Stands dar. Sollten sich Anforderungen ändern
oder Sie Fragen zu einzelnen Details haben, lassen Sie uns die
Positionen gerne gemeinsam besprechen.
</PDFText>
<PDFView
style={{
marginTop: 24,
padding: 16,
backgroundColor: COLORS.GRID,
borderLeftWidth: 2,
borderLeftColor: COLORS.DIVIDER,
}}
>
<PDFText style={[styles.moduleLabel, { marginBottom: 8 }]}>
Haben Sie Fragen?
</PDFText>
<PDFText style={styles.moduleDesc}>
Ich erkläre Ihnen gerne noch einmal persönlich, was die technische
Umsetzung für Ihr Projekt bedeutet und wie wir die nächsten Schritte
gemeinsam gehen können.
</PDFText>
<PDFView style={{ marginTop: 16 }}>
<PDFText style={styles.moduleLabel}>Kontakt:</PDFText>
<PDFText
style={[
styles.moduleDesc,
{ color: COLORS.CHARCOAL, fontWeight: "bold" },
]}
>
Marc Mintel marc@mintel.me
</PDFText>
</PDFView>
</>
</PDFView>
</PDFView>
</>
);

View File

@@ -1,55 +1,159 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
import { DocumentTitle } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
StyleSheet,
} from "@react-pdf/renderer";
import { DocumentTitle, COLORS, FONT_SIZES } from "../SharedUI";
const styles = StyleSheet.create({
table: { marginTop: 12 },
tableHeader: { flexDirection: 'row', paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: '#334155', marginBottom: 12 },
tableRow: { flexDirection: 'row', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#f8fafc', 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: '#334155', 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: '#334155', alignItems: 'baseline' },
table: { marginTop: 12 },
tableHeader: {
flexDirection: "row",
paddingBottom: 8,
borderBottomWidth: 1,
borderBottomColor: COLORS.CHARCOAL,
marginBottom: 12,
},
tableRow: {
flexDirection: "row",
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: COLORS.GRID,
alignItems: "flex-start",
},
colPos: { width: "8%" },
colDesc: { width: "62%" },
colQty: { width: "10%", textAlign: "center" },
colPrice: { width: "20%", textAlign: "right" },
headerText: {
fontSize: FONT_SIZES.TINY,
fontWeight: "bold",
textTransform: "uppercase",
letterSpacing: 1,
color: COLORS.TEXT_DIM,
},
posText: { fontSize: FONT_SIZES.TINY, color: COLORS.TEXT_LIGHT },
itemTitle: {
fontSize: FONT_SIZES.LABEL,
fontWeight: "bold",
color: COLORS.CHARCOAL,
marginBottom: 4,
},
itemDesc: {
fontSize: FONT_SIZES.SMALL,
color: COLORS.TEXT_DIM,
lineHeight: 1.4,
},
priceText: {
fontSize: FONT_SIZES.BODY,
fontWeight: "bold",
color: COLORS.CHARCOAL,
},
summaryContainer: {
borderTopWidth: 1,
borderTopColor: COLORS.CHARCOAL,
paddingTop: 8,
},
summaryRow: {
flexDirection: "row",
justifyContent: "flex-end",
paddingVertical: 4,
alignItems: "baseline",
},
summaryLabel: {
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_DIM,
textTransform: "uppercase",
letterSpacing: 1,
fontWeight: "bold",
marginRight: 12,
},
summaryValue: {
fontSize: FONT_SIZES.BODY,
fontWeight: "bold",
width: 100,
textAlign: "right",
color: COLORS.CHARCOAL,
},
totalRow: {
flexDirection: "row",
justifyContent: "flex-end",
paddingTop: 12,
marginTop: 8,
borderTopWidth: 2,
borderTopColor: COLORS.CHARCOAL,
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>
))}
export const EstimationModule = ({
state,
positions,
totalPrice,
date,
}: any) => (
<>
<DocumentTitle
title="Kostenschätzung"
subLines={[
`Datum: ${date}`,
`Projekt: ${state.projectType === "website" ? "Website" : "Web App"}`,
]}
isHero={true}
/>
<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 style={styles.summaryContainer} wrap={false}>
<PDFView style={styles.summaryRow}><PDFText style={styles.summaryLabel}>Nettobetrag</PDFText><PDFText style={styles.summaryValue}>{totalPrice.toLocaleString('de-DE')} </PDFText></PDFView>
<PDFView style={styles.summaryRow}><PDFText style={styles.summaryLabel}>Umsatzsteuer (19%)</PDFText><PDFText style={styles.summaryValue}>{(totalPrice * 0.19).toLocaleString('de-DE')} </PDFText></PDFView>
<PDFView style={styles.totalRow}><PDFText style={styles.summaryLabel}>Gesamtbetrag (Brutto)</PDFText><PDFText style={[styles.summaryValue, { fontSize: 14 }]}>{(totalPrice * 1.19).toLocaleString('de-DE')} </PDFText></PDFView>
</PDFView>
</>
))}
</PDFView>
<PDFView style={styles.summaryContainer} wrap={false}>
<PDFView style={styles.summaryRow}>
<PDFText style={styles.summaryLabel}>Nettobetrag</PDFText>
<PDFText style={styles.summaryValue}>
{totalPrice.toLocaleString("de-DE")}
</PDFText>
</PDFView>
<PDFView style={styles.summaryRow}>
<PDFText style={styles.summaryLabel}>Umsatzsteuer (19%)</PDFText>
<PDFText style={styles.summaryValue}>
{(totalPrice * 0.19).toLocaleString("de-DE")}
</PDFText>
</PDFView>
<PDFView style={styles.totalRow}>
<PDFText style={styles.summaryLabel}>Gesamtbetrag (Brutto)</PDFText>
<PDFText
style={[styles.summaryValue, { fontSize: FONT_SIZES.HEADING }]}
>
{(totalPrice * 1.19).toLocaleString("de-DE")}
</PDFText>
</PDFView>
</PDFView>
</>
);

View File

@@ -1,81 +1,92 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, Image as PDFImage, StyleSheet } from '@react-pdf/renderer';
import { COLORS, FONT_SIZES } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
Image as PDFImage,
StyleSheet,
} from "@react-pdf/renderer";
import { COLORS, FONT_SIZES } from "../SharedUI";
const styles = StyleSheet.create({
titlePage: {
flex: 1, // Fill the whole page
padding: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: COLORS.WHITE,
},
titleBrandIcon: {
width: 80,
height: 80,
backgroundColor: COLORS.CHARCOAL,
borderRadius: 16,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 40,
},
brandIconText: {
fontSize: 40,
color: COLORS.WHITE,
fontWeight: 'bold'
},
titleProjectName: {
fontSize: FONT_SIZES.H1,
fontWeight: 'bold',
color: COLORS.CHARCOAL,
marginBottom: 16,
textAlign: 'center',
maxWidth: '85%',
lineHeight: 1.2,
},
titleCustomerName: {
fontSize: FONT_SIZES.H3,
color: COLORS.TEXT_DIM,
marginBottom: 40,
textAlign: 'center',
maxWidth: '80%',
},
titleDocumentType: {
fontSize: FONT_SIZES.BODY + 1, // ~10
color: COLORS.TEXT_LIGHT,
textTransform: 'uppercase',
letterSpacing: 4,
marginBottom: 12,
},
titleDivider: {
width: 40,
height: 2,
backgroundColor: COLORS.CHARCOAL,
marginBottom: 40,
},
titleDate: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_LIGHT,
marginTop: 40,
},
titlePage: {
flex: 1, // Fill the whole page
padding: 60,
justifyContent: "center",
alignItems: "center",
backgroundColor: COLORS.WHITE,
},
titleBrandIcon: {
width: 80,
height: 80,
backgroundColor: COLORS.CHARCOAL,
borderRadius: 16,
alignItems: "center",
justifyContent: "center",
marginBottom: 40,
},
brandIconText: {
fontSize: 40,
color: COLORS.WHITE,
fontWeight: "bold",
},
titleProjectName: {
fontSize: FONT_SIZES.HERO,
fontWeight: "bold",
color: COLORS.CHARCOAL,
marginBottom: 16,
textAlign: "center",
maxWidth: "85%",
lineHeight: 1.2,
},
titleCustomerName: {
fontSize: FONT_SIZES.HEADING,
color: COLORS.TEXT_DIM,
marginBottom: 40,
textAlign: "center",
maxWidth: "80%",
},
titleDocumentType: {
fontSize: FONT_SIZES.BODY + 1, // ~10
color: COLORS.TEXT_LIGHT,
textTransform: "uppercase",
letterSpacing: 4,
marginBottom: 12,
},
titleDivider: {
width: 40,
height: 2,
backgroundColor: COLORS.CHARCOAL,
marginBottom: 40,
},
titleDate: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_LIGHT,
marginTop: 40,
},
});
export const FrontPageModule = ({ state, headerIcon, date }: any) => {
const fullTitle = `Digitale Webpräsenz für\n${state.companyName || "Ihr Projekt"}`;
const fullTitle = `Digitale Webpräsenz für\n${state.companyName || "Ihr Projekt"}`;
// Responsive font size based on length
const fontSize = fullTitle.length > 60 ? 14 : fullTitle.length > 40 ? 18 : 22;
// Responsive font size based on length
const fontSize = fullTitle.length > 60 ? 14 : fullTitle.length > 40 ? 18 : 22;
return (
<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.titleProjectName, { fontSize }]}>{fullTitle}</PDFText>
<PDFView style={{ marginBottom: 40 }} />
<PDFText style={styles.titleDate}>{date} | Marc Mintel</PDFText>
</PDFView>
);
return (
<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.titleProjectName, { fontSize }]}>
{fullTitle}
</PDFText>
<PDFView style={{ marginBottom: 40 }} />
<PDFText style={styles.titleDate}>{date} | Marc Mintel</PDFText>
</PDFView>
);
};

View File

@@ -1,77 +1,125 @@
'use client';
"use client";
import * as React from 'react';
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
import { DocumentTitle, Divider, COLORS, FONT_SIZES } from '../SharedUI';
import * as React from "react";
import {
View as PDFView,
Text as PDFText,
StyleSheet,
} from "@react-pdf/renderer";
import { DocumentTitle, Divider, COLORS, FONT_SIZES } from "../SharedUI";
const styles = StyleSheet.create({
section: { marginBottom: 32 },
intro: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 24, textAlign: 'justify' },
sitemapTree: { marginTop: 8 },
rootNode: {
padding: 12,
backgroundColor: COLORS.GRID,
marginBottom: 20,
borderLeftWidth: 2,
borderLeftColor: COLORS.CHARCOAL
},
rootTitle: { fontSize: FONT_SIZES.H3, fontWeight: 'bold', color: COLORS.CHARCOAL, letterSpacing: 0.5 },
categorySection: { marginBottom: 20 },
categoryHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 6,
borderBottomWidth: 1,
borderBottomColor: COLORS.BLUEPRINT,
marginBottom: 10
},
categoryIcon: { width: 8, height: 8, backgroundColor: COLORS.GRID, borderInlineWidth: 1, borderColor: COLORS.DIVIDER, marginRight: 10 },
categoryTitle: { fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.CHARCOAL, textTransform: 'uppercase', letterSpacing: 1 },
pagesGrid: { flexDirection: 'row', flexWrap: 'wrap' },
pageCard: {
width: '48%',
marginRight: '2%',
marginBottom: 12,
padding: 10,
borderWidth: 1,
borderColor: COLORS.GRID,
backgroundColor: '#fafafa'
},
pageTitle: { fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.TEXT_MAIN, marginBottom: 2 },
pageDesc: { fontSize: FONT_SIZES.TINY, color: COLORS.TEXT_DIM, lineHeight: 1.4 },
section: { marginBottom: 32 },
intro: {
fontSize: FONT_SIZES.BODY,
color: COLORS.TEXT_DIM,
lineHeight: 1.4,
marginBottom: 24,
textAlign: "justify",
},
sitemapTree: { marginTop: 8 },
rootNode: {
padding: 12,
backgroundColor: COLORS.GRID,
marginBottom: 20,
borderLeftWidth: 2,
borderLeftColor: COLORS.CHARCOAL,
},
rootTitle: {
fontSize: FONT_SIZES.HEADING,
fontWeight: "bold",
color: COLORS.CHARCOAL,
letterSpacing: 0.5,
},
categorySection: { marginBottom: 20 },
categoryHeader: {
flexDirection: "row",
alignItems: "center",
paddingBottom: 6,
borderBottomWidth: 1,
borderBottomColor: COLORS.BLUEPRINT,
marginBottom: 10,
},
categoryIcon: {
width: 8,
height: 8,
backgroundColor: COLORS.GRID,
borderInlineWidth: 1,
borderColor: COLORS.DIVIDER,
marginRight: 10,
},
categoryTitle: {
fontSize: FONT_SIZES.BODY,
fontWeight: "bold",
color: COLORS.CHARCOAL,
textTransform: "uppercase",
letterSpacing: 1,
},
pagesGrid: { flexDirection: "row", flexWrap: "wrap" },
pageCard: {
width: "48%",
marginRight: "2%",
marginBottom: 12,
padding: 10,
borderWidth: 1,
borderColor: COLORS.GRID,
backgroundColor: "#fafafa",
},
pageTitle: {
fontSize: FONT_SIZES.BODY,
fontWeight: "bold",
color: COLORS.TEXT_MAIN,
marginBottom: 4,
},
pageDesc: {
fontSize: FONT_SIZES.TINY,
color: COLORS.TEXT_DIM,
lineHeight: 1.3,
},
});
export const SitemapModule = ({ state }: any) => (
<>
<DocumentTitle title="Informationsarchitektur" />
<PDFView style={styles.section}>
<PDFText style={styles.intro}>
Die folgende Struktur definiert die logische Hierarchie und Benutzerführung. Sie dient als Bauplan für die technische Umsetzung und stellt sicher, dass alle relevanten Geschäftsbereiche intuitiv auffindbar sind.
</PDFText>
<>
<DocumentTitle title="Informationsarchitektur" isHero={true} />
<PDFView style={styles.section}>
<PDFText style={styles.intro}>
Die folgende Struktur definiert die logische Hierarchie und
Benutzerführung. Sie dient als Bauplan für die technische Umsetzung und
stellt sicher, dass alle relevanten Geschäftsbereiche intuitiv
auffindbar sind.
</PDFText>
<PDFView style={styles.sitemapTree}>
<PDFView style={styles.rootNode}>
<PDFText style={styles.rootTitle}>Seitenstruktur</PDFText>
</PDFView>
{state.sitemap?.map((cat: any, i: number) => (
<PDFView key={i} style={styles.categorySection} wrap={false}>
<PDFView style={styles.categoryHeader}>
<PDFView style={styles.categoryIcon} />
<PDFText style={styles.categoryTitle}>{cat.category}</PDFText>
</PDFView>
<PDFView style={styles.pagesGrid}>
{cat.pages.map((p: any, j: number) => (
<PDFView key={j} style={[styles.pageCard, j % 2 === 1 ? { marginRight: 0 } : {}]}>
<PDFText style={styles.pageTitle}>{p.title}</PDFText>
{p.desc && <PDFText style={styles.pageDesc}>{p.desc}</PDFText>}
</PDFView>
))}
</PDFView>
</PDFView>
))}
</PDFView>
<PDFView style={styles.sitemapTree}>
<PDFView style={styles.rootNode}>
<PDFText style={styles.rootTitle}>Seitenstruktur</PDFText>
</PDFView>
</>
{state.sitemap?.map((cat: any, i: number) => (
<PDFView key={i} style={styles.categorySection} wrap={false}>
<PDFView style={styles.categoryHeader}>
<PDFView style={styles.categoryIcon} />
<PDFText style={styles.categoryTitle}>{cat.category}</PDFText>
</PDFView>
<PDFView style={styles.pagesGrid}>
{cat.pages.map((p: any, j: number) => (
<PDFView
key={j}
style={[
styles.pageCard,
j % 2 === 1 ? { marginRight: 0 } : {},
]}
>
<PDFText style={styles.pageTitle}>{p.title}</PDFText>
{p.desc && (
<PDFText style={styles.pageDesc}>{p.desc}</PDFText>
)}
</PDFView>
))}
</PDFView>
</PDFView>
))}
</PDFView>
</PDFView>
</>
);