Fleet
-Devices
+Управление
+Объекты
+No device selected
-Waiting for agent check-in.
+Объекты не найдены
+Измените фильтры или дождитесь подключения агента.
+Device
@@ -441,6 +491,6 @@diff --git a/UI_CHECKLIST.md b/UI_CHECKLIST.md new file mode 100644 index 0000000..7cac3bc --- /dev/null +++ b/UI_CHECKLIST.md @@ -0,0 +1,190 @@ +# UI/UX Checklist + +Обозначения: + +- `[x]` — уже реализовано. +- `[ ]` — требуется сделать. +- `[~]` — реализовано частично, требуется переработка. + +## Текущее состояние + +- [x] Авторизация оператора. +- [x] Список устройств. +- [x] Просмотр состояния устройства. +- [x] Метрики и предупреждения. +- [x] DHCP- и Wi-Fi-клиенты. +- [x] Команды и история выполнения. +- [x] UCI-конфигурация и staged apply. +- [x] Управление пакетами. +- [x] Удаленный SSH-доступ и LuCI. +- [x] Аудит действий. +- [~] Обзор устройства понятен оператору. +- [~] История метрик пригодна для быстрого анализа. +- [~] Удаленный доступ запускается простым сценарием. +- [ ] Основные и экспертные функции визуально разделены. + +## Этап 0. UX-аудит + +- [ ] Составить полный список текущих экранов, панелей и кнопок. +- [ ] Для каждого элемента назначить действие: оставить, перенести, объединить или скрыть. +- [ ] Зафиксировать терминологию для оператора. +- [ ] Зафиксировать набор статусов и цветов. +- [ ] Определить основные пользовательские сценарии. +- [ ] Определить структуру навигации и маршруты страниц. +- [ ] Определить desktop и mobile макеты. + +## Этап 1. Каркас и объекты + +- [x] Добавить постоянную основную навигацию. +- [x] Добавить верхнюю панель с глобальным обновлением. +- [x] Создать полноширинную таблицу объектов. +- [x] Добавить статус объекта с текстом и индикатором. +- [x] Показывать имя, модель, группу, WAN, клиентов, проблемы и последний контакт. +- [x] Добавить поиск по имени, модели, ID, WAN-адресу, группе и тегам. +- [x] Добавить фильтр по статусу. +- [x] Добавить фильтр по группе. +- [x] Добавить фильтр по наличию проблем. +- [ ] Добавить сортировку колонок. +- [x] Открывать объект кликом по строке. +- [ ] Показывать массовые действия только после выбора объектов. +- [ ] Добавить loading, empty и error states таблицы. +- [ ] Проверить таблицу на 1366 px и 1920 px. +- [ ] Сделать мобильное представление списка объектов. + +## Этап 2. Карточка и обзор объекта + +- [ ] Создать верхнюю панель объекта. +- [ ] Добавить хлебную крошку возврата к объектам. +- [ ] Добавить вкладки «Обзор», «Клиенты», «Сеть», «Операции», «Сведения», «Эксперт». +- [ ] Показать статус и время последнего контакта. +- [ ] Показать WAN и доступность сервера. +- [ ] Показать CPU/load, память, uptime и число клиентов. +- [ ] Убрать дублирующиеся метрики. +- [ ] Показать активные предупреждения с приоритетами. +- [ ] Добавить компактные графики метрик. +- [ ] Добавить выбор периода графиков. +- [ ] Добавить основную кнопку открытия LuCI. +- [ ] Добавить кнопку запуска диагностики. +- [ ] Перенести перезагрузку и опасные действия в отдельное меню. +- [ ] Убрать raw inventory с обзорной страницы. +- [ ] Убрать произвольные команды с обзорной страницы. +- [ ] Убрать редактирование группы и тегов с обзорной страницы. + +## Этап 3. Клиенты + +- [ ] Объединить DHCP leases и Wi-Fi stations в единую модель клиента. +- [ ] Создать таблицу клиентов. +- [ ] Показывать online/offline состояние. +- [ ] Показывать имя, IP, MAC и тип подключения. +- [ ] Для Wi-Fi показывать сеть, сигнал и скорость. +- [ ] Для Ethernet показывать интерфейс или порт, если доступно. +- [ ] Добавить поиск по имени, IP и MAC. +- [ ] Добавить фильтры по состоянию и типу подключения. +- [ ] Добавить понятное состояние отсутствия клиентов. +- [ ] Проверить длинные имена и неизвестные значения. + +## Этап 3. Сеть + +- [ ] Создать сводку WAN-подключения. +- [ ] Показывать состояние интернет-доступа и внешний IP. +- [ ] Создать список LAN/WAN/Wi-Fi/VPN интерфейсов. +- [ ] Показывать адреса, состояние и счетчики трафика. +- [ ] Показать основные маршруты. +- [ ] Показывать Wi-Fi сети и подключенных клиентов. +- [ ] Спрятать низкоуровневые детали в раскрываемые блоки. +- [ ] Добавить loading, empty и error states. + +## Этап 4. Операции + +- [ ] Объединить отдельные диагностические кнопки в один сценарий. +- [ ] Показывать прогресс диагностики. +- [ ] Показывать итог диагностики понятным языком. +- [ ] Добавить рекомендации по найденным проблемам. +- [ ] Упростить создание удаленной сессии до выбора длительности. +- [ ] Спрятать host, порты и параметры туннеля в дополнительные настройки. +- [ ] Добавить действия «Открыть LuCI» и «Скопировать SSH». +- [ ] Показывать оставшееся время удаленной сессии. +- [ ] Добавить понятное закрытие удаленной сессии. +- [ ] Объединить Preview и Stage в сценарий «Проверить → Применить». +- [ ] Показывать изменения перед применением. +- [ ] Понятно объяснять commit confirmed и автоматический откат. +- [ ] Добавить безопасные пресеты настроек. +- [ ] Перенести низкоуровневое управление пакетами в экспертный режим. +- [ ] Добавить подтверждение опасных операций. + +## Этап 5. Сведения + +- [ ] Показать модель, плату и версию OpenWrt. +- [ ] Показать hostname и идентификатор устройства. +- [ ] Показать группу и теги. +- [ ] Добавить редактирование группы и тегов. +- [ ] Показать даты регистрации и последнего контакта. +- [ ] Показать версию агента, когда она будет доступна. +- [ ] Добавить копирование технических идентификаторов. + +## Этап 5. Экспертный режим + +- [ ] Перенести raw inventory. +- [ ] Перенести произвольные команды. +- [ ] Перенести raw UCI show/set/commit/revert. +- [ ] Перенести низкоуровневое управление пакетами. +- [ ] Перенести полную историю команд. +- [ ] Сократить основные фильтры истории до «Все», «Выполняются», «Ошибка». +- [ ] Перенести дополнительные фильтры в меню. +- [ ] Перенести аудит. +- [ ] Добавить предупреждение перед опасными действиями. +- [ ] Подготовить экспертный режим к разграничению прав. + +## Убираем и объединяем + +- [ ] Убрать локальные Reload / Refresh из отдельных панелей. +- [ ] Добавить автообновление данных. +- [x] Оставить одну глобальную кнопку обновления на экране объектов. +- [x] Убрать постоянный выбор массовой команды. +- [ ] Показывать массовые действия только при выбранных объектах. +- [ ] Объединить дублирующие блоки состояния и метрик. +- [ ] Объединить диагностические действия. +- [ ] Объединить Preview и Stage. +- [ ] Спрятать технические поля удаленного доступа. +- [ ] Спрятать raw JSON и raw UCI от оператора. +- [ ] Убрать опасные действия из основных кнопок. + +## Общие состояния и качество + +- [ ] Добавить skeleton loading. +- [ ] Добавить единый компонент пустого состояния. +- [ ] Добавить единый компонент ошибки. +- [ ] Добавить toast-уведомления. +- [ ] Добавить единые диалоги подтверждения. +- [ ] Показывать request ID в раскрываемых деталях ошибки. +- [ ] Добавить tooltips для непонятных иконок. +- [ ] Проверить навигацию клавиатурой. +- [ ] Проверить видимый focus state. +- [ ] Проверить контраст текста и статусов. +- [ ] Проверить отсутствие наложений элементов. +- [ ] Проверить отсутствие обрезанного текста. +- [ ] Проверить преднамеренность горизонтального скролла таблиц. +- [ ] Проверить интерфейс на desktop, tablet и mobile. +- [ ] Сделать браузерные скриншоты ключевых экранов. + +## Требования к API + +- [ ] Нормализовать данные клиентов. +- [ ] Нормализовать состояние интерфейсов. +- [ ] Подготовить временные ряды для графиков. +- [ ] Добавить операторские операции поверх сырых команд. +- [ ] Добавить серверную пагинацию списка объектов при необходимости. +- [ ] Добавить серверный поиск и сортировку при необходимости. + +## Приемка редизайна + +- [ ] За 10 секунд видно, какие объекты требуют внимания. +- [ ] Причина проблемы понятна без raw JSON. +- [ ] LuCI открывается максимум за два клика. +- [ ] Диагностика запускается максимум за три клика. +- [ ] Обычный оператор не видит raw UCI и произвольные команды. +- [ ] Опасные действия требуют явного подтверждения. +- [ ] Основные сценарии работают на ширине 1366 px. +- [ ] Основные сценарии работают на ширине 1920 px. +- [ ] Основные сценарии доступны на мобильном экране. +- [ ] Ошибки API показываются понятным языком. diff --git a/UI_ROADMAP.md b/UI_ROADMAP.md new file mode 100644 index 0000000..1d5a525 --- /dev/null +++ b/UI_ROADMAP.md @@ -0,0 +1,179 @@ +# UI/UX Roadmap + +## Цель + +Сделать RMM-интерфейс удобным для оператора, который обслуживает роутеры, но не обязан знать OpenWrt, UCI и внутренние команды агента. + +Главный экран должен за несколько секунд отвечать на три вопроса: + +1. Какие роутеры требуют внимания? +2. Что именно не работает? +3. Какое безопасное действие можно выполнить? + +Скриншоты Netcraze используются как ориентир по плотности, навигации и представлению данных, без копирования бренда и интерфейса один в один. + +## Роли + +### Оператор + +- Видит состояние объектов, клиентов, сети и предупреждений. +- Запускает готовые диагностики и безопасные операции. +- Открывает удаленный доступ и LuCI. +- Не работает с raw JSON, UCI-командами и техническими параметрами туннеля. + +### Эксперт + +- Имеет доступ ко всем существующим низкоуровневым функциям. +- Просматривает raw inventory, историю команд и аудит. +- Выполняет произвольные команды, UCI-операции и управление пакетами. + +На первом этапе экспертный режим будет отдельным разделом интерфейса. Разграничение прав можно добавить позже. + +## Принципы интерфейса + +- Плотный рабочий интерфейс без маркетинговых блоков и декоративных карточек. +- Единая навигация и предсказуемые вкладки. +- Статусы читаются по тексту и цвету: норма, внимание, ошибка, недоступен. +- Основные действия доступны сразу, редкие и опасные спрятаны в меню. +- Одна глобальная кнопка обновления вместо кнопок обновления в каждом блоке. +- Автообновление данных с понятным временем последнего контакта. +- Русские операторские названия; технические термины остаются в экспертном режиме. +- Любая опасная операция требует подтверждения и объясняет последствия. +- Ошибки показывают понятное описание; request ID доступен в раскрываемых деталях. + +## Целевая структура + +### 1. Объекты + +Первый экран после входа: полноширинная таблица всех роутеров. + +Основные колонки: + +- Статус. +- Имя объекта. +- Модель и группа. +- WAN / интернет. +- Клиенты. +- Проблемы. +- Последний контакт. + +Доступные действия: + +- Поиск. +- Фильтры по статусу, группе и проблемам. +- Открытие объекта кликом по строке. +- Массовые действия появляются только после выбора объектов. + +### 2. Карточка объекта + +Верхняя часть содержит имя, статус, последний контакт и основные действия. + +Вкладки: + +- **Обзор** — здоровье роутера, предупреждения, основные метрики и графики. +- **Клиенты** — единый список DHCP- и Wi-Fi-клиентов. +- **Сеть** — WAN, интерфейсы, Wi-Fi, маршруты и счетчики. +- **Операции** — диагностика, удаленный доступ, обновления и безопасные настройки. +- **Сведения** — модель, прошивка, группа, теги и идентификаторы. +- **Эксперт** — raw inventory, команды, UCI, пакеты, история и аудит. + +## Что меняем в текущем UI + +| Сейчас | После редизайна | +| --- | --- | +| Список устройств в узкой боковой панели | Полноширинная таблица объектов | +| Длинная страница со всеми функциями | Карточка объекта с вкладками | +| Много кнопок Reload / Refresh | Автообновление и одна глобальная кнопка | +| Raw Inventory на обзорной странице | Раздел «Эксперт» | +| Выбор произвольной команды на обзорной странице | Раздел «Эксперт» | +| Пять отдельных кнопок диагностики | Единый сценарий «Запустить диагностику» | +| Raw UCI и множество кнопок | Пошаговое применение безопасных настроек; raw UCI в «Эксперт» | +| Preview и Stage как отдельные действия | Сценарий «Проверить изменения → Применить» | +| Технические поля SSH-туннеля | Выбор длительности и кнопки «Открыть LuCI» / «Скопировать SSH» | +| Редактирование группы и тегов в обзоре | Вкладка «Сведения» | +| Все статусы истории команд отдельными кнопками | Основные фильтры и меню дополнительных фильтров | +| Опасные действия рядом с обычными | Отдельное меню с подтверждением | + +## Этапы реализации + +### Этап 0. UX-аудит и фундамент + +- Сопоставить каждый текущий элемент с действием: оставить, перенести, объединить или скрыть. +- Зафиксировать операторские названия и статусы. +- Подготовить общий layout, компоненты статусов, кнопок, таблиц и уведомлений. +- Определить адаптивное поведение для desktop и mobile. + +### Этап 1. Навигация и список объектов + +- Добавить постоянную навигацию по основным разделам. +- Перенести список роутеров в плотную таблицу. +- Добавить поиск, фильтры и сортировку. +- Показывать проблемы и последний контакт непосредственно в строке объекта. +- Показывать массовые действия только после выбора строк. + +### Этап 2. Обзор объекта + +- Добавить верхнюю панель объекта и вкладки. +- Собрать компактную строку состояния. +- Показать WAN, загрузку, память, uptime, клиентов и предупреждения. +- Добавить основные графики за выбранный период. +- Оставить только главные действия: LuCI, диагностика и меню операций. + +### Этап 3. Клиенты и сеть + +- Объединить DHCP leases и Wi-Fi stations в таблицу клиентов. +- Добавить поиск и фильтры клиентов. +- Создать отдельный экран интерфейсов и подключений. +- Показать понятные состояния WAN, LAN и Wi-Fi. + +### Этап 4. Операторские операции + +- Сделать пошаговую диагностику с прогрессом и итогом. +- Упростить создание удаленной сессии. +- Добавить безопасные формы настроек вместо raw UCI. +- Представить обновления и пакеты как понятные операции. +- Показывать ход, результат и рекомендации после операции. + +### Этап 5. Экспертный режим + +- Перенести существующие технические возможности без потери функциональности. +- Разместить raw inventory, команды, UCI, пакеты, историю и аудит. +- Отделить опасные операции и добавить явные подтверждения. +- Подготовить интерфейс к будущему разграничению прав. + +### Этап 6. Состояния и полировка + +- Добавить skeleton, empty, loading, offline и error states. +- Добавить toast-уведомления и единый стиль подтверждений. +- Проверить навигацию клавиатурой и фокус. +- Проверить таблицы, переносы текста и отсутствие наложений. +- Проверить desktop, tablet и mobile. + +### Этап 7. Недостающие данные API + +- Нормализовать запись клиента из DHCP и Wi-Fi. +- Нормализовать состояние интерфейсов и подключений. +- Подготовить временные ряды для графиков. +- Добавить операторские операции поверх сырых команд. +- При необходимости добавить серверную пагинацию, поиск и сортировку. + +## Приоритет первой итерации + +1. Новый layout и полноширинный список объектов. +2. Карточка объекта с вкладками. +3. Новый обзор объекта. +4. Упрощенный удаленный доступ и LuCI. +5. Перенос технических функций в «Эксперт». + +## Критерии готовности + +- Оператор за 10 секунд понимает, какие объекты требуют внимания. +- Состояние объекта понятно без просмотра JSON и истории команд. +- LuCI открывается не более чем за два клика. +- Диагностика запускается не более чем за три клика. +- В обычном режиме нет raw UCI, произвольных команд и параметров туннеля. +- Опасные операции всегда требуют явного подтверждения. +- Интерфейс корректно работает на ширине 1366, 1920 и на мобильном экране. +- Нет случайного горизонтального скролла, наложений и обрезанного текста. +- Ошибки API отображаются понятно и позволяют раскрыть технические детали. + diff --git a/web/app.js b/web/app.js index 072f6e6..3b0687e 100644 --- a/web/app.js +++ b/web/app.js @@ -22,10 +22,17 @@ const els = { apiState: document.querySelector("#apiState"), refreshBtn: document.querySelector("#refreshBtn"), deviceList: document.querySelector("#deviceList"), + fleetView: document.querySelector("#fleetView"), + fleetTotalCount: document.querySelector("#fleetTotalCount"), + fleetOnlineCount: document.querySelector("#fleetOnlineCount"), + fleetOfflineCount: document.querySelector("#fleetOfflineCount"), + fleetAlertCount: document.querySelector("#fleetAlertCount"), + navAlertCount: document.querySelector("#navAlertCount"), statusLine: document.querySelector("#statusLine"), pageTitle: document.querySelector("#pageTitle"), emptyState: document.querySelector("#emptyState"), deviceView: document.querySelector("#deviceView"), + backToFleetBtn: document.querySelector("#backToFleetBtn"), deviceName: document.querySelector("#deviceName"), deviceMeta: document.querySelector("#deviceMeta"), deviceBadge: document.querySelector("#deviceBadge"), @@ -269,6 +276,26 @@ function deviceDisplayName(device) { return device.id || "unknown"; } +function deviceModel(device) { + return device && device.inventory && device.inventory.board && device.inventory.board.model + ? device.inventory.board.model + : "-"; +} + +function deviceClientCount(device) { + const leases = Array.isArray(device.inventory && device.inventory.dhcp_leases) ? device.inventory.dhcp_leases.length : 0; + const wifi = Array.isArray(device.inventory && device.inventory.wifi_clients) ? device.inventory.wifi_clients.length : 0; + return { leases, wifi, total: Math.max(leases, wifi) }; +} + +function deviceWanSummary(device) { + const wanIP = device.inventory && device.inventory.wan_ip; + const checks = Array.isArray(device.metrics && device.metrics.connectivity_checks) ? device.metrics.connectivity_checks : []; + if (!device.online) return { label: "Нет данных", className: "offline" }; + if (checks.some((check) => !check.reachable)) return { label: wanIP || "Есть потери связи", className: "warning" }; + return { label: wanIP || "Подключено", className: "online" }; +} + function filteredDevices() { const search = els.fleetSearch.value.trim().toLowerCase(); const group = els.fleetGroupFilter.value.trim().toLowerCase(); @@ -284,6 +311,8 @@ function filteredDevices() { deviceDisplayName(device), device.id, device.openwrt_version, + deviceModel(device), + device.inventory && device.inventory.wan_ip, device.group, ...(device.tags || []), ].join(" ").toLowerCase(); @@ -296,27 +325,46 @@ function filteredDevices() { function renderDevices() { const devices = filteredDevices(); els.deviceList.innerHTML = ""; + const online = state.devices.filter((device) => device.online).length; + const alerts = state.devices.filter((device) => device.active_alerts).length; + els.fleetTotalCount.textContent = state.devices.length; + els.fleetOnlineCount.textContent = online; + els.fleetOfflineCount.textContent = state.devices.length - online; + els.fleetAlertCount.textContent = alerts; + els.navAlertCount.textContent = alerts; if (devices.length === 0) { - const empty = document.createElement("div"); - empty.className = "device-item"; - empty.textContent = "No devices"; - els.deviceList.appendChild(empty); + els.emptyState.classList.remove("is-hidden"); return; } + els.emptyState.classList.add("is-hidden"); for (const device of devices) { const displayName = deviceDisplayName(device); + const clients = deviceClientCount(device); + const wan = deviceWanSummary(device); const item = document.createElement("button"); item.type = "button"; item.className = `device-item ${device.id === state.selectedDeviceId ? "is-selected" : ""}`; item.innerHTML = ` -
API: checking
+Проверка сервера
Fleet
-Управление
+Waiting for agent check-in.
+Измените фильтры или дождитесь подключения агента.
+Device
@@ -441,6 +491,6 @@