feat(pdf): rename acquisition-library to pdf-library and update package name to @mintel/pdf
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 13s
Monorepo Pipeline / 🏗️ Build (push) Failing after 11s
Monorepo Pipeline / 🧪 Test (push) Failing after 25s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 1s
Monorepo Pipeline / 🧹 Lint (push) Failing after 13s
Monorepo Pipeline / 🏗️ Build (push) Failing after 11s
Monorepo Pipeline / 🧪 Test (push) Failing after 25s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
This commit is contained in:
224
packages/pdf-library/src/logic/pricing/calculator.ts
Normal file
224
packages/pdf-library/src/logic/pricing/calculator.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import { FormState, Position, Totals } from "./types.js";
|
||||
import {
|
||||
FEATURE_LABELS,
|
||||
FUNCTION_LABELS,
|
||||
API_LABELS,
|
||||
PAGE_LABELS,
|
||||
} from "./constants.js";
|
||||
|
||||
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) > 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) > 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) > 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 || 0) +
|
||||
(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;
|
||||
}
|
||||
332
packages/pdf-library/src/logic/pricing/constants.ts
Normal file
332
packages/pdf-library/src/logic/pricing/constants.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
import { FormState } from "./types.js";
|
||||
|
||||
export const PRICING = {
|
||||
BASE_WEBSITE: 5440, // Updated to match AI prompt requirement in Pass 1
|
||||
PAGE: 600,
|
||||
FEATURE: 1500,
|
||||
FUNCTION: 800,
|
||||
NEW_DATASET: 450,
|
||||
HOSTING_MONTHLY: 250,
|
||||
STORAGE_EXPANSION_MONTHLY: 10,
|
||||
CMS_SETUP: 1500,
|
||||
CMS_CONNECTION_PER_FEATURE: 1500,
|
||||
API_INTEGRATION: 800,
|
||||
APP_HOURLY: 120,
|
||||
};
|
||||
|
||||
export const initialState: FormState = {
|
||||
projectType: "website",
|
||||
// Company
|
||||
companyName: "",
|
||||
employeeCount: "",
|
||||
// Existing Presence
|
||||
existingWebsite: "",
|
||||
socialMedia: [],
|
||||
socialMediaUrls: {},
|
||||
existingDomain: "",
|
||||
wishedDomain: "",
|
||||
// Project
|
||||
websiteTopic: "",
|
||||
selectedPages: ["Home"],
|
||||
otherPages: [],
|
||||
otherPagesCount: 0,
|
||||
features: [],
|
||||
otherFeatures: [],
|
||||
otherFeaturesCount: 0,
|
||||
functions: [],
|
||||
otherFunctions: [],
|
||||
otherFunctionsCount: 0,
|
||||
apiSystems: [],
|
||||
otherTech: [],
|
||||
otherTechCount: 0,
|
||||
assets: [],
|
||||
otherAssets: [],
|
||||
otherAssetsCount: 0,
|
||||
newDatasets: 0,
|
||||
cmsSetup: false,
|
||||
storageExpansion: 0,
|
||||
name: "",
|
||||
email: "",
|
||||
role: "",
|
||||
message: "",
|
||||
sitemapFile: null,
|
||||
contactFiles: [],
|
||||
// Design
|
||||
designVibe: "minimal",
|
||||
colorScheme: ["#ffffff", "#f8fafc", "#0f172a"],
|
||||
references: [],
|
||||
designWishes: "",
|
||||
// Maintenance
|
||||
expectedAdjustments: "low",
|
||||
languagesList: ["Deutsch"],
|
||||
personName: "",
|
||||
// Timeline
|
||||
deadline: "flexible",
|
||||
// Web App specific
|
||||
targetAudience: "internal",
|
||||
userRoles: [],
|
||||
dataSensitivity: "standard",
|
||||
platformType: "web-only",
|
||||
// Meta
|
||||
dontKnows: [],
|
||||
visualStaging: "standard",
|
||||
complexInteractions: "standard",
|
||||
// AI generated / Post-processed
|
||||
briefingSummary: "",
|
||||
designVision: "",
|
||||
positionDescriptions: {},
|
||||
taxId: "",
|
||||
sitemap: [],
|
||||
};
|
||||
|
||||
export const PAGE_SAMPLES = [
|
||||
{ id: "Home", label: "Startseite", desc: "Der erste Eindruck Ihrer Marke." },
|
||||
{ id: "About", label: "Über uns", desc: "Ihre Geschichte und Ihr Team." },
|
||||
{ id: "Services", label: "Leistungen", desc: "Übersicht Ihres Angebots." },
|
||||
{ id: "Contact", label: "Kontakt", desc: "Anlaufstelle für Ihre Kunden." },
|
||||
{
|
||||
id: "Landing",
|
||||
label: "Landingpage",
|
||||
desc: "Optimiert für Marketing-Kampagnen.",
|
||||
},
|
||||
{ id: "Legal", label: "Rechtliches", desc: "Impressum & Datenschutz." },
|
||||
];
|
||||
|
||||
export const FEATURE_OPTIONS = [
|
||||
{
|
||||
id: "blog_news",
|
||||
label: "Blog / News",
|
||||
desc: "Ein Bereich für aktuelle Beiträge und Neuigkeiten.",
|
||||
},
|
||||
{
|
||||
id: "products",
|
||||
label: "Produktbereich",
|
||||
desc: "Katalog Ihrer Leistungen oder Produkte.",
|
||||
},
|
||||
{
|
||||
id: "jobs",
|
||||
label: "Karriere / Jobs",
|
||||
desc: "Stellenanzeigen und Bewerbungsoptionen.",
|
||||
},
|
||||
{
|
||||
id: "refs",
|
||||
label: "Referenzen / Cases",
|
||||
desc: "Präsentation Ihrer Projekte.",
|
||||
},
|
||||
{ id: "events", label: "Events / Termine", desc: "Veranstaltungskalender." },
|
||||
];
|
||||
|
||||
export const FUNCTION_OPTIONS = [
|
||||
{ id: "search", label: "Suche", desc: "Volltextsuche über alle Inhalte." },
|
||||
{
|
||||
id: "filter",
|
||||
label: "Filter-Systeme",
|
||||
desc: "Kategorisierung und Sortierung.",
|
||||
},
|
||||
{ id: "pdf", label: "PDF-Export", desc: "Automatisierte PDF-Erstellung." },
|
||||
{
|
||||
id: "forms",
|
||||
label: "Individuelle Formular-Logik",
|
||||
desc: "Smarte Validierung & mehrstufige Prozesse.",
|
||||
},
|
||||
];
|
||||
|
||||
export const API_OPTIONS = [
|
||||
{
|
||||
id: "crm",
|
||||
label: "CRM System",
|
||||
desc: "HubSpot, Salesforce, Pipedrive etc.",
|
||||
},
|
||||
{
|
||||
id: "erp",
|
||||
label: "ERP / Warenwirtschaft",
|
||||
desc: "SAP, Microsoft Dynamics, Xentral etc.",
|
||||
},
|
||||
{
|
||||
id: "stripe",
|
||||
label: "Stripe / Payment",
|
||||
desc: "Zahlungsabwicklung und Abonnements.",
|
||||
},
|
||||
{
|
||||
id: "newsletter",
|
||||
label: "Newsletter / Marketing",
|
||||
desc: "Mailchimp, Brevo, ActiveCampaign etc.",
|
||||
},
|
||||
{
|
||||
id: "ecommerce",
|
||||
label: "E-Commerce / Shop",
|
||||
desc: "Shopify, WooCommerce, Shopware Sync.",
|
||||
},
|
||||
{
|
||||
id: "hr",
|
||||
label: "HR / Recruiting",
|
||||
desc: "Personio, Workday, Recruitee etc.",
|
||||
},
|
||||
{
|
||||
id: "realestate",
|
||||
label: "Immobilien",
|
||||
desc: "OpenImmo, FlowFact, Immowelt Sync.",
|
||||
},
|
||||
{
|
||||
id: "calendar",
|
||||
label: "Termine / Booking",
|
||||
desc: "Calendly, Shore, Doctolib etc.",
|
||||
},
|
||||
{
|
||||
id: "social",
|
||||
label: "Social Media Sync",
|
||||
desc: "Automatisierte Posts oder Feeds.",
|
||||
},
|
||||
{
|
||||
id: "maps",
|
||||
label: "Google Maps / Places",
|
||||
desc: "Standortsuche und Kartenintegration.",
|
||||
},
|
||||
{
|
||||
id: "analytics",
|
||||
label: "Custom Analytics",
|
||||
desc: "Anbindung an spezialisierte Tracking-Tools.",
|
||||
},
|
||||
];
|
||||
|
||||
export const ASSET_OPTIONS = [
|
||||
{
|
||||
id: "existing_website",
|
||||
label: "Bestehende Website",
|
||||
desc: "Inhalte oder Struktur können übernommen werden.",
|
||||
},
|
||||
{ id: "logo", label: "Logo", desc: "Vektordatei Ihres Logos." },
|
||||
{
|
||||
id: "styleguide",
|
||||
label: "Styleguide",
|
||||
desc: "Farben, Schriften, Design-Vorgaben.",
|
||||
},
|
||||
{
|
||||
id: "content_concept",
|
||||
label: "Inhalts-Konzept",
|
||||
desc: "Struktur und Texte sind bereits geplant.",
|
||||
},
|
||||
{
|
||||
id: "media",
|
||||
label: "Bild/Video-Material",
|
||||
desc: "Professionelles Bildmaterial vorhanden.",
|
||||
},
|
||||
{ id: "icons", label: "Icons", desc: "Eigene Icon-Sets vorhanden." },
|
||||
{
|
||||
id: "illustrations",
|
||||
label: "Illustrationen",
|
||||
desc: "Eigene Illustrationen vorhanden.",
|
||||
},
|
||||
{
|
||||
id: "fonts",
|
||||
label: "Fonts",
|
||||
desc: "Lizenzen für Hausschriften vorhanden.",
|
||||
},
|
||||
];
|
||||
|
||||
export const DESIGN_OPTIONS = [
|
||||
{
|
||||
id: "minimal",
|
||||
label: "Minimalistisch",
|
||||
desc: "Viel Weißraum, klare Typografie.",
|
||||
},
|
||||
{
|
||||
id: "bold",
|
||||
label: "Mutig & Laut",
|
||||
desc: "Starke Kontraste, große Schriften.",
|
||||
},
|
||||
{
|
||||
id: "nature",
|
||||
label: "Natürlich",
|
||||
desc: "Sanfte Erdtöne, organische Formen.",
|
||||
},
|
||||
{ id: "tech", label: "Technisch", desc: "Präzise Linien, dunkle Akzente." },
|
||||
];
|
||||
|
||||
export const EMPLOYEE_OPTIONS = [
|
||||
{ id: "1-5", label: "1-5 Mitarbeiter" },
|
||||
{ id: "6-20", label: "6-20 Mitarbeiter" },
|
||||
{ id: "21-100", label: "21-100 Mitarbeiter" },
|
||||
{ id: "100+", label: "100+ Mitarbeiter" },
|
||||
];
|
||||
|
||||
export const SOCIAL_MEDIA_OPTIONS = [
|
||||
{ id: "instagram", label: "Instagram" },
|
||||
{ id: "linkedin", label: "LinkedIn" },
|
||||
{ id: "facebook", label: "Facebook" },
|
||||
{ id: "twitter", label: "Twitter / X" },
|
||||
{ id: "tiktok", label: "TikTok" },
|
||||
{ id: "youtube", label: "YouTube" },
|
||||
];
|
||||
|
||||
export const VIBE_LABELS: Record<string, string> = {
|
||||
minimal: "Minimalistisch",
|
||||
bold: "Mutig & Laut",
|
||||
nature: "Natürlich",
|
||||
tech: "Technisch",
|
||||
};
|
||||
|
||||
export const DEADLINE_LABELS: Record<string, string> = {
|
||||
asap: "So schnell wie möglich",
|
||||
"2-3-months": "In 2-3 Monaten",
|
||||
"3-6-months": "In 3-6 Monaten",
|
||||
flexible: "Flexibel",
|
||||
};
|
||||
|
||||
export const ASSET_LABELS: Record<string, string> = {
|
||||
existing_website: "Bestehende Website",
|
||||
logo: "Logo",
|
||||
styleguide: "Styleguide",
|
||||
content_concept: "Inhalts-Konzept",
|
||||
media: "Bild/Video-Material",
|
||||
icons: "Icons",
|
||||
illustrations: "Illustrationen",
|
||||
fonts: "Fonts",
|
||||
};
|
||||
|
||||
export const FEATURE_LABELS: Record<string, string> = {
|
||||
blog_news: "Blog / News",
|
||||
products: "Produktbereich",
|
||||
jobs: "Karriere / Jobs",
|
||||
refs: "Referenzen / Cases",
|
||||
events: "Events / Termine",
|
||||
};
|
||||
|
||||
export const FUNCTION_LABELS: Record<string, string> = {
|
||||
search: "Suche",
|
||||
filter: "Filter-Systeme",
|
||||
pdf: "PDF-Export",
|
||||
forms: "Individuelle Formular-Logik",
|
||||
members: "Mitgliederbereich",
|
||||
calendar: "Event-Kalender",
|
||||
multilang: "Mehrsprachigkeit",
|
||||
chat: "Echtzeit-Chat",
|
||||
};
|
||||
|
||||
export const API_LABELS: Record<string, string> = {
|
||||
crm_erp: "CRM / ERP",
|
||||
payment: "Payment",
|
||||
marketing: "Marketing",
|
||||
ecommerce: "E-Commerce",
|
||||
maps: "Google Maps / Places",
|
||||
social: "Social Media Sync",
|
||||
analytics: "Custom Analytics",
|
||||
};
|
||||
|
||||
export const SOCIAL_LABELS: Record<string, string> = {
|
||||
instagram: "Instagram",
|
||||
linkedin: "LinkedIn",
|
||||
facebook: "Facebook",
|
||||
twitter: "Twitter / X",
|
||||
tiktok: "TikTok",
|
||||
youtube: "YouTube",
|
||||
};
|
||||
|
||||
export const PAGE_LABELS: Record<string, string> = {
|
||||
Home: "Startseite",
|
||||
About: "Über uns",
|
||||
Services: "Leistungen",
|
||||
Contact: "Kontakt",
|
||||
Landing: "Landingpage",
|
||||
Legal: "Impressum & Datenschutz",
|
||||
};
|
||||
89
packages/pdf-library/src/logic/pricing/types.ts
Normal file
89
packages/pdf-library/src/logic/pricing/types.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
export type ProjectType = 'website' | 'web-app';
|
||||
|
||||
export interface FormState {
|
||||
projectType: ProjectType;
|
||||
// Company
|
||||
companyName: string;
|
||||
employeeCount: string;
|
||||
// Existing Presence
|
||||
existingWebsite: string;
|
||||
socialMedia: string[];
|
||||
socialMediaUrls: Record<string, string>;
|
||||
existingDomain: string;
|
||||
wishedDomain: string;
|
||||
// Project
|
||||
websiteTopic: string;
|
||||
selectedPages: string[];
|
||||
otherPages: string[];
|
||||
otherPagesCount: number;
|
||||
features: string[];
|
||||
otherFeatures: string[];
|
||||
otherFeaturesCount: number;
|
||||
functions: string[];
|
||||
otherFunctions: string[];
|
||||
otherFunctionsCount: number;
|
||||
apiSystems: string[];
|
||||
otherTech: string[];
|
||||
otherTechCount: number;
|
||||
assets: string[];
|
||||
otherAssets: string[];
|
||||
otherAssetsCount: number;
|
||||
newDatasets: number;
|
||||
cmsSetup: boolean;
|
||||
storageExpansion: number;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
message: string;
|
||||
sitemapFile: any;
|
||||
contactFiles: any[];
|
||||
// Design
|
||||
designVibe: string;
|
||||
colorScheme: string[];
|
||||
references: string[];
|
||||
designWishes: string;
|
||||
// Maintenance
|
||||
expectedAdjustments: string;
|
||||
languagesList: string[];
|
||||
// Timeline
|
||||
deadline: string;
|
||||
// Web App specific
|
||||
targetAudience: string;
|
||||
userRoles: string[];
|
||||
dataSensitivity: string;
|
||||
platformType: string;
|
||||
// Meta
|
||||
dontKnows: string[];
|
||||
visualStaging: string;
|
||||
complexInteractions: string;
|
||||
gridDontKnows?: Record<string, string>;
|
||||
briefingSummary?: string;
|
||||
companyAddress?: string;
|
||||
companyPhone?: string;
|
||||
personName?: string;
|
||||
taxId?: string;
|
||||
designVision?: string;
|
||||
positionDescriptions?: Record<string, string>;
|
||||
sitemap?: {
|
||||
category: string;
|
||||
pages: { title: string; desc: string }[];
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
pos: number;
|
||||
title: string;
|
||||
desc: string;
|
||||
qty: number;
|
||||
price: number;
|
||||
isRecurring?: boolean;
|
||||
}
|
||||
export interface Totals {
|
||||
totalPrice: number;
|
||||
monthlyPrice: number;
|
||||
totalPagesCount: number;
|
||||
totalFeatures: number;
|
||||
totalFunctions: number;
|
||||
totalApis: number;
|
||||
languagesCount: number;
|
||||
}
|
||||
Reference in New Issue
Block a user