# Панель экосистемы Статическая навигационная панель для быстрого доступа к сервисам инфраструктуры. Поддерживает иерархию **раздел → подраздел → элемент**, поиск, дерево навигации, несколько режимов отображения и настройку внешнего вида. Стек: HTML, CSS, vanilla JavaScript. Без сборщиков и фреймворков. ## Возможности - **Древовидная навигация** слева в стиле файлового менеджера - **Каталог карточек** справа с collapsible-разделами - **Поиск** по названию, тегам, типу сервиса и родительским разделам - **4 режима отображения**: плитки, список, компактные плитки, компактный список - **Темы**: светлая / тёмная, 4 пресета акцента + свой градиент - **Настройки** через popup (иконка ⚙) - **Изменяемая ширина** бокового меню (drag) - **Сохранение состояния** в `localStorage` (настройки UI, раскрытие веток) - **Конфигурация через JSON** — локальный объект или загрузка по URL ## Быстрый старт Панель использует `fetch`, поэтому её нужно открывать через HTTP-сервер, а не напрямую как `file://`. ```bash cd ecosystem npx serve . ``` Открой в браузере адрес, который покажет сервер (обычно `http://localhost:3000`). ## Структура проекта ``` ecosystem/ ├── index.html # Разметка страницы ├── styles.css # Стили, темы, адаптив ├── script.js # Логика, рендер, настройки ├── settings.json # Источник данных панели (URL или inline) ├── panel-data.json # Данные навигации (по умолчанию) ├── assets/ # SVG-иконки сервисов └── README.md ``` ## Конфигурация ### `settings.json` Указывает, откуда загружать данные панели. **Вариант 1 — загрузка из файла или URL:** ```json { "dataUrl": "./panel-data.json" } ``` Можно указать внешний адрес: ```json { "dataUrl": "https://example.com/ecosystem/panel-data.json" } ``` **Вариант 2 — inline-объект в том же файле:** ```json { "dataUrl": "", "data": { "title": "Панель экосистемы", "sections": [] } } ``` > Если `dataUrl` не пустой, он имеет **приоритет** над полем `data`. При ошибке загрузки или невалидном JSON на странице показывается экран с описанием проблемы. ### `panel-data.json` Основной файл с содержимым панели. ```json { "title": "Панель экосистемы", "sections": [ { "id": "user", "number": "1", "title": "Интерфейс пользователя", "description": "Описание раздела", "groups": [ { "id": "message-filtering", "number": "1.1", "title": "Управление фильтрацией", "description": "Описание подраздела", "items": [ { "number": "1.1.1", "title": "Редактирование правил", "type": "rules", "tags": ["правила", "фильтрация"], "url": "https://rules.example.com" } ] } ] } ], "serviceMeta": {} } ``` ### Поля данных | Уровень | Поля | Описание | |---------|------|----------| | **Корень** | `title` | Заголовок панели (sidebar + ``) | | | `sections` | Массив разделов | | | `serviceMeta` | Опционально: доп. типы сервисов | | **section** | `id`, `number`, `title`, `description`, `groups` | Крупный раздел | | **group** | `id`, `number`, `title`, `description`, `items` | Подраздел | | **item** | `number`, `title`, `type`, `tags`, `url` | Кликабельный элемент | - `type` — ключ из `serviceMeta` (определяет иконку и подпись типа) - `tags` — используются в поиске и опционально в дереве - `url` — ссылка перехода; `"#"` означает заглушку (клик не ведёт никуда) ### Типы сервисов (`serviceMeta`) Базовые типы заданы в `script.js`: `ceph`, `cloudbeaver`, `config`, `gitea`, `grafana`, `ipmi`, `kubernetes`, `ldap`, `messages`, `opensearch`, `postgresql`, `prometheus`, `rabbitmq`, `redmine`, `rules`, `xpath`, `xmpp` В `panel-data.json` можно добавить или переопределить типы: ```json { "serviceMeta": { "my-service": { "label": "My Service", "icon": "assets/config.svg" } } } ``` ## Настройки интерфейса (⚙) Доступны в popup-окне и сохраняются в `localStorage` (`ecosystem-panel-preferences`): | Настройка | Описание | |-----------|----------| | **Тема** | Светлая / тёмная | | **Вид** | Плитки, список, компакт плитки, компакт список | | **Нумерация** | Показывать номера разделов, подразделов и элементов | | **Теги дерева** | Поисковые теги рядом с элементами в дереве | | **2 колонки в дереве** | Элементы папок в 2 колонки (взаимоисключимо с тегами) | | **Авто раскрывать поиск** | При поиске сворачивать ветки без совпадений | | **Акцент** | violet, cyan, lime, amber или свой градиент | ### Свой градиент Кнопка **«Свой»** открывает настройки: - угол (0–360°) - цвет «от» и «до» Значения сохраняются в `localStorage`. ## Поведение интерфейса ### Поиск Фильтрует каталог и подсвечивает совпадения в дереве и карточках. Ищет по: - названию элемента - номеру - тегам - типу сервиса - названиям родительских разделов ### Дерево навигации - Клик по **папке** — раскрытие/сворачивание - Клик по **названию** — прокрутка к элементу в каталоге - Состояние раскрытия сохраняется отдельно для дерева и контента (`ecosystem-panel-collapse-state`) ### Адаптив - **Touch-устройства** (`pointer: coarse` / `hover: none`): stacked-layout, меню сверху - **Мышь** (`pointer: fine` + `hover: hover`): sidebar + resizer + каталог в колонках - **2 колонки в дереве на touch**: в portrait или при ширине от 520px ### Ширина layout - Обычный режим: max **1560px**, sidebar до **560px** - Режим «2 колонки в дереве»: max **1920px**, sidebar до **760px** ## localStorage | Ключ | Содержимое | |------|------------| | `ecosystem-panel-preferences` | Тема, акцент, вид, ширина sidebar, toggle-настройки | | `ecosystem-panel-collapse-state` | Раскрытые/свернутые ветки дерева и каталога | ## Разработка ### Добавить новый элемент 1. Открой `panel-data.json` 2. Найди нужный `group` в `sections` 3. Добавь объект в `items`: ```json { "number": "2.5.3", "title": "Новый сервис", "type": "gitea", "tags": ["gitea", "новый"], "url": "https://gitea.example.com" } ``` 4. Обнови страницу ### Добавить новую иконку 1. Положи SVG в `assets/` 2. Зарегистрируй тип в `serviceMeta` внутри `panel-data.json` или используй существующий `type` ### Внешний источник данных Укажи URL в `settings.json`: ```json { "dataUrl": "https://your-server/panel-data.json" } ``` Убедись, что сервер отдаёт корректный `Content-Type: application/json` и разрешает CORS, если домен отличается от панели. ## Ограничения - Требуется HTTP-сервер (`fetch` не работает с `file://`) - Внешний `dataUrl` должен быть доступен по CORS - Настройки UI хранятся локально в браузере и не синхронизируются между устройствами ## Лицензия Приватный проект. Используй и изменяй по своему усмотрению.