Компоненты интерфейса
Единая библиотека компонентов интерфейса биллинга: состав, боковые панели, поиск внутри раздела и устройство библиотеки.
Единые компоненты интерфейса
Каталог живёт в Настройки → Лицензия и расширения → Единые компоненты
(/admin/settings/components/) и содержит 59 компонентов с описанием, публичным API,
местами применения и живыми демо. Среди последних добавленных:
- SmitDrawer — боковая панель проекта: карточки склада, тикеты, профиль, настройки AI. Панели складываются стопкой, закрываются по Esc и клику мимо, возвращают фокус на кнопку, из которой открылись.
- SmitIconSelect — селект с иконками; на нём все фильтры «Линейка услуг».
- SmitTable — таблица-основа: карточки на телефоне плюс перетаскивание, ширина и состав колонок с сохранением в аккаунте.
- SmitOrgSwitch — смена организации у объекта: меню, подтверждение со списком последствий и запрос на сервер.
- SmitInfiniteTable.fromArray — порционная отрисовка уже загруженного списка: строки рисуются по мере прокрутки.
Библиотека переиспользуемых UI-компонентов админки — единый визуальный язык вместо десятков локальных копий одной и той же логики. Каждый компонент — самодостаточный <script> без сборки и внешних зависимостей: подключается один раз в base_adminlte.html, публикует своё API в window.* и работает в любом разделе. Все компоненты поддерживают тёмную тему, мобильную вёрстку и prefers-reduced-motion.
Живой каталог с демо: /admin/settings/components/ — карточка на каждый компонент с описанием, API и работающим примером.

Состав библиотеки
| Компонент | Назначение | Где применяется |
|---|---|---|
| OrgBadge | Единый бейдж организации: favicon/иконка + акцентный цвет. | Линейки услуг, канбан CRM, тарифы, staff, Реестр документов, video, ATOL, mailserver |
| PdfViewer | Модалка-iframe предпросмотра PDF: загрузчик, ловля ошибок, кнопка скачивания. | Реестр документов, CRM «Файлы», video-проекты, лицензии, captive-portal |
| SmitDateRange | Диапазон дат: пресеты (сегодня / 7 дн / месяц / …) + календарь, память выбора. | Журнал сообщений, PayLog, FinOps, аудит, CRM-дашборд, тайм-трекинг (14 шаблонов) |
| SmitMultiSearch | Мульти-поиск: сам определяет тип запроса (телефон / договор / адрес / сумма). | Воронки CRM (эталон), глобальный поиск клиентов |
| ProfileSelect | Селект сотрудника: аватар + имя + группы + поиск. Кандидаты — только реальные операторы. | Канбан CRM, дровер сделки, задачи, наряды, финоперации |
| SmitResponsibleSelect | Фильтр «Ответственный»: обёртка над ProfileSelect + «Все» / «Без ответственного» + счётчик-бейдж. | Списки CRM, Воронки CRM, Поддержка (бейдж = открытые тикеты) |
| SmitTariffSelect | Красивый селект тарифа: обложка + скорость ↓/↑ + цена, поиск. | Дровер сделки CRM (вкладка «Подключение») |
| SmitPhone | Номер телефона: иконка + маска +7 (917) 727-17-96, тултип, click-to-call. | Контакты клиента, карточки CRM/лидов, поиск, тикеты, staff, голосовая почта |
| SmitHelpIcon | Аккуратная круглая i-иконка справки со ссылкой на документацию. | Заголовки разделов CRM/Поддержка (get_help_link), карточки настроек |
| SmitWatchButton | Кнопка «Наблюдаю» — только иконка глаза + счётчик, зелёный фон в активном состоянии. | Поддержка (фильтр «Наблюдаю»); задел — «Наблюдать» в CRM/тикетах |
| CallButton | Кнопка «Позвонить» (click-to-call через провайдера телефонии). | Тикеты, CRM, поиск, карточка клиента |
| SmitDrawer | Боковая панель: заголовок с иконкой и подзаголовком, вкладки, кнопки действий, загрузка содержимого по адресу, стопка вложенных панелей. | Карточки позиции и единицы склада, тикеты Поддержки, дровер сделки и заявки CRM, карточка клиента, профиль, разделы AI, редактор ботов, монитор сессий |
| SmitToast | Всплывающие уведомления (стек справа-сверху / снизу на мобильном). | Уведомления во всех разделах |
| SmitConfirm | Модалка подтверждения вместо confirm() (тот ломал тёмную тему). | Подтверждение опасных действий |
| SmitSectionSearch | Строка поиска внутри раздела: стоит в линию с полосой вкладок, подсказки со значком подраздела, Ctrl+K, на телефоне — над вкладками во всю ширину. Ищет только по своему разделу. | Настройки системы, ЛК и мобильные, AI-ассистент, Интеграции, Сообщения, Банковские выписки, Модули, Виджеты, карточка СОРМ, профиль сотрудника, каталог компонентов |
| SmitSaveBar | Панель сохранения: появляется, когда штатная кнопка ушла за экран; подтверждает правку ответственной настройки и пишет её в аудит. | Разделы настроек биллинга |
| SmitCopy | Копирование в буфер (clipboard + execCommand fallback + тост). | Замена ~29 копий clipboard-логики (IP / MAC / договор / e-mail / телефон) |
Боковые панели: одна механика на весь биллинг
Боковая панель одна на весь биллинг: раздел передаёт заголовок, иконку, вкладки и содержимое, а затемнение, обработку Esc, блокировку прокрутки и порядок слоёв (карточка клиента → видео) берёт на себя компонент.
- Стопка. Панель, открытая поверх другой, ложится сверху и слегка сдвигает нижнюю. Esc и клик мимо закрывают только верхнюю — предыдущая остаётся там, где вы её оставили.
- Содержимое по адресу. Пока грузятся данные, показывается скелет; если запрос не прошёл — «Повторить», а не пустая панель.
- Возврат фокуса. После закрытия фокус возвращается на кнопку или строку таблицы, из которой панель открыли — с клавиатуры работать можно без мыши.
- На телефоне панель занимает весь экран, закрывается свайпом вниз по заголовку и кнопкой «Закрыть».
flowchart LR
T["Строка таблицы
или кнопка"] --> D1["Панель 1
карточка позиции"]
D1 --> D2["Панель 2
карточка единицы"]
D2 --> D3["Панель 3
вложенная — видео, IPTV"]
D3 -. "Esc / клик мимо" .-> D2
D2 -. "Esc / клик мимо" .-> D1
D1 -. "закрытие" .-> T
Поиск внутри раздела
В разделах настроек десятки полей и до тринадцати вкладок. Строка поиска
стоит в одной линии с полосой вкладок и ищет только по своему
разделу — глобальный поиск клиентов живёт в шапке админки, и путать их
не нужно. Курсор в поле ставит Ctrl+K из любого места страницы.
Подсказка показывает путь «Вкладка · Карточка» и значок подраздела. Если настройка на открытой вкладке — поле подсвечивается на месте; если на соседней — раздел откроется на ней и подсветит нужное поле.
flowchart LR
Q["Оператор вводит
«порог»"] --> IDX["Индекс раздела"]
IDX --> D["Поля открытой
страницы"]
IDX --> J["Серверный список
(вкладки грузятся
по одной)"]
IDX --> F["Свой источник:
карточки, строки таблиц"]
D --> HIT["Подсказка:
Порог суммы (₽)
Сообщения · Низкий баланс"]
J --> HIT
F --> HIT
HIT --> HERE{"Настройка
на этой вкладке?"}
HERE -- да --> MARK["Показать вкладку
и подсветить поле"]
HERE -- нет --> GO["Перейти на вкладку
?find=<поле>"]
GO --> MARK
Список полей для разделов, где вкладки грузятся по одной, собирается из самих шаблонов вкладок — вести его руками не нужно: добавили поле в форму, оно само появилось в поиске. Разбор выполняется один раз после обновления биллинга.
flowchart TD
T["Шаблоны вкладок раздела"] --> P["Разбор: подпись ↔ поле
+ карточка, в которой оно лежит"]
P --> C["Список настроек раздела
(«Настройки системы» — 124 поля)"]
C --> S["Строка поиска"]
N["Новое поле в форме"] --> T
Где вкладки лежат на одной странице (Интеграции, Сообщения), поиск берёт поля прямо со страницы и сам переключает вкладку, в которой нашлась настройка. Где искать нужно не поле, а блок (профиль сотрудника) — в подсказки попадают заголовки карточек.
Как это устроено
Каждый компонент следует одному паттерну: самовыполняющаяся функция с защитой от повторной инициализации (if (window.X) return), публичное API в window.X, инъекция своих стилей в <head>. CSS подключается в шапке base_adminlte.html, JS — в подвале. Разделу достаточно вызвать API или разметить элемент data-атрибутом — компонент сам себя инициализирует.
flowchart LR
REG["base_adminlte.html
CSS в head + JS в подвале"] --> BUS["window.OrgBadge / SmitPhone /
ProfileSelect / SmitToast / …"]
BUS --> M1["Разметка: data-org-badge
data-smit-phone / data-smit-daterange"]
BUS --> M2["JS-вызов: OrgBadge.html id
ProfileSelect.create"]
M1 --> RENDER["Автоинициализация
+ MutationObserver для AJAX"]
M2 --> RENDER
RENDER --> THEME["Тёмная тема · мобильная вёрстка
prefers-reduced-motion · a11y"]
Почему единые компоненты. Исправление и улучшение живут в одном файле, а эффект виден сразу во всех разделах: правка не теряется по копиям, тёмная тема не ломается точечно.
Важно при добавлении. Новые файлы static/js/*.js и *.css подхватываются whitenoise только при старте web-контейнера — после добавления компонента нужен manage.py collectstatic + перезапуск web (обычный build web новые статик-файлы не собирает).