From 615c256f8b08cabe854abc0642e90794b839f68a Mon Sep 17 00:00:00 2001 From: benya Date: Thu, 4 Jun 2026 20:42:40 +0300 Subject: [PATCH] Improve device information UX --- UI_CHECKLIST.md | 14 +++--- web/app.js | 40 +++++++++++++++++ web/index.html | 95 ++++++++++++++++++++++++++++++++++++---- web/styles.css | 112 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 245 insertions(+), 16 deletions(-) diff --git a/UI_CHECKLIST.md b/UI_CHECKLIST.md index f0166cf..eb4ce82 100644 --- a/UI_CHECKLIST.md +++ b/UI_CHECKLIST.md @@ -114,13 +114,13 @@ ## Этап 5. Сведения -- [ ] Показать модель, плату и версию OpenWrt. -- [ ] Показать hostname и идентификатор устройства. -- [ ] Показать группу и теги. -- [ ] Добавить редактирование группы и тегов. -- [ ] Показать даты регистрации и последнего контакта. +- [x] Показать модель, плату и версию OpenWrt. +- [x] Показать hostname и идентификатор устройства. +- [x] Показать группу и теги. +- [x] Добавить редактирование группы и тегов. +- [x] Показать даты регистрации и последнего контакта. - [ ] Показать версию агента, когда она будет доступна. -- [ ] Добавить копирование технических идентификаторов. +- [x] Добавить копирование технических идентификаторов. ## Этап 5. Экспертный режим @@ -132,7 +132,7 @@ - [ ] Сократить основные фильтры истории до «Все», «Выполняются», «Ошибка». - [ ] Перенести дополнительные фильтры в меню. - [x] Перенести аудит. -- [ ] Добавить предупреждение перед опасными действиями. +- [x] Добавить предупреждение перед опасными действиями. - [ ] Подготовить экспертный режим к разграничению прав. ## Убираем и объединяем diff --git a/web/app.js b/web/app.js index d72973a..1d6aa5d 100644 --- a/web/app.js +++ b/web/app.js @@ -43,6 +43,18 @@ const els = { deviceName: document.querySelector("#deviceName"), deviceMeta: document.querySelector("#deviceMeta"), deviceBadge: document.querySelector("#deviceBadge"), + informationStatus: document.querySelector("#informationStatus"), + infoHostname: document.querySelector("#infoHostname"), + infoModel: document.querySelector("#infoModel"), + infoOpenWrt: document.querySelector("#infoOpenWrt"), + infoTarget: document.querySelector("#infoTarget"), + infoSystem: document.querySelector("#infoSystem"), + infoKernel: document.querySelector("#infoKernel"), + infoCreatedAt: document.querySelector("#infoCreatedAt"), + infoLastSeenAt: document.querySelector("#infoLastSeenAt"), + infoDeviceId: document.querySelector("#infoDeviceId"), + infoBoardName: document.querySelector("#infoBoardName"), + infoRootfs: document.querySelector("#infoRootfs"), healthSummary: document.querySelector("#healthSummary"), lastSeen: document.querySelector("#lastSeen"), loadAvg: document.querySelector("#loadAvg"), @@ -424,11 +436,30 @@ function renderDeviceDetail(device) { els.fleetGroup.value = device.group || ""; els.fleetTags.value = Array.isArray(device.tags) ? device.tags.join(", ") : ""; els.inventoryJson.textContent = JSON.stringify(device.inventory || {}, null, 2); + renderDeviceInformation(device); renderHealthSummary(device); renderClients(device); renderInterfaceCounters(device); } +function renderDeviceInformation(device) { + const board = device.inventory && device.inventory.board ? device.inventory.board : {}; + const release = board.release || {}; + els.informationStatus.textContent = device.online ? "На связи" : "Не на связи"; + els.informationStatus.className = `badge ${device.online ? "online" : "offline"}`; + els.infoHostname.textContent = deviceDisplayName(device); + els.infoModel.textContent = board.model || "-"; + els.infoOpenWrt.textContent = release.description || device.openwrt_version || "-"; + els.infoTarget.textContent = release.target || "-"; + els.infoSystem.textContent = board.system || "-"; + els.infoKernel.textContent = board.kernel || "-"; + els.infoCreatedAt.textContent = formatDate(device.created_at); + els.infoLastSeenAt.textContent = formatDate(device.last_seen_at); + els.infoDeviceId.textContent = device.id || "-"; + els.infoBoardName.textContent = board.board_name || "-"; + els.infoRootfs.textContent = board.rootfs_type || "-"; +} + function formatMemory(memory) { if (!memory || !memory.total_kb) return "-"; return `${kbToMb(memory.used_kb)} / ${kbToMb(memory.total_kb)} MB`; @@ -1224,6 +1255,15 @@ els.copyCommandOutputBtn.addEventListener("click", async () => { setStatus("Output copied"); }); +for (const button of document.querySelectorAll(".copy-info-btn")) { + button.addEventListener("click", async () => { + const source = document.querySelector(`#${button.dataset.copyInfo}`); + if (!source) return; + await navigator.clipboard.writeText(source.textContent); + setStatus("Значение скопировано"); + }); +} + for (const button of document.querySelectorAll(".preset-btn")) { button.addEventListener("click", () => { sendPresetCommand(button.dataset.preset, button.dataset.action).catch((error) => setStatus(error.message)); diff --git a/web/index.html b/web/index.html index 6f9765a..c6ed061 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,7 @@ OpenWrt RMM - +
@@ -173,23 +173,95 @@
+
+
+
+

Сведения об устройстве

+

Основная информация, полученная от роутера

+
+ - +
+
+
+ Имя устройства + - +
+
+ Модель + - +
+
+ Версия OpenWrt + - +
+
+ Платформа + - +
+
+ Система + - +
+
+ Версия ядра + - +
+
+ Зарегистрирован + - +
+
+ Последний контакт + - +
+
+
+
-

Группа и теги

- +
+

Организация

+

Группа и теги помогают находить и фильтровать объекты

+
+
+
+
+
+

Технические реквизиты

+

Используются при диагностике и обращении в поддержку

+
+
+
+
+ ID устройства + - + +
+
+ Board name + - + +
+
+ Root filesystem + - +
+
+
+
Последний контакт @@ -229,6 +301,13 @@
+
+
+ Экспертный режим + Здесь находятся низкоуровневые команды и настройки OpenWrt. Ошибочное изменение может нарушить связь с роутером. +
+
+
@@ -552,6 +631,6 @@
- + diff --git a/web/styles.css b/web/styles.css index 06eea36..953c824 100644 --- a/web/styles.css +++ b/web/styles.css @@ -1134,6 +1134,100 @@ select { display: none; } +.information-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + border: 1px solid var(--line); + border-radius: 8px; + overflow: hidden; +} + +.information-item { + display: grid; + gap: 6px; + min-height: 82px; + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + background: var(--surface-raised); + padding: 14px; +} + +.information-item:nth-child(4n) { + border-right: 0; +} + +.information-item:nth-last-child(-n + 4) { + border-bottom: 0; +} + +.information-item span, +.technical-details span { + color: var(--muted); + font-size: 12px; +} + +.information-item strong { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.technical-details { + display: grid; + gap: 8px; +} + +.technical-details > div { + display: grid; + grid-template-columns: 160px minmax(0, 1fr) auto; + gap: 12px; + align-items: center; + min-height: 48px; + border-bottom: 1px solid var(--line); + padding: 4px 0; +} + +.technical-details > div:last-child { + border-bottom: 0; +} + +.technical-details code { + overflow: hidden; + color: var(--text); + text-overflow: ellipsis; + white-space: nowrap; +} + +.copy-info-btn { + min-height: 30px; + color: var(--accent); +} + +.expert-notice { + display: flex; + gap: 12px; + align-items: center; + border: 1px solid #715d31; + border-radius: 8px; + background: #342e22; + margin-bottom: 14px; + padding: 14px; +} + +.expert-notice div { + display: grid; + gap: 4px; +} + +.expert-notice strong { + color: var(--warn); +} + +.expert-notice span { + color: var(--muted); + font-size: 13px; +} + .metric { min-width: 0; border: 1px solid var(--line); @@ -1400,7 +1494,23 @@ select { } .client-toolbar, - .network-health { + .network-health, + .information-grid { + grid-template-columns: 1fr; + } + + .information-item, + .information-item:nth-child(4n), + .information-item:nth-last-child(-n + 4) { + border-right: 0; + border-bottom: 1px solid var(--line); + } + + .information-item:last-child { + border-bottom: 0; + } + + .technical-details > div { grid-template-columns: 1fr; }