Files
ipam_control/docs/changes/009-bulk-actions/PLAN.md
T
ayurishchevandClaude Sonnet 5 cd09ef0805 Задачи 006-010: пользователи, исправление удаления, журнал отказов, групповые операции, автовыделение префиксов
006 Пользователи: API /users (CRUD, смена своего пароля), раздел UI «Пользователи»,
    события журнала user.*, защита от отключения/удаления себя и последнего админа.
007 Исправление удаления организации: VRF удаляются явным DELETE до организации
    (без relationship() порядок DELETE не гарантирован → ложный 409).
008 Журнал фиксирует отказы в удалении (<entity>.delete_blocked) со списком
    мешающих объектов в «Данных»: организация, VRF, тип устройства, префикс, пользователь.
009 Выбор строк чекбоксами и групповые операции в UI (удаление, смена типа устройств,
    статус префиксов и адресов, доступ пользователей); цикл запросов из UI, итог и список отказов.
010 Автовыделение следующего вложенного префикса: POST/GET /prefixes/{id}/subnets/next,
    первый свободный выровненный блок; пункт «Добавить вложенный (авто)» в меню префикса.

Тесты: 14 (добавлены сценарии для 006, 007/008, 010); исправлена нестабильность
тестов журнала (IPv6-группы с ведущими нулями нормализуются PostgreSQL).
Документация: README.md, docs/changes/006-010 (планы и итоги).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 13:25:33 +03:00

8.0 KiB
Raw Blame History

Множественный выбор и групповые операции в UI (изменение 009)

Context

Сейчас каждую запись можно удалить или изменить только по одной через меню «⋯». Нужен выбор нескольких строк чекбоксами и групповые операции над выбранными на всех экранах управления, кроме «Журнала». Решения пользователя: операции — удаление везде, плюс доступ пользователей, смена типа устройств, статусы префиксов и адресов; выполнение — цикл запросов из UI (бэкенд, API, схема БД и миграции не меняются).

Охват

Экран Чекбокс у строк Групповые операции
Организации все Удалить
Операторы все Удалить
Устройства все Удалить; «Сменить тип ▾»
Префиксы только листовые (как и меню «⋯» сейчас) Удалить; «Статус ▾» (Активен/Резерв/Устарел)
Адреса подсети только занятые/резерв/устаревшие (у «Свободен» нет id) Удалить; «Статус ▾» (Назначен/Резерв/Устаревший)
Пользователи все, кроме своей учётной записи (чекбокс disabled) Удалить; «Разрешить доступ»; «Отключить доступ»
Журнал — не затрагивается —
Не входят: окна «Управление VRF» и «Управление типами» (это модальные мини-списки, а не вкладки). Для роли viewer чекбоксов нет (запись запрещена).

Реализация (web/app.js, web/styles.css)

  • Состояние: S.sel (Set id) и S.selectable (id выбираемых строк текущего экрана). S сбрасывается при смене экрана; при каждой перерисовке выделение усекается до присутствующих строк (pruneSel), поэтому поиск/фильтры не оставляют «невидимых» выбранных.
  • Общие фрагменты рядом с badge/btn/iconBtn: selCell(id, disabled) (чекбокс в <label>, id="sel-<id>" — фокус восстанавливается штатным кодом draw()), selAllCell() (в шапке; indeterminate выставляется после рендера в draw()), selCols(cols) (добавляет колонку 40px; для не-админа — без неё), bulkBar({...}) (панель «Выбрано: N · действия · Снять выделение», рендерится только при S.sel.size), bulkMenu(id, label, items) (на существующих popMenu/data-action="menu").
  • Экраны: в screens.orgs/isps/devices/prefixes/address/users — selCols в --cols строк и шапки (у орг. выносится в константу), selCell первой ячейкой, панель bulkBar под фильтрами, класс selected у выбранных строк. Клики по чекбоксу не открывают строку: общий обработчик уже пропускает input/label (изменение 005).
  • Исполнитель bulk(title, ids, labelOf, call, after): последовательно вызывает существующие DELETE/PATCH (/organizations, /isps, /devices, /prefixes, /addresses, /users), ловит ApiError по каждому элементу, останавливается на 401, защищён от повторного запуска (S.busy). По итогу: toast «выполнено N из M», перерисовка, успешные снимаются с выделения (отказавшие остаются выбранными), при отказах — окно «Не выполнено для: …» со списком «объект — причина» (openDialog). Для организаций после цикла вызывается loadOrgs().
  • Подтверждение удаления — confirm(), как в одиночных действиях («Удалить выбранное: 3 оператора?»). Префиксы удаляются без force: с адресами будут отклонены и попадут в список отказов (принудительное удаление остаётся в одиночном меню).
  • Диспетчеризация: новые actions: sel, sel-all, sel-clear, bulk-del, bulk-access; в actions.pick ветка bulk-status / bulk-type до общего rowActions-поиска. Экран определяется через route() (prefixes с аргументом = «адреса»).
  • CSS: .sel-cell (центрирование, чекбокс 16 px, accent-color: var(--primary)), .bulkbar (строка 48 px, фон #f3f7ff, .pop от top:40px), .tr.selected{background:#eef3ff}.

Журнал и бэкенд

Каждое отклонённое удаление в цикле само попадает в журнал как <entity>.delete_blocked (изменение 008); каждое успешное действие — обычной записью по объекту. Групповая операция отдельным событием не пишется. Серверные правила сохраняются (нельзя удалить/понизить себя и последнего админа → строка в списке отказов).

Артефакты (правила проекта)

docs/changes/009-bulk-actions/PLAN.md (копия плана) и SUMMARY.md; README.md — в раздел «Поведение таблиц UI»: выбор чекбоксами, панель групповых действий, охват и ограничения. Автотесты: бэкенд не менялся, новых тестов нет (правило «минимум тестов»); существующие 13 должны проходить.

Проверка

  1. node --check web/app.js; docker compose -p ipam_control_006 up -d --build app; venv/bin/python -m pytest -q.
  2. Интерфейс (http://192.168.5.9:8088), по возможности через браузерную автоматизацию (skill claude-in-chrome), иначе вручную:
    • Операторы: выбрать 2–3 → «Удалить» → подтверждение → строки исчезли, toast «выполнено 3 из 3»; «Выбрать все» в шапке и состояние «частично» работают.
    • Организации: выбрать организацию с префиксами и пустую → удалено 1 из 2, окно отказов с причиной; в «Журнале» появилась organization.delete_blocked.
    • Устройства: «Сменить тип»; Префиксы/Адреса: «Статус»; Пользователи: «Отключить/Разрешить доступ», у своей строки чекбокс недоступен.
    • Для viewer чекбоксов и панели нет; на «Журнале» — нет.
  3. Тестовые данные для проверки создавать отдельными демо-записями (не удалять существующие демо-данные стенда).