This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/pitch-deck/components/ui/AnnualCashflowChart.tsx
BreakPilot Dev b464366341
Some checks failed
ci/woodpecker/push/integration Pipeline failed
ci/woodpecker/push/main Pipeline failed
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
feat: Add staged funding model, financial compute engine, annex slides and UI enhancements
Restructure financial plan from single 200k SAFE to realistic staged funding
(25k Stammkapital, 25k Angel, 200k Wandeldarlehen, 1M Series A = 1.25M total).
Add 60-month compute engine with CAPEX/OPEX accounting, cash constraints,
hardware financing (30% upfront / 70% leasing), and revenue-based hiring caps.
Rebuild TheAskSlide with 4-event funding timeline, update i18n (DE/EN),
chat agent core messages, and add 15 new annex/technology slides with
supporting UI components (KPICard, RunwayGauge, WaterfallChart, etc.).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 21:20:02 +01:00

206 lines
7.3 KiB
TypeScript

'use client'
import { FMResult } from '@/lib/types'
import { AccountingStandard } from './AnnualPLTable'
import {
Bar,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
ReferenceLine,
Line,
ComposedChart,
Cell,
} from 'recharts'
interface AnnualCashflowChartProps {
results: FMResult[]
initialFunding: number
lang: 'de' | 'en'
standard?: AccountingStandard
}
interface AnnualCFRow {
year: string
revenue: number
costs: number
netCashflow: number
cashBalance: number
cumulativeFundingNeed: number
// HGB specific
operatingCF?: number
investingCF?: number
financingCF?: number
}
export default function AnnualCashflowChart({ results, initialFunding, lang, standard = 'hgb' }: AnnualCashflowChartProps) {
const de = lang === 'de'
// Aggregate into yearly
const yearMap = new Map<number, FMResult[]>()
for (const r of results) {
if (!yearMap.has(r.year)) yearMap.set(r.year, [])
yearMap.get(r.year)!.push(r)
}
let cumulativeNeed = 0
const data: AnnualCFRow[] = Array.from(yearMap.entries()).map(([year, months]) => {
const revenue = months.reduce((s, m) => s + m.revenue_eur, 0)
const costs = months.reduce((s, m) => s + m.total_costs_eur, 0)
const netIncome = months.reduce((s, m) => s + (m.net_income_eur || m.revenue_eur - m.total_costs_eur), 0)
const depreciation = months.reduce((s, m) => s + (m.depreciation_eur || 0), 0)
const cogs = months.reduce((s, m) => s + m.cogs_eur, 0)
const lastMonth = months[months.length - 1]
const netCF = netIncome
if (netCF < 0) cumulativeNeed += Math.abs(netCF)
// Operating CF = Net Income + Depreciation (non-cash add-back)
const operatingCF = netIncome + depreciation
// Investing CF = Hardware COGS (approximation for CapEx)
const investingCF = -cogs
// Financing CF = 0 for now (no debt/equity events modeled)
const financingCF = 0
return {
year: year.toString(),
revenue: Math.round(revenue),
costs: Math.round(costs),
netCashflow: Math.round(netCF),
cashBalance: Math.round(lastMonth.cash_balance_eur),
cumulativeFundingNeed: Math.round(cumulativeNeed),
operatingCF: Math.round(operatingCF),
investingCF: Math.round(investingCF),
financingCF: Math.round(financingCF),
}
})
const formatValue = (value: number) => {
if (Math.abs(value) >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`
if (Math.abs(value) >= 1_000) return `${(value / 1_000).toFixed(0)}k`
return value.toString()
}
// Calculate total funding needed beyond initial funding
const totalFundingGap = Math.max(0, cumulativeNeed - initialFunding)
const isUSGAAP = standard === 'usgaap'
return (
<div>
{/* Summary Cards */}
<div className="grid grid-cols-3 gap-2 mb-3">
<div className="text-center">
<p className="text-[9px] text-white/30 uppercase tracking-wider">
{de ? 'Startkapital' : 'Initial Funding'}
</p>
<p className="text-sm font-bold text-white">{formatValue(initialFunding)} EUR</p>
</div>
<div className="text-center">
<p className="text-[9px] text-white/30 uppercase tracking-wider">
{de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need'}
</p>
<p className="text-sm font-bold text-amber-400">{formatValue(cumulativeNeed)} EUR</p>
</div>
<div className="text-center">
<p className="text-[9px] text-white/30 uppercase tracking-wider">
{de ? 'Finanzierungsluecke' : 'Funding Gap'}
</p>
<p className={`text-sm font-bold ${totalFundingGap > 0 ? 'text-red-400' : 'text-emerald-400'}`}>
{totalFundingGap > 0 ? formatValue(totalFundingGap) + ' EUR' : (de ? 'Gedeckt' : 'Covered')}
</p>
</div>
</div>
{/* Chart */}
<div className="w-full h-[220px]">
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={data} margin={{ top: 10, right: 10, left: 0, bottom: 0 }}>
<XAxis
dataKey="year"
stroke="rgba(255,255,255,0.3)"
tick={{ fill: 'rgba(255,255,255,0.5)', fontSize: 11 }}
/>
<YAxis
stroke="rgba(255,255,255,0.1)"
tick={{ fill: 'rgba(255,255,255,0.4)', fontSize: 10 }}
tickFormatter={formatValue}
/>
<Tooltip
contentStyle={{
background: 'rgba(10, 10, 26, 0.95)',
border: '1px solid rgba(255,255,255,0.1)',
borderRadius: 12,
color: '#fff',
fontSize: 11,
}}
formatter={(value: number, name: string) => {
const labels: Record<string, string> = isUSGAAP
? {
netCashflow: 'Net Cash Flow',
cashBalance: 'Cash Balance',
cumulativeFundingNeed: 'Cum. Funding Need',
}
: {
netCashflow: de ? 'Netto-Cashflow' : 'Net Cash Flow',
cashBalance: de ? 'Cash-Bestand' : 'Cash Balance',
cumulativeFundingNeed: de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need',
}
return [formatValue(value) + ' EUR', labels[name] || name]
}}
/>
<ReferenceLine y={0} stroke="rgba(255,255,255,0.2)" />
{/* Net Cashflow Bars */}
<Bar dataKey="netCashflow" radius={[4, 4, 4, 4]} barSize={28}>
{data.map((entry, i) => (
<Cell
key={i}
fill={entry.netCashflow >= 0 ? 'rgba(34, 197, 94, 0.7)' : 'rgba(239, 68, 68, 0.6)'}
/>
))}
</Bar>
{/* Cash Balance Line */}
<Line
type="monotone"
dataKey="cashBalance"
stroke="#6366f1"
strokeWidth={2.5}
dot={{ r: 4, fill: '#6366f1', stroke: '#1e1b4b', strokeWidth: 2 }}
/>
{/* Cumulative Funding Need Line */}
<Line
type="monotone"
dataKey="cumulativeFundingNeed"
stroke="#f59e0b"
strokeWidth={2}
strokeDasharray="5 5"
dot={{ r: 3, fill: '#f59e0b' }}
/>
</ComposedChart>
</ResponsiveContainer>
</div>
{/* Legend */}
<div className="flex items-center justify-center gap-4 mt-2 text-[9px] text-white/40">
<span className="flex items-center gap-1">
<span className="w-3 h-2.5 rounded-sm bg-emerald-500/70 inline-block" />
<span className="w-3 h-2.5 rounded-sm bg-red-500/60 inline-block" />
{isUSGAAP ? 'Net Cash Flow' : (de ? 'Netto-Cashflow' : 'Net Cash Flow')}
</span>
<span className="flex items-center gap-1">
<span className="w-4 h-0.5 bg-indigo-500 inline-block" />
{isUSGAAP ? 'Cash Balance' : (de ? 'Cash-Bestand' : 'Cash Balance')}
</span>
<span className="flex items-center gap-1">
<span className="w-4 h-0.5 inline-block" style={{ borderBottom: '2px dashed #f59e0b' }} />
{isUSGAAP ? 'Cum. Funding Need' : (de ? 'Kum. Finanzbedarf' : 'Cum. Funding Need')}
</span>
</div>
</div>
)
}