Files
cloud-ip-validator/internal/dashboard/templates/analytics.html
T
ayurishchevandClaude Sonnet 5.5 068c10ea1c Analytics: compare two finished runs
New page /analytics/compare and API GET /admin/analytics/compare (+ /lists/{group}):
the administrator picks an old (A) and a new (B) run; the report shows the new
addresses (only in B), the ones that left (only in A) and the common ones whose
membership in the seven indicators (pass, partial, fail, egress https any/all,
ingress ssh any/all) differs, with a "what changed" summary per address; the
dynamics of each indicator (delta = new - left + entered - exited) and a verdict
transition matrix. Every number opens a list with CSV. Cancelled addresses are not
part of a run. The list dialog moved to a shared analytics-dialog.js and template;
/analytics got a "compare with another run" button.

Docs, plan and summary in docs/changes/.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 10:26:37 +03:00

123 lines
5.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "analytics_page"}}
<!doctype html>
<html lang="ru">
<head>{{template "html_head" .}}
<link rel="stylesheet" href="/static/analytics.css">
</head>
<body>
<div class="bg-grid"></div>
<input type="checkbox" id="nav-toggle" class="nav-toggle">
<div class="shell">
{{template "sidebar_nav" .}}
<div>
{{template "topbar_mobile" .}}
<main class="main">
<div id="error-banner">{{template "banner_inner" .Banner}}</div>
{{template "analytics_content" .}}
</main>
</div>
</div>
{{if .HasRun}}
<script type="application/json" id="analytics-data">{{.DataJSON}}</script>
<script src="/static/analytics-dialog.js"></script>
<script src="/static/analytics.js"></script>
{{end}}
</body>
</html>
{{end}}
{{define "analytics_content"}}
<div class="an">
<div class="an-head">
<h1 class="an-title">Аналитика</h1>
<p class="an-sub">Один выбранный запуск. Данные других запусков на странице не участвуют.</p>
</div>
{{if not .Runs}}
<section class="an-panel">
<p class="an-note">Запусков проверки пока нет. Они появляются, когда адреса ставятся в очередь на странице <a href="/ips">«Очередь IP»</a> или запускается автоматический цикл.</p>
</section>
{{else}}
<section class="an-panel" aria-label="Выбор запуска">
<div class="an-runbar">
<label for="an-run">Запуск</label>
{{if .PrevURL}}<a class="an-btn" href="{{.PrevURL}}" aria-label="Предыдущий запуск">◀</a>{{else}}<span class="an-btn" aria-disabled="true" style="opacity:.4">◀</span>{{end}}
<select id="an-run" onchange="if (this.value) location.href = '/analytics?run=' + encodeURIComponent(this.value)">
{{range .Runs}}<option value="{{.ID}}"{{if .Selected}} selected{{end}}{{if .Disabled}} disabled{{end}}>{{.Label}}</option>
{{end}}
</select>
{{if .NextURL}}<a class="an-btn" href="{{.NextURL}}" aria-label="Следующий запуск">▶</a>{{else}}<span class="an-btn" aria-disabled="true" style="opacity:.4">▶</span>{{end}}
{{if .HasRun}}<a class="an-btn" href="/analytics/compare?target={{.RunID}}">Сравнить с другим запуском</a>{{end}}
</div>
{{if .HasRun}}<p class="an-note" id="an-runnote"></p>{{else}}<p class="an-note">Завершённых запусков пока нет: данные появятся, когда все адреса запуска получат итог.</p>{{end}}
</section>
{{end}}
{{if .HasRun}}
<div class="an-kpis" id="an-kpis"></div>
<div class="an-cols">
<section class="an-panel" aria-labelledby="an-h-reasons">
<h2 id="an-h-reasons">Почему partial</h2>
<div class="an-rows" id="an-reasons"></div>
<p class="an-note">Адрес считается один раз, по главной причине. Всё, где есть egress, учитывается как egress.</p>
</section>
<section class="an-panel" aria-labelledby="an-h-dq">
<h2 id="an-h-dq">Качество данных</h2>
<div class="an-dq" id="an-dq"></div>
<p class="an-note">Вердикт ставится при агрегации. Результаты, пришедшие позже, остаются в этом же запуске, но в вердикт не входят.</p>
</section>
</div>
<section class="an-panel" aria-labelledby="an-h-sub">
<div class="an-head">
<h2 id="an-h-sub">Подсети</h2>
<div class="an-runbar">
<div class="an-tabs" role="group" aria-label="Сортировка подсетей">
<button type="button" data-sort="worst" aria-pressed="true">хуже всего</button>
<button type="button" data-sort="size" aria-pressed="false">больше адресов</button>
</div>
<button class="an-btn" id="an-allsub" type="button"></button>
</div>
</div>
<div class="an-scroll"><table id="an-subtbl"></table></div>
<p class="an-note">Строка ведёт в «Реестр» с фильтром по запуску и подсети.</p>
</section>
<section class="an-panel" aria-labelledby="an-h-eg">
<div class="an-head">
<h2 id="an-h-eg">Egress по целям</h2>
<div class="an-tabs" role="group" aria-label="Тип проверки" id="an-types"></div>
</div>
<div class="an-rows" id="an-targets"></div>
<div id="an-matrixwrap">
<div class="an-head"><h2 style="margin-top:6px">Подсеть × цель</h2>
<div class="an-legend" id="an-hlegend"></div></div>
<div class="an-scroll"><table class="an-heat" id="an-matrix"></table></div>
</div>
<p class="an-note" id="an-matrixnote"></p>
</section>
<div class="an-cols">
<section class="an-panel" aria-labelledby="an-h-in">
<h2 id="an-h-in">Ingress по площадкам</h2>
<div class="an-scroll"><table id="an-sites"></table></div>
</section>
<section class="an-panel" aria-labelledby="an-h-err">
<h2 id="an-h-err">Классы ошибок ingress</h2>
<div class="an-rows" id="an-errs"></div>
</section>
</div>
<section class="an-panel" aria-labelledby="an-h-val">
<h2 id="an-h-val">Валидаторы: доля провалов egress https</h2>
<div class="an-vals" id="an-vals" role="img" aria-label="Доля проваленных https-проверок по валидаторам"></div>
<div class="an-vlab"><span id="an-vfirst"></span><span id="an-vavg"></span><span id="an-vlast"></span></div>
<p class="an-note">Ровная полоса значит: проблема зависит от подсети адреса, а не от валидатора.</p>
</section>
{{template "analytics_dialog"}}
{{end}}
</div>
{{end}}