diff --git a/pitch-deck/components/slides/FinancialsSlide.tsx b/pitch-deck/components/slides/FinancialsSlide.tsx index 5aad988..aba69c8 100644 --- a/pitch-deck/components/slides/FinancialsSlide.tsx +++ b/pitch-deck/components/slides/FinancialsSlide.tsx @@ -1,19 +1,13 @@ 'use client' -import { useState } from 'react' -import { Language } from '@/lib/types' +import { useEffect, useState } from 'react' +import { Language, FMComputeResponse } 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 from '../ui/AnnualPLTable' import AnnualCashflowChart from '../ui/AnnualCashflowChart' type FinTab = 'overview' | 'guv' | 'cashflow' @@ -24,34 +18,56 @@ interface FinancialsSlideProps { export default function FinancialsSlide({ lang }: FinancialsSlideProps) { const i = t(lang) - const fm = useFinancialModel() const [activeTab, setActiveTab] = useState('overview') + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + const [guv, setGuv] = useState([]) const de = lang === 'de' - const [useFinanzplan, setUseFinanzplan] = useState(false) - const activeResults = useFinanzplan ? fm.finanzplanResults : fm.activeResults - const summary = activeResults?.summary - const lastResult = activeResults?.results[activeResults.results.length - 1] + // Auto-load Finanzplan data + useEffect(() => { + async function load() { + try { + // Compute Finanzplan and get FMResult format + const res = await fetch('/api/financial-model/compute', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ source: 'finanzplan' }), + }) + if (res.ok) { + const d = await res.json() + setData(d) + } + // Load GuV separately + const guvRes = await fetch('/api/finanzplan/guv') + if (guvRes.ok) { + const guvData = await guvRes.json() + setGuv(guvData.rows || []) + } + } catch (e) { + console.error('Failed to load Finanzplan:', e) + } finally { + setLoading(false) + } + } + load() + }, []) - // Build scenario color map - const scenarioColors: Record = {} - fm.scenarios.forEach(s => { scenarioColors[s.id] = s.color }) + const summary = data?.summary + const results = data?.results || [] + const lastResult = results.length > 0 ? results[results.length - 1] : null - // 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 + // Find break-even month (first month where revenue > costs) + const breakEvenMonth = results.findIndex(r => r.revenue_eur > r.total_costs_eur && r.revenue_eur > 0) + const breakEvenYear = breakEvenMonth >= 0 ? results[breakEvenMonth]?.year : null const tabs: { id: FinTab; label: string }[] = [ - { id: 'overview', label: de ? 'Uebersicht' : 'Overview' }, + { id: 'overview', label: de ? 'Übersicht' : 'Overview' }, { id: 'guv', label: de ? 'GuV (Jahres)' : 'P&L (Annual)' }, - { id: 'cashflow', label: de ? 'Cashflow & Finanzbedarf' : 'Cashflow & Funding' }, + { id: 'cashflow', label: de ? 'Cashflow' : 'Cash Flow' }, ] - if (fm.loading) { + if (loading) { return (
@@ -66,27 +82,13 @@ export default function FinancialsSlide({ lang }: FinancialsSlideProps) { {i.financials.title}

{i.financials.subtitle}

-
- - -
{/* Hero KPI Cards */}
= 0 ? 'up' : 'neutral'} color="#eab308" delay={0.2} - subLabel={summary?.break_even_month ? `~${Math.ceil((summary.break_even_month) / 12) + 2025}` : ''} + subLabel={breakEvenMonth >= 0 ? `${de ? 'Monat' : 'Month'} ${breakEvenMonth + 1}` : ''} /> = 3 ? 'up' : 'down'} + trend={(lastResult?.cash_balance_eur || 0) > 0 ? 'up' : 'down'} color="#a855f7" delay={0.25} + subLabel="EUR" />
@@ -138,155 +140,119 @@ export default function FinancialsSlide({ lang }: FinancialsSlideProps) { ))}
- {/* Main content: 3-column layout */} -
- {/* Left: Charts (8 columns) */} -
- - {/* TAB: Overview — monatlicher Chart + Waterfall + Unit Economics */} - {activeTab === 'overview' && ( - <> - -
-
-

- {de ? 'Umsatz vs. Kosten (60 Monate)' : 'Revenue vs. Costs (60 months)'} -

-
- {de ? 'Umsatz' : 'Revenue'} - {de ? 'Kosten' : 'Costs'} - {de ? 'Kunden' : 'Customers'} -
-
- -
-
- -
- -
-

- {de ? 'Cash-Flow (Quartal)' : 'Cash Flow (Quarterly)'} -

- {activeResults && } -
-
- - -
-
- -
- {lastResult && ( - a.key === 'churn_rate_monthly')?.value as number || 3} - lang={lang} - /> - )} -
-
-
- - )} - - {/* TAB: GuV — Annual P&L Table */} - {activeTab === 'guv' && activeResults && ( - -
-
-

- {de ? 'Gewinn- und Verlustrechnung (5 Jahre)' : 'Profit & Loss Statement (5 Years)'} -

-

- {de ? 'Alle Werte in EUR' : 'All values in EUR'} -

-
- -
-
- )} - - {/* TAB: Cashflow & Finanzbedarf */} - {activeTab === 'cashflow' && activeResults && ( - -
-

- {de ? 'Jaehrlicher Cashflow & Finanzbedarf' : 'Annual Cash Flow & Funding Requirements'} -

- -
-
- )} -
- - {/* Right: Controls (4 columns) */} -
- {/* Scenario Switcher */} - + {/* TAB: Overview — monatlicher Chart */} + {activeTab === 'overview' && ( +
+
- { - fm.setActiveScenarioId(id) - }} - onToggleCompare={() => { - if (!fm.compareMode) { - fm.computeAll() - } - fm.setCompareMode(!fm.compareMode) - }} +
+

+ {de ? 'Umsatz vs. Kosten (60 Monate)' : 'Revenue vs. Costs (60 months)'} +

+
+ {de ? 'Umsatz' : 'Revenue'} + {de ? 'Kosten' : 'Costs'} + Cash +
+
+
- {/* Assumption Sliders */} - -
-

- {i.financials.adjustAssumptions} -

- {fm.activeScenario && ( - { - if (fm.activeScenarioId) { - fm.updateAssumption(fm.activeScenarioId, key, value) - } - }} - lang={lang} - /> - )} - {fm.computing && ( -
-
- {de ? 'Berechne...' : 'Computing...'} -
- )} -
- +
+ +
+

+ {de ? 'Cash-Flow (Quartal)' : 'Cash Flow (Quarterly)'} +

+ {data && } +
+
+ + +
+

+ {de ? 'Jährlicher Cashflow' : 'Annual Cash Flow'} +

+ {data && ( + + )} +
+
+
-
+ )} + + {/* TAB: GuV — aus Finanzplan DB */} + {activeTab === 'guv' && ( + +
+
+

+ {de ? 'Gewinn- und Verlustrechnung (5 Jahre)' : 'Profit & Loss Statement (5 Years)'} +

+

{de ? 'Alle Werte in EUR' : 'All values in EUR'}

+
+ + + + + {[2026, 2027, 2028, 2029, 2030].map(y => ( + + ))} + + + + {guv.map((row: any) => { + const label = row.row_label || '' + const values = row.values || {} + const isBold = row.is_sum_row || label.includes('EBIT') || label.includes('Summe') || label.includes('Rohergebnis') || label.includes('Gesamtleistung') || label.includes('Jahresueberschuss') || label.includes('Ergebnis') + + return ( + + + {[2026, 2027, 2028, 2029, 2030].map(y => { + const v = Math.round(values[`y${y}`] || 0) + return ( + + ) + })} + + ) + })} + +
{de ? 'Position' : 'Item'}{y}
{label} 0 ? (isBold ? 'text-white/80' : 'text-white/50') : 'text-white/15'} ${isBold ? 'font-bold' : ''}`}> + {v === 0 ? '—' : v.toLocaleString('de-DE')} +
+
+
+ )} + + {/* TAB: Cashflow */} + {activeTab === 'cashflow' && data && ( + +
+

+ {de ? 'Jährlicher Cashflow & Liquiditätsentwicklung' : 'Annual Cash Flow & Liquidity Development'} +

+ +
+
+ )}
) }