# Контейнеры

Общая модель контейнеров мобильного приложения: типы контейнеров, привязка к группам, структура таблиц БД и наследование настроек.

## Структура контейнеров мобильного приложения

Общие сведения о контейнерах как сущности (назначение, шаблоны, кнопки навбара, доступность по группам) — в разделе [Администрирование](https://help.1forma.ru/domains/mobile/admin.md).

Контейнеры — это верхнеуровневые строительные блоки интерфейса мобильного приложения 1Форма. Каждый контейнер определяет отдельную область экрана или функциональный раздел.

## Ключевые контейнеры

В любой системе должны присутствовать четыре основных контейнера (плюс контейнер избранного). Без групповых привязок они доступны всем пользователям.

### Dashboard

Главная страница приложения. Может состоять из нескольких вложенных дашбордов (sub-dashboards), каждый из которых представляет набор плиток с данными, счётчиками или навигацией по разделам.

- Шаблон: `dashboard`
- ClientId: `Dashboard`
- Контейнер по умолчанию (Id=1361, Order=24) — должен идти первым, отображается для всех пользователей без групповых привязок

### TabBar

Нижняя панель кнопок приложения (tab bar). Обеспечивает быструю навигацию между основными разделами.

- Шаблон: `toolbar`
- ClientId: `TabBar`
- Контейнер по умолчанию (Id=1501) — используется всеми пользователями, у которых нет персонального TabBar через групповую привязку

### MainMenu

Боковое меню приложения (drawer / hamburger menu). Содержит пункты навигации, ссылки на разделы, информацию о пользователе.

- Шаблон: `menu`
- ClientId: `MainMenu`
- Если контейнер полностью пустой — бокового меню в приложении не будет

### FavouritesMenu

Контейнер избранного — отображает список избранных элементов пользователя.

- Шаблон: `dashboard`
- ClientId: `FavouritesMenu`

### Устаревшие контейнеры

#### CategorySelectorMenu

Набор элементов, отображавшихся при выборе категории (например, при создании задачи). Обычно включал «Избранное» и «РМГ» (рабочая мобильная группа).

- Шаблон: `menu`
- ClientId: `CategorySelectorMenu`

## Сборка выдачи контейнеров

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

## Привязка контейнеров к группам (ролям)

Контейнеры могут быть привязаны к группам пользователей через таблицу `APIContainerToGroups`. Это позволяет:

- Назначать разным ролям разные дашборды (например, CEO видит сводку по подразделениям, сотрудник — свои задачи)
- Давать персональные TabBar и MainMenu отдельным группам
- Контейнеры без групповой привязки доступны всем пользователям

### Порядок приоритета (Order)

Поле `Order` определяет порядок, в котором контейнеры применяются. Контейнер с **меньшим** `Order` имеет более высокий приоритет — у кого `Order` ниже, тот применяется первым.

### Настройка привязки к группам

Привязка настраивается кнопкой **«Группы»** в строке контейнера: создаётся связь «контейнер → группа», для каждой группы — отдельная запись (`APIContainerToGroups`).

**Логика выбора контейнера** при нескольких записях с одинаковым `Id`:

1. Контейнер с наименьшим `Order`, для которого указана группа пользователя И смарт-фильтр = `true`.
2. Если ни одна группа не подошла — контейнер с наименьшим `Order` без указанных групп И смарт-фильтр = `true`.
3. Если и такого нет — клиент получает произвольный контейнер с этим `Id`.

> **Рекомендация:** всегда настраивать контейнер по умолчанию (пустые группы + наименьший `Order`), чтобы пользователи не видели произвольный контейнер.

### Доступность блоков внутри контейнера

На уровне блока используется ключ `groups` — массив ID групп **через запятую**. При непустом значении клиент проверяет пересечение групп пользователя и массива; если пересечение пустое — блок не отображается. Фильтрация по группам может срабатывать на уровне стека (папки) целиком, а внутри папки — дополнительные фильтрации и скрытия отдельных вложенных элементов. Отображение блоков **не меняется при замещении** (impersonation) — учитываются только группы реального пользователя.

### Права администратора в редакторе (предпросмотр)

В обычной выдаче контейнеров (`POST /app/v1.2/api/mobile/containers/`) фильтры по группам (`APIContainerToGroups`) и смарт-выражению применяются единообразно ко всем пользователям, включая администраторов. Особого «обходного» доступа в выдаче нет — администратор получает в мобильном приложении ровно те же контейнеры, что прошли бы групповой и смарт-фильтр для обычного пользователя.

Открыть **любой** контейнер независимо от привязки к группам можно только в редакторе контейнера в админке. Для этого используется отдельный маршрут предпросмотра `api/containers/preview`: если администратор не входит в группу контейнера, для предпросмотра пропускаются фильтры по группам и смарт-выражениям и отдаётся первая активная запись с заданным `ClientId`. На этот маршрут переключён редактор контейнера (`administration/containers`).

> При ручной проверке учитывайте: предпросмотр (`preview`) и фактическая выдача в МП могут отличаться, потому что preview пропускает групповые и смарт-фильтры.

## Настройка контейнеров

Каждый блок контейнера (плитка, папка, пункт меню, кнопка таббара) отрисовывается по своему **шаблону блока** — набору ключей, определяющих вид и поведение элемента. «Блок» здесь — это элемент контейнера, а не блок секции шаблона задачи (см. [Мобильные шаблоны](https://help.1forma.ru/domains/mobile/mobile-templates.md)). Основные шаблоны блоков: `dashboard`, `dashboardItem`, `taskSourceDashboardItem`, `dashboardStack`, `dashboardSignatures`, `dashboardCustomURL`, `menu`, `menuItem`, `menuSeparator`, `tasksFeed`, `toolbar`, `toolbarItem`, `userProfile`.

### Типы блоков

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

При настройке выбирайте **корректный тип блока** в палитре: если тип выбран неверно, клиент может не отрисовать блок или не выполнить ожидаемое действие.

Если ID блока не оговорён специально (в таблице ниже), он может быть произвольным.

| Тип блока в Палитре | Описание | Возможные значения ID блока |
|---|---|---|
| Папка | Создание иерархии вложенных элементов | `Stack` (для шаблона `dashboardStack` — списка дашбордов). Для папки приходит **пустой `type`**; в id для наглядности пишут `Folder`, но ориентироваться лучше на пустой `type`. ID у плиток не уникален |
| Синдикат | Блок указывает на набор данных (Лента, Задачи, Заголовок, Категория, Звонки, Контакты, Вопросы, Комментарии, Подписи, Отчёты, Группа пользователей). Сервер 1Ф автоматически отдаёт счётчики, заголовки, ID сущностей | см. подтаблицу «Допустимые Id блока-синдиката» ниже |
| Настройки | Используется в левом главном меню | `Settings` |
| Задача | Полный текст и номер задачи | `TaskNNN`, где `NNN` — номер задачи |
| Контакт | Данные пользователя (ФИО, ID контакта, открытые доп. поля профиля) | `UserNNN`, где `NNN` — ID пользователя (корректно как элемент бокового меню) |
| Ссылка | Ссылка относительно веб-адреса «Первой Формы» или на внешний ресурс | |
| Форма новой задачи | По клику открывается форма постановки новой задачи | `NewTaskNNN`, где `NNN` — ID категории |
| Дашборд | По клику открывается дополнительный рабочий стол | Id контейнера с шаблоном `dashboard` |
| Группа пользователей | Данные всех пользователей группы (плитки с контактами на рабочем столе) | `UserGroupNNN`, где `NNN` — ID группы |
| Отчёт | Аналогично ссылке `Link`, но для web-версий отчётов | |
| Избранное | Контейнер избранного пользователя | `FavouritesMenu` |
| Избранное РМГ | Избранное рабочей мобильной группы (исторический сценарий, см. `CategorySelectorMenu (Deprecated)`) | |

> **Не путать «типы блоков в Палитре» и `APITemplate`.** Палитра редактора предлагает **12 типов** блоков (таблица выше) — это то, что выбирает администратор. На уровне БД блокам соответствует 21 `APITemplate` (`dashboardItem`, `taskSourceDashboardItem`, `dashboardStack` и т.д.) — это технические шаблоны рендеринга, а не пункты палитры. Полная структура шаблонов — в разделе «Структура таблиц БД» ниже.



### Создание контейнера

Минимальный административный сценарий создания контейнера:

1. Нажать «Создать» и задать `Id` — он должен начинаться с латинской буквы.
2. Выбрать правильный шаблон контейнера: `dashboard`, `menu` или `toolbar`.
3. Назначить корректный системный `Id` для системных экранов (`Dashboard`, `MainMenu`, `TabBar`, `FavouritesMenu`).
4. Задать `Order`, чтобы контейнер не перехватывал выдачу случайно.
5. Добавить только те блоки, чьи шаблоны и действия реально поддерживаются целевым экраном.
6. Сохранить контейнер и проверить его под живой учётной записью, а не только в preview.

Прочие операции в строке/форме контейнера: **«Создать копию»** — полный дубль контейнера; **«Удалить»** — на форме или в контекстном меню строки; **«Редактор»** — открывает дерево блоков и макет, блоки перетаскиваются мышкой. Блоки «Избранное» и «Избранное РМГ» можно добавлять только в контейнеры с шаблоном `menu`.

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

### Редактирование основных настроек контейнера

При изменении контейнера в первую очередь нужно проверять:

- `Id` и шаблон контейнера;
- `Order` и групповую привязку;
- базовые template-ключи контейнера;
- `rightBarButtons`, если контейнер поддерживает верхнюю панель;
- визуальные ключи и platform-specific поведение.

Критическая ошибка этого слоя — менять `Id` уже используемого системного контейнера без пересборки всей навигационной схемы.

### Редактирование содержимого контейнера

Редактирование содержимого контейнера выполняется через палитру блоков и их шаблоны.

Практические правила:

- сначала выбирать тип блока, потом уже настраивать template-ключи;
- не смешивать сценарии разных экранов: `toolbar` требует `toolbarItem`, `menu` — `menuItem`, `dashboard` — dashboard-family шаблоны;
- вложенность через папки допустима только там, где клиент реально умеет её отрисовать;
- после правок нужно проверять не только визуальный вид, но и `onTap`/`interactions`.

### Копирование контейнера

Копирование контейнера допустимо как способ быстро получить новую вариацию под другую группу или роль, но после копирования нужно сразу проверить:

- что у нового контейнера уникальный `Id`, если только несколько контейнеров с одним `Id` не заданы намеренно;
- что `Order` не конфликтует с существующей выдачей;
- что групповые фильтры и смарт-фильтры не остались от старого сценария;
- что ссылки на кастомные сорсы, категории и дашборды не указывают на старую роль.

### Удаление контейнера

Удалять контейнер безопасно только после проверки, что:

- он не является дефолтным контейнером по `Id`;
- его не выбирает ни одна группа пользователей по `Order` и фильтрам;
- на него не ссылаются плитки, пункты меню, `toolbarItem` или внешние инструкции;
- для устаревших контейнеров вроде `CategorySelectorMenu` больше нет зависимого клиента старой версии.

### Допустимые `Id` блока-синдиката

Для блока-синдиката сервер автоматически отдаёт данные по системному `Id`. Полный список:

| Id блока | Что отдаёт сервер |
|---|---|
| `Calls` | звонки и история звонков |
| `Chats` | список чатов |
| `Contacts` | список контактов |
| `CustomTaskSource` | содержимое публикации пакета действий |
| `Feed` | лента (вопросы, комментарии и т.д.) |
| `DemoSpeech` | голосовая постановка задачи (временно используемый ключ) |
| `TasksFeed` | лента задач (новые, просроченные, личные и т.д.) |
| `MyQuestions` | мои вопросы |
| `QuestionsToMe` | вопросы мне |
| `MyQuestionsAndAnswers` | исходящие вопросы и ответы на них |
| `QuestionToMeAndAnswers` | входящие вопросы и ответы на них |
| `OwnTasks` | личные задачи |
| `OverdueTasks` | просроченные задачи |
| `ToSign` | задачи на подписи |
| `Reports` | отчёты |
| `UserProfile` | профиль текущего пользователя (корректно работает как блок рабочего стола) |
| `TaskSourceNNN` | задачи из источника данных, где `NNN` — ID источника данных задач |
| `SubcatNNN` | задачи из категории, где `NNN` — ID категории |
| `SubcatAll` | задачи из всех категорий |
| `separator` | заголовок-разделитель между элементами контейнера (только визуальный) |

Элементы палитры «**Избранное**» и «**Избранное РМГ**» — наборы блоков, отражающих ссылки в блоке Избранное конкретного пользователя и его рабочие места. Их можно добавлять только в контейнеры с шаблоном `menu` (полноценная древовидная структура); параметры шаблона на эти блоки не влияют.

Если блок использует системный `Id`, но неверный шаблон или неподдерживаемый экран-контейнер, клиент может без ошибки отрисовать его некорректно.

### Лимиты отображения

Практические ограничения отображения:

- для `TabBar` поддерживается максимум 5 элементов;
- в `TabBar` допускается только один полноценный `Dashboard`-элемент;
- для `MainMenu` в верхней панели выводится максимум 2 `rightBarButtons`, остальные уходят в меню «Ещё»;
- безопасная глубина вложенности дашборда — не глубже папки и одного уровня её содержимого;
- права на первый уровень контейнера и на вложенные элементы могут различаться — это следует проверять на клиенте.

### Счётчики и скрытие блоков

`counter` — число, рассчитываемое сервером в зависимости от конфигурации блока (кол-во задач, вопросов, подписей и т.д.). Ненулевое значение отображается как значок-счётчик на плитке.

Структура `data.tasksCounters`:

```json
{
  "tasksCounters": {
    "allTasksCount": null,
    "newTasksCount": 0,
    "overdueTasksCount": 0,
    "allTasksUserOwns": 2,
    "allTasksUserPerforms": 0
  }
}
```

> Если в шаблоне блока `hideOnZeroCount = 1` и `counter = 0` или `null` — блок не отображается.

### Экран История звонков

Экран истории звонков относится к отдельному mobile-сценарию коммуникаций и обычно открывается через:

- `openCallHistory` в `rightBarButtons`;
- `Calls` как системный `Id` блока;
- связанные навигационные точки из `TabBar`, `Dashboard` или `MainMenu`.

Помимо настройки самого action, проверьте, что у пользователя есть доступ к связанному контуру ВКС и коммуникаций.

Пример JSON-кнопки для открытия истории звонков в `rightBarButtons`:

```json
{
  "action": "openCallHistory",
  "icon": "RecentTasks",
  "tickerAlias": "missedCalls",
  "tickerColor": "E05040"
}
```

`openCallHistory` показывает все звонки ВКС текущего пользователя за последнее время — статичный экран со списком звонков и их итоговым статусом. Фиксированно отображаются ближайшие 100 звонков: входящие/исходящие, входы в свою/чужие комнаты ВКС, входы/приёмы вызовов с комнат задач. При тапе на пункт — переход в соответствующую комнату ВКС.

### `rightBarButtons`

`rightBarButtons` — это JSON-массив объектов `onTap`, который описывает кнопки в правой части верхней панели.

Особенности ключа:

- применяется в контейнерах `dashboard` и `menu`;
- для `toolbar` он не используется — у нижнего меню другой механизм настройки кнопок;
- набор action для `rightBarButtons` различается между Android и iOS;
- настройки `rightBarButtons` в MTF-шаблоне задачи могут перекрывать контейнерный nav bar на форме задачи.

Типовые сценарии использования:

- история звонков;
- календарь;
- ВКС;
- глобальный поиск;
- кнопка открытия `TaskSource`;
- badge/counter-индикация через `tickerAlias`.

#### Где применяется ключ

| Контейнер / шаблон | Ключ применяется | Где описано |
|---|---|---|
| **Dashboard** (шаблон `dashboard`) | да | в настройках контейнера или в шаблоне `dashboard` |
| **MainMenu** / боковое меню (шаблон `menu`) | да | в настройках контейнера или в шаблоне `menu` |
| **TabBar** (шаблон `toolbar`) | **нет** — кнопки нижнего меню настраиваются другим способом | для кнопок тулбара используется `addButton` элемента `toolbarItem` |
| **Шаблоны задач** (категория, MTF) | да | Если rbb настроен в шаблоне MTF, он перекрывает rbb контейнера на всех вкладках формы. |

#### Структура одного объекта-кнопки

| Поле | Описание | Пример |
|---|---|---|
| `action` | onTap-действие при тапе на кнопку (см. матрицу поддержки ниже) | `"openCallHistory"` |
| `icon` | название преднастроенной иконки | `"RecentTasks"`, `"VideoCall"`, `"Notification"`, `"Calendar"`, `"Search"` |
| `tickerAlias` | алиас счётчика, который рисуется на кнопке. Системные алиасы: `badge`, `missedCalls`. Можно указать число — id кастомного счётчика | `"badge"` |
| `tickerColor` | цвет счётчика (hex без `#`) | `"E05040"` |
| `dataSourceUrl` | URL источника данных, обязателен для `openTaskSource`, для остальных action игнорируется | `"/app/v1.2/api/publications/action/mob-notifications"` |
| `id` | id объекта (категории, юзера, задачи) — для `openSubcategory`, `openTask` и т.п. | `2867` |
| `epId` | ДП-источник id (например, ДП с номером БТФ для `openSocial` → кнопка «Читать статью») | `76950` |
| `title` | подпись/заголовок кнопки | |
| `items` | вложенные пункты для `openMenu` (внутри подменю — тоже onTap actions). **На iOS внутри `items` срабатывают только 5 action**: `openTaskSource`, `openCategoryTree`, `openSubcategory`, `openNewTask`, `openCallHistory` — остальные на iOS тихо игнорируются (на Android — полный набор) | `[{ "action": "openTaskSource", "id": 67 }]` |

#### Поддерживаемые action на платформах

Множество action, которые работают в `rightBarButtons`, **отличается на Android и iOS**. Ниже — матрица фактической поддержки. Расширенные описания самих action — в разделе «Навигация и `onTap`» документа [Администрирование — обзор](https://help.1forma.ru/domains/mobile/admin.md).

> ⚠️ **Эта таблица описывает поведение только rbb-кнопок навбара.** Если действие нужно на тапе по плитке `dashboardItem` или пункту `menuItem`, оно настраивается через ключ `interactions` блока — там на iOS поддержан более широкий набор action. Полный перечень — в корневой таблице `onTap`.

| Action | Android (rbb) | iOS (rbb навбара) | Комментарий |
|---|---|---|---|
| `openTaskSource` | ✅ | ✅ | |
| `openLink` | ✅ | ✅ | |
| `openCalendar` | ✅ | ✅ | |
| `openVideoConferenceRoom` | ✅ | ✅ | Android дополнительно проверяет валидность настроек ВКС перед показом кнопки |
| `openCallHistory` | ✅ | ✅ | |
| `openGlobalSearch` | ✅ | ✅ | |
| `scanCode` / `scanQRCode` / `scanEAN13Code` | ✅ | ✅ | |
| `dismiss` | ✅ | ✅ | |
| `openMenu` | ✅ — внутри `items` поддержаны все action | ⚠️ — внутри `items` только: `openTaskSource`, `openCategoryTree`, `openSubcategory`, `openNewTask`, `openCallHistory`. Остальные в подменю тихо игнорируются | Поведение разное |
| `openMail` | ✅ | ❌ в rbb навбара | На iOS работает только в header бокового меню. НЕ работает как кнопка rbb и НЕ работает в `interactions` плиток |
| `openSocial` | ✅ | ❌ в rbb навбара | На iOS работает через `interactions` плиток `dashboardItem`/пунктов `menuItem` и в header бокового меню. НЕ работает как кнопка rbb |
| `openNewTask` | ✅ | ❌ в rbb навбара | На iOS работает через `interactions` плиток. В rbb на iOS — только как ответ на `scanCode` |
| `openTask`, `showAlert` | ✅ | ❌ в rbb навбара | На iOS `openTask` — default-фолбэк в `interactions` плиток; `showAlert` — только как ответ на `scanCode` |
| `openSubcategory`, `openCommentsFeed`, `openSignatures`, `openTasksFeed`, `openUsedBlock`, `openUserProfile`, `openCalendarEvent`, `forbidOpen` | ✅ | ❌ в rbb навбара | На iOS работают через `interactions` плиток/пунктов меню, не через rbb |
| `openCategoryTree` | ✅ | ❌ в rbb навбара | На iOS не работает как отдельная кнопка rbb навбара, но работает внутри `openMenu.items` (вложенного подменю) и в дереве категорий |
| `openTaskHistory`, `openDisk`, `openHRLink` | ✅ | ❌ в rbb навбара | На iOS есть в модели (`openTasksHistory`, `openDisk`, `openHRLink`) и обрабатываются через `interactions` плиток/пунктов меню, но не как кнопка rbb навбара |
| `copy`, `check` | ✅ | ❌ | На iOS не являются навигационными action плитки: `copy` используется в контекстных меню, `check` — служебное; как кнопка rbb или `interactions` не срабатывают |

> **Важно при настройке кросс-платформенного контейнера:** если rbb должен работать одинаково на Android и iOS, ограничьтесь набором из первых девяти строк (помеченных ✅✅).

![Пример кнопок rightBarButtons](https://help.1forma.ru/help-images/mobile/dashboard_rbb_phone_notification.jpg)

#### Пример: rightBarButtons главного дашборда (Dashboard)

```json
[
  {
    "action": "openCallHistory",
    "icon": "RecentTasks",
    "tickerAlias": "missedCalls",
    "tickerColor": "E05040"
  },
  {
    "action": "openTaskSource",
    "dataSourceUrl": "/app/v1.2/api/publications/action/mob-notifications",
    "icon": "Notification",
    "tickerAlias": "badge"
  },
  {
    "action": "openGlobalSearch",
    "icon": "Search"
  }
]
```

#### Пример: rightBarButtons бокового меню (MainMenu)

```json
[
  {
    "action": "openVideoConferenceRoom",
    "icon": "VideoCall"
  },
  {
    "action": "openCalendar",
    "icon": "Calendar"
  }
]
```

### `addButton`

`addButton` — это массив действий `openNewTask`, который используется:

- либо в task template / списке задач;
- либо в элементе контейнера `toolbar`.

- `addButton` описывает доступные пункты создания;
- решающие поля — `action = openNewTask` и `id` категории.

### Практические темы по `Dashboard`

#### Как добавить первую плитку на дэш

Минимальный сценарий:

1. Открыть нужный контейнер `Dashboard`.
2. Добавить элемент палитры `dashboardItem` или `taskSourceDashboardItem`.
3. Заполнить заголовок, иконку и действие при тапе.
4. При необходимости указать `catId`, `dataSourceUrl`, размеры и групповые ограничения.
5. Сохранить контейнер и проверить, что плитка не скрывается из-за пустого счётчика, групп или фильтров.

#### Особенности плиток `taskSourceDashboardItem`

Правила для таких плиток:

- без `tickerAlias` числовой badge может не появиться;
- при `hideOnZeroCount = 1` плитка может исчезнуть полностью;
- пустые состояния нужно проектировать осознанно через `emptyContentTitle` и глобальный плейсхолдер;
- поведение плитки зависит одновременно от настроек task source и block template.

#### Плитка для открытия ленты соцсети

`dashboardItem` может использовать `openSocial` через `interactions`.

Обязательно проверьте:

- задан ли `catId`;
- нужен ли `epId` для сценария чтения статьи;
- не перепутан ли сценарий соцсети с обычным `openTaskSource` или `openSubcategory`.

#### Главный дэш со списком дэшей

Если рабочих столов несколько, выделите главный `Dashboard`, который работает как навигационный хаб:

- основной контейнер открывается первым;
- дополнительные `Dashboard<Suffix>` или `FavouritesMenu` открываются через плитки, пункты меню или отдельные табы;
- при нескольких учётных записях пользователь дополнительно листает между основными дэшами аккаунтов.

#### Отдельный дашборд `FavouritesMenu`

`FavouritesMenu` — отдельный контейнер избранного:

- контейнер имеет фиксированный `Id = FavouritesMenu`;
- может открываться как плиткой на главном дэше, так и из бокового меню;
- использует те же данные, что и пользовательское избранное, но другой способ отображения.

Настройка открытия с главного дашборда:

1. На главном дашборде добавить элемент типа «Дашборд» с шаблоном `dashboard`.
2. Указать в поле `id` значение `FavouritesMenu`.
3. Содержимое контейнера МП загружает отдельным запросом (`POST /app/v1.2/api/mobile/containers/` с телом `['FavouritesMenu']`).

> Цвет иерархии можно менять только для контейнера `FavouritesMenu`.

### Практические темы по `MainMenu`

#### Структура меню с папкой избранного и `menuSeparator`

`MainMenu` может содержать:

- обычные `menuItem`;
- папки с вложенными пунктами;
- `menuSeparator` для визуального разделения;
- отдельную папку/секцию для `Избранного` и `Избранного РМГ`.

Это важно для структурирования длинного меню без превращения его в плоский нечитабельный список.

#### Открытие ленты соцсети из пункта меню

Любой `menuItem` может быть настроен на `openSocial`.

Правила те же, что и для dashboard-плитки:

- `catId` обязателен;
- `epId` нужен, если сценарий должен открывать статью через ячейку;
- кросс-платформенность нужно проверять на реальном клиенте, а не только по preview.

#### `rightBarButtons` бокового меню

В `MainMenu` верхняя панель часто используется для:

- запуска личной ВКС;
- перехода в календарь;
- дополнительных service-entry сценариев.

При проектировании нужно учитывать ограничение по числу видимых кнопок и то, что остальное может уйти в overflow-меню.

### Практические темы по `TabBar`

#### Параметры шаблона `toolbar`

Для `TabBar` критичны:

- `itemColor`;
- `selectedItemColor`;
- `badgeItemColor`;
- `containerBackgroundColor`.

Они управляют цветом обычного и активного состояния табов, фоном счётчика и общим фоном контейнера.

#### Элементы внутри блока и настраиваемые `Id`

`TabBar` строится вокруг `toolbarItem` и системных `Id`:

- `Calls`, `Chats`, `Contacts`, `Settings`, `Feed`, `Dashboard`, `Folder`, `ToSign`;
- `SubcatNNN`, `TaskSourceNNN`, `CustomTaskSource` для специальных сценариев;
- подпись и иконка берутся из template-ключей блока.

Кнопка «Лента» (`Feed`) меняет заголовок в зависимости от выбранной категории/раздела (если выбраны задачи — заголовок «Задачи» и т.д.).

Ключи шаблона `toolbaritem` (элемент нижнего меню):

| Ключ | Описание |
|---|---|
| `badgeItemColor` | Цвет фона счётчика (hex `#123456`, с опциональной альфой `#123456FF`, краткие `#000` / `#000F`) |
| `fallBackTitle` | Подпись под иконкой |
| `groups` | ID групп через запятую (массив чисел) |
| `icon` | Название иконки вкладки |
| `isDefault` | `1` — вкладка открывается при старте приложения. Если у всех `0` — дефолтная `Dashboard` или первая по порядку |
| `topTitle` | Текст заголовка экрана. Если пустой — берётся `fallBackTitle` |
| `id` | Название шаблона (`toolbaritem`, `tasksFeed`) |

> **Локализация.** В шаблоне `toolbaritem` локализуем только `topTitle` (`Localizable = true`); `fallBackTitle` — не локализуем. См. § «Локализация ключей шаблона контейнера».

#### Календарь как элемент таббара

Календарный сценарий можно выводить через элемент-синдикат с `Id = SubcatXXX`, где `XXX` — системная категория календаря.

#### Кастомный сорс в таббаре

Для кастомного источника в `TabBar` используется:

- системный `Id = CustomTaskSource`;
- `dataSourceUrl` в настройках элемента;
- отдельная проверка визуального и навигационного поведения на клиенте.

#### `addButton` в `toolbarItem`

`addButton` в `toolbarItem` — это практический replacement для старых сценариев выбора категории:

- через него настраиваются точки создания задач, чатов и связанных объектов;
- он должен проектироваться с учётом доступных категорий и мобильного UX;
- его не нужно смешивать с контейнерными `rightBarButtons`.

### Исторические и пограничные темы

#### `CategorySelectorMenu (Deprecated)`

Этот контейнер устарел и не должен использоваться в новых конфигурациях:

- `CategorySelectorMenu` сохраняется только как справка по старым конфигурациям;
- новые сценарии строятся на `addButton`, `TabBar`, `MainMenu`, `FavouritesMenu` и стандартной навигации по категориям и источникам данных задач.

#### `Избранное` и `Избранное РМГ`

Избранное отображается двумя способами:

- как часть `MainMenu` через папку/пункты меню;
- как отдельный дашборд `FavouritesMenu`.

Содержательно туда входят:

- пользовательское `Избранное`;
- `Избранное РМГ`, если оно задано в рабочем месте группы;
- `menuSeparator` или аналогичный визуальный разделитель между группами элементов.

## Локализация ключей шаблона контейнера

На странице «Значение ключей шаблона для контейнера» (`administration/mobile-container-template/{ID}`) рядом с текстовыми ключами, у которых `APITemplateKey.Localizable = true`, отображается стандартная кнопка локализации (иконка глобуса). Переводы хранятся в `APIContainerTemplateValue` и подтягиваются по языку интерфейса мобильного приложения.

**Локализуемые ключи** (по `APITemplateKey.Localizable = true`):

| Шаблон | Локализуемые ключи |
|---|---|
| `dashboard` | `fallBackTitle` |
| `dashboardItem` | `fallBackTitle` |
| `tasksFeed` | `fallBackTitle` |
| `dashboardStack` | `fallBackTitle` |
| `dashboardSignatures` | `fallBackTitle` |
| `taskSourceDashboardItem` | `fallBackTitle` |
| `toolbaritem` | `topTitle` |

Для шаблонов `menu`, `menuitem`, `webPortal` и других текстовые ключи не локализуются — кнопка не отображается. Для новых, ещё не сохранённых значений ключа кнопка также недоступна — сначала сохраните значение, затем локализуйте.

## Структура таблиц БД

Контейнеры описываются 10 связанными таблицами. Общая иерархия:

```
APITemplate (шаблон)
 ├── APITemplateKey (ключи шаблона)
 │    └── APITemplateValue (значения ключей по умолчанию)
 │
 ├── APIContainer (контейнер = экземпляр шаблона)
 │    ├── APIContainerTemplateValue (значения ключей для контейнера)
 │    ├── APIContainerToGroups (привязка к группам/ролям)
 │    │
 │    ├── APIBlock (блок внутри контейнера)
 │    │    └── APIBlockTemplateValue (значения ключей для блока)
 │    │
 │    └── APIFolder (папка — группировка блоков)
 │         ├── APIFolderTemplateValue (значения ключей для папки)
 │         └── APIBlock.FolderId → (блоки внутри папки)
```

### Уровень шаблонов (определение структуры)

**APITemplate** — 21 шаблон. Определяет тип сущности (контейнера, блока, элемента).
- Поля: `Id`, `ClientId`, `Description`
- Ключевые шаблоны контейнеров: `toolbar`, `menu`, `dashboard`, `settings`, `userProfile`
- Шаблоны блоков: `toolbaritem`, `menuitem`, `dashboardItem`, `taskSourceDashboardItem`, `dashboardCustomURL`, `dashboardBanner`, `dashboardSignatures`, `dashboardStack`, `tasksFeed`, `dashboardMediaItem`, `dashboardUserExtParamList` (Id=24, список значений ДП пользователя; не используется) и др.
- Вспомогательные: `userAreaFolder`, `menuSeparator`, `contactsFolder`, `orgStructure`

**APITemplateKey** — 267 ключей. Определяет допустимые настройки для каждого шаблона.
- Поля: `Id`, `ClientId` (имя ключа), `TemplateId` → APITemplate, `ValueTypeId`, `Localizable`
- Например, у шаблона `dashboardItem` (Id=7) — 38 ключей, у `taskSourceDashboardItem` (Id=15) — 43 ключа
- `ValueTypeId`: тип значения (0–5)

**APITemplateValue** — 479 значений по умолчанию. Задаёт дефолтные значения ключей шаблона, которые наследуются, если не переопределены на уровне контейнера/блока/папки.
- Поля: `Id`, `KeyId` → APITemplateKey, `Value`

### Уровень экземпляров (конкретные настройки)

**APIContainer** — 82 контейнера. Экземпляр шаблона — конкретный Dashboard, TabBar, MainMenu и т.д.
- Поля: `Id`, `ClientId`, `TemplateId` → APITemplate, `Active`, `Order`, `Description`
- `Order` определяет приоритет (меньше = выше)
- `Active` — включён/выключен

**APIBlock** — 2571 блок. Элементы внутри контейнеров (плитки дашборда, пункты меню, кнопки таббара).
- Поля: `Id`, `ClientId`, `TemplateId` → APITemplate, `ContainerId` → APIContainer, `FolderId` → APIFolder (опционально), `Action`, `Order`, `Active`, `Description`, `UseSpecialTemplate`, `AddCommentsToTask`
- 719 блоков принадлежат папкам (имеют `FolderId`)

**APIFolder** — 158 папок. Группировка блоков внутри контейнера (вложенные дашборды, разделы меню).
- Поля: `Id`, `ClientId`, `TemplateId` → APITemplate, `ContainerId` → APIContainer, `Action`, `Order`, `Description`, `Active`
- Используются в 47 контейнерах

### Таблицы значений (переопределение настроек)

Три таблицы с одинаковой структурой — переопределяют значения ключей шаблона на уровне конкретного экземпляра:

**APIContainerTemplateValue** — 817 значений для контейнеров.
- Поля: `ContainerId` → APIContainer, `TemplateKeyId` → APITemplateKey, `Value`

**APIBlockTemplateValue** — 61 138 значений для блоков. Самая большая таблица — здесь хранится основная масса настроек (тексты плиток, иконки, цвета, ссылки, фильтры и т.д.).
- Поля: `BlockId` → APIBlock, `TemplateKeyId` → APITemplateKey, `Value`

**APIFolderTemplateValue** — 2 119 значений для папок.
- Поля: `FolderId` → APIFolder, `TemplateKeyId` → APITemplateKey, `Value`

### Привязка к ролям

**APIContainerToGroups** — 79 привязок. Связывает контейнеры с группами пользователей.
- Поля: `Id`, `ApiContainerId` → APIContainer, `GroupId`
- 57 уникальных контейнеров привязаны к 22 группам
- Контейнеры без привязки доступны всем

### Принцип наследования значений

Значение ключа для конкретного блока определяется по цепочке:
1. **APIBlockTemplateValue** — если задано для блока, используется оно
2. **APITemplateValue** — иначе берётся значение по умолчанию из шаблона
3. Аналогично для контейнеров (APIContainerTemplateValue) и папок (APIFolderTemplateValue)
