feat(pdf): migrate PDF generation to @mintel/pdf package
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Failing after 1m3s
Build & Deploy / 🏗️ Build (push) Failing after 3m5s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 7s
Build & Deploy / 🧪 QA (push) Failing after 1m3s
Build & Deploy / 🏗️ Build (push) Failing after 3m5s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
@@ -4,6 +4,7 @@ import withMintelConfig from "@mintel/next-config";
|
|||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
|
transpilePackages: ["@mintel/pdf"],
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
const umamiUrl =
|
const umamiUrl =
|
||||||
process.env.UMAMI_API_ENDPOINT ||
|
process.env.UMAMI_API_ENDPOINT ||
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mintel/pdf": "link:../../../at-mintel/packages/pdf-library",
|
||||||
"@react-pdf/renderer": "^4.3.2",
|
"@react-pdf/renderer": "^4.3.2",
|
||||||
"@remotion/bundler": "^4.0.414",
|
"@remotion/bundler": "^4.0.414",
|
||||||
"@remotion/cli": "^4.0.414",
|
"@remotion/cli": "^4.0.414",
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { execSync } from "node:child_process";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { FileCacheAdapter } from "../src/utils/cache/file-adapter.js";
|
import { FileCacheAdapter } from "../src/utils/cache/file-adapter.js";
|
||||||
|
|
||||||
import { initialState, PRICING } from "../src/logic/pricing/constants.js";
|
import { initialState, PRICING } from "@mintel/pdf";
|
||||||
import { calculateTotals } from "../src/logic/pricing/calculator.js";
|
import { calculateTotals } from "@mintel/pdf";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const OPENROUTER_KEY = process.env.OPENROUTER_KEY;
|
const OPENROUTER_KEY = process.env.OPENROUTER_KEY;
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import * as readline from "node:readline/promises";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { createElement } from "react";
|
import { createElement } from "react";
|
||||||
import { renderToFile } from "@react-pdf/renderer";
|
import { renderToFile } from "@react-pdf/renderer";
|
||||||
import { calculateTotals } from "../src/logic/pricing/calculator.js";
|
import { calculateTotals, initialState, PRICING } from "@mintel/pdf";
|
||||||
import { CombinedQuotePDF } from "../src/components/CombinedQuotePDF.js";
|
import { CombinedQuotePDF } from "../src/components/CombinedQuotePDF.js";
|
||||||
import { initialState, PRICING } from "../src/logic/pricing/constants.js";
|
|
||||||
import {
|
import {
|
||||||
getTechDetails,
|
getTechDetails,
|
||||||
getPrinciples,
|
getPrinciples,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
Footer,
|
Footer,
|
||||||
FoldingMarks,
|
FoldingMarks,
|
||||||
DocumentTitle,
|
DocumentTitle,
|
||||||
} from "./pdf/SharedUI";
|
SimpleLayout,
|
||||||
import { SimpleLayout } from "./pdf/SimpleLayout";
|
} from "@mintel/pdf";
|
||||||
|
|
||||||
const localStyles = PDFStyleSheet.create({
|
const localStyles = PDFStyleSheet.create({
|
||||||
sectionContainer: {
|
sectionContainer: {
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Document as PDFDocument } from "@react-pdf/renderer";
|
import { Document as PDFDocument } from "@react-pdf/renderer";
|
||||||
import { EstimationPDF } from "./EstimationPDF";
|
import {
|
||||||
import { AgbsPDF } from "./AgbsPDF";
|
EstimationPDF,
|
||||||
import { ClosingModule } from "./pdf/modules/CommonModules";
|
AgbsPDF,
|
||||||
import { SimpleLayout } from "./pdf/SimpleLayout";
|
ClosingModule,
|
||||||
|
SimpleLayout,
|
||||||
|
} from "@mintel/pdf";
|
||||||
|
|
||||||
interface CombinedProps {
|
interface CombinedProps {
|
||||||
estimationProps: any;
|
estimationProps: any;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import * as confetti from "canvas-confetti";
|
|||||||
|
|
||||||
import { FormState, Step } from "./ContactForm/types";
|
import { FormState, Step } from "./ContactForm/types";
|
||||||
import { PRICING, initialState } from "./ContactForm/constants";
|
import { PRICING, initialState } from "./ContactForm/constants";
|
||||||
import { calculateTotals } from "../logic/pricing/calculator";
|
import { calculateTotals } from "@mintel/pdf";
|
||||||
import { PriceCalculation } from "./ContactForm/components/PriceCalculation";
|
import { PriceCalculation } from "./ContactForm/components/PriceCalculation";
|
||||||
import { ShareModal } from "./ShareModal";
|
import { ShareModal } from "./ShareModal";
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function PriceCalculation({
|
|||||||
setPdfLoading(true);
|
setPdfLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { EstimationPDF } = await import("../../EstimationPDF");
|
const { EstimationPDF } = await import("@mintel/pdf");
|
||||||
const doc = (
|
const doc = (
|
||||||
<EstimationPDF
|
<EstimationPDF
|
||||||
state={state}
|
state={state}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { calculatePositions as logicCalculatePositions } from '../../logic/pricing';
|
import { calculatePositions as logicCalculatePositions } from "@mintel/pdf";
|
||||||
import { FormState } from './types';
|
import { FormState } from "./types";
|
||||||
|
|
||||||
export type { Position } from '../../logic/pricing';
|
export type { Position } from "@mintel/pdf";
|
||||||
|
|
||||||
export const calculatePositions = (state: FormState, pricing: any) => logicCalculatePositions(state as any, pricing);
|
export const calculatePositions = (state: FormState, pricing: any) =>
|
||||||
|
logicCalculatePositions(state as any, pricing);
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import { calculatePositions } from "../logic/pricing";
|
|
||||||
import { Page as PDFPage } from "@react-pdf/renderer";
|
|
||||||
import { pdfStyles } from "./pdf/SharedUI";
|
|
||||||
import { DINLayout } from "./pdf/DINLayout";
|
|
||||||
import { SimpleLayout } from "./pdf/SimpleLayout";
|
|
||||||
|
|
||||||
// Modules
|
|
||||||
import { FrontPageModule } from "./pdf/modules/FrontPageModule";
|
|
||||||
import { BriefingModule } from "./pdf/modules/BriefingModule";
|
|
||||||
import { SitemapModule } from "./pdf/modules/SitemapModule";
|
|
||||||
import { EstimationModule } from "./pdf/modules/EstimationModule";
|
|
||||||
import {
|
|
||||||
TransparenzModule,
|
|
||||||
techPageModule as TechPageModule,
|
|
||||||
MaintenanceModule,
|
|
||||||
StandardsModule,
|
|
||||||
} from "./pdf/modules/CommonModules";
|
|
||||||
import { AboutModule, CrossSellModule } from "./pdf/modules/BrandingModules";
|
|
||||||
|
|
||||||
interface PDFProps {
|
|
||||||
state: any;
|
|
||||||
totalPrice: number;
|
|
||||||
monthlyPrice: number;
|
|
||||||
totalPagesCount: number;
|
|
||||||
pricing: any;
|
|
||||||
mode?: "estimation" | "full";
|
|
||||||
headerIcon?: string;
|
|
||||||
footerLogo?: string;
|
|
||||||
techDetails?: { t: string; d: string }[];
|
|
||||||
principles?: { t: string; d: string }[];
|
|
||||||
maintenanceDetails?: { t: string; d: string }[];
|
|
||||||
standardsDetails?: { t: string; d: string }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EstimationPDF = ({
|
|
||||||
state,
|
|
||||||
totalPrice,
|
|
||||||
pricing,
|
|
||||||
mode = "full",
|
|
||||||
headerIcon,
|
|
||||||
footerLogo,
|
|
||||||
techDetails,
|
|
||||||
principles,
|
|
||||||
maintenanceDetails,
|
|
||||||
standardsDetails,
|
|
||||||
...props
|
|
||||||
}: PDFProps) => {
|
|
||||||
const date = new Date().toLocaleDateString("de-DE", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
});
|
|
||||||
|
|
||||||
const positions = calculatePositions(state, pricing);
|
|
||||||
|
|
||||||
const companyData = {
|
|
||||||
name: "Marc Mintel",
|
|
||||||
address1: "Georg-Meistermann-Straße 7",
|
|
||||||
address2: "54586 Schüller",
|
|
||||||
ustId: "DE367588065",
|
|
||||||
};
|
|
||||||
|
|
||||||
const bankData = {
|
|
||||||
name: "N26",
|
|
||||||
bic: "NTSBDEB1XXX",
|
|
||||||
iban: "DE50 1001 1001 2620 4328 65",
|
|
||||||
};
|
|
||||||
|
|
||||||
const commonProps = {
|
|
||||||
state,
|
|
||||||
date,
|
|
||||||
icon: headerIcon,
|
|
||||||
footerLogo,
|
|
||||||
companyData,
|
|
||||||
bankData,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mode === "estimation") {
|
|
||||||
return (
|
|
||||||
<DINLayout {...commonProps} showAddress={true} showFooterDetails={true}>
|
|
||||||
<EstimationModule
|
|
||||||
state={state}
|
|
||||||
positions={positions}
|
|
||||||
totalPrice={totalPrice}
|
|
||||||
date={date}
|
|
||||||
/>
|
|
||||||
</DINLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Full Portfolio Mode
|
|
||||||
let pageCounter = 1;
|
|
||||||
const getPageNum = () => (pageCounter++).toString().padStart(2, "0");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PDFPage size="A4" style={pdfStyles.titlePage}>
|
|
||||||
<FrontPageModule state={state} headerIcon={headerIcon} date={date} />
|
|
||||||
</PDFPage>
|
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<BriefingModule state={state} />
|
|
||||||
</SimpleLayout>
|
|
||||||
|
|
||||||
{state.sitemap && state.sitemap.length > 0 && (
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<SitemapModule state={state} />
|
|
||||||
</SimpleLayout>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<EstimationModule
|
|
||||||
state={state}
|
|
||||||
positions={positions}
|
|
||||||
totalPrice={totalPrice}
|
|
||||||
date={date}
|
|
||||||
/>
|
|
||||||
</SimpleLayout>
|
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<TransparenzModule pricing={pricing} />
|
|
||||||
</SimpleLayout>
|
|
||||||
|
|
||||||
{standardsDetails && standardsDetails.length > 0 && (
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<StandardsModule
|
|
||||||
standardsDetails={standardsDetails}
|
|
||||||
principles={principles}
|
|
||||||
/>
|
|
||||||
</SimpleLayout>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{techDetails && techDetails.length > 0 && (
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<TechPageModule techDetails={techDetails} headerIcon={headerIcon} />
|
|
||||||
</SimpleLayout>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{maintenanceDetails && maintenanceDetails.length > 0 && (
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<MaintenanceModule maintenanceDetails={maintenanceDetails} />
|
|
||||||
</SimpleLayout>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<AboutModule />
|
|
||||||
</SimpleLayout>
|
|
||||||
|
|
||||||
<SimpleLayout {...commonProps} pageNumber={getPageNum()}>
|
|
||||||
<CrossSellModule state={state} />
|
|
||||||
</SimpleLayout>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { Page as PDFPage } from '@react-pdf/renderer';
|
|
||||||
import { FoldingMarks, Header, Footer, pdfStyles } from './SharedUI';
|
|
||||||
|
|
||||||
interface DINLayoutProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
sender?: string;
|
|
||||||
recipient?: {
|
|
||||||
title: string;
|
|
||||||
subtitle?: string;
|
|
||||||
address?: string;
|
|
||||||
phone?: string;
|
|
||||||
email?: string;
|
|
||||||
taxId?: string;
|
|
||||||
};
|
|
||||||
icon?: string;
|
|
||||||
footerLogo?: string;
|
|
||||||
companyData: any;
|
|
||||||
bankData: any;
|
|
||||||
showAddress?: boolean;
|
|
||||||
showFooterDetails?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DINLayout = ({
|
|
||||||
children,
|
|
||||||
sender,
|
|
||||||
recipient,
|
|
||||||
icon,
|
|
||||||
footerLogo,
|
|
||||||
companyData,
|
|
||||||
bankData,
|
|
||||||
showAddress = true,
|
|
||||||
showFooterDetails = true
|
|
||||||
}: DINLayoutProps) => {
|
|
||||||
return (
|
|
||||||
<PDFPage size="A4" style={pdfStyles.page}>
|
|
||||||
<FoldingMarks />
|
|
||||||
<Header
|
|
||||||
sender={sender}
|
|
||||||
recipient={recipient}
|
|
||||||
icon={icon}
|
|
||||||
showAddress={showAddress}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
<Footer
|
|
||||||
logo={footerLogo}
|
|
||||||
companyData={companyData}
|
|
||||||
bankData={bankData}
|
|
||||||
showDetails={showFooterDetails}
|
|
||||||
/>
|
|
||||||
</PDFPage>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,810 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
View as PDFView,
|
|
||||||
Text as PDFText,
|
|
||||||
StyleSheet,
|
|
||||||
Link as PDFLink,
|
|
||||||
Image as PDFImage,
|
|
||||||
Font,
|
|
||||||
} from "@react-pdf/renderer";
|
|
||||||
|
|
||||||
// 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 = {
|
|
||||||
HERO: 24, // Main Page Titles
|
|
||||||
HEADING: 14, // Section Headers
|
|
||||||
BODY: 11, // Standard Content
|
|
||||||
LABEL: 10, // Bold Labels / Keys
|
|
||||||
SMALL: 9, // Descriptions / Footnotes
|
|
||||||
TINY: 8, // Metadata / Unit prices
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mintel Industrial Glyphs (strictly 1px stroke, 12x12px grid)
|
|
||||||
export const IndustrialGlyph = ({
|
|
||||||
type,
|
|
||||||
color = COLORS.TEXT_LIGHT,
|
|
||||||
size = 12,
|
|
||||||
}: {
|
|
||||||
type: string;
|
|
||||||
color?: string;
|
|
||||||
size?: number;
|
|
||||||
}) => {
|
|
||||||
const stroke = 1;
|
|
||||||
const scale = size / 12;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "base": // Skeletal cube base
|
|
||||||
return (
|
|
||||||
<PDFView style={{ width: size, height: size, position: "relative" }}>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 2 * scale,
|
|
||||||
left: 2 * scale,
|
|
||||||
width: 8 * scale,
|
|
||||||
height: 8 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: 4 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
backgroundColor: "white",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "pages": // Layered rectangles
|
|
||||||
return (
|
|
||||||
<PDFView style={{ width: size, height: size, position: "relative" }}>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 3 * scale,
|
|
||||||
left: 3 * scale,
|
|
||||||
width: 6 * scale,
|
|
||||||
height: 8 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: 6 * scale,
|
|
||||||
height: 8 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
backgroundColor: "white",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "modules": // Four small squares grid
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
flexDirection: "row",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
gap: 2 * scale,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 4 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 4 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 4 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 4 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "logic": // Diamond with center point
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 8 * scale,
|
|
||||||
height: 8 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
transform: "rotate(45deg)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 2 * scale,
|
|
||||||
height: 2 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
position: "absolute",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "interface": // Three horizontal lines of varying length
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
justifyContent: "center",
|
|
||||||
gap: 2 * scale,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 10 * scale,
|
|
||||||
height: stroke,
|
|
||||||
backgroundColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{ width: 6 * scale, height: stroke, backgroundColor: color }}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 10 * scale,
|
|
||||||
height: stroke,
|
|
||||||
backgroundColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "management": // Framed grid
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
padding: 1 * scale,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: 2 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
marginBottom: 1 * scale,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: 2 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
marginBottom: 1 * scale,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{ width: "100%", height: 2 * scale, backgroundColor: color }}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "reveal": // Ascending bars
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "flex-end",
|
|
||||||
gap: 1 * scale,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 2 * scale,
|
|
||||||
height: 4 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
opacity: 0.4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 2 * scale,
|
|
||||||
height: 7 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
opacity: 0.7,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: 2 * scale,
|
|
||||||
height: 10 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
case "maintenance": // Circle with vertical notch
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
borderRadius: 6 * scale,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: color,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: stroke,
|
|
||||||
height: 4 * scale,
|
|
||||||
backgroundColor: color,
|
|
||||||
marginTop: 1 * scale,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
borderWidth: stroke,
|
|
||||||
borderColor: COLORS.BLUEPRINT,
|
|
||||||
borderStyle: "dashed",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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: {
|
|
||||||
paddingTop: 45, // DIN 5008
|
|
||||||
paddingLeft: 70, // ~25mm
|
|
||||||
paddingRight: 57, // ~20mm
|
|
||||||
paddingBottom: 80, // Safe buffer for absolute footer
|
|
||||||
backgroundColor: COLORS.WHITE,
|
|
||||||
fontFamily: "Helvetica",
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
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: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
marginBottom: 20,
|
|
||||||
minHeight: 120,
|
|
||||||
},
|
|
||||||
addressBlock: {
|
|
||||||
width: "55%",
|
|
||||||
marginTop: 45, // DIN 5008 positioning for window
|
|
||||||
},
|
|
||||||
senderLine: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
textDecoration: "underline",
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
recipientAddress: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
brandLogoContainer: {
|
|
||||||
width: "40%",
|
|
||||||
alignItems: "flex-end",
|
|
||||||
},
|
|
||||||
brandIconContainer: {
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
backgroundColor: "#0f172a",
|
|
||||||
borderRadius: 8,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
brandIconText: {
|
|
||||||
color: COLORS.WHITE,
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
titleInfo: {
|
|
||||||
marginBottom: 24,
|
|
||||||
},
|
|
||||||
mainTitle: {
|
|
||||||
fontSize: FONT_SIZES.HEADING,
|
|
||||||
fontWeight: "bold",
|
|
||||||
marginBottom: 4,
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
|
||||||
subTitle: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
marginTop: 2,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
section: {
|
|
||||||
marginBottom: 32,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: FONT_SIZES.LABEL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 1,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 32,
|
|
||||||
left: 70,
|
|
||||||
right: 57,
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderTopColor: COLORS.GRID,
|
|
||||||
paddingTop: 16,
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
},
|
|
||||||
footerColumn: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: "flex-start",
|
|
||||||
},
|
|
||||||
footerLogo: {
|
|
||||||
height: 20,
|
|
||||||
width: "auto",
|
|
||||||
objectFit: "contain",
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
footerText: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
// NEW LAYOUT PRIMITIVES
|
|
||||||
asymmetryContainer: {
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 32,
|
|
||||||
},
|
|
||||||
asymmetryLeft: {
|
|
||||||
width: "32%",
|
|
||||||
},
|
|
||||||
asymmetryRight: {
|
|
||||||
width: "63%",
|
|
||||||
},
|
|
||||||
specRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
paddingVertical: 6,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.GRID,
|
|
||||||
},
|
|
||||||
specLabel: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
|
||||||
specValue: {
|
|
||||||
fontSize: FONT_SIZES.SMALL,
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
blueprintBox: {
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: COLORS.GRID,
|
|
||||||
padding: 16,
|
|
||||||
backgroundColor: "#fafafa",
|
|
||||||
},
|
|
||||||
footerLabel: {
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
},
|
|
||||||
pageNumber: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.DIVIDER,
|
|
||||||
fontWeight: "bold",
|
|
||||||
marginTop: 8,
|
|
||||||
textAlign: "right",
|
|
||||||
},
|
|
||||||
foldingMark: {
|
|
||||||
position: "absolute",
|
|
||||||
left: 20,
|
|
||||||
width: 10,
|
|
||||||
borderTopWidth: 0.5,
|
|
||||||
borderTopColor: COLORS.DIVIDER,
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
width: "100%",
|
|
||||||
height: 1,
|
|
||||||
backgroundColor: COLORS.DIVIDER,
|
|
||||||
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.TINY,
|
|
||||||
color: COLORS.BLUEPRINT,
|
|
||||||
fontFamily: "Helvetica",
|
|
||||||
letterSpacing: 1,
|
|
||||||
},
|
|
||||||
// Atoms
|
|
||||||
industrialListItem: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
marginBottom: 6,
|
|
||||||
},
|
|
||||||
industrialBulletBox: {
|
|
||||||
width: 6,
|
|
||||||
height: 6,
|
|
||||||
backgroundColor: COLORS.DIVIDER,
|
|
||||||
marginRight: 8,
|
|
||||||
marginTop: 5,
|
|
||||||
},
|
|
||||||
industrialTitle: {
|
|
||||||
fontSize: FONT_SIZES.HERO,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 6,
|
|
||||||
letterSpacing: 0, // Reset for clarity
|
|
||||||
},
|
|
||||||
industrialSubtitle: {
|
|
||||||
fontSize: FONT_SIZES.LABEL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
marginBottom: 16,
|
|
||||||
letterSpacing: 2,
|
|
||||||
},
|
|
||||||
industrialTextLead: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_MAIN,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
industrialText: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
industrialCard: {
|
|
||||||
padding: 16,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: COLORS.BLUEPRINT,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
industrialCardTitle: {
|
|
||||||
fontSize: FONT_SIZES.BODY + 1, // 10
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 4,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
|
||||||
darkBox: {
|
|
||||||
marginTop: 32,
|
|
||||||
padding: 24,
|
|
||||||
backgroundColor: COLORS.CHARCOAL,
|
|
||||||
color: COLORS.WHITE,
|
|
||||||
},
|
|
||||||
darkTitle: {
|
|
||||||
fontSize: FONT_SIZES.HERO,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.WHITE,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
darkText: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={pdfStyles.industrialListItem}>
|
|
||||||
<PDFView style={pdfStyles.industrialBulletBox} />
|
|
||||||
{children}
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const IndustrialCard = ({
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
style = {},
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
style?: any;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={[pdfStyles.industrialCard, style]}>
|
|
||||||
<PDFText style={pdfStyles.industrialCardTitle}>{title}</PDFText>
|
|
||||||
{children}
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const FoldingMarks = () => (
|
|
||||||
<>
|
|
||||||
<PDFView style={[pdfStyles.foldingMark, { top: 297.6 }]} fixed />
|
|
||||||
<PDFView style={[pdfStyles.foldingMark, { top: 420.9, width: 15 }]} fixed />
|
|
||||||
<PDFView style={[pdfStyles.foldingMark, { top: 595.3 }]} fixed />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Divider = ({ style = {} }: { style?: any }) => (
|
|
||||||
<PDFView style={[pdfStyles.divider, style]} />
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Footer = ({
|
|
||||||
logo,
|
|
||||||
companyData,
|
|
||||||
bankData,
|
|
||||||
showDetails = true,
|
|
||||||
showPageNumber = true,
|
|
||||||
}: {
|
|
||||||
logo?: string;
|
|
||||||
companyData: any;
|
|
||||||
bankData: any;
|
|
||||||
showDetails?: boolean;
|
|
||||||
showPageNumber?: boolean;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={pdfStyles.footer}>
|
|
||||||
<PDFView style={pdfStyles.footerColumn}>
|
|
||||||
{logo ? (
|
|
||||||
<PDFImage src={logo} style={pdfStyles.footerLogo} />
|
|
||||||
) : (
|
|
||||||
<PDFText style={{ fontSize: 12, fontWeight: "bold", marginBottom: 8 }}>
|
|
||||||
marc mintel
|
|
||||||
</PDFText>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
{showDetails && (
|
|
||||||
<>
|
|
||||||
<PDFView style={pdfStyles.footerColumn}>
|
|
||||||
<PDFText style={pdfStyles.footerText}>
|
|
||||||
<PDFText style={pdfStyles.footerLabel}>{companyData.name}</PDFText>
|
|
||||||
{"\n"}
|
|
||||||
{companyData.address1}
|
|
||||||
{"\n"}
|
|
||||||
{companyData.address2}
|
|
||||||
{"\n"}UST: {companyData.ustId}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={[pdfStyles.footerColumn, { alignItems: "flex-end" }]}>
|
|
||||||
{showPageNumber && (
|
|
||||||
<PDFText
|
|
||||||
style={pdfStyles.pageNumber}
|
|
||||||
render={({ pageNumber, totalPages }) =>
|
|
||||||
`${pageNumber} / ${totalPages}`
|
|
||||||
}
|
|
||||||
fixed
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!showDetails && (
|
|
||||||
<PDFView style={[pdfStyles.footerColumn, { alignItems: "flex-end" }]}>
|
|
||||||
{showPageNumber && (
|
|
||||||
<PDFText
|
|
||||||
style={pdfStyles.pageNumber}
|
|
||||||
render={({ pageNumber, totalPages }) =>
|
|
||||||
`${pageNumber} / ${totalPages}`
|
|
||||||
}
|
|
||||||
fixed
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Header = ({
|
|
||||||
sender,
|
|
||||||
recipient,
|
|
||||||
icon,
|
|
||||||
showAddress = true,
|
|
||||||
}: {
|
|
||||||
sender?: string;
|
|
||||||
recipient?: {
|
|
||||||
title: string;
|
|
||||||
subtitle?: string;
|
|
||||||
email?: string;
|
|
||||||
address?: string;
|
|
||||||
phone?: string;
|
|
||||||
taxId?: string;
|
|
||||||
};
|
|
||||||
icon?: string;
|
|
||||||
showAddress?: boolean;
|
|
||||||
}) => (
|
|
||||||
<PDFView
|
|
||||||
style={[
|
|
||||||
pdfStyles.header,
|
|
||||||
showAddress ? {} : { minHeight: 40, marginBottom: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<PDFView style={pdfStyles.addressBlock}>
|
|
||||||
{showAddress && sender && (
|
|
||||||
<>
|
|
||||||
<PDFText style={pdfStyles.senderLine}>{sender}</PDFText>
|
|
||||||
{recipient && (
|
|
||||||
<PDFView style={pdfStyles.recipientAddress}>
|
|
||||||
<PDFText style={{ fontWeight: "bold" }}>
|
|
||||||
{recipient.title}
|
|
||||||
</PDFText>
|
|
||||||
{recipient.subtitle && <PDFText>{recipient.subtitle}</PDFText>}
|
|
||||||
{recipient.address && <PDFText>{recipient.address}</PDFText>}
|
|
||||||
{recipient.phone && <PDFText>{recipient.phone}</PDFText>}
|
|
||||||
{recipient.email && <PDFText>{recipient.email}</PDFText>}
|
|
||||||
{recipient.taxId && <PDFText>USt-ID: {recipient.taxId}</PDFText>}
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={pdfStyles.brandLogoContainer}>
|
|
||||||
<PDFView style={pdfStyles.brandIconContainer}>
|
|
||||||
{icon ? (
|
|
||||||
<PDFImage src={icon} style={{ width: 24, height: 24 }} />
|
|
||||||
) : (
|
|
||||||
<PDFText style={pdfStyles.brandIconText}>M</PDFText>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const DocumentTitle = ({
|
|
||||||
title,
|
|
||||||
subLines,
|
|
||||||
isHero = false,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
subLines?: string[];
|
|
||||||
isHero?: boolean;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={pdfStyles.titleInfo}>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
pdfStyles.mainTitle,
|
|
||||||
{ fontSize: isHero ? FONT_SIZES.HERO : FONT_SIZES.HEADING },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</PDFText>
|
|
||||||
{subLines?.map((line, i) => (
|
|
||||||
<PDFText
|
|
||||||
key={i}
|
|
||||||
style={[
|
|
||||||
pdfStyles.subTitle,
|
|
||||||
i === 1 ? { fontWeight: "bold", color: COLORS.CHARCOAL } : {},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{line}
|
|
||||||
</PDFText>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TechnicalSpec = ({
|
|
||||||
label,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={pdfStyles.specRow}>
|
|
||||||
<PDFText style={pdfStyles.specLabel}>{label}</PDFText>
|
|
||||||
<PDFText style={pdfStyles.specValue}>{value}</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const AsymmetryView = ({
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
style,
|
|
||||||
}: {
|
|
||||||
left: React.ReactNode;
|
|
||||||
right: React.ReactNode;
|
|
||||||
style?: any;
|
|
||||||
}) => (
|
|
||||||
<PDFView style={[pdfStyles.asymmetryContainer, style]}>
|
|
||||||
<PDFView style={pdfStyles.asymmetryLeft}>{left}</PDFView>
|
|
||||||
<PDFView style={pdfStyles.asymmetryRight}>{right}</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
);
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { Page as PDFPage, View as PDFView, Text as PDFText, StyleSheet } from '@react-pdf/renderer';
|
|
||||||
import { Header, Footer, pdfStyles, BlueprintBackground } from './SharedUI';
|
|
||||||
|
|
||||||
const simpleStyles = StyleSheet.create({
|
|
||||||
industrialPage: {
|
|
||||||
padding: 30,
|
|
||||||
paddingTop: 20,
|
|
||||||
backgroundColor: '#ffffff',
|
|
||||||
},
|
|
||||||
industrialNumber: {
|
|
||||||
fontSize: 60,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#f1f5f9',
|
|
||||||
position: 'absolute',
|
|
||||||
top: -10,
|
|
||||||
right: 0,
|
|
||||||
zIndex: -1,
|
|
||||||
},
|
|
||||||
industrialSection: {
|
|
||||||
marginTop: 16,
|
|
||||||
paddingTop: 12,
|
|
||||||
flexDirection: 'row',
|
|
||||||
position: 'relative',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
interface SimpleLayoutProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
pageNumber?: string;
|
|
||||||
icon?: string;
|
|
||||||
footerLogo?: string;
|
|
||||||
companyData: any;
|
|
||||||
bankData: any;
|
|
||||||
showPageNumber?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SimpleLayout = ({
|
|
||||||
children,
|
|
||||||
pageNumber,
|
|
||||||
icon,
|
|
||||||
footerLogo,
|
|
||||||
companyData,
|
|
||||||
bankData,
|
|
||||||
showPageNumber = true
|
|
||||||
}: SimpleLayoutProps) => {
|
|
||||||
return (
|
|
||||||
<PDFPage size="A4" style={[pdfStyles.page, simpleStyles.industrialPage]}>
|
|
||||||
<BlueprintBackground />
|
|
||||||
<Header icon={icon} showAddress={false} />
|
|
||||||
{pageNumber && <PDFText style={simpleStyles.industrialNumber}>{pageNumber}</PDFText>}
|
|
||||||
<PDFView style={simpleStyles.industrialSection}>
|
|
||||||
<PDFView style={{ width: '100%' }}>
|
|
||||||
{children}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
<Footer
|
|
||||||
logo={footerLogo}
|
|
||||||
companyData={companyData}
|
|
||||||
bankData={bankData}
|
|
||||||
showDetails={false}
|
|
||||||
showPageNumber={showPageNumber}
|
|
||||||
/>
|
|
||||||
</PDFPage>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
View as PDFView,
|
|
||||||
Text as PDFText,
|
|
||||||
StyleSheet,
|
|
||||||
} from "@react-pdf/renderer";
|
|
||||||
import {
|
|
||||||
DocumentTitle,
|
|
||||||
IndustrialListItem,
|
|
||||||
IndustrialCard,
|
|
||||||
Divider,
|
|
||||||
COLORS,
|
|
||||||
FONT_SIZES,
|
|
||||||
} from "../SharedUI";
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
industrialTextLead: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_MAIN,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
marginBottom: 16,
|
|
||||||
},
|
|
||||||
industrialText: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
industrialGrid2: { flexDirection: "row" },
|
|
||||||
industrialCol: { width: "46%" },
|
|
||||||
});
|
|
||||||
|
|
||||||
export const AboutModule = () => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle
|
|
||||||
title="Expertise & Profil"
|
|
||||||
subLines={["Entwicklung & Technischer Partner für den Mittelstand"]}
|
|
||||||
isHero={true}
|
|
||||||
/>
|
|
||||||
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
|
|
||||||
|
|
||||||
<PDFView style={{ marginTop: 24 }}>
|
|
||||||
<PDFText style={styles.industrialTextLead}>
|
|
||||||
Begleitung mittelständischer Unternehmen und Agenturen bei der
|
|
||||||
Realisierung anspruchsvoller Web-Projekte. Als Senior Software Developer
|
|
||||||
mit over 15 Jahren Erfahrung wird das gesamte technische Spektrum
|
|
||||||
abgedeckt – von der Architektur bis zum fertigen Produkt.
|
|
||||||
</PDFText>
|
|
||||||
|
|
||||||
<PDFView style={[styles.industrialGrid2, { marginTop: 20 }]}>
|
|
||||||
<PDFView style={[styles.industrialCol, { marginRight: "8%" }]}>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.industrialText,
|
|
||||||
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 8 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Erfahrung & Substanz
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Der Werdegang umfasst alle Ebenen der Webentwicklung: von der
|
|
||||||
Teamleitung in Kreativagenturen bis zur Softwareentwicklung für
|
|
||||||
internationale Konzerne.
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Die Kenntnis komplexer Enterprise-Systeme wird mit der Agilität
|
|
||||||
kombiniert, die im Mittelstand gefordert ist. Dieses Wissen
|
|
||||||
ermöglicht den Bau von Lösungen, die technologisch auf Augenhöhe mit
|
|
||||||
Konzern-Standards sind, jedoch ohne unnötigen bürokratischen
|
|
||||||
Overhead auskommen.
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
|
|
||||||
<PDFView style={styles.industrialCol}>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.industrialText,
|
|
||||||
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 8 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Fokus Einzelentwicklung
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Die Umsetzung erfolgt bewusst als spezialisierter Einzelentwickler.
|
|
||||||
Dies garantiert maximale Geschwindigkeit, direkte Kommunikationswege
|
|
||||||
und volle technologische Verantwortung.
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Als direkter technischer Sparringspartner bleibt die Codebasis von
|
|
||||||
der ersten bis zur letzten Zeile transparent und wartbar. Diese
|
|
||||||
Unmittelbarkeit stellt sicher, dass Ergebnisse sowohl technisch
|
|
||||||
sauber als auch wirtschaftlich sinnvoll realisiert werden.
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
marginTop: 32,
|
|
||||||
paddingVertical: 16,
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderTopColor: COLORS.GRID,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.industrialText,
|
|
||||||
{ fontWeight: "bold", color: COLORS.CHARCOAL, marginBottom: 4 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Infrastruktur & Souveränität
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Es wird keine instabile Prototyp-Software geliefert, sondern
|
|
||||||
produktionsreife Systeme, die technisch skalierbar bleiben. Die
|
|
||||||
Codebasis folgt modernen Standards – bei wachsenden Ansprüchen oder
|
|
||||||
dem Wechsel zu einer Agentur kann der Quellcode jederzeit nahtlos
|
|
||||||
übernommen und weitergeführt werden.
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title={title} subLines={[subtitle]} isHero={true} />
|
|
||||||
<Divider style={{ marginVertical: 16, backgroundColor: COLORS.GRID }} />
|
|
||||||
<PDFView style={[styles.industrialGrid2, { marginTop: 16 }]}>
|
|
||||||
{isWebsite ? (
|
|
||||||
<>
|
|
||||||
<PDFView style={[styles.industrialCol, { marginRight: "8%" }]}>
|
|
||||||
<PDFText style={styles.industrialTextLead}>
|
|
||||||
Über die klassische Webpräsenz hinaus werden maßgeschneiderte
|
|
||||||
Lösungen zur Automatisierung von Routine-Prozessen angeboten.
|
|
||||||
Dies ermöglicht eine signifikante Effizienzsteigerung im
|
|
||||||
Tagesgeschäft.
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={[styles.industrialText, { fontWeight: "bold" }]}>
|
|
||||||
Keine Abos. Keine komplexen neuen Systeme. Gezielte
|
|
||||||
Zeitersparnis.
|
|
||||||
</PDFText>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
marginTop: 24,
|
|
||||||
padding: 16,
|
|
||||||
backgroundColor: "#f8fafc",
|
|
||||||
borderLeftWidth: 2,
|
|
||||||
borderLeftColor: COLORS.GRID,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.industrialText,
|
|
||||||
{
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Individuelle Analyse
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Spezifische Prozesse werden auf technisches
|
|
||||||
Automatisierungspotenzial untersucht. Das Ergebnis liefert
|
|
||||||
Klarheit über die wirtschaftliche Sinnhaftigkeit einer
|
|
||||||
Umsetzung.
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={styles.industrialCol}>
|
|
||||||
<IndustrialCard title="DOKUMENT-AUTOMATION">
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Erstellung von PDF-Angeboten, Berichten oder Protokollen in
|
|
||||||
Sekunden statt Stunden.
|
|
||||||
</PDFText>
|
|
||||||
</IndustrialCard>
|
|
||||||
<IndustrialCard title="EXCEL-LOGIK">
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Intelligente Tabellen und automatisierte Auswertungen
|
|
||||||
bestehender Datensätze.
|
|
||||||
</PDFText>
|
|
||||||
</IndustrialCard>
|
|
||||||
<IndustrialCard title="KI-ASSISTENZ">
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Effiziente Verarbeitung von analogen Dokumenten oder
|
|
||||||
handschriftlichen Notizen mittels KI.
|
|
||||||
</PDFText>
|
|
||||||
</IndustrialCard>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<PDFView style={{ width: "100%" }}>
|
|
||||||
<PDFText style={styles.industrialTextLead}>
|
|
||||||
Bereitstellung einer stabilen technischen Basis ohne
|
|
||||||
Abhängigkeiten von Baukasten-Systemen oder Agenturen.
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.industrialText}>
|
|
||||||
Entwicklung performanter Frontends und skalierbarer Backends. Die
|
|
||||||
Auslieferung erfolgt als kontrollierbarer und nachhaltiger
|
|
||||||
Quellcode.
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
"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.LABEL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
marginBottom: 8,
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
},
|
|
||||||
visionText: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_MAIN,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
textAlign: "justify",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const BriefingModule = ({ state }: any) => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Projektdetails" isHero={true} />
|
|
||||||
{state.briefingSummary && (
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
<PDFText style={styles.sectionTitle}>Briefing Analyse</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={{
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_MAIN,
|
|
||||||
lineHeight: 1.6,
|
|
||||||
textAlign: "justify",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{state.briefingSummary}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
{state.designVision && (
|
|
||||||
<PDFView
|
|
||||||
style={[
|
|
||||||
styles.section,
|
|
||||||
{
|
|
||||||
padding: 12,
|
|
||||||
borderLeftWidth: 2,
|
|
||||||
borderLeftColor: COLORS.DIVIDER,
|
|
||||||
backgroundColor: COLORS.GRID,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.sectionTitle,
|
|
||||||
{ color: COLORS.CHARCOAL, marginBottom: 4 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Strategische Vision
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.visionText}>{state.designVision}</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -1,443 +0,0 @@
|
|||||||
"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,
|
|
||||||
TechnicalSpec,
|
|
||||||
AsymmetryView,
|
|
||||||
} from "../SharedUI";
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
section: { marginBottom: 24 },
|
|
||||||
moduleLabel: {
|
|
||||||
fontSize: FONT_SIZES.LABEL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
moduleDesc: {
|
|
||||||
fontSize: FONT_SIZES.SMALL,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.5,
|
|
||||||
},
|
|
||||||
ledgerRow: {
|
|
||||||
paddingVertical: 14,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.GRID,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
},
|
|
||||||
ledgerPrice: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
},
|
|
||||||
ledgerUnit: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginLeft: 2,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const techPageModule = ({ techDetails }: any) => {
|
|
||||||
// Focus on the first 3 items as "Featured Specs", the rest as high-density grid
|
|
||||||
const featured = techDetails?.slice(0, 3) || [];
|
|
||||||
const rest = techDetails?.slice(3) || [];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Technische Umsetzung" isHero={true} />
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
{/* FEATURED SPECS - Editorial focus */}
|
|
||||||
{featured.map((item: any, i: number) => (
|
|
||||||
<PDFView key={i} style={{ marginBottom: 24 }}>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{ color: COLORS.BLUEPRINT, fontSize: FONT_SIZES.TINY },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
FOKUS_{i + 1}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{ fontSize: FONT_SIZES.HEADING, marginBottom: 8 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.t}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleDesc,
|
|
||||||
{ fontSize: FONT_SIZES.BODY, color: COLORS.TEXT_MAIN },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.d}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Divider style={{ marginVertical: 24, backgroundColor: COLORS.GRID }} />
|
|
||||||
|
|
||||||
{/* TECHNICAL GRID - High density */}
|
|
||||||
<PDFView style={{ flexDirection: "row", flexWrap: "wrap", gap: 12 }}>
|
|
||||||
{rest.map((item: any, i: number) => (
|
|
||||||
<PDFView
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
width: "31%",
|
|
||||||
padding: 10,
|
|
||||||
backgroundColor: "#fdfdfd",
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.BLUEPRINT,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
marginBottom: 4,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.t.toUpperCase()}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[styles.moduleDesc, { fontSize: FONT_SIZES.TINY }]}
|
|
||||||
>
|
|
||||||
{item.d}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MaintenanceModule = ({ maintenanceDetails }: any) => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle
|
|
||||||
title="Monitoring, Security & techn. Support"
|
|
||||||
isHero={true}
|
|
||||||
/>
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
<PDFView style={{ flexDirection: "row", flexWrap: "wrap", gap: 16 }}>
|
|
||||||
{maintenanceDetails?.map((item: any, i: number) => (
|
|
||||||
<PDFView
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
width: "48%",
|
|
||||||
padding: 16,
|
|
||||||
backgroundColor: COLORS.GRID,
|
|
||||||
borderLeftWidth: 2,
|
|
||||||
borderLeftColor: COLORS.DIVIDER,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.t.toUpperCase()}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleDesc,
|
|
||||||
{
|
|
||||||
fontSize: FONT_SIZES.SMALL,
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.d}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StandardsModule = ({ standardsDetails, principles }: any) => {
|
|
||||||
const independence = standardsDetails?.find(
|
|
||||||
(s: any) => s.t === "Unabhängigkeit",
|
|
||||||
);
|
|
||||||
const others =
|
|
||||||
standardsDetails?.filter((s: any) => s.t !== "Unabhängigkeit") || [];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Meine Standards" isHero={true} />
|
|
||||||
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
{/* FOCUS: UNABHÄNGIGKEIT & PRINCIPLES */}
|
|
||||||
<AsymmetryView
|
|
||||||
left={
|
|
||||||
<PDFView>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
MODERNE PRINZIPIEN
|
|
||||||
</PDFText>
|
|
||||||
{principles?.map((p: any, i: number) => (
|
|
||||||
<PDFView key={i} style={{ marginBottom: 12 }}>
|
|
||||||
<PDFText
|
|
||||||
style={[styles.moduleLabel, { fontSize: FONT_SIZES.TINY }]}
|
|
||||||
>
|
|
||||||
{p.t.toUpperCase()}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleDesc,
|
|
||||||
{ fontSize: FONT_SIZES.TINY, opacity: 0.8 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{p.d}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
}
|
|
||||||
right={
|
|
||||||
<PDFView>
|
|
||||||
{/* HERO BOX: UNABHÄNGIGKEIT */}
|
|
||||||
{independence && (
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
backgroundColor: COLORS.CHARCOAL,
|
|
||||||
padding: 24,
|
|
||||||
marginBottom: 32,
|
|
||||||
borderLeftWidth: 4,
|
|
||||||
borderLeftColor: COLORS.TEXT_LIGHT,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText
|
|
||||||
style={{
|
|
||||||
fontSize: FONT_SIZES.HEADING,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.WHITE,
|
|
||||||
letterSpacing: 1,
|
|
||||||
marginBottom: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{independence.t.toUpperCase()}
|
|
||||||
</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={{
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.WHITE,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
opacity: 0.9,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{independence.d}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* VERTICAL STACK OF OTHER STANDARDS */}
|
|
||||||
<PDFView>
|
|
||||||
{others.map((item: any, i: number) => (
|
|
||||||
<PDFView
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
marginBottom: 16,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.GRID,
|
|
||||||
paddingBottom: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText style={styles.moduleLabel}>{item.t}</PDFText>
|
|
||||||
<PDFText style={styles.moduleDesc}>{item.d}</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TransparenzModule = ({ pricing }: any) => {
|
|
||||||
const sorglosPrice = (pricing.HOSTING_MONTHLY || 250) * 12;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Preis-Transparenz & Modell" isHero={true} />
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
<PDFView style={{ borderTopWidth: 1, borderTopColor: COLORS.CHARCOAL }}>
|
|
||||||
{[
|
|
||||||
{
|
|
||||||
l: "Fundament",
|
|
||||||
d: "Bereitstellung der techn. Infrastruktur & System-Umgebung.",
|
|
||||||
p: pricing.BASE_WEBSITE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Einzelseiten",
|
|
||||||
d: "Individuelle Gestaltung, Layout & responsive Struktur.",
|
|
||||||
p: pricing.PAGE,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Core Features",
|
|
||||||
d: "Geschlossene Datensysteme mit eigener Datenstruktur.",
|
|
||||||
p: pricing.FEATURE,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Logik & Funktionen",
|
|
||||||
d: "Interaktive Funktions-Bausteine & Prozess-Logik.",
|
|
||||||
p: pricing.FUNCTION,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Schnittstellen",
|
|
||||||
d: "Synchronisation mit externen Zielsystemen.",
|
|
||||||
p: pricing.API_INTEGRATION,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Inhalts-Verwaltung",
|
|
||||||
d: "Schnittstelle zur eigenständigen Daten-Pflege (optional).",
|
|
||||||
p: pricing.CMS_CONNECTION_PER_FEATURE,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Sprachversionen",
|
|
||||||
d: "Skalierung der System-Architektur auf Zweit-Sprachen.",
|
|
||||||
p: "+20%",
|
|
||||||
isLang: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Initial-Pflege",
|
|
||||||
d: "Konvertierung & Aufbereitung von Bestandsdaten.",
|
|
||||||
p: pricing.NEW_DATASET,
|
|
||||||
unit: "/ Stk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
l: "Sorglos Betrieb",
|
|
||||||
d: "Hosting, Instandhaltung, Security & techn. Support.",
|
|
||||||
p: sorglosPrice,
|
|
||||||
unit: "/ Jahr",
|
|
||||||
},
|
|
||||||
].map((item: any, i: number) => (
|
|
||||||
<PDFView key={i} style={styles.ledgerRow}>
|
|
||||||
<PDFView style={{ width: "25%" }}>
|
|
||||||
<PDFText style={styles.moduleLabel}>
|
|
||||||
{item.l.toUpperCase()}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={{ width: "50%" }}>
|
|
||||||
<PDFText style={styles.moduleDesc}>{item.d}</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={{ width: "25%", alignItems: "flex-end" }}>
|
|
||||||
<PDFText style={styles.ledgerPrice}>
|
|
||||||
{typeof item.p === "number"
|
|
||||||
? `${item.p.toLocaleString("de-DE")} €`
|
|
||||||
: item.p}
|
|
||||||
{item.unit && (
|
|
||||||
<PDFText style={styles.ledgerUnit}> {item.unit}</PDFText>
|
|
||||||
)}
|
|
||||||
</PDFText>
|
|
||||||
{item.sub && (
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleDesc,
|
|
||||||
{
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginTop: 2,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{item.sub}
|
|
||||||
</PDFText>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ClosingModule = () => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Abschluss & Kontakt" isHero={true} />
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleLabel,
|
|
||||||
{ fontSize: FONT_SIZES.HEADING, marginBottom: 12 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Vielen Dank für Ihr Interesse!
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.moduleDesc}>
|
|
||||||
Die aufgeführten Positionen stellen eine detaillierte Schätzung auf
|
|
||||||
Basis unseres aktuellen Stands dar. Sollten sich Anforderungen ändern
|
|
||||||
oder Sie Fragen zu einzelnen Details haben, lassen Sie uns die
|
|
||||||
Positionen gerne gemeinsam besprechen.
|
|
||||||
</PDFText>
|
|
||||||
<PDFView
|
|
||||||
style={{
|
|
||||||
marginTop: 24,
|
|
||||||
padding: 16,
|
|
||||||
backgroundColor: COLORS.GRID,
|
|
||||||
borderLeftWidth: 2,
|
|
||||||
borderLeftColor: COLORS.DIVIDER,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PDFText style={[styles.moduleLabel, { marginBottom: 8 }]}>
|
|
||||||
Haben Sie Fragen?
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={styles.moduleDesc}>
|
|
||||||
Ich erkläre Ihnen gerne noch einmal persönlich, was die technische
|
|
||||||
Umsetzung für Ihr Projekt bedeutet und wie wir die nächsten Schritte
|
|
||||||
gemeinsam gehen können.
|
|
||||||
</PDFText>
|
|
||||||
<PDFView style={{ marginTop: 16 }}>
|
|
||||||
<PDFText style={styles.moduleLabel}>Kontakt:</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[
|
|
||||||
styles.moduleDesc,
|
|
||||||
{ color: COLORS.CHARCOAL, fontWeight: "bold" },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Marc Mintel – marc@mintel.me
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
"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({
|
|
||||||
table: { marginTop: 12 },
|
|
||||||
tableHeader: {
|
|
||||||
flexDirection: "row",
|
|
||||||
paddingBottom: 8,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
tableRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
paddingVertical: 10,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.GRID,
|
|
||||||
alignItems: "flex-start",
|
|
||||||
},
|
|
||||||
colPos: { width: "8%" },
|
|
||||||
colDesc: { width: "62%" },
|
|
||||||
colQty: { width: "10%", textAlign: "center" },
|
|
||||||
colPrice: { width: "20%", textAlign: "right" },
|
|
||||||
headerText: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 1,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
},
|
|
||||||
posText: { fontSize: FONT_SIZES.TINY, color: COLORS.TEXT_LIGHT },
|
|
||||||
itemTitle: {
|
|
||||||
fontSize: FONT_SIZES.LABEL,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
itemDesc: {
|
|
||||||
fontSize: FONT_SIZES.SMALL,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
},
|
|
||||||
priceText: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
},
|
|
||||||
summaryContainer: {
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderTopColor: COLORS.CHARCOAL,
|
|
||||||
paddingTop: 8,
|
|
||||||
},
|
|
||||||
summaryRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
paddingVertical: 4,
|
|
||||||
alignItems: "baseline",
|
|
||||||
},
|
|
||||||
summaryLabel: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 1,
|
|
||||||
fontWeight: "bold",
|
|
||||||
marginRight: 12,
|
|
||||||
},
|
|
||||||
summaryValue: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
width: 100,
|
|
||||||
textAlign: "right",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
},
|
|
||||||
totalRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
paddingTop: 12,
|
|
||||||
marginTop: 8,
|
|
||||||
borderTopWidth: 2,
|
|
||||||
borderTopColor: COLORS.CHARCOAL,
|
|
||||||
alignItems: "baseline",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const EstimationModule = ({
|
|
||||||
state,
|
|
||||||
positions,
|
|
||||||
totalPrice,
|
|
||||||
date,
|
|
||||||
}: any) => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle
|
|
||||||
title="Kostenschätzung"
|
|
||||||
subLines={[
|
|
||||||
`Datum: ${date}`,
|
|
||||||
`Projekt: ${state.projectType === "website" ? "Website" : "Web App"}`,
|
|
||||||
]}
|
|
||||||
isHero={true}
|
|
||||||
/>
|
|
||||||
<PDFView style={styles.table}>
|
|
||||||
<PDFView style={styles.tableHeader}>
|
|
||||||
<PDFText style={[styles.headerText, styles.colPos]}>Pos</PDFText>
|
|
||||||
<PDFText style={[styles.headerText, styles.colDesc]}>
|
|
||||||
Beschreibung
|
|
||||||
</PDFText>
|
|
||||||
<PDFText style={[styles.headerText, styles.colQty]}>Menge</PDFText>
|
|
||||||
<PDFText style={[styles.headerText, styles.colPrice]}>Betrag</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
{positions.map((item: any, i: number) => (
|
|
||||||
<PDFView key={i} style={styles.tableRow} wrap={false}>
|
|
||||||
<PDFText style={[styles.posText, styles.colPos]}>
|
|
||||||
{item.pos.toString().padStart(2, "0")}
|
|
||||||
</PDFText>
|
|
||||||
<PDFView style={styles.colDesc}>
|
|
||||||
<PDFText style={styles.itemTitle}>{item.title}</PDFText>
|
|
||||||
<PDFText style={styles.itemDesc}>
|
|
||||||
{state.positionDescriptions?.[item.title] || item.desc}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFText style={[styles.posText, styles.colQty]}>{item.qty}</PDFText>
|
|
||||||
<PDFText style={[styles.priceText, styles.colPrice]}>
|
|
||||||
{item.price > 0
|
|
||||||
? `${item.price.toLocaleString("de-DE")} €`
|
|
||||||
: "n. A."}
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={styles.summaryContainer} wrap={false}>
|
|
||||||
<PDFView style={styles.summaryRow}>
|
|
||||||
<PDFText style={styles.summaryLabel}>Nettobetrag</PDFText>
|
|
||||||
<PDFText style={styles.summaryValue}>
|
|
||||||
{totalPrice.toLocaleString("de-DE")} €
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={styles.summaryRow}>
|
|
||||||
<PDFText style={styles.summaryLabel}>Umsatzsteuer (19%)</PDFText>
|
|
||||||
<PDFText style={styles.summaryValue}>
|
|
||||||
{(totalPrice * 0.19).toLocaleString("de-DE")} €
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
<PDFView style={styles.totalRow}>
|
|
||||||
<PDFText style={styles.summaryLabel}>Gesamtbetrag (Brutto)</PDFText>
|
|
||||||
<PDFText
|
|
||||||
style={[styles.summaryValue, { fontSize: FONT_SIZES.HEADING }]}
|
|
||||||
>
|
|
||||||
{(totalPrice * 1.19).toLocaleString("de-DE")} €
|
|
||||||
</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
"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.HERO,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 16,
|
|
||||||
textAlign: "center",
|
|
||||||
maxWidth: "85%",
|
|
||||||
lineHeight: 1.2,
|
|
||||||
},
|
|
||||||
titleCustomerName: {
|
|
||||||
fontSize: FONT_SIZES.HEADING,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
marginBottom: 40,
|
|
||||||
textAlign: "center",
|
|
||||||
maxWidth: "80%",
|
|
||||||
},
|
|
||||||
titleDocumentType: {
|
|
||||||
fontSize: FONT_SIZES.BODY + 1, // ~10
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 4,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
titleDivider: {
|
|
||||||
width: 40,
|
|
||||||
height: 2,
|
|
||||||
backgroundColor: COLORS.CHARCOAL,
|
|
||||||
marginBottom: 40,
|
|
||||||
},
|
|
||||||
titleDate: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_LIGHT,
|
|
||||||
marginTop: 40,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const FrontPageModule = ({ state, headerIcon, date }: any) => {
|
|
||||||
const fullTitle = `Digitale Webpräsenz für\n${state.companyName || "Ihr Projekt"}`;
|
|
||||||
|
|
||||||
// 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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
"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: 32 },
|
|
||||||
intro: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
marginBottom: 24,
|
|
||||||
textAlign: "justify",
|
|
||||||
},
|
|
||||||
sitemapTree: { marginTop: 8 },
|
|
||||||
rootNode: {
|
|
||||||
padding: 12,
|
|
||||||
backgroundColor: COLORS.GRID,
|
|
||||||
marginBottom: 20,
|
|
||||||
borderLeftWidth: 2,
|
|
||||||
borderLeftColor: COLORS.CHARCOAL,
|
|
||||||
},
|
|
||||||
rootTitle: {
|
|
||||||
fontSize: FONT_SIZES.HEADING,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
},
|
|
||||||
categorySection: { marginBottom: 20 },
|
|
||||||
categoryHeader: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
paddingBottom: 6,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: COLORS.BLUEPRINT,
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
categoryIcon: {
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
backgroundColor: COLORS.GRID,
|
|
||||||
borderInlineWidth: 1,
|
|
||||||
borderColor: COLORS.DIVIDER,
|
|
||||||
marginRight: 10,
|
|
||||||
},
|
|
||||||
categoryTitle: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.CHARCOAL,
|
|
||||||
textTransform: "uppercase",
|
|
||||||
letterSpacing: 1,
|
|
||||||
},
|
|
||||||
pagesGrid: { flexDirection: "row", flexWrap: "wrap" },
|
|
||||||
pageCard: {
|
|
||||||
width: "48%",
|
|
||||||
marginRight: "2%",
|
|
||||||
marginBottom: 12,
|
|
||||||
padding: 10,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: COLORS.GRID,
|
|
||||||
backgroundColor: "#fafafa",
|
|
||||||
},
|
|
||||||
pageTitle: {
|
|
||||||
fontSize: FONT_SIZES.BODY,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: COLORS.TEXT_MAIN,
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
pageDesc: {
|
|
||||||
fontSize: FONT_SIZES.TINY,
|
|
||||||
color: COLORS.TEXT_DIM,
|
|
||||||
lineHeight: 1.3,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const SitemapModule = ({ state }: any) => (
|
|
||||||
<>
|
|
||||||
<DocumentTitle title="Informationsarchitektur" isHero={true} />
|
|
||||||
<PDFView style={styles.section}>
|
|
||||||
<PDFText style={styles.intro}>
|
|
||||||
Die folgende Struktur definiert die logische Hierarchie und
|
|
||||||
Benutzerführung. Sie dient als Bauplan für die technische Umsetzung und
|
|
||||||
stellt sicher, dass alle relevanten Geschäftsbereiche intuitiv
|
|
||||||
auffindbar sind.
|
|
||||||
</PDFText>
|
|
||||||
|
|
||||||
<PDFView style={styles.sitemapTree}>
|
|
||||||
<PDFView style={styles.rootNode}>
|
|
||||||
<PDFText style={styles.rootTitle}>Seitenstruktur</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
|
|
||||||
{state.sitemap?.map((cat: any, i: number) => (
|
|
||||||
<PDFView key={i} style={styles.categorySection} wrap={false}>
|
|
||||||
<PDFView style={styles.categoryHeader}>
|
|
||||||
<PDFView style={styles.categoryIcon} />
|
|
||||||
<PDFText style={styles.categoryTitle}>{cat.category}</PDFText>
|
|
||||||
</PDFView>
|
|
||||||
|
|
||||||
<PDFView style={styles.pagesGrid}>
|
|
||||||
{cat.pages.map((p: any, j: number) => (
|
|
||||||
<PDFView
|
|
||||||
key={j}
|
|
||||||
style={[
|
|
||||||
styles.pageCard,
|
|
||||||
j % 2 === 1 ? { marginRight: 0 } : {},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<PDFText style={styles.pageTitle}>{p.title}</PDFText>
|
|
||||||
{p.desc && (
|
|
||||||
<PDFText style={styles.pageDesc}>{p.desc}</PDFText>
|
|
||||||
)}
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
))}
|
|
||||||
</PDFView>
|
|
||||||
</PDFView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
import { FormState, Position, Totals } from "./types";
|
|
||||||
import {
|
|
||||||
FEATURE_LABELS,
|
|
||||||
FUNCTION_LABELS,
|
|
||||||
API_LABELS,
|
|
||||||
PAGE_LABELS,
|
|
||||||
} from "./constants";
|
|
||||||
|
|
||||||
export function calculateTotals(state: FormState, pricing: any): Totals {
|
|
||||||
if (state.projectType !== "website") {
|
|
||||||
return {
|
|
||||||
totalPrice: 0,
|
|
||||||
monthlyPrice: 0,
|
|
||||||
totalPagesCount: 0,
|
|
||||||
totalFeatures: 0,
|
|
||||||
totalFunctions: 0,
|
|
||||||
totalApis: 0,
|
|
||||||
languagesCount: 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 totalFeatures =
|
|
||||||
(state.features?.length || 0) +
|
|
||||||
(state.otherFeatures?.length || 0) +
|
|
||||||
(state.otherFeaturesCount || 0);
|
|
||||||
const totalFunctions =
|
|
||||||
(state.functions?.length || 0) +
|
|
||||||
(state.otherFunctions?.length || 0) +
|
|
||||||
(state.otherFunctionsCount || 0);
|
|
||||||
const totalApis =
|
|
||||||
(state.apiSystems?.length || 0) +
|
|
||||||
(state.otherTech?.length || 0) +
|
|
||||||
(state.otherTechCount || 0);
|
|
||||||
|
|
||||||
let total = pricing.BASE_WEBSITE;
|
|
||||||
total += totalPagesCount * pricing.PAGE;
|
|
||||||
total += totalFeatures * pricing.FEATURE;
|
|
||||||
total += totalFunctions * pricing.FUNCTION;
|
|
||||||
total += totalApis * pricing.API_INTEGRATION;
|
|
||||||
total += (state.newDatasets || 0) * pricing.NEW_DATASET;
|
|
||||||
|
|
||||||
if (state.cmsSetup) {
|
|
||||||
total += Math.max(1, totalFeatures) * pricing.CMS_CONNECTION_PER_FEATURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const languagesCount = state.languagesList?.length || 1;
|
|
||||||
if (languagesCount > 1) {
|
|
||||||
total *= 1 + (languagesCount - 1) * 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const monthlyPrice =
|
|
||||||
pricing.HOSTING_MONTHLY +
|
|
||||||
(state.storageExpansion || 0) * pricing.STORAGE_EXPANSION_MONTHLY;
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalPrice: Math.round(total),
|
|
||||||
monthlyPrice: Math.round(monthlyPrice),
|
|
||||||
totalPagesCount,
|
|
||||||
totalFeatures,
|
|
||||||
totalFunctions,
|
|
||||||
totalApis,
|
|
||||||
languagesCount,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function calculatePositions(state: FormState, pricing: any): Position[] {
|
|
||||||
const positions: Position[] = [];
|
|
||||||
let pos = 1;
|
|
||||||
|
|
||||||
if (state.projectType === "website") {
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Das technische Fundament",
|
|
||||||
desc: "Projekt-Setup, Infrastruktur, Hosting-Bereitstellung, Grundstruktur & Design-Vorlage, technisches SEO-Basics, Analytics.",
|
|
||||||
qty: 1,
|
|
||||||
price: pricing.BASE_WEBSITE,
|
|
||||||
});
|
|
||||||
|
|
||||||
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 = [
|
|
||||||
...(state.selectedPages || []).map((p: string) => PAGE_LABELS[p] || p),
|
|
||||||
...(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({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Individuelle Seiten",
|
|
||||||
desc: `Gestaltung und Umsetzung von ${totalPagesCount} individuellen Seiten-Layouts (${uniquePages.join(", ")}).`,
|
|
||||||
qty: totalPagesCount,
|
|
||||||
price: totalPagesCount * pricing.PAGE,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (state.features.length > 0 || (state.otherFeatures?.length || 0) > 0) {
|
|
||||||
const allFeatures = [
|
|
||||||
...state.features.map((f: string) => FEATURE_LABELS[f] || f),
|
|
||||||
...(state.otherFeatures || []),
|
|
||||||
];
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "System-Module (Features)",
|
|
||||||
desc: `Implementierung funktionaler Bereiche: ${allFeatures.join(", ")}. Inklusive Datenstruktur und Darstellung.`,
|
|
||||||
qty: allFeatures.length,
|
|
||||||
price: allFeatures.length * pricing.FEATURE,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.functions.length > 0 || (state.otherFunctions?.length || 0) > 0) {
|
|
||||||
const allFunctions = [
|
|
||||||
...state.functions.map((f: string) => FUNCTION_LABELS[f] || f),
|
|
||||||
...(state.otherFunctions || []),
|
|
||||||
];
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Logik-Funktionen",
|
|
||||||
desc: `Implementierung technischer Logik: ${allFunctions.join(", ")}.`,
|
|
||||||
qty: allFunctions.length,
|
|
||||||
price: allFunctions.length * pricing.FUNCTION,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.apiSystems.length > 0 || (state.otherTech?.length || 0) > 0) {
|
|
||||||
const allApis = [
|
|
||||||
...state.apiSystems.map((a: string) => API_LABELS[a] || a),
|
|
||||||
...(state.otherTech || []),
|
|
||||||
];
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Schnittstellen (API)",
|
|
||||||
desc: `Anbindung externer Systeme zur Datensynchronisation: ${allApis.join(", ")}.`,
|
|
||||||
qty: allApis.length,
|
|
||||||
price: allApis.length * pricing.API_INTEGRATION,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.cmsSetup) {
|
|
||||||
const totalFeatures =
|
|
||||||
state.features.length +
|
|
||||||
(state.otherFeatures?.length || 0) +
|
|
||||||
(state.otherFeaturesCount || 0);
|
|
||||||
const qty = Math.max(1, totalFeatures);
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Inhalts-Verwaltung",
|
|
||||||
desc: "Anbindung der System-Module an das Redaktions-System zur eigenständigen Pflege von Inhalten.",
|
|
||||||
qty: qty,
|
|
||||||
price: qty * pricing.CMS_CONNECTION_PER_FEATURE,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.newDatasets > 0) {
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Inhaltliche Initial-Pflege",
|
|
||||||
desc: `Manuelle Übernahme und Aufbereitung von ${state.newDatasets} Datensätzen (Produkte, Artikel) in das Zielsystem.`,
|
|
||||||
qty: state.newDatasets,
|
|
||||||
price: state.newDatasets * pricing.NEW_DATASET,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const languagesCount = state.languagesList.length || 1;
|
|
||||||
if (languagesCount > 1) {
|
|
||||||
const subtotal = positions.reduce((sum, p) => sum + p.price, 0);
|
|
||||||
const factorPrice = subtotal * ((languagesCount - 1) * 0.2);
|
|
||||||
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Mehrsprachigkeit",
|
|
||||||
desc: `Erweiterung des Systems auf ${languagesCount} Sprachen (Struktur & Logik).`,
|
|
||||||
qty: languagesCount,
|
|
||||||
price: Math.round(factorPrice),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const monthlyRate =
|
|
||||||
pricing.HOSTING_MONTHLY +
|
|
||||||
state.storageExpansion * pricing.STORAGE_EXPANSION_MONTHLY;
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Sorglos Betrieb (1 Jahr)",
|
|
||||||
desc: `Inklusive 1 Jahr Sicherung des technischen Betriebs, Hosting, Instandhaltung, Sicherheits-Updates und techn. Support gemäß AGB Punkt 7a.`,
|
|
||||||
qty: 1,
|
|
||||||
price: monthlyRate * 12,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
positions.push({
|
|
||||||
pos: pos++,
|
|
||||||
title: "Web App / Software Entwicklung",
|
|
||||||
desc: "Individuelle Software-Entwicklung nach Aufwand. Abrechnung erfolgt auf Stundenbasis.",
|
|
||||||
qty: 1,
|
|
||||||
price: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return positions;
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './constants';
|
export * from "./constants";
|
||||||
export * from './calculator';
|
export * from "./types";
|
||||||
export * from './types';
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@directus/sdk": "21.0.0",
|
"@directus/sdk": "21.0.0",
|
||||||
"@eslint/compat": "^2.0.2"
|
"@eslint/compat": "^2.0.2",
|
||||||
|
"@mintel/acquisition": "link:../at-mintel/packages/acquisition-library"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
pnpm-lock.yaml
generated
84
pnpm-lock.yaml
generated
@@ -18,6 +18,9 @@ importers:
|
|||||||
"@eslint/compat":
|
"@eslint/compat":
|
||||||
specifier: ^2.0.2
|
specifier: ^2.0.2
|
||||||
version: 2.0.2(eslint@10.0.0(jiti@1.21.7))
|
version: 2.0.2(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
"@mintel/acquisition":
|
||||||
|
specifier: link:../at-mintel/packages/acquisition-library
|
||||||
|
version: link:../at-mintel/packages/acquisition-library
|
||||||
devDependencies:
|
devDependencies:
|
||||||
"@eslint/eslintrc":
|
"@eslint/eslintrc":
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
@@ -30,7 +33,7 @@ importers:
|
|||||||
version: 1.7.8
|
version: 1.7.8
|
||||||
"@mintel/eslint-config":
|
"@mintel/eslint-config":
|
||||||
specifier: ^1.7.3
|
specifier: ^1.7.3
|
||||||
version: 1.7.8(@typescript-eslint/parser@8.54.0(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3))(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)
|
version: 1.7.8(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)
|
||||||
"@mintel/husky-config":
|
"@mintel/husky-config":
|
||||||
specifier: ^1.7.3
|
specifier: ^1.7.3
|
||||||
version: 1.7.8
|
version: 1.7.8
|
||||||
@@ -91,6 +94,9 @@ importers:
|
|||||||
"@directus/sdk":
|
"@directus/sdk":
|
||||||
specifier: 21.0.0
|
specifier: 21.0.0
|
||||||
version: 21.0.0
|
version: 21.0.0
|
||||||
|
"@mintel/pdf":
|
||||||
|
specifier: link:../../../at-mintel/packages/pdf-library
|
||||||
|
version: link:../../../at-mintel/packages/pdf-library
|
||||||
"@opentelemetry/api":
|
"@opentelemetry/api":
|
||||||
specifier: ^1.9.0
|
specifier: ^1.9.0
|
||||||
version: 1.9.0
|
version: 1.9.0
|
||||||
@@ -126,7 +132,7 @@ importers:
|
|||||||
version: 10.38.0
|
version: 10.38.0
|
||||||
"@sentry/nextjs":
|
"@sentry/nextjs":
|
||||||
specifier: 10.38.0
|
specifier: 10.38.0
|
||||||
version: 10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)
|
version: 10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)
|
||||||
"@types/canvas-confetti":
|
"@types/canvas-confetti":
|
||||||
specifier: ^1.9.0
|
specifier: ^1.9.0
|
||||||
version: 1.9.0
|
version: 1.9.0
|
||||||
@@ -13317,15 +13323,32 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
"@mintel/eslint-config@1.7.8(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)":
|
||||||
|
dependencies:
|
||||||
|
"@eslint/eslintrc": 3.3.3
|
||||||
|
"@eslint/js": 9.39.2
|
||||||
|
"@next/eslint-plugin-next": 16.1.6
|
||||||
|
eslint-config-next: 16.1.6(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)
|
||||||
|
eslint-plugin-react: 7.37.5(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
eslint-plugin-react-hooks: 7.0.1(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
typescript-eslint: 8.54.0(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- "@typescript-eslint/parser"
|
||||||
|
- eslint
|
||||||
|
- eslint-import-resolver-webpack
|
||||||
|
- eslint-plugin-import-x
|
||||||
|
- supports-color
|
||||||
|
- typescript
|
||||||
|
|
||||||
"@mintel/husky-config@1.7.8":
|
"@mintel/husky-config@1.7.8":
|
||||||
dependencies:
|
dependencies:
|
||||||
"@commitlint/config-conventional": 20.4.1
|
"@commitlint/config-conventional": 20.4.1
|
||||||
|
|
||||||
"@mintel/next-config@1.7.8(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.18)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.96.1)":
|
"@mintel/next-config@1.7.8(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.18)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(webpack@5.96.1)":
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sentry/nextjs": 10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)
|
"@sentry/nextjs": 10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)
|
||||||
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-intl: 4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
next-intl: 4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- "@babel/core"
|
- "@babel/core"
|
||||||
- "@opentelemetry/api"
|
- "@opentelemetry/api"
|
||||||
@@ -13348,7 +13371,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@directus/sdk": 21.0.0
|
"@directus/sdk": 21.0.0
|
||||||
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-intl: 4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
next-intl: 4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||||
zod: 3.22.3
|
zod: 3.22.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- "@babel/core"
|
- "@babel/core"
|
||||||
@@ -14276,7 +14299,7 @@ snapshots:
|
|||||||
"@sentry/utils": 7.120.4
|
"@sentry/utils": 7.120.4
|
||||||
localforage: 1.10.0
|
localforage: 1.10.0
|
||||||
|
|
||||||
"@sentry/nextjs@10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)":
|
"@sentry/nextjs@10.38.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(webpack@5.96.1)":
|
||||||
dependencies:
|
dependencies:
|
||||||
"@opentelemetry/api": 1.9.0
|
"@opentelemetry/api": 1.9.0
|
||||||
"@opentelemetry/semantic-conventions": 1.39.0
|
"@opentelemetry/semantic-conventions": 1.39.0
|
||||||
@@ -16402,6 +16425,26 @@ snapshots:
|
|||||||
- eslint-plugin-import-x
|
- eslint-plugin-import-x
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
eslint-config-next@16.1.6(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3):
|
||||||
|
dependencies:
|
||||||
|
"@next/eslint-plugin-next": 16.1.6
|
||||||
|
eslint: 10.0.0(jiti@1.21.7)
|
||||||
|
eslint-import-resolver-node: 0.3.9
|
||||||
|
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
eslint-plugin-jsx-a11y: 6.10.2(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
eslint-plugin-react: 7.37.5(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
eslint-plugin-react-hooks: 7.0.1(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
globals: 16.4.0
|
||||||
|
typescript-eslint: 8.54.0(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3)
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.9.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- "@typescript-eslint/parser"
|
||||||
|
- eslint-import-resolver-webpack
|
||||||
|
- eslint-plugin-import-x
|
||||||
|
- supports-color
|
||||||
|
|
||||||
eslint-import-resolver-node@0.3.9:
|
eslint-import-resolver-node@0.3.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
@@ -16465,6 +16508,33 @@ snapshots:
|
|||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@10.0.0(jiti@1.21.7)):
|
||||||
|
dependencies:
|
||||||
|
"@rtsao/scc": 1.1.0
|
||||||
|
array-includes: 3.1.9
|
||||||
|
array.prototype.findlastindex: 1.2.6
|
||||||
|
array.prototype.flat: 1.3.3
|
||||||
|
array.prototype.flatmap: 1.3.3
|
||||||
|
debug: 3.2.7
|
||||||
|
doctrine: 2.1.0
|
||||||
|
eslint: 10.0.0(jiti@1.21.7)
|
||||||
|
eslint-import-resolver-node: 0.3.9
|
||||||
|
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@10.0.0(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@10.0.0(jiti@1.21.7))
|
||||||
|
hasown: 2.0.2
|
||||||
|
is-core-module: 2.16.1
|
||||||
|
is-glob: 4.0.3
|
||||||
|
minimatch: 3.1.2
|
||||||
|
object.fromentries: 2.0.8
|
||||||
|
object.groupby: 1.0.3
|
||||||
|
object.values: 1.2.1
|
||||||
|
semver: 6.3.1
|
||||||
|
string.prototype.trimend: 1.0.9
|
||||||
|
tsconfig-paths: 3.15.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- eslint-import-resolver-typescript
|
||||||
|
- eslint-import-resolver-webpack
|
||||||
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-jsx-a11y@6.10.2(eslint@10.0.0(jiti@1.21.7)):
|
eslint-plugin-jsx-a11y@6.10.2(eslint@10.0.0(jiti@1.21.7)):
|
||||||
dependencies:
|
dependencies:
|
||||||
aria-query: 5.3.2
|
aria-query: 5.3.2
|
||||||
@@ -18003,7 +18073,7 @@ snapshots:
|
|||||||
|
|
||||||
next-intl-swc-plugin-extractor@4.8.2: {}
|
next-intl-swc-plugin-extractor@4.8.2: {}
|
||||||
|
|
||||||
next-intl@4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
next-intl@4.8.2(@swc/helpers@0.5.18)(next@16.1.6(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
"@formatjs/intl-localematcher": 0.5.10
|
"@formatjs/intl-localematcher": 0.5.10
|
||||||
"@parcel/watcher": 2.5.6
|
"@parcel/watcher": 2.5.6
|
||||||
|
|||||||
Reference in New Issue
Block a user