fix(blog): refine Recharts gradients/tooltips and fix DiagramGantt crash
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 41s
Build & Deploy / 🏗️ Build (push) Failing after 23s
Build & Deploy / 🧪 QA (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🩺 Health Check (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 41s
Build & Deploy / 🏗️ Build (push) Failing after 23s
Build & Deploy / 🧪 QA (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🩺 Health Check (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
This commit is contained in:
@@ -35,7 +35,8 @@ ${(tasks || [])
|
||||
const deps = task.dependencies?.length
|
||||
? `, after ${task.dependencies.join(" ")}`
|
||||
: "";
|
||||
return ` ${task.name} :${task.id}, ${task.start}, ${task.duration}${deps}`;
|
||||
const safeName = task.name.replace(/:/g, " -");
|
||||
return ` ${safeName} :${task.id}, ${task.start}, ${task.duration}${deps}`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
|
||||
|
||||
@@ -3,130 +3,169 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ComponentShareButton } from '../ComponentShareButton';
|
||||
import { Reveal } from '../Reveal';
|
||||
import { Check, X, Shield, Zap, Expand, Database } from 'lucide-react';
|
||||
import { ComposedChart, Line, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import { ShieldAlert, Rocket, Database, Lock } from 'lucide-react';
|
||||
|
||||
const archData = [
|
||||
{ year: 'Jahr 1', saasInvest: 50, saasIndep: 40, customInvest: 120, customIndep: 80 },
|
||||
{ year: 'Jahr 2', saasInvest: 100, saasIndep: 30, customInvest: 140, customIndep: 90 },
|
||||
{ year: 'Jahr 3', saasInvest: 155, saasIndep: 20, customInvest: 155, customIndep: 95 },
|
||||
{ year: 'Jahr 4', saasInvest: 220, saasIndep: 10, customInvest: 170, customIndep: 100 },
|
||||
{ year: 'Jahr 5', saasInvest: 290, saasIndep: 5, customInvest: 185, customIndep: 100 },
|
||||
];
|
||||
|
||||
export function ArchitectureBuilder({ className = '' }: { className?: string }) {
|
||||
const [stack, setStack] = useState<'saas' | 'custom'>('saas');
|
||||
const [stack, setStack] = useState<'saas' | 'custom'>('custom');
|
||||
|
||||
return (
|
||||
<Reveal direction="up" delay={0.1}>
|
||||
<div className={`not-prose max-w-4xl mx-auto my-12 relative group ${className}`}>
|
||||
<div className={`absolute -inset-1 blur opacity-30 rounded-3xl transition-all duration-700 ${stack === 'saas' ? 'bg-gradient-to-r from-orange-100 to-red-100' : 'bg-gradient-to-r from-emerald-100 to-blue-100'}`} />
|
||||
<div className={`absolute -inset-1 blur opacity-30 rounded-3xl transition-all duration-700 ${stack === 'saas' ? 'bg-gradient-to-r from-orange-200 to-red-100' : 'bg-gradient-to-r from-emerald-200 to-blue-100'}`} />
|
||||
|
||||
<div id="sim-arch-builder" className="relative bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col">
|
||||
<div id="sim-arch-builder" className="relative bg-white rounded-2xl border border-slate-200 shadow-xl shadow-slate-200/50 overflow-hidden flex flex-col">
|
||||
<div data-share-wrapper="true" className="absolute top-4 right-4 md:opacity-0 group-hover:opacity-100 transition-opacity z-50">
|
||||
<ComponentShareButton
|
||||
targetId="sim-arch-builder"
|
||||
title="Architektur Vergleich: Miete vs Eigentum"
|
||||
shareText="Sehen Sie sich diesen Vergleich zwischen Software-Miete und eigenem System an:"
|
||||
title="Architektur: Investment vs. Unabhängigkeit"
|
||||
shareText="Sehen Sie sich den zeitlichen Verlauf an: Software-Miete vs. digitales Eigentum."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-6 border-b border-slate-200 bg-slate-50">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-2xl">🏗️</span>
|
||||
<h3 className="font-bold text-slate-900 m-0">Architektur-Vergleich: Mieten vs. Bauen</h3>
|
||||
<div className="p-6 md:p-8 border-b border-slate-200 bg-slate-50 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-slate-200 rounded-full blur-3xl -mr-32 -mt-32 opacity-50" />
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-2xl">🏗️</span>
|
||||
<h3 className="font-bold text-slate-900 m-0 text-xl">Zeitlicher Verlauf: Investment vs. Unabhängigkeit</h3>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 m-0 leading-relaxed max-w-2xl">
|
||||
Wie Plattform-Abhängigkeit (Lock-in) über die Jahre Ihre Skalierbarkeit zerstört, während ein Headless-System echte Souveränität aufbaut.
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 m-0 leading-relaxed max-w-2xl">
|
||||
Entdecken Sie die strukturellen Unterschiede zwischen einem "Out-of-the-Box" Baukasten und einer maßgeschneiderten Architektur für Ihr Unternehmen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row">
|
||||
{/* Control Panel */}
|
||||
<div className="w-full md:w-1/3 p-6 bg-slate-50 md:border-r border-b md:border-b-0 border-slate-100 space-y-4">
|
||||
<label className="text-[10px] font-bold text-slate-400 uppercase tracking-widest block mb-4">Ihr System-Ansatz</label>
|
||||
|
||||
<div className="flex flex-col">
|
||||
{/* Control Tabs */}
|
||||
<div className="flex border-b border-slate-100">
|
||||
<button
|
||||
onClick={() => setStack('saas')}
|
||||
className={`w-full text-left p-4 rounded-xl border-2 transition-all ${stack === 'saas' ? 'border-orange-500 bg-white shadow-sm' : 'border-transparent hover:bg-slate-100'}`}
|
||||
className={`flex-1 p-4 flex flex-col items-center justify-center gap-2 transition-all ${stack === 'saas' ? 'bg-white border-b-2 border-orange-500' : 'bg-slate-50 text-slate-400 hover:bg-slate-100'}`}
|
||||
>
|
||||
<div className="font-bold text-slate-900 text-sm">🏡 Software Mieten (SaaS)</div>
|
||||
<div className="text-xs text-slate-500 mt-1">Baukästen, Plugins, fixe Lizenzen</div>
|
||||
<div className={`p-2 rounded-full ${stack === 'saas' ? 'bg-orange-100 text-orange-600' : 'bg-slate-200'}`}>
|
||||
<Lock size={20} />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className={`font-bold text-sm ${stack === 'saas' ? 'text-slate-900' : ''}`}>SaaS / Baukasten</div>
|
||||
<div className="text-[10px] uppercase tracking-wider mt-0.5 opacity-70">Gekauft ist schneller</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setStack('custom')}
|
||||
className={`w-full text-left p-4 rounded-xl border-2 transition-all ${stack === 'custom' ? 'border-emerald-500 bg-white shadow-sm' : 'border-transparent hover:bg-slate-100'}`}
|
||||
className={`flex-1 p-4 flex flex-col items-center justify-center gap-2 transition-all ${stack === 'custom' ? 'bg-white border-b-2 border-emerald-500' : 'bg-slate-50 text-slate-400 hover:bg-slate-100'}`}
|
||||
>
|
||||
<div className="font-bold text-slate-900 text-sm">🏰 System Bauen (Custom)</div>
|
||||
<div className="text-xs text-slate-500 mt-1">Eigenes IP, Headless, Skalierbar</div>
|
||||
<div className={`p-2 rounded-full ${stack === 'custom' ? 'bg-emerald-100 text-emerald-600' : 'bg-slate-200'}`}>
|
||||
<Rocket size={20} />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className={`font-bold text-sm ${stack === 'custom' ? 'text-slate-900' : ''}`}>Build-First / Headless</div>
|
||||
<div className="text-[10px] uppercase tracking-wider mt-0.5 opacity-70">Architektur als Asset</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Visualization Panel */}
|
||||
<div className="w-full md:w-2/3 p-6 md:p-8 bg-white min-h-[400px] flex flex-col justify-center">
|
||||
{/* Chart Area */}
|
||||
<div className="p-6 md:p-8 bg-white">
|
||||
<div className="h-[350px] w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<ComposedChart data={archData} margin={{ top: 20, right: 30, left: 20, bottom: 0 }}>
|
||||
<defs>
|
||||
<linearGradient id="barSaas" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#fb923c" stopOpacity={0.8} />
|
||||
<stop offset="100%" stopColor="#fb923c" stopOpacity={0.3} />
|
||||
</linearGradient>
|
||||
<linearGradient id="barCustom" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="#94a3b8" stopOpacity={0.8} />
|
||||
<stop offset="100%" stopColor="#94a3b8" stopOpacity={0.3} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{stack === 'saas' ? (
|
||||
<div className="animate-in fade-in slide-in-from-right-4 duration-500">
|
||||
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||
<div className="p-4 rounded-xl border border-orange-200 bg-orange-50/50 relative overflow-hidden">
|
||||
<div className="text-3xl mb-2">📦</div>
|
||||
<div className="font-bold text-sm text-orange-900">Standard-Features</div>
|
||||
<div className="text-xs text-orange-700/70 mt-1 hidden sm:block">Sie zahlen auch für 80%, die Sie nicht nutzen.</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl border border-red-200 bg-red-50/50 relative overflow-hidden">
|
||||
<div className="absolute top-2 right-2 flex gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-red-400 animate-pulse" />
|
||||
</div>
|
||||
<div className="text-3xl mb-2">🔌</div>
|
||||
<div className="font-bold text-sm text-red-900">Plugin-Chaos</div>
|
||||
<div className="text-xs text-red-700/70 mt-1 hidden sm:block">Jedes Update ist ein Sicherheitsrisiko.</div>
|
||||
</div>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
||||
|
||||
<XAxis
|
||||
dataKey="year"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#64748b', fontSize: 12, fontWeight: 500 }}
|
||||
dy={10}
|
||||
/>
|
||||
|
||||
{/* Left Y-Axis: Cost */}
|
||||
<YAxis
|
||||
yAxisId="left"
|
||||
tickFormatter={(val) => `€${val}k`}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#94a3b8', fontSize: 12 }}
|
||||
dx={-10}
|
||||
/>
|
||||
|
||||
{/* Right Y-Axis: Independence */}
|
||||
<YAxis
|
||||
yAxisId="right"
|
||||
orientation="right"
|
||||
tickFormatter={(val) => `${val}%`}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#14b8a6', fontSize: 12, fontWeight: 'bold' }}
|
||||
dx={10}
|
||||
domain={[0, 100]}
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
contentStyle={{ borderRadius: '12px', border: '1px solid #e2e8f0', boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
formatter={(value: number, name: string) => {
|
||||
if (name === 'saasInvest') return [`€${value}.000`, 'Kumuliertes Investment (Miete + Addons)'];
|
||||
if (name === 'customInvest') return [`€${value}.000`, 'Kumuliertes Investment (Bau + Betrieb)'];
|
||||
if (name === 'saasIndep') return [`${value}%`, 'Technische Unabhängigkeit (Lock-in sinkt)'];
|
||||
if (name === 'customIndep') return [`${value}%`, 'Technische Unabhängigkeit & Datenhoheit'];
|
||||
return [value, name];
|
||||
}}
|
||||
labelStyle={{ fontWeight: 'bold', color: '#0f172a', marginBottom: '8px' }}
|
||||
/>
|
||||
|
||||
{stack === 'saas' ? (
|
||||
<>
|
||||
<Bar yAxisId="left" dataKey="saasInvest" fill="#fb923c" radius={[4, 4, 0, 0]} barSize={40} animationDuration={1000} />
|
||||
<Line yAxisId="right" type="monotone" dataKey="saasIndep" stroke="#ef4444" strokeWidth={4} dot={{ r: 6, fill: "#ef4444", strokeWidth: 2, stroke: "#fff" }} animationDuration={1500} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Bar yAxisId="left" dataKey="customInvest" fill="#94a3b8" radius={[4, 4, 0, 0]} barSize={40} animationDuration={1000} />
|
||||
<Line yAxisId="right" type="monotone" dataKey="customIndep" stroke="#10b981" strokeWidth={4} dot={{ r: 6, fill: "#10b981", strokeWidth: 2, stroke: "#fff" }} animationDuration={1500} />
|
||||
</>
|
||||
)}
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Impact Summary */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-8">
|
||||
<div className={`p-4 rounded-xl border flex gap-4 ${stack === 'saas' ? 'bg-orange-50 border-orange-100' : 'bg-emerald-50 border-emerald-100'}`}>
|
||||
<div className={`mt-1 rounded-full p-2 h-fit ${stack === 'saas' ? 'bg-orange-100 text-orange-600' : 'bg-emerald-100 text-emerald-600'}`}>
|
||||
{stack === 'saas' ? <ShieldAlert size={20} /> : <Database size={20} />}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-red-100 text-red-600 flex items-center justify-center shrink-0 border border-red-200"><X size={12} strokeWidth={3} /></div>
|
||||
<span>Abhängigkeit von Preiserhöhungen</span>
|
||||
<div>
|
||||
<div className={`font-bold text-sm ${stack === 'saas' ? 'text-orange-900' : 'text-emerald-900'}`}>
|
||||
{stack === 'saas' ? 'Massiver Platform Lock-in' : '100% Datenhoheit'}
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-red-100 text-red-600 flex items-center justify-center shrink-0 border border-red-200"><X size={12} strokeWidth={3} /></div>
|
||||
<span>Kein Aufbau von eigenem Firmenwert (IP)</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-orange-100 text-orange-600 flex items-center justify-center shrink-0 border border-orange-200"><Zap size={12} /></div>
|
||||
<span>Träge Ladezeiten durch geteilte Server</span>
|
||||
<div className="text-xs mt-1 text-slate-600 leading-relaxed">
|
||||
{stack === 'saas'
|
||||
? 'Ihre Daten und Prozesse liegen bei Dritten. Umzüge werden nach Jahr 2 exponentiell teuers und riskant.'
|
||||
: 'Sie besitzen die Kern-Datenbanken und APIs. Keine Lizenzschocks, keine Blackbox-Updates.'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="animate-in fade-in slide-in-from-right-4 duration-500">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-8">
|
||||
<div className="p-4 rounded-xl border border-emerald-200 bg-emerald-50/50 relative">
|
||||
<div className="text-emerald-500 mb-2"><Zap size={24} /></div>
|
||||
<div className="font-bold text-sm text-emerald-900">Edge Content</div>
|
||||
<div className="text-[10px] text-emerald-700/70 mt-1">Instante globale Ladezeiten.</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl border border-blue-200 bg-blue-50/50 relative">
|
||||
<div className="text-blue-500 mb-2"><Database size={24} /></div>
|
||||
<div className="font-bold text-sm text-blue-900">Headless API</div>
|
||||
<div className="text-[10px] text-blue-700/70 mt-1">Daten systemunabhängig.</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl border border-slate-200 bg-slate-50 relative">
|
||||
<div className="text-slate-500 mb-2"><Expand size={24} /></div>
|
||||
<div className="font-bold text-sm text-slate-900">Custom UI</div>
|
||||
<div className="text-[10px] text-slate-500 mt-1">100% Passform für Sie.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-emerald-100 text-emerald-600 flex items-center justify-center shrink-0 border border-emerald-200"><Check size={12} strokeWidth={3} /></div>
|
||||
<span>0€ monatliche Fixkosten für Lizenzen</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-emerald-100 text-emerald-600 flex items-center justify-center shrink-0 border border-emerald-200"><Check size={12} strokeWidth={3} /></div>
|
||||
<span>Sie besitzen den Code = Echter Firmenwertaufbau</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm text-slate-600">
|
||||
<div className="w-6 h-6 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center shrink-0 border border-blue-200"><Shield size={12} /></div>
|
||||
<span>Maximale Security (keine Standard-Plugins als Einfallstor)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,150 +3,195 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ComponentShareButton } from '../ComponentShareButton';
|
||||
import { Reveal } from '../Reveal';
|
||||
import { TrendingUp, Coins, PiggyBank, Briefcase } from 'lucide-react';
|
||||
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { Briefcase, Coins, PiggyBank, TrendingUp } from 'lucide-react';
|
||||
|
||||
const data = [
|
||||
{ year: 'Jahr 1', saasCost: 40000, saasAsset: 0, customCost: 120000, customAsset: 120000 },
|
||||
{ year: 'Jahr 2', saasCost: 85000, saasAsset: 0, customCost: 140000, customAsset: 110000 },
|
||||
{ year: 'Jahr 3', saasCost: 135000, saasAsset: 0, customCost: 160000, customAsset: 100000 },
|
||||
{ year: 'Jahr 4', saasCost: 190000, saasAsset: 0, customCost: 180000, customAsset: 90000 },
|
||||
{ year: 'Jahr 5', saasCost: 250000, saasAsset: 0, customCost: 200000, customAsset: 85000 },
|
||||
];
|
||||
|
||||
const formatCurrency = (val: number) => new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(val);
|
||||
|
||||
export function DigitalAssetVisualizer({ className = '' }: { className?: string }) {
|
||||
const [view, setView] = useState<'liability' | 'asset'>('asset');
|
||||
const [view, setView] = useState<'saas' | 'custom'>('custom');
|
||||
|
||||
return (
|
||||
<Reveal direction="up" delay={0.1}>
|
||||
<div className={`not-prose max-w-4xl mx-auto my-12 relative group ${className}`}>
|
||||
<div className={`absolute -inset-1 blur opacity-30 rounded-3xl transition-all duration-700 ${view === 'liability' ? 'bg-gradient-to-r from-red-100 to-orange-100' : 'bg-gradient-to-r from-indigo-100 to-emerald-100'}`} />
|
||||
<div className={`absolute -inset-1 blur opacity-30 rounded-3xl transition-all duration-700 ${view === 'saas' ? 'bg-gradient-to-r from-red-200 to-orange-100' : 'bg-gradient-to-r from-indigo-200 to-emerald-100'}`} />
|
||||
|
||||
<div id="sim-digital-asset" className="relative bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col">
|
||||
<div id="sim-digital-asset" className="relative bg-white rounded-2xl border border-slate-200 shadow-xl shadow-slate-200/50 overflow-hidden flex flex-col">
|
||||
<div data-share-wrapper="true" className="absolute top-4 right-4 md:opacity-0 group-hover:opacity-100 transition-opacity z-50">
|
||||
<ComponentShareButton
|
||||
targetId="sim-digital-asset"
|
||||
title="Digitales Asset vs. Verbindlichkeit"
|
||||
shareText="Verstehen Sie den Unterschied zwischen Software-Miete und echtem digitalen Eigentum:"
|
||||
title="Software: Ausgabe oder Investment?"
|
||||
shareText="Sehen Sie sich den finanziellen Verlauf von Software-Miete (SaaS) gegenüber einer Inhouse-Eigenentwicklung an:"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-6 border-b border-slate-200 bg-slate-50">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-2xl">💎</span>
|
||||
<h3 className="font-bold text-slate-900 m-0">Software: Ausgabe oder Investment?</h3>
|
||||
<div className="p-6 md:p-8 border-b border-slate-200 bg-slate-50 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-slate-200 rounded-full blur-3xl -mr-32 -mt-32 opacity-50" />
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-2xl">💰</span>
|
||||
<h3 className="font-bold text-slate-900 m-0 text-xl">Software: Ausgabe oder Investment?</h3>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 m-0 leading-relaxed max-w-2xl">
|
||||
Kumulierte Kosten im Vergleich zum bilanzierbaren Unternehmenswert über 5 Jahre.
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 m-0 leading-relaxed max-w-2xl">
|
||||
Visualisieren Sie den Unterschied zwischen Software, die monatlich Geld verbrennt (SaaS-Miete), und Software, die als Firmenwert bilanziert werden kann (Eigenbau).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row">
|
||||
{/* Control Panel */}
|
||||
<div className="w-full md:w-1/3 p-6 bg-slate-50 md:border-r border-b md:border-b-0 border-slate-100 space-y-4">
|
||||
<label className="text-[10px] font-bold text-slate-400 uppercase tracking-widest block mb-4">Wirtschaftliche Sicht</label>
|
||||
|
||||
<div className="flex flex-col">
|
||||
{/* Control Tabs */}
|
||||
<div className="flex border-b border-slate-100">
|
||||
<button
|
||||
onClick={() => setView('liability')}
|
||||
className={`w-full flex items-center gap-3 text-left p-4 rounded-xl border-2 transition-all ${view === 'liability' ? 'border-red-400 bg-white shadow-sm' : 'border-transparent hover:bg-slate-100'}`}
|
||||
onClick={() => setView('saas')}
|
||||
className={`flex-1 p-4 flex flex-col items-center justify-center gap-2 transition-all ${view === 'saas' ? 'bg-white border-b-2 border-red-500' : 'bg-slate-50 text-slate-400 hover:bg-slate-100'}`}
|
||||
>
|
||||
<div className={`p-2 rounded-lg ${view === 'liability' ? 'bg-red-100 text-red-600' : 'bg-slate-200 text-slate-500'}`}>
|
||||
<Coins size={18} />
|
||||
<div className={`p-2 rounded-full ${view === 'saas' ? 'bg-red-100 text-red-600' : 'bg-slate-200'}`}>
|
||||
<Coins size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold text-slate-900 text-sm">Verbindlichkeit (SaaS)</div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Laufende Kosten, 0% Eigentum</div>
|
||||
<div className="text-center">
|
||||
<div className={`font-bold text-sm ${view === 'saas' ? 'text-slate-900' : ''}`}>SaaS-Miete (Verbindlichkeit)</div>
|
||||
<div className="text-[10px] uppercase tracking-wider mt-0.5 opacity-70">100% OpEx</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setView('asset')}
|
||||
className={`w-full flex items-center gap-3 text-left p-4 rounded-xl border-2 transition-all ${view === 'asset' ? 'border-indigo-400 bg-white shadow-sm' : 'border-transparent hover:bg-slate-100'}`}
|
||||
onClick={() => setView('custom')}
|
||||
className={`flex-1 p-4 flex flex-col items-center justify-center gap-2 transition-all ${view === 'custom' ? 'bg-white border-b-2 border-indigo-500' : 'bg-slate-50 text-slate-400 hover:bg-slate-100'}`}
|
||||
>
|
||||
<div className={`p-2 rounded-lg ${view === 'asset' ? 'bg-indigo-100 text-indigo-600' : 'bg-slate-200 text-slate-500'}`}>
|
||||
<Briefcase size={18} />
|
||||
<div className={`p-2 rounded-full ${view === 'custom' ? 'bg-indigo-100 text-indigo-600' : 'bg-slate-200'}`}>
|
||||
<Briefcase size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold text-slate-900 text-sm">Digitales Asset (Custom)</div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Firmenwertaufbau, 100% IP</div>
|
||||
<div className="text-center">
|
||||
<div className={`font-bold text-sm ${view === 'custom' ? 'text-slate-900' : ''}`}>Eigenentwicklung (Asset)</div>
|
||||
<div className="text-[10px] uppercase tracking-wider mt-0.5 opacity-70">CapEx + IP-Aufbau</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Visualization Panel */}
|
||||
<div className="w-full md:w-2/3 p-6 md:p-8 bg-white min-h-[350px] flex flex-col justify-center">
|
||||
{/* Chart Area */}
|
||||
<div className="p-6 md:p-8 bg-white">
|
||||
<div className="h-[350px] w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 0 }}>
|
||||
<defs>
|
||||
<linearGradient id="colorCostSaas" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#ef4444" stopOpacity={0.15} />
|
||||
<stop offset="95%" stopColor="#ef4444" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
<linearGradient id="colorCostCustom" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#94a3b8" stopOpacity={0.15} />
|
||||
<stop offset="95%" stopColor="#94a3b8" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
<linearGradient id="colorAsset" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#4f46e5" stopOpacity={0.2} />
|
||||
<stop offset="95%" stopColor="#4f46e5" stopOpacity={0.02} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
||||
<XAxis
|
||||
dataKey="year"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#64748b', fontSize: 12, fontWeight: 500 }}
|
||||
dy={10}
|
||||
/>
|
||||
<YAxis
|
||||
tickFormatter={(val) => `€${val / 1000}k`}
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: '#64748b', fontSize: 12 }}
|
||||
dx={-10}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{ borderRadius: '8px', border: '1px solid #f1f5f9', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)', padding: '6px 10px', fontSize: '11px', backgroundColor: 'rgba(255, 255, 255, 0.95)', backdropFilter: 'blur(4px)' }}
|
||||
itemStyle={{ fontSize: '11px', padding: '2px 0' }}
|
||||
formatter={(value: number, name: string) => {
|
||||
if (name === 'saasCost') return [formatCurrency(value), 'Kumulierte Kosten (Miete)'];
|
||||
if (name === 'customCost') return [formatCurrency(value), 'Kumulierte Kosten (Bau + Wartung)'];
|
||||
if (name === 'customAsset') return [formatCurrency(value), 'Firmenwert (Digitales Eigentum)'];
|
||||
if (name === 'saasAsset') return [formatCurrency(value), 'Firmenwert (IP)'];
|
||||
return [value, name];
|
||||
}}
|
||||
labelStyle={{ fontWeight: 'bold', color: '#0f172a', marginBottom: '4px', fontSize: '11px' }}
|
||||
/>
|
||||
|
||||
{view === 'liability' ? (
|
||||
<div className="animate-in fade-in slide-in-from-right-4 duration-500">
|
||||
<div className="flex flex-col items-center justify-center space-y-6 mb-8 mt-4">
|
||||
{view === 'saas' ? (
|
||||
<>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="saasCost"
|
||||
stroke="#ef4444"
|
||||
strokeWidth={3}
|
||||
fillOpacity={1}
|
||||
fill="url(#colorCostSaas)"
|
||||
animationDuration={1500}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="saasAsset"
|
||||
stroke="#cbd5e1"
|
||||
strokeWidth={2}
|
||||
fillOpacity={0}
|
||||
animationDuration={1500}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="customAsset"
|
||||
stroke="#4f46e5"
|
||||
strokeWidth={3}
|
||||
fillOpacity={1}
|
||||
fill="url(#colorAsset)"
|
||||
animationDuration={1500}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="customCost"
|
||||
stroke="#64748b"
|
||||
strokeWidth={2}
|
||||
strokeDasharray="5 5"
|
||||
fillOpacity={1}
|
||||
fill="url(#colorCostCustom)"
|
||||
animationDuration={1500}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
{/* Money Burn Animation */}
|
||||
<div className="relative w-32 h-32 flex items-center justify-center">
|
||||
{/* Fire background blob */}
|
||||
<div className="absolute inset-4 bg-orange-400 rounded-full blur-xl opacity-20 animate-pulse" />
|
||||
|
||||
{/* Money flying away */}
|
||||
<div className="absolute top-0 right-0 animate-bounce delay-75 text-red-500 opacity-50">-€</div>
|
||||
<div className="absolute top-4 left-4 animate-bounce delay-150 text-red-500 opacity-40">-€</div>
|
||||
<div className="absolute bottom-8 right-8 animate-bounce delay-300 text-red-500 opacity-60">-€</div>
|
||||
|
||||
<div className="w-20 h-20 bg-red-50 border border-red-200 text-red-500 rounded-full flex items-center justify-center text-3xl shadow-sm z-10">
|
||||
💸
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-sm font-bold text-red-900">Der ewige Geldabfluss</div>
|
||||
<p className="text-xs text-red-700 mt-2 max-w-[250px] mx-auto opacity-80">
|
||||
Jeden Monat fließt Kapital an externe Software-Anbieter ab. Kündigen Sie, ist Ihr System sofort offline.
|
||||
</p>
|
||||
</div>
|
||||
{/* Summary Metrics */}
|
||||
<div className="grid grid-cols-2 gap-4 mt-8">
|
||||
<div className={`p-4 rounded-xl border ${view === 'saas' ? 'bg-red-50 border-red-100' : 'bg-slate-50 border-slate-200'}`}>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">
|
||||
Total Spend (Jahr 5)
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 rounded-xl p-4 border border-slate-200 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">Unternehmenswert</div>
|
||||
<div className="font-bold text-slate-900">0,00 €</div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Software gehört Anbieter</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">Skalierungs-Kosten</div>
|
||||
<div className="font-bold text-red-600 flex items-center gap-1">Linear <TrendingUp size={12} /></div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Mehr User = Mehr Kosten</div>
|
||||
</div>
|
||||
<div className={`text-2xl font-bold flex items-center gap-2 ${view === 'saas' ? 'text-red-600' : 'text-slate-700'}`}>
|
||||
{view === 'saas' ? '250.000 €' : '200.000 €'}
|
||||
{view === 'saas' && <TrendingUp size={16} className="text-red-500" />}
|
||||
</div>
|
||||
<div className="text-xs text-slate-500 mt-1">Sunk Costs (Geldabfluss)</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="animate-in fade-in slide-in-from-right-4 duration-500">
|
||||
<div className="flex flex-col items-center justify-center space-y-6 mb-8 mt-4">
|
||||
|
||||
{/* Asset Building Animation */}
|
||||
<div className="relative w-32 h-32 flex items-center justify-center">
|
||||
{/* Glow background blob */}
|
||||
<div className="absolute inset-4 bg-indigo-400 rounded-full blur-xl opacity-20" />
|
||||
|
||||
{/* Value accumulating */}
|
||||
<div className="absolute -top-2 left-1/2 -ml-3 animate-pulse text-indigo-500 text-xl font-bold">+</div>
|
||||
|
||||
<div className="w-20 h-20 bg-indigo-50 border border-indigo-200 text-indigo-600 rounded-2xl flex items-center justify-center shadow-sm z-10 rotate-3 transition-transform hover:rotate-6 hover:scale-105">
|
||||
<PiggyBank size={32} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="text-sm font-bold text-indigo-900">Das aktivierbare Asset</div>
|
||||
<p className="text-xs text-indigo-700 mt-2 max-w-[280px] mx-auto opacity-80">
|
||||
Maßgeschneiderte Software gehört zu 100% Ihnen. Das Intellectual Property (IP) mehrt den Wert Ihrer GmbH.
|
||||
</p>
|
||||
</div>
|
||||
<div className={`p-4 rounded-xl border ${view === 'saas' ? 'bg-slate-50 border-slate-200' : 'bg-indigo-50 border-indigo-100'}`}>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">
|
||||
Bilanzierbarer Wert (IP)
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-50 rounded-xl p-4 border border-slate-200 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">Unternehmenswert</div>
|
||||
<div className="font-bold text-emerald-600 flex items-center gap-1">Steigt aktiv <TrendingUp size={12} /></div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Kann bilanziert werden</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1">Skalierungs-Kosten</div>
|
||||
<div className="font-bold text-slate-900">Flach (0€ Lizenzen)</div>
|
||||
<div className="text-[10px] text-slate-500 mt-0.5">Nur reine Serverkosten</div>
|
||||
</div>
|
||||
<div className={`text-2xl font-bold flex items-center gap-2 ${view === 'saas' ? 'text-slate-400' : 'text-indigo-600'}`}>
|
||||
{view === 'saas' ? '0 €' : '85.000 €'}
|
||||
{view === 'custom' && <PiggyBank size={18} className="text-indigo-500" />}
|
||||
</div>
|
||||
<div className="text-xs text-slate-500 mt-1">Erhöht den Unternehmenswert</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user