feat(pitch-deck): collapsible year view in Finanzplan + remove section labels
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 30s
CI / test-python-voice (push) Successful in 31s
CI / test-bqas (push) Successful in 31s
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 30s
CI / test-python-voice (push) Successful in 31s
CI / test-bqas (push) Successful in 31s
- Year navigation: "Alle Jahre" shows 5 annual columns, individual years show 12 months - Default starts at single year view - Annual view: flow rows show yearly sum, balance rows show Dec value - Removed [section] labels from row display - Footer sum only shown in monthly view (not annual) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -500,18 +500,24 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Year Navigation — not for GuV, KPIs, Charts (annual views) */}
|
||||
{/* Year Navigation — not for GuV, KPIs, Charts */}
|
||||
{!['guv', 'kpis', 'charts'].includes(activeSheet) && (
|
||||
<div className="flex items-center justify-center gap-4 mb-2">
|
||||
<button onClick={() => setYearOffset(Math.max(0, yearOffset - 1))} disabled={yearOffset === 0}
|
||||
className="p-1 text-white/40 hover:text-white/70 disabled:opacity-20">
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<span className="text-sm font-bold text-white">{currentYear}</span>
|
||||
<button onClick={() => setYearOffset(Math.min(4, yearOffset + 1))} disabled={yearOffset === 4}
|
||||
className="p-1 text-white/40 hover:text-white/70 disabled:opacity-20">
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
<div className="flex items-center justify-center gap-1 mb-2">
|
||||
<button
|
||||
onClick={() => setYearOffset(-1)}
|
||||
className={`px-3 py-1 text-[10px] font-medium rounded-lg transition-colors ${yearOffset === -1 ? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30' : 'text-white/40 hover:text-white/70 hover:bg-white/[0.05]'}`}
|
||||
>
|
||||
{de ? 'Alle Jahre' : 'All Years'}
|
||||
</button>
|
||||
{[2026, 2027, 2028, 2029, 2030].map((y, idx) => (
|
||||
<button
|
||||
key={y}
|
||||
onClick={() => setYearOffset(idx)}
|
||||
className={`px-3 py-1 text-[10px] font-medium rounded-lg transition-colors ${yearOffset === idx ? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30' : 'text-white/40 hover:text-white/70 hover:bg-white/[0.05]'}`}
|
||||
>
|
||||
{y}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -558,21 +564,30 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
/* === Monthly Grid (all other sheets) === */
|
||||
/* === Monthly/Annual Grid (all other sheets) === */
|
||||
<table className="w-full text-[10px]">
|
||||
<thead>
|
||||
<tr className="border-b border-white/10">
|
||||
<th className="text-left py-1.5 px-2 text-white/60 font-medium sticky left-0 bg-slate-900/90 backdrop-blur min-w-[160px]">
|
||||
{de ? 'Position' : 'Item'}
|
||||
</th>
|
||||
<th className="text-right py-1.5 px-2 text-white/60 font-medium min-w-[70px]">
|
||||
{currentYear}
|
||||
</th>
|
||||
{MONTH_LABELS.map((label, idx) => (
|
||||
<th key={idx} className="text-right py-1.5 px-1.5 text-white/50 font-normal min-w-[55px]">
|
||||
{label}
|
||||
</th>
|
||||
))}
|
||||
{yearOffset === -1 ? (
|
||||
// All years view
|
||||
[2026, 2027, 2028, 2029, 2030].map(y => (
|
||||
<th key={y} className="text-right py-1.5 px-3 text-white/60 font-medium min-w-[80px]">{y}</th>
|
||||
))
|
||||
) : (
|
||||
<>
|
||||
<th className="text-right py-1.5 px-2 text-white/60 font-medium min-w-[70px]">
|
||||
{currentYear}
|
||||
</th>
|
||||
{MONTH_LABELS.map((label, idx) => (
|
||||
<th key={idx} className="text-right py-1.5 px-1.5 text-white/50 font-normal min-w-[55px]">
|
||||
{label}
|
||||
</th>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -668,39 +683,59 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
{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>}
|
||||
{row.section && <span className="text-white/50 ml-1">[{row.section}]</span>}
|
||||
</div>
|
||||
</td>
|
||||
<td className={`text-right py-1 px-2 font-medium ${annual < 0 ? 'text-red-400' : isSumRow ? 'text-white/80' : 'text-white/50'}`}>
|
||||
{formatCell(annual)}
|
||||
</td>
|
||||
{Array.from({ length: 12 }, (_, idx) => {
|
||||
const mKey = `m${monthStart + idx}`
|
||||
const v = values[mKey] || 0
|
||||
|
||||
return (
|
||||
<td
|
||||
key={idx}
|
||||
className={`text-right py-1 px-1.5 ${
|
||||
v < 0 ? 'text-red-400/70' : v > 0 ? (isSumRow ? 'text-white/70' : 'text-white/50') : 'text-white/15'
|
||||
} ${isEditable ? 'cursor-pointer hover:bg-indigo-500/10' : ''}`}
|
||||
onDoubleClick={() => {
|
||||
if (!isEditable) return
|
||||
const input = prompt(`${label} — ${MONTH_LABELS[idx]} ${currentYear}`, String(v))
|
||||
if (input !== null) handleCellEdit(row.id, mKey, input)
|
||||
}}
|
||||
>
|
||||
{formatCell(v)}
|
||||
{yearOffset === -1 ? (
|
||||
// All years view: show annual values per year
|
||||
[2026, 2027, 2028, 2029, 2030].map(y => {
|
||||
const yStart = (y - 2026) * 12 + 1
|
||||
const yEnd = yStart + 11
|
||||
let yVal = 0
|
||||
if (isUnitPrice) {
|
||||
yVal = values[`m${yEnd}`] || 0
|
||||
} else if (isBalanceRow) {
|
||||
yVal = values[`m${yEnd}`] || 0
|
||||
} else {
|
||||
for (let m = yStart; m <= yEnd; m++) yVal += values[`m${m}`] || 0
|
||||
}
|
||||
return (
|
||||
<td key={y} className={`text-right py-1 px-3 ${yVal < 0 ? 'text-red-400' : yVal > 0 ? (isSumRow ? 'text-white/80' : 'text-white/50') : 'text-white/15'} ${isSumRow ? 'font-bold' : ''}`}>
|
||||
{formatCell(Math.round(yVal))}
|
||||
</td>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
<td className={`text-right py-1 px-2 font-medium ${annual < 0 ? 'text-red-400' : isSumRow ? 'text-white/80' : 'text-white/50'}`}>
|
||||
{formatCell(annual)}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
{Array.from({ length: 12 }, (_, idx) => {
|
||||
const mKey = `m${monthStart + idx}`
|
||||
const v = values[mKey] || 0
|
||||
return (
|
||||
<td
|
||||
key={idx}
|
||||
className={`text-right py-1 px-1.5 ${
|
||||
v < 0 ? 'text-red-400/70' : v > 0 ? (isSumRow ? 'text-white/70' : 'text-white/50') : 'text-white/15'
|
||||
} ${isEditable ? 'cursor-pointer hover:bg-indigo-500/10' : ''}`}
|
||||
onDoubleClick={() => {
|
||||
if (!isEditable) return
|
||||
const input = prompt(`${label} — ${MONTH_LABELS[idx]} ${currentYear}`, String(v))
|
||||
if (input !== null) handleCellEdit(row.id, mKey, input)
|
||||
}}
|
||||
>
|
||||
{formatCell(v)}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
{/* Summenzeile für relevante Sheets */}
|
||||
{['personalkosten', 'betriebliche', 'investitionen', 'sonst_ertraege'].includes(activeSheet) && rows.length > 0 && (() => {
|
||||
// Berechne Summe über alle Zeilen die keine Summenzeilen sind
|
||||
{yearOffset !== -1 && ['personalkosten', 'betriebliche', 'investitionen'].includes(activeSheet) && rows.length > 0 && (() => {
|
||||
const sumValues: Record<string, number> = {}
|
||||
let sumAnnual = 0
|
||||
const nonSumRows = rows.filter(r => {
|
||||
@@ -724,10 +759,7 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId,
|
||||
{de ? 'SUMME' : 'TOTAL'}
|
||||
</td>
|
||||
<td className={`text-right py-1.5 px-2 font-bold text-xs ${sumAnnual < 0 ? 'text-red-400' : 'text-white/80'}`}>
|
||||
{['kunden', 'kunden_summary'].includes(activeSheet)
|
||||
? formatCell(sumValues[`m${monthEnd}`] || 0)
|
||||
: formatCell(sumAnnual)
|
||||
}
|
||||
{formatCell(sumAnnual)}
|
||||
</td>
|
||||
{Array.from({ length: 12 }, (_, idx) => {
|
||||
const mKey = `m${monthStart + idx}`
|
||||
|
||||
Reference in New Issue
Block a user