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; }