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/slides/FinancialsSlide.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

314 lines
13 KiB
TypeScript

'use client'
import { useState } from 'react'
import { Language } from '@/lib/types'
import { t } from '@/lib/i18n'
import { useFinancialModel } from '@/lib/hooks/useFinancialModel'
import GradientText from '../ui/GradientText'
import FadeInView from '../ui/FadeInView'
import FinancialChart from '../ui/FinancialChart'
import FinancialSliders from '../ui/FinancialSliders'
import KPICard from '../ui/KPICard'
import RunwayGauge from '../ui/RunwayGauge'
import WaterfallChart from '../ui/WaterfallChart'
import UnitEconomicsCards from '../ui/UnitEconomicsCards'
import ScenarioSwitcher from '../ui/ScenarioSwitcher'
import AnnualPLTable, { AccountingStandard } from '../ui/AnnualPLTable'
import AnnualCashflowChart from '../ui/AnnualCashflowChart'
type FinTab = 'overview' | 'guv' | 'cashflow'
interface FinancialsSlideProps {
lang: Language
}
export default function FinancialsSlide({ lang }: FinancialsSlideProps) {
const i = t(lang)
const fm = useFinancialModel()
const [activeTab, setActiveTab] = useState<FinTab>('overview')
const [accountingStandard, setAccountingStandard] = useState<AccountingStandard>('hgb')
const de = lang === 'de'
const activeResults = fm.activeResults
const summary = activeResults?.summary
const lastResult = activeResults?.results[activeResults.results.length - 1]
// Build scenario color map
const scenarioColors: Record<string, string> = {}
fm.scenarios.forEach(s => { scenarioColors[s.id] = s.color })
// Build compare results (exclude active scenario)
const compareResults = new Map(
Array.from(fm.results.entries()).filter(([id]) => id !== fm.activeScenarioId)
)
// Initial funding from assumptions
const initialFunding = (fm.activeScenario?.assumptions.find(a => a.key === 'initial_funding')?.value as number) || 200000
const tabs: { id: FinTab; label: string }[] = [
{ id: 'overview', label: de ? 'Uebersicht' : 'Overview' },
{ id: 'guv', label: de ? 'GuV (Jahres)' : 'P&L (Annual)' },
{ id: 'cashflow', label: de ? 'Cashflow & Finanzbedarf' : 'Cashflow & Funding' },
]
if (fm.loading) {
return (
<div className="flex items-center justify-center h-full">
<div className="w-8 h-8 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
</div>
)
}
return (
<div className="max-w-6xl mx-auto px-4">
<FadeInView className="text-center mb-3">
<h2 className="text-3xl md:text-4xl font-bold mb-1">
<GradientText>{i.financials.title}</GradientText>
</h2>
<p className="text-sm text-white/50 max-w-2xl mx-auto">{i.financials.subtitle}</p>
</FadeInView>
{/* Hero KPI Cards */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 mb-3">
<KPICard
label={`ARR 2030`}
value={summary ? Math.round(summary.final_arr / 1_000_000 * 10) / 10 : 0}
suffix=" Mio."
decimals={1}
trend="up"
color="#6366f1"
delay={0.1}
subLabel="EUR"
/>
<KPICard
label={de ? 'Kunden 2030' : 'Customers 2030'}
value={summary?.final_customers || 0}
trend="up"
color="#22c55e"
delay={0.15}
/>
<KPICard
label="Break-Even"
value={summary?.break_even_month || 0}
suffix={de ? ' Mo.' : ' mo.'}
trend={summary?.break_even_month && summary.break_even_month <= 24 ? 'up' : 'down'}
color="#eab308"
delay={0.2}
subLabel={summary?.break_even_month ? `~${Math.ceil((summary.break_even_month) / 12) + 2025}` : ''}
/>
<KPICard
label="LTV/CAC"
value={summary?.final_ltv_cac || 0}
suffix="x"
decimals={1}
trend={(summary?.final_ltv_cac || 0) >= 3 ? 'up' : 'down'}
color="#a855f7"
delay={0.25}
/>
</div>
{/* Tab Navigation + Accounting Standard Toggle */}
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-1">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`px-3 py-1.5 rounded-lg text-xs transition-all
${activeTab === tab.id
? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30'
: 'bg-white/[0.04] text-white/40 border border-transparent hover:text-white/60 hover:bg-white/[0.06]'
}`}
>
{tab.label}
</button>
))}
</div>
{/* HGB / US GAAP Toggle — only visible on GuV and Cashflow tabs */}
{(activeTab === 'guv' || activeTab === 'cashflow') && (
<div className="flex items-center gap-1 bg-white/[0.04] rounded-lg p-0.5 border border-white/[0.06]">
<button
onClick={() => setAccountingStandard('hgb')}
className={`px-2.5 py-1 rounded-md text-[10px] font-medium transition-all
${accountingStandard === 'hgb'
? 'bg-indigo-500/25 text-indigo-300 shadow-sm'
: 'text-white/40 hover:text-white/60'
}`}
>
HGB
</button>
<button
onClick={() => setAccountingStandard('usgaap')}
className={`px-2.5 py-1 rounded-md text-[10px] font-medium transition-all
${accountingStandard === 'usgaap'
? 'bg-indigo-500/25 text-indigo-300 shadow-sm'
: 'text-white/40 hover:text-white/60'
}`}
>
US GAAP
</button>
</div>
)}
</div>
{/* Main content: 3-column layout */}
<div className="grid md:grid-cols-12 gap-3">
{/* Left: Charts (8 columns) */}
<div className="md:col-span-8 space-y-3">
{/* TAB: Overview — monatlicher Chart + Waterfall + Unit Economics */}
{activeTab === 'overview' && (
<>
<FadeInView delay={0.1}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-3">
<div className="flex items-center justify-between mb-2">
<p className="text-xs text-white/40">
{de ? 'Umsatz vs. Kosten (60 Monate)' : 'Revenue vs. Costs (60 months)'}
</p>
<div className="flex items-center gap-3 text-[9px]">
<span className="flex items-center gap-1"><span className="w-2 h-0.5 bg-indigo-500 inline-block" /> {de ? 'Umsatz' : 'Revenue'}</span>
<span className="flex items-center gap-1"><span className="w-2 h-0.5 bg-red-400 inline-block" style={{ borderBottom: '1px dashed' }} /> {de ? 'Kosten' : 'Costs'}</span>
<span className="flex items-center gap-1"><span className="w-2 h-0.5 bg-emerald-500 inline-block" /> {de ? 'Kunden' : 'Customers'}</span>
</div>
</div>
<FinancialChart
activeResults={activeResults}
compareResults={compareResults}
compareMode={fm.compareMode}
scenarioColors={scenarioColors}
lang={lang}
/>
</div>
</FadeInView>
<div className="grid md:grid-cols-2 gap-3">
<FadeInView delay={0.2}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-3">
<p className="text-xs text-white/40 mb-2">
{de ? 'Cash-Flow (Quartal)' : 'Cash Flow (Quarterly)'}
</p>
{activeResults && <WaterfallChart results={activeResults.results} lang={lang} />}
</div>
</FadeInView>
<FadeInView delay={0.25}>
<div className="space-y-3">
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-3 flex justify-center">
<RunwayGauge
months={lastResult?.runway_months || 0}
size={120}
label={de ? 'Runway (Monate)' : 'Runway (months)'}
/>
</div>
{lastResult && (
<UnitEconomicsCards
cac={lastResult.cac_eur}
ltv={lastResult.ltv_eur}
ltvCacRatio={lastResult.ltv_cac_ratio}
grossMargin={lastResult.gross_margin_pct}
churnRate={fm.activeScenario?.assumptions.find(a => a.key === 'churn_rate_monthly')?.value as number || 3}
lang={lang}
/>
)}
</div>
</FadeInView>
</div>
</>
)}
{/* TAB: GuV — Annual P&L Table */}
{activeTab === 'guv' && activeResults && (
<FadeInView delay={0.1}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-4">
<div className="flex items-center justify-between mb-3">
<p className="text-xs text-white/40">
{accountingStandard === 'hgb'
? (de ? 'Gewinn- und Verlustrechnung (HGB, 5 Jahre)' : 'Profit & Loss Statement (HGB, 5 Years)')
: (de ? 'Gewinn- und Verlustrechnung (US GAAP, 5 Jahre)' : 'Profit & Loss Statement (US GAAP, 5 Years)')
}
</p>
<p className="text-[9px] text-white/20">
{de ? 'Alle Werte in EUR' : 'All values in EUR'}
</p>
</div>
<AnnualPLTable results={activeResults.results} lang={lang} standard={accountingStandard} />
</div>
</FadeInView>
)}
{/* TAB: Cashflow & Finanzbedarf */}
{activeTab === 'cashflow' && activeResults && (
<FadeInView delay={0.1}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-4">
<p className="text-xs text-white/40 mb-3">
{accountingStandard === 'hgb'
? (de ? 'Kapitalflussrechnung (HGB)' : 'Cash Flow Statement (HGB)')
: (de ? 'Cash Flow Statement (US GAAP)' : 'Cash Flow Statement (US GAAP)')
}
</p>
<AnnualCashflowChart
results={activeResults.results}
initialFunding={initialFunding}
lang={lang}
standard={accountingStandard}
/>
</div>
</FadeInView>
)}
</div>
{/* Right: Controls (4 columns) */}
<div className="md:col-span-4 space-y-3">
{/* Scenario Switcher */}
<FadeInView delay={0.15}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-3">
<ScenarioSwitcher
scenarios={fm.scenarios}
activeId={fm.activeScenarioId}
compareMode={fm.compareMode}
onSelect={(id) => {
fm.setActiveScenarioId(id)
}}
onToggleCompare={() => {
if (!fm.compareMode) {
fm.computeAll()
}
fm.setCompareMode(!fm.compareMode)
}}
lang={lang}
/>
</div>
</FadeInView>
{/* Assumption Sliders */}
<FadeInView delay={0.2}>
<div className="bg-white/[0.05] backdrop-blur-xl border border-white/10 rounded-2xl p-3">
<p className="text-[10px] text-white/40 uppercase tracking-wider mb-2">
{i.financials.adjustAssumptions}
</p>
{fm.activeScenario && (
<FinancialSliders
assumptions={fm.activeScenario.assumptions}
onAssumptionChange={(key, value) => {
if (fm.activeScenarioId) {
fm.updateAssumption(fm.activeScenarioId, key, value)
}
}}
lang={lang}
/>
)}
{fm.computing && (
<div className="flex items-center gap-2 mt-2 text-[10px] text-indigo-400">
<div className="w-3 h-3 border border-indigo-400 border-t-transparent rounded-full animate-spin" />
{de ? 'Berechne...' : 'Computing...'}
</div>
)}
</div>
</FadeInView>
</div>
</div>
</div>
)
}