feat(pitch-deck): larger chart labels + 2 new charts (Liquidität + Revenue vs Costs)
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m6s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 33s
CI / test-python-voice (push) Successful in 34s
CI / test-bqas (push) Successful in 28s
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m6s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 33s
CI / test-python-voice (push) Successful in 34s
CI / test-bqas (push) Successful in 28s
Charts tab: - All bar labels increased from 7-8px to 11px (readable) - New: Liquidität (Jahresende) bar chart — shows cash position per year - New: Umsatz vs. Gesamtkosten — side-by-side bars per year - All charts read from fpKPIs (fp_* source of truth) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -357,7 +357,7 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
|||||||
<div className="flex items-end gap-1 w-full justify-center" style={{ height: '160px' }}>
|
<div className="flex items-end gap-1 w-full justify-center" style={{ height: '160px' }}>
|
||||||
{/* MRR bar */}
|
{/* 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="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">
|
<div className="text-[11px] text-white/80 text-center -mt-4 whitespace-nowrap font-semibold">
|
||||||
{d.mrr >= 100000 ? `${Math.round(d.mrr/1000)}k` : d.mrr.toLocaleString('de-DE')}
|
{d.mrr >= 100000 ? `${Math.round(d.mrr/1000)}k` : d.mrr.toLocaleString('de-DE')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -392,12 +392,12 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
|||||||
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
|
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
|
||||||
{k.ebit >= 0 ? (
|
{k.ebit >= 0 ? (
|
||||||
<div className="bg-emerald-500/60 rounded-t w-full" style={{ height: `${h}px` }}>
|
<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(k.ebit/1000)}k</div>
|
<div className="text-[11px] text-emerald-300 text-center -mt-4 whitespace-nowrap font-semibold">{Math.round(k.ebit/1000)}k</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col justify-end h-full">
|
<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="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(k.ebit/1000)}k</div>
|
<div className="text-[11px] text-red-300 text-center mt-1 whitespace-nowrap font-semibold">{Math.round(k.ebit/1000)}k</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -421,7 +421,7 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
|||||||
<div key={idx} className="flex flex-col items-center">
|
<div key={idx} className="flex flex-col items-center">
|
||||||
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
|
<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 / maxEmp) * 100}px` }}>
|
<div className="bg-amber-500/60 rounded-t w-full" style={{ height: `${(d.val / maxEmp) * 100}px` }}>
|
||||||
<div className="text-[8px] text-amber-300 text-center -mt-3 font-bold">{d.val}</div>
|
<div className="text-[11px] text-amber-300 text-center -mt-4 font-bold">{d.val}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] text-white/40 mt-1">{d.year}</span>
|
<span className="text-[10px] text-white/40 mt-1">{d.year}</span>
|
||||||
@@ -431,6 +431,71 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
|||||||
</div>
|
</div>
|
||||||
</GlassCard>
|
</GlassCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Liquidität Chart */}
|
||||||
|
<GlassCard hover={false} className="p-4">
|
||||||
|
<h3 className="text-xs font-bold text-cyan-400 uppercase tracking-wider mb-3">{de ? 'Liquidität (Jahresende)' : 'Cash Position (Year-End)'}</h3>
|
||||||
|
<div className="grid grid-cols-5 gap-1 items-end h-36">
|
||||||
|
{(() => {
|
||||||
|
const years = ['y2026','y2027','y2028','y2029','y2030']
|
||||||
|
const data = years.map(y => ({ year: y.slice(1), val: fpKPIs[y]?.liquiditaet || 0 }))
|
||||||
|
const maxAbs = Math.max(...data.map(d => Math.abs(d.val)), 1)
|
||||||
|
return data.map((d, idx) => {
|
||||||
|
const h = Math.abs(d.val) / maxAbs * 100
|
||||||
|
const label = Math.abs(d.val) >= 1000000 ? `${(d.val/1000000).toFixed(1)}M` : `${Math.round(d.val/1000)}k`
|
||||||
|
return (
|
||||||
|
<div key={idx} className="flex flex-col items-center">
|
||||||
|
<div className="w-10 flex flex-col justify-end" style={{ height: '110px' }}>
|
||||||
|
<div className={`${d.val >= 0 ? 'bg-cyan-500/60 rounded-t' : 'bg-red-500/60 rounded-b'} w-full`} style={{ height: `${Math.max(h, 4)}px` }}>
|
||||||
|
<div className={`text-[11px] ${d.val >= 0 ? 'text-cyan-300 -mt-4' : 'text-red-300 mt-1'} text-center whitespace-nowrap font-semibold`}>{label}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] text-white/40 mt-1">{d.year}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</GlassCard>
|
||||||
|
|
||||||
|
{/* Umsatz vs. Kosten Chart */}
|
||||||
|
<GlassCard hover={false} className="p-4">
|
||||||
|
<h3 className="text-xs font-bold text-indigo-400 uppercase tracking-wider mb-3">{de ? 'Umsatz vs. Gesamtkosten' : 'Revenue vs. Total Costs'}</h3>
|
||||||
|
<div className="grid grid-cols-5 gap-1 items-end h-36">
|
||||||
|
{(() => {
|
||||||
|
const years = ['y2026','y2027','y2028','y2029','y2030']
|
||||||
|
const data = years.map(y => ({
|
||||||
|
year: y.slice(1),
|
||||||
|
rev: fpKPIs[y]?.revenue || 0,
|
||||||
|
costs: (fpKPIs[y]?.personal || 0) + Math.abs(fpKPIs[y]?.ebit || 0) + (fpKPIs[y]?.revenue || 0) - (fpKPIs[y]?.ebit || 0) - (fpKPIs[y]?.revenue || 0),
|
||||||
|
}))
|
||||||
|
// Simpler: costs = revenue - ebit
|
||||||
|
const data2 = years.map(y => ({
|
||||||
|
year: y.slice(1),
|
||||||
|
rev: fpKPIs[y]?.revenue || 0,
|
||||||
|
costs: (fpKPIs[y]?.revenue || 0) - (fpKPIs[y]?.ebit || 0),
|
||||||
|
}))
|
||||||
|
const maxVal = Math.max(...data2.map(d => Math.max(d.rev, d.costs)), 1)
|
||||||
|
return data2.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: '100px' }}>
|
||||||
|
<div className="w-6 bg-indigo-500/60 rounded-t" style={{ height: `${(d.rev / maxVal) * 90}px` }}>
|
||||||
|
<div className="text-[10px] text-indigo-300 text-center -mt-3 whitespace-nowrap font-semibold">{d.rev >= 1000000 ? `${(d.rev/1000000).toFixed(1)}M` : `${Math.round(d.rev/1000)}k`}</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-6 bg-red-500/40 rounded-t" style={{ height: `${(d.costs / maxVal) * 90}px` }}>
|
||||||
|
<div className="text-[10px] text-red-300 text-center -mt-3 whitespace-nowrap font-semibold">{d.costs >= 1000000 ? `${(d.costs/1000000).toFixed(1)}M` : `${Math.round(d.costs/1000)}k`}</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" /> {de ? 'Umsatz' : 'Revenue'}</span>
|
||||||
|
<span className="flex items-center gap-1"><span className="w-3 h-2 bg-red-500/40 rounded inline-block" /> {de ? 'Kosten' : 'Costs'}</span>
|
||||||
|
</div>
|
||||||
|
</GlassCard>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user