/
keorlov
/
collsim
Обзор
Документация
Войти
/
keorlov
/
collsim
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
frontend/src/components/MetricsTable.jsx
91 строка
5 KB
Konstantin Orlov
wip
14 июл 2026, 12:10
14 июл 2026, 12:10
c8f2d41
Код
Авторство
О чём код?
const fmt = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 0 }); const METRIC_ROWS = [ { key: "day", label: "День" }, { key: "current_count", label: "Текущие кредиты" }, { key: "soft_count", label: "Просрочка (1–89 дн.)" }, { key: "dpd_1_30_count", label: "DPD 1–30" }, { key: "dpd_31_60_count", label: "DPD 31–60" }, { key: "dpd_61_89_count", label: "DPD 61–89" }, { key: "hard_count", label: "Hard default (90+ дн.)" }, { key: "closed_count", label: "Погашено" }, { key: "sold_count", label: "Списано" }, { key: "total_balance", label: "Баланс портфеля", fmt: (v) => fmt.format(v) + " ₽" }, { key: "past_due_balance", label: "Past Due Balance (soft)", fmt: (v) => fmt.format(v) + " ₽" }, { key: "npl_balance", label: "NPL Balance (hard)", fmt: (v) => fmt.format(v) + " ₽" }, { key: "npl_pct", label: "NPL Ratio", fmt: (v) => v.toFixed(2) + "%" }, { key: "cost_of_risk", label: "Cost of Risk", fmt: (v) => (v * 100).toFixed(4) + "%" }, { key: "cession_losses", label: "Cession Losses", fmt: (v) => fmt.format(v) + " ₽" }, { key: "interest_income_today", label: "NIM Income", fmt: (v) => fmt.format(v) + " ₽" }, { key: "daily_budget_spent", label: "Затраты (всего)", fmt: (v) => fmt.format(v) + " ₽" }, { key: "spent_sms", label: "Затраты SMS", fmt: (v) => fmt.format(v) + " ₽" }, { key: "spent_call", label: "Затраты Звонки", fmt: (v) => fmt.format(v) + " ₽" }, { key: "recovered_today", label: "Взыскано (всего)", fmt: (v) => fmt.format(v) + " ₽" }, { key: "recovered_sms", label: "Взыскание SMS", fmt: (v) => fmt.format(v) + " ₽" }, { key: "recovered_call", label: "Взыскание Звонки", fmt: (v) => fmt.format(v) + " ₽" }, { key: "recovered_waiting", label: "Взыскание Waiting", fmt: (v) => fmt.format(v) + " ₽" }, { key: "new_clients", label: "Новые клиенты" }, { key: "inflow_soft", label: "Вход в просрочку" }, { key: "outflow_cured", label: "Излечено (cure)" }, { key: "outflow_hard", label: "Уход в hard" }, { key: "cured_today", label: "Cured today" }, { key: "sold_today", label: "Продано" }, ]; function fmtDiff(userVal, baseVal, formatter) { if (baseVal === undefined || baseVal === null || baseVal === 0) return ""; const diff = userVal - baseVal; const pct = (diff / baseVal) * 100; const sign = diff >= 0 ? "+" : ""; const diffStr = formatter ? formatter(diff) : fmt.format(diff); return `${sign}${diffStr} (${sign}${pct.toFixed(1)}%)`; } function diffClass(userVal, baseVal, key) { if (baseVal === undefined || baseVal === null || baseVal === 0 || key === "day") return ""; const diff = userVal - baseVal; if (key === "npl_pct" || key === "hard_count" || key === "soft_count" || key === "npl_balance" || key === "past_due_balance" || key === "daily_budget_spent" || key === "spent_sms" || key === "spent_call" || key === "cost_of_risk" || key === "inflow_soft" || key === "outflow_hard") { return diff < 0 ? "diff-good" : diff > 0 ? "diff-bad" : ""; } return diff > 0 ? "diff-good" : diff < 0 ? "diff-bad" : ""; } export default function MetricsTable({ userMetrics, baselineMetrics }) { if (!userMetrics?.length) return null; return ( <div className="table-scroll"> <table className="stat-table metrics-table"> <thead> <tr> <th>Метрика</th> <th>Пользователь</th> {baselineMetrics && <th>Базовая</th>} {baselineMetrics && <th>Разница</th>} </tr> </thead> <tbody> {METRIC_ROWS.map((row) => { const last = userMetrics[userMetrics.length - 1]; const baseLast = baselineMetrics?.[baselineMetrics.length - 1]; const val = last[row.key]; const baseVal = baseLast?.[row.key]; const formatted = row.fmt ? row.fmt(val) : fmt.format(val); const baseFormatted = row.fmt ? row.fmt(baseVal) : (baseVal !== undefined ? fmt.format(baseVal) : "—"); const diff = baselineMetrics ? fmtDiff(val, baseVal, row.fmt) : ""; const cls = diffClass(val, baseVal, row.key); return ( <tr key={row.key}> <td className="metric-label-cell">{row.label}</td> <td className="user-col">{formatted}</td> {baselineMetrics && <td className="baseline-col">{baseFormatted}</td>} {baselineMetrics && <td className={`diff-col ${cls}`}>{diff || "—"}</td>} </tr> ); })} </tbody> </table> </div> ); }