Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
69 lines
4.0 KiB
TypeScript
69 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import GlassCard from '../ui/GlassCard'
|
|
import { formatCell } from './FinanzplanSlide.helpers'
|
|
|
|
interface KPIsTabProps {
|
|
fpKPIs: Record<string, Record<string, number>>
|
|
de: boolean
|
|
}
|
|
|
|
export default function KPIsTab({ fpKPIs, de }: KPIsTabProps) {
|
|
const years = ['y2026', 'y2027', 'y2028', 'y2029', 'y2030']
|
|
const v = (yk: string, key: string) => fpKPIs[yk]?.[key] || 0
|
|
|
|
return (
|
|
<GlassCard hover={false} className="p-4">
|
|
<h3 className="text-sm font-bold text-emerald-400 uppercase tracking-wider mb-4">{de ? 'Wichtige Kennzahlen (pro Jahr)' : 'Key Metrics (per year)'}</h3>
|
|
<table className="w-full text-xs">
|
|
<thead>
|
|
<tr className="border-b border-white/10">
|
|
<th className="text-left py-2 px-2 text-white/60 font-medium">KPI</th>
|
|
{[2026, 2027, 2028, 2029, 2030].map(y => (
|
|
<th key={y} className="text-right py-2 px-3 text-white/60 font-medium">{y}</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{!fpKPIs['y2026'] ? (
|
|
<tr><td colSpan={6} className="text-center py-4 text-white/30">{de ? 'Finanzplan wird geladen...' : 'Loading financial plan...'}</td></tr>
|
|
) : [
|
|
{ label: 'MRR (Dez)', values: years.map(y => v(y, 'mrr')), unit: '\u20AC', bold: true },
|
|
{ label: 'ARR (Dez \u00d7 12)', values: years.map(y => v(y, 'arr')), unit: '\u20AC', bold: true },
|
|
{ label: de ? 'Kunden (Dez)' : 'Customers (Dec)', values: years.map(y => v(y, 'customers')), unit: '', bold: false },
|
|
{ label: de ? 'ACV (Umsatz/Kunden)' : 'ACV (Revenue/Customers)', values: years.map(y => v(y, 'arpu')), unit: '\u20AC', bold: false },
|
|
{ label: 'Gross Margin', values: years.map(y => v(y, 'grossMargin')), unit: '%', bold: false },
|
|
{ label: de ? 'Umsatzwachstum (YoY)' : 'Revenue Growth (YoY)', values: years.map(y => v(y, 'nrr')), unit: '%', bold: false },
|
|
{ label: de ? 'Mitarbeiter' : 'Employees', values: years.map(y => v(y, 'headcount')), unit: '', bold: false },
|
|
{ label: de ? 'Umsatz/Mitarbeiter' : 'Revenue/Employee', values: years.map(y => v(y, 'revPerEmp')), unit: '\u20AC', bold: false },
|
|
{ label: de ? 'Personalkosten' : 'Personnel Costs', values: years.map(y => v(y, 'personal')), unit: '\u20AC', bold: false },
|
|
{ label: 'EBIT', values: years.map(y => v(y, 'ebit')), unit: '\u20AC', bold: true },
|
|
{ label: de ? 'EBIT-Marge' : 'EBIT Margin', values: years.map(y => v(y, 'ebitMargin')), unit: '%', bold: false },
|
|
{ label: de ? 'Steuern' : 'Taxes', values: years.map(y => v(y, 'steuern')), unit: '\u20AC', bold: false },
|
|
{ label: de ? 'Jahres\u00fcberschuss' : 'Net Income', values: years.map(y => v(y, 'netIncome')), unit: '\u20AC', bold: true },
|
|
{ label: de ? 'Liquidit\u00e4t (Dez)' : 'Cash (Dec)', values: years.map(y => v(y, 'liquiditaet')), unit: '\u20AC', bold: true },
|
|
{ label: 'Burn Rate', values: years.map(y => v(y, 'burnRate')), unit: '\u20AC/Mo', bold: false },
|
|
].map((row, idx) => (
|
|
<tr key={idx} className={`border-b border-white/[0.03] ${row.bold ? 'bg-white/[0.03]' : ''}`}>
|
|
<td className={`py-1.5 px-2 ${row.bold ? 'font-bold text-white/80' : 'text-white/60'}`}>{row.label}</td>
|
|
{row.values.map((val, i) => {
|
|
const num = typeof val === 'number' ? val : 0
|
|
const display = typeof val === 'string' ? val : (
|
|
row.unit === '%' ? `${val}%` :
|
|
row.unit === '\u20AC/Mo' ? formatCell(num) + '/Mo' :
|
|
formatCell(num)
|
|
)
|
|
return (
|
|
<td key={i} className={`text-right py-1.5 px-3 font-mono ${num < 0 ? 'text-red-400' : row.bold ? 'text-white/80 font-bold' : 'text-white/50'}`}>
|
|
{display}
|
|
</td>
|
|
)
|
|
})}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</GlassCard>
|
|
)
|
|
}
|