Browse Source

docs: Полностью переписан DEVICES_PANEL.md - ключевые фичи

Переструктурировал документ с фокусом на ФУНКЦИОНАЛ, а не реализацию:

## Структура:

### КЛЮЧЕВЫЕ ФИЧИ (без технической реализации):

1. **Devices Panel:**
   A. Универсальный поиск ⭐ КЛЮЧЕВАЯ ФИЧА
      - Один поиск по всем полям
      - Примеры: MAC, IP, SSID, URL
   B. Компактная таблица с максимумом информации
   C. Inline editing (опасно, не тащим)
   D. Сортировка по любой колонке

2. **Clients Panel:**
   A. Список клиентов с их устройствами
   B. Редактирование даты оплаты
   C. Доп. информация о клиенте

3. **Add Device Panel:**
   A. Создание клиента + устройства одновременно
   B. Простой контроль оплаты/лимитов

### ЧТО ТАЩИМ:

Обязательные фичи (Must Have):
1. ✅ Универсальный поиск (HIGH priority)
2. ✅ Компактная таблица с сортировкой
3. ✅ Создание org с bulk import устройств
4. ✅ Статусы и индикаторы
5. ✅ Модальные окна вместо inline editing

Опциональные (Nice to Have):
6. ⚠️ Bulk actions (массовые операции)
7. ⚠️ Фильтры по статусу (chips)
8. ⚠️ Экспорт CSV/Excel

### ЧТО НЕ ТАЩИМ:

❌ Inline editing - опасно
❌ OpenVPN - по ТЗ не нужно
❌ Хардкод URL в БД
❌ WiFi credentials в таблице devices
❌ Текстовые связи через ;
❌ Plain text пароли
❌ Отсутствие auth

### ПРИОРИТЕТЫ:

✅ Уже есть (MVP): CRUD, JWT, RBAC, multi-tenant
🔨 TODO 1: Универсальный поиск (HIGH)
🔨 TODO 2: Bulk import устройств (MEDIUM)
🔨 TODO 3: Фильтры по статусу (LOW)

### Сравнительная таблица Legacy → New:

14 ключевых фич с статусами (Done/TODO/Убрали)

Фокус на бизнес-логике, без технических деталей реализации
root 1 month ago
parent
commit
9a165fb2a6
1 changed files with 697 additions and 573 deletions
  1. 697 573
      DEVICES_PANEL.md

+ 697 - 573
DEVICES_PANEL.md

@@ -1,622 +1,746 @@
-# Анализ Legacy Devices Panel
+# Legacy Admin Panel - Анализ ключевых фич
 
-## Обзор legacy проекта
+## Расположение
+`/home/user/work/luckfox/mybeacon-legacy/www/my-wifi/panel/`
 
-Расположение: `/home/user/work/luckfox/mybeacon-legacy/www/my-wifi/panel/`
+## Структура legacy админки
 
-Технологии:
-- **Backend**: PHP 5.x/7.x + MySQL (MariaDB)
-- **Frontend**: jQuery + DataTables + jEditable
-- **Стиль**: Bootstrap 3 + кастомная тема
+### 3 основные страницы:
 
-## Структура БД (Legacy)
+1. **Devices (index.php)** - управление устройствами (радарами)
+2. **Clients (clients.php)** - управление клиентами (организациями)
+3. **Add Device (adddev.php)** - добавление клиента с устройствами
 
-### Таблица `configs` (устройства)
-```sql
-CREATE TABLE `configs` (
-  `id` int(11) PRIMARY KEY AUTO_INCREMENT,
-  `time` timestamp DEFAULT current_timestamp(),  -- Last seen
-  `mac` varchar(40) UNIQUE NOT NULL,
-  `wf_client_ssid` varchar(60),                  -- WiFi SSID для подключения
-  `wf_client_psk` varchar(60),                   -- WiFi пароль
-  `ovpn_flag` tinyint(1),                        -- OpenVPN включен
-  `ovpn_addr` varchar(60),                       -- URL для получения VPN конфига
-  `wf_flag` tinyint(1),                          -- WiFi сбор включен
-  `wf_addr` varchar(60),                         -- URL для отправки WiFi данных
-  `bt_flag` tinyint(1),                          -- BLE сбор включен
-  `bt_addr` varchar(60),                         -- URL для отправки BLE данных
-  `fw_flag` tinyint(1),                          -- Firmware update включен
-  `fw_addr` varchar(60),                         -- URL для получения firmware
-  `reboot_flag` tinyint(1),                      -- Флаг перезагрузки
-  `ip` varchar(40)                               -- IP адрес устройства
-);
-```
-
-### Таблица `users` (клиенты/организации)
+---
+
+## КЛЮЧЕВЫЕ ФИЧИ (без технической реализации)
+
+### 1. Devices Panel - Управление устройствами
+
+#### A. Универсальный поиск ⭐ КЛЮЧЕВАЯ ФИЧА
+
+**Что делает:**
+- Пользователь вводит любой текст в одно поле
+- Система ищет по **всем** колонкам таблицы одновременно
+- Фильтрация в реальном времени (при вводе)
+
+**Примеры поиска:**
+- `ac:84` → найдет все MAC-адреса содержащие "ac:84"
+- `192.168` → найдет все устройства с IP в этой подсети
+- `Office_WiFi` → найдет все устройства подключенные к этому SSID
+- `beacon.ru` → найдет все устройства с этим адресом отправки данных
+
+**Почему важно:**
+- Админ не знает точный MAC → вводит часть → быстро находит
+- Админ помнит что "где-то было 192.168..." → вводит → находит
+- Не нужно помнить в какой колонке искать
+- Один поисковый запрос вместо 15 фильтров
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - универсальное поисковое поле с поиском по всем полям
+
+---
+
+#### B. Компактная таблица с максимумом информации
+
+**Что делает:**
+- Вся важная информация видна сразу в таблице
+- Не нужно кликать на строку чтобы увидеть детали
+- Все 15 полей конфига видны в одной строке
+
+**Колонки:**
+```
+#  MAC         time      wf_ssid  wf_psk  ovpn_flag  ovpn_addr  wf_flag  wf_addr  bt_flag  bt_addr  fw_flag  fw_addr  reboot_flag  ip
+1  ac:84:...   10:25     Office   pass123      1      https://      1     https://    1     https://    0       -           0      192.168.5.244
+```
+
+**Почему важно:**
+- Быстрый просмотр состояния всех устройств
+- Сравнение конфигов разных устройств
+- Видно кто онлайн (last seen), кто офлайн
+
+**Решение для нового проекта:**
+⚠️ ЧАСТИЧНО - компактная таблица с основным, детали в модальном окне
+
+---
+
+#### C. Быстрое редактирование прямо в таблице (inline editing)
+
+**Что делает:**
+- Кликнул на ячейку → она превратилась в input
+- Изменил значение → нажал "Ok" → сохранено
+- Не нужно открывать формы, модальные окна
+- Изменение 1 поля = 2 клика (клик на ячейку + клик Ok)
+
+**Use case:**
+- Админ видит что у device #5 неправильный wf_ssid
+- Кликает на ячейку
+- Меняет "Office" на "Office_Guest"
+- Клик Ok → сохранено
+- Время: 5 секунд
+
+**Почему опасно:**
+- Случайный клик → случайное изменение
+- Нет подтверждения
+- Нет валидации
+- Нет истории кто и что изменил
+
+**Решение для нового проекта:**
+❌ НЕ ТАЩИМ - слишком опасно, заменяем модальными окнами с подтверждением
+
+---
+
+#### D. Сортировка по любой колонке
+
+**Что делает:**
+- Клик на заголовок колонки → сортировка по возрастанию
+- Еще клик → сортировка по убыванию
+- Работает для всех колонок (кроме действий)
+
+**Use cases:**
+- Отсортировать по `time` → увидеть кто давно не подключался
+- Отсортировать по `MAC` → найти все устройства одного производителя
+- Отсортировать по `ip` → сгруппировать по подсетям
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - сортировка обязательно
+
+---
+
+### 2. Clients Panel - Управление клиентами (организациями)
+
+#### A. Список клиентов с их устройствами
+
+**Что показывает:**
+```
+#  Логин                  Пароль     Устройства              Оплачено до  Информация               Оплата
+1  shop@example.com       12345      ac:84:c6:42:17:90      2025-05-01   ИП Иванов, +79001234567  Оплачено до мая
+                                     cc:2d:e0:ca:9f:7e
+                                     d8:0d:17:5e:07:94
+
+2  mall@example.com       54321      b8:27:eb:c1:46:0e      2025-03-15   ТЦ "Мега", Петров        Лимит 1000
+```
+
+**Ключевая фича:**
+- Сразу видно сколько устройств у клиента
+- MAC-адреса отображаются вертикально (каждый с новой строки)
+- Информация о клиенте и оплате в одной строке
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - список организаций с количеством устройств и статусом
+
+---
+
+#### B. Редактирование даты оплаты
+
+**Что делает:**
+- В колонке "Оплачено до" дата является ссылкой
+- Клик → переход на tilledit.php?id=123
+- Отдельная страница с календарем
+- Выбрать новую дату → сохранить
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - но через модальное окно, не отдельную страницу
+
+---
+
+#### C. Хранение доп. информации о клиенте
+
+**Что хранится:**
+- Юридическое лицо / ИП
+- Контактное лицо
+- Телефон
+- Email
+- Адрес
+- Комментарии
+
+**Формат:** Текстовое поле (textarea), админ пишет произвольный текст
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - но структурированно (отдельные поля, не текст)
+
+---
+
+### 3. Add Device Panel - Создание клиента с устройствами
+
+#### A. Одновременное создание клиента и привязка устройств
+
+**Workflow:**
+```
+1. Админ вводит:
+   - Логин клиента (email)
+   - Пароль
+   - Список MAC-адресов устройств (textarea, через ;)
+     Пример: d8:0d:17:5e:07:94;ac:84:c6:42:17:90;cc:2d:e0:ca:9f:7e;
+   - Информация о клиенте (textarea)
+   - Оплата (checkbox: оплачен / лимит 1000)
+
+2. Система создает:
+   - Запись в таблице `users` (клиент)
+   - Записи в таблице `user_devices` (связь клиент-устройство)
+   - Автоматически все MAC из списка привязываются к клиенту
+```
+
+**Почему удобно:**
+- Не нужно сначала создать клиента, потом добавлять устройства по одному
+- Массовое добавление устройств (скопировал список MAC → вставил → готово)
+
+**Решение для нового проекта:**
+✅ ТАЩИМ - форма создания организации с возможностью сразу добавить несколько устройств
+
+---
+
+#### B. Простой контроль оплаты/лимитов
+
+**Логика:**
+- Checkbox "Оплачен"
+- Если НЕ оплачен → устанавливается лимит 1000 (детекций/событий)
+- Поле "Оплачено до" (дата)
+- Поле "Оплата" (текст статуса)
+
+**Use case:**
+- Клиент на пробном периоде → checkbox снят → лимит 1000
+- Клиент оплатил → checkbox включен → безлимит
+- Админ видит когда заканчивается период → продлевает
+
+**Решение для нового проекта:**
+⚠️ ЧАСТИЧНО - заменяем на статусы организации: `pending`, `active`, `suspended`
+
+---
+
+## ЧТО ТАЩИМ В НОВЫЙ ПРОЕКТ
+
+### Обязательные фичи (Must Have)
+
+#### 1. ✅ Универсальный поиск
+
+**Где:**
+- Devices panel
+- Organizations panel
+- Users panel
+
+**Реализация:**
+```
+Поисковое поле → backend ищет по полям:
+- Devices: MAC, simple_id, organization.name, organization.owner_email, IP
+- Organizations: name, contact_email, owner.email
+- Users: email, full_name, organization.name
+```
+
+**Фичи:**
+- Debounce 300ms (не слать запрос на каждый символ)
+- Минимум 2 символа для поиска
+- Подсветка найденного (опционально)
+- Placeholder с примерами: "Поиск по MAC, организации, владельцу..."
+
+---
+
+#### 2. ✅ Компактная таблица с сортировкой
+
+**Devices:**
+```
+Simple ID  |  MAC Address   |  Organization  |  Status  |  Last Seen  |  Actions
+   #1      | ac:84:c6:...  |  Shop LLC      | online   |  2 min ago  |  Edit Delete
+   #2      | cc:2d:e0:...  |  Mall Inc      | offline  |  5 days ago |  Edit Delete
+```
+
+**Сортировка:**
+- По Simple ID (default: newest first)
+- По Organization (alphabetically)
+- По Status (online → offline → error)
+- По Last Seen (newest → oldest)
+
+**Клик на заголовок** → меняет сортировку
+**Индикатор направления** → стрелка ↑↓
+
+---
+
+#### 3. ✅ Создание организации с устройствами
+
+**Форма Create Organization:**
+
+**Шаг 1: Информация об организации**
+```
+Name: ________________
+Contact Email: ________
+Owner Email: __________
+WiFi Enabled: ☐
+BLE Enabled: ☐
+```
+
+**Шаг 2: Добавить устройства (опционально)**
+```
+⊞ Add Device
+
+MAC Address: __________________  [Remove]
+MAC Address: __________________  [Remove]
+MAC Address: __________________  [Remove]
+
+[+ Add Another Device]
+```
+
+**Или массовое добавление:**
+```
+☐ Bulk import from list
+
+Paste MAC addresses (one per line or separated by ; or ,):
+┌─────────────────────────────────────┐
+│ ac:84:c6:42:17:90                  │
+│ cc:2d:e0:ca:9f:7e;d8:0d:17:5e:07:94│
+│ b8:27:eb:c1:46:0e                  │
+└─────────────────────────────────────┘
+
+[Parse & Add]  →  "Found 4 MAC addresses"
+```
+
+**Кнопки:**
+```
+[Create Organization]  [Cancel]
+```
+
+**После создания:**
+- Organization создана
+- Devices добавлены и автоматически привязаны
+- Owner получает email с доступом
+
+---
+
+#### 4. ✅ Статусы и индикаторы
+
+**Device status badges:**
+```css
+.status-online    → зеленый  (#10b981)
+.status-offline   → серый    (#6b7280)
+.status-error     → красный  (#ef4444)
+```
+
+**Last seen human-readable:**
+```
+2 minutes ago
+5 hours ago
+3 days ago
+Never (для новых устройств)
+```
+
+**Organization status:**
+```
+pending  → желтый  (ожидает активации superadmin)
+active   → зеленый (работает)
+suspended → красный (заблокирована)
+```
+
+---
+
+#### 5. ✅ Модальные окна для редактирования
+
+**Вместо inline editing:**
+
+**Edit Device Modal:**
+```
+┌─────────────────────────────────────┐
+│ Edit Device #5                      │
+│                                     │
+│ MAC Address: ac:84:c6:42:17:90      │
+│ (read-only, не редактируется)       │
+│                                     │
+│ Organization: [Dropdown ▼]          │
+│   → Shop LLC                        │
+│   → Mall Inc                        │
+│   → Not assigned                    │
+│                                     │
+│ Status: [Dropdown ▼]                │
+│   → Online                          │
+│   → Offline                         │
+│   → Error                           │
+│                                     │
+│ Config (JSON):                      │
+│ ┌─────────────────────────┐         │
+│ │ {                       │         │
+│ │   "wifi_enabled": true  │         │
+│ │ }                       │         │
+│ └─────────────────────────┘         │
+│                                     │
+│ [Save Changes]  [Cancel]            │
+└─────────────────────────────────────┘
+```
+
+**Преимущества:**
+- Валидация перед сохранением
+- Подтверждение действия
+- Невозможно случайно изменить
+- Audit log: кто, когда, что изменил
+
+---
+
+### Опциональные фичи (Nice to Have)
+
+#### 6. ⚠️ Bulk actions (массовые действия)
+
+**Что:**
+- Checkbox в каждой строке таблицы
+- Checkbox "Select All" в заголовке
+- Панель массовых действий появляется при выборе
+
+**Пример:**
+```
+☑ Select All    (5 selected)
+
+[Change Organization ▼]  [Delete Selected]  [Export CSV]
+```
+
+**Use cases:**
+- Выбрать 10 устройств → переназначить в другую организацию
+- Выбрать 5 офлайн устройств → удалить
+- Выбрать все → экспорт в Excel
+
+---
+
+#### 7. ⚠️ Фильтры по статусу (chips)
+
+**Вместо dropdown:**
+
+```
+Status:  [All]  [Online]  [Offline]  [Error]
+         ─────   (123)     (45)       (2)
+```
+
+**Клик на chip** → фильтрует таблицу
+**Счетчик** → показывает количество устройств в каждом статусе
+
+---
+
+#### 8. ⚠️ Экспорт в CSV/Excel
+
+**Кнопка:** "Export to Excel"
+
+**Что экспортирует:**
+- Текущий отфильтрованный список (с учетом поиска)
+- Все колонки таблицы
+- Формат: .xlsx (Excel) или .csv
+
+**Use case:**
+- Админ хочет отчет по всем устройствам организации "Shop LLC"
+- Поиск "Shop LLC" → 10 devices найдено
+- Export → получает Excel с 10 строками
+
+---
+
+## ЧТО НЕ ТАЩИМ
+
+### ❌ 1. Inline editing
+
+**Почему НЕ тащим:**
+- Опасно (случайные изменения)
+- Нет валидации
+- Нет подтверждения
+- Нет audit trail
+
+**Замена:** Модальные окна
+
+---
+
+### ❌ 2. OpenVPN туннели
+
+**Почему НЕ тащим:**
+- По ТЗ: "Сейчас у нас VPN не будет"
+- Убираем:
+  - `ovpn_flag`
+  - `ovpn_addr`
+  - VPN страницу
+
+---
+
+### ❌ 3. Множественные флаги и URL в БД
+
+**Legacy имело:**
 ```sql
-CREATE TABLE `users` (
-  `id` int(11) PRIMARY KEY AUTO_INCREMENT,
-  `login` text NOT NULL,
-  `password` text NOT NULL,
-  `device` text NOT NULL,                        -- MAC-адреса через ";" (b8:27:eb:c1:46:0e;cc:2d:e0:ca:9f:7e;)
-  `addTime` timestamp DEFAULT current_timestamp(),
-  `dopinfo` text,                                -- Доп. информация о клиенте
-  `paidTill` text,                               -- Текст об оплате
-  `folder` text,                                 -- Папка для файлов (не использовалось)
-  `till` date DEFAULT '2022-05-03',              -- Дата оплаты до
-  `dopfolder` varchar(128)                       -- Доп. папка (не использовалось)
-);
-```
-
-### Таблица `user_devices` (связь)
+wf_flag       tinyint(1)
+wf_addr       varchar(60)  -- "https://beacon.e-bash.ru/wifi_receiver.php"
+bt_flag       tinyint(1)
+bt_addr       varchar(60)  -- "https://beacon.e-bash.ru/newfilebeacon.php"
+fw_flag       tinyint(1)
+fw_addr       varchar(60)  -- "https://beacon.e-bash.ru/fw_update.php?mac="
+```
+
+**Проблемы:**
+- Хардкод URL в БД
+- Для изменения URL нужно обновить все записи
+- Нет версионирования API
+
+**Новый подход:**
+- Модульность на уровне Organization: `wifi_enabled`, `ble_enabled`
+- Единые API endpoints (не хардкод в БД):
+  - `/api/v1/device/config` - получить конфиг
+  - `/api/v1/device/wifi-events` - отправить WiFi данные
+  - `/api/v1/device/ble-events` - отправить BLE данные
+- Device config как JSONB для гибкости
+
+---
+
+### ❌ 4. WiFi credentials в таблице устройств
+
+**Legacy:**
 ```sql
-CREATE TABLE `user_devices` (
-  `mac` varchar(40),
-  `name` varchar(255),
-  `user_id` int(11),
-  `max1000` tinyint(1),                          -- Лимит (0=оплачен, 1=лимит 1000)
-  `segment` tinyint(1)                           -- Для Yandex.Аудитории
-);
+wf_client_ssid  varchar(60)   -- WiFi SSID
+wf_client_psk   varchar(60)   -- WiFi пароль (plain text!)
 ```
 
-## Навигация (Legacy)
+**Проблемы:**
+- Пароли в открытом виде
+- Одинаковые для всех устройств организации
+- При смене пароля нужно обновлять все devices
+
+**Новый подход:**
+- WiFi credentials на уровне Organization (в `config` JSONB)
+- Шифрование паролей
+- Device получает через защищенный API endpoint
+
+---
 
+### ❌ 5. Текстовые связи через разделители
+
+**Legacy:**
+```sql
+device  text NOT NULL  -- "b8:27:eb:c1:46:0e;cc:2d:e0:ca:9f:7e;d8:0d:17:5e:07:94;"
 ```
-1. Устройства (index.php)       - главная страница, список всех устройств
-2. Добавление (adddev.php)       - форма добавления клиента с устройствами
-3. Клиенты (clients.php)         - список клиентов
-4. Инструкция (info.php)         - документация
-5. VPN (внешняя ссылка)          - сканер VPN подключений
+
+**Проблемы:**
+- Невозможно сделать JOIN
+- Невозможно сделать foreign key constraint
+- Невозможно индексировать
+- Парсинг строки каждый раз
+
+**Новый подход:**
+- Нормализованная БД
+- Таблица `devices` с `organization_id` (foreign key)
+- Один device = одна строка
+
+---
+
+### ❌ 6. Пароли в открытом виде
+
+**Legacy:**
+```sql
+password  text NOT NULL  -- "12345" (plain text)
 ```
 
-## Функционал Devices Panel (index.php)
+**Новый подход:**
+- `hashed_password` varchar(255)
+- bcrypt с salt
+- Минимум 8 символов, complexity requirements
 
-### Таблица устройств
+---
 
-**Колонки:**
-1. `#` - порядковый номер
-2. `MAC-адрес` - читаемый (не редактируемый)
-3. `время` - last seen (не редактируемый)
-4. `wf_ssid` - **РЕДАКТИРУЕМАЯ** (inline editing)
-5. `wf_psk` - **РЕДАКТИРУЕМАЯ**
-6. `ovpn_flag` - **РЕДАКТИРУЕМАЯ**
-7. `ovpn_addr` - **РЕДАКТИРУЕМАЯ**
-8. `wf_flag` - **РЕДАКТИРУЕМАЯ**
-9. `wf_addr` - **РЕДАКТИРУЕМАЯ**
-10. `bt_flag` - **РЕДАКТИРУЕМАЯ**
-11. `bt_addr` - **РЕДАКТИРУЕМАЯ**
-12. `fw_flag` - **РЕДАКТИРУЕМАЯ**
-13. `fw_addr` - **РЕДАКТИРУЕМАЯ**
-14. `reboot_flag` - **РЕДАКТИРУЕМАЯ**
-15. `ip` - **РЕДАКТИРУЕМАЯ**
-
-### Ключевая фича: Inline Editing
-
-**Библиотека**: [jEditable](http://www.appelsiini.net/projects/jeditable)
-
-**Как работает:**
-1. Пользователь кликает на ячейку таблицы (с классом `.editable-td`)
-2. Ячейка превращается в `<input>` с кнопками "Ok" и "Cancel"
-3. При нажатии "Ok" отправляется AJAX запрос:
-   ```javascript
-   $.get('savewf.php', {
-     p1: value,           // новое значение
-     p2: $(this).attr('id'),  // ID ячейки (имя поля)
-     p3: $(this).parent('tr').attr('id')  // ID строки (id записи)
-   })
-   ```
-4. Backend (`savewf.php`) выполняет SQL:
-   ```sql
-   UPDATE `configs` SET `{field_name}` = '{value}' WHERE `id`='{id}'
-   ```
-5. Ячейка возвращается к обычному виду с новым значением
-
-### Поиск и фильтрация
-
-**Библиотека**: [DataTables](https://datatables.net/)
-
-**Функционал:**
-- Глобальный поиск по **всем** колонкам таблицы
-- Пользователь вводит любой текст в поле поиска
-- DataTables фильтрует строки в реальном времени
-- Работает по: MAC, SSID, IP, адресам, времени, флагам
-
-**Код:**
-```javascript
-var editableTable = exampleDatatable.dataTable({
-    order: [[ 1, 'desc' ]],  // Сортировка по ID по убыванию
-    columnDefs: [ { orderable: false, targets: [ 0 ] } ]
-});
-$('.dataTables_filter input').attr('placeholder', 'Search');
-```
-
-### Сортировка
-
-- Сортировка по любой колонке (кроме первой)
-- По умолчанию: по ID (времени регистрации) по убыванию
-
-## Функционал Clients Panel (clients.php)
-
-### Таблица клиентов
+### ❌ 7. Отсутствие аутентификации в админке
 
-**Колонки:**
-1. `#` - порядковый номер
-2. `Логин`
-3. `Пароль`
-4. `Устройства` - MAC-адреса через `<br>` (разделены по `;`)
-5. `Оплачено до` - ссылка на редактирование даты
-6. `Информация` - допинфо о клиенте
-7. `Оплата` - текст статуса оплаты
-
-### Особенности
-
-- Список устройств выводится вертикально (`implode('<br>', explode(';', $line['device']))`)
-- Ссылка на редактирование даты: `<a href="tilledit.php?id={id}">{till}</a>`
-- Нет inline editing (в отличие от devices)
-- Только чтение данных
-
-## Функционал Add Device (adddev.php)
-
-### Форма создания клиента
-
-**Поля:**
-1. `Логин` - required
-2. `Пароль` - required
-3. `Список устройств` - textarea, через `;` в конце
-   Пример: `d8:0d:17:5e:07:94;ac:84:c6:42:17:90;`
-4. `Информация о клиенте` - textarea (юрлицо, имя, телефон, email)
-5. `Оплата` - textarea (текст до какого числа оплачено)
-6. `Оплачен` - checkbox (max1000: 0=оплачен, 1=лимит)
-
-### Логика создания
-
-```php
-// 1. Валидация
-if (login == null || password == null || device == null) {
-  error("Поля логин, пароль и устройства не могут быть пустыми");
-}
-
-// 2. Проверка существования логина
-$query = "SELECT * FROM `users` WHERE `login` = '{login}'";
-if (row_count > 0) {
-  error("Логин уже занят");
-}
-
-// 3. Создание пользователя
-INSERT INTO `users` (login, password, device, dopinfo, paidTill, till)
-VALUES ('{login}', '{password}', '{device}', '{dopinfo}', '{paidTill}', CURDATE());
-
-$user_id = mysqli_insert_id();
-
-// 4. Создание связей user_devices
-$macs = explode(";", device);
-foreach ($macs as $mac) {
-  INSERT INTO `user_devices` (mac, name, user_id, max1000, segment)
-  VALUES ('{mac}', '{mac}', {user_id}, {max1000}, 0);
-}
-```
-
-## Проблемы Legacy кода
-
-### Безопасность
-
-1. **SQL Injection** - прямая конкатенация в запросах:
-   ```php
-   $query = "UPDATE `configs` SET `".$_REQUEST['p2']."` = '".$_REQUEST['p1']."' WHERE `id`='".$_REQUEST['p3']."'";
-   ```
-2. **XSS** - нет экранирования вывода
-3. **Пароли в открытом виде** - хранятся как plain text
-4. **Нет CSRF защиты**
-5. **Нет валидации типов данных**
-
-### Архитектура
-
-1. **Нет разделения ролей** - все пользователи видят всё
-2. **Нет multi-tenant изоляции** - одна таблица на всех
-3. **Нет аутентификации в админке** - доступ без проверки
-4. **Хардкод credentials** в каждом файле:
-   ```php
-   $dbname = 'wifi';
-   $dbuser = 'p328882_wifi';
-   $dbpass = '0354c0598ld';
-   ```
-5. **Связь many-to-many через текст** - MAC-адреса через `;`
-
-### UX
-
-1. **Inline editing опасен** - легко случайно изменить данные
-2. **Нет подтверждения удаления**
-3. **Нет истории изменений** (audit logs)
-4. **Нет валидации MAC-адресов**
-5. **Нет статусов устройств** (online/offline/error)
-
-## Анализ требований для нового проекта
-
-### Что взять из Legacy
-
-#### 1. **Универсальный поиск по всем полям** ⭐ КЛЮЧЕВАЯ ФИЧА
+**Legacy:**
+- Нет проверки кто заходит в админку
+- Любой кто знает URL может зайти
 
-**Почему важно:**
-- Быстро найти устройство по MAC
-- Найти устройства организации
-- Найти по IP адресу
-- Найти по owner/admin email
+**Новый подход:**
+- JWT аутентификация
+- Role-based access control
+- Session management
+- Audit logging
+
+---
+
+## ПРИОРИТЕТЫ РЕАЛИЗАЦИИ
 
-**Реализация в новом проекте:**
+### ✅ Уже есть (MVP)
+
+1. CRUD устройств с модальными окнами
+2. CRUD организаций
+3. CRUD пользователей
+4. JWT аутентификация
+5. Multi-tenant изоляция
+6. RBAC (6 ролей)
+7. Базовая таблица с сортировкой
+
+### 🔨 TODO Шаг 1: Поиск (приоритет HIGH)
 
 **Backend:**
 ```python
-# backend/app/api/v1/superadmin/devices.py или client/devices.py
-@router.get("/devices", response_model=DeviceListResponse)
+@router.get("/devices")
 async def get_devices(
     search: Optional[str] = None,
     organization_id: Optional[int] = None,
+    status: Optional[str] = None,
+    sort_by: str = "simple_id",
+    sort_order: str = "desc",
     offset: int = 0,
-    limit: int = 50,
-    db: AsyncSession = Depends(get_db),
-    current_user: User = Depends(get_current_user)
+    limit: int = 50
 ):
     query = select(Device)
 
-    # Фильтр по организации (для multi-tenant)
-    if current_user.role != "superadmin":
-        query = query.where(Device.organization_id == current_user.organization_id)
-    elif organization_id:
-        query = query.where(Device.organization_id == organization_id)
-
-    # Универсальный поиск
+    # Search filter
     if search:
-        search_filter = or_(
-            Device.mac_address.ilike(f"%{search}%"),
-            Device.simple_id.cast(String).ilike(f"%{search}%"),
-            Organization.name.ilike(f"%{search}%"),
-            User.email.ilike(f"%{search}%"),
-            User.full_name.ilike(f"%{search}%")
-        )
-        query = (
-            query
-            .outerjoin(Organization)
-            .outerjoin(User, User.organization_id == Device.organization_id)
-            .where(search_filter)
+        query = query.join(Organization).where(
+            or_(
+                Device.mac_address.ilike(f"%{search}%"),
+                cast(Device.simple_id, String).ilike(f"%{search}%"),
+                Organization.name.ilike(f"%{search}%"),
+                Organization.contact_email.ilike(f"%{search}%")
+            )
         )
 
+    # Status filter
+    if status:
+        query = query.where(Device.status == status)
+
+    # Sorting
+    if sort_order == "asc":
+        query = query.order_by(asc(getattr(Device, sort_by)))
+    else:
+        query = query.order_by(desc(getattr(Device, sort_by)))
+
     total = await db.scalar(select(func.count()).select_from(query.subquery()))
     devices = await db.scalars(query.offset(offset).limit(limit))
 
-    return DeviceListResponse(devices=devices.all(), total=total)
+    return {"devices": devices.all(), "total": total}
 ```
 
-**Frontend (Vue 3):**
+**Frontend:**
 ```vue
-<!-- frontend/src/views/superadmin/DevicesView.vue -->
-<template>
-  <div class="page">
-    <div class="page-header">
-      <h1>{{ $t('devices.title') }}</h1>
-
-      <!-- Поле поиска -->
-      <div class="search-box">
-        <input
-          v-model="searchQuery"
-          @input="debouncedSearch"
-          :placeholder="$t('devices.searchPlaceholder')"
-          type="text"
-        />
-        <i class="search-icon">🔍</i>
-      </div>
-    </div>
-
-    <table class="data-table">
-      <thead>
-        <tr>
-          <th>{{ $t('devices.simpleId') }}</th>
-          <th>{{ $t('devices.macAddress') }}</th>
-          <th>{{ $t('devices.organization') }}</th>
-          <th>{{ $t('common.status') }}</th>
-          <th>{{ $t('devices.lastSeen') }}</th>
-        </tr>
-      </thead>
-      <tbody>
-        <tr v-for="device in devices" :key="device.id">
-          <td>#{{ device.simple_id }}</td>
-          <td><code>{{ device.mac_address }}</code></td>
-          <td>{{ getOrganizationName(device.organization_id) }}</td>
-          <td><span class="badge" :class="`status-${device.status}`">{{ device.status }}</span></td>
-          <td>{{ formatDate(device.last_seen_at) }}</td>
-        </tr>
-      </tbody>
-    </table>
-
-    <!-- Пагинация -->
-    <div class="pagination">
-      <button @click="prevPage" :disabled="offset === 0">Prev</button>
-      <span>{{ currentPage }} / {{ totalPages }}</span>
-      <button @click="nextPage" :disabled="!hasMore">Next</button>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from 'vue'
-import { debounce } from 'lodash-es'
-import devicesApi from '@/api/devices'
-
-const devices = ref([])
-const searchQuery = ref('')
-const offset = ref(0)
-const limit = ref(50)
-const total = ref(0)
-
-const hasMore = computed(() => offset.value + limit.value < total.value)
-const totalPages = computed(() => Math.ceil(total.value / limit.value))
-const currentPage = computed(() => Math.floor(offset.value / limit.value) + 1)
-
-async function loadDevices() {
-  try {
-    const response = await devicesApi.getAllSuperadmin({
-      search: searchQuery.value,
-      offset: offset.value,
-      limit: limit.value
-    })
-    devices.value = response.devices
-    total.value = response.total
-  } catch (err) {
-    console.error(err)
-  }
-}
-
-const debouncedSearch = debounce(() => {
-  offset.value = 0  // Reset to first page
-  loadDevices()
-}, 300)  // 300ms delay
-
-function nextPage() {
-  offset.value += limit.value
-  loadDevices()
-}
-
-function prevPage() {
-  offset.value = Math.max(0, offset.value - limit.value)
-  loadDevices()
-}
-
-onMounted(loadDevices)
-</script>
-```
-
-**Переводы (i18n):**
-```javascript
-// frontend/src/i18n/index.js
-const messages = {
-  ru: {
-    devices: {
-      searchPlaceholder: 'Поиск по MAC, организации, владельцу...'
-    }
-  },
-  en: {
-    devices: {
-      searchPlaceholder: 'Search by MAC, organization, owner...'
-    }
-  }
-}
-```
-
-#### 2. **Быстрый просмотр в таблице**
-
-- Компактная таблица с основной информацией
-- Сортировка по любой колонке
-- Pagination для больших списков
-
-#### 3. **Inline индикаторы**
-
-- Статус устройства (online/offline/error) - цветные badges
-- Дата last_seen с человекочитаемым форматом
-- Organization name вместо ID
-
-### Что НЕ брать из Legacy
-
-#### 1. **Inline Editing** ❌
-
-**Проблемы:**
-- Случайные изменения (кликнул мимо)
-- Нет валидации
-- Нет подтверждения
-- Нет истории изменений
-
-**Решение:** Модальные окна для редактирования (как сейчас)
-
-#### 2. **Текстовые связи (MAC через `;`)** ❌
+<input
+  v-model="searchQuery"
+  @input="debouncedSearch"
+  placeholder="Поиск по MAC, организации, email..."
+  class="search-input"
+/>
+```
 
-**Проблема:** `device` text NOT NULL - "b8:27:eb:c1:46:0e;cc:2d:e0:ca:9f:7e;"
+### 🔨 TODO Шаг 2: Bulk import устройств (приоритет MEDIUM)
 
-**Решение:** Нормализованная БД с foreign keys (как сейчас)
+**Форма Create Organization:**
 
-#### 3. **OpenVPN туннели** ❌
+Добавить секцию:
+```
+☐ Bulk add devices
 
-**Из ТЗ:** "Сейчас у нас VPN не будет"
+Paste MAC addresses:
+┌────────────────────────────┐
+│ ac:84:c6:42:17:90         │
+│ cc:2d:e0:ca:9f:7e         │
+│ d8:0d:17:5e:07:94         │
+└────────────────────────────┘
 
-**Убираем:**
-- `ovpn_flag`
-- `ovpn_addr`
-- VPN страница в навигации
+Separator: [; or , or newline ▼]
 
-#### 4. **Множественные флаги и адреса** ❌
+[Parse]  →  "✓ Found 3 valid MAC addresses"
 
-**Legacy имело:**
-- `wf_flag` + `wf_addr` (WiFi)
-- `bt_flag` + `bt_addr` (BLE)
-- `fw_flag` + `fw_addr` (Firmware)
+[Create Organization with 3 devices]
+```
 
-**Новый подход:**
-- Модульность на уровне Organization: `wifi_enabled`, `ble_enabled`
-- Единый API endpoint вместо хардкод URL в БД
-- Config как JSONB для гибкости
+**Backend:**
+```python
+@router.post("/organizations")
+async def create_organization(
+    data: OrganizationCreateSchema,
+    devices: Optional[List[str]] = None  # List of MAC addresses
+):
+    # Create organization
+    org = Organization(**data.dict())
+    db.add(org)
+    await db.flush()
+
+    # Create devices if provided
+    if devices:
+        for mac in devices:
+            device = Device(
+                mac_address=mac,
+                organization_id=org.id,
+                simple_id=await get_next_simple_id()
+            )
+            db.add(device)
+
+    await db.commit()
+    return org
+```
 
-#### 5. **Inline WiFi credentials** ❌
+### 🔨 TODO Шаг 3: Фильтры по статусу (приоритет LOW)
 
-**Legacy:** `wf_client_ssid` и `wf_client_psk` в таблице configs
+**UI:**
+```vue
+<div class="status-filters">
+  <button
+    v-for="status in ['all', 'online', 'offline', 'error']"
+    :class="{ active: selectedStatus === status }"
+    @click="selectedStatus = status"
+  >
+    {{ status }} ({{ counts[status] }})
+  </button>
+</div>
+```
 
-**Проблема:**
-- Пароли в открытом виде
-- Одинаковые для всех устройств организации
+**Backend:**
+```python
+@router.get("/devices/counts")
+async def get_device_counts():
+    counts = await db.execute(
+        select(
+            Device.status,
+            func.count(Device.id).label('count')
+        ).group_by(Device.status)
+    )
+    return {row.status: row.count for row in counts}
+```
 
-**Решение:**
-- Credentials на уровне Organization (в зашифрованном виде или в vault)
-- Device получает через защищенный API
-
-## Новая структура Devices Panel
-
-### Фильтры (обязательно)
-
-**Для новой админки:**
-
-1. **Глобальный поиск** (текстовое поле)
-   - По MAC-адресу
-   - По simple_id (#1, #2, #3)
-   - По имени организации
-   - По email владельца организации
-
-2. **Фильтр по организации** (dropdown для superadmin)
-   - "Все организации"
-   - "Не назначены"
-   - Список организаций
-
-3. **Фильтр по статусу** (chips/badges)
-   - Online
-   - Offline
-   - Error
-
-4. **Сортировка**
-   - По Simple ID (по умолчанию)
-   - По дате последнего подключения
-   - По организации
-   - По статусу
-
-### Таблица устройств
-
-**Колонки (минимум):**
-
-| Колонка | Описание | Для Superadmin | Для Client |
-|---------|----------|----------------|------------|
-| Simple ID | #1, #2, #3 | ✅ | ✅ |
-| MAC Address | `ac:84:c6:d4:9c:c4` | ✅ | ✅ |
-| Organization | Название организации | ✅ | ❌ (скрыта) |
-| Owner | Email владельца | ✅ | ✅ |
-| Status | online/offline/error | ✅ | ✅ |
-| Last Seen | "2 минуты назад" | ✅ | ✅ |
-| Actions | Edit, Delete | ✅ | Owner/Admin only |
-
-### Детали устройства (модальное окно или отдельная страница)
-
-**Общая информация:**
-- Simple ID
-- MAC Address
-- Organization
-- Status
-- Created At
-- Last Seen At
-- IP Address (если онлайн)
-
-**Конфигурация (JSONB):**
-```json
-{
-  "wifi_enabled": true,
-  "ble_enabled": true,
-  "upload_interval": 300,
-  "scan_interval": 60,
-  "custom_settings": {}
-}
-```
-
-**История подключений:**
-- Таблица последних 10 подключений
-- Timestamp, IP, Duration
-
-**Действия:**
-- Изменить организацию (superadmin)
-- Изменить статус
-- Удалить устройство (с подтверждением)
-- Просмотр audit logs
-
-## Сравнение Legacy vs Новый проект
-
-| Функция | Legacy | Новый проект |
-|---------|--------|--------------|
-| **Аутентификация** | ❌ Нет | ✅ JWT (access + refresh) |
-| **Multi-tenant** | ❌ Все в одной куче | ✅ Изоляция по organization_id |
-| **Роли** | ❌ Нет | ✅ 6 ролей (superadmin, owner, admin, manager, operator, viewer) |
-| **Поиск** | ✅ По всем полям | ✅ По MAC, org, user, simple_id |
-| **Редактирование** | ✅ Inline (опасно) | ✅ Модальные окна с валидацией |
-| **Simple ID** | ❌ Нет | ✅ Auto-increment (#1, #2, #3) |
-| **OpenVPN** | ✅ Было | ❌ Убрали |
-| **Audit Logs** | ❌ Нет | ✅ Полное логирование |
-| **Статусы** | ❌ Нет | ✅ online/offline/error |
-| **Пагинация** | ✅ DataTables | ✅ Backend pagination |
-| **i18n** | ❌ Только русский | ✅ RU/EN |
-
-## Приоритеты реализации
-
-### Шаг 1: Базовый функционал (уже есть)
-- ✅ CRUD устройств
-- ✅ Фильтр по организации
-- ✅ Статусы
-- ✅ Last seen
-
-### Шаг 2: Поиск (TODO)
-- [ ] Универсальное поле поиска
-- [ ] Backend endpoint с фильтрацией
-- [ ] Debounce для поиска
-- [ ] Подсветка результатов
-
-### Шаг 3: Детали устройства (TODO)
-- [ ] Модальное окно с полной информацией
-- [ ] История подключений
-- [ ] Редактирование конфига (JSONB)
-- [ ] Просмотр audit logs для устройства
-
-### Шаг 4: Улучшения UX (TODO)
-- [ ] Сортировка таблицы
-- [ ] Фильтр по статусу (chips)
-- [ ] Экспорт в CSV/Excel
-- [ ] Bulk actions (массовое удаление/изменение)
-
-## Выводы
-
-### Ключевые изменения от Legacy
-
-1. **Безопасность на первом месте:**
-   - JWT аутентификация
-   - RBAC система прав
-   - SQL injection защита (SQLAlchemy)
-   - Audit logging
-
-2. **Multi-tenant архитектура:**
-   - Изоляция данных по организациям
-   - Разные уровни доступа
-
-3. **UX улучшения:**
-   - Simple ID вместо длинных MAC
-   - Модальные окна вместо inline edit
-   - Подтверждение опасных действий
-   - i18n поддержка
-
-4. **Современный стек:**
-   - FastAPI вместо PHP
-   - Vue 3 вместо jQuery
-   - PostgreSQL вместо MySQL
-   - REST API вместо прямых SQL запросов
-
-### Что сохранили
-
-1. **Универсальный поиск** - ключевая фича для удобства
-2. **Компактная таблица** - быстрый просмотр списка
-3. **Статусы и индикаторы** - визуальная обратная связь
-4. **Пагинация** - для больших списков
-
-### Что убрали
-
-1. OpenVPN туннели (по ТЗ)
-2. Inline editing (небезопасно)
-3. Текстовые связи через `;`
-4. Хардкод URL в БД
-5. WiFi credentials в таблице устройств
+---
+
+## ИТОГО: Сравнение Legacy → New
+
+| Фича | Legacy | New Project | Status |
+|------|--------|-------------|--------|
+| **Универсальный поиск** | ✅ DataTables | ✅ Backend SQL | 🔨 TODO |
+| **Компактная таблица** | ✅ Все поля видны | ✅ Основные поля | ✅ Done |
+| **Сортировка** | ✅ По всем колонкам | ✅ По ключевым | ✅ Done |
+| **Inline editing** | ✅ jEditable | ❌ Убрали | - |
+| **Модальные окна** | ❌ Не было | ✅ Для редактирования | ✅ Done |
+| **Bulk add devices** | ✅ Textarea с ";" | ✅ Парсинг списка | 🔨 TODO |
+| **Multi-tenant** | ❌ Все в одной куче | ✅ Изоляция | ✅ Done |
+| **RBAC** | ❌ Нет ролей | ✅ 6 ролей | ✅ Done |
+| **Audit logs** | ❌ Нет | ✅ Полное логирование | ✅ Done |
+| **JWT auth** | ❌ Нет | ✅ Access + refresh | ✅ Done |
+| **Статусы** | ❌ Только last seen | ✅ online/offline/error | ✅ Done |
+| **Simple ID** | ❌ Только MAC | ✅ #1, #2, #3 | ✅ Done |
+| **OpenVPN** | ✅ Было | ❌ Убрали | - |
+| **WiFi creds в БД** | ✅ Plain text | ❌ API endpoint | ✅ Done |
+| **Пароли юзеров** | ❌ Plain text | ✅ Bcrypt hash | ✅ Done |
+
+---
+
+## ВЫВОДЫ
+
+### Что сохранили (essence, не реализацию):
+
+1. ✅ **Универсальный поиск** - самая полезная фича для быстрой работы
+2. ✅ **Компактное отображение** - вся информация на виду
+3. ✅ **Быстрое создание** - организация + устройства одним действием
+4. ✅ **Сортировка** - удобный просмотр больших списков
+
+### Что выбросили:
+
+1. ❌ **Inline editing** - опасно, заменили на модальные окна
+2. ❌ **OpenVPN** - не нужно по ТЗ
+3. ❌ **Хардкод в БД** - заменили на JSONB config + API endpoints
+4. ❌ **Текстовые связи** - заменили на нормализованную БД
+5. ❌ **Plain text пароли** - заменили на bcrypt
+6. ❌ **Отсутствие auth** - добавили JWT + RBAC
+
+### Что улучшили:
+
+1. ✨ **Безопасность:** JWT, RBAC, bcrypt, SQL injection защита
+2. ✨ **Multi-tenant:** изоляция данных по организациям
+3. ✨ **Audit trail:** кто, когда, что сделал
+4. ✨ **Модальные окна:** валидация, подтверждение, UX
+5. ✨ **Simple ID:** удобные номера вместо MAC
+6. ✨ **Статусы:** online/offline/error с индикаторами
+7. ✨ **i18n:** RU/EN поддержка
+
+### Next steps:
+
+1. 🔨 Реализовать универсальный поиск (HIGH priority)
+2. 🔨 Bulk import устройств (MEDIUM priority)
+3. 🔨 Фильтры по статусу (LOW priority)
+4. 🔨 Export to CSV/Excel (LOW priority)