fix(ui): full-row host click, agent version label on host card

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-05 10:20:32 +10:00
parent adfc69c656
commit d9893497ce
3 changed files with 36 additions and 33 deletions
+14 -17
View File
@@ -17,29 +17,26 @@ a {
color: #7eb8ff; color: #7eb8ff;
} }
/* Клик по хосту в таблице — как пункты левого меню (полоска слева, без подчёркивания). */ /* Клик по строке хоста — как пункты левого меню (полоска слева на всю строку). */
.sac-host-nav-link { .sac-host-row {
display: inline-block;
padding: 0.2rem 0.45rem 0.2rem 0.3rem;
margin: -0.2rem -0.35rem;
border-radius: 4px;
border-left: 3px solid transparent;
color: #c5d0dc;
text-decoration: none;
cursor: pointer; cursor: pointer;
transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease; transition: background 0.12s ease, box-shadow 0.12s ease;
} }
.sac-host-nav-link:hover { .sac-host-row:hover,
.sac-host-row:focus-visible {
background: #1e2d3d; background: #1e2d3d;
color: #fff; box-shadow: inset 3px 0 0 #3db8ff;
border-left-color: #3db8ff; outline: none;
} }
.sac-host-nav-link.router-link-active { .sac-host-row td.actions {
background: #1a3a52; cursor: default;
color: #fff; }
border-left-color: #3db8ff;
.sac-host-row:hover td.actions,
.sac-host-row:focus-visible td.actions {
background: transparent;
} }
.layout { .layout {
+2 -3
View File
@@ -8,11 +8,10 @@
<p> <p>
<strong>Hostname:</strong> {{ host.hostname }} <strong>Hostname:</strong> {{ host.hostname }}
· <strong>ID:</strong> {{ host.id }} · <strong>ID:</strong> {{ host.id }}
· <strong>Агент:</strong> · <strong>Агент:</strong> <span :class="'agent-' + host.agent_status">{{ agentLabel(host.agent_status) }}</span>
<span :class="'agent-' + host.agent_status">{{ agentLabel(host.agent_status) }}</span>
</p> </p>
<p> <p>
<strong>Product:</strong> {{ host.product }} {{ host.product_version || "" }} <strong>Версия агента:</strong> {{ host.product_version || "" }}
· <strong>OS:</strong> {{ host.os_family }} {{ host.os_version || "" }} · <strong>OS:</strong> {{ host.os_family }} {{ host.os_version || "" }}
· <strong>IPv4:</strong> {{ host.ipv4 || "—" }} · <strong>IPv4:</strong> {{ host.ipv4 || "—" }}
</p> </p>
+20 -13
View File
@@ -11,7 +11,7 @@
<p v-else-if="loading">Загрузка</p> <p v-else-if="loading">Загрузка</p>
<template v-else> <template v-else>
<p>Всего: {{ data?.total ?? 0 }}</p> <p>Всего: {{ data?.total ?? 0 }}</p>
<table> <table class="hosts-table">
<thead> <thead>
<tr> <tr>
<th><button type="button" class="th-sort" @click="setSort('id')">ID {{ sortMark('id') }}</button></th> <th><button type="button" class="th-sort" @click="setSort('id')">ID {{ sortMark('id') }}</button></th>
@@ -29,24 +29,24 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="h in sortedItems" :key="h.id"> <tr
v-for="h in sortedItems"
:key="h.id"
class="sac-host-row"
tabindex="0"
:title="`Карточка хоста ${h.hostname}`"
@click="openHost(h.id)"
@keydown.enter="openHost(h.id)"
>
<td>{{ h.id }}</td> <td>{{ h.id }}</td>
<td> <td>{{ h.display_name || h.hostname }}</td>
<RouterLink
:to="`/hosts/${h.id}`"
class="sac-host-nav-link"
:title="`Карточка хоста ${h.hostname}`"
>
{{ h.display_name || h.hostname }}
</RouterLink>
</td>
<td>{{ h.hostname }}</td> <td>{{ h.hostname }}</td>
<td>{{ h.product_version || "—" }}</td> <td>{{ h.product_version || "—" }}</td>
<td>{{ h.os_family }}</td> <td>{{ h.os_family }}</td>
<td>{{ h.ipv4 || "—" }}</td> <td>{{ h.ipv4 || "—" }}</td>
<td :class="'agent-' + h.agent_status">{{ agentLabel(h.agent_status) }}</td> <td :class="'agent-' + h.agent_status">{{ agentLabel(h.agent_status) }}</td>
<td>{{ h.last_heartbeat_at ? formatDt(h.last_heartbeat_at) : "—" }}</td> <td>{{ h.last_heartbeat_at ? formatDt(h.last_heartbeat_at) : "—" }}</td>
<td> <td @click.stop>
<RouterLink <RouterLink
v-if="h.last_daily_report_at" v-if="h.last_daily_report_at"
:to="{ path: '/reports', query: { hostname: h.hostname } }" :to="{ path: '/reports', query: { hostname: h.hostname } }"
@@ -57,7 +57,7 @@
</td> </td>
<td>{{ formatDt(h.last_seen_at) }}</td> <td>{{ formatDt(h.last_seen_at) }}</td>
<td>{{ h.event_count ?? 0 }}</td> <td>{{ h.event_count ?? 0 }}</td>
<td class="actions"> <td class="actions" @click.stop>
<button <button
type="button" type="button"
class="danger" class="danger"
@@ -76,8 +76,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { apiFetch, type HostListResponse, type HostSummary } from "../api"; import { apiFetch, type HostListResponse, type HostSummary } from "../api";
const router = useRouter();
const data = ref<HostListResponse | null>(null); const data = ref<HostListResponse | null>(null);
const loading = ref(false); const loading = ref(false);
const error = ref(""); const error = ref("");
@@ -147,6 +150,10 @@ function agentLabel(status: string) {
return "unknown"; return "unknown";
} }
function openHost(id: number) {
router.push(`/hosts/${id}`);
}
function agentStatusOrder(status: string) { function agentStatusOrder(status: string) {
if (status === "online") return 0; if (status === "online") return 0;
if (status === "stale") return 1; if (status === "stale") return 1;