File size: 3,892 Bytes
0e23a69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
const formatMoney = (n) =>
    n >= 1e6 ? `$${(n / 1e6).toFixed(2)}M`
        : n >= 1e3 ? `$${(n / 1e3).toFixed(1)}K`
            : `$${Math.abs(n).toFixed(0)}`

const formatPct = (v) => `${(v * 100).toFixed(0)}%`
const fmtDelta = (key, d) => {
    if (key === 'revenue' || key === 'burn_rate') return formatMoney(Math.abs(d))
    if (key === 'runway_months') return `${Math.abs(d).toFixed(1)}mo`
    if (key === 'profitability_score') return Math.abs(d).toFixed(1)
    return formatPct(Math.abs(d))
}

// ASCII progress bar — no chart.js, pure terminal
function AsciiBar({ value, max = 1, width = 8 }) {
    const filled = Math.round((value / max) * width)
    const empty = width - filled
    return (
        <span style={{ color: 'var(--muted)', letterSpacing: 0 }}>
            [<span style={{ color: 'var(--primary)', textShadow: 'var(--glow-sm)' }}>
                {'█'.repeat(Math.max(0, filled))}
            </span>
            {'░'.repeat(Math.max(0, empty))}]
        </span>
    )
}

const TILES = [
    { key: 'profitability_score', label: 'SCORE',    fmt: (v) => v.toFixed(1),            max: 100 },
    { key: 'revenue',             label: 'REVENUE',  fmt: formatMoney,                     max: null },
    { key: 'burn_rate',           label: 'BURN',     fmt: formatMoney,                     max: null },
    { key: 'runway_months',       label: 'RUNWAY',   fmt: (v) => `${v.toFixed(1)}mo`,      max: 24 },
    { key: 'product_readiness',   label: 'PRODUCT',  fmt: formatPct,                       max: 1 },
    { key: 'market_share',        label: 'MARKET',   fmt: formatPct,                       max: 1 },
    { key: 'team_morale',         label: 'MORALE',   fmt: formatPct,                       max: 1 },
    { key: 'investor_confidence', label: 'INVEST',   fmt: formatPct,                       max: 1 },
    { key: 'regulatory_risk',     label: 'REG_RSK',  fmt: formatPct,                       max: 1 },
]

function scoreTile(key, val) {
    if (key === 'regulatory_risk') return val > 0.65 ? 'bad' : val > 0.35 ? 'warn' : 'good'
    if (key === 'runway_months')   return val > 12   ? 'good' : val > 6   ? 'warn' : 'bad'
    if (key === 'profitability_score') return val >= 60 ? 'good' : val >= 35 ? 'warn' : 'bad'
    if (key === 'burn_rate') return ''
    return val > 0.65 ? 'good' : val > 0.35 ? 'warn' : 'bad'
}

export default function MetricsPanel({ state, prevState }) {
    if (!state) return null

    return (
        <div className="metrics-strip">
            {TILES.map(({ key, label, fmt, max }) => {
                const val   = state[key] ?? 0
                const prev  = prevState?.[key]
                const delta = prev !== undefined ? val - prev : null
                const cls   = scoreTile(key, val)
                const barVal = max ? Math.min(val, max) : null

                return (
                    <div key={key} className="metric-tile">
                        <div className="m-icon-label">
                            <span className="m-label">{label}</span>
                        </div>
                        <div className="m-value-row">
                            <span className={`m-value ${cls}`}>{fmt(val)}</span>
                            {delta !== null && Math.abs(delta) > 0.001 && (
                                <span className={`m-delta ${delta > 0 ? 'pos' : 'neg'}`}>
                                    {delta > 0 ? '+' : '−'}{fmtDelta(key, delta)}
                                </span>
                            )}
                        </div>
                        {barVal !== null && (
                            <div style={{ marginTop: '0.15rem' }}>
                                <AsciiBar value={barVal} max={max} width={6} />
                            </div>
                        )}
                    </div>
                )
            })}
        </div>
    )
}