refactor: Centralize PDF styling with COLORS and FONT_SIZES and enhance module content and dynamic title generation.
This commit is contained in:
@@ -271,12 +271,12 @@ Output language: GERMAN (Strict).
|
|||||||
Focus 100% on the BRIEFING text provided by the user. Use the DISTILLED_CRAWL only as background context for terms or company details. If there is a conflict, the BRIEFING is the absolute source of truth.
|
Focus 100% on the BRIEFING text provided by the user. Use the DISTILLED_CRAWL only as background context for terms or company details. If there is a conflict, the BRIEFING is the absolute source of truth.
|
||||||
|
|
||||||
### OBJECTIVES:
|
### OBJECTIVES:
|
||||||
- Extract companyName (Strictly the name, no descriptors).
|
- Extract **companyName**: The full legal and brand name (e.g., "E-TIB GmbH"). Use signatures and crawl data.
|
||||||
- Extract companyAddress (Full address if found).
|
- Extract **personName**: The name of the primary human contact (e.g., "Danny Joseph"). **CRITICAL**: Check email signatures and "Mit freundlichen Grüßen" blocks. DO NOT use "Sie", "Firma" or generic terms if a name exists.
|
||||||
- Extract personName (Primary contact if found).
|
- Extract **existingWebsite**: The primary URL mentioned in the briefing or signature (e.g., "www.e-tib.com").
|
||||||
- Extract **websiteTopic**: This MUST be a single, short branch name (e.g., "Kabeltiefbau", "Logistik", "Anwaltskanzlei"). ABSOLUTELY NO SENTENCES.
|
- Extract **websiteTopic**: A short descriptor of the CORE BUSINESS (e.g., "Kabeltiefbau"). MAX 3 WORDS.
|
||||||
- Map to internal IDs for selectedPages, features, functions, apiSystems, assets.
|
- **isRelaunch**: Set to TRUE if the briefing mentions an existing website, a URL, or if the company is an established entity (e.g. "Gruppe", "GmbH seit 20XX"). Assume a presence exists that needs a modern "Zentrale Webpräsenz".
|
||||||
- Identify if isRelaunch is true (briefing mentions existing site or URL).
|
- **CRITICAL LOGIC**: If a URL is mentioned, isRelaunch MUST be TRUE.
|
||||||
- For all textual values (deadline, websiteTopic, targetAudience etc.): USE GERMAN.
|
- For all textual values (deadline, websiteTopic, targetAudience etc.): USE GERMAN.
|
||||||
- **multilang**: ONLY if the briefing mentions multiple target languages (e.g., DE/EN).
|
- **multilang**: ONLY if the briefing mentions multiple target languages (e.g., DE/EN).
|
||||||
- **maps**: If "Google Maps" or location maps are mentioned or implicit (Contact page).
|
- **maps**: If "Google Maps" or location maps are mentioned or implicit (Contact page).
|
||||||
@@ -295,6 +295,7 @@ Focus 100% on the BRIEFING text provided by the user. Use the DISTILLED_CRAWL on
|
|||||||
"companyName": string,
|
"companyName": string,
|
||||||
"companyAddress": string,
|
"companyAddress": string,
|
||||||
"personName": string,
|
"personName": string,
|
||||||
|
"existingWebsite": string,
|
||||||
"websiteTopic": string,
|
"websiteTopic": string,
|
||||||
"isRelaunch": boolean,
|
"isRelaunch": boolean,
|
||||||
"selectedPages": string[],
|
"selectedPages": string[],
|
||||||
@@ -305,7 +306,7 @@ Focus 100% on the BRIEFING text provided by the user. Use the DISTILLED_CRAWL on
|
|||||||
"deadline": string (GERMAN),
|
"deadline": string (GERMAN),
|
||||||
"targetAudience": "B2B" | "B2C" | "Internal" | string (GERMAN),
|
"targetAudience": "B2B" | "B2C" | "Internal" | string (GERMAN),
|
||||||
"expectedAdjustments": "low" | "medium" | "high" | string (GERMAN),
|
"expectedAdjustments": "low" | "medium" | "high" | string (GERMAN),
|
||||||
"employeeCount": string
|
"employeeCount": "ca. 10+" | "ca. 50+" | "ca. 100+" | "ca. 250+" | "ca. 500+" | "ca. 1000+"
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const pass1UserPrompt = `BRIEFING (TRUTH SOURCE):\n${briefing}\n\nCOMMENTS:\n${comments}\n\nDISTILLED_CRAWL (CONTEXT ONLY):\n${distilledCrawl}`;
|
const pass1UserPrompt = `BRIEFING (TRUTH SOURCE):\n${briefing}\n\nCOMMENTS:\n${comments}\n\nDISTILLED_CRAWL (CONTEXT ONLY):\n${distilledCrawl}`;
|
||||||
@@ -352,25 +353,26 @@ ${JSON.stringify(facts, null, 2)}
|
|||||||
// 3. PASS 3: Strategic Content (Bespoke Strategy)
|
// 3. PASS 3: Strategic Content (Bespoke Strategy)
|
||||||
console.log(' ↳ Pass 3: Strategic Content (Bespoke Strategy)...');
|
console.log(' ↳ Pass 3: Strategic Content (Bespoke Strategy)...');
|
||||||
const pass3SystemPrompt = `
|
const pass3SystemPrompt = `
|
||||||
You are a high-end Digital Architect. Analyze the BRIEFING.
|
You are a high-end Digital Architect. Your goal is to make the CUSTOMER feel 100% understood.
|
||||||
ABSOLUTE RULE: OUTPUT MUST BE 100% GERMAN.
|
Analyze the BRIEFING and the EXISTING WEBSITE context.
|
||||||
|
|
||||||
### TONE & COMMUNICATION PRINCIPLES (MANDATORY):
|
### TONE & COMMUNICATION PRINCIPLES (STRICT):
|
||||||
${tone}
|
${tone}
|
||||||
|
|
||||||
### OBJECTIVE:
|
### OBJECTIVE:
|
||||||
1. **briefingSummary**: Summarize the project's essence for the CUSTOMER.
|
1. **briefingSummary**: A deep, respectful summary of the status quo and the target state.
|
||||||
- FOLLOW PRINCIPLE 1, 5 & 6: Clear, direct, no marketing fluff.
|
- **LENGTH**: EXACTLY TWO PARAGRAPHS. Minimum 8 sentences total.
|
||||||
- **TONE**: Write naturally in the Ich-Form. Avoid starting every sentence with "Ich".
|
- **MIRROR TEST**: Acknowledge the EXISTING website specifically. Why does the new project make sense NOW?
|
||||||
- **MIRROR TEST**: Capture unique customer "hooks" or personality.
|
- **ABSOLUTE RULE**: DO NOT claim "keine digitale Repräsentation", "erstmals abgebildet", "Erstplatzierung" or "kommunikative Lücke" regarding existence if Pass 1 identified this as a RELAUNCH (isRelaunch=true). EXPLICITLY acknowledge the existing context and the NEED FOR EVOLUTION/MODERNIZATION.
|
||||||
- Focus 100% on the BRIEFING (TRUTH SOURCE).
|
- **RESPECT**: Explicitly incorporate the customer's expressed wishes.
|
||||||
2. **designVision**: A solid, grounded, and high-quality description of the look & feel.
|
- **ABSOLUTE RULE**: DO NOT INVENT DETAILS. Do not mention specific people (e.g., "Frieder Helmich"), software versions, or internal details NOT present in the briefing.
|
||||||
- FOLLOW PRINCIPLE 1, 3 & 6: Fact-based, professional, high density of information.
|
- **TONE**: Natural Ich-Form. Clear, direct, zero marketing fluff.
|
||||||
- **TONE**: Natural Ich-Form. Focus on the execution and technical decisions.
|
2. **designVision**: A high-density, professional vision of the future execution.
|
||||||
- **BESPOKE ELEMENTS**: If the client mentions specific layout ideas, incorporate these.
|
- **LENGTH**: EXACTLY TWO PARAGRAPHS. Minimum 6 sentences total.
|
||||||
- **NO ARROGANCE**: Eliminate all "high-end", "world-class" language.
|
- **INVESTMENT VALUE**: Plant a clear picture of a stable, high-quality system.
|
||||||
- **SIMPLE & CLEAR**: Use simple German. No buzzwords.
|
- **TECHNICAL PRECISION**: Focus on execution (Typografie, Visual Logic, Performance).
|
||||||
- 3-5 sentences of deep analysis.
|
- **NO FLUFF**: Do NOT focus on "Full-Screen Hero Video" as the main thing. Focus on the FIRM's essence and how we translate it into a professional tool.
|
||||||
|
- **ABSOLUTE RULE**: NO HALLUCINATIONS. Stay general yet precise. No "Verzicht auf Stockmaterial" unless explicitly stated.
|
||||||
|
|
||||||
### OUTPUT FORMAT (Strict JSON):
|
### OUTPUT FORMAT (Strict JSON):
|
||||||
{
|
{
|
||||||
@@ -380,7 +382,10 @@ ${tone}
|
|||||||
`;
|
`;
|
||||||
const p3Resp = await axios.post('https://openrouter.ai/api/v1/chat/completions', {
|
const p3Resp = await axios.post('https://openrouter.ai/api/v1/chat/completions', {
|
||||||
model: 'google/gemini-3-flash-preview',
|
model: 'google/gemini-3-flash-preview',
|
||||||
messages: [{ role: 'system', content: pass3SystemPrompt }, { role: 'user', content: `BRIEFING (TRUTH SOURCE):\n${briefing}` }],
|
messages: [
|
||||||
|
{ role: 'system', content: pass3SystemPrompt },
|
||||||
|
{ role: 'user', content: `BRIEFING (TRUTH SOURCE):\n${briefing}\n\nEXISTING WEBSITE (CONTEXT):\n${distilledCrawl}\n\nEXTRACTED FACTS:\n${JSON.stringify(facts, null, 2)}` }
|
||||||
|
],
|
||||||
response_format: { type: 'json_object' }
|
response_format: { type: 'json_object' }
|
||||||
}, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' } });
|
}, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' } });
|
||||||
addUsage(p3Resp.data);
|
addUsage(p3Resp.data);
|
||||||
@@ -455,10 +460,12 @@ Each position in the quote must be perfectly justified and detailed.
|
|||||||
- **BAD**: "Ich programmiere Scroll-Effekte."
|
- **BAD**: "Ich programmiere Scroll-Effekte."
|
||||||
- **GOOD**: "Visuelle Inszenierung der Meilensteine durch Scroll-aktivierte Timeline-Elemente."
|
- **GOOD**: "Visuelle Inszenierung der Meilensteine durch Scroll-aktivierte Timeline-Elemente."
|
||||||
|
|
||||||
### FORBIDDEN PHRASES:
|
### POSITION RULES (STRICT):
|
||||||
- "Erweiterte Formulare", "Verschiedene Funktionen", "Allgemeine Logik", "Optimierte Darstellung", "Individuelle Formular-Logik".
|
1. **Basis Website Setup**: This position MUST ALWAYS contain exactly these 7 points: "Projekt-Setup & Infrastruktur, Hosting-Bereitstellung, Grundstruktur & Design-Vorlage, technisches SEO-Basics, Analytics (mit automatischem Mail-Report), Testing-, Staging- & Production-Umgebung, Livegang."
|
||||||
|
2. **Sorglos-Betrieb (Hosting)**: Describe the service (Hosting, SSL, Security-Updates, 24/7 Monitoring, Portfolio-Update-Service). NEVER mention "Inklusive Basis-Infrastruktur".
|
||||||
11. **NO "MARKETING LINGO"**: Never say "avoids branding" or "maximizes performance". Say "Implements HTML5 Video Player". ALWAYS DESCRIBE THE TASK.
|
3. **LOGIC CONSISTENCY**: If Pass 1 extracted 1 function, you MUST describe exactly 1 function scope. If you describe two things (e.g., "Formular AND Search") but the count is 1, it is a FAIL.
|
||||||
|
4. **SIMPLICITY**: Write in "Simple German". High density of information, but easy for a CEO. No jargon.
|
||||||
|
5. **NO IMPLEMENTATION DETAILS**: Focus on WHAT is done, not HOW (no libraries, no technical "under-the-hood" talk).
|
||||||
|
|
||||||
### DATA CONTEXT:
|
### DATA CONTEXT:
|
||||||
${JSON.stringify({ facts, details, strategy, ia }, null, 2)}
|
${JSON.stringify({ facts, details, strategy, ia }, null, 2)}
|
||||||
@@ -483,23 +490,15 @@ You are the "Industrial Critic". Your goal is to catch quality regressions and e
|
|||||||
Analyze the CURRENT_STATE against the BRIEFING_TRUTH.
|
Analyze the CURRENT_STATE against the BRIEFING_TRUTH.
|
||||||
|
|
||||||
### CRITICAL ERROR CHECKLIST (FAIL IF FOUND):
|
### CRITICAL ERROR CHECKLIST (FAIL IF FOUND):
|
||||||
1. **Placeholder Leakage**: Catch "null", "undefined", or generic strings like "Verschiedene Funktionen", "Erweiterte Formulare".
|
1. **Hallucination Leakage**: FAIL if names of people (e.g., "Frieder Helmich"), specific software versions, or invented details are used unless they appear EXACTLY in the BRIEFING.
|
||||||
2. **Detail Loss**: The user mentioned specific terms. Are they present? If not, ADD THEM.
|
- **CRITICAL**: Forbid "Sie", "Ansprechpartner" or "Unternehmen" for personName if a name IS in the briefing. If none is in briefing, use empty string.
|
||||||
3. **Consistency**: Ensure the count of pages in "Individuelle Seiten" matches the sitemap pages.
|
2. **Logic Conflict**: FAIL if isRelaunch is true but briefingSummary claims no website exists or uses phrases like "Da aktuell keine digitale Repräsentation vorliegt", "erstmals abgebildet", "Erstplatzierung" or "Lücke schließen" (regarding existence).
|
||||||
4. **Deadlines**: Ensure relative dates (e.g., "April / Mai") are resolved to the year 2026.
|
- FAIL if the description in positionDescriptions mentions more items than extracted in facts.
|
||||||
5. **TONE & WORDING FAILURE**:
|
3. **Implementation Fluff**: FAIL if "React", "Next.js", "TypeScript", "Tailwind" or other tech-stack details are mentioned. Focus on Concept & Result.
|
||||||
- FAIL if "Ich" or "Mein" is used in positionDescriptions.
|
4. **Length Check**: Briefing and Vision MUST be significantly long (EXACTLY 2 paragraphs each, minimum 8 sentences for briefing, 6 for vision).
|
||||||
- FAIL if "wir" or "unser" is used anywhere.
|
|
||||||
- FAIL if a sentence in briefingSummary is too long or marketing-heavy.
|
|
||||||
- FAIL if "möglicherweise", "grundsätzlich", "in der Regel" is used.
|
|
||||||
- FAIL if passive voice ("es wird") is used.
|
|
||||||
6. **MAPPING FAILURE**:
|
|
||||||
- FAIL if visual elements (Scroll-Effekte, Slider, Hover) are in "Logik-Funktionen". They MUST be in "Visuelle Inszenierung".
|
|
||||||
- FAIL if multi-step forms or calculators are in "Logik-Funktionen". They MUST be in "Komplexe Interaktion".
|
|
||||||
7. **PROMPT REWRITE**: If you find any of these errors, rewrite the field entirely to be 100% compliant.
|
|
||||||
|
|
||||||
### MISSION:
|
### MISSION:
|
||||||
Return updated fields ONLY. Specifically focus on hardening 'positionDescriptions', 'sitemap', and 'briefingSummary'.
|
Return updated fields ONLY. Specifically focus on hardening 'positionDescriptions', 'sitemap', 'briefingSummary', and 'designVision'.
|
||||||
|
|
||||||
### DATA CONTEXT:
|
### DATA CONTEXT:
|
||||||
${JSON.stringify({ facts, strategy, ia, positionsData }, null, 2)}
|
${JSON.stringify({ facts, strategy, ia, positionsData }, null, 2)}
|
||||||
@@ -527,7 +526,12 @@ ${JSON.stringify({ facts, strategy, ia, positionsData }, null, 2)}
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cleanedReflection = unwrap(reflection);
|
const cleanedReflection = unwrap(reflection);
|
||||||
return { ...result, ...cleanedReflection };
|
Object.entries(cleanedReflection).forEach(([key, value]) => {
|
||||||
|
if (value && value !== "" && value !== "null") {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
let finalState = mergeReflection({
|
let finalState = mergeReflection({
|
||||||
|
|||||||
@@ -78,43 +78,50 @@ export const EstimationPDF = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Full Portfolio Mode
|
// Full Portfolio Mode
|
||||||
|
let pageCounter = 1;
|
||||||
|
const getPageNum = () => (pageCounter++).toString().padStart(2, '0');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PDFPage size="A4" style={pdfStyles.page}>
|
<PDFPage size="A4" style={pdfStyles.titlePage}>
|
||||||
<FrontPageModule state={state} headerIcon={headerIcon} date={date} />
|
<FrontPageModule state={state} headerIcon={headerIcon} date={date} />
|
||||||
</PDFPage>
|
</PDFPage>
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="01">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<BriefingModule state={state} />
|
<BriefingModule state={state} />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
|
|
||||||
{state.sitemap && state.sitemap.length > 0 && (
|
{state.sitemap && state.sitemap.length > 0 && (
|
||||||
<SimpleLayout {...commonProps} pageNumber="03">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<SitemapModule state={state} />
|
<SitemapModule state={state} />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="04">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<EstimationModule state={state} positions={positions} totalPrice={totalPrice} date={date} />
|
<EstimationModule state={state} positions={positions} totalPrice={totalPrice} date={date} />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="05">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<TransparenzModule pricing={pricing} />
|
<TransparenzModule pricing={pricing} />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="06">
|
{techDetails && techDetails.length > 0 && (
|
||||||
<TechPageModule techDetails={techDetails} headerIcon={headerIcon} />
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
</SimpleLayout>
|
<TechPageModule techDetails={techDetails} headerIcon={headerIcon} />
|
||||||
|
</SimpleLayout>
|
||||||
|
)}
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="07">
|
{principles && principles.length > 0 && (
|
||||||
<PrinciplesModule principles={principles} />
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
</SimpleLayout>
|
<PrinciplesModule principles={principles} />
|
||||||
|
</SimpleLayout>
|
||||||
|
)}
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="08">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<AboutModule />
|
<AboutModule />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber="09">
|
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
||||||
<CrossSellModule state={state} />
|
<CrossSellModule state={state} />
|
||||||
</SimpleLayout>
|
</SimpleLayout>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,23 +1,51 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import { View as PDFView, Text as PDFText, StyleSheet, Link as PDFLink, Image as PDFImage, Font } from '@react-pdf/renderer';
|
||||||
Text as PDFText,
|
|
||||||
View as PDFView,
|
|
||||||
StyleSheet as PDFStyleSheet,
|
|
||||||
Image as PDFImage
|
|
||||||
} from '@react-pdf/renderer';
|
|
||||||
|
|
||||||
export const pdfStyles = PDFStyleSheet.create({
|
// INDUSTRIAL DESIGN SYSTEM TOKENS
|
||||||
|
export const COLORS = {
|
||||||
|
CHARCOAL: '#0f172a', // Slate 900
|
||||||
|
TEXT_MAIN: '#334155', // Slate 700
|
||||||
|
TEXT_DIM: '#64748b', // Slate 500
|
||||||
|
TEXT_LIGHT: '#94a3b8', // Slate 400
|
||||||
|
DIVIDER: '#cbd5e1', // Slate 300
|
||||||
|
GRID: '#f1f5f9', // Slate 100
|
||||||
|
BLUEPRINT: '#e2e8f0', // Slate 200
|
||||||
|
WHITE: '#ffffff'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FONT_SIZES = {
|
||||||
|
H1: 24,
|
||||||
|
H2: 18,
|
||||||
|
H3: 12,
|
||||||
|
BODY: 9,
|
||||||
|
TINY: 7,
|
||||||
|
SUB: 8,
|
||||||
|
BLUEPRINT: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register a more technical font if possible, or use Helvetica with varying weights
|
||||||
|
// Note: helvetica-bold is standard in react-pdf
|
||||||
|
|
||||||
|
export const pdfStyles = StyleSheet.create({
|
||||||
page: {
|
page: {
|
||||||
paddingTop: 45, // DIN 5008
|
paddingTop: 45, // DIN 5008
|
||||||
paddingLeft: 70, // ~25mm
|
paddingLeft: 70, // ~25mm
|
||||||
paddingRight: 57, // ~20mm
|
paddingRight: 57, // ~20mm
|
||||||
paddingBottom: 80, // Safe buffer for absolute footer
|
paddingBottom: 80, // Safe buffer for absolute footer
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: COLORS.WHITE,
|
||||||
fontFamily: 'Helvetica',
|
fontFamily: 'Helvetica',
|
||||||
fontSize: 10,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#000000',
|
color: COLORS.CHARCOAL,
|
||||||
|
},
|
||||||
|
titlePage: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: COLORS.WHITE,
|
||||||
|
fontFamily: 'Helvetica',
|
||||||
|
color: COLORS.CHARCOAL,
|
||||||
|
padding: 0, // NO PADDING to prevent inner overflow page breaks
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -31,13 +59,13 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
marginTop: 45, // DIN 5008 positioning for window
|
marginTop: 45, // DIN 5008 positioning for window
|
||||||
},
|
},
|
||||||
senderLine: {
|
senderLine: {
|
||||||
fontSize: 7,
|
fontSize: FONT_SIZES.TINY,
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
color: '#666666',
|
color: COLORS.TEXT_DIM,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
recipientAddress: {
|
recipientAddress: {
|
||||||
fontSize: 10,
|
fontSize: FONT_SIZES.BODY,
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
},
|
},
|
||||||
brandLogoContainer: {
|
brandLogoContainer: {
|
||||||
@@ -47,14 +75,14 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
brandIconContainer: {
|
brandIconContainer: {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
backgroundColor: '#000000',
|
backgroundColor: '#0f172a',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
brandIconText: {
|
brandIconText: {
|
||||||
color: '#ffffff',
|
color: COLORS.WHITE,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
@@ -62,27 +90,27 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
},
|
},
|
||||||
mainTitle: {
|
mainTitle: {
|
||||||
fontSize: 12,
|
fontSize: FONT_SIZES.H3,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
color: '#000000',
|
color: COLORS.CHARCOAL,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
},
|
},
|
||||||
subTitle: {
|
subTitle: {
|
||||||
fontSize: 9,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#666666',
|
color: COLORS.TEXT_DIM,
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
},
|
},
|
||||||
section: {
|
section: {
|
||||||
marginBottom: 32,
|
marginBottom: 32,
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
fontSize: 8,
|
fontSize: FONT_SIZES.SUB,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
color: '#999999',
|
color: COLORS.TEXT_LIGHT,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
@@ -91,7 +119,7 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
left: 70,
|
left: 70,
|
||||||
right: 57,
|
right: 57,
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
borderTopColor: '#f1f5f9',
|
borderTopColor: COLORS.GRID,
|
||||||
paddingTop: 16,
|
paddingTop: 16,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
@@ -108,17 +136,17 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
footerText: {
|
footerText: {
|
||||||
fontSize: 7,
|
fontSize: FONT_SIZES.TINY,
|
||||||
color: '#94a3b8',
|
color: COLORS.TEXT_LIGHT,
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
},
|
},
|
||||||
footerLabel: {
|
footerLabel: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#64748b',
|
color: COLORS.TEXT_DIM,
|
||||||
},
|
},
|
||||||
pageNumber: {
|
pageNumber: {
|
||||||
fontSize: 7,
|
fontSize: FONT_SIZES.TINY,
|
||||||
color: '#cbd5e1',
|
color: COLORS.DIVIDER,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
@@ -128,14 +156,41 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
left: 20,
|
left: 20,
|
||||||
width: 10,
|
width: 10,
|
||||||
borderTopWidth: 0.5,
|
borderTopWidth: 0.5,
|
||||||
borderTopColor: '#cbd5e1',
|
borderTopColor: COLORS.DIVIDER,
|
||||||
},
|
},
|
||||||
divider: {
|
divider: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 1,
|
height: 1,
|
||||||
backgroundColor: '#f1f5f9',
|
backgroundColor: COLORS.DIVIDER,
|
||||||
marginVertical: 12,
|
marginVertical: 12,
|
||||||
},
|
},
|
||||||
|
blueprintGrid: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
zIndex: -10,
|
||||||
|
},
|
||||||
|
gridLineH: {
|
||||||
|
width: '100%',
|
||||||
|
height: 0.5,
|
||||||
|
backgroundColor: COLORS.GRID,
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
gridLineV: {
|
||||||
|
width: 0.5,
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: COLORS.GRID,
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
technicalMarker: {
|
||||||
|
position: 'absolute',
|
||||||
|
fontSize: FONT_SIZES.BLUEPRINT,
|
||||||
|
color: COLORS.BLUEPRINT,
|
||||||
|
fontFamily: 'Helvetica',
|
||||||
|
letterSpacing: 1,
|
||||||
|
},
|
||||||
// Atoms
|
// Atoms
|
||||||
industrialListItem: {
|
industrialListItem: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -145,47 +200,47 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
industrialBulletBox: {
|
industrialBulletBox: {
|
||||||
width: 6,
|
width: 6,
|
||||||
height: 6,
|
height: 6,
|
||||||
backgroundColor: '#0f172a',
|
backgroundColor: COLORS.CHARCOAL,
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
},
|
},
|
||||||
industrialTitle: {
|
industrialTitle: {
|
||||||
fontSize: 24,
|
fontSize: FONT_SIZES.H1,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#0f172a',
|
color: COLORS.CHARCOAL,
|
||||||
marginBottom: 6,
|
marginBottom: 6,
|
||||||
letterSpacing: -0.5,
|
letterSpacing: 0, // Reset for clarity
|
||||||
},
|
},
|
||||||
industrialSubtitle: {
|
industrialSubtitle: {
|
||||||
fontSize: 8,
|
fontSize: FONT_SIZES.SUB,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#94a3b8',
|
color: COLORS.TEXT_LIGHT,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
letterSpacing: 2,
|
letterSpacing: 2,
|
||||||
},
|
},
|
||||||
industrialTextLead: {
|
industrialTextLead: {
|
||||||
fontSize: 10,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#334155',
|
color: COLORS.TEXT_MAIN,
|
||||||
lineHeight: 1.6,
|
lineHeight: 1.6,
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
industrialText: {
|
industrialText: {
|
||||||
fontSize: 9,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#64748b',
|
color: COLORS.TEXT_DIM,
|
||||||
lineHeight: 1.6,
|
lineHeight: 1.6,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
industrialCard: {
|
industrialCard: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: '#e2e8f0',
|
borderColor: COLORS.BLUEPRINT,
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
industrialCardTitle: {
|
industrialCardTitle: {
|
||||||
fontSize: 10,
|
fontSize: FONT_SIZES.BODY + 1, // 10
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#0f172a',
|
color: COLORS.CHARCOAL,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
@@ -193,22 +248,30 @@ export const pdfStyles = PDFStyleSheet.create({
|
|||||||
darkBox: {
|
darkBox: {
|
||||||
marginTop: 32,
|
marginTop: 32,
|
||||||
padding: 24,
|
padding: 24,
|
||||||
backgroundColor: '#0f172a',
|
backgroundColor: COLORS.CHARCOAL,
|
||||||
color: '#ffffff',
|
color: COLORS.WHITE,
|
||||||
},
|
},
|
||||||
darkTitle: {
|
darkTitle: {
|
||||||
fontSize: 18,
|
fontSize: FONT_SIZES.H2,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#ffffff',
|
color: COLORS.WHITE,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
darkText: {
|
darkText: {
|
||||||
fontSize: 9,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#94a3b8',
|
color: COLORS.TEXT_LIGHT,
|
||||||
lineHeight: 1.6,
|
lineHeight: 1.6,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const styles = pdfStyles;
|
||||||
|
|
||||||
|
export const BlueprintBackground = () => (
|
||||||
|
<PDFView style={styles.blueprintGrid} fixed>
|
||||||
|
{/* Clean background - grid lines removed per user request */}
|
||||||
|
</PDFView>
|
||||||
|
);
|
||||||
|
|
||||||
export const IndustrialListItem = ({ children }: { children: React.ReactNode }) => (
|
export const IndustrialListItem = ({ children }: { children: React.ReactNode }) => (
|
||||||
<PDFView style={pdfStyles.industrialListItem}>
|
<PDFView style={pdfStyles.industrialListItem}>
|
||||||
<PDFView style={pdfStyles.industrialBulletBox} />
|
<PDFView style={pdfStyles.industrialBulletBox} />
|
||||||
@@ -238,5 +301,5 @@ export const Header = ({ sender, recipient, icon, showAddress = true }: { sender
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const DocumentTitle = ({ title, subLines }: { title: string; subLines?: string[] }) => (
|
export const DocumentTitle = ({ title, subLines }: { title: string; subLines?: string[] }) => (
|
||||||
<PDFView style={pdfStyles.titleInfo}><PDFText style={pdfStyles.mainTitle}>{title}</PDFText>{subLines?.map((line, i) => (<PDFText key={i} style={[pdfStyles.subTitle, i === 1 ? { fontWeight: 'bold', color: '#000000' } : {}]}>{line}</PDFText>))}</PDFView>
|
<PDFView style={pdfStyles.titleInfo}><PDFText style={pdfStyles.mainTitle}>{title}</PDFText>{subLines?.map((line, i) => (<PDFText key={i} style={[pdfStyles.subTitle, i === 1 ? { fontWeight: 'bold', color: '#0f172a' } : {}]}>{line}</PDFText>))}</PDFView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Page as PDFPage, View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
import { Page as PDFPage, View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||||
import { Header, Footer, pdfStyles } from './SharedUI';
|
import { Header, Footer, pdfStyles, BlueprintBackground } from './SharedUI';
|
||||||
|
|
||||||
const simpleStyles = StyleSheet.create({
|
const simpleStyles = StyleSheet.create({
|
||||||
industrialPage: {
|
industrialPage: {
|
||||||
@@ -47,6 +47,7 @@ export const SimpleLayout = ({
|
|||||||
}: SimpleLayoutProps) => {
|
}: SimpleLayoutProps) => {
|
||||||
return (
|
return (
|
||||||
<PDFPage size="A4" style={[pdfStyles.page, simpleStyles.industrialPage]}>
|
<PDFPage size="A4" style={[pdfStyles.page, simpleStyles.industrialPage]}>
|
||||||
|
<BlueprintBackground />
|
||||||
<Header icon={icon} showAddress={false} />
|
<Header icon={icon} showAddress={false} />
|
||||||
{pageNumber && <PDFText style={simpleStyles.industrialNumber}>{pageNumber}</PDFText>}
|
{pageNumber && <PDFText style={simpleStyles.industrialNumber}>{pageNumber}</PDFText>}
|
||||||
<PDFView style={simpleStyles.industrialSection}>
|
<PDFView style={simpleStyles.industrialSection}>
|
||||||
|
|||||||
@@ -2,22 +2,22 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||||
import { IndustrialListItem, IndustrialCard, Divider } from '../SharedUI';
|
import { IndustrialListItem, IndustrialCard, Divider, COLORS, FONT_SIZES } from '../SharedUI';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
industrialTitle: { fontSize: 24, fontWeight: 'bold', color: '#0f172a', marginBottom: 6, letterSpacing: -0.5 },
|
industrialTitle: { fontSize: FONT_SIZES.H1, fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 6, letterSpacing: -1 },
|
||||||
industrialSubtitle: { fontSize: 8, fontWeight: 'bold', color: '#94a3b8', textTransform: 'uppercase', marginBottom: 16, letterSpacing: 2 },
|
industrialSubtitle: { fontSize: FONT_SIZES.SUB, fontWeight: 'bold', color: COLORS.TEXT_LIGHT, textTransform: 'uppercase', marginBottom: 16, letterSpacing: 2 },
|
||||||
industrialTextLead: { fontSize: 10, color: '#334155', lineHeight: 1.6, marginBottom: 12 },
|
industrialTextLead: { fontSize: FONT_SIZES.H3, color: COLORS.TEXT_MAIN, lineHeight: 1.6, marginBottom: 12 },
|
||||||
industrialText: { fontSize: 9, color: '#64748b', lineHeight: 1.6, marginBottom: 8 },
|
industrialText: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 8 },
|
||||||
industrialGrid2: { flexDirection: 'row', gap: 32 },
|
industrialGrid2: { flexDirection: 'row', gap: 32 },
|
||||||
industrialCol: { width: '48%' },
|
industrialCol: { width: '48%' },
|
||||||
darkBox: { marginTop: 32, padding: 24, backgroundColor: '#0f172a', color: '#ffffff' },
|
darkBox: { marginTop: 32, padding: 24, backgroundColor: COLORS.CHARCOAL, color: COLORS.WHITE },
|
||||||
darkTitle: { fontSize: 18, fontWeight: 'bold', color: '#ffffff', marginBottom: 8 },
|
darkTitle: { fontSize: FONT_SIZES.H2, fontWeight: 'bold', color: COLORS.WHITE, marginBottom: 8 },
|
||||||
darkText: { fontSize: 9, color: '#94a3b8', lineHeight: 1.6 },
|
darkText: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_LIGHT, lineHeight: 1.6 },
|
||||||
industrialBulletBox: {
|
industrialBulletBox: {
|
||||||
width: 6,
|
width: 6,
|
||||||
height: 6,
|
height: 6,
|
||||||
backgroundColor: '#0f172a',
|
backgroundColor: COLORS.CHARCOAL,
|
||||||
marginRight: 8,
|
marginRight: 8,
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
},
|
},
|
||||||
@@ -27,16 +27,16 @@ export const AboutModule = () => (
|
|||||||
<>
|
<>
|
||||||
<PDFText style={styles.industrialTitle}>Über mich</PDFText>
|
<PDFText style={styles.industrialTitle}>Über mich</PDFText>
|
||||||
<PDFText style={styles.industrialSubtitle}>Direkt. Sauber. Verantwortlich.</PDFText>
|
<PDFText style={styles.industrialSubtitle}>Direkt. Sauber. Verantwortlich.</PDFText>
|
||||||
<Divider style={{ marginVertical: 16, backgroundColor: '#f8fafc' }} />
|
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
|
||||||
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
|
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
|
||||||
<PDFView style={styles.industrialCol}>
|
<PDFView style={styles.industrialCol}>
|
||||||
<PDFText style={styles.industrialTextLead}>Ich entwickle Websysteme seit 15 Jahren. Ich kenne Agenturen, Konzerne und Startups. Ich arbeite alleine, weil ich Verantwortung nicht teilen will.</PDFText>
|
<PDFText style={styles.industrialTextLead}>Seit 15 Jahren entstehen unter meiner Leitung Websysteme für Agenturen, Konzerne und Startups. Ich arbeite bewusst alleine, um die volle Verantwortung für jedes Projekt zu tragen.</PDFText>
|
||||||
<PDFText style={styles.industrialText}>Technik scheitert selten an Bits und Bytes. Sie scheitert an unklaren Zuständigkeiten. Ich bin Ihr einziger Ansprechpartner. Ich treffe die Entscheidungen und ich löse die Probleme.</PDFText>
|
<PDFText style={styles.industrialText}>Technik scheitert selten an Bits und Bytes, sondern meist an unklaren Zuständigkeiten. Als Ihr direkter Ansprechpartner treffe ich die notwendigen Entscheidungen und löse technische Probleme ohne Umwege.</PDFText>
|
||||||
<PDFView style={{ marginTop: 16 }}>
|
<PDFView style={{ marginTop: 16 }}>
|
||||||
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Mein Standard:</PDFText>
|
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Mein Standard:</PDFText>
|
||||||
<IndustrialListItem><PDFText style={styles.industrialText}>Ich liefere Code ohne Altlasten.</PDFText></IndustrialListItem>
|
<IndustrialListItem><PDFText style={styles.industrialText}>Code-Lieferung ohne technische Altlasten.</PDFText></IndustrialListItem>
|
||||||
<IndustrialListItem><PDFText style={styles.industrialText}>Ich baue Systeme, die ohne mich laufen.</PDFText></IndustrialListItem>
|
<IndustrialListItem><PDFText style={styles.industrialText}>Entwicklung von Systemen, die autark operieren.</PDFText></IndustrialListItem>
|
||||||
<IndustrialListItem><PDFText style={styles.industrialText}>Ich arbeite ohne Overhead und Stille Post.</PDFText></IndustrialListItem>
|
<IndustrialListItem><PDFText style={styles.industrialText}>Verzicht auf Overhead und Kommunikationsverluste.</PDFText></IndustrialListItem>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
<PDFView style={styles.industrialCol}>
|
<PDFView style={styles.industrialCol}>
|
||||||
@@ -45,10 +45,10 @@ export const AboutModule = () => (
|
|||||||
<IndustrialListItem><PDFText style={styles.industrialText}>Ich antworte direkt auf technische Fragen.</PDFText></IndustrialListItem>
|
<IndustrialListItem><PDFText style={styles.industrialText}>Ich antworte direkt auf technische Fragen.</PDFText></IndustrialListItem>
|
||||||
<IndustrialListItem><PDFText style={styles.industrialText}>Ich garantiere die Umsetzung.</PDFText></IndustrialListItem>
|
<IndustrialListItem><PDFText style={styles.industrialText}>Ich garantiere die Umsetzung.</PDFText></IndustrialListItem>
|
||||||
</IndustrialCard>
|
</IndustrialCard>
|
||||||
<IndustrialCard title="KEIN BALLAST" style={{ backgroundColor: '#ffffff', borderColor: '#cbd5e1' }}>
|
<IndustrialCard title="KEIN BALLAST" style={{ backgroundColor: COLORS.WHITE, borderColor: COLORS.DIVIDER }}>
|
||||||
<PDFText style={[styles.industrialText, { color: '#94a3b8' }]}>Keine Projektmanager.</PDFText>
|
<PDFText style={[styles.industrialText, { color: COLORS.TEXT_LIGHT }]}>Keine Projektmanager.</PDFText>
|
||||||
<PDFText style={[styles.industrialText, { color: '#94a3b8' }]}>Keine Vertriebler.</PDFText>
|
<PDFText style={[styles.industrialText, { color: COLORS.TEXT_LIGHT }]}>Keine Vertriebler.</PDFText>
|
||||||
<PDFText style={[styles.industrialText, { color: '#94a3b8' }]}>Kein Ticket-Chaos.</PDFText>
|
<PDFText style={[styles.industrialText, { color: COLORS.TEXT_LIGHT }]}>Kein Ticket-Chaos.</PDFText>
|
||||||
</IndustrialCard>
|
</IndustrialCard>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
@@ -64,16 +64,16 @@ export const CrossSellModule = ({ state }: any) => {
|
|||||||
<>
|
<>
|
||||||
<PDFText style={styles.industrialTitle}>{title}</PDFText>
|
<PDFText style={styles.industrialTitle}>{title}</PDFText>
|
||||||
<PDFText style={styles.industrialSubtitle}>{subtitle}</PDFText>
|
<PDFText style={styles.industrialSubtitle}>{subtitle}</PDFText>
|
||||||
<Divider style={{ marginVertical: 16, backgroundColor: '#f8fafc' }} />
|
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
|
||||||
<PDFView style={[styles.industrialGrid2, { marginTop: 12 }]} >
|
<PDFView style={[styles.industrialGrid2, { marginTop: 12 }]} >
|
||||||
{isWebsite ? (
|
{isWebsite ? (
|
||||||
<>
|
<>
|
||||||
<PDFView style={styles.industrialCol}>
|
<PDFView style={styles.industrialCol}>
|
||||||
<PDFText style={styles.industrialTextLead}>Ich identifiziere manuelle Abläufe in Ihrem Unternehmen und ersetze sie durch Software. Ich eliminiere Tippfehler und Zeitfresser.</PDFText>
|
<PDFText style={styles.industrialTextLead}>Manuelle Abläufe binden Kapazitäten. Durch maßgeschneiderte Software ersetze ich fehleranfällige Prozesse und eliminiere Zeitfresser in Ihrem Unternehmen.</PDFText>
|
||||||
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Ich schaffe Zeit für wertschöpfende Arbeit. Ich digitalisiere das Chaos.</PDFText>
|
<PDFText style={[styles.industrialText, { fontWeight: 'bold' }]}>Ziel ist der Gewinn wertvoller Arbeitszeit. Digitalisierung ordnet das Chaos.</PDFText>
|
||||||
<PDFView style={styles.darkBox}>
|
<PDFView style={styles.darkBox}>
|
||||||
<PDFText style={styles.darkTitle}>Direktes Feedback</PDFText>
|
<PDFText style={styles.darkTitle}>Individuelle Prüfung</PDFText>
|
||||||
<PDFText style={styles.darkText}>Ich prüfe Ihren Prozess innerhalb von 48 Stunden. Ich sage Ihnen sofort, ob eine Automatisierung wirtschaftlich sinnvoll ist.</PDFText>
|
<PDFText style={styles.darkText}>Ich analysiere Ihren spezifischen Prozess auf technisches Automatisierungspotenzial. Das Ergebnis liefert Klarheit darüber, ob eine Umsetzung wirtschaftlich sinnvoll ist.</PDFText>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
<PDFView style={styles.industrialCol}>
|
<PDFView style={styles.industrialCol}>
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||||
import { DocumentTitle } from '../SharedUI';
|
import { DocumentTitle, COLORS, FONT_SIZES } from '../SharedUI';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
section: { marginBottom: 24 },
|
section: { marginBottom: 24 },
|
||||||
sectionTitle: { fontSize: 10, fontWeight: 'bold', marginBottom: 8, color: '#0f172a' },
|
sectionTitle: { fontSize: FONT_SIZES.BODY + 1, fontWeight: 'bold', marginBottom: 8, color: COLORS.CHARCOAL },
|
||||||
configGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 6, marginTop: 6 },
|
configGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 6, marginTop: 6 },
|
||||||
configItem: { width: '24%', marginBottom: 4 },
|
configItem: { width: '24%', marginBottom: 4 },
|
||||||
configLabel: { fontSize: 5, color: '#94a3b8', textTransform: 'uppercase', marginBottom: 2 },
|
configLabel: { fontSize: FONT_SIZES.BLUEPRINT, color: COLORS.TEXT_LIGHT, textTransform: 'uppercase', marginBottom: 2 },
|
||||||
configValue: { fontSize: 7, color: '#0f172a', fontWeight: 'bold' },
|
configValue: { fontSize: FONT_SIZES.TINY, color: COLORS.CHARCOAL, fontWeight: 'bold' },
|
||||||
visionText: { fontSize: 9, color: '#334155', lineHeight: 1.8, textAlign: 'justify' },
|
visionText: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_MAIN, lineHeight: 1.8, textAlign: 'justify' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BriefingModule = ({ state }: any) => (
|
export const BriefingModule = ({ state }: any) => (
|
||||||
@@ -26,15 +26,15 @@ export const BriefingModule = ({ state }: any) => (
|
|||||||
<PDFView style={styles.section}>
|
<PDFView style={styles.section}>
|
||||||
<PDFText style={styles.sectionTitle}>Kern-Informationen</PDFText>
|
<PDFText style={styles.sectionTitle}>Kern-Informationen</PDFText>
|
||||||
<PDFView style={styles.configGrid}>
|
<PDFView style={styles.configGrid}>
|
||||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Ansprechpartner</PDFText><PDFText style={styles.configValue}>{state.personName || "Sie"}</PDFText></PDFView>
|
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Kontakt</PDFText><PDFText style={styles.configValue}>{state.personName || "—"}</PDFText></PDFView>
|
||||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Projektart</PDFText><PDFText style={styles.configValue}>{state.statusQuo || (state.existingWebsite ? 'Relaunch' : 'Neuentwicklung')}</PDFText></PDFView>
|
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Projekttyp</PDFText><PDFText style={styles.configValue}>{state.isRelaunch ? 'Website Evolution' : (state.statusQuo || 'Neukonzeption')}</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}>Mitarbeiter</PDFText><PDFText style={styles.configValue}>{state.employeeCount || "—"}</PDFText></PDFView>
|
||||||
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Zeitplan</PDFText><PDFText style={styles.configValue}>{state.deadline || 'Flexibel'}</PDFText></PDFView>
|
<PDFView style={styles.configItem}><PDFText style={styles.configLabel}>Zeitplan</PDFText><PDFText style={styles.configValue}>{state.deadline || 'Flexibel'}</PDFText></PDFView>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
{state.designVision && (
|
{state.designVision && (
|
||||||
<PDFView style={[styles.section, { padding: 16, borderLeftWidth: 2, borderLeftColor: '#000000', backgroundColor: '#f9fafb' }]}>
|
<PDFView style={[styles.section, { padding: 16, borderLeftWidth: 2, borderLeftColor: COLORS.DIVIDER, backgroundColor: COLORS.GRID }]}>
|
||||||
<PDFText style={[styles.sectionTitle, { color: '#000000' }]}>Strategische Vision</PDFText>
|
<PDFText style={[styles.sectionTitle, { color: COLORS.CHARCOAL }]}>Strategische Vision</PDFText>
|
||||||
<PDFText style={styles.visionText}>{state.designVision}</PDFText>
|
<PDFText style={styles.visionText}>{state.designVision}</PDFText>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View as PDFView, Text as PDFText, StyleSheet, Image as PDFImage } from '@react-pdf/renderer';
|
import { View as PDFView, Text as PDFText, StyleSheet, Image as PDFImage } from '@react-pdf/renderer';
|
||||||
import { DocumentTitle, Divider } from '../SharedUI';
|
import { DocumentTitle, Divider, COLORS, FONT_SIZES } from '../SharedUI';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
section: { marginBottom: 24 },
|
section: { marginBottom: 24 },
|
||||||
pricingGrid: { marginTop: 24 },
|
pricingGrid: { marginTop: 24 },
|
||||||
pricingRow: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: '#f1f5f9', paddingVertical: 10, alignItems: 'flex-start' },
|
pricingRow: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: COLORS.DIVIDER, paddingVertical: 10, alignItems: 'flex-start' },
|
||||||
pricingTitle: { width: '30%', fontSize: 9, fontWeight: 'bold', color: '#0f172a' },
|
pricingTitle: { width: '30%', fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.CHARCOAL },
|
||||||
pricingDesc: { width: '55%', fontSize: 8, color: '#64748b', lineHeight: 1.4 },
|
pricingDesc: { width: '55%', fontSize: FONT_SIZES.SUB, color: COLORS.TEXT_DIM, lineHeight: 1.4 },
|
||||||
pricingTag: { width: '15%', fontSize: 9, fontWeight: 'bold', textAlign: 'right' },
|
pricingTag: { width: '15%', fontSize: FONT_SIZES.BODY, fontWeight: 'bold', textAlign: 'right', color: COLORS.CHARCOAL },
|
||||||
configLabel: { fontSize: 5, color: '#94a3b8', textTransform: 'uppercase', marginBottom: 8 },
|
configLabel: { fontSize: FONT_SIZES.BLUEPRINT, color: COLORS.TEXT_LIGHT, 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 CHROME_ICON = '/Users/marcmintel/Projects/mintel.me/src/assets/browser/chrome.png'; // Fallback to a placeholder if not found
|
||||||
@@ -21,7 +21,7 @@ export const techPageModule = ({ techDetails, headerIcon }: any) => (
|
|||||||
<>
|
<>
|
||||||
<DocumentTitle title="Technische Umsetzung" />
|
<DocumentTitle title="Technische Umsetzung" />
|
||||||
<PDFView style={styles.section}>
|
<PDFView style={styles.section}>
|
||||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 16 }}>Ich entwickle Websites als moderne, performante Websysteme.</PDFText>
|
<PDFText style={{ fontSize: FONT_SIZES.SUB, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 16 }}>Ich entwickle Websites als moderne, performante Websysteme.</PDFText>
|
||||||
<PDFView style={styles.pricingGrid}>
|
<PDFView style={styles.pricingGrid}>
|
||||||
{techDetails?.map((item: any, i: number) => (
|
{techDetails?.map((item: any, i: number) => (
|
||||||
<PDFView key={i} style={styles.pricingRow}>
|
<PDFView key={i} style={styles.pricingRow}>
|
||||||
@@ -38,9 +38,9 @@ export const TransparenzModule = ({ pricing }: any) => (
|
|||||||
<>
|
<>
|
||||||
<DocumentTitle title="Preis-Transparenz & Modell" />
|
<DocumentTitle title="Preis-Transparenz & Modell" />
|
||||||
<PDFView style={styles.section}>
|
<PDFView style={styles.section}>
|
||||||
<PDFText style={{ fontSize: 10, fontWeight: 'bold', color: '#000000', marginBottom: 8 }}>Festpreise statt Stundenabrechnung</PDFText>
|
<PDFText style={{ fontSize: FONT_SIZES.BODY + 1, fontWeight: 'bold', color: COLORS.CHARCOAL, marginBottom: 8 }}>Festpreise statt Stundenabrechnung</PDFText>
|
||||||
<PDFText style={{ fontSize: 8, color: '#64748b', lineHeight: 1.6, marginBottom: 12 }}>Ich biete Planungssicherheit. Ich kalkuliere nach einem modularen Festpreis-System. Sie zahlen für Ergebnisse, nicht für die Zeit. Ich schließe versteckte Kosten aus.</PDFText>
|
<PDFText style={{ fontSize: FONT_SIZES.SUB, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 12 }}>Ich biete Planungssicherheit. Ich kalkuliere nach einem modularen Festpreis-System. Sie zahlen für Ergebnisse, nicht für die Zeit. Ich schließe versteckte Kosten aus.</PDFText>
|
||||||
<Divider style={{ backgroundColor: '#f8fafc', marginTop: 12 }} />
|
<Divider style={{ marginTop: 12 }} />
|
||||||
</PDFView>
|
</PDFView>
|
||||||
<PDFView style={styles.section}>
|
<PDFView style={styles.section}>
|
||||||
<PDFView style={styles.pricingGrid}>
|
<PDFView style={styles.pricingGrid}>
|
||||||
@@ -72,7 +72,7 @@ export const TransparenzModule = ({ pricing }: any) => (
|
|||||||
<PDFView style={styles.pricingRow}>
|
<PDFView style={styles.pricingRow}>
|
||||||
<PDFText style={styles.pricingTitle}>6. Integrationen</PDFText>
|
<PDFText style={styles.pricingTitle}>6. Integrationen</PDFText>
|
||||||
<PDFText style={styles.pricingDesc}>Ich binde Drittsysteme wie CRM, ERP oder Stripe an. Ich richte CMS-Schnittstellen zur unabhängigen Inhaltsverwaltung ein.</PDFText>
|
<PDFText style={styles.pricingDesc}>Ich binde Drittsysteme wie CRM, ERP oder Stripe an. Ich richte CMS-Schnittstellen zur unabhängigen Inhaltsverwaltung ein.</PDFText>
|
||||||
<PDFText style={styles.pricingTag}>ab {pricing.API_INTEGRATION?.toLocaleString('de-DE')} €</PDFText>
|
<PDFText style={styles.pricingTag}>ab {pricing.API_INTEGRATION?.toLocaleString('de-DE')} € / Stück</PDFText>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
<PDFView style={styles.pricingRow}>
|
<PDFView style={styles.pricingRow}>
|
||||||
<PDFText style={styles.pricingTitle}>7. Betrieb (12 Monate)</PDFText>
|
<PDFText style={styles.pricingTitle}>7. Betrieb (12 Monate)</PDFText>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DocumentTitle } from '../SharedUI';
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
table: { marginTop: 12 },
|
table: { marginTop: 12 },
|
||||||
tableHeader: { flexDirection: 'row', paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: '#000000', marginBottom: 12 },
|
tableHeader: { flexDirection: 'row', paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: '#334155', marginBottom: 12 },
|
||||||
tableRow: { flexDirection: 'row', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#f8fafc', alignItems: 'flex-start' },
|
tableRow: { flexDirection: 'row', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#f8fafc', alignItems: 'flex-start' },
|
||||||
colPos: { width: '8%' },
|
colPos: { width: '8%' },
|
||||||
colDesc: { width: '62%' },
|
colDesc: { width: '62%' },
|
||||||
@@ -17,11 +17,11 @@ const styles = StyleSheet.create({
|
|||||||
itemTitle: { fontSize: 10, fontWeight: 'bold', marginBottom: 4 },
|
itemTitle: { fontSize: 10, fontWeight: 'bold', marginBottom: 4 },
|
||||||
itemDesc: { fontSize: 8, color: '#666666', lineHeight: 1.4 },
|
itemDesc: { fontSize: 8, color: '#666666', lineHeight: 1.4 },
|
||||||
priceText: { fontSize: 10, fontWeight: 'bold' },
|
priceText: { fontSize: 10, fontWeight: 'bold' },
|
||||||
summaryContainer: { borderTopWidth: 1, borderTopColor: '#000000', paddingTop: 8 },
|
summaryContainer: { borderTopWidth: 1, borderTopColor: '#334155', paddingTop: 8 },
|
||||||
summaryRow: { flexDirection: 'row', justifyContent: 'flex-end', paddingVertical: 4, alignItems: 'baseline' },
|
summaryRow: { flexDirection: 'row', justifyContent: 'flex-end', paddingVertical: 4, alignItems: 'baseline' },
|
||||||
summaryLabel: { fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1, fontWeight: 'bold', marginRight: 12 },
|
summaryLabel: { fontSize: 7, color: '#64748b', textTransform: 'uppercase', letterSpacing: 1, fontWeight: 'bold', marginRight: 12 },
|
||||||
summaryValue: { fontSize: 9, fontWeight: 'bold', width: 100, textAlign: 'right' },
|
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' },
|
totalRow: { flexDirection: 'row', justifyContent: 'flex-end', paddingTop: 12, marginTop: 8, borderTopWidth: 2, borderTopColor: '#334155', alignItems: 'baseline' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EstimationModule = ({ state, positions, totalPrice, date }: any) => (
|
export const EstimationModule = ({ state, positions, totalPrice, date }: any) => (
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View as PDFView, Text as PDFText, Image as PDFImage, StyleSheet } from '@react-pdf/renderer';
|
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({
|
const styles = StyleSheet.create({
|
||||||
titlePage: {
|
titlePage: {
|
||||||
|
flex: 1, // Fill the whole page
|
||||||
padding: 60,
|
padding: 60,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '90%',
|
backgroundColor: COLORS.WHITE,
|
||||||
},
|
},
|
||||||
titleBrandIcon: {
|
titleBrandIcon: {
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 80,
|
height: 80,
|
||||||
backgroundColor: '#000000',
|
backgroundColor: COLORS.CHARCOAL,
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -21,53 +23,59 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
brandIconText: {
|
brandIconText: {
|
||||||
fontSize: 40,
|
fontSize: 40,
|
||||||
color: '#ffffff',
|
color: COLORS.WHITE,
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
},
|
},
|
||||||
titleProjectName: {
|
titleProjectName: {
|
||||||
fontSize: 24,
|
fontSize: FONT_SIZES.H1,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#0f172a',
|
color: COLORS.CHARCOAL,
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
textTransform: 'uppercase',
|
maxWidth: '85%',
|
||||||
letterSpacing: 2,
|
lineHeight: 1.2,
|
||||||
},
|
},
|
||||||
titleCustomerName: {
|
titleCustomerName: {
|
||||||
fontSize: 14,
|
fontSize: FONT_SIZES.H3,
|
||||||
color: '#64748b',
|
color: COLORS.TEXT_DIM,
|
||||||
marginBottom: 40,
|
marginBottom: 40,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
maxWidth: '80%',
|
||||||
},
|
},
|
||||||
titleDocumentType: {
|
titleDocumentType: {
|
||||||
fontSize: 10,
|
fontSize: FONT_SIZES.BODY + 1, // ~10
|
||||||
color: '#94a3b8',
|
color: COLORS.TEXT_LIGHT,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: 4,
|
letterSpacing: 4,
|
||||||
marginBottom: 8,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
titleDivider: {
|
titleDivider: {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 2,
|
height: 2,
|
||||||
backgroundColor: '#000000',
|
backgroundColor: COLORS.CHARCOAL,
|
||||||
marginBottom: 40,
|
marginBottom: 40,
|
||||||
},
|
},
|
||||||
titleDate: {
|
titleDate: {
|
||||||
fontSize: 9,
|
fontSize: FONT_SIZES.BODY,
|
||||||
color: '#94a3b8',
|
color: COLORS.TEXT_LIGHT,
|
||||||
marginTop: 'auto',
|
marginTop: 40,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const FrontPageModule = ({ state, headerIcon, date }: any) => (
|
export const FrontPageModule = ({ state, headerIcon, date }: any) => {
|
||||||
<PDFView style={styles.titlePage}>
|
const fullTitle = `Digitale Webpräsenz für\n${state.companyName || "Ihr Projekt"}`;
|
||||||
<PDFView style={styles.titleBrandIcon}>
|
|
||||||
{headerIcon ? <PDFImage src={headerIcon} style={{ width: 40, height: 40 }} /> : <PDFText style={styles.brandIconText}>M</PDFText>}
|
// 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={styles.titleDivider} />
|
||||||
|
<PDFText style={styles.titleDate}>{date} | Marc Mintel</PDFText>
|
||||||
</PDFView>
|
</PDFView>
|
||||||
<PDFText style={styles.titleDocumentType}>Konzept & Kostenschätzung</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 || "Ihr Projekt"}</PDFText>
|
|
||||||
<PDFText style={styles.titleDate}>{date} | Marc Mintel</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -2,32 +2,32 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
import { View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
||||||
import { DocumentTitle, Divider } from '../SharedUI';
|
import { DocumentTitle, Divider, COLORS, FONT_SIZES } from '../SharedUI';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
section: { marginBottom: 24 },
|
section: { marginBottom: 24 },
|
||||||
sitemapTree: { marginTop: 20 },
|
sitemapTree: { marginTop: 20 },
|
||||||
sitemapRootNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
|
sitemapRootNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
|
||||||
sitemapRootDot: { width: 6, height: 6, borderRadius: 3, backgroundColor: '#000000', marginRight: 10 },
|
sitemapRootDot: { width: 6, height: 6, borderRadius: 3, backgroundColor: COLORS.CHARCOAL, marginRight: 10 },
|
||||||
sitemapRootTitle: { fontSize: 10, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 },
|
sitemapRootTitle: { fontSize: FONT_SIZES.H3, fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1, color: COLORS.CHARCOAL },
|
||||||
sitemapMainLine: { position: 'absolute', left: 2, top: 20, bottom: 0, width: 0.5, backgroundColor: '#cbd5e1' },
|
sitemapMainLine: { position: 'absolute', left: 2, top: 20, bottom: 0, width: 0.5, backgroundColor: COLORS.DIVIDER },
|
||||||
sitemapBranch: { marginLeft: 20, marginBottom: 12, position: 'relative' },
|
sitemapBranch: { marginLeft: 20, marginBottom: 12, position: 'relative' },
|
||||||
sitemapNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 4 },
|
sitemapNode: { flexDirection: 'row', alignItems: 'center', marginBottom: 4 },
|
||||||
sitemapRootIcon: { width: 4, height: 4, backgroundColor: '#000000', marginRight: 8 },
|
sitemapRootIcon: { width: 4, height: 4, backgroundColor: COLORS.CHARCOAL, marginRight: 8 },
|
||||||
sitemapBranchTitle: { fontSize: 8, fontWeight: 'bold' },
|
sitemapBranchTitle: { fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.TEXT_MAIN },
|
||||||
sitemapLeaf: { marginLeft: 12, borderLeftWidth: 0.5, borderLeftColor: '#cbd5e1', paddingLeft: 12, marginTop: 4 },
|
sitemapLeaf: { marginLeft: 12, borderLeftWidth: 0.5, borderLeftColor: COLORS.DIVIDER, paddingLeft: 12, marginTop: 4 },
|
||||||
sitemapLeafNode: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 6 },
|
sitemapLeafNode: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 6 },
|
||||||
sitemapLeafPointer: { fontSize: 7, color: '#94a3b8', marginRight: 6 },
|
sitemapLeafPointer: { fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_LIGHT, marginRight: 6 },
|
||||||
sitemapLeafTitle: { fontSize: 7, fontWeight: 'bold' },
|
sitemapLeafTitle: { fontSize: FONT_SIZES.BODY, fontWeight: 'bold', color: COLORS.TEXT_MAIN },
|
||||||
sitemapLeafDesc: { fontSize: 6, color: '#64748b', lineHeight: 1.3, marginTop: 1 },
|
sitemapLeafDesc: { fontSize: FONT_SIZES.TINY, color: COLORS.TEXT_DIM, lineHeight: 1.3, marginTop: 1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SitemapModule = ({ state }: any) => (
|
export const SitemapModule = ({ state }: any) => (
|
||||||
<>
|
<>
|
||||||
<DocumentTitle title="Seitenstruktur" />
|
<DocumentTitle title="Seitenstruktur" />
|
||||||
<PDFView style={styles.section}>
|
<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>
|
<PDFText style={{ fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_DIM, lineHeight: 1.6, marginBottom: 16 }}>Die folgende Struktur bildet das Fundament für die Benutzerführung und Informationsarchitektur Ihres Projekts.</PDFText>
|
||||||
<Divider style={{ backgroundColor: '#f8fafc', marginBottom: 20 }} />
|
<Divider style={{ marginBottom: 20 }} />
|
||||||
<PDFView style={styles.sitemapTree}>
|
<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.sitemapRootNode}><PDFView style={styles.sitemapRootDot} /><PDFText style={styles.sitemapRootTitle}>{state.websiteTopic || 'Digitales Ökosystem'}</PDFText></PDFView>
|
||||||
<PDFView style={styles.sitemapMainLine} />
|
<PDFView style={styles.sitemapMainLine} />
|
||||||
|
|||||||
@@ -14,16 +14,25 @@ export function calculatePositions(state: FormState, pricing: any): Position[] {
|
|||||||
price: pricing.BASE_WEBSITE
|
price: pricing.BASE_WEBSITE
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPagesCount = (state.selectedPages?.length || 0) + (state.otherPages?.length || 0) + (state.otherPagesCount || 0);
|
const sitemapPagesCount = state.sitemap?.reduce((acc: number, cat: any) => acc + (cat.pages?.length || 0), 0) || 0;
|
||||||
|
const totalPagesCount = Math.max(
|
||||||
|
(state.selectedPages?.length || 0) + (state.otherPages?.length || 0) + (state.otherPagesCount || 0),
|
||||||
|
sitemapPagesCount
|
||||||
|
);
|
||||||
|
|
||||||
const allPages = [
|
const allPages = [
|
||||||
...(state.selectedPages || []).map((p: string) => PAGE_LABELS[p] || p),
|
...(state.selectedPages || []).map((p: string) => PAGE_LABELS[p] || p),
|
||||||
...(state.otherPages || [])
|
...(state.otherPages || []),
|
||||||
|
...(state.sitemap?.flatMap((cat: any) => cat.pages?.map((p: any) => p.title)) || [])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Deduplicate labels
|
||||||
|
const uniquePages = Array.from(new Set(allPages));
|
||||||
|
|
||||||
positions.push({
|
positions.push({
|
||||||
pos: pos++,
|
pos: pos++,
|
||||||
title: 'Individuelle Seiten',
|
title: 'Individuelle Seiten',
|
||||||
desc: `Gestaltung und Umsetzung von ${totalPagesCount} individuellen Seiten-Layouts (${allPages.join(', ')}).`,
|
desc: `Gestaltung und Umsetzung von ${totalPagesCount} individuellen Seiten-Layouts (${uniquePages.join(', ')}).`,
|
||||||
qty: totalPagesCount,
|
qty: totalPagesCount,
|
||||||
price: totalPagesCount * pricing.PAGE
|
price: totalPagesCount * pricing.PAGE
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,23 +3,23 @@
|
|||||||
"requestsFailed": 0,
|
"requestsFailed": 0,
|
||||||
"requestsRetries": 0,
|
"requestsRetries": 0,
|
||||||
"requestsFailedPerMinute": 0,
|
"requestsFailedPerMinute": 0,
|
||||||
"requestsFinishedPerMinute": 142,
|
"requestsFinishedPerMinute": 158,
|
||||||
"requestMinDurationMillis": 287,
|
"requestMinDurationMillis": 610,
|
||||||
"requestMaxDurationMillis": 1881,
|
"requestMaxDurationMillis": 2310,
|
||||||
"requestTotalFailedDurationMillis": 0,
|
"requestTotalFailedDurationMillis": 0,
|
||||||
"requestTotalFinishedDurationMillis": 11907,
|
"requestTotalFinishedDurationMillis": 12652,
|
||||||
"crawlerStartedAt": "2026-02-03T18:37:56.387Z",
|
"crawlerStartedAt": "2026-02-03T18:57:19.097Z",
|
||||||
"crawlerFinishedAt": "2026-02-03T18:37:59.745Z",
|
"crawlerFinishedAt": "2026-02-03T18:57:22.128Z",
|
||||||
"statsPersistedAt": "2026-02-03T18:37:59.745Z",
|
"statsPersistedAt": "2026-02-03T18:57:22.128Z",
|
||||||
"crawlerRuntimeMillis": 3370,
|
"crawlerRuntimeMillis": 3044,
|
||||||
"crawlerLastStartTimestamp": 1770143876375,
|
"crawlerLastStartTimestamp": 1770145039084,
|
||||||
"requestRetryHistogram": [
|
"requestRetryHistogram": [
|
||||||
8
|
8
|
||||||
],
|
],
|
||||||
"statsId": 0,
|
"statsId": 0,
|
||||||
"requestAvgFailedDurationMillis": null,
|
"requestAvgFailedDurationMillis": null,
|
||||||
"requestAvgFinishedDurationMillis": 1488,
|
"requestAvgFinishedDurationMillis": 1582,
|
||||||
"requestTotalDurationMillis": 11907,
|
"requestTotalDurationMillis": 12652,
|
||||||
"requestsTotal": 8,
|
"requestsTotal": 8,
|
||||||
"requestsWithStatusCode": {},
|
"requestsWithStatusCode": {},
|
||||||
"errors": {},
|
"errors": {},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"retiredSessionsCount": 0,
|
"retiredSessionsCount": 0,
|
||||||
"sessions": [
|
"sessions": [
|
||||||
{
|
{
|
||||||
"id": "session_cHq4dMfwXI",
|
"id": "session_4ERnLfhRfe",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -14,28 +14,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "pjedkv3ggc43er8m2edt6i3gqi",
|
"value": "7faeptahkgpngts9h84iishemk",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:57.817Z",
|
"creation": "2026-02-03T18:57:19.776Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:57.817Z"
|
"lastAccessed": "2026-02-03T18:57:19.776Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:56.432Z",
|
"expiresAt": "2026-02-03T19:47:19.140Z",
|
||||||
"createdAt": "2026-02-03T18:37:56.432Z",
|
"createdAt": "2026-02-03T18:57:19.140Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_YbphnNZDOG",
|
"id": "session_b0TzOu99YB",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -46,28 +46,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "ja7mdd60clptutmne95nsea0f4",
|
"value": "dis65ncl0oggk09keriqi85sc3",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:58.124Z",
|
"creation": "2026-02-03T18:57:20.400Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:58.124Z"
|
"lastAccessed": "2026-02-03T18:57:20.400Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.842Z",
|
"expiresAt": "2026-02-03T19:47:19.796Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.842Z",
|
"createdAt": "2026-02-03T18:57:19.796Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_j4wCqQrHhE",
|
"id": "session_FQDoGeShzw",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -78,28 +78,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "bl0s9u6150j1fe56qq5p81raur",
|
"value": "aa5n3vgkgl8tg6dbn5vimrkr60",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.132Z",
|
"creation": "2026-02-03T18:57:21.228Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.132Z"
|
"lastAccessed": "2026-02-03T18:57:21.228Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.844Z",
|
"expiresAt": "2026-02-03T19:47:19.798Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.844Z",
|
"createdAt": "2026-02-03T18:57:19.798Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_km9kX8juX5",
|
"id": "session_7qPl1RuIhU",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -110,28 +110,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "7kb8am8on70lsg0498u61mjh80",
|
"value": "h98q2ebuq62iuei16vj2gr27p9",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.617Z",
|
"creation": "2026-02-03T18:57:21.562Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.617Z"
|
"lastAccessed": "2026-02-03T18:57:21.562Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.845Z",
|
"expiresAt": "2026-02-03T19:47:19.799Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.845Z",
|
"createdAt": "2026-02-03T18:57:19.799Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_0LdWEWTmbd",
|
"id": "session_nT83yclMaD",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -142,28 +142,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "67m4qccak2or4g35v85dghk7lo",
|
"value": "64s6fh151m5i2vqe61qpe4slpq",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.603Z",
|
"creation": "2026-02-03T18:57:21.682Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.603Z"
|
"lastAccessed": "2026-02-03T18:57:21.682Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.846Z",
|
"expiresAt": "2026-02-03T19:47:19.800Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.846Z",
|
"createdAt": "2026-02-03T18:57:19.800Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_7tIybVMGvQ",
|
"id": "session_U8c4xgDdgZ",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -174,28 +174,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "9tvsa0um2dbqqt64us08bjpugt",
|
"value": "fmep2i6s7s8hvdakvkv6gsfpqb",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.563Z",
|
"creation": "2026-02-03T18:57:21.862Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.563Z"
|
"lastAccessed": "2026-02-03T18:57:21.862Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.847Z",
|
"expiresAt": "2026-02-03T19:47:19.801Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.847Z",
|
"createdAt": "2026-02-03T18:57:19.801Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_oFBMQoKZBM",
|
"id": "session_brbza8jFOb",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -206,28 +206,28 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "4oab9p5g0sjkpg0bad165hvss5",
|
"value": "7klasgpre1eajidc52coun8cg4",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.725Z",
|
"creation": "2026-02-03T18:57:22.108Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.725Z"
|
"lastAccessed": "2026-02-03T18:57:22.108Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.848Z",
|
"expiresAt": "2026-02-03T19:47:19.802Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.848Z",
|
"createdAt": "2026-02-03T18:57:19.802Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "session_7tuITPzdEJ",
|
"id": "session_J02AHwBimj",
|
||||||
"cookieJar": {
|
"cookieJar": {
|
||||||
"version": "tough-cookie@6.0.0",
|
"version": "tough-cookie@6.0.0",
|
||||||
"storeType": "MemoryCookieStore",
|
"storeType": "MemoryCookieStore",
|
||||||
@@ -238,22 +238,22 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
"key": "8a164f127e89bfa6ad5b54e0547581b9",
|
||||||
"value": "l0i9ipa1hduoo18scpeb489ian",
|
"value": "24drbvceqmebqusaqpv78ge3mv",
|
||||||
"domain": "www.schleicher-gruppe.de",
|
"domain": "www.schleicher-gruppe.de",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"hostOnly": true,
|
"hostOnly": true,
|
||||||
"creation": "2026-02-03T18:37:59.633Z",
|
"creation": "2026-02-03T18:57:21.727Z",
|
||||||
"lastAccessed": "2026-02-03T18:37:59.633Z"
|
"lastAccessed": "2026-02-03T18:57:21.727Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"userData": {},
|
"userData": {},
|
||||||
"maxErrorScore": 3,
|
"maxErrorScore": 3,
|
||||||
"errorScoreDecrement": 0.5,
|
"errorScoreDecrement": 0.5,
|
||||||
"expiresAt": "2026-02-03T19:27:57.853Z",
|
"expiresAt": "2026-02-03T19:47:19.806Z",
|
||||||
"createdAt": "2026-02-03T18:37:57.853Z",
|
"createdAt": "2026-02-03T18:57:19.806Z",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"maxUsageCount": 50,
|
"maxUsageCount": 50,
|
||||||
"errorScore": 0
|
"errorScore": 0
|
||||||
|
|||||||
Reference in New Issue
Block a user