chore: overhaul infrastructure and integrate @mintel packages
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
- Restructure to pnpm monorepo (site moved to apps/web) - Integrate @mintel/tsconfig, @mintel/eslint-config, @mintel/husky-config - Implement Docker service architecture (Varnish, Directus, Gatekeeper) - Setup environment-aware Gitea Actions deployment
This commit is contained in:
108
apps/web/src/components/pdf/modules/BrandingModules.tsx
Normal file
108
apps/web/src/components/pdf/modules/BrandingModules.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
'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';
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
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 }} />
|
||||
|
||||
<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={[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";
|
||||
|
||||
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>
|
||||
)}
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
29
apps/web/src/components/pdf/modules/BriefingModule.tsx
Normal file
29
apps/web/src/components/pdf/modules/BriefingModule.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'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';
|
||||
|
||||
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' },
|
||||
});
|
||||
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
106
apps/web/src/components/pdf/modules/CommonModules.tsx
Normal file
106
apps/web/src/components/pdf/modules/CommonModules.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
'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';
|
||||
|
||||
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 },
|
||||
});
|
||||
|
||||
export const techPageModule = ({ techDetails, headerIcon }: any) => (
|
||||
<>
|
||||
<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>
|
||||
))}
|
||||
</PDFView>
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
|
||||
export const TransparenzModule = ({ pricing }: any) => (
|
||||
<>
|
||||
<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>
|
||||
</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>
|
||||
))}
|
||||
</PDFView>
|
||||
</>
|
||||
);
|
||||
|
||||
55
apps/web/src/components/pdf/modules/EstimationModule.tsx
Normal file
55
apps/web/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: '#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' },
|
||||
});
|
||||
|
||||
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}>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>
|
||||
</>
|
||||
);
|
||||
81
apps/web/src/components/pdf/modules/FrontPageModule.tsx
Normal file
81
apps/web/src/components/pdf/modules/FrontPageModule.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
'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';
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
|
||||
export const FrontPageModule = ({ state, headerIcon, date }: any) => {
|
||||
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;
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
77
apps/web/src/components/pdf/modules/SitemapModule.tsx
Normal file
77
apps/web/src/components/pdf/modules/SitemapModule.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
'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';
|
||||
|
||||
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 },
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
Reference in New Issue
Block a user