Точки монтирования
Точка монтирования — это место в админ-панели, где может появиться ваш виджет. Нужные вы объявляете в bito.locations.surfaces; точка, в которой код действительно работает, лежит в bito.context.surface.
{
"bito": {
"locations": {
"surfaces": ["customer_profile", "card_phone", "list:customers", "global"],
"persistent": true
}
}
}Элемент должен быть одной из именованных точек ниже, строкой list:<page_name> или global. Всё остальное, как и дубликат, не проходит валидацию при загрузке.
Пять вкладок профилей
| Точка | Экран |
|---|---|
customer_profile | Профиль клиента |
supplier_profile | Профиль поставщика |
employee_profile | Профиль сотрудника |
natural_person_profile | Профиль физического лица |
legal_person_profile | Профиль юридического лица |
Они монтируются напрямую, вкладкой в собственной ленте вкладок профиля, — никакого меню, из которого пользователь сначала выбирает вас, нет. Место вкладки берётся из сохранённого компанией порядка разделов профиля, и её можно перетаскивать, как любой другой раздел. В bito.context.entity_id лежит идентификатор просматриваемого профиля.
widget_page
Точка монтирования страниц левого меню. Её не нужно указывать в bito.locations.surfaces и самим добавлять в меню действий: страницы создаёт объявление bito.left_menu. Их URL — /widget/{widget_code}[/{subitem}] и {префикс модуля}/widget/{widget_code}/{subitem}; они строятся из кода виджета вашего приложения, а если его нет — из client_id.
Bito монтирует виджет с bito.context.surface, равным widget_page, и с открытой страницей в bito.context.menu: { location, item_code, subitem_code }. После bind_actions он вызывает initMenuPage с тем же объектом и вызывает его снова, без перемонтирования, когда пользователь переходит к другому подпункту того же блока.
Страница открывается только для установленного релиза с принятым согласием, а монтирование отклоняется для релиза, в манифесте которого нет left_menu. Названия меню разрешаются из i18n архива при загрузке, поэтому текст навигации не расходится с проверенным пакетом.
sale_order_card
Лента вкладок карточки заказа на канбан-доске. Контракт монтирования тот же, что у вкладки профиля, но в bito.context.entity_id лежит заказ. Карточка сначала спрашивает, какие виджеты объявили эту точку, и только потом рисует вкладки, поэтому ваша появляется рядом со встроенными.
Компания может переставлять эти вкладки как угодно, с одним исключением: первая вкладка карточки закреплена, потому что несёт собственные поля заказа. Лента не даёт ни перетащить её, ни бросить что-либо выше неё — так что ваша вкладка может встать на любое место начиная со второго.
card_phone
Это не вкладка. Виджет, объявивший card_phone, предлагается пунктом в меню клика по номеру телефона в карточке, рядом с «Копировать» и «Позвонить». Пока пользователь не выберет пункт, ничего не монтируется; затем виджет монтируется в модальном окне, номер лежит в bito.context.phone, а запись карточки — в bito.context.entity_id.
Это точка для телефонии и мессенджеров: интеграции click-to-dial отдельная вкладка не нужна.
Это не обход чего-либо. Монтирование идёт через тот же эндпоинт mount и тот же экран согласия на релиз, что и у остальных точек, — просто происходит позже, когда пользователь сам его попросит.
Точки list:
Точка list: помещает ваш виджет в меню действий страницы-списка. page_name — это собственное имя таблицы в админ-панели; строка должна быть list:, затем строчная буква, затем строчные буквы, цифры или подчёркивания: list:customers, list:sale_order.
Пользователь выделяет строки, открывает меню и выбирает ваш пункт. entity_id вы при этом не получаете — у действия над списком нет одной записи. Вместо него приходит bito.context.selection:
var sel = bito.context.selection
// { ids: ["6712...", "6713..."], isAll: false, page_name: "customers" }isAll означает, что пользователь выбрал всё, что попадает под текущий фильтр, а не только идентификаторы из ids.
global
Постоянный, всегда смонтированный слот. Он живёт вне маршрутизатора, поэтому переживает переходы между страницами и монтируется один раз на вкладку браузера, и он пропускается на экранах без интерфейсной обвязки — вход, печать, встраивание и онбординг.
Два важных момента:
TIP
Включает любая из двух форм. И persistent: true, и указание global в surfaces читаются как согласие на постоянный слот — одним общим предикатом, который используют и список глобальных виджетов, и ограничение ниже, так что разойтись они не могут. Предпочитайте persistent: true: это явная, документированная форма, которая говорит то, что означает.
Глобальный виджет работает без обвязки: ни карточки, ни спиннера, ни оповещения об ошибке, ни иконки настроек. Сдерживание сбоев при этом действует — виджет, дважды бросивший исключение, всё так же отключается, — просто он падает молча, а не выталкивает карточку ошибки на каждый экран. Хотите быть видимым — рисуйте собственный интерфейс.
У компании одновременно может быть включено не более пяти постоянных виджетов. Ограничение применяется на сервере в момент принятия согласия, то есть в момент, когда виджет начинает работать. Шестой получает отдельную ошибку, а не тихо отбрасывается.
Что должно быть верно до монтирования
Монтирование отклоняется, если не выполнено всё перечисленное:
- приложение установлено в этой компании;
- релиз находится в статусе
approved(либо вы — владеющий им активный разработчик, который смотрит собственный черновик); - его файлы распакованы;
- компания приняла согласие именно на этот релиз.
Пока согласие не принято, ответ на mount вообще не содержит адреса скрипта, поэтому виджет без согласия не может исполниться — он может только показать запрос согласия.
Предпросмотр до одобрения
Активный разработчик может смонтировать собственный черновой релиз в своей же компании — через отдельный эндпоинт предпросмотра, который явно принимает идентификатор релиза. Боевой путь монтирования идентификатор релиза принять не способен в принципе, поэтому случайно предпросмотреть через него нельзя. В предпросмотре настроек установки нет — bito.settings.get() пуст.