2026-09-23 12:13:23 +03:00
{{define "overview_stats"}}
2026-08-23 21:58:19 +03:00
< div class = "stat-grid" >
< div class = "stat-card accented" >< span class = "value" > {{.Status.TotalIPs}}</ span >< span class = "label" > всего IP</ span ></ div >
< div class = "stat-card" >< span class = "value" > {{.Status.TotalValidators}}</ span >< span class = "label" > валидаторов</ span ></ div >
2026-08-23 20:39:22 +03:00
{{range $state, $count := .Status.IPsByState}}
2026-08-26 22:19:42 +03:00
< div class = "stat-card{{if eq $state " failed "}} bad {{ else if eq $ state " checking "}} accented {{ end }}" >< span class = "value" > {{$count}}</ span >< span class = "label" > {{$state}}</ span ></ div >
2026-08-23 20:39:22 +03:00
{{end}}
</ div >
2026-10-01 19:31:11 +03:00
{{if .Progress.Total}}
< div class = "overall-progress" id = "overview-progress" >
< p > Готово {{.Progress.Done}} из {{.Progress.Total}} ({{.Progress.Percent}}%) · в работе {{.Progress.Active}} · в очереди {{.Progress.Queued}}{{if .Progress.ETA}} · осталось ≈ {{.Progress.ETA}}{{end}}</ p >
< progress max = "{{.Progress.Total}}" value = "{{.Progress.Done}}" aria-label = "Готово" ></ progress >
</ div >
{{end}}
{{with .Status.ResultsByOverall}}{{if or (index . "pass") (index . "partial") (index . "fail") (index . "cancelled")}}
< p class = "muted" id = "overview-results" style = "margin-top:8px" > Итоги проверок: pass {{index . "pass"}} · partial {{index . "partial"}} · fail {{index . "fail"}} · cancelled {{index . "cancelled"}}</ p >
{{end}}{{end}}
{{with .Scan}}
< p class = "muted" id = "overview-scan" style = "margin-top:8px" > Сканирование: {{.StateLabel}}{{if .DryRun}} (пробное){{end}} · прочитано страниц {{.Pages}}, найдено адресов {{.Discovered}}, свободных {{.Free}}{{if .Handled}}, обработано {{.Handled}}{{end}} · < a href = "/ips" > подробнее</ a ></ p >
{{end}}
2026-10-01 10:28:53 +03:00
{{with .AutoCycle}}{{if .Enabled}}
< p class = "muted" id = "overview-auto-cycle" style = "margin-top:12px" >< span class = "pill pill-info" > Автоцикл активен</ span > · {{.PhaseLabel}} · следующий запуск: {{fmtTime .NextRunAt}}</ p >
{{end}}{{end}}
2026-09-23 12:13:23 +03:00
{{end}}
2026-08-23 20:39:22 +03:00
2026-09-23 12:13:23 +03:00
{{/* Out-of-band counterpart of overview_stats, appended to every
/overview/fragment response alongside overview_tables (the actual
hx-target) — same OOB idiom as templates/layout.html's error_banner.
Needed because overview_stats lives outside the polled #overview-tables
div (see overview.html) so the DOM order can be stats -> filter form ->
tables, yet the stat counts still refresh on every poll tick. */}}
{{define "overview_stats_oob"}}< div id = "overview-stats" hx-swap-oob = "true" > {{template "overview_stats" .}}</ div > {{end}}
{{define "overview_tables"}}
2026-10-01 19:31:11 +03:00
< h2 class = "section-title" > В работе</ h2 >
{{if .ActiveItems}}
2026-08-23 21:58:19 +03:00
< div class = "panel" >
< div class = "table-scroll" >
< table >
2026-08-23 20:39:22 +03:00
< thead >< tr >< th > Адрес</ th >< th > Состояние</ th >< th > Валидатор</ th >< th > Попытка</ th >< th > Назначено</ th ></ tr ></ thead >
< tbody >
2026-10-01 19:31:11 +03:00
{{range .ActiveItems}}
2026-08-24 10:29:08 +03:00
{{$b := ipBadge .State .OverallResult .FIPAssociatedAt 0}}
2026-08-23 20:39:22 +03:00
< tr >
2026-08-23 21:58:19 +03:00
< td class = "addr" data-label = "Адрес" >< a href = "/ips/{{.IPAddress}}" > {{.IPAddress}}</ a ></ td >
< td data-label = "Состояние" >< span class = "pill {{$b.Class}}" > {{$b.Label}}</ span ></ td >
< td data-label = "Валидатор" > {{deref .OwnerValidatorID}}</ td >
< td class = "num" data-label = "Попытка" > {{.AttemptNumber}}</ td >
< td class = "num" data-label = "Назначено" > {{fmtTime .AssignedAt}}</ td >
2026-08-23 20:39:22 +03:00
</ tr >
{{end}}
</ tbody >
</ table >
2026-08-23 21:15:31 +03:00
</ div >
2026-08-23 21:58:19 +03:00
</ div >
2026-10-01 19:31:11 +03:00
{{if gt .ActiveTotal (len .ActiveItems)}}< p class = "muted" > Показано {{len .ActiveItems}} из {{.ActiveTotal}} · < a href = "/ips?state=active{{if .Query}}&q={{.Query}}{{end}}" > все в работе</ a ></ p > {{end}}
2026-08-23 20:39:22 +03:00
{{else}}
2026-09-23 11:58:53 +03:00
{{if or .Query .StatusFilter}}< p class = "muted" > Ничего не найдено по текущему фильтру.</ p >
{{else}}< p class = "muted" > Сейчас нет адресов в обработке.</ p > {{end}}
2026-08-23 20:39:22 +03:00
{{end}}
2026-10-01 19:31:11 +03:00
{{if not .StatusFilter}}
< p class = "queue-line" > В очереди: < b > {{.QueuedTotal}}</ b > · < a href = "/ips?state=queued{{if .Query}}&q={{.Query}}{{end}}" > открыть весь список</ a ></ p >
{{if .QueuedItems}}
< div class = "panel" >
< div class = "table-scroll" >
< table class = "compact" >
< thead >< tr >< th > Ближайшие в очереди</ th >< th > Состояние</ th ></ tr ></ thead >
< tbody >
{{range .QueuedItems}}
{{$b := ipBadge .State .OverallResult .FIPAssociatedAt 0}}
< tr >
< td class = "addr" data-label = "Адрес" >< a href = "/ips/{{.IPAddress}}" > {{.IPAddress}}</ a ></ td >
< td data-label = "Состояние" >< span class = "pill {{$b.Class}}" > {{$b.Label}}</ span ></ td >
</ tr >
{{end}}
</ tbody >
</ table >
</ div >
</ div >
{{if gt .QueuedTotal (len .QueuedItems)}}< p class = "muted" > Показаны ближайшие {{len .QueuedItems}} из {{.QueuedTotal}}.</ p > {{end}}
{{end}}
{{end}}
2026-08-23 21:58:19 +03:00
< h2 class = "section-title" > Последние {{.LastN}} завершённых</ h2 >
< div class = "breakdown" >
< span > pass < b > {{index .Breakdown "pass"}}</ b ></ span >
< span > partial < b > {{index .Breakdown "partial"}}</ b ></ span >
< span > fail < b > {{index .Breakdown "fail"}}</ b ></ span >
< span > cancelled < b > {{index .Breakdown "cancelled"}}</ b ></ span >
</ div >
2026-08-23 20:39:22 +03:00
{{if .LastCompleted}}
2026-08-23 21:58:19 +03:00
< div class = "panel" >
< div class = "table-scroll" >
< table >
2026-08-23 20:39:22 +03:00
< thead >< tr >< th > Адрес</ th >< th > Итог</ th >< th > Завершено</ th ></ tr ></ thead >
< tbody >
{{range .LastCompleted}}
2026-08-24 10:29:08 +03:00
{{$b := ipBadge .State .OverallResult .FIPAssociatedAt 0}}
2026-08-23 20:39:22 +03:00
< tr >
2026-08-23 21:58:19 +03:00
< td class = "addr" data-label = "Адрес" >< a href = "/ips/{{.IPAddress}}" > {{.IPAddress}}</ a ></ td >
< td data-label = "Итог" >< span class = "pill {{$b.Class}}" > {{$b.Label}}</ span ></ td >
< td class = "num" data-label = "Завершено" > {{fmtTime .AggregatedAt}}</ td >
2026-08-23 20:39:22 +03:00
</ tr >
{{end}}
</ tbody >
</ table >
2026-08-23 21:15:31 +03:00
</ div >
2026-08-23 21:58:19 +03:00
</ div >
2026-08-23 20:39:22 +03:00
{{else}}
2026-09-23 11:58:53 +03:00
{{if or .Query .StatusFilter}}< p class = "muted" > Ничего не найдено по текущему фильтру.</ p >
{{else}}< p class = "muted" > Пока ничего не завершено.</ p > {{end}}
2026-08-23 20:39:22 +03:00
{{end}}
{{end}}