# Admin UI — бизнес-логика

Admin UI — универсальный интерфейс администрирования платформы: единое дерево настроек и типовые формы, через которые администратор настраивает предметные домены (задачи, подписи, почту и другие), не работая с базой напрямую. Сам домен не хранит предметную логику — он задаёт способ, которым эти настройки создаются и применяются. Раздел адресован администраторам и аналитикам: объясняет роль автоадминки, её бизнес-правила, границы и типовые проблемы при настройке доменов через общий интерфейс.

## Обзор и модель домена Admin UI

`admin-ui` — инфраструктурный домен администрирования платформы. Он не хранит предметную логику задач/подписей/почты, но задаёт способ, которым администратор настраивает эти домены через универсальный интерфейс.

Базовая роль домена:

- предоставить единое дерево админ-настроек;
- обеспечить универсальные формы чтения/редактирования данных;
- связать конфигурацию (`dbadmin`, EntityEditor, custom-forms) с runtime-эффектом в целевых доменах.

**Основные сущности**

| Сущность | Назначение |
|---|---|
| `AdminForms` | описание формы администрирования (alias, url, флаги действий) |
| `AdminFormFolders` | иерархия папок админ-дерева |
| `DbaQueryFormSchema` | метаданные формы/полей/секций |
| `DbaQueryObject` | чтение данных формы |
| `DbaCRUDObject` | универсальные операции insert/update/delete |

**Ключевая связка**

`Пункт меню -> Alias -> Конфиг формы -> Данные -> CRUD-операция`

## Бизнес-правила и основные сценарии Admin UI

1. Доступ к дереву и операциям админки ограничен суперадминским контуром.
2. Если `AdminForms.Url` пуст, форма открывается как autoadmin (`/admin-ds/forms/{alias}/grid`).
3. Если `Url` задан, форма может работать как custom-route или legacy-экран.
4. Метаданные формы — это часть бизнес-контракта; их ошибка эквивалентна поломке функционала домена, который через форму настраивается.
5. Состав полей формы автоадминки задаётся её метаданными: в схему формы попадают только те колонки объекта, которые заведены в её секциях.

Этим же правилом ограничивается доступ к чувствительным данным. Форма «Пользователи» (`users`) построена над таблицей пользователей, и колонки хеша и соли пароля в её секциях не заведены — в схему формы они не приходят, поэтому в таблице и в фильтрах по колонкам их нет. Выгрузка списка пользователей в Excel собирается отдельным набором полей на стороне сервера, от состава колонок формы не зависит и хеша с солью не содержит. Сами поля остаются в таблице пользователей; работа с паролями — предмет домена [`auth`](https://help.1forma.ru/domains/auth/business.md).

6. Состав дерева админки зависит от поставки. В 1F Certificate Edition (ФСТЭК-сборке) в него не попадают пункты, ведущие к обслуживающим утилитам, которых в объекте оценки нет: такой пункт открывал бы страницу неустановленной программы. Пустой пункт из дерева убирается при сборке профиля, сама запись формы остаётся в базе общей для обеих поставок.

**1. Поиск и открытие настройки**

Администратор открывает дерево, выбирает форму и попадает либо в autoadmin-grid/edit, либо в кастомный экран.

**2. Изменение конфигурации через универсальную форму**

Форма грузит схему и данные, администратор редактирует запись, backend выполняет `DbaCRUDObject`.

**3. Обновление структуры админки**

При добавлении/изменении `AdminForms`/`AdminFormFolders` меняется карта навигации для админ-пользователей.

**4. Работа с кастомной формой сервисов**

Администратор открывает раздел «Подключения» в административной панели, выбирает пункт «Сервисы» и попадает в кастомную форму управления сервисами. Форма отображает список зарегистрированных сервисов с возможностью создания нового, редактирования или удаления существующего. При создании/редактировании состав полей на форме автоматически подстраивается под выбранный тип сервиса (ExchangeWebService, Diadoc, СБИС, Dadata, PayControl, Tika и др.). Для каждого типа отображаются только релевантные настройки подключения (URL, credentials, таймауты, дополнительные параметры).

## Защита несохранённых изменений

Пока форма административной страницы изменена и не сохранена, уход с неё не выполняется молча: система спрашивает, что делать с внесёнными правками. Правило общее для кастомных страниц администрирования — настроек категории, карточек пользователя и группы, автоматизаций и подписей, системных настроек, сводных разделов, шаблонов задач и справочников.

**Когда появляется вопрос.** Окно открывается при попытке уйти с изменённой формы: кнопкой «Отмена» в самой форме, крестиком окна, клавишей `Esc`, кликом по фону модального окна, по хлебным крошкам, кнопке «Назад» или ссылке, открываемой в текущем окне, а также при переключении вкладки внутри страницы. Если изменений нет, уход выполняется сразу.

**Что предлагает окно.** Вопрос «Сохранить изменения» с тремя действиями: **«Сохранить»** записывает данные и продолжает исходный переход только после успешной записи — если запись не прошла, форма остаётся открытой с внесёнными данными и показывает причину отказа; **«Не сохранять»** выполняет переход без записи, правки отбрасываются; **«Отмена»** и крестик окна закрывают вопрос, и пользователь остаётся в форме.

Если среди изменённых форм нет ни одной сохраняемой, показывается двухкнопочное подтверждение «Не сохранять» / «Отмена». Страница, которая уходит сама — после удаления сущности или успешного сохранения, — вопроса не вызывает: такой уход разрешается отдельно и действует на один переход.

**Где правила нет.** Экраны, которые записывают изменения сразу при действии, отложенного состояния не держат и вопроса не показывают: в карточке пользователя это вкладки «Сервис», «Группы» и «Вид», в карточке группы — вкладка «Состав». Журналы администрирования — табличные представления только на чтение, редактируемых форм в них нет.

## Навигация: хлебные крошки

Хлебные крошки — горизонтальная цепочка ссылок над содержимым административной страницы. Она показывает путь от корня админки до открытой настройки и позволяет вернуться на любой промежуточный уровень, не перебирая дерево заново.

**Состав цепочки**

| Уровень | Что показывает | Переход по клику |
|---|---|---|
| Значок «домой» | корень административной панели | `/administration` |
| Раздел верхнего уровня | «Категории» или «Пользователи» — в зависимости от того, к чему относится настройка | грид автоадминки соответствующего справочника |
| Объект | категория или пользователь, настройки которого открыты | страница настроек объекта |
| Настройка | название открытой настройки и её вложенные уровни | задаётся страницей; часть уровней некликабельна |
| Текущая страница | последний элемент цепочки | без перехода |
**Иконка категории.** У страниц, относящихся к категории, перед цепочкой выводится иконка этой категории — тот же знак, что виден в дереве категорий и в списках задач. Иконку задают в настройках категории, на странице «Общие настройки». Если у категории иконка не задана, шапка выглядит как раньше — только название без значка. На глобальных страницах, не привязанных к категории, и на странице прав, открытой для раздела, иконка не выводится.

**Глобальные страницы без категории.** Часть административных настроек относится к площадке в целом, а не к категории или пользователю. На таких страницах цепочка сокращается до значка «домой» и названия открытой страницы: уровни раздела и объекта не выводятся, поэтому ссылок на категорию и её грид нет. Название последнего элемента не кликабельно, а значок «домой» ведёт в корень административной панели, как и в полной цепочке.

Название последнего элемента страница берёт из того же ресурса, что и собственный заголовок, поэтому подпись в цепочке совпадает с заголовком на экране и переводится вместе с ним. Так устроены, в частности, вставки CSS и JS, оргструктура, публикации, наборы стикеров и HTML-шаблон подписи ВКС.

**Имя и идентификатор**

Основной текст элемента — имя объекта: название категории, имя пользователя, название настройки. Числовой идентификатор выводится отдельным элементом-суффиксом в скобках сразу за именем. Цепочка читается по именам, при этом ID остаётся на виду — он нужен при обращении в поддержку и при сверке настроек между контурами. Если имя недоступно, вместо него отображается `ID <номер>`.

По той же логике формируется заголовок вкладки браузера: имя объекта, идентификатор в скобках, название открытого раздела и название системы.

На страницах, которые работают через EntityEditor (`/admin/entity/...`), в цепочке показывается текущий раздел — описание схемы сущности.

**Несохранённые изменения на страницах сущностей.** Страницы сущностей, открываемые по адресу `/admin/entity/...` (типы групп, быстрые ответы, регистры, короткие ссылки и другие), берегут внесённые, но не сохранённые данные. Если на такой странице есть несохранённые изменения, уход по хлебным крошкам, кнопке «Назад» или по ссылке в этом же окне открывает окно «Сохранить изменения» с тремя действиями. «Сохранить» записывает данные и выполняет переход только после успешной записи: если сохранить не удалось — например, не заполнены обязательные поля, — страница остаётся открытой с внесёнными данными, а на экране появляется сообщение о неправильно заполненных полях. «Не сохранять» выполняет переход, ничего не записывая. «Отмена» и крестик оставляют на странице с уже введёнными данными. Если изменений нет, уход выполняется сразу, без окна.

**Сброс внесённых изменений.** Кнопка «Сбросить» возвращает все поля страницы сущности к состоянию на момент её открытия или последнего сохранения, а внесённые и не сохранённые правки отбрасывает. Локализованные текстовые поля — однострочный текст, многострочный текст, форматированный текст и код — возвращают при сбросе исходные значения переводов на всех языках, включая правки, сделанные в окне локализации. После сброса форма считается неизменённой, поэтому уход со страницы выполняется сразу, без окна «Сохранить изменения».

Правило относится к самой странице сущности: редактор отдельного поля внутри неё имеет собственную логику сохранения и под это окно не подпадает, как и табличные представления сущности (`/admin/entity/.../represent/...`).

**Скрытие цепочки**

Административные страницы открываются не только из дерева админки, но и как встроенные блоки — например, из карточки задачи. В такие ссылки добавляется `hideAdminBreadcrumbs=1` — признак встроенного режима, в котором цепочка не выводится: путь пользователю уже задан контекстом, из которого он пришёл, а собственная навигация страницы дублировала бы его. Префикс `/noframe/` в маршруте решает смежную задачу — убирает шапку и боковую панель.

**Источник перехода на кастомных страницах.** На страницах настроек категории и на экранах автоматизации цепочка показывает не только текущую настройку, но и то, откуда администратор пришёл. Открыв редактор пакета действий, смарт-правила или смарт-выражения, администратор видит в крошках источник перехода и категорию:

| Откуда открыли | Что показывают крошки |
|---|---|
| Из категории | Категория → название сущности |
| Из списка «Все смарты» | Все → категория пакета → название сущности |
| Из «Общих смартов» | Общие смарты → категория пакета → название сущности |
| Из настроек представления | Настройки представления → категория → название настройки |

Каждый узел цепочки, кроме текущего, кликабельный: клик по «Все» или «Общим смартам» возвращает на список смарт-автоматизаций с открытой вкладкой, клик по категории — на её настройки.

## Примечания и подсказки категории

В дереве настроек категории есть группа «Примечания». Она объединяет две формы автоадминки; обе открываются с предфильтром по текущей категории:

- **Примечания** (`subcat-notes`) — заголовок и текст произвольного примечания. Флаги записи определяют, где оно показывается: при создании задачи и/или в открытой задаче.
- **Подсказки** (`subcat-state-hints`) — текст, привязанный к статусу маршрута; выводится, когда задача находится в этом статусе. Заполняются на отдельном экране AdminSPA, по одной подсказке на статус.

Примечания остаются типовым примером связки «пункт дерева, псевдоним формы, универсальный CRUD»: отдельного экрана у них нет. У подсказок экран свой: пункт дерева ведёт на отдельную страницу AdminSPA, где слева список уже заданных подсказок, а справа — выбор статуса и текст. Список статусов приходит по текущей категории, и статусы, для которых подсказка уже есть, в выборе не показываются: у статуса может быть только одна подсказка. Сохранение при этом идёт через ту же форму с псевдонимом `subcat-state-hints`, то есть данные и права прежние.

## Универсальные шаблоны: сохранение содержания

Редактор содержания универсального шаблона (пункт «Шаблонизация» в блоке «Формы» категории) открывается модальным окном поверх списка шаблонов. Если содержание изменено и окно закрывают — крестиком, клавишей Esc, кликом по фону или кнопкой «Отмена», — открывается вопрос с тремя кнопками:

- «Сохранить» — содержание записывается в шаблон, окно закрывается только после успешного ответа сервера, а список шаблонов обновляется;
- «Не сохранять» — окно закрывается, внесённые изменения отменяются;
- «Отмена» — окно остаётся открытым, введённый текст сохраняется для продолжения правки.

Если запись завершилась ошибкой, окно остаётся открытым с введённым текстом и попытку можно повторить. Когда содержание не менялось, окно закрывается без вопроса.

Вопрос задаётся только в режиме правки содержания: в окне привязки шаблона из списка содержание не редактируется, и закрытие проверки не требует.

## Журналы: ссылка на конкретную запись

Строку журнала можно передать ссылкой — коллега откроет нужную запись, не ища её глазами. Журналы живут в двух видах экранов: страницы раздела «Журналы» (`/administration/data-source/<источник>`) и журнальные формы автоадминки (`/admin-ds/forms/<псевдоним>/grid`). В обоих случаях строку табличного представления можно передать ссылкой. Для этого в контекстном меню строки (правая кнопка мыши) есть пункт **«Копировать ссылку»**: он помещает в буфер обмена абсолютный адрес вида

`https://<хост>/spa/administration/data-source/<источник>?openLog=<ключ записи>`

Ссылка собирается от адреса текущей страницы, поэтому несёт хост того стенда, с которого её скопировали, — при пересылке коллеге указывайте контур, на котором запись нужно открыть. Переход по ссылке открывает тот же журнал, что и обычный переход из раздела «Журналы».

Пункт показывается только для журнальных источников — тех, чей адрес начинается с `log-` (журнал ошибок, действий, автоматизаций, заданий, PUSH, ошибок бизнес-логики, почтовых джобов, синхронизации 1С, телефонных звонков, удалённых задач, экспорта в Excel). На страницах административных справочников пункт не появляется: ключ записи известен только для журналов. Если у строки нет значения ключа, пункт для неё не показывается.

**Журнальные формы автоадминки.** Кроме страниц раздела «Журналы» ссылку отдаёт и список журнальных форм автоадминки — журнал мобильного приложения, лог потоков, лог транзакций, журнал очередей сообщений, журнал действий с файлами и журнал очереди. Признак здесь не «адрес начинается с log-», а сама форма из списка: у журнальных форм пункт «Копировать ссылку» есть, у остальных административных форм — нет, даже если ключ записи у строки есть. Если в форме строка не даёт единственного ключа, пункт для неё не показывается.

Журналы живут в двух видах экранов, и перечень для каждого свой:

- **страницы раздела «Журналы»** — источники с префиксом `log-`: журнал ошибок, действий, автоматизаций, заданий, PUSH-уведомлений, ошибок бизнес-логики, почтовых джобов, синхронизации 1С, телефонных звонков, удалённых задач и экспорта в Excel;
- **журнальные формы автоадминки** — формы журнала мобильных событий, лога потоков, лога транзакций источника данных, лога очереди сообщений и лога действий с файлами.

**Открытие записи по ссылке.** Адрес с параметром `openLog` открывает не просто журнал: табличное представление ставит фильтр по скрытой колонке с ключом записи и раскрывает найденную строку. Для страницы журнала это `https://<хост>/spa/administration/data-source/<источник>?openLog=<ключ записи>`, для журнальной формы автоадминки — `https://<хост>/spa/admin-ds/forms/<псевдоним>/grid?openLog=<ключ записи>`.

Если параметр пуст, содержит не число или такого ключа в журнале нет, журнал открывается как обычно: без ошибки, без фильтра и без раскрытой строки. Поставленный по ссылке фильтр держится только на время открытия ссылки — в сохранённые настройки табличного представления он не попадает, поэтому при следующем обычном открытии журнала его нет. На нежурнальных экранах — страницах административных справочников и обычных формах автоадминки — параметр игнорируется. Устройство произвольных источников данных и кнопок-действий описано в разделе [Произвольные источники данных](https://help.1forma.ru/domains/grids/custom-data-sources.md).

## Версии объектов БД

SQL-объекты базы данных — хранимые процедуры, функции, представления — открываются в редакторе: «Администрирование» → «Объекты БД» → выбрать объект. В редакторе есть пункт «Просмотр версий»: он открывает список сохранённых версий объекта. В строке версии выводятся её номер и дата создания; номер подписан как «Версия № N».

Дата версии — это дата создания самой версии (в ответе API — `versionCreateDate`), а не дата последнего изменения объекта и не время открытия панели. У каждой версии своя дата, и при повторном открытии панели даты не меняются. Номер версии растёт с каждой правкой объекта, поэтому по номеру всегда видно, какая правка сделана позже.

История версий смарт-выражений и Lua-скриптов устроена так же: у каждой версии своя дата создания, текущее время в неё не подставляется.

**Сравнение двух версий.** Любые две версии из списка можно сравнить между собой: выбирается пара, и различия показываются построчно — общим для платформы компонентом сравнения, тем же, что используется в остальных местах. В сравнении участвуют версии одного объекта, поэтому пара проверяется: слева должна стоять более ранняя версия, справа — более поздняя, а у смарт-выражений и скриптов ещё и совпадать тип содержимого. Сравнение работает и для версий объектов БД — процедур и функций, — и для версий смарт-выражений и скриптов, включая языки скриптов: подсветка в сравнении берётся по типу скрипта, а не одной жёстко заданной подсветкой. Одиночный просмотр версии и восстановление значения в редакторе при этом не меняются, панель сравнения открывается правее редактора и правой полосы прокрутки не имеет.

**Признак ошибки отображения.** Если в панели «Просмотр версий» у всех версий объекта показана одна и та же дата, совпадающая с моментом открытия панели, а номера версий при этом разные, — это ошибка отображения, а не потеря данных: в системе даты версий сохранены правильно. О такой панели стоит сообщить в поддержку, приложив схему и имя процедуры.

## Инциденты, риски и границы домена Admin UI

**Типовые бизнес-инциденты**

| Симптом | Вероятная первопричина |
|---|---|
| «Нужный раздел админки пропал» | проблема в дереве `forms/tree` или структуре папок; на стенде в 1F Certificate Edition (ФСТЭК-сборке) пункт может не отдаваться по составу поставки |
| «Открывается не тот экран» | неверный `Url` у формы |
| «Сохраняю, но эффект не тот/нет» | ошибка payload/action или несоответствие схемы форме |
| «После изменения меню всё по-старому» | stale-кэш дерева на клиенте |

**Риски домена**

1. Ошибка в инфраструктурной форме бьёт по целому предметному домену.
2. Универсальный CRUD ускоряет разработку, но увеличивает риск ошибок метаданных.
3. Несогласованность `alias/url/schema` приводит к ложным симптомам «backend не работает».

**Границы домена**

`admin-ui` отвечает за доставку и исполнение административного UI-контракта. Семантика конкретных параметров остаётся в профильных доменах (`auth`, `tasks`, `integrations`, `spaces`, `social-network` и т.д.).

**Связанные документы по Admin UI**

- [../system/admin.md](https://help.1forma.ru/domains/system/admin.md)
