fix(pitch-deck): GESAMTUMSATZ sum, Engineering stats, betriebliche accordion
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m10s
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 29s
CI / test-bqas (push) Successful in 31s
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m10s
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 29s
CI / test-bqas (push) Successful in 31s
- Fix GESAMTUMSATZ: only sum section='revenue' rows (not price/quantity) - Fix Materialaufwand SUMME: only sum section='cost' rows - Kunden GESAMT: trust DB values instead of wrong frontend recompute - Engineering: 500K+ LoC, 385 RAG docs, 25K+ controls, remove modules card - Betriebliche Aufwendungen: clickable category headers with ▸/▾ toggle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,33 +30,26 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
|
||||
const heroStats = [
|
||||
{
|
||||
value: '481K',
|
||||
value: '500K+',
|
||||
label: de ? 'Zeilen Code' : 'Lines of Code',
|
||||
sub: 'Go · Python · TypeScript',
|
||||
color: 'text-indigo-400',
|
||||
borderColor: 'border-indigo-500/30',
|
||||
},
|
||||
{
|
||||
value: '320',
|
||||
value: '385',
|
||||
label: de ? 'Dokumente im RAG' : 'Documents in RAG',
|
||||
sub: de ? 'EU · DACH · Frameworks · Urteile' : 'EU · DACH · Frameworks · Rulings',
|
||||
color: 'text-emerald-400',
|
||||
borderColor: 'border-emerald-500/30',
|
||||
},
|
||||
{
|
||||
value: '70K+',
|
||||
value: '25K+',
|
||||
label: de ? 'Compliance Controls' : 'Compliance Controls',
|
||||
sub: de ? '6 Pipeline-Versionen' : '6 pipeline versions',
|
||||
color: 'text-purple-400',
|
||||
borderColor: 'border-purple-500/30',
|
||||
},
|
||||
{
|
||||
value: '12',
|
||||
label: de ? 'Produkt-Module' : 'Product Modules',
|
||||
sub: de ? 'Security · Compliance · KI' : 'Security · Compliance · AI',
|
||||
color: 'text-amber-400',
|
||||
borderColor: 'border-amber-500/30',
|
||||
},
|
||||
]
|
||||
|
||||
const languageBreakdown = [
|
||||
@@ -139,7 +132,7 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
|
||||
{/* Hero Stats */}
|
||||
<FadeInView delay={0.1}>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-5">
|
||||
<div className="grid grid-cols-3 gap-3 mb-5">
|
||||
{heroStats.map((stat, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
|
||||
@@ -95,6 +95,8 @@ interface FpScenario { id: string; name: string; is_default: boolean }
|
||||
export default function FinanzplanSlide({ lang, investorId, preferredScenarioId, isWandeldarlehen }: FinanzplanSlideProps) {
|
||||
const [sheets, setSheets] = useState<SheetMeta[]>([])
|
||||
const [scenarios, setScenarios] = useState<FpScenario[]>([])
|
||||
const [openCats, setOpenCats] = useState<Set<string>>(new Set())
|
||||
const toggleCat = (cat: string) => setOpenCats(prev => { const n = new Set(prev); n.has(cat) ? n.delete(cat) : n.add(cat); return n })
|
||||
const [selectedScenarioId, setSelectedScenarioId] = useState<string>('')
|
||||
const [activeSheet, setActiveSheet] = useState<string>('guv')
|
||||
const [rows, setRows] = useState<SheetRow[]>([])
|
||||
@@ -644,15 +646,27 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
return row
|
||||
}
|
||||
|
||||
// === Kunden/Umsatz GESAMT rows ===
|
||||
else if (label.includes('GESAMT') || label.includes('Bestandskunden gesamt') || label.includes('GESAMTUMSATZ') || label.includes('SUMME Material')) {
|
||||
// Sum all non-sum rows
|
||||
// === Umsatzerlöse: GESAMTUMSATZ = sum of revenue rows only ===
|
||||
else if (label.includes('GESAMTUMSATZ')) {
|
||||
sourceRows = rows.filter(r => {
|
||||
const rLabel = getLabel(r)
|
||||
return !rLabel.includes('GESAMT') && !rLabel.includes('Summe') && !rLabel.includes('SUMME') && !rLabel.includes('Bestandskunden gesamt') && !rLabel.includes('GESAMTUMSATZ')
|
||||
const sec = (r as Record<string, unknown>).section as string || ''
|
||||
return sec === 'revenue' && !getLabel(r).includes('GESAMTUMSATZ')
|
||||
})
|
||||
}
|
||||
|
||||
// === Materialaufwand: SUMME = sum of cost rows only ===
|
||||
else if (label.includes('SUMME Material') || (activeSheet === 'materialaufwand' && label === 'SUMME')) {
|
||||
sourceRows = rows.filter(r => {
|
||||
const sec = (r as Record<string, unknown>).section as string || ''
|
||||
return sec === 'cost' && getLabel(r) !== 'SUMME'
|
||||
})
|
||||
}
|
||||
|
||||
// === Kunden GESAMT rows — trust DB values (engine computed) ===
|
||||
else if (label.includes('GESAMT') || label.includes('Bestandskunden gesamt')) {
|
||||
return row
|
||||
}
|
||||
|
||||
if (sourceRows.length === 0) return row
|
||||
|
||||
const computed: Record<string, number> = {}
|
||||
@@ -665,12 +679,26 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
})
|
||||
|
||||
return computedRows
|
||||
})().map(row => {
|
||||
})().filter(row => {
|
||||
// For betriebliche: hide detail rows if their category is collapsed
|
||||
if (activeSheet !== 'betriebliche') return true
|
||||
const cat = row.category as string || ''
|
||||
const label = getLabel(row)
|
||||
// Always show: sum rows, Personalkosten, Abschreibungen, category="summe"
|
||||
if (row.is_sum_row || cat === 'summe' || cat === 'personal' || cat === 'abschreibungen') return true
|
||||
if (label === 'Personalkosten' || label === 'Abschreibungen') return true
|
||||
// Detail rows: only show if category is open
|
||||
return openCats.has(cat)
|
||||
}).map(row => {
|
||||
const values = getValues(row)
|
||||
const label = getLabel(row)
|
||||
const isSumRow = row.is_sum_row || label.includes('GESAMT') || label.includes('Summe') || label.includes('ÜBERSCHUSS') || label.includes('LIQUIDITÄT') || label.includes('UEBERSCHUSS') || label.includes('LIQUIDITAET')
|
||||
const isTotalRow = label.includes('GESAMT') || label.includes('Bestandskunden gesamt') || label.includes('GESAMTUMSATZ') || label.includes('SUMME')
|
||||
const isEditable = false // read-only for investors
|
||||
const cat = row.category as string || ''
|
||||
// Make category sum rows clickable (accordion)
|
||||
const isCatHeader = activeSheet === 'betriebliche' && row.is_sum_row && cat !== 'summe' && cat !== 'personal' && cat !== 'abschreibungen'
|
||||
const isCatOpen = openCats.has(cat)
|
||||
// Balance rows show Dec value, flow rows show annual sum
|
||||
const isBalanceRow = label.includes('Kontostand') || label === 'LIQUIDITÄT' || label === 'LIQUIDITAET'
|
||||
const isUnitPrice = (row as Record<string, unknown>).section === 'unit_cost' || (row as Record<string, unknown>).section === 'einkauf' || label.includes('Einkaufspreis')
|
||||
@@ -692,8 +720,11 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
key={row.id}
|
||||
className={`${isTotalRow ? 'border-t-2 border-t-white/20 border-b border-b-white/[0.03]' : 'border-b border-white/[0.03]'} ${isSumRow ? 'bg-white/[0.03]' : ''} hover:bg-white/[0.02]`}
|
||||
>
|
||||
<td className={`py-1 px-2 sticky left-0 bg-slate-900/90 backdrop-blur ${isSumRow ? 'font-bold text-white/80' : 'text-white/60'}`}>
|
||||
<td className={`py-1 px-2 sticky left-0 bg-slate-900/90 backdrop-blur ${isSumRow ? 'font-bold text-white/80' : 'text-white/60'} ${isCatHeader ? 'cursor-pointer select-none' : ''}`}
|
||||
onClick={isCatHeader ? () => toggleCat(cat) : undefined}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
{isCatHeader && <span className="text-[10px] text-indigo-400 w-3 shrink-0">{isCatOpen ? '▾' : '▸'}</span>}
|
||||
{isEditable && <span className="w-1 h-1 rounded-full bg-indigo-400 flex-shrink-0" />}
|
||||
<span className="truncate"><LabelWithTooltip label={label} /></span>
|
||||
{row.position && <span className="text-white/50 ml-1">({row.position})</span>}
|
||||
|
||||
Reference in New Issue
Block a user