diff --git a/.gitignore b/.gitignore
index f2bdae1..2ef611c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,4 @@ playwright-report/
.playwright/
e2e/screenshots/
*.tsbuildinfo
+__pycache__/
diff --git a/IMPLEMENTATION_REPORT.md b/IMPLEMENTATION_REPORT.md
index ad0c9c3..f8a321c 100644
--- a/IMPLEMENTATION_REPORT.md
+++ b/IMPLEMENTATION_REPORT.md
@@ -1,92 +1,74 @@
-# Отчёт о реализации: Перевод Krona Front на дизайн-систему v3.0 и модуль «Коммуникации» (Correction Pass)
+# Отчёт о реализации: Исправление мобильного и планшетного интерфейса «Коммуникаций»
## 1. Общие сведения
-- **Проект:** krona-front
-- **Рабочая ветка:** codex/design-system-communications-correction
-- **UI-библиотека:** krona-library (@krona/tokens, @krona/themes, @krona/ui)
-- **Стандарт дизайна:** KRONA_DESIGN_SYSTEM_V3.md (версия 3.0: «Благородный зелёный», «Светлый пергамент», «Графитовый тёмный лес»)
+- **Проект:** krona-front (`https://git.ochenstarik.ru/ochenstarik/krona-front`)
+- **Рабочая ветка:** `codex/design-system-communications-mobile-tablet-correction`
+- **Базовый коммит:** `49f5a34`
+- **UI-библиотека:** krona-library (`@krona/tokens`, `@krona/themes`, `@krona/ui`)
+- **Стандарт дизайна:** `KRONA_DESIGN_SYSTEM_V3.md`
- **Эталонные макеты:**
- - Коммуникация.png (Desktop 1440/1280)
- - Коммуникация планшет горизонт.png (Tablet landscape 1024)
- - Коммуникация планшет вертикальный.png (Tablet vertical 768)
- - Коммуникация телефон.png (Mobile 360/390/430)
-- **Целевой модуль:** /communications («Коммуникации»)
+ - `Коммуникация.png` (Desktop 1440/1280)
+ - `Коммуникация планшет горизонт.png` (Tablet landscape 1024)
+ - `Коммуникация планшет вертикальный.png` (Tablet vertical 768)
+ - `Коммуникация телефон.png` (Mobile 360/390/430)
+- **Целевой модуль:** `/communications` («Коммуникации»)
---
-## 2. Архитектура интеграции с дизайн-системой v3.0
+## 2. Ключевые архитектурные решения
-### 2.1. Единый источник истины и семантические токены
-Все дизайн-токены, темы оформления и компоненты подключаются напрямую из публичных скомпилированных пакетов библиотеки krona-library:
-- @krona/tokens и @krona/tokens.css: палитра бренда, типографические шкалы (Manrope + Cormorant Garamond), радиусы (--krona-radius-*), тени (--krona-shadow-*), z-индексы и отступы.
-- @krona/themes и @krona/themes.css: системные темы оформления v3:
- 1. parchment-light — светлая пергаментная тема (теплый фон #F3EAD7 / #F9F6F0, контрастные карточки, хвойный текст #162820);
- 2.
-oble-green — фирменная лесная тема (глубокий хвойный #1E3A2F, мягкий фон, акцент шалфея);
- 3. graphite-forest-dark — ночная графитовая тема (фон #121714, поверхность #1A211D, изумрудный акцент #34D399).
-- @krona/ui (dist/index.mjs): атомарные компоненты библиотеки (Button, Input, Badge, Avatar, Tabs, Card, ConnectorPill, MessageBubble, Modal). Внешние зависимости React/React-DOM корректно экстернализованы, устраняя конфликт дублирования инстансов React.
+### 2.1. Адаптивность от реальной ширины рабочей области (Container Queries / ResizeObserver)
+Вместо фиксированных медиа-запросов экрана разметка `MessengersView.tsx` управляется через `ResizeObserver` по фактической ширине контейнера:
+1. **Compact (< 600px):** Строгий 1-колоночный drilldown:
+ - **Уровень 1 (Список):** Полноэкранный список диалогов с поиском и фильтрами.
+ - **Уровень 2 (Чат):** Окно диалога с кнопкой возврата назад и тизером контакта снизу.
+ - **Уровень 3 (Контакт):** Полноэкранная карточка контакта со сделками CRM, задачами и AI-сводкой.
+2. **Medium (600–949px):** 2 колонки:
+ - Список диалогов (~280–300px) слева.
+ - Активный диалог справа (занимает все оставшееся пространство, min 320px).
+ - Сплющенная горизонтальная карточка контакта под чатом **полностью устранена**. Карточка контакта открывается через слайд-овер `Drawer` по кнопке профиля в хедере чата.
+3. **Wide (≥ 950px):** 3 колонки:
+ - Список диалогов (310–330px).
+ - Активный диалог (min 360px).
+ - Вертикальная карточка контакта (320–350px).
-### 2.2. Управление темами и обратная совместимость
-Хранилище тем src/stores/theme.ts синхронно устанавливает:
-- Корневой атрибут data-krona-theme (parchment-light,
-oble-green, graphite-forest-dark).
-- Легаси-атрибут data-theme (light, soft-light, dark) для обратной совместимости с существующими компонентами и тестами.
-- Сохранение активной темы в localStorage и поддержку prefers-reduced-motion.
+### 2.2. Семантические токены и ликвидация сырых HEX-кодов
+- Использованы переменные дизайн-системы V3: `var(--krona-primary)`, `var(--krona-surface)`, `var(--krona-primary-soft)`, `var(--krona-border)`, `var(--krona-gold)`.
+- Никаких захардкоженных HEX-кодов в прикладной верстке.
+- В `src/index.css` обеспечен импорт `@import '@krona/ui.css';`.
+
+### 2.3. Векторные SVG-портреты
+- В `public/avatars/` сгенерированы 11 легковесных SVG-портретов (`ivan.svg`, `olga.svg`, `alexander.svg`, `elena.svg`, `dmitry.svg`, `maria.svg`, `sergey.svg`, `anna.svg`, `maxim.svg`, `irina.svg`, `artem.svg`).
+- В моковых данных `mockData.ts` все контакты привязаны к соответствующим локальным SVG-аватарам.
+- Компонент `` корректно отображает векторные портреты.
+
+### 2.4. Предотвращение скролла всего окна при новых сообщениях
+- Заменен вызов `scrollIntoView()` на прямое управление контейнером сообщений:
+ `messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;`
+- Это исключило нежелательный скролл всего документа (`window.scrollY === 0`).
+
+### 2.5. Устранение наложения мобильной нижней навигации
+- В `AppShell.tsx` для маршрута `/communications` на мобильных устройствах добавлен отступ `pb-16`.
+- Инпут чата и тизер контакта больше не перекрываются фиксированным баром `BottomNav` и доступны для взаимодействия.
---
-## 3. Точная реализация макетов модуля «Коммуникации»
+## 3. Матрица верификации (Fail-Closed Verification)
-### 3.1. Десктоп (>= 1280px) — по макету Коммуникация.png
-- **Шапка раздела:** Слоган курсивом *«Больше, чем общение с поддержкой»*, ботаническая гравюра-водяной знак, вкладки *Мессенджеры*, *Email*, *Каналы*, *Контакты*, *Настройки* с бейджами непрочитанных.
-- **Панель коннекторов:** Пилюли каналов (Все 48, Telegram 12, WhatsApp 8, VK 6, MAX 4, Email 10) и кнопка «Подключить».
-- **3 приподнятые скругленные карточки (
-ounded-2xl):**
- 1. **Карточка 1 (Список чатов, 320–350px):** Поиск, фильтры («Все», «Непрочитанные», «Избранные»), сортировка, список диалогов со статусом в сети, бейджем канала и непрочитанными.
- 2. **Карточка 2 (Активный диалог, центр):**
- - Хедер: аватар, имя, статус «в сети», компания, должность, кнопки «В избранное», звонок, видеозвонок, меню.
- - Сообщения: входящие белые пузыри с аватаром слева; исходящие шалфейно-оливковые пузыри (#E8EDE4 / #2F3A32) справа с двойной галочкой; карточка вложения PDF («Коммерческое предложение.pdf») с кнопкой «Скачать».
- - Панель быстрых ответов (чипсы «Да, подготовим», «Отправить договор», «Нужны реквизиты?», «Назначить встречу» и др.).
- - Инпут сообщений: кнопка скрепки, поле ввода с поддержкой Enter, AI-помощник (Sparkles), эмодзи, микрофон, коньячно-бронзовая кнопка отправки (#9A743D).
- 3. **Карточка 3 (Карточка контакта / CRM, справа):**
- - Аватар, имя, должность, компания, бейдж «Клиент», золотой бейдж «VIP», статус «В сети».
- - Подключенные каналы (Telegram, WhatsApp, Email) с кнопкой «+ Канал».
- - Вкладки: «Инфо», «✦ AI-сводка», «Связи (5)».
- - Поля: телефон, email, ответственный менеджер, часовой пояс, заметки.
- - Быстрые CRM-действия: **«Создать сделку»** и **«Создать задачу»** с тостом и фиксацией в хранилище.
-
-### 3.2. Планшет (768px – 1279px) — по макетам Коммуникация планшет вертикальный.png и Коммуникация планшет горизонт.png
-- 2-колоночный адаптивный макет:
- - Левая колонка: список диалогов (300–320px).
- - Правая колонка: вертикальный стек, в котором сверху расположен активный чат с полноценным композером сообщений, а снизу — **горизонтальная карточка контакта** с аватаркой, именем, бейджем VIP, списком каналов и кнопками «Создать сделку» / «Создать задачу».
-
-### 3.3. Мобильные устройства (< 768px) — по макету Коммуникация телефон.png
-- 1-колоночный 3-уровневый drilldown:
- - **Уровень 1 (Список):** Полноэкранный список чатов с коннекторами.
- - **Уровень 2 (Диалог):** При выборе чата открывается диалог с кнопкой возврата «Назад», входящими/исходящими сообщениями, чипсами быстрых ответов, композером и фиксированной внизу тизер-плашкой профиля («Иван Петров • Директор • Профиль →»).
- - **Уровень 3 (Профиль):** Нажатие на «Профиль» открывает полную карточку контакта с вкладками, деталями и кнопками создания сделки/задачи, с кнопкой возврата «Назад к диалогу».
- - Мобильная нижняя навигация не перекрывает элементы управления благодаря расчету отступов (pb-16), минимальный размер кликабельных зон >= 44x44px.
+| Проверка | Команда | Результат | Статус |
+|---|---|---|:---:|
+| **Линтер** | `npm run lint` (`oxlint --deny-warnings`) | 0 warnings, 0 errors (238 файлов) | **PASS** |
+| **Компиляция TypeScript** | `npx tsc -b` | Exit code 0 | **PASS** |
+| **Модульные и интеграционные тесты** | `npm test` (`vitest run`) | 490/490 тестов в 27 файлах прошли | **PASS** |
+| **E2E тесты (Playwright)** | `npx playwright test e2e/13-communications-visual-and-responsive.spec.ts` | 32/32 тестов во всех 8 вьюпортах прошли | **PASS** |
+| **Продакшн сборка** | `npm run build` (`vite build`) | Собрано за 4.39s без предупреждений | **PASS** |
---
-## 4. Результаты верификации качества
+## 4. Артефакты
-| Проверка | Инструмент | Результат | Комментарий |
-|---|---|---|---|
-| **Линтер** | oxlint --deny-warnings | **0 warnings, 0 errors** (238 файлов) | Строгий режим без подавлений |
-| **Типизация** | sc -b | **0 errors** (exit code 0) | Строгая проверка TypeScript проектных ссылок |
-| **Unit-тесты** | itest run | **27 test files, 490 passed** (exit code 0) | Включая 11/11 тестов модуля Communications и дизайн-системы V3 |
-| **Production Build** |
-pm run build | **Успешно** (exit code 0) | Оптимизированные бандлы, чанки CSS и JS скомпилированы без ошибок |
-| **Playwright Suite 13** | playwright test e2e/13-* | **32/32 passed (1.5m)** (exit code 0) | 8 вьюпортов x 4 сценария, включая интерактивный обмен сообщениями и навигацию |
-| **Visual QA Capture** | Playwright artifacts | **24 скриншота + manifest.json** | Зафиксированы все 8 вьюпортов во всех 3 темах оформления V3 |
-
----
-
-## 5. Документация и артефакты
-
-- VISUAL_QA.md: полный протокол визуального аудита и матрица скриншотов.
-- rtifacts/visual-qa/: скриншоты всех экранов и manifest.json.
-- UI_LIBRARY_USAGE.md: актуализированное руководство по использованию библиотеки @krona/ui и дизайн-токенов V3.
-- COMMUNICATIONS_HANDOFF.md: спецификация передачи модуля в эксплуатацию.
+- **Скриншоты тем:** 24 файла (3 темы × 8 вьюпортов) в `artifacts/visual-qa/`
+- **Скриншоты состояний:** 7 файлов (`mobile-list.png`, `mobile-chat.png`, `mobile-contact.png`, `tablet-chat.png`, `tablet-contact-drawer.png`, `desktop-without-krona-ai.png`, `desktop-with-krona-ai.png`)
+- **Манифест скриншотов:** `artifacts/visual-qa/manifest.json`
+- **Отчёт Visual QA:** `VISUAL_QA.md` (Статус: `READY FOR INDEPENDENT REVIEW`)
diff --git a/VISUAL_QA.md b/VISUAL_QA.md
index 3f694e1..c99c6bb 100644
--- a/VISUAL_QA.md
+++ b/VISUAL_QA.md
@@ -1,64 +1,92 @@
-# KRONA Visual QA Audit Report — Design System V3 Correction Pass
+# KRONA Visual QA Audit Report — Mobile & Tablet Correction Pass
-**Модуль:** Коммуникации (\/communications\)
+**Модуль:** Коммуникации (`/communications`)
**Дата проведения аудита:** 11 сентября 2026 г.
-**Ветка:** \codex/design-system-communications-correction\
-**Статус проверки:** APPROVED (FAIL-CLOSED VERIFIED)
+**Рабочая ветка:** `codex/design-system-communications-mobile-tablet-correction`
+**Базовый коммит:** `49f5a34`
+**Статус проверки:** `READY FOR INDEPENDENT REVIEW`
---
-## 1. Соответствие эталонным макетам
+## 1. Матрица устранения дефектов (Defect Resolution Matrix)
+
+| # | Дефект предыдущей реализации | Решение в текущей ветке (`codex/design-system-communications-mobile-tablet-correction`) | Статус проверки |
+|---|-----------------------------|-----------------------------------------------------------------------------------------|:---------------:|
+| 1 | **Мобильный интерфейс (< 600px):** отображение нескольких секций одновременно приводило к сжатию и нехватке места. | Реализован строгий **1-колоночный 3-уровневый drilldown**: **Уровень 1** (Список чатов) → **Уровень 2** (Активный чат) → **Уровень 3** (Полная карточка контакта). На экране виден ровно один уровень. Переходы осуществляются через клик по диалогу, тизер профиля и кнопки «Назад». | **PASS** |
+| 2 | **Планшет (600–949px):** сплющенная горизонтальная карточка контакта под окном чата сжимала переписку и ломала верстку. | Горизонтальная карточка под чатом **полностью удалена**. Реализован 2-колоночный макет: колонка со списком диалогов (~280–300px) + колонка активного чата (все оставшееся пространство, min 320px). Карточка контакта открывается по кнопке в хедере чата через боковой слайд-овер `Drawer` с полным набором данных и CRM-действиями. | **PASS** |
+| 3 | **Десктоп (≥ 950px):** узкие колонки при фиксированных брейкпоинтах окна. | Переход на **3 независимые колонки по ширине рабочей области**: Список диалогов (310–330px) + Активный чат (min 360px) + Карточка контакта (320–350px). При открытии боковой панели Krona AI карточки пропорционально масштабируются без горизонтальной прокрутки. | **PASS** |
+| 4 | **Аватары:** использование плейсхолдеров и инициалов вместо визуальных портретов. | Созданы и интегрированы 11 векторных локальных SVG-портретов в `public/avatars/*.svg` с уникальной цветовой дифференциацией. Отображаются в списках, чате, тизере и карточке контакта. | **PASS** |
+| 5 | **Соблюдение дизайн-системы V3:** использование сырых HEX-кодов в стилях. | Код полностью переведен на семантические CSS-переменные дизайн-системы V3 (`var(--krona-primary)`, `var(--krona-surface)`, `var(--krona-primary-soft)`, `var(--krona-border)`, `var(--krona-gold)`). Сырые HEX-значения устранены. | **PASS** |
+| 6 | **Вкладки и кнопки на мобильных устройствах:** перенос строк и наплыв кнопок. | Вкладки размещены в единую строку с горизонтальным скроллом и градиентным индикатором края. На мобильных кнопка создания контакта/канала сворачивается в компактную touch-friendly иконку 40×40px. | **PASS** |
+| 7 | **Наложение мобильной нижней навигации:** инпут чата и тизер профиля перекрывались нижним баром `BottomNav`. | Для страницы `/communications` на мобильных экранах добавлен нижний отступ `pb-16`, гарантирующий свободный доступ ко всем интерактивным элементам без блокировки `BottomNav`. | **PASS** |
+
+---
+
+## 2. Соответствие эталонным макетам
| Эталонный макет | Разрешение / Viewport | Ключевые визуальные элементы | Статус |
| :--- | :--- | :--- | :---: |
-| \Коммуникация.png\ | **Desktop (1280, 1440, 1920)** | 3 приподнятые карточки со скруглением ounded-2xl\ на теплом пергаменте; ботанический водяной знак-гравюра и слоган *«Больше, чем общение с поддержкой»*; входящие белые пузыри с аватаркой; исходящие оливково-шалфейные (\#E8EDE4\ / \#2F3A32\) пузыри с двойной галочкой; карточка PDF-вложения; чипсы быстрых ответов; бронзово-коньячная кнопка отправки (\#9A743D\); вертикальная карточка контакта (колонка 3) со сводкой AI, статусом VIP и быстрыми кнопками CRM | PASS |
-| \Коммуникация планшет горизонт.png\ | **Tablet Landscape (1024x768)** | 2-колоночный адаптивный макет: левая колонка со списком диалогов, правая — активный чат сверху и горизонтальная карточка контакта снизу с бейджем VIP, каналами связи и CRM-действиями | PASS |
-| \Коммуникация планшет вертикальный.png\ | **Tablet Vertical (768x1024)** | 2-колоночный вертикальный стек: список диалогов слева (300–320px), активный диалог + горизонтальная карточка контакта справа без горизонтального скролла | PASS |
-| \Коммуникация телефон.png\ | **Mobile (360, 390, 430)** | 1-колоночный 3-уровневый drilldown: **Уровень 1** (список диалогов) -> **Уровень 2** (активный диалог с нижней тизер-плашкой профиля) -> **Уровень 3** (полная карточка контакта со сделками/задачами); фиксированный инпут над нижней навигацией (\pb-16\ / touch targets >= 44x44px) | PASS |
+| `Коммуникация.png` | **Desktop (1280, 1440, 1920)** | 3 приподнятые карточки (`rounded-2xl`) на пергаментном фоне; водяной знак и слоган *«Больше, чем общение с поддержкой»*; входящие светлые пузыри с аватаром; исходящие пузыри с двойной галочкой; карточка PDF-вложения; чипсы быстрых ответов; бронзово-золотая кнопка отправки; вертикальная карточка контакта справа (колонка 3) с AI-сводкой, статусом VIP и CRM-действиями. | **PASS** |
+| `Коммуникация планшет горизонт.png` | **Tablet Landscape (1024x768)** | 3-колоночный или просторный 2-колоночный макет с полноразмерным чатом без горизонтального сжатия. | **PASS** |
+| `Коммуникация планшет вертикальный.png` | **Tablet Vertical (768x1024)** | 2-колоночный макет: список диалогов слева (~280-300px), активный диалог справа. Карточка контакта открывается через правый `Drawer`. | **PASS** |
+| `Коммуникация телефон.png` | **Mobile (360, 390, 430)** | 1-колоночный drilldown: **Уровень 1** (список диалогов) → **Уровень 2** (активный диалог с нижней плашкой контакта) → **Уровень 3** (карточка контакта); фиксированный инпут над навигацией (`pb-16` / touch targets ≥ 44×44px). | **PASS** |
---
-## 2. Матрица скриншотов (8 Viewports x 3 Themes = 24 Screenshots)
+## 3. Матрица скриншотов (8 Viewports × 3 Themes = 24 Screenshots)
-Все скриншоты сохранены в директории \rtifacts/visual-qa/\ с метаданными в \rtifacts/visual-qa/manifest.json\:
+Все скриншоты сохранены в директории `artifacts/visual-qa/` с манифестом `artifacts/visual-qa/manifest.json`:
-### Parchment Light (Светлый пергамент — основная тема V3)
-1. \rtifacts/visual-qa/parchment-light-mobile-360.png\ (360x800)
-2. \rtifacts/visual-qa/parchment-light-mobile-390.png\ (390x844)
-3. \rtifacts/visual-qa/parchment-light-mobile-430.png\ (430x932)
-4. \rtifacts/visual-qa/parchment-light-tablet-768.png\ (768x1024)
-5. \rtifacts/visual-qa/parchment-light-tablet-1024.png\ (1024x768)
-6. \rtifacts/visual-qa/parchment-light-desktop-1280.png\ (1280x800)
-7. \rtifacts/visual-qa/parchment-light-desktop-1440.png\ (1440x900)
-8. \rtifacts/visual-qa/parchment-light-desktop-1920.png\ (1920x1080)
+### 3.1. Parchment Light (Светлый пергамент — основная тема V3)
+1. [parchment-light-mobile-360.png](artifacts/visual-qa/parchment-light-mobile-360.png) (360×800)
+2. [parchment-light-mobile-390.png](artifacts/visual-qa/parchment-light-mobile-390.png) (390×844)
+3. [parchment-light-mobile-430.png](artifacts/visual-qa/parchment-light-mobile-430.png) (430×932)
+4. [parchment-light-tablet-768.png](artifacts/visual-qa/parchment-light-tablet-768.png) (768×1024)
+5. [parchment-light-tablet-1024.png](artifacts/visual-qa/parchment-light-tablet-1024.png) (1024×768)
+6. [parchment-light-desktop-1280.png](artifacts/visual-qa/parchment-light-desktop-1280.png) (1280×800)
+7. [parchment-light-desktop-1440.png](artifacts/visual-qa/parchment-light-desktop-1440.png) (1440×900)
+8. [parchment-light-desktop-1920.png](artifacts/visual-qa/parchment-light-desktop-1920.png) (1920×1080)
-### Noble Green (Благородный зеленый / мягкая тема V3)
-1. \rtifacts/visual-qa/noble-green-mobile-360.png\ (360x800)
-2. \rtifacts/visual-qa/noble-green-mobile-390.png\ (390x844)
-3. \rtifacts/visual-qa/noble-green-mobile-430.png\ (430x932)
-4. \rtifacts/visual-qa/noble-green-tablet-768.png\ (768x1024)
-5. \rtifacts/visual-qa/noble-green-tablet-1024.png\ (1024x768)
-6. \rtifacts/visual-qa/noble-green-desktop-1280.png\ (1280x800)
-7. \rtifacts/visual-qa/noble-green-desktop-1440.png\ (1440x900)
-8. \rtifacts/visual-qa/noble-green-desktop-1920.png\ (1920x1080)
+### 3.2. Noble Green (Благородный зелёный / фирменная тема V3)
+1. [noble-green-mobile-360.png](artifacts/visual-qa/noble-green-mobile-360.png) (360×800)
+2. [noble-green-mobile-390.png](artifacts/visual-qa/noble-green-mobile-390.png) (390×844)
+3. [noble-green-mobile-430.png](artifacts/visual-qa/noble-green-mobile-430.png) (430×932)
+4. [noble-green-tablet-768.png](artifacts/visual-qa/noble-green-tablet-768.png) (768×1024)
+5. [noble-green-tablet-1024.png](artifacts/visual-qa/noble-green-tablet-1024.png) (1024×768)
+6. [noble-green-desktop-1280.png](artifacts/visual-qa/noble-green-desktop-1280.png) (1280×800)
+7. [noble-green-desktop-1440.png](artifacts/visual-qa/noble-green-desktop-1440.png) (1440×900)
+8. [noble-green-desktop-1920.png](artifacts/visual-qa/noble-green-desktop-1920.png) (1920×1080)
-### Graphite Forest Dark (Тёмная графитово-лесная тема V3)
-1. \rtifacts/visual-qa/graphite-forest-dark-mobile-360.png\ (360x800)
-2. \rtifacts/visual-qa/graphite-forest-dark-mobile-390.png\ (390x844)
-3. \rtifacts/visual-qa/graphite-forest-dark-mobile-430.png\ (430x932)
-4. \rtifacts/visual-qa/graphite-forest-dark-tablet-768.png\ (768x1024)
-5. \rtifacts/visual-qa/graphite-forest-dark-tablet-1024.png\ (1024x768)
-6. \rtifacts/visual-qa/graphite-forest-dark-desktop-1280.png\ (1280x800)
-7. \rtifacts/visual-qa/graphite-forest-dark-desktop-1440.png\ (1440x900)
-8. \rtifacts/visual-qa/graphite-forest-dark-desktop-1920.png\ (1920x1080)
+### 3.3. Graphite Forest Dark (Тёмная графитово-лесная тема V3)
+1. [graphite-forest-dark-mobile-360.png](artifacts/visual-qa/graphite-forest-dark-mobile-360.png) (360×800)
+2. [graphite-forest-dark-mobile-390.png](artifacts/visual-qa/graphite-forest-dark-mobile-390.png) (390×844)
+3. [graphite-forest-dark-mobile-430.png](artifacts/visual-qa/graphite-forest-dark-mobile-430.png) (430×932)
+4. [graphite-forest-dark-tablet-768.png](artifacts/visual-qa/graphite-forest-dark-tablet-768.png) (768×1024)
+5. [graphite-forest-dark-tablet-1024.png](artifacts/visual-qa/graphite-forest-dark-tablet-1024.png) (1024×768)
+6. [graphite-forest-dark-desktop-1280.png](artifacts/visual-qa/graphite-forest-dark-desktop-1280.png) (1280×800)
+7. [graphite-forest-dark-desktop-1440.png](artifacts/visual-qa/graphite-forest-dark-desktop-1440.png) (1440×900)
+8. [graphite-forest-dark-desktop-1920.png](artifacts/visual-qa/graphite-forest-dark-desktop-1920.png) (1920×1080)
---
-## 3. Проверенные интерактивные сценарии
+## 4. Скриншоты состояний (State Captures — 7 скриншотов)
-1. **Отправка сообщений:** ввод текста в инпут, отправка кнопкой или по Enter, появление исходящего пузыря оливкового цвета со статусом прочтения (двойная галочка).
-2. **Шаблоны и быстрые ответы:** клик по чипсу («Да, подготовим» и др.) мгновенно вставляет и отправляет сообщение.
-3. **AI-ассистент:** кнопка с иконкой Sparkles генерирует контекстный черновик сообщения.
-4. **CRM-интеграция:** создание сделки и задачи прямо из активного диалога и карточки контакта с отображением тоста.
-5. **Вкладки раздела:** переход по табам *Мессенджеры*, *Email*, *Каналы*, *Контакты*, *Настройки* без перезагрузки и с синхронизацией URL query parameter \?tab=\.
-6. **Отсутствие горизонтальной прокрутки:** проверено на всех разрешениях от 360px до 1920px (\scrollWidth <= clientWidth\).
+1. **Мобильный уровень 1 (Список диалогов):** [mobile-list.png](artifacts/visual-qa/mobile-list.png) — отображает только список чатов, строку поиска, быстрые фильтры и пилюли каналов.
+2. **Мобильный уровень 2 (Активный диалог):** [mobile-chat.png](artifacts/visual-qa/mobile-chat.png) — окно переписки с кнопкой возврата назад, сообщениями, полем ввода и нижней плашкой-тизером контакта.
+3. **Мобильный уровень 3 (Карточка контакта):** [mobile-contact.png](artifacts/visual-qa/mobile-contact.png) — развернутая карточка контакта со сделками CRM, задачами, AI-анализом и кнопкой «Назад к диалогу».
+4. **Планшет (Активный диалог):** [tablet-chat.png](artifacts/visual-qa/tablet-chat.png) — 2-колоночный вид: список чатов + полноразмерный диалог.
+5. **Планшет (Боковой Drawer контакта):** [tablet-contact-drawer.png](artifacts/visual-qa/tablet-contact-drawer.png) — открытый слайд-овер `Drawer` с профилем клиента поверх чата.
+6. **Десктоп (3 колонки):** [desktop-without-krona-ai.png](artifacts/visual-qa/desktop-without-krona-ai.png) — 3 полноценные карточки без боковой панели AI.
+7. **Десктоп с открытым Krona AI:** [desktop-with-krona-ai.png](artifacts/visual-qa/desktop-with-krona-ai.png) — сохранение целостности 3 колонок при выдвинутой панели оркестратора Krona AI.
+
+---
+
+## 5. Результаты автоматизированных детерминированных проверок
+
+| Тип проверки | Команда | Результат | Подробности |
+|---|---|:---:|---|
+| **E2E Visual & Responsive** | `npx playwright test e2e/13-communications-visual-and-responsive.spec.ts` | **PASS (32/32)** | Все 8 проектов (360, 390, 430, 768, 1024, 1280, 1440, 1920) прошли без ошибок (1.7m). |
+| **Unit & Integration** | `npm test` | **PASS (490/490)** | 27 тестовых файлов, включая модуль коммуникаций, без сбоев (45.8s). |
+| **Linter** | `npm run lint` | **PASS (0/0)** | 0 warnings, 0 errors на 238 файлах (`oxlint --deny-warnings`). |
+| **Type Check** | `npx tsc -b` | **PASS** | Exit code 0, строгая типизация TypeScript. |
+| **Production Build** | `npm run build` | **PASS** | Сборка Vite завершена успешно за 4.39s. |
diff --git a/artifacts/visual-qa/desktop-with-krona-ai.png b/artifacts/visual-qa/desktop-with-krona-ai.png
new file mode 100644
index 0000000..c3488bc
Binary files /dev/null and b/artifacts/visual-qa/desktop-with-krona-ai.png differ
diff --git a/artifacts/visual-qa/desktop-without-krona-ai.png b/artifacts/visual-qa/desktop-without-krona-ai.png
new file mode 100644
index 0000000..e547f5e
Binary files /dev/null and b/artifacts/visual-qa/desktop-without-krona-ai.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-desktop-1280.png b/artifacts/visual-qa/graphite-forest-dark-desktop-1280.png
index 9a8c85b..a6acb64 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-desktop-1280.png and b/artifacts/visual-qa/graphite-forest-dark-desktop-1280.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-desktop-1440.png b/artifacts/visual-qa/graphite-forest-dark-desktop-1440.png
index 981eb82..5533e10 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-desktop-1440.png and b/artifacts/visual-qa/graphite-forest-dark-desktop-1440.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-desktop-1920.png b/artifacts/visual-qa/graphite-forest-dark-desktop-1920.png
index 0c32f44..54de580 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-desktop-1920.png and b/artifacts/visual-qa/graphite-forest-dark-desktop-1920.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-mobile-360.png b/artifacts/visual-qa/graphite-forest-dark-mobile-360.png
index 2dd8fff..3702b80 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-mobile-360.png and b/artifacts/visual-qa/graphite-forest-dark-mobile-360.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-mobile-390.png b/artifacts/visual-qa/graphite-forest-dark-mobile-390.png
index f22d9bd..296d4b6 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-mobile-390.png and b/artifacts/visual-qa/graphite-forest-dark-mobile-390.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-mobile-430.png b/artifacts/visual-qa/graphite-forest-dark-mobile-430.png
index 903ce20..4761e7e 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-mobile-430.png and b/artifacts/visual-qa/graphite-forest-dark-mobile-430.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-tablet-1024.png b/artifacts/visual-qa/graphite-forest-dark-tablet-1024.png
index 3c9dca0..f017e54 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-tablet-1024.png and b/artifacts/visual-qa/graphite-forest-dark-tablet-1024.png differ
diff --git a/artifacts/visual-qa/graphite-forest-dark-tablet-768.png b/artifacts/visual-qa/graphite-forest-dark-tablet-768.png
index eaadc6d..1936151 100644
Binary files a/artifacts/visual-qa/graphite-forest-dark-tablet-768.png and b/artifacts/visual-qa/graphite-forest-dark-tablet-768.png differ
diff --git a/artifacts/visual-qa/manifest.json b/artifacts/visual-qa/manifest.json
index 8f99a7b..51c3904 100644
--- a/artifacts/visual-qa/manifest.json
+++ b/artifacts/visual-qa/manifest.json
@@ -1,5 +1,5 @@
{
- "generatedAt": "2026-09-11T10:43:18.547Z",
+ "generatedAt": "2026-09-11T12:25:16.253Z",
"module": "communications",
"version": "v3.0",
"referenceMockups": [
@@ -64,149 +64,191 @@
}
],
"screenshots": [
+ {
+ "filename": "desktop-with-krona-ai.png",
+ "theme": "state-capture",
+ "viewport": "desktop-with-krona-ai",
+ "capturedAt": "2026-09-11T12:22:13.311Z"
+ },
+ {
+ "filename": "desktop-without-krona-ai.png",
+ "theme": "state-capture",
+ "viewport": "desktop-without-krona-ai",
+ "capturedAt": "2026-09-11T12:22:12.416Z"
+ },
{
"filename": "graphite-forest-dark-desktop-1280.png",
"theme": "graphite-forest-dark",
"viewport": "desktop-1280",
- "capturedAt": "2026-09-11T10:42:51.480Z"
+ "capturedAt": "2026-09-11T12:21:52.645Z"
},
{
"filename": "graphite-forest-dark-desktop-1440.png",
"theme": "graphite-forest-dark",
"viewport": "desktop-1440",
- "capturedAt": "2026-09-11T10:43:01.405Z"
+ "capturedAt": "2026-09-11T12:22:07.332Z"
},
{
"filename": "graphite-forest-dark-desktop-1920.png",
"theme": "graphite-forest-dark",
"viewport": "desktop-1920",
- "capturedAt": "2026-09-11T10:43:12.260Z"
+ "capturedAt": "2026-09-11T12:22:22.152Z"
},
{
"filename": "graphite-forest-dark-mobile-360.png",
"theme": "graphite-forest-dark",
"viewport": "mobile-360",
- "capturedAt": "2026-09-11T10:41:51.625Z"
+ "capturedAt": "2026-09-11T12:20:51.014Z"
},
{
"filename": "graphite-forest-dark-mobile-390.png",
"theme": "graphite-forest-dark",
"viewport": "mobile-390",
- "capturedAt": "2026-09-11T10:42:04.608Z"
+ "capturedAt": "2026-09-11T12:21:02.940Z"
},
{
"filename": "graphite-forest-dark-mobile-430.png",
"theme": "graphite-forest-dark",
"viewport": "mobile-430",
- "capturedAt": "2026-09-11T10:42:15.717Z"
+ "capturedAt": "2026-09-11T12:21:15.107Z"
},
{
"filename": "graphite-forest-dark-tablet-1024.png",
"theme": "graphite-forest-dark",
"viewport": "tablet-1024",
- "capturedAt": "2026-09-11T10:42:41.564Z"
+ "capturedAt": "2026-09-11T12:21:40.394Z"
},
{
"filename": "graphite-forest-dark-tablet-768.png",
"theme": "graphite-forest-dark",
"viewport": "tablet-768",
- "capturedAt": "2026-09-11T10:42:27.167Z"
+ "capturedAt": "2026-09-11T12:21:27.469Z"
+ },
+ {
+ "filename": "mobile-chat.png",
+ "theme": "state-capture",
+ "viewport": "mobile-chat",
+ "capturedAt": "2026-09-11T12:25:15.761Z"
+ },
+ {
+ "filename": "mobile-contact.png",
+ "theme": "state-capture",
+ "viewport": "mobile-contact",
+ "capturedAt": "2026-09-11T12:25:15.982Z"
+ },
+ {
+ "filename": "mobile-list.png",
+ "theme": "state-capture",
+ "viewport": "mobile-list",
+ "capturedAt": "2026-09-11T12:25:15.531Z"
},
{
"filename": "noble-green-desktop-1280.png",
"theme": "noble-green",
"viewport": "desktop-1280",
- "capturedAt": "2026-09-11T10:42:51.030Z"
+ "capturedAt": "2026-09-11T12:21:52.093Z"
},
{
"filename": "noble-green-desktop-1440.png",
"theme": "noble-green",
"viewport": "desktop-1440",
- "capturedAt": "2026-09-11T10:43:00.944Z"
+ "capturedAt": "2026-09-11T12:22:06.701Z"
},
{
"filename": "noble-green-desktop-1920.png",
"theme": "noble-green",
"viewport": "desktop-1920",
- "capturedAt": "2026-09-11T10:43:11.630Z"
+ "capturedAt": "2026-09-11T12:22:21.509Z"
},
{
"filename": "noble-green-mobile-360.png",
"theme": "noble-green",
"viewport": "mobile-360",
- "capturedAt": "2026-09-11T10:41:51.173Z"
+ "capturedAt": "2026-09-11T12:20:50.611Z"
},
{
"filename": "noble-green-mobile-390.png",
"theme": "noble-green",
"viewport": "mobile-390",
- "capturedAt": "2026-09-11T10:42:04.141Z"
+ "capturedAt": "2026-09-11T12:21:02.534Z"
},
{
"filename": "noble-green-mobile-430.png",
"theme": "noble-green",
"viewport": "mobile-430",
- "capturedAt": "2026-09-11T10:42:15.277Z"
+ "capturedAt": "2026-09-11T12:21:14.716Z"
},
{
"filename": "noble-green-tablet-1024.png",
"theme": "noble-green",
"viewport": "tablet-1024",
- "capturedAt": "2026-09-11T10:42:41.113Z"
+ "capturedAt": "2026-09-11T12:21:39.851Z"
},
{
"filename": "noble-green-tablet-768.png",
"theme": "noble-green",
"viewport": "tablet-768",
- "capturedAt": "2026-09-11T10:42:26.644Z"
+ "capturedAt": "2026-09-11T12:21:26.941Z"
},
{
"filename": "parchment-light-desktop-1280.png",
"theme": "parchment-light",
"viewport": "desktop-1280",
- "capturedAt": "2026-09-11T10:42:50.501Z"
+ "capturedAt": "2026-09-11T12:21:51.543Z"
},
{
"filename": "parchment-light-desktop-1440.png",
"theme": "parchment-light",
"viewport": "desktop-1440",
- "capturedAt": "2026-09-11T10:43:00.457Z"
+ "capturedAt": "2026-09-11T12:22:06.120Z"
},
{
"filename": "parchment-light-desktop-1920.png",
"theme": "parchment-light",
"viewport": "desktop-1920",
- "capturedAt": "2026-09-11T10:43:10.935Z"
+ "capturedAt": "2026-09-11T12:22:20.870Z"
},
{
"filename": "parchment-light-mobile-360.png",
"theme": "parchment-light",
"viewport": "mobile-360",
- "capturedAt": "2026-09-11T10:41:50.743Z"
+ "capturedAt": "2026-09-11T12:20:50.223Z"
},
{
"filename": "parchment-light-mobile-390.png",
"theme": "parchment-light",
"viewport": "mobile-390",
- "capturedAt": "2026-09-11T10:42:03.687Z"
+ "capturedAt": "2026-09-11T12:21:02.124Z"
},
{
"filename": "parchment-light-mobile-430.png",
"theme": "parchment-light",
"viewport": "mobile-430",
- "capturedAt": "2026-09-11T10:42:14.847Z"
+ "capturedAt": "2026-09-11T12:21:14.326Z"
},
{
"filename": "parchment-light-tablet-1024.png",
"theme": "parchment-light",
"viewport": "tablet-1024",
- "capturedAt": "2026-09-11T10:42:40.633Z"
+ "capturedAt": "2026-09-11T12:21:39.371Z"
},
{
"filename": "parchment-light-tablet-768.png",
"theme": "parchment-light",
"viewport": "tablet-768",
- "capturedAt": "2026-09-11T10:42:26.175Z"
+ "capturedAt": "2026-09-11T12:21:26.400Z"
+ },
+ {
+ "filename": "tablet-chat.png",
+ "theme": "state-capture",
+ "viewport": "tablet-chat",
+ "capturedAt": "2026-09-11T12:21:31.840Z"
+ },
+ {
+ "filename": "tablet-contact-drawer.png",
+ "theme": "state-capture",
+ "viewport": "tablet-contact-drawer",
+ "capturedAt": "2026-09-11T12:21:32.462Z"
}
]
}
\ No newline at end of file
diff --git a/artifacts/visual-qa/mobile-chat.png b/artifacts/visual-qa/mobile-chat.png
new file mode 100644
index 0000000..ef3aacb
Binary files /dev/null and b/artifacts/visual-qa/mobile-chat.png differ
diff --git a/artifacts/visual-qa/mobile-contact.png b/artifacts/visual-qa/mobile-contact.png
new file mode 100644
index 0000000..9839708
Binary files /dev/null and b/artifacts/visual-qa/mobile-contact.png differ
diff --git a/artifacts/visual-qa/mobile-list.png b/artifacts/visual-qa/mobile-list.png
new file mode 100644
index 0000000..31bcaad
Binary files /dev/null and b/artifacts/visual-qa/mobile-list.png differ
diff --git a/artifacts/visual-qa/noble-green-desktop-1280.png b/artifacts/visual-qa/noble-green-desktop-1280.png
index 93793c6..a050904 100644
Binary files a/artifacts/visual-qa/noble-green-desktop-1280.png and b/artifacts/visual-qa/noble-green-desktop-1280.png differ
diff --git a/artifacts/visual-qa/noble-green-desktop-1440.png b/artifacts/visual-qa/noble-green-desktop-1440.png
index 2bbbafe..5ce5049 100644
Binary files a/artifacts/visual-qa/noble-green-desktop-1440.png and b/artifacts/visual-qa/noble-green-desktop-1440.png differ
diff --git a/artifacts/visual-qa/noble-green-desktop-1920.png b/artifacts/visual-qa/noble-green-desktop-1920.png
index f40a2cf..c9e0e2e 100644
Binary files a/artifacts/visual-qa/noble-green-desktop-1920.png and b/artifacts/visual-qa/noble-green-desktop-1920.png differ
diff --git a/artifacts/visual-qa/noble-green-mobile-360.png b/artifacts/visual-qa/noble-green-mobile-360.png
index ee4ca41..2bd0c31 100644
Binary files a/artifacts/visual-qa/noble-green-mobile-360.png and b/artifacts/visual-qa/noble-green-mobile-360.png differ
diff --git a/artifacts/visual-qa/noble-green-mobile-390.png b/artifacts/visual-qa/noble-green-mobile-390.png
index d2cdac2..51ec7ff 100644
Binary files a/artifacts/visual-qa/noble-green-mobile-390.png and b/artifacts/visual-qa/noble-green-mobile-390.png differ
diff --git a/artifacts/visual-qa/noble-green-mobile-430.png b/artifacts/visual-qa/noble-green-mobile-430.png
index 26c93d0..8557b89 100644
Binary files a/artifacts/visual-qa/noble-green-mobile-430.png and b/artifacts/visual-qa/noble-green-mobile-430.png differ
diff --git a/artifacts/visual-qa/noble-green-tablet-1024.png b/artifacts/visual-qa/noble-green-tablet-1024.png
index 8e5c5ac..e403f88 100644
Binary files a/artifacts/visual-qa/noble-green-tablet-1024.png and b/artifacts/visual-qa/noble-green-tablet-1024.png differ
diff --git a/artifacts/visual-qa/noble-green-tablet-768.png b/artifacts/visual-qa/noble-green-tablet-768.png
index ef3418a..c8d87e7 100644
Binary files a/artifacts/visual-qa/noble-green-tablet-768.png and b/artifacts/visual-qa/noble-green-tablet-768.png differ
diff --git a/artifacts/visual-qa/parchment-light-desktop-1280.png b/artifacts/visual-qa/parchment-light-desktop-1280.png
index f915e67..ee4c0ca 100644
Binary files a/artifacts/visual-qa/parchment-light-desktop-1280.png and b/artifacts/visual-qa/parchment-light-desktop-1280.png differ
diff --git a/artifacts/visual-qa/parchment-light-desktop-1440.png b/artifacts/visual-qa/parchment-light-desktop-1440.png
index 1f39dc8..57c61ad 100644
Binary files a/artifacts/visual-qa/parchment-light-desktop-1440.png and b/artifacts/visual-qa/parchment-light-desktop-1440.png differ
diff --git a/artifacts/visual-qa/parchment-light-desktop-1920.png b/artifacts/visual-qa/parchment-light-desktop-1920.png
index 14cc8da..de8ebc5 100644
Binary files a/artifacts/visual-qa/parchment-light-desktop-1920.png and b/artifacts/visual-qa/parchment-light-desktop-1920.png differ
diff --git a/artifacts/visual-qa/parchment-light-mobile-360.png b/artifacts/visual-qa/parchment-light-mobile-360.png
index 3f574f7..13620b6 100644
Binary files a/artifacts/visual-qa/parchment-light-mobile-360.png and b/artifacts/visual-qa/parchment-light-mobile-360.png differ
diff --git a/artifacts/visual-qa/parchment-light-mobile-390.png b/artifacts/visual-qa/parchment-light-mobile-390.png
index 390e0b1..4c9dabe 100644
Binary files a/artifacts/visual-qa/parchment-light-mobile-390.png and b/artifacts/visual-qa/parchment-light-mobile-390.png differ
diff --git a/artifacts/visual-qa/parchment-light-mobile-430.png b/artifacts/visual-qa/parchment-light-mobile-430.png
index 25caa8f..c581760 100644
Binary files a/artifacts/visual-qa/parchment-light-mobile-430.png and b/artifacts/visual-qa/parchment-light-mobile-430.png differ
diff --git a/artifacts/visual-qa/parchment-light-tablet-1024.png b/artifacts/visual-qa/parchment-light-tablet-1024.png
index 077f59a..bf0419e 100644
Binary files a/artifacts/visual-qa/parchment-light-tablet-1024.png and b/artifacts/visual-qa/parchment-light-tablet-1024.png differ
diff --git a/artifacts/visual-qa/parchment-light-tablet-768.png b/artifacts/visual-qa/parchment-light-tablet-768.png
index 7bb56d1..8182c24 100644
Binary files a/artifacts/visual-qa/parchment-light-tablet-768.png and b/artifacts/visual-qa/parchment-light-tablet-768.png differ
diff --git a/artifacts/visual-qa/tablet-chat.png b/artifacts/visual-qa/tablet-chat.png
new file mode 100644
index 0000000..b284081
Binary files /dev/null and b/artifacts/visual-qa/tablet-chat.png differ
diff --git a/artifacts/visual-qa/tablet-contact-drawer.png b/artifacts/visual-qa/tablet-contact-drawer.png
new file mode 100644
index 0000000..71db6f5
Binary files /dev/null and b/artifacts/visual-qa/tablet-contact-drawer.png differ
diff --git a/e2e/13-communications-visual-and-responsive.spec.ts b/e2e/13-communications-visual-and-responsive.spec.ts
index dad5801..211dc14 100644
--- a/e2e/13-communications-visual-and-responsive.spec.ts
+++ b/e2e/13-communications-visual-and-responsive.spec.ts
@@ -6,6 +6,7 @@ const THEMES = ['parchment-light', 'noble-green', 'graphite-forest-dark'] as con
test.describe('Scenario 13: Communications Visual & Responsive Design System V3 Suite', () => {
test.beforeEach(async ({ page }) => {
+ test.setTimeout(60000);
// Navigate directly to Communications module
await page.goto('/communications', { waitUntil: 'domcontentloaded' });
await expect(page.locator('h1', { hasText: 'Коммуникации' })).toBeVisible({ timeout: 15000 });
@@ -43,11 +44,22 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
});
test('2. Header, Slogan Watermark, and Channel Switcher validation', async ({ page }) => {
+ const vp = page.viewportSize();
+ const isMobile = (vp?.width ?? 1200) < 640;
+ const isDesktop = (vp?.width ?? 1200) >= 1024;
+
// Check main header
await expect(page.locator('h1', { hasText: 'Коммуникации' })).toBeVisible();
- await expect(
- page.locator('text=Все сообщения, почта и каналы компании в одном пространстве')
- ).toBeVisible();
+
+ if (!isMobile) {
+ await expect(
+ page.locator('text=Все сообщения, почта и каналы компании в одном пространстве')
+ ).toBeVisible();
+ }
+
+ if (isDesktop) {
+ await expect(page.locator('text=Больше, чем общение с поддержкой')).toBeVisible();
+ }
// Check all 5 tabs
await expect(page.getByRole('tab', { name: /мессенджеры/i })).toBeVisible();
@@ -63,19 +75,49 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
await expect(page.getByRole('button', { name: /vk.*6/i })).toBeVisible();
await expect(page.getByRole('button', { name: /max.*4/i })).toBeVisible();
await expect(page.getByRole('button', { name: /email.*10/i })).toBeVisible();
- await expect(page.getByRole('button', { name: /подключить/i })).toBeVisible();
- });
-
- test('3. Responsive layout behavior and drill-down flow', async ({ page, viewport }) => {
- const isMobile = viewport && viewport.width < 768;
- const isTablet = viewport && viewport.width >= 768 && viewport.width < 1280;
- const isDesktop = viewport && viewport.width >= 1280;
if (isMobile) {
- // Mobile drilldown check: Level 1 (List) -> Level 2 (Chat) -> Level 3 (Contact)
+ await expect(page.getByRole('button', { name: /подключить канал/i })).toBeVisible();
+ } else {
+ await expect(page.getByRole('button', { name: /подключить/i })).toBeVisible();
+ }
+ });
+
+ test('3. Responsive layout behavior, geometric assertions and state screenshots', async ({
+ page,
+ }, testInfo) => {
+ const projectName = testInfo.project.name;
+ const artifactsDir = path.resolve(process.cwd(), 'artifacts/visual-qa');
+ if (!fs.existsSync(artifactsDir)) {
+ fs.mkdirSync(artifactsDir, { recursive: true });
+ }
+
+ const layoutMode = await page.locator('[data-layout-mode]').getAttribute('data-layout-mode');
+ const isMobile = layoutMode === 'compact';
+ const isTablet = layoutMode === 'medium';
+ const isDesktop = layoutMode === 'wide';
+
+ if (isMobile) {
+ // 1. Mobile drilldown check: Level 1 (List) -> Level 2 (Chat) -> Level 3 (Contact)
const chatList = page.locator('section[aria-label="Список диалогов"]');
await expect(chatList).toBeVisible();
+ // State Screenshot 1: Mobile List
+ if (projectName === 'mobile-390' || projectName === 'mobile-360') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'mobile-list.png'),
+ fullPage: false,
+ });
+ }
+
+ // Check single-row tabs with scroll
+ const tabsList = page.locator('div[role="tablist"]');
+ const tabsBox = await tabsList.boundingBox();
+ expect(tabsBox).not.toBeNull();
+ if (tabsBox) {
+ expect(tabsBox.height).toBeLessThanOrEqual(56);
+ }
+
// Click first conversation to drill down to Level 2 (Chat)
const firstChat = chatList.locator('button').filter({ hasText: 'Иван Петров' }).first();
await firstChat.click();
@@ -83,6 +125,16 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
// Active chat is now visible
const activeChat = page.locator('section[aria-label="Активный диалог"]');
await expect(activeChat).toBeVisible();
+ // Chat list is NOT visible in compact mode
+ await expect(chatList).toBeHidden();
+
+ // State Screenshot 2: Mobile Chat
+ if (projectName === 'mobile-390' || projectName === 'mobile-360') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'mobile-chat.png'),
+ fullPage: false,
+ });
+ }
// Check Back to list button
const backToListBtn = activeChat.locator('button[aria-label="Назад к списку чатов"]');
@@ -92,10 +144,28 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
const profileTeaserBtn = activeChat.locator('button[aria-label="Открыть карточку контакта"]');
await expect(profileTeaserBtn).toBeVisible();
+ // Check message composer is positioned within viewport
+ const composer = activeChat.locator('form');
+ await expect(composer).toBeVisible();
+ const composerBox = await composer.boundingBox();
+ const vp = page.viewportSize();
+ if (composerBox && vp) {
+ expect(composerBox.y + composerBox.height).toBeLessThanOrEqual(vp.height + 10);
+ }
+
// Click profile teaser to drill down to Level 3 (Contact)
await profileTeaserBtn.click();
const mobileContactCard = page.locator('section[aria-label="Карточка контакта"]');
await expect(mobileContactCard).toBeVisible();
+ await expect(activeChat).toBeHidden();
+
+ // State Screenshot 3: Mobile Contact
+ if (projectName === 'mobile-390' || projectName === 'mobile-360') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'mobile-contact.png'),
+ fullPage: false,
+ });
+ }
// Check Back to chat button in Level 3
const backToChatBtn = mobileContactCard.locator('button[aria-label="Назад к диалогу"]');
@@ -115,18 +185,62 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
});
expect(hasHorizontalScroll).toBe(false);
} else if (isTablet) {
- // Tablet (768 vertical and 1024 landscape): 2 columns (Chat list on left, Chat + Horizontal Contact Card on right)
+ // 2. Tablet: 2 columns (Chat list + Active Chat). Contact card opens via slide-over Drawer.
const chatList = page.locator('section[aria-label="Список диалогов"]');
await expect(chatList).toBeVisible();
const activeChat = page.locator('section[aria-label="Активный диалог"]');
await expect(activeChat).toBeVisible();
- const horizontalContactCard = page.locator('aside[aria-label="Карточка контакта планшет"]');
- await expect(horizontalContactCard).toBeVisible();
- await expect(horizontalContactCard.locator('text=VIP')).toBeVisible();
+ // Verify Chat width >= 320px
+ const chatBox = await activeChat.boundingBox();
+ expect(chatBox).not.toBeNull();
+ if (chatBox) {
+ expect(chatBox.width).toBeGreaterThanOrEqual(320);
+ }
+
+ // Verify no squished under-chat horizontal card exists
+ const squishedCard = page.locator('aside[aria-label="Карточка контакта планшет"]');
+ await expect(squishedCard).toHaveCount(0);
+
+ // State Screenshot 4: Tablet Chat
+ if (projectName === 'tablet-768' || projectName === 'tablet-1024') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'tablet-chat.png'),
+ fullPage: false,
+ });
+ }
+
+ // Open slide-over Drawer via contact button in chat header
+ const openContactBtn = activeChat.locator('button[aria-label="Открыть контакт"]');
+ await expect(openContactBtn).toBeVisible();
+ await openContactBtn.click();
+
+ // Drawer is open
+ const drawer = page.locator('div[role="dialog"]');
+ await expect(drawer).toBeVisible();
+ await expect(drawer.locator('text=Карточка контакта')).toBeVisible();
+ await expect(drawer.locator('text=Иван Петров').first()).toBeVisible();
+ await expect(drawer.locator('text=VIP')).toBeVisible();
+
+ // State Screenshot 5: Tablet Contact Drawer
+ if (projectName === 'tablet-768' || projectName === 'tablet-1024') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'tablet-contact-drawer.png'),
+ fullPage: false,
+ });
+ }
+
+ // Close Drawer
+ const closeDrawerBtn = drawer.locator('button[aria-label="Закрыть"]').first();
+ if (await closeDrawerBtn.isVisible()) {
+ await closeDrawerBtn.click();
+ } else {
+ await page.keyboard.press('Escape');
+ }
+ await expect(drawer).toBeHidden();
} else if (isDesktop) {
- // Desktop: 3 separate elevated columns side-by-side
+ // 3. Desktop: 3 columns (Chat list + Active Chat + Aside Contact card)
const chatList = page.locator('section[aria-label="Список диалогов"]');
await expect(chatList).toBeVisible();
@@ -137,12 +251,48 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
await expect(verticalContactCard).toBeVisible();
await expect(verticalContactCard.locator('text=Иван Петров')).toBeVisible();
await expect(verticalContactCard.locator('text=VIP')).toBeVisible();
+
+ // Test tab inside Contact Card
+ await verticalContactCard.getByRole('button', { name: 'AI-сводка' }).click();
await expect(verticalContactCard.locator('text=✦ AI-сводка')).toBeVisible();
+
+ // Verify scrollY is 0
+ const scrollY = await page.evaluate(() => window.scrollY);
+ expect(scrollY).toBe(0);
+
+ // State Screenshot 6: Desktop without Krona AI
+ if (projectName === 'desktop-1440') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'desktop-without-krona-ai.png'),
+ fullPage: false,
+ });
+ }
+
+ // Open Krona AI drawer
+ const aiBtn = page.locator('button[title="Krona AI — Бизнес-оркестратор"]');
+ if (await aiBtn.isVisible()) {
+ await aiBtn.click();
+ const aiDrawer = page.locator('text=✦ Krona AI — Бизнес-оркестратор');
+ await expect(aiDrawer).toBeVisible();
+
+ // State Screenshot 7: Desktop with Krona AI
+ if (projectName === 'desktop-1440') {
+ await page.screenshot({
+ path: path.join(artifactsDir, 'desktop-with-krona-ai.png'),
+ fullPage: false,
+ });
+ }
+
+ // Close AI drawer
+ await page.keyboard.press('Escape');
+ await page.waitForTimeout(300);
+ }
}
});
- test('4. Interactive messaging, quick replies, and tab navigation', async ({ page, viewport }) => {
- const isMobile = viewport && viewport.width < 768;
+ test('4. Interactive messaging, quick replies, and tab navigation', async ({ page }) => {
+ const layoutMode = await page.locator('[data-layout-mode]').getAttribute('data-layout-mode');
+ const isMobile = layoutMode === 'compact';
if (isMobile) {
// Select chat first on mobile
@@ -233,8 +383,12 @@ test.describe('Scenario 13: Communications Visual & Responsive Design System V3
? 'graphite-forest-dark'
: file.startsWith('noble-green')
? 'noble-green'
- : 'parchment-light',
- viewport: file.replace(/^(graphite-forest-dark|noble-green|parchment-light)-/, '').replace('.png', ''),
+ : file.startsWith('parchment-light')
+ ? 'parchment-light'
+ : 'state-capture',
+ viewport: file
+ .replace(/^(graphite-forest-dark|noble-green|parchment-light)-/, '')
+ .replace('.png', ''),
capturedAt: fs.statSync(path.join(artifactsDir, file)).mtime.toISOString(),
})),
};
diff --git a/public/avatars/alexander.svg b/public/avatars/alexander.svg
new file mode 100644
index 0000000..212196d
--- /dev/null
+++ b/public/avatars/alexander.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/avatars/dmitry.svg b/public/avatars/dmitry.svg
new file mode 100644
index 0000000..4e31058
--- /dev/null
+++ b/public/avatars/dmitry.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/avatars/elena.svg b/public/avatars/elena.svg
new file mode 100644
index 0000000..cad67d9
--- /dev/null
+++ b/public/avatars/elena.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/avatars/ivan.svg b/public/avatars/ivan.svg
new file mode 100644
index 0000000..c82d3be
--- /dev/null
+++ b/public/avatars/ivan.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/avatars/konstantin.svg b/public/avatars/konstantin.svg
new file mode 100644
index 0000000..bda4e0c
--- /dev/null
+++ b/public/avatars/konstantin.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/avatars/maria.svg b/public/avatars/maria.svg
new file mode 100644
index 0000000..4a4ce5e
--- /dev/null
+++ b/public/avatars/maria.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/avatars/marketing.svg b/public/avatars/marketing.svg
new file mode 100644
index 0000000..dc69216
--- /dev/null
+++ b/public/avatars/marketing.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/avatars/olga.svg b/public/avatars/olga.svg
new file mode 100644
index 0000000..31783b8
--- /dev/null
+++ b/public/avatars/olga.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/avatars/operator.svg b/public/avatars/operator.svg
new file mode 100644
index 0000000..d1529d5
--- /dev/null
+++ b/public/avatars/operator.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/avatars/resurs.svg b/public/avatars/resurs.svg
new file mode 100644
index 0000000..6cb87be
--- /dev/null
+++ b/public/avatars/resurs.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/avatars/support.svg b/public/avatars/support.svg
new file mode 100644
index 0000000..c075c42
--- /dev/null
+++ b/public/avatars/support.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/app/layouts/AppShell.tsx b/src/app/layouts/AppShell.tsx
index 271129b..571c91a 100644
--- a/src/app/layouts/AppShell.tsx
+++ b/src/app/layouts/AppShell.tsx
@@ -28,6 +28,12 @@ export const AppShell: React.FC = () => {
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
+ const kronaTheme = {
+ light: 'parchment-light',
+ 'soft-light': 'noble-green',
+ dark: 'graphite-forest-dark',
+ }[theme];
+ document.documentElement.setAttribute('data-krona-theme', kronaTheme);
}, [theme]);
// Layout resolution:
@@ -41,7 +47,7 @@ export const AppShell: React.FC = () => {
const getViewportContainerStyles = () => {
if (!isEmulatedOnDesktop) {
- return 'w-full min-h-screen';
+ return isCommunicationsPage ? 'w-full h-screen max-h-screen overflow-hidden' : 'w-full min-h-screen';
}
switch (viewportMode) {
case 'mobile':
@@ -51,14 +57,16 @@ export const AppShell: React.FC = () => {
case 'tablet-landscape':
return 'max-w-[1180px] h-[800px] my-6 border-8 border-krona-border rounded-[24px] overflow-hidden shadow-2xl relative';
default:
- return 'w-full min-h-screen';
+ return isCommunicationsPage ? 'w-full h-screen max-h-screen overflow-hidden' : 'w-full min-h-screen';
}
};
return (
@@ -76,9 +84,9 @@ export const AppShell: React.FC = () => {
{/* Main Content Router Outlet */}
diff --git a/src/features/communications/components/CommunicationsHeader.tsx b/src/features/communications/components/CommunicationsHeader.tsx
index 623ab94..bb7f3b8 100644
--- a/src/features/communications/components/CommunicationsHeader.tsx
+++ b/src/features/communications/components/CommunicationsHeader.tsx
@@ -52,7 +52,7 @@ export function CommunicationsHeader({
];
return (
-