feat: KPIs + Grafiken Reiter im Finanzplan + ROI korrigiert

KPIs Tab: 15 Kennzahlen pro Jahr (2026-2030)
  MRR, ARR, Kunden, ARPU, Mitarbeiter, Umsatz/MA, Personalkosten,
  EBIT, EBIT-Marge, Steuern, Jahresüberschuss, Serverkosten/Kunde,
  Bruttomarge, Burn Rate, Runway

Grafiken Tab:
  - MRR & Kundenentwicklung (Balkendiagramm, 5 Jahre)
  - EBIT (Rot/Grün je nach Verlust/Gewinn)
  - Personalaufbau (Balkendiagramm 5→35)

ROI korrigiert (Ersparnis ÷ Preis):
  KMU: 3,5x, Mittelstand: 7,5x, Konzern: 20,8x

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-28 23:17:31 +01:00
parent 159d07efd5
commit 1b5c2a156c
2 changed files with 185 additions and 8 deletions

View File

@@ -6,7 +6,7 @@ import { t } from '@/lib/i18n'
import GradientText from '../ui/GradientText'
import FadeInView from '../ui/FadeInView'
import GlassCard from '../ui/GlassCard'
import { RefreshCw, Download, ChevronLeft, ChevronRight } from 'lucide-react'
import { RefreshCw, Download, ChevronLeft, ChevronRight, BarChart3, Target } from 'lucide-react'
interface FinanzplanSlideProps {
lang: Language
@@ -140,6 +140,25 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
{s.rows > 0 && <span className="ml-1 text-[8px] opacity-50">({s.rows})</span>}
</button>
))}
{/* KPIs + Grafiken Tabs */}
<span className="text-white/10 mx-1">|</span>
{[
{ id: 'kpis', label: 'KPIs', icon: Target },
{ id: 'charts', label: de ? 'Grafiken' : 'Charts', icon: BarChart3 },
].map(tab => (
<button
key={tab.id}
onClick={() => setActiveSheet(tab.id)}
className={`px-3 py-1.5 text-[10px] font-medium rounded-lg whitespace-nowrap transition-colors flex items-center gap-1 ${
activeSheet === tab.id
? 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/30'
: 'text-white/40 hover:text-white/70 hover:bg-white/[0.05]'
}`}
>
<tab.icon className="w-3 h-3" />
{tab.label}
</button>
))}
<div className="flex-1" />
<button
onClick={handleCompute}
@@ -151,8 +170,164 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
</button>
</div>
{/* Year Navigation — not for GuV (annual view) */}
{activeSheet !== 'guv' && (
{/* === KPIs Tab === */}
{activeSheet === 'kpis' && (
<GlassCard hover={false} className="p-4">
<h3 className="text-sm font-bold text-emerald-400 uppercase tracking-wider mb-4">{de ? 'Wichtige Kennzahlen (pro Jahr)' : 'Key Metrics (per year)'}</h3>
<table className="w-full text-xs">
<thead>
<tr className="border-b border-white/10">
<th className="text-left py-2 px-2 text-white/60 font-medium">KPI</th>
{[2026, 2027, 2028, 2029, 2030].map(y => (
<th key={y} className="text-right py-2 px-3 text-white/60 font-medium">{y}</th>
))}
</tr>
</thead>
<tbody>
{(() => {
// Compute KPIs from loaded data — we need liquidität and umsatz data
// These are approximate since we don't have all sheets loaded simultaneously
const kpiRows = [
{ label: 'MRR (Dez)', values: [6100, 84450, 267950, 517650, 834750], unit: '€', bold: true },
{ label: 'ARR', values: [73200, 1013400, 3215400, 6211800, 10017000], unit: '€', bold: true },
{ label: de ? 'Kunden (Dez)' : 'Customers (Dec)', values: [14, 117, 370, 726, 1200], unit: '', bold: false },
{ label: 'ARPU (MRR/Kunden)', values: [436, 722, 724, 713, 696], unit: '€', bold: false },
{ label: de ? 'Mitarbeiter' : 'Employees', values: [5, 10, 17, 25, 35], unit: '', bold: false },
{ label: de ? 'Umsatz/Mitarbeiter' : 'Revenue/Employee', values: [14640, 101340, 189141, 248472, 286200], unit: '€', bold: false },
{ label: de ? 'Personalkosten' : 'Personnel Costs', values: [58768, 740968, 1353764, 2154301, 3129479], unit: '€', bold: false },
{ label: 'EBIT', values: [-95099, -566293, -4019, 1315689, 3144137], unit: '€', bold: true },
{ label: de ? 'EBIT-Marge' : 'EBIT Margin', values: [-130, -56, -1, 21, 31], unit: '%', bold: false },
{ label: de ? 'Steuern' : 'Taxes', values: [0, 0, 0, 182565, 882717], unit: '€', bold: false },
{ label: de ? 'Jahresüberschuss' : 'Net Income', values: [-95099, -566293, -4019, 1133124, 2261420], unit: '€', bold: true },
{ label: de ? 'Serverkosten/Kunde' : 'Server Cost/Customer', values: [100, 100, 100, 100, 100], unit: '€', bold: false },
{ label: de ? 'Bruttomarge' : 'Gross Margin', values: [100, 100, 92, 90, 88], unit: '%', bold: false },
{ label: 'Burn Rate (Dez)', values: [44734, 28364, 0, 0, 0], unit: '€/Mo', bold: false },
{ label: de ? 'Runway (Monate)' : 'Runway (months)', values: [19, 4, '∞', '∞', '∞'], unit: '', bold: false },
]
return kpiRows.map((row, idx) => (
<tr key={idx} className={`border-b border-white/[0.03] ${row.bold ? 'bg-white/[0.03]' : ''}`}>
<td className={`py-1.5 px-2 ${row.bold ? 'font-bold text-white/80' : 'text-white/60'}`}>{row.label}</td>
{row.values.map((v, i) => {
const num = typeof v === 'number' ? v : 0
const display = typeof v === 'string' ? v : (
row.unit === '%' ? `${v}%` :
row.unit === '€/Mo' ? formatCell(num) + '/Mo' :
formatCell(num)
)
return (
<td key={i} className={`text-right py-1.5 px-3 font-mono ${num < 0 ? 'text-red-400' : row.bold ? 'text-white/80 font-bold' : 'text-white/50'}`}>
{display}
</td>
)
})}
</tr>
))
})()}
</tbody>
</table>
</GlassCard>
)}
{/* === Charts Tab === */}
{activeSheet === 'charts' && (
<div className="space-y-4">
{/* MRR + Kunden Chart */}
<GlassCard hover={false} className="p-4">
<h3 className="text-xs font-bold text-indigo-400 uppercase tracking-wider mb-3">{de ? 'MRR & Kundenentwicklung' : 'MRR & Customer Growth'}</h3>
<div className="grid grid-cols-5 gap-1 items-end h-48">
{[
{ year: '2026', mrr: 6100, cust: 14, max_mrr: 834750, max_cust: 1200 },
{ year: '2027', mrr: 84450, cust: 117, max_mrr: 834750, max_cust: 1200 },
{ year: '2028', mrr: 267950, cust: 370, max_mrr: 834750, max_cust: 1200 },
{ year: '2029', mrr: 517650, cust: 726, max_mrr: 834750, max_cust: 1200 },
{ year: '2030', mrr: 834750, cust: 1200, max_mrr: 834750, max_cust: 1200 },
].map((d, idx) => (
<div key={idx} className="flex flex-col items-center gap-1">
<div className="flex items-end gap-1 w-full justify-center" style={{ height: '160px' }}>
{/* MRR bar */}
<div className="w-8 bg-indigo-500/60 rounded-t transition-all" style={{ height: `${(d.mrr / d.max_mrr) * 150}px` }}>
<div className="text-[7px] text-white/70 text-center -mt-3 whitespace-nowrap">
{d.mrr >= 100000 ? `${Math.round(d.mrr/1000)}k` : d.mrr.toLocaleString('de-DE')}
</div>
</div>
{/* Kunden bar */}
<div className="w-8 bg-emerald-500/60 rounded-t transition-all" style={{ height: `${(d.cust / d.max_cust) * 150}px` }}>
<div className="text-[7px] text-white/70 text-center -mt-3">{d.cust}</div>
</div>
</div>
<span className="text-[10px] text-white/40">{d.year}</span>
</div>
))}
</div>
<div className="flex justify-center gap-6 mt-2 text-[10px]">
<span className="flex items-center gap-1"><span className="w-3 h-2 bg-indigo-500/60 rounded inline-block" /> MRR ()</span>
<span className="flex items-center gap-1"><span className="w-3 h-2 bg-emerald-500/60 rounded inline-block" /> {de ? 'Kunden' : 'Customers'}</span>
</div>
</GlassCard>
{/* EBIT + Cash Chart */}
<div className="grid md:grid-cols-2 gap-4">
<GlassCard hover={false} className="p-4">
<h3 className="text-xs font-bold text-purple-400 uppercase tracking-wider mb-3">EBIT</h3>
<div className="grid grid-cols-5 gap-1 items-end h-36">
{[
{ year: '2026', val: -95099 },
{ year: '2027', val: -566293 },
{ year: '2028', val: -4019 },
{ year: '2029', val: 1315689 },
{ year: '2030', val: 3144137 },
].map((d, idx) => {
const maxAbs = 3144137
const h = Math.abs(d.val) / maxAbs * 100
return (
<div key={idx} className="flex flex-col items-center">
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
{d.val >= 0 ? (
<div className="bg-emerald-500/60 rounded-t w-full" style={{ height: `${h}px` }}>
<div className="text-[7px] text-emerald-300 text-center -mt-3 whitespace-nowrap">{Math.round(d.val/1000)}k</div>
</div>
) : (
<div className="flex flex-col justify-end h-full">
<div className="bg-red-500/60 rounded-b w-full" style={{ height: `${h}px` }}>
<div className="text-[7px] text-red-300 text-center mt-1 whitespace-nowrap">{Math.round(d.val/1000)}k</div>
</div>
</div>
)}
</div>
<span className="text-[10px] text-white/40 mt-1">{d.year}</span>
</div>
)
})}
</div>
</GlassCard>
<GlassCard hover={false} className="p-4">
<h3 className="text-xs font-bold text-amber-400 uppercase tracking-wider mb-3">{de ? 'Personalaufbau' : 'Headcount'}</h3>
<div className="grid grid-cols-5 gap-1 items-end h-36">
{[
{ year: '2026', val: 5 },
{ year: '2027', val: 10 },
{ year: '2028', val: 17 },
{ year: '2029', val: 25 },
{ year: '2030', val: 35 },
].map((d, idx) => (
<div key={idx} className="flex flex-col items-center">
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
<div className="bg-amber-500/60 rounded-t w-full" style={{ height: `${(d.val / 35) * 100}px` }}>
<div className="text-[8px] text-amber-300 text-center -mt-3 font-bold">{d.val}</div>
</div>
</div>
<span className="text-[10px] text-white/40 mt-1">{d.year}</span>
</div>
))}
</div>
</GlassCard>
</div>
</div>
)}
{/* Year Navigation — not for GuV, KPIs, Charts (annual views) */}
{!['guv', 'kpis', 'charts'].includes(activeSheet) && (
<div className="flex items-center justify-center gap-4 mb-2">
<button onClick={() => setYearOffset(Math.max(0, yearOffset - 1))} disabled={yearOffset === 0}
className="p-1 text-white/40 hover:text-white/70 disabled:opacity-20">
@@ -166,7 +341,8 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
</div>
)}
{/* Data Grid */}
{/* Data Grid — not shown for KPIs and Charts */}
{!['kpis', 'charts'].includes(activeSheet) && (
<GlassCard hover={false} className="p-2 overflow-x-auto">
{loading ? (
<div className="text-center py-8 text-white/30 text-sm">{de ? 'Lade...' : 'Loading...'}</div>
@@ -330,10 +506,11 @@ export default function FinanzplanSlide({ lang }: FinanzplanSlideProps) {
</table>
)}
</GlassCard>
)}
<p className="text-center text-[9px] text-white/40 mt-2">
{de
? 'Doppelklick auf blaue Zellen zum Bearbeiten · Gruendung: 01.08.2026'
? 'Doppelklick auf blaue Zellen zum Bearbeiten · Gründung: 01.08.2026'
: 'Double-click blue cells to edit · Founding: 01.08.2026'}
</p>
</div>

View File

@@ -32,7 +32,7 @@ export default function SavingsSlide({ lang }: SavingsSlideProps) {
totalWithout: '97.750',
totalWith: '44.530',
totalSave: '53.220',
roi: '9,1x',
roi: '3,5x',
},
{
icon: Factory,
@@ -52,7 +52,7 @@ export default function SavingsSlide({ lang }: SavingsSlideProps) {
totalWithout: '419.500',
totalWith: '193.880',
totalSave: '225.620',
roi: '12,6x',
roi: '7,5x',
},
{
icon: Building,
@@ -72,7 +72,7 @@ export default function SavingsSlide({ lang }: SavingsSlideProps) {
totalWithout: '2.113.500',
totalWith: '1.074.080',
totalSave: '1.039.420',
roi: '17,4x',
roi: '20,8x',
},
]