256 lines
16 KiB
HTML
256 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<!-- Дизайн: Стандартный дизайн. Сохраняй все пять CSS; подробности в design.md. -->
|
||
<html lang="ru" data-theme="light">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>КФУ — финансовая устойчивость</title>
|
||
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-components.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-page.css">
|
||
<link rel="stylesheet" href="design-system/vibe-theme.css">
|
||
<script src="design-system/icons/kt-ai-icons.js"></script>
|
||
<script src="vendor/xlsx.full.min.js"></script>
|
||
<style>
|
||
@layer product {
|
||
[hidden] { display: none !important; }
|
||
a.kt-ai-btn { text-decoration: none; }
|
||
|
||
.kfu-shell { display: grid; grid-template-columns: var(--kt-ai-sidebar-width) minmax(0, 1fr); min-height: 100dvh; }
|
||
.kfu-main { min-width: 0; background: var(--kt-ai-bg); display: flex; flex-direction: column; }
|
||
|
||
.kfu-content { box-sizing: border-box; width: 100%; min-width: 0; padding: var(--kt-ai-space-8) var(--kt-ai-space-8) var(--kt-ai-space-11); max-width: 1440px; margin: 0 auto; }
|
||
|
||
.kfu-title-row { display: flex; align-items: baseline; gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-5); flex-wrap: wrap; }
|
||
.kfu-title-row h1 { font-size: var(--kt-ai-text-xl); font-weight: 400; margin: 0; letter-spacing: -.02em; }
|
||
.kfu-title-row .kt-ai-meta { white-space: nowrap; }
|
||
|
||
.kfu-actions { display: flex; gap: var(--kt-ai-space-3); flex-wrap: wrap; margin-bottom: var(--kt-ai-space-6); }
|
||
.dropzone { display: inline-flex; align-items: center; gap: var(--kt-ai-space-4); padding: 10px 16px;
|
||
border: 1.5px dashed var(--kt-ai-border-strong); border-radius: var(--vibe-card-radius);
|
||
background: var(--kt-ai-bg-elevated); color: var(--kt-ai-fg-muted); cursor: pointer; transition: border-color .15s, background .15s; }
|
||
.dropzone:hover { border-color: var(--kt-ai-primary); background: var(--kt-ai-bg-hover); color: var(--kt-ai-fg); }
|
||
.dropzone input[type=file] { display: none; }
|
||
|
||
.kfu-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--kt-ai-space-4); margin: 0 0 var(--kt-ai-space-6); }
|
||
.kfu-kpis .kt-ai-kpi { background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--vibe-card-radius); padding: 12px 14px; }
|
||
.kfu-kpis .kt-ai-kpi .label { font-size: 10px; }
|
||
.kfu-kpis .kt-ai-kpi .value { font-size: var(--kt-ai-text-xl); }
|
||
|
||
.kfu-toolbar { display: flex; gap: var(--kt-ai-space-3); flex-wrap: wrap; align-items: center; margin-bottom: var(--kt-ai-space-4); }
|
||
.kfu-toolbar .kt-ai-search { min-width: 240px; }
|
||
.kfu-toolbar .kt-ai-select { min-width: 150px; width: auto; }
|
||
.kfu-toolbar .kt-ai-checkbox { display: inline-flex; align-items: center; gap: 6px; font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); }
|
||
|
||
.kfu-table-wrap { box-sizing: border-box; max-width: 100%; background: var(--kt-ai-card-bg); border: 1px solid var(--kt-ai-card-border); border-radius: var(--vibe-card-radius);
|
||
overflow-x: auto; box-shadow: var(--kt-ai-card-shadow); }
|
||
.kfu-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
||
.kfu-table colgroup col:nth-child(1) { width: 34%; }
|
||
.kfu-table colgroup col:nth-child(2) { width: 16%; }
|
||
.kfu-table colgroup col:nth-child(3) { width: 17%; }
|
||
.kfu-table colgroup col:nth-child(4) { width: 17%; }
|
||
.kfu-table colgroup col:nth-child(5) { width: 16%; }
|
||
.kfu-table th { text-align: right; padding: 10px 10px; font-size: var(--kt-ai-text-xs); font-weight: 600; color: var(--kt-ai-fg-muted);
|
||
text-transform: uppercase; letter-spacing: .4px; border-bottom: 1px solid var(--kt-ai-divider); white-space: nowrap; cursor: pointer; user-select: none; overflow: hidden; text-overflow: ellipsis; }
|
||
.kfu-table th:first-child, .kfu-table td:first-child { text-align: left; }
|
||
.kfu-table th .th-sub { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--kt-ai-fg-faint); font-size: 10px; white-space: normal; }
|
||
.kfu-table th.sorted-asc::after { content: " ↑"; color: var(--kt-ai-link); }
|
||
.kfu-table th.sorted-desc::after { content: " ↓"; color: var(--kt-ai-link); }
|
||
.kfu-table td { padding: 9px 10px; border-bottom: 1px solid var(--kt-ai-divider); font-size: var(--kt-ai-text-sm); text-align: right;
|
||
font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.kfu-table td:first-child { text-align: left; }
|
||
.kfu-table tbody tr { cursor: pointer; transition: background .1s; }
|
||
.kfu-table tbody tr:hover { background: var(--kt-ai-bg-hover); }
|
||
.kfu-table tbody tr.is-group td { background: var(--kt-ai-bg-soft); font-weight: 600; }
|
||
.kfu-table td .none { color: var(--kt-ai-fg-faint); }
|
||
.kfu-table td .inf { color: var(--kt-ai-link); font-weight: 600; }
|
||
|
||
.zone-pill { display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px; border-radius: var(--vibe-button-radius);
|
||
font-size: var(--kt-ai-text-xs); font-weight: 600; }
|
||
.zone-pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
|
||
.zone-pill.z-green { background: var(--kt-ai-status-ok-bg); color: var(--kt-ai-status-ok-fg); }
|
||
.zone-pill.z-yellow { background: var(--kt-ai-status-warn-bg); color: var(--kt-ai-status-warn-fg); }
|
||
.zone-pill.z-red { background: var(--kt-ai-status-risk-bg); color: var(--kt-ai-status-risk-fg); }
|
||
.zone-pill.z-nodefault { background: var(--kt-ai-bg-active); color: var(--kt-ai-fg-muted); }
|
||
.zone-pill.z-nodata { background: transparent; border: 1px dashed var(--kt-ai-border-strong); color: var(--kt-ai-fg-muted); }
|
||
|
||
.kfu-empty { padding: 48px 24px; text-align: center; color: var(--kt-ai-fg-muted); }
|
||
.kfu-empty svg { width: 32px; height: 32px; margin-bottom: 12px; opacity: .5; }
|
||
|
||
.kfu-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 520px; max-width: 94vw; background: var(--kt-ai-bg-elevated);
|
||
border-left: 1px solid var(--kt-ai-border); box-shadow: -8px 0 24px rgb(12 10 9 / .08); z-index: 100;
|
||
padding: var(--kt-ai-space-7); overflow-y: auto; display: none; }
|
||
.kfu-drawer.open { display: block; animation: kfuSlide .18s ease-out; }
|
||
@keyframes kfuSlide { from { transform: translateX(24px); opacity: .4; } to { transform: none; opacity: 1; } }
|
||
.kfu-drawer-head { display: flex; align-items: flex-start; gap: var(--kt-ai-space-3); margin-bottom: var(--kt-ai-space-5); }
|
||
.kfu-drawer-head h2 { font-size: var(--kt-ai-text-lg); font-weight: 500; margin: 0 0 4px; letter-spacing: -.01em; }
|
||
.kfu-drawer-head .kt-ai-icon-button { margin-left: auto; }
|
||
.kfu-drawer-section { margin-bottom: var(--kt-ai-space-6); }
|
||
.kfu-drawer-section h3 { font-size: var(--kt-ai-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .6px;
|
||
color: var(--kt-ai-fg-muted); margin: 0 0 10px; }
|
||
.kfu-metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--kt-ai-space-3); }
|
||
.kfu-metric { border: 1px solid var(--kt-ai-card-border); border-radius: var(--vibe-card-radius); padding: 10px 12px; background: var(--kt-ai-bg); }
|
||
.kfu-metric .mlabel { font-size: var(--kt-ai-text-xs); color: var(--kt-ai-fg-muted); margin-bottom: 4px; display: flex; align-items: center; justify-content: space-between; gap: 6px; }
|
||
.kfu-metric .mval { font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
|
||
.kfu-metric .mhint { font-size: 11px; color: var(--kt-ai-fg-faint); margin-top: 3px; font-variant-numeric: tabular-nums; }
|
||
.kfu-formula { font-size: 11px; color: var(--kt-ai-fg-faint); margin-top: 5px; font-family: var(--kt-ai-font-mono); }
|
||
.kfu-sources { width: 100%; border-collapse: collapse; font-size: var(--kt-ai-text-xs); }
|
||
.kfu-sources td { padding: 5px 0; border-bottom: 1px dashed var(--kt-ai-divider); vertical-align: top; }
|
||
.kfu-sources td:last-child { text-align: right; font-family: var(--kt-ai-font-mono); color: var(--kt-ai-fg-muted); white-space: nowrap; padding-left: 12px; }
|
||
.kfu-scenario-lbl { font-size: var(--kt-ai-text-sm); color: var(--kt-ai-fg-muted); margin-bottom: 10px; }
|
||
.kfu-scenario-row { display: flex; gap: 10px; align-items: stretch; }
|
||
.kfu-scenario-row .kt-ai-input { flex: 1; font-variant-numeric: tabular-nums; }
|
||
.kfu-delta { font-size: var(--kt-ai-text-sm); margin-top: 10px; padding: 10px 12px; border-radius: var(--vibe-card-radius);
|
||
background: var(--kt-ai-status-info-bg); color: var(--kt-ai-status-info-fg); font-variant-numeric: tabular-nums; }
|
||
.kfu-delta b { font-weight: 700; }
|
||
.kfu-drawer-actions { display: flex; gap: var(--kt-ai-space-3); margin-top: var(--kt-ai-space-6); flex-wrap: wrap; }
|
||
.kfu-drawer-actions .kt-ai-btn { flex: 1; }
|
||
|
||
.kfu-mock-banner { display: flex; align-items: center; gap: var(--kt-ai-space-4); padding: 10px 16px; margin-bottom: var(--kt-ai-space-5);
|
||
border-radius: var(--vibe-card-radius); background: var(--kt-ai-chip-default-bg); color: var(--kt-ai-fg); font-size: var(--kt-ai-text-sm); }
|
||
.kfu-mock-banner button { margin-left: auto; }
|
||
|
||
.kfu-toast-stack { position: fixed; bottom: 20px; right: 20px; z-index: 200; display: flex; flex-direction: column; gap: 8px; }
|
||
.kfu-toast { padding: 10px 16px; border-radius: 10px; background: var(--kt-ai-fg); color: var(--kt-ai-bg-elevated);
|
||
font-size: var(--kt-ai-text-sm); box-shadow: var(--kt-ai-card-shadow-hover); display: flex; align-items: center; gap: 8px;
|
||
animation: kfuToastIn .15s ease-out; }
|
||
.kfu-toast.err { background: var(--kt-ai-status-risk-fg); }
|
||
@keyframes kfuToastIn { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||
|
||
.kfu-footnote { margin-top: var(--kt-ai-space-5); font-size: 11px; color: var(--kt-ai-fg-faint); }
|
||
|
||
@media (max-width: 860px) {
|
||
.kfu-shell { grid-template-columns: 1fr; }
|
||
.kt-ai-sidebar { display: none; }
|
||
.kfu-content { padding: var(--kt-ai-space-5); }
|
||
.kfu-metrics { grid-template-columns: 1fr; }
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="kt-ai-app">
|
||
<div class="kfu-shell">
|
||
|
||
<aside class="kt-ai-sidebar">
|
||
<div class="kt-ai-brand-row">
|
||
<div style="width:28px;height:28px;border-radius:8px;background:var(--kt-ai-primary);color:var(--kt-ai-fg-on-fill);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px">V</div>
|
||
<div style="line-height:1.15">
|
||
<strong style="font-size:13px">Vibe AI</strong><br>
|
||
<span style="font-size:11px;color:var(--kt-ai-fg-muted)">КФУ · финансовая устойчивость</span>
|
||
</div>
|
||
</div>
|
||
<nav>
|
||
<div class="kt-ai-nav-group">Разделы</div>
|
||
<a class="kt-ai-nav-item" data-active="true" href="#top">
|
||
<svg class="kt-icon" aria-hidden="true"><use href="#dashboard"></use></svg>
|
||
<span>Анализ факта 2025</span>
|
||
</a>
|
||
</nav>
|
||
<div style="margin-top:auto">
|
||
<div class="kt-ai-nav-group">Правила</div>
|
||
<div style="padding:4px 8px;font-size:11px;color:var(--kt-ai-fg-faint);line-height:1.55">
|
||
К1 = (долг + прочие) / EBITDA<br>
|
||
К2 = EBITDA / проценты<br>
|
||
К3 = (долг + прочие) / капитал<br>
|
||
<span style="color:var(--kt-ai-status-ok-fg)">●</span> до цели ·
|
||
<span style="color:var(--kt-ai-status-warn-fg)">●</span> до порога ·
|
||
<span style="color:var(--kt-ai-status-risk-fg)">●</span> выше порога
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="kfu-main">
|
||
<header class="kt-ai-topbar" id="top">
|
||
<span style="font-size:var(--kt-ai-text-xs);color:var(--kt-ai-fg-muted)">КФУ · факт 2025</span>
|
||
<div style="flex:1"></div>
|
||
<button class="kt-ai-btn" data-size="sm" id="btnExport" hidden>
|
||
<svg class="kt-icon kt-icon-sm" aria-hidden="true"><use href="#download"></use></svg>
|
||
Выгрузить отчёт .xlsx
|
||
</button>
|
||
</header>
|
||
|
||
<div class="kfu-content">
|
||
<div class="kfu-title-row">
|
||
<h1>Финансовая устойчивость</h1>
|
||
<span class="kt-ai-meta" id="mSource">файл не загружен</span>
|
||
</div>
|
||
|
||
<div class="kfu-actions">
|
||
<label class="dropzone" id="dropzone" title="Выбрать .xlsx с исходными данными">
|
||
<svg class="kt-icon kt-icon-lg" aria-hidden="true"><use href="#upload"></use></svg>
|
||
<span><b>Загрузить .xlsx</b><br><span class="kt-ai-meta">файл остаётся в браузере, на сервер не отправляется</span></span>
|
||
<input type="file" id="fileInput" accept=".xlsx,.xls">
|
||
</label>
|
||
<button class="kt-ai-btn" data-variant="ghost" id="btnSample">
|
||
<svg class="kt-icon kt-icon-sm" aria-hidden="true"><use href="#spreadsheet"></use></svg>
|
||
Учебный пример
|
||
</button>
|
||
</div>
|
||
|
||
<div class="kfu-mock-banner" id="mockBanner" hidden>
|
||
<svg class="kt-icon" aria-hidden="true"><use href="#info"></use></svg>
|
||
<span><b>Учебный пример.</b> Все компании и суммы синтетические, созданы только для демонстрации интерфейса.</span>
|
||
<button class="kt-ai-btn" data-variant="ghost" data-size="sm" id="btnClearMock">Очистить</button>
|
||
</div>
|
||
|
||
<div class="kfu-kpis" id="kpis" hidden></div>
|
||
|
||
<div class="kfu-toolbar" id="toolbar" hidden>
|
||
<div class="kt-ai-search-field">
|
||
<input class="kt-ai-search" id="q" placeholder="Поиск по названию…">
|
||
</div>
|
||
<select class="kt-ai-select" id="zoneFilter">
|
||
<option value="">Все зоны</option>
|
||
<option value="green">Зелёные</option>
|
||
<option value="yellow">Жёлтые</option>
|
||
<option value="red">Красные</option>
|
||
</select>
|
||
<select class="kt-ai-select" id="normFilter">
|
||
<option value="">Нормативы: все</option>
|
||
<option value="with">С нормативом</option>
|
||
<option value="without">Без норматива</option>
|
||
</select>
|
||
<label class="kfu-check-wrap" style="margin-left:auto">
|
||
<span class="kt-ai-checkbox"><input type="checkbox" id="onlyRisk"></span>
|
||
Только красные
|
||
</label>
|
||
</div>
|
||
|
||
<div class="kfu-table-wrap" id="tableWrap" hidden>
|
||
<table class="kfu-table">
|
||
<colgroup>
|
||
<col><col><col><col><col>
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th data-k="name">Объект</th>
|
||
<th>Итог</th>
|
||
<th data-k="k1">К1<span class="th-sub">Debt/EBITDA</span></th>
|
||
<th data-k="k2">К2<span class="th-sub">EBITDA/Int</span></th>
|
||
<th data-k="k3">К3<span class="th-sub">Debt/Equity</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tbody"></tbody>
|
||
</table>
|
||
<div class="kfu-empty" id="emptyState" hidden>
|
||
<svg class="kt-icon" aria-hidden="true"><use href="#search"></use></svg>
|
||
<div>Ничего не найдено. Измените фильтры или сбросьте запрос.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="kfu-footnote" id="footnote"></p>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<aside class="kfu-drawer" id="drawer" role="dialog" aria-modal="false" aria-labelledby="drawerTitle">
|
||
<div id="drawerBody"></div>
|
||
</aside>
|
||
|
||
<div class="kfu-toast-stack" id="toasts"></div>
|
||
|
||
<script src="kfu-client.js"></script>
|
||
</body>
|
||
</html>
|