From 11ad4f8eb36f2b6019c9d7fd5a1cea1fb908c6ce Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 19 Jul 2026 23:45:13 +0300 Subject: [PATCH] :memo: docs(channels): add ADR-HUB-0037 and SPEC-HUB-0027 with design baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Канал обработки становится объектом управления: список, карточка, создание, политика, подключения и счётчики. Разделы AI-агентов и знаний не затрагиваются — связь с агентом остаётся одной строкой со ссылкой. Co-Authored-By: Claude Opus 4.8 --- design/baseline/Каналы обработки/README.md | 80 + .../_adherence.oxlintrc.json | 508 +++ .../_ds_bundle.js | 3489 +++++++++++++++++ .../_ds_manifest.json | 1 + .../readme.md | 216 + .../styles.css | 4 + .../tokens/colors.css | 88 + .../tokens/effects.css | 40 + .../tokens/spacing.css | 36 + .../tokens/typography.css | 42 + .../Каналы обработки/assets/custocrm-mark.svg | 4 + design/baseline/Каналы обработки/support.js | 1841 +++++++++ .../Каналы обработки/Каналы Baseline.dc.html | 609 +++ 13 files changed, 6958 insertions(+) create mode 100644 design/baseline/Каналы обработки/README.md create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_adherence.oxlintrc.json create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_ds_bundle.js create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_ds_manifest.json create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/readme.md create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/styles.css create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/tokens/colors.css create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/tokens/effects.css create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/tokens/spacing.css create mode 100644 design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/tokens/typography.css create mode 100644 design/baseline/Каналы обработки/assets/custocrm-mark.svg create mode 100644 design/baseline/Каналы обработки/support.js create mode 100644 design/baseline/Каналы обработки/Каналы Baseline.dc.html diff --git a/design/baseline/Каналы обработки/README.md b/design/baseline/Каналы обработки/README.md new file mode 100644 index 0000000..9e9dd36 --- /dev/null +++ b/design/baseline/Каналы обработки/README.md @@ -0,0 +1,80 @@ +# Каналы обработки — design baseline (DG-02) + +Макет раздела **Компания → Каналы** (CustoCRM / ООО «Эдевс») по SPEC-HUB-0027 / ADR-HUB-0037. + +## Состав + +``` +Каналы Baseline.dc.html — макет (6 кадров: A–E + состояния) +support.js — рантайм Design Component (шаблон + логика) +_ds/edevs-hub-design-system-…/ — дизайн-система: токены, styles.css, _ds_bundle.js +assets/custocrm-mark.svg — логотип CustoCRM +``` + +## Запуск + +Открыть `Каналы Baseline.dc.html` через локальный HTTP-сервер +(из-за загрузки внешних файлов `file://` не подойдёт): + +```bash +python3 -m http.server 8080 +# → http://localhost:8080/Каналы%20Baseline.dc.html +``` + +## Карта кадров (SPEC-HUB-0027 §11.6) + +- **A** — Список каналов: полная оболочка (sidebar + topbar). Фильтры-`UnderlineTabs` с count + (`Все · Продажи · Поддержка · Без отдела`), поиск, тумблер «Показывать архивные». + Плотная `baseline-table` в `table-card`. Колонки `КАНАЛ / ПРОДУКТ / ПОДКЛЮЧЕНИЯ / + AI-АГЕНТ / ДИАЛОГИ / ⋯`. Показано открытое меню строки с заблокированным «Удалить» + и причиной. §11.1. +- **B** — Карточка канала **одной страницей, без вкладок**. Заголовок (`code · отдел · + продукт`, действия «Изменить / Деактивировать / Удалить») и секции сверху вниз: + Назначение, Политика, Подключения, AI-агент (одна строка: имя · статус · модель + + ссылка «Открыть агента» на страницу агента наружу), Счётчики (открытые диалоги, + подключения + ссылка на диалоги). Настроек агента и знаний в карточке нет. §11.2. +- **C** — Секция «Политика» на **непродуктовом** канале «Партнёрская линия». + Пять флагов с однострочным пояснением следствия; три флага (checkout P1, + attribution P2, обязательная идентичность P3) выключены и заблокированы с причиной. §11.2. +- **D** — Секция «Подключения». Список подключений канала с отвязкой, действие + «Привязать» и модалка **переноса** подключения из другого канала (`force`), + с оговоркой, что диалоги остаются в прежнем канале. §11.1 / §6.6. +- **E** — Мастер создания, **три шага** (Канал · Назначение · Подключения). Показан + финальный экран: канал создан, ведут его операторы, ссылка в раздел AI для создания + агента отдельным действием. Настроек агента и знаний в мастере нет. §11.3. +- **S** — Состояния: пустой список (приглашение без «здесь пусто»), канал без агента + (секция AI-агент), архивный канал, ограниченный доступ (department scope), конфликт + удаления (409 с блокерами). §11.5 / §7.2. + +## Зафиксированные решения по открытым вопросам (README §5 задания) + +1. **Канал без отдела.** В колонке `КАНАЛ` под именем идёт `code · <чип «Без отдела»>` — + нейтральный чип на `--n-9`, а не пустая ячейка: канал читается как намеренно + безотдельный, а не как незаполненный черновик. Плюс отдельный таб-фильтр «Без отдела». + Такой канал виден только organization-scoped `channels.view` (§5.2). +2. **Архивный канал (`isActive=false`).** Отдельный статус `Архивный` (полая точка, + приглушённый текст, фон строки `#fcfcfc`), а не «Выключено». По умолчанию скрыт, + показывается тумблером «Показывать архивные». Меню строки предлагает только + «Активировать». История диалогов сохраняется и доступна ссылкой из карточки + («История сохраняется даже после деактивации канала»). +3. **Запрещённый инвариантом флаг политики.** Флаг выключен **и** заблокирован, рядом — + бейдж «Запрещено инвариантом» / «Недоступно» и однострочная причина со ссылкой на + инвариант (P1/P2/P3) и способ разблокировать («Назначить продукт»). Причина + объясняется, а не игнорируется молча при сохранении. Кадр C, флаг checkout выделен. +4. **Колонка «Диалоги» в списке.** Оставлена. Обоснование: это дешёвый агрегат + (`counters.openConversations`, один запрос на список, §6.1), а открытые диалоги — + ключевой операционный сигнал канала. Полный разрез счётчиков (открытые диалоги и + подключения) остаётся в секции «Счётчики» карточки; в списке — только открытые + диалоги, numeric. Счётчика знаний в карточке канала нет: знания принадлежат агенту. + +## Технические примечания + +- Вёрстка — инлайн-стили поверх токенов дизайн-системы (`var(--*)` из `_ds/.../tokens`). + Цвета/типографика/отступы менять только через токены; сырые hex вне токенов не + используются (кроме служебного фона холста и подсветок строк из baseline-образца). +- `ChannelBadge` (точка + короткий лейбл MAX/TG/Web на токенах `--channel-*`) применяется + экономно — только в колонке «Подключения» списка и в секции «Подключения» карточки + (ADR-HUB-0013). +- Терминология: MAX/TG/Web — «Подключения»; «Канал» — сущность маршрутизации. +- Данные — примеры; авторизация, инварианты и счётчики вычисляются на backend + (frontend не источник прав), см. SPEC-HUB-0027 §5–§7. diff --git a/design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_adherence.oxlintrc.json b/design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_adherence.oxlintrc.json new file mode 100644 index 0000000..8419c5c --- /dev/null +++ b/design/baseline/Каналы обработки/_ds/edevs-hub-design-system-e4c9df12-82c8-42b3-b819-63d92dfb8702/_adherence.oxlintrc.json @@ -0,0 +1,508 @@ +{ + "plugins": [ + "react", + "import" + ], + "rules": { + "react/forbid-elements": [ + "warn", + { + "forbid": [] + } + ], + "no-restricted-imports": [ + "warn", + { + "patterns": [ + { + "group": [ + "components/cards/**", + "components/core/**", + "components/inbox/**", + "components/status/**", + "ui_kits/checkout/**", + "ui_kits/internal-hub/**", + "ui_kits/web-chat/**" + ], + "message": "Import design-system components from 'index.js', not component internals." + } + ] + } + ], + "no-restricted-syntax": [ + "warn", + { + "selector": "Literal[value=/#[0-9a-fA-F]{3,8}\\b/]", + "message": "Raw hex color — use a design-system color token via var()." + }, + { + "selector": "Literal[value=/\\b\\d+px\\b/]", + "message": "Raw px value — use a design-system spacing token via var()." + }, + { + "selector": "JSXOpeningElement[name.name='ActorBadge'] > JSXAttribute > JSXIdentifier[name!=/^(?:actor|initials|color|key|ref|className|style|children)$/]", + "message": " doesn't accept that prop. Declared props: actor, initials, color." + }, + { + "selector": "JSXOpeningElement[name.name='ActorBadge'] > JSXAttribute[name.name='actor'] > Literal[value!=/^(?:ai|human)$/]", + "message": " actor must be one of 'ai' | 'human'." + }, + { + "selector": "JSXOpeningElement[name.name='AttentionStatus'] > JSXAttribute > JSXIdentifier[name!=/^(?:level|label|key|ref|className|style|children)$/]", + "message": " doesn't accept that prop. Declared props: level, label." + }, + { + "selector": "JSXOpeningElement[name.name='AttentionStatus'] > JSXAttribute[name.name='level'] > Literal[value!=/^(?:ok|attention|critical)$/]", + "message": " level must be one of 'ok' | 'attention' | 'critical'." + }, + { + "selector": "JSXOpeningElement[name.name='Avatar'] > JSXAttribute > JSXIdentifier[name!=/^(?:initials|color|size|key|ref|className|style|children)$/]", + "message": " doesn't accept that prop. Declared props: initials, color, size." + }, + { + "selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute > JSXIdentifier[name!=/^(?:children|variant|size|disabled|onClick|type|key|ref|className|style|children)$/]", + "message": " + + + + +
+
+ + + + +
+
+
+ + Поиск по имени и коду +
+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КАНАЛПРОДУКТПОДКЛЮЧЕНИЯAI-АГЕНТДИАЛОГИ
FoxRay — продажи
foxray-sales · Продажи
FoxRayTGWeb
FoxRay SalesАктивен
12 + + +
+ Открыть + Привязать подключение +
+
Деактивировать
+
+ + Удалить
Нельзя: 12 диалогов, агент
+
+
+
FoxRay — поддержка
foxray-support · Поддержка
FoxRayWeb
FoxRay SupportАктивен
7
FirePage — продажи
firepage-sales · Продажи
FirePageMAX
FirePage SalesАктивен
5
Партнёрская линия
partners · Без отдела
— непродуктовыйTGНет агента
операторский канал
3
FoxRay — вебинары
foxray-webinars · Продажи
FoxRay—Архивный0
+
+ 5 каналов · 1 архивный + Счётчик «Диалоги» — открытые диалоги по Conversation.channel +
+
+ + + + + + +
+
+ B +

Карточка канала — одна страница

+ без вкладок · секции сверху вниз: Назначение · Политика · Подключения · AI-агент · Счётчики +
+ +
+ +
+ Каналы/FoxRay — продажи +
+ +
+
+

FoxRay — продажи

+
foxray-sales·Продажи·FoxRay
+
+
+ Активен + + + +
+
+ + +
+ + +
+

Назначение

Отдел и продукт редактируются здесь
+
+
ОтделПродажи
+
ПродуктFoxRay
+
Кодfoxray-sales
+
Создан02 июл 2026
+
+
+ + +
+

Политика

Пресет: Продажи
+
+
Обязательная продуктовая идентичность
Диалог требует авторизации как владелец продукта.
+ +
+
+
Анонимные сессии
Разрешает диалоги без идентификации пользователя.
+ +
+
+
Самозаявленный контакт
Позволяет пользователю указать контакт вручную.
+ +
+
+
Attribution продаж
Связывает продажу с диалогом канала.
+ +
+
+
Коммерческие действия (checkout)
Разрешает оформление заказа в диалоге.
+ +
+
+ + +
+

Подключения

+
+
+ TG +
FoxRay Sales Bot
Telegram · @foxray_sales_bot
+ OK + +
+
+ Web +
Виджет на foxray.ru
Web Chat · embed foxray-sales
+ OK + +
+
+
+ + +
+
AI-АГЕНТ
+
+ +
+ FoxRay Sales + · + Активен + · + claude-sonnet-4.6 +
+ Открыть агента +
+
+ + +
+ +
+
ОТКРЫТЫЕ ДИАЛОГИ
12
+
ПОДКЛЮЧЕНИЯ
2
+
+
+ +
+
+
+ + +
+
+ C +

Секция «Политика» — запрещённый инвариантом флаг

+ пять флагов с пояснением следствия · флаг, запрещённый инвариантом, объясняет причину +
+ +
+
+ Каналы/Партнёрская линия +
+
+
+

Партнёрская линия

+
partners·Без отдела·— непродуктовый
+
+ Активен +
+ +
+ +
+
СЕКЦИЯ «ПОЛИТИКА» · 5 ФЛАГОВ
+ +
+ +
+
Обязательная продуктовая идентичностьНедоступно
+
Требует продукта (инвариант P3). Пользователь должен быть авторизован как владелец продукта. Назначьте продукт каналу, чтобы включить.
+
+
+ +
+ +
+
Анонимные сессии
+
Разрешает диалоги без идентификации пользователя — партнёр может написать без входа.
+
+
+ +
+ +
+
Самозаявленный контакт
+
Позволяет пользователю указать email или телефон вручную для обратной связи.
+
+
+ +
+ +
+
Attribution продажНедоступно
+
Attribution недоступна непродуктовому каналу (инвариант P2). Свяжите канал с продуктом, чтобы включить.
+
+
+ +
+ +
+
Коммерческие действия (checkout)Запрещено инвариантом
+
Коммерческие действия недоступны непродуктовому каналу (инвариант P1). Флаг не будет сохранён включённым, пока каналу не назначен продукт — причина показана, а не игнорируется молча.
+ Назначить продукт +
+
+
+ +
+
+

Контекст политики

+
+
Продукт— непродуктовый
+
ОтделБез отдела
+
ПресетСвой (CUSTOM)
+
+
+
+
Инварианты проверяются по итоговому состоянию: частичное сохранение запрещено. Чтобы включить коммерческие флаги, сначала назначьте продукт в секции «Назначение».
+
+
+
+
+
+ + +
+
+ D +

Секция «Подключения» — привязка и перенос

+ список подключений канала · привязка · перенос между каналами с явным подтверждением (force) +
+ +
+
+ Каналы/FoxRay — продажи +
+
+

FoxRay — продажи

+
+
+

Подключения

+ + + + + + + + + + + + + + + + + + + + + + + +
ПОДКЛЮЧЕНИЕПРОВАЙДЕРСТАТУС
FoxRay Sales Bot
@foxray_sales_bot
TGOK
Виджет на foxray.ru
embed foxray-sales
WebOK
+
+ + +
+
+
+ +
+

Перенести подключение в этот канал?

+

Подключение «FirePage MAX Bot» уже привязано к каналу «FirePage — продажи». Подключение принадлежит ровно одному каналу — перенос отвяжет его от прежнего.

+
+
+
+
+ MAX +
FirePage — продажиFoxRay — продажи
+
+

Существующие диалоги остаются в прежнем канале (Conversation.connection — PROTECT). Действие пишется в аудит как перенос.

+
+
+ + + +
+
+
+
+ + +
+
+ E +

Мастер создания канала — три шага

+ Канал · Назначение · Подключения · финальный экран операторского канала +
+ +
+

Создание канала

+

Три шага. Настройка агента и знаний в мастер не входит — агент создаётся отдельным действием в разделе AI. Канал создаётся по завершении шага 2.

+ + +
+
Канал
+
+
Назначение
+
+
Подключения
+
+ + +
+
+
+

Канал создан

+

Партнёрская линия — операторский канал: AI-агента нет, диалоги ведут операторы. Канал принимает подключения, диалоги, заказы и attribution.

+ +
+
+
+

Сводка канала

+
+
НазваниеПартнёрская линия
+
Кодpartners
+
ОтделБез отдела
+
Продукт— непродуктовый
+
ПодключенияTG · 1
+
+
+
+
Нужен AI-агент? Создайте его отдельным действием в разделе AI — он подключится к этому каналу.
+ Перейти в раздел AI +
+
+
+
+
+ + +
+
+ S +

Служебные состояния

+ пустой список · канал без агента · архивный канал · ограниченный доступ · конфликт удаления +
+
+ +
+
+
Создайте первый канал
+

Канал маршрутизирует диалоги от подключений к отделу, продукту и — при необходимости — к AI-агенту.

+ +
+ +
+
СЕКЦИЯ «AI-АГЕНТ» · КАНАЛ БЕЗ АГЕНТА
+
+
+
Агента нет, канал ведут операторы
+

Валидное состояние канала. Агент создаётся отдельным действием в разделе AI.

+ Создать агента в разделе AI +
+
+ +
+
АРХИВНЫЙ КАНАЛ
+
+
FoxRay — вебинары
foxray-webinars
+ Архивный +
+

Не принимает новые диалоги и не выбирается при привязке подключения. История диалогов сохранена и доступна.

+ +
+ +
+
+
Доступ ограничен отделом
+

Ваш scope channels.view охватывает отдел «Поддержка». Каналы других отделов и канал без отдела не показываются; счётчики фильтров считаются по видимому подмножеству.

+
+ +
+
+ +

Канал нельзя удалить

Есть связанные записи. Вместо удаления используйте деактивацию — история сохранится.

+
+
+
БЛОКИРУЮЩИЕ СВЯЗИ
+
+
Диалоги143
+
Подключения1
+
Агент1
+
+
+
+ + + +
+
+
+
+ + + + +