# Панель экосистемы Статическая навигационная панель для быстрого доступа к сервисам инфраструктуры. Поддерживает гибкую иерархию данных, поиск, дерево навигации, несколько режимов отображения и настройку внешнего вида. Стек: HTML, CSS, vanilla JavaScript. Без сборщиков и фреймворков. ## Оглавление - [Возможности](#возможности) - [Быстрый старт](#быстрый-старт) - [Структура проекта](#структура-проекта) - [Конфигурация](#конфигурация) - [`settings.json`](#settingsjson) - [Форматы иерархии](#форматы-иерархии) - [Схема `panel-data`](#схема-panel-data) - [Поля данных](#поля-данных) - [Примеры данных](#примеры-данных) - [Готовые демо-страницы (`examples/`)](#готовые-демо-страницы-examples) - [Типы сервисов (`serviceMeta`)](#типы-сервисов-servicemeta) - [Рекомендуемые типы сервисов](#рекомендуемые-типы-сервисов) - [Настройки интерфейса (⚙)](#настройки-интерфейса-) - [Поведение интерфейса](#поведение-интерфейса) - [localStorage](#localstorage) - [Разработка](#разработка) - [Ограничения](#ограничения) - [Лицензия](#лицензия) ## Возможности - **Древовидная навигация** слева в стиле файлового менеджера - **Каталог карточек** справа с 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 # Данные навигации (рабочий файл) ├── examples/ # Готовые демо-страницы (см. examples/README.md) │ ├── index.html # Оглавление примеров │ ├── shared/ # styles, script, assets для демо │ ├── full/ # section → group → item │ ├── section-items/ # section → item │ ├── flat/ # плоский items[] │ └── mixed/ # items + groups ├── panel-data-full.example.json # JSON-сниппет (без страницы) ├── assets/ # SVG-иконки сервисов └── README.md ``` ## Конфигурация ### `settings.json` Указывает, откуда загружать данные панели. **Вариант 1 — загрузка из файла или URL:** ```json { "dataUrl": "./panel-data.json" } ``` Можно указать внешний адрес или любой из example-файлов: ```json { "dataUrl": "./panel-data-flat.example.json" } ``` **Вариант 2 — inline-объект в том же файле:** ```json { "dataUrl": "", "data": { "title": "Заголовок панели", "sections": [] } } ``` > Если `dataUrl` не пустой, он имеет **приоритет** над полем `data`. При ошибке загрузки или невалидном JSON на странице показывается экран с описанием проблемы. ### Форматы иерархии Панель поддерживает три базовых формата. Старый **section → group → item** работает без изменений. | Формат | Структура | Дерево | Каталог | |--------|-----------|--------|---------| | **Полная** | `sections[].groups[].items[]` | раздел → подраздел → файлы | collapsible разделы и подразделы | | **Раздел → item** | `sections[].items[]` | раздел → файлы | карточки внутри раздела | | **Плоский список** | корневой `items[]` | плоский список файлов | только сетка карточек | | **Смешанный** | `sections[].items[]` + `groups[]` | прямые файлы, затем подразделы | прямые карточки, затем подразделы | Минимум данных: корень должен содержать `sections` **или** `items`. Каждый раздел — `groups` **или** `items`. Каждый элемент — `title` и `type`. ### Схема `panel-data` Абстрактная структура файла данных (без привязки к конкретным сервисам): ```json { "title": "<заголовок панели>", "sections": [ { "id": "", "number": "<опционально>", "title": "<название раздела>", "description": "<опционально>", "items": [ { "id": "<стабильный-ключ>", "number": "<опционально>", "title": "<название>", "type": "<ключ из serviceMeta>", "tags": ["<тег>"], "url": "<ссылка>" } ], "groups": [ { "id": "", "number": "<опционально>", "title": "<название подраздела>", "description": "<опционально>", "items": ["<элементы — та же форма>"] } ] } ], "items": ["<альтернатива sections: плоский список>"], "serviceMeta": { "": { "label": "<подпись типа>", "icon": "assets/<иконка>.svg" } } } ``` Используй **либо** `sections`, **либо** корневой `items`. Внутри раздела — **либо** `groups`, **либо** `items`, **либо** оба. ### Поля данных | Уровень | Поля | Описание | |---------|------|----------| | **Корень** | `title` | Заголовок панели (sidebar + ``) | | | `sections` | Массив разделов (если нет корневого `items`) | | | `items` | Плоский список без разделов | | | `serviceMeta` | Дополнительные или переопределённые типы сервисов | | **section** | `id`, `title` | Обязательные идентификатор и название | | | `number`, `description` | Опционально | | | `groups`, `items` | Хотя бы одно из двух | | **group** | `id`, `title`, `items` | Подраздел с элементами | | | `number`, `description` | Опционально | | **item** | `title`, `type` | Обязательные название и тип | | | `url` | Ссылка; `"#"` — заглушка | | | `tags` | Массив строк для поиска | | | `id` | Стабильный ключ (DOM, фокус из дерева) | | | `number` | Отображаемый номер | - `type` — ключ из встроенного `defaultServiceMeta` или из `serviceMeta` в JSON - `id` элемента нужен, если нет `number` — иначе ключ генерируется автоматически ### Примеры данных Готовые демо-страницы — в папке [`examples/`](examples/README.md). Корневые `panel-data-*.example.json` — те же структуры в виде JSON-сниппетов. ### Готовые демо-страницы (`examples/`) Каждый пример — отдельная страница с `index.html`, `settings.json` и `panel-data.json`. Общие ресурсы в `examples/shared/`. ```bash cd examples npx serve . ``` | Путь | Формат | |------|--------| | `/` | оглавление примеров | | `/full/` | section → group → item | | `/section-items/` | section → item | | `/flat/` | плоский `items[]` | | `/mixed/` | items + groups в разделе | Подробнее: [examples/README.md](examples/README.md). ### Типы сервисов (`serviceMeta`) Базовые типы заданы в `script.js` (`defaultServiceMeta`): `ceph`, `cloudbeaver`, `config`, `gitea`, `grafana`, `ipmi`, `kubernetes`, `ldap`, `messages`, `opensearch`, `postgresql`, `prometheus`, `rabbitmq`, `redmine`, `rules`, `xpath`, `xmpp` Переопределение или добавление типов в JSON: ```json { "serviceMeta": { "my-type": { "label": "Подпись", "icon": "assets/config.svg" } } } ``` ### Рекомендуемые типы сервисов Популярные `type`, которые можно добавить в `serviceMeta` (иконки — переиспользовать из `assets/`): | Категория | Типы (`type`) | Комментарий | |-----------|---------------|-------------| | **Прокси / балансировка** | `nginx`, `traefik` | reverse proxy, ingress | | **Контейнеры** | `docker`, `portainer`, `argocd` | runtime, UI, GitOps | | **CI/CD** | `jenkins`, `gitlab`, `harbor` | сборка, репозиторий, registry | | **Базы данных** | `mysql`, `mongodb`, `redis` | рядом с уже имеющимися `postgresql`, `opensearch` | | **Очереди / стриминг** | `kafka` | аналог `rabbitmq` | | **Мониторинг / логи** | `loki`, `zabbix`, `sentry` | логи, метрики, ошибки | | **Безопасность / IAM** | `vault`, `keycloak`, `consul` | секреты, SSO, service mesh | | **Поиск** | `elasticsearch`, `sonarqube` | полнотекст, качество кода | | **Хранилище** | `minio` | S3-совместимое | | **IaC** | `terraform` | инфраструктура как код | | **Трекеры** | `jira` | задачи, рядом с `redmine` | Чтобы подключить тип: 1. Добавь запись в `serviceMeta` вашего JSON **или** в `defaultServiceMeta` в `script.js` 2. При необходимости положи отдельную SVG в `assets/` 3. Укажи `"type": "nginx"` (и т.д.) в элементах ## Настройки интерфейса (⚙) Доступны в 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` (или свой JSON из `dataUrl`) 2. Найди нужный раздел / подраздел **или** добавь в `items` / корневой `items` 3. Добавь объект с полями `title`, `type`, при необходимости `tags`, `url`, `id`, `number` 4. Обнови страницу ### Добавить иконку и тип 1. Положи SVG в `assets/` (или переиспользуй существующую) 2. Зарегистрируй тип в `serviceMeta` JSON или в `defaultServiceMeta` в `script.js` ### Внешний источник данных ```json { "dataUrl": "https://your-server/panel-data.json" } ``` Убедись, что сервер отдаёт `Content-Type: application/json` и разрешает CORS при другом домене. ## Ограничения - Требуется HTTP-сервер (`fetch` не работает с `file://`) - Внешний `dataUrl` должен быть доступен по CORS - Настройки UI хранятся локально в браузере и не синхронизируются между устройствами ## Лицензия Приватный проект. Используй и изменяй по своему усмотрению.