Show check-type usage and stats on the target groups dashboard page

Each target group's relationship to the check types that reference it was
previously invisible without cross-referencing /check-types by hand.
handleTargetsPage/renderTargetsTable now resolve that server-side via
loadTargetsPage (group -> referencing check types, enabled or not) and
render it as a status pill per group, plus a stats row (group count,
total targets, check types using them, unused groups) kept live via an
out-of-band swap on every create/update/delete.

Also auto-sizes the targets textarea as you type (targets.html), and
lets the stat-card grid collapse to however many state cards actually
exist instead of leaving empty background where a fixed repeat(6, ...)
had no card to fill.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ayurishchevandClaude Sonnet 5 committed 2026-09-18 10:52:27 +03:00
1 parent 95f8066eed
commit 792b7fbb84
4 files changed
+244 -18

No files matched your search

+51 -3
View File
@@ -264,7 +264,7 @@ nav.nav-groups { display: flex; flex-direction: column; gap: 1px; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
.main { padding: 22px 26px 60px; max-width: 1180px; }
.main { padding: 22px 26px 60px; max-width: 1180px; margin-inline: auto; }
.topbar {
display: flex; align-items: baseline; justify-content: space-between;
margin-bottom: 18px; flex-wrap: wrap; gap: 10px;
@@ -273,7 +273,13 @@ nav.nav-groups { display: flex; flex-direction: column; gap: 1px; }
/* ---------- stat cards ---------- */
.stat-grid {
display: grid; grid-template-columns: repeat(6, 1fr);
/* auto-fit instead of a fixed repeat(6, ...): the state histogram this
drives is a variable-length map (whichever ip_queue states are
actually present), so a fixed column count left visible empty
background showing through the unused grid tracks whenever fewer
than 6 cards exist. auto-fit collapses empty tracks and lets the
real cards stretch to fill the row instead. */
display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1px; background: var(--border);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
@@ -475,7 +481,6 @@ code.inline { font-family: var(--font-mono); background: var(--surface-alt); bor
display: grid; place-items: center; cursor: pointer; flex-shrink: 0;
}
.main { padding: 16px 14px 50px; }
.stat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
@@ -496,3 +501,46 @@ code.inline { font-family: var(--font-mono); background: var(--surface-alt); bor
}
.actions { justify-content: flex-start; margin-top: 6px; }
}
/* ---------- targets page: group summary + usage ---------- */
/* 4-card variant of .stat-grid for pages whose summary row isn't the
dynamic ips_by_state histogram (which wants 6 columns). */
.stat-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .stat-grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
/* Neutral data token for listing individual targets inline — distinct
from .pill (which always carries a status meaning via its color + dot):
a chip is just a value, not a state. */
.chip-list { display: flex; flex-wrap: wrap; gap: 5px; max-width: 460px; }
.chip {
font-family: var(--font-mono);
font-size: 11.5px;
padding: 2px 7px;
border-radius: var(--radius-xs);
background: var(--surface-alt);
border: 1px solid var(--border-soft);
color: var(--text-muted);
white-space: nowrap;
}
/* Row group name + target count, stacked. */
.group-name { display: flex; flex-direction: column; gap: 2px; }
.group-name .count { font-size: 11px; color: var(--text-faint); font-weight: 400; text-transform: none; letter-spacing: 0; font-family: var(--font-ui); }
/* "Used by" cell — wraps its pills with a small gap, same idiom as
chip-list, kept separate since these ARE status-colored .pill elements. */
.used-by { display: flex; flex-wrap: wrap; gap: 4px; }
/* Inline edit row, toggled open by the "Изменить" button — collapses by
default so the table stays scannable instead of showing N always-open
textareas at once. */
tr.edit-row td { background: var(--surface-alt); padding: 14px 16px; }
tr.edit-row textarea { flex: 1 1 auto; }
/* Auto-growing textarea (new-group form + per-row edit forms above) —
stretches with content instead of showing an internal scrollbar
immediately; only caps out (and starts scrolling) past ~20 lines.
Height is set by JS (see templates/targets.html), computed from the
field's own line-height, so it stays correct if font-size ever changes. */
textarea.autosize { overflow-y: hidden; resize: none; }