From 1cff9e2183e8e57685232d16092ab86b88aa65ea Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 13 Sep 2026 07:22:35 +0300 Subject: [PATCH] =?UTF-8?q?:sparkles:=20feat(onboarding):=20=D0=B2=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=80=D0=B4=20=C2=AB=D0=9D=D0=B0=D1=87=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=C2=BB=20=D0=B2?= =?UTF-8?q?=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=20=C2=AB=D0=97=D0=B0=D0=BF=D1=83=D1=81=D0=BA=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Онбординг из восьми шагов: приветствие, визард с точным путём и превью результата на каждом шаге, тур «Показать где» с подсветкой реального элемента и финальный экран. Точки возврата — ссылка внизу субменю «Настроек» и пилюля в углу рабочей области. Вёрстка по макету design/baseline/Онбординг. Признак «закрыл» и «прошёл» живёт на членстве человека в организации, а не в localStorage: требование — показать визард всем, кто его ещё не закрывал, включая тех, кто работает в установке давно. У каждого он свой, поэтому один администратор не прячет визард команде. Прогресс шагов считается по факту настройки, ручное «Далее» его не подменяет. Заодно все селекты приложения переведены на общее меню: SelectMenu в shared/ui-controls, поверх него FilterDropdown (фильтры списков) и SelectField (поля форм). Нативных setCategoryId(event.target.value)}> - {catalog.categories.map((item) => ( - - ))} - - - - + [String(item.id), knowledgeCategoryPath(catalog.categories, item.id) || item.name])} + /> - - - - - -
-
-

Поиск и переключатели

Компактный поиск, switch и segmented control.

- ui-controls.tsx · table-controls.css -
-
-
-
SearchInputSearchInput
-
- -
-
-
-
SwitchButton.ui-switch
-
- - - -
-
-
-
SegmentedSegmented
-
-
-
-
-
-
- -
-
-

Вкладки и пагинация

Навигация внутри раздела и управление страницами таблицы.

- ui-controls.tsx · ui-controls.css -
-
-
-
UnderlineTabsUnderlineTabs
-
-
- - - - -
-
-
-
-
TablePaginationTablePagination
-
-
- Показано 1–20 из 74 - -
-
-
-
-
- - - - - - - diff --git a/design/design-system/data-display.html b/design/design-system/data-display.html deleted file mode 100644 index 6460a12..0000000 --- a/design/design-system/data-display.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - Данные и статусы · Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Данные и статусы
- Data display -
-
-
-
-
Data display
-

Представление данных

-

Компоненты для статусов, ролей, продуктов, каналов, идентичности и кратких показателей.

-
-
- -
-
-

Статусы

Полный набор ключей StatusPillKey.

- src/shared/ui.tsx -
-
-
StatusPill12 состояний
-
- Работает - Активен - Опубликован - Заблокирован - Неактивен - Приглашён - Архивный - Черновик - Ошибка - Проверяется - Не проверялось -
-
-
- -
-
-

Роли

Роли сотрудников организации.

- src/shared/ui.tsx -
-
-
RoleBadgeOWNER · ADMIN · EMPLOYEE
-
- Владелец - Администратор - Сотрудник -
-
-
- -
-
-

Продукты

Плотный tag для таблиц и нейтральный mark для текста.

- ui.tsx · utils.ts -
-
-
-
ProductTagproductAccent()
-
- FoxRay - Chatballs - FirePage -
-
-
-
ProductMarkProductMark
-
- FoxRay - Chatballs -
-
-
-
- -
-
-

Каналы

Короткие метки провайдеров из общего словаря.

- badges.tsx · providers.ts -
-
-
ChannelBadgeEMAIL · MAX · TG · WEB
-
- Email - MAX - TG - Web - Unknown -
-
-
- -
-
-

Идентичность и показатели

Avatar, KeyValue и MetricBox.

- ui.tsx · form-controls.tsx -
-
-
-
Avatarinitials()
-
- АЕ - ИК - EH -
-
-
-
KeyValueKeyValue
-
-
ОрганизацияООО «ЭДЕВС»
-
ВладелецАндрей Ермолаев
-
-
-
-
MetricBoxMetricBox
-
-
Активные агенты6
-
-
-
-
- -
-
-

Иконки

Набор Icon из shared. Здесь показаны наиболее частые роли.

- src/shared/icons.tsx -
-
-
-

grid

-

box

-

list

-

columns

-

search

-

more

-

edit

-

trash

-

warning

-

bell

-

user

-

message

-
-
-
-
-
-
- - - - diff --git a/design/design-system/foundations.html b/design/design-system/foundations.html deleted file mode 100644 index df3c97b..0000000 --- a/design/design-system/foundations.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - Токены · Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Токены
- Foundations -
-
-
-
-
Foundations
-

Токены интерфейса

-

Значения собраны из текущих общих CSS-файлов и темы UI. Названия отражают фактическое назначение значений во фронтенде.

-
-
- -
-
-

Нейтральная палитра

Текст, границы, фоны и disabled-состояния.

- src/styles/base.css -
-
-
neutral.1--n-1 · #1f1f1f
-
neutral.2--n-2 · #262626
-
neutral.3--n-3 · #595959
-
neutral.4--n-4 · #8c8c8c
-
neutral.5--n-5 · #bfbfbf
-
neutral.6--n-6 · #d9d9d9
-
neutral.7--n-7 · #e8e8e8
-
neutral.8--n-8 · #f0f0f0
-
neutral.9--n-9 · #f5f5f5
-
neutral.10--n-10 · #fafafa
-
-
- -
-
-

Основные и семантические цвета

Интерактивные элементы, статусы и AI-акцент.

- base.css · theme.ts -
-
-
color.primary--primary · #1677ff
-
color.primary.hover--primary-hover · #0958d9
-
color.primary.bg--primary-bg · #e6f4ff
-
color.primary.border--primary-border · #91caff
-
color.success--success · #52c41a
-
color.success.bg--success-bg · #f6ffed
-
color.warning.text--warning-text · #d48806
-
color.warning.bg--warning-bg · #fffbe6
-
color.error.text--error-text · #cf1322
-
color.error.bg--error-bg · #fff2f0
-
color.ai--ai · #722ed1
-
color.ai.bg--ai-bg · #f9f0ff
-
-
- -
-
-

Каналы

Акцентные пары из общего словаря провайдеров.

- src/shared/providers.ts -
-
-
provider.email.bg#fff7e6 · text #d48806
-
provider.max.bg#f2f0ff · text #6b5be0
-
provider.telegram.bg#eaf6fd · text #2f8fd0
-
provider.web.bg#e8f7f4 · text #0f9b8e
-
-
- -
-
-

Типографика

Системный sans-serif и моноширинный стек для идентификаторов.

- base.css · page.css -
-
-
-
Основной шрифт--font-sans
-
-
Заголовок страницы · 24 / 700
-
Заголовок компонента · 14 / 600
-
Основной текст интерфейса · 13.5 / 400
-
Подпись поля · 12 / 500
-
-
-
-
Моноширинный--font-mono
-
-
org_01J9R7Q6ZWPB
-
connection_telegram_primary
-
2026-07-30T14:13:00Z
-
-
-
-
- -
-
-

Радиусы

Корневые токены и используемые малые значения controls.

- base.css · shared/*.css -
-
-
radius.4
4px · focus
-
radius.6
6px · badges
-
radius.md
8px · controls
-
radius.lg
9px · buttons
-
radius.xl
10px · cards
-
radius.2xl
12px · panels
-
radius.4xl
14px · dialogs
-
radius.pill
999px · tags
-
-
- -
-
-

Интервалы и размеры controls

Повторяющиеся значения общих компонентов.

- shared/*.css · layout/*.css -
-
-
-

Spacing scale

-
space.1
4px
-
space.2
6px
-
space.3
8px
-
space.4
12px
-
space.5
14px
-
space.6
16px
-
space.7
20px
-
space.8
24px
-
space.9
28px
-
-
-

Control heights

-
control.xs
24px
-
control.sm
30px
-
control.compact
34px
-
control.action
36px
-
control.md
38px
-
switch.md
42×24
-
switch.lg
46×26
-
shell.bar
56px
-
-
-
- -
-
-

Тени

Общие elevation-уровни и характерные component shadows.

- base.css · shared/*.css -
-
-
shadow.xs
cards
-
shadow.sm
panels
-
shadow.button
primary
-
shadow.lg
dialogs
-
-
-
-
-
- - - - diff --git a/design/design-system/index.html b/design/design-system/index.html deleted file mode 100644 index 4081770..0000000 --- a/design/design-system/index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Обзор
- Текущий frontend -
-
-
-
-
Chatballs UI inventory
-

Дизайн-система текущего фронтенда

-

Статический справочник существующих токенов, компонентов, паттернов и компоновок Chatballs. Примеры повторяют действующие CSS-классы и визуальные решения из apps/internal-ui/src.

-
-
- -
- Назначение: перед разработкой нового интерфейса открыть нужный раздел, найти существующий элемент и использовать его как эталон. Каталог ничего не меняет в приложении и не требует React. -
- -
-
-

Разделы

От базовых значений до компоновки целых экранов.

-
- -
- -
-
-

Уровни системы

Как читать и использовать каталог.

-
-
-

1. Foundations

Фактические значения, на которых построен интерфейс: цвет, размер, радиус, тень и типографика.

-

2. Components

Переиспользуемые элементы из src/shared и их состояния.

-

3. Patterns

Устоявшиеся композиции: таблица с фильтрами, карточка, форма, диалог.

-

4. Layouts

Каркас целой страницы или рабочего пространства из существующих частей.

-
-
- -
-
-

Основные источники

Файлы фронтенда, по которым собран этот справочник.

-
-
-
-
Токеныsrc/styles/base.css · packages/ui/src/theme.ts
-
Controlssrc/shared/ui-controls.* · form-controls.*
-
Data displaysrc/shared/ui.tsx · badges.tsx · design-components.css
-
Таблицыsrc/shared/table-controls.css
-
Layoutsrc/layout/styles.css · topbar.css · page.css
-
Иконкиsrc/shared/icons.tsx
-
-
-
-
-
-
- - - - diff --git a/design/design-system/inventory.html b/design/design-system/inventory.html deleted file mode 100644 index d8022fb..0000000 --- a/design/design-system/inventory.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - Инвентарь · Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Инвентарь
- Inventory -
-
-
-
-
Inventory
-

Инвентарь фронтенда

-

Реестр включает PascalCase-компоненты из TSX-файлов, системные CSS-компоненты, дизайн-токены и основные layout-паттерны. Всего записей: —.

-
-
- -
- Область: в таблице отдельно помечены системные, layout-, application- и feature-компоненты. Это инвентаризация существующего состава без оценки и без проверки соблюдения дизайн-системы. -
- -
- -
-
- - - - - -
НазваниеТипКатегорияОбластьИсходникВарианты / роль
-
-
-
-
-
-
- - - - - diff --git a/design/design-system/inventory.json b/design/design-system/inventory.json deleted file mode 100644 index 026f43f..0000000 --- a/design/design-system/inventory.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"0.1.0","generatedFrom":"apps/internal-ui/src","generatedOn":"2026-07-30","count":315,"items":[{"name":"Link","kind":"CSS component","category":"Actions","scope":"System","visibility":"system","source":"src/shared/links.css","variants":"base, strong, neutral, muted, mono, icon","documentedAt":"components.html"},{"name":"AppDropdown","kind":"CSS pattern","category":"Overlays","scope":"System","visibility":"system","source":"src/shared/menu.css","variants":"default, wide, channels, warning, danger, success","documentedAt":"patterns.html"},{"name":"BaselineTable","kind":"CSS pattern","category":"Tables","scope":"System","visibility":"system","source":"src/shared/table-controls.css","variants":"header, row hover, numeric, actions","documentedAt":"patterns.html"},{"name":"FilterBar","kind":"CSS pattern","category":"Tables","scope":"System","visibility":"system","source":"src/shared/table-controls.css","variants":"default, employees","documentedAt":"patterns.html"},{"name":"TableCard","kind":"CSS pattern","category":"Tables","scope":"System","visibility":"system","source":"src/shared/table-controls.css","variants":"default, scroll, footer","documentedAt":"patterns.html"},{"name":"ApplicationShell","kind":"Layout pattern","category":"Layouts","scope":"System","visibility":"system","source":"src/layout/*.css","variants":"sidebar, topbar, scroll, page","documentedAt":"layouts.html"},{"name":"ConversationWorkspacePattern","kind":"Page pattern","category":"Layouts","scope":"System","visibility":"system","source":"features/conversations","variants":"dialog list, thread, context","documentedAt":"layouts.html"},{"name":"DetailPage","kind":"Page pattern","category":"Layouts","scope":"System","visibility":"system","source":"feature detail pages","variants":"header, main content, rail","documentedAt":"layouts.html"},{"name":"ListPage","kind":"Page pattern","category":"Layouts","scope":"System","visibility":"system","source":"feature list pages","variants":"header, filters, table, pagination","documentedAt":"layouts.html"},{"name":"SettingsPagePattern","kind":"Page pattern","category":"Layouts","scope":"System","visibility":"system","source":"settings/profile/administration","variants":"section cards, forms, local actions","documentedAt":"layouts.html"},{"name":"App","kind":"React component","category":"Application","scope":"Application","visibility":"exported","source":"apps/internal-ui/src/App.tsx","variants":"—","documentedAt":""},{"name":"SurfaceApp","kind":"React component","category":"Application","scope":"Application","visibility":"local","source":"apps/internal-ui/src/main.tsx","variants":"—","documentedAt":""},{"name":"AdministrationPage","kind":"React component","category":"Feature: administration","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/administration/AdministrationPage.tsx","variants":"—","documentedAt":""},{"name":"AuditTable","kind":"React component","category":"Feature: administration","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/administration/AuditTable.tsx","variants":"—","documentedAt":""},{"name":"CloudSubscriptionPanel","kind":"React component","category":"Feature: administration","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/administration/CloudSubscriptionPanel.tsx","variants":"—","documentedAt":""},{"name":"OrganizationLogoField","kind":"React component","category":"Feature: administration","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/administration/OrganizationLogoField.tsx","variants":"—","documentedAt":""},{"name":"OrganizationSettingsForm","kind":"React component","category":"Feature: administration","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/administration/OrganizationSettingsForm.tsx","variants":"—","documentedAt":""},{"name":"AgentEditForm","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AgentEditForm.tsx","variants":"—","documentedAt":""},{"name":"AgentsKpiStrip","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/AgentsKpiStrip.tsx","variants":"—","documentedAt":""},{"name":"AgentsTable","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/AgentsTable.tsx","variants":"—","documentedAt":""},{"name":"AiAgentCreatePage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/AiAgentCreatePage.tsx","variants":"—","documentedAt":""},{"name":"AiAgentDetailHeader","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentDetailHeader.tsx","variants":"—","documentedAt":""},{"name":"AiAgentDetailPage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentDetailPage.tsx","variants":"—","documentedAt":""},{"name":"AiAgentInstructionsTab","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentInstructionsTab.tsx","variants":"—","documentedAt":""},{"name":"AiAgentKnowledgeTab","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentKnowledgeTab.tsx","variants":"—","documentedAt":""},{"name":"AiAgentMetricsTab","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentMetricsTab.tsx","variants":"—","documentedAt":""},{"name":"AiAgentOverviewTab","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/detail/AiAgentOverviewTab.tsx","variants":"—","documentedAt":""},{"name":"AiAgentsPage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/AiAgentsPage.tsx","variants":"—","documentedAt":""},{"name":"CategoryManagement","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/CategoryManagement.tsx","variants":"—","documentedAt":""},{"name":"CreateAgentFooter","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/CreateAgentFooter.tsx","variants":"—","documentedAt":""},{"name":"CreateAgentStepCard","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/CreateAgentStepCard.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeAccessNotice","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgePage.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeAttachmentsCard","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeAttachmentsCard.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeBulkActions","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeBulkActions.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeBulkDialog","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeBulkDialog.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeCategoryTree","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeCategoryTree.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeCreatePage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeCreatePage.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeDepartmentPicker","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeDepartmentPicker.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeDetailPage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeDetailPage.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeDetailSidebar","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeDetailSidebar.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeEditorBreadcrumb","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeEditorBreadcrumb.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeEditorCard","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeEditorCard.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeEditorFields","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeEditorFields.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeImportModal","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeImportModal.tsx","variants":"—","documentedAt":""},{"name":"KnowledgePage","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgePage.tsx","variants":"—","documentedAt":""},{"name":"KnowledgePageHeader","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgePageHeader.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeScopeConflictModal","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeScopeConflictModal.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeStep","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/KnowledgeStep.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeTable","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeTable.tsx","variants":"—","documentedAt":""},{"name":"KnowledgeToolbar","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/knowledge/KnowledgeToolbar.tsx","variants":"—","documentedAt":""},{"name":"ModelStep","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/ModelStep.tsx","variants":"—","documentedAt":""},{"name":"ProductChoiceStep","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/ProductChoiceStep.tsx","variants":"—","documentedAt":""},{"name":"PromptStep","kind":"React component","category":"Feature: ai","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/ai/create/PromptStep.tsx","variants":"—","documentedAt":""},{"name":"AuthChangePassword","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthChangePassword.tsx","variants":"—","documentedAt":""},{"name":"AuthCodeInput","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthCodeInput.tsx","variants":"—","documentedAt":""},{"name":"AuthField","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthField.tsx","variants":"—","documentedAt":""},{"name":"AuthFrame","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthFrame.tsx","variants":"—","documentedAt":""},{"name":"AuthLogin","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthLogin.tsx","variants":"—","documentedAt":""},{"name":"AuthPasswordRecovery","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthPasswordRecovery.tsx","variants":"—","documentedAt":""},{"name":"AuthResetPassword","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthResetPassword.tsx","variants":"—","documentedAt":""},{"name":"AuthTotpCode","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthTotpCode.tsx","variants":"—","documentedAt":""},{"name":"AuthTotpSetup","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/AuthTotpSetup.tsx","variants":"—","documentedAt":""},{"name":"TotpQr","kind":"React component","category":"Feature: auth","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/auth/TotpQr.tsx","variants":"—","documentedAt":""},{"name":"AgentCell","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/channels/ChannelRow.tsx","variants":"—","documentedAt":""},{"name":"ChannelAgentSection","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelAgentSection.tsx","variants":"—","documentedAt":""},{"name":"ChannelArchivedNotice","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelArchivedNotice.tsx","variants":"—","documentedAt":""},{"name":"ChannelAssignmentSection","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelAssignmentSection.tsx","variants":"—","documentedAt":""},{"name":"ChannelConnectionsSection","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelConnectionsSection.tsx","variants":"—","documentedAt":""},{"name":"ChannelConnectionsTable","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelConnectionsTable.tsx","variants":"—","documentedAt":""},{"name":"ChannelCountersSection","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelCountersSection.tsx","variants":"—","documentedAt":""},{"name":"ChannelCreateWizard","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelCreateWizard.tsx","variants":"—","documentedAt":""},{"name":"ChannelDangerActions","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelDangerActions.tsx","variants":"—","documentedAt":""},{"name":"ChannelDeactivationDialog","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelDeactivationDialog.tsx","variants":"—","documentedAt":""},{"name":"ChannelDeleteDialog","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelDeleteDialog.tsx","variants":"—","documentedAt":""},{"name":"ChannelDetailHeader","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelDetailHeader.tsx","variants":"—","documentedAt":""},{"name":"ChannelDetailPage","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelDetailPage.tsx","variants":"—","documentedAt":""},{"name":"ChannelPolicySection","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelPolicySection.tsx","variants":"—","documentedAt":""},{"name":"ChannelRow","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelRow.tsx","variants":"—","documentedAt":""},{"name":"ChannelWizardAssignmentStep","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelWizardAssignmentStep.tsx","variants":"—","documentedAt":""},{"name":"ChannelWizardChannelStep","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelWizardChannelStep.tsx","variants":"—","documentedAt":""},{"name":"ChannelWizardSummary","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelWizardSummary.tsx","variants":"—","documentedAt":""},{"name":"ChannelsPage","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelsPage.tsx","variants":"—","documentedAt":""},{"name":"ChannelsTable","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/channels/ChannelsTable.tsx","variants":"—","documentedAt":""},{"name":"DestinationCard","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/channels/ChannelWizardSummary.tsx","variants":"—","documentedAt":""},{"name":"PolicyRows","kind":"React component","category":"Feature: channels","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/channels/ChannelPolicySection.tsx","variants":"—","documentedAt":""},{"name":"CommandCenter","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/CommandCenterPage.tsx","variants":"—","documentedAt":""},{"name":"CommandHeader","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/CommandHeader.tsx","variants":"—","documentedAt":""},{"name":"CommandRail","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/CommandRail.tsx","variants":"—","documentedAt":""},{"name":"CompanyStatusBanner","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/CompanyStatusBanner.tsx","variants":"—","documentedAt":""},{"name":"DepartmentCard","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/command/center/SalesDepartmentCard.tsx","variants":"—","documentedAt":""},{"name":"DepartmentsColumn","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/SalesDepartmentCard.tsx","variants":"—","documentedAt":""},{"name":"MetricGroup","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/MetricGroup.tsx","variants":"—","documentedAt":""},{"name":"RailCard","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/RailCard.tsx","variants":"—","documentedAt":""},{"name":"SmallAiMetric","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/command/center/CommandRail.tsx","variants":"—","documentedAt":""},{"name":"SmallMetric","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/command/center/CompanyStatusBanner.tsx","variants":"—","documentedAt":""},{"name":"StatusLabel","kind":"React component","category":"Feature: command","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/command/center/StatusLabel.tsx","variants":"—","documentedAt":""},{"name":"CallOverlay","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/CallOverlay.tsx","variants":"—","documentedAt":""},{"name":"Composer","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/Composer.tsx","variants":"—","documentedAt":""},{"name":"ContactAvatar","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/ContactAvatar.tsx","variants":"—","documentedAt":""},{"name":"ContextSection","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/ContextSection.tsx","variants":"—","documentedAt":""},{"name":"ConversationActions","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/ConversationActions.tsx","variants":"—","documentedAt":""},{"name":"ConversationThread","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/ConversationThread.tsx","variants":"—","documentedAt":""},{"name":"ConversationWorkspace","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/ConversationWorkspace.tsx","variants":"—","documentedAt":"layouts.html"},{"name":"DialogList","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/DialogList.tsx","variants":"—","documentedAt":""},{"name":"DialogListItem","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/conversations/DialogList.tsx","variants":"—","documentedAt":""},{"name":"DialogTab","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/conversations/DialogList.tsx","variants":"—","documentedAt":""},{"name":"EmailMessageBody","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/EmailMessageBody.tsx","variants":"—","documentedAt":""},{"name":"FieldRow","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/conversations/FieldRow.tsx","variants":"—","documentedAt":""},{"name":"Message","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/conversations/ConversationThread.tsx","variants":"—","documentedAt":""},{"name":"MessageRow","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/conversations/ConversationThread.tsx","variants":"—","documentedAt":""},{"name":"StatusBadge","kind":"React component","category":"Feature: conversations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/conversations/ConversationThread.tsx","variants":"—","documentedAt":""},{"name":"DepartmentCard","kind":"React component","category":"Feature: departments","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/departments/DepartmentCard.tsx","variants":"—","documentedAt":""},{"name":"DepartmentsPage","kind":"React component","category":"Feature: departments","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/departments/DepartmentsPage.tsx","variants":"—","documentedAt":""},{"name":"AccessProfileEditor","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/AccessProfileEditor.tsx","variants":"—","documentedAt":""},{"name":"AccessProfileList","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/AccessProfileList.tsx","variants":"—","documentedAt":""},{"name":"AccessProfilesPage","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/AccessProfilesPage.tsx","variants":"—","documentedAt":""},{"name":"CreateField","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/employees/EmployeeCreateDrawer.tsx","variants":"—","documentedAt":""},{"name":"CreateStep","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/employees/EmployeeCreateDrawer.tsx","variants":"—","documentedAt":""},{"name":"EmployeeAccessSection","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeAccessSection.tsx","variants":"—","documentedAt":""},{"name":"EmployeeCreateDrawer","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeCreateDrawer.tsx","variants":"—","documentedAt":""},{"name":"EmployeeDetailHeader","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeDetailHeader.tsx","variants":"—","documentedAt":""},{"name":"EmployeeDetailPage","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeDetailPage.tsx","variants":"—","documentedAt":""},{"name":"EmployeeDetailRail","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeDetailRail.tsx","variants":"—","documentedAt":""},{"name":"EmployeeDetailSections","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeDetailSections.tsx","variants":"—","documentedAt":""},{"name":"EmployeeIdentitySections","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeIdentitySections.tsx","variants":"—","documentedAt":""},{"name":"EmployeeRow","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/employees/EmployeeTable.tsx","variants":"—","documentedAt":""},{"name":"EmployeeSecuritySections","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeSecuritySections.tsx","variants":"—","documentedAt":""},{"name":"EmployeeTable","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeeTable.tsx","variants":"—","documentedAt":""},{"name":"EmployeesFilters","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeesFilters.tsx","variants":"—","documentedAt":""},{"name":"EmployeesPage","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/EmployeesListPage.tsx","variants":"—","documentedAt":""},{"name":"OwnershipTransferModal","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/employees/OwnershipTransferModal.tsx","variants":"—","documentedAt":""},{"name":"RoleOption","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/employees/EmployeeCreateDrawer.tsx","variants":"—","documentedAt":""},{"name":"RoleOption","kind":"React component","category":"Feature: employees","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/employees/OwnershipTransferModal.tsx","variants":"—","documentedAt":""},{"name":"Heading","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/help-center/MarkdownContent.tsx","variants":"—","documentedAt":""},{"name":"HelpArrowIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"HelpArticle","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpArticle.tsx","variants":"—","documentedAt":""},{"name":"HelpCenterApp","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpCenterApp.tsx","variants":"—","documentedAt":""},{"name":"HelpChevronIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"HelpFolderIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"HelpHome","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpHome.tsx","variants":"—","documentedAt":""},{"name":"HelpLayout","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpLayout.tsx","variants":"—","documentedAt":""},{"name":"HelpMessageIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"HelpSearch","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpSearch.tsx","variants":"—","documentedAt":""},{"name":"HelpSearchIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"HelpThumbIcon","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/HelpIcons.tsx","variants":"—","documentedAt":""},{"name":"MarkdownContent","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/MarkdownContent.tsx","variants":"—","documentedAt":""},{"name":"PortalWebWidget","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/PortalWebWidget.tsx","variants":"—","documentedAt":""},{"name":"SupportLauncher","kind":"React component","category":"Feature: help-center","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/help-center/SupportLauncher.tsx","variants":"—","documentedAt":""},{"name":"ConnectionIcon","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/rows.tsx","variants":"—","documentedAt":""},{"name":"ConnectionsTable","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/ConnectionsTable.tsx","variants":"—","documentedAt":""},{"name":"EmailFields","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/EmailFields.tsx","variants":"—","documentedAt":""},{"name":"IntegrationForm","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/IntegrationForm.tsx","variants":"—","documentedAt":""},{"name":"IntegrationsPage","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/IntegrationsPage.tsx","variants":"—","documentedAt":""},{"name":"MailboxSection","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/integrations/EmailFields.tsx","variants":"—","documentedAt":""},{"name":"ProvidersTable","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/ProvidersTable.tsx","variants":"—","documentedAt":""},{"name":"RowActions","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/rows.tsx","variants":"—","documentedAt":""},{"name":"StatusCell","kind":"React component","category":"Feature: integrations","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/integrations/rows.tsx","variants":"—","documentedAt":""},{"name":"NotificationDrawer","kind":"React component","category":"Feature: notifications","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/notifications/NotificationDrawer.tsx","variants":"—","documentedAt":""},{"name":"Section","kind":"React component","category":"Feature: notifications","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/notifications/NotificationDrawer.tsx","variants":"—","documentedAt":""},{"name":"OfferFormModal","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/OfferFormModal.tsx","variants":"—","documentedAt":""},{"name":"PriceFormModal","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/PriceFormModal.tsx","variants":"—","documentedAt":""},{"name":"ProductAccessTab","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductAccessTab.tsx","variants":"—","documentedAt":""},{"name":"ProductAgentCard","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductAgentCard.tsx","variants":"—","documentedAt":""},{"name":"ProductChannelsTab","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductChannelsTab.tsx","variants":"—","documentedAt":""},{"name":"ProductDetailHeader","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductDetailHeader.tsx","variants":"—","documentedAt":""},{"name":"ProductDetailPage","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductDetailPage.tsx","variants":"—","documentedAt":""},{"name":"ProductEmptyTab","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductEmptyTab.tsx","variants":"—","documentedAt":""},{"name":"ProductFormModal","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/ProductFormModal.tsx","variants":"—","documentedAt":""},{"name":"ProductOffersTab","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductOffersTab.tsx","variants":"—","documentedAt":""},{"name":"ProductOverviewTab","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/detail/ProductOverviewTab.tsx","variants":"—","documentedAt":""},{"name":"ProductRow","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/ProductRow.tsx","variants":"—","documentedAt":""},{"name":"ProductsHeaderActions","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/ProductsHeaderActions.tsx","variants":"—","documentedAt":""},{"name":"ProductsPage","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/ProductsPage.tsx","variants":"—","documentedAt":""},{"name":"ProductsTable","kind":"React component","category":"Feature: products","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/products/ProductsTable.tsx","variants":"—","documentedAt":""},{"name":"ProfileField","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileField.tsx","variants":"—","documentedAt":""},{"name":"ProfileHeader","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileHeader.tsx","variants":"—","documentedAt":""},{"name":"ProfileInfoForm","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileInfoForm.tsx","variants":"—","documentedAt":""},{"name":"ProfileNotificationsCard","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileNotificationsCard.tsx","variants":"—","documentedAt":""},{"name":"ProfilePage","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfilePage.tsx","variants":"—","documentedAt":""},{"name":"ProfilePasswordForm","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfilePasswordForm.tsx","variants":"—","documentedAt":""},{"name":"ProfileSessionsCard","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileSessionsCard.tsx","variants":"—","documentedAt":""},{"name":"ProfileTotpCard","kind":"React component","category":"Feature: profile","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/profile/ProfileTotpCard.tsx","variants":"—","documentedAt":""},{"name":"ActivityList","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientOverviewTab.tsx","variants":"—","documentedAt":""},{"name":"AddSaleDialog","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/registry/AddSaleDialog.tsx","variants":"—","documentedAt":""},{"name":"ClientContext","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/dialogs/context/ClientContext.tsx","variants":"—","documentedAt":""},{"name":"FilterSelect","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/registry/SalesRegistryFilters.tsx","variants":"—","documentedAt":""},{"name":"FulfillmentTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/tables/FulfillmentTable.tsx","variants":"—","documentedAt":""},{"name":"HistoryContext","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/dialogs/context/HistoryContext.tsx","variants":"—","documentedAt":""},{"name":"OrdersTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/tables/OrdersTable.tsx","variants":"—","documentedAt":""},{"name":"PaymentsTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/tables/PaymentsTable.tsx","variants":"—","documentedAt":""},{"name":"ProductContext","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/dialogs/context/ProductContext.tsx","variants":"—","documentedAt":""},{"name":"RightTabButton","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/dialogs/context/SalesContextTabs.tsx","variants":"—","documentedAt":""},{"name":"SaleActionPanel","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/sale-detail/SaleActionPanel.tsx","variants":"—","documentedAt":""},{"name":"SaleDetailPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/sale-detail/SaleDetailPage.tsx","variants":"—","documentedAt":""},{"name":"SaleEventTimeline","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/sale-detail/SaleEventTimeline.tsx","variants":"—","documentedAt":""},{"name":"SalesActor","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/overview/SalesActorsCard.tsx","variants":"—","documentedAt":""},{"name":"SalesActorsCard","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesActorsCard.tsx","variants":"—","documentedAt":""},{"name":"SalesChannelsCard","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesChannelsCard.tsx","variants":"—","documentedAt":""},{"name":"SalesChart","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesChart.tsx","variants":"—","documentedAt":""},{"name":"SalesClientAuditTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientAuditTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientConsentTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientConsentTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientDetailPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientDetailPage.tsx","variants":"—","documentedAt":""},{"name":"SalesClientDialogsTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientDialogsTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientHeader","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientHeader.tsx","variants":"—","documentedAt":""},{"name":"SalesClientIdentitiesTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientIdentitiesTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientOverviewTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientOverviewTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientRow","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/clients/SalesClientRow.tsx","variants":"—","documentedAt":""},{"name":"SalesClientSalesTab","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientSalesTab.tsx","variants":"—","documentedAt":""},{"name":"SalesClientTabs","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientTabs.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsEmpty","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/clients/SalesClientsTable.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsFilters","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/clients/SalesClientsFilters.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsHeader","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/clients/SalesClientsHeader.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/SalesClientsPage.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsPagination","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/clients/SalesClientsPagination.tsx","variants":"—","documentedAt":""},{"name":"SalesClientsTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/clients/SalesClientsTable.tsx","variants":"—","documentedAt":""},{"name":"SalesContextPanel","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/dialogs/SalesContextPanel.tsx","variants":"—","documentedAt":""},{"name":"SalesContextTabs","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/dialogs/context/SalesContextTabs.tsx","variants":"—","documentedAt":""},{"name":"SalesDialogsPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/SalesDialogsPage.tsx","variants":"—","documentedAt":""},{"name":"SalesFilterDropdown","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/clients/SalesClientsFilters.tsx","variants":"—","documentedAt":""},{"name":"SalesKpiCard","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/overview/SalesKpiSection.tsx","variants":"—","documentedAt":""},{"name":"SalesKpiSection","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesKpiSection.tsx","variants":"—","documentedAt":""},{"name":"SalesListCard","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesListCard.tsx","variants":"—","documentedAt":""},{"name":"SalesOrderDetailPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/order-detail/SalesOrderDetailPage.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersEmpty","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersTableCard.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersHeader","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersHeader.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersPage.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersPagination","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersPagination.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersTableCard","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersTableCard.tsx","variants":"—","documentedAt":""},{"name":"SalesOrdersTabs","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/SalesOrdersTabs.tsx","variants":"—","documentedAt":""},{"name":"SalesOverviewGrid","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesOverviewGrid.tsx","variants":"—","documentedAt":""},{"name":"SalesOverviewHeader","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesOverviewHeader.tsx","variants":"—","documentedAt":""},{"name":"SalesOverviewPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/SalesOverviewPage.tsx","variants":"—","documentedAt":""},{"name":"SalesProductsTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/overview/SalesProductsTable.tsx","variants":"—","documentedAt":""},{"name":"SalesRegistryFilters","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/registry/SalesRegistryFilters.tsx","variants":"—","documentedAt":""},{"name":"SalesRegistryPage","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/registry/SalesRegistryPage.tsx","variants":"—","documentedAt":""},{"name":"SalesRegistryTable","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/registry/SalesRegistryTable.tsx","variants":"—","documentedAt":""},{"name":"SortableTh","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/clients/SalesClientsTable.tsx","variants":"—","documentedAt":""},{"name":"StatusBadge","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/sales/orders/StatusBadge.tsx","variants":"—","documentedAt":""},{"name":"SummaryGrid","kind":"React component","category":"Feature: sales","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/sales/client-detail/SalesClientOverviewTab.tsx","variants":"—","documentedAt":""},{"name":"SettingsPage","kind":"React component","category":"Feature: settings","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/settings/SettingsPage.tsx","variants":"—","documentedAt":"layouts.html"},{"name":"OperatorCardSection","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/support/context/OperatorCards.tsx","variants":"—","documentedAt":""},{"name":"OperatorCards","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support/context/OperatorCards.tsx","variants":"—","documentedAt":""},{"name":"OperatorField","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/support/context/OperatorCards.tsx","variants":"—","documentedAt":""},{"name":"RightTabButton","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"local","source":"apps/internal-ui/src/features/support/context/SupportContextPanel.tsx","variants":"—","documentedAt":""},{"name":"SupportContextPanel","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support/context/SupportContextPanel.tsx","variants":"—","documentedAt":""},{"name":"SupportDialogsPage","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support/SupportDialogsPage.tsx","variants":"—","documentedAt":""},{"name":"SupportHistory","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support/context/SupportHistory.tsx","variants":"—","documentedAt":""},{"name":"SupportOverviewPage","kind":"React component","category":"Feature: support","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support/SupportOverviewPage.tsx","variants":"—","documentedAt":""},{"name":"PortalArticleEditor","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalArticleEditor.tsx","variants":"—","documentedAt":""},{"name":"PortalArticleTable","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalArticleTable.tsx","variants":"—","documentedAt":""},{"name":"PortalCategoryManagement","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalCategoryManagement.tsx","variants":"—","documentedAt":""},{"name":"PortalContent","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalContent.tsx","variants":"—","documentedAt":""},{"name":"PortalCreateDialog","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalCreateDialog.tsx","variants":"—","documentedAt":""},{"name":"PortalDomainSettings","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalDomainSettings.tsx","variants":"—","documentedAt":""},{"name":"PortalProductSettings","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalProductSettings.tsx","variants":"—","documentedAt":""},{"name":"PortalSettings","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalSettings.tsx","variants":"—","documentedAt":""},{"name":"PortalWidgetSettings","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/PortalWidgetSettings.tsx","variants":"—","documentedAt":""},{"name":"SupportPortalDetailPage","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/SupportPortalDetailPage.tsx","variants":"—","documentedAt":""},{"name":"SupportPortalsPage","kind":"React component","category":"Feature: support-portals","scope":"Feature","visibility":"exported","source":"apps/internal-ui/src/features/support-portals/SupportPortalsPage.tsx","variants":"—","documentedAt":""},{"name":"Shell","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/Shell.tsx","variants":"—","documentedAt":"layouts.html"},{"name":"ShellRouteContent","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/ShellRouteContent.tsx","variants":"—","documentedAt":""},{"name":"Sidebar","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/Sidebar.tsx","variants":"—","documentedAt":""},{"name":"SidebarLink","kind":"React component","category":"Layout","scope":"System","visibility":"local","source":"apps/internal-ui/src/layout/Sidebar.tsx","variants":"—","documentedAt":""},{"name":"SidebarNavSection","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/SidebarNavSection.tsx","variants":"—","documentedAt":""},{"name":"SidebarUserMenu","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/SidebarUserMenu.tsx","variants":"—","documentedAt":""},{"name":"TopBar","kind":"React component","category":"Layout","scope":"System","visibility":"exported","source":"apps/internal-ui/src/layout/TopBar.tsx","variants":"—","documentedAt":""},{"name":"ActionButton","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"—","documentedAt":"components.html"},{"name":"Avatar","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":"data-display.html"},{"name":"Button","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"primary, secondary, action, danger-outline","documentedAt":"components.html"},{"name":"CategoryManagementDialog","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/CategoryManagementDialog.tsx","variants":"—","documentedAt":""},{"name":"CategoryManagementRow","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/CategoryManagementRow.tsx","variants":"—","documentedAt":""},{"name":"CategoryTree","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/CategoryTree.tsx","variants":"—","documentedAt":""},{"name":"ChannelBadge","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/badges.tsx","variants":"email, max, telegram, web, plain","documentedAt":"data-display.html"},{"name":"ContentEditorBreadcrumb","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/ContentEditorBreadcrumb.tsx","variants":"—","documentedAt":""},{"name":"ContentEditorCard","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/ContentEditorCard.tsx","variants":"—","documentedAt":""},{"name":"ContentLibraryTable","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/ContentLibraryTable.tsx","variants":"—","documentedAt":""},{"name":"ContentLibraryToolbar","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/content-library/ContentLibraryToolbar.tsx","variants":"—","documentedAt":""},{"name":"ContentState","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"primary, warning","documentedAt":"patterns.html"},{"name":"DecisionDialog","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/DecisionDialog.tsx","variants":"warning, danger","documentedAt":"patterns.html"},{"name":"EmptyState","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":"patterns.html"},{"name":"ErrorScreen","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":""},{"name":"FormField","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"editable, readonly, disabled, invalid, mono, wide","documentedAt":"components.html"},{"name":"Icon","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/icons.tsx","variants":"—","documentedAt":"data-display.html"},{"name":"IconButton","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"bordered, bare, danger","documentedAt":"components.html"},{"name":"KeyValue","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"—","documentedAt":"data-display.html"},{"name":"LoadingScreen","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":""},{"name":"LoadingState","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"page, inline","documentedAt":"patterns.html"},{"name":"LogoIcon","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/icons.tsx","variants":"—","documentedAt":""},{"name":"MaxLogo","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/icons.tsx","variants":"—","documentedAt":""},{"name":"MetricBox","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"—","documentedAt":"data-display.html"},{"name":"PageHeader","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":"patterns.html"},{"name":"PasswordField","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":""},{"name":"PermissionScreen","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":""},{"name":"ProductMark","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":"data-display.html"},{"name":"ProductTag","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"foxray, default product accent","documentedAt":"data-display.html"},{"name":"ReadOnlyField","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"—","documentedAt":""},{"name":"RoleBadge","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"owner, admin, employee","documentedAt":"data-display.html"},{"name":"Rows","kind":"React component","category":"Shared UI","scope":"System","visibility":"local","source":"apps/internal-ui/src/shared/content-library/CategoryTree.tsx","variants":"—","documentedAt":""},{"name":"SearchInput","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"—","documentedAt":"components.html"},{"name":"SelectField","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"—","documentedAt":"components.html"},{"name":"ShieldIcon","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/icons.tsx","variants":"—","documentedAt":""},{"name":"StatusPill","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui.tsx","variants":"12 status keys","documentedAt":"data-display.html"},{"name":"SwitchButton","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"off, on, disabled, large","documentedAt":"components.html"},{"name":"TablePagination","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"default, active, disabled, ellipsis","documentedAt":"components.html"},{"name":"TelegramLogo","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/icons.tsx","variants":"—","documentedAt":""},{"name":"TextAreaField","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/form-controls.tsx","variants":"—","documentedAt":"components.html"},{"name":"ToneBadge","kind":"React component","category":"Shared UI","scope":"System","visibility":"exported","source":"apps/internal-ui/src/shared/ui-controls.tsx","variants":"—","documentedAt":""},{"name":"Control sizes","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/shared/*.css","variants":"24, 30, 34, 36, 38, 42×24, 46×26, 56","documentedAt":"foundations.html"},{"name":"Neutral palette","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/styles/base.css","variants":"10 neutral values","documentedAt":"foundations.html"},{"name":"Provider colors","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/shared/providers.ts","variants":"email, max, telegram, web","documentedAt":"foundations.html"},{"name":"Radius scale","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/styles/base.css","variants":"4, 6, 8, 9, 10, 12, 14, pill","documentedAt":"foundations.html"},{"name":"Semantic colors","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/styles/base.css","variants":"primary, success, warning, error, ai","documentedAt":"foundations.html"},{"name":"Shadow scale","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/styles/base.css","variants":"xs, sm, button, lg","documentedAt":"foundations.html"},{"name":"Spacing scale","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/shared/*.css","variants":"4–28 px recurring values","documentedAt":"foundations.html"},{"name":"Typography","kind":"Token set","category":"Foundations","scope":"System","visibility":"system","source":"src/styles/base.css","variants":"sans, mono, page heading, body, caption","documentedAt":"foundations.html"}]} diff --git a/design/design-system/layouts.html b/design/design-system/layouts.html deleted file mode 100644 index 1c2d9d5..0000000 --- a/design/design-system/layouts.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - Макеты страниц · Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Макеты страниц
- Layouts -
-
-
-
-
Layouts
-

Компоновка экранов

-

Наглядные схемы существующих типов экранов. Они показывают состав и иерархию, сохраняя текущий shell, интервалы, поверхности и controls.

-
-
- -
-
-

Application shell + list page

Sidebar, topbar, page header, filter bar и table card.

- layout/* · list features -
-
- -
-
Организация / СотрудникиРабочий контур
-
-
СотрудникиУправление доступом и ролями
-
ВсеАктивные
-
-
СотрудникРольСтатус
-
Андрей ЕрмолаевВладелецАктивен•••
-
Ирина КовалеваАдминистраторПриглашён•••
-
Максим СеровСотрудникНеактивен•••
-
Елена МироноваСотрудникАктивен•••
-
-
-
-
-
- -
-
-

Detail page

Основная колонка, боковой rail и секции редактирования.

- employee/channel detail features -
-
-
Entity detailheader + content + rail
-
- -
-
-

Основные параметры

-

Назначение и политика

-
- -
-
-
-
- -
-
-

Settings page

Последовательность тематических секций с локальными действиями.

- settings · profile · administration -
-
-
Settings compositionpage header + section cards
-
- -
-

Организация

-

Безопасность

Двухфакторная аутентификация
Уведомления о входе
-
-
-
-
- -
-
-

Conversation workspace

Список диалогов, центральный thread и контекст клиента.

- features/conversations -
-
- -
-
Иван Петров · Telegram
-
Здравствуйте! Подскажите, какой тариф подходит для двух агентов?
Для двух активных ИИ-агентов подойдёт тариф Startup.
-
-
- -
-
-
-
-
- - - - diff --git a/design/design-system/patterns.html b/design/design-system/patterns.html deleted file mode 100644 index 786f488..0000000 --- a/design/design-system/patterns.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - Паттерны · Chatballs Design System - - - - - - - - - -
- -
-
-
Design System/Паттерны
- Compositions -
-
-
-
-
Patterns
-

Композиции компонентов

-

Устоявшиеся конструкции текущего интерфейса: заголовок страницы, фильтры, таблица, состояния, меню и диалоги решений.

-
-
- -
-
-

Заголовок страницы

Название, пояснение и основное действие справа.

- PageHeader · layout/page.css -
-
-
PageHeader.page-header
-
- -
-
-
- -
-
-

Таблица со служебной панелью

Filter bar, стандартная таблица, row actions и пагинация.

- table-controls.css · feature tables -
-
-
List table pattern.filter-bar · .baseline-table
-
-
- -
Статус
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
СотрудникРольСтатусПродукты
АЕ
ВладелецАктивенChatballs
ИК
АдминистраторПриглашёнFoxRay
МС
СотрудникНеактивенFirePage
-
- -
-
-
-
- -
-
-

Состояния контента

Пустой раздел, предупреждение и загрузка.

- ContentState · EmptyState · LoadingState -
-
-
-
Primary stateContentState
-
-
Данных пока нет

Создайте первый элемент, чтобы начать работу.

-
-
-
-
Warning statetone="warning"
-
-
Подключение требует внимания

Проверьте параметры интеграции.

-
-
-
-
Inline statesEmptyState · LoadingState
-
-
Загрузка…
-
Нет результатов
-
-
-
-
- -
-
-

Выпадающее меню

Единая структура пунктов действий и семантических состояний.

- menu.css -
-
-
App dropdown.app-dropdown
-
- -
-
-
- -
-
-

Decision dialogs

Предупреждающее и опасное подтверждение.

- DecisionDialog.tsx · design-components.css -
-
-
-
Warningtone="warning"
-
-
-
-
Dangertone="danger"
-
-
-
-
-
-
-
- -
- -
- -
- -
- - - - diff --git a/tests/e2e/internal-ui.spec.ts b/tests/e2e/internal-ui.spec.ts index f741a94..4962bd4 100644 --- a/tests/e2e/internal-ui.spec.ts +++ b/tests/e2e/internal-ui.spec.ts @@ -1,4 +1,4 @@ -import { expect, type Page, test } from "@playwright/test"; +import { expect, type Locator, type Page, test } from "@playwright/test"; const ORGANIZATION_PUBLIC_ID = "123e4567-e89b-12d3-a456-426614174000"; const SECOND_ORGANIZATION_PUBLIC_ID = "223e4567-e89b-12d3-a456-426614174000"; @@ -18,6 +18,36 @@ const MANAGER_NAV = ["Чат", "Контакты", "Агенты", "Сотруд // интерфейс: именно их ждали прежние редакции этих тестов. const REMOVED_FROM_UI = ["Командный центр", "Отделы", "Продажи", "Каналы", "Подключения", "Продукты"]; +// Состояние онбординга «Начало работы». По умолчанию сценарии получают +// пройденный визард — иначе он перекрывал бы проверяемый экран; тесты самого +// онбординга подставляют «ещё не открывал». +const ONBOARDING_STEPS_DONE = { + providerConnected: true, + agentActive: true, + knowledgeFilled: true, + connectionBound: true, + widgetPublished: true, + employeeInvited: true, + platformConfigured: true, + firstConversation: true, +}; + +type OnboardingMock = { steps: Record; dismissedAt: string | null; completedAt: string | null }; + +const ONBOARDING_DISMISSED: OnboardingMock = { + steps: ONBOARDING_STEPS_DONE, + dismissedAt: "2026-01-01T00:00:00Z", + completedAt: "2026-01-01T00:00:00Z", +}; + +// Человек в системе давно, но онбординга ещё не видел: признаков нет, часть +// шагов уже выполнена по факту. +const ONBOARDING_FRESH: OnboardingMock = { + steps: { ...ONBOARDING_STEPS_DONE, widgetPublished: false, platformConfigured: false, firstConversation: false }, + dismissedAt: null, + completedAt: null, +}; + const GROUPS = [ { id: 1, name: "Операторы", color: "#1677ff", memberCount: 1, memberIds: [7], createdAt: "2026-02-02T10:00:00Z" }, { id: 2, name: "Поддержка", color: "#2aa876", memberCount: 0, memberIds: [], createdAt: "2026-02-02T10:00:00Z" }, @@ -86,6 +116,9 @@ function identityFor(role: Role, memberships = [membershipFor(role)]) { } const OWNER_IDENTITY = identityFor("OWNER"); +// Администратор организации, но не установки: разделы и шаги про саму +// установку («Платформа», «Домен и почта») ему не показываются. +const ADMIN_IDENTITY = identityFor("ADMIN"); const EMPLOYEE_IDENTITY = identityFor("EMPLOYEE"); const EMPLOYEE_PERMISSIONS = { @@ -161,7 +194,7 @@ const INVITATION = { expiresAt: "2026-09-17T10:00:00Z", }; -async function mockInstance(page: Page) { +async function mockInstance(page: Page, onboarding: OnboardingMock = ONBOARDING_DISMISSED, onboardingActions: string[] = []) { await page.route("**/api/v1/setup/", (route) => route.fulfill({ json: { needsSetup: false } })); await page.route("**/api/v1/instance/**", (route) => route.fulfill({ json: {} })); await page.route("**/api/v1/organizations/*/conversations/**", (route) => { @@ -177,8 +210,20 @@ async function mockInstance(page: Page) { }); await page.route("**/api/v1/organizations/*/company/**", (route) => { const path = new URL(route.request().url()).pathname; - if (path.endsWith("/company/launch-checklist/")) { - return route.fulfill({ json: { agentCreated: true, connectionBound: true, employeeInvited: true, done: true } }); + if (path.endsWith("/company/onboarding/")) { + if (route.request().method() === "POST") { + const action = JSON.parse(route.request().postData() ?? "{}").action; + onboardingActions.push(action); + const now = "2026-03-01T10:00:00Z"; + return route.fulfill({ + json: { + steps: onboarding.steps, + dismissedAt: action === "restart" ? null : now, + completedAt: action === "complete" ? now : null, + }, + }); + } + return route.fulfill({ json: onboarding }); } return route.fulfill({ json: { items: GROUPS } }); }); @@ -223,9 +268,9 @@ async function mockSession(page: Page, user: object | null) { ); } -async function login(page: Page, user: object) { +async function login(page: Page, user: object, onboarding: OnboardingMock = ONBOARDING_DISMISSED, onboardingActions: string[] = []) { await mockSession(page, null); - await mockInstance(page); + await mockInstance(page, onboarding, onboardingActions); await mockEmployees(page); await page.route("**/api/v1/auth/login/", (route) => route.fulfill({ json: { authenticated: true, user } })); await page.goto("/"); @@ -561,10 +606,196 @@ test("экран сотрудников: список, создание, кар await expect(transferDialog).toBeVisible(); // Кандидатами могут быть только активные администраторы: сотрудника в списке // быть не должно. - const candidates = transferDialog.locator("select"); - await expect(candidates.locator("option")).toHaveCount(1); - await expect(candidates.locator("option")).toContainText("Анна Ким"); + await transferDialog.getByRole("button", { name: "Новый владелец" }).click(); + const candidates = page.locator(".app-dropdown.is-field .ant-dropdown-menu-item"); + await expect(candidates).toHaveCount(1); + await expect(candidates).toContainText("Анна Ким"); + await page.keyboard.press("Escape"); await page.screenshot({ path: "test-results/employees-ownership.png", fullPage: true }); await transferDialog.getByRole("button", { name: "Отмена" }).click(); await expect(transferDialog).toHaveCount(0); }); + +// Онбординг «Начало работы» (дизайн-базлайн, handoff 2026-09-13). Требование +// заказчика: визард показывается владельцу и администратору, пока он его не +// закроет, — в том числе тем, кто работает в установке давно. Поэтому признак +// «закрыл» живёт на сервере, а не в localStorage, и мок отдаёт его пустым. + +/** Ждёт конца ob-rise: до него карточка полупрозрачна и снимок выходит блёклым. */ +const settled = (locator: Locator) => expect(locator).toHaveCSS("opacity", "1"); + +test("онбординг открывается владельцу, который его ещё не закрывал", async ({ page }) => { + const actions: string[] = []; + await login(page, OWNER_IDENTITY, ONBOARDING_FRESH, actions); + + const welcome = page.locator(".ob-welcome"); + await expect(welcome).toBeVisible(); + // Число в заголовке — словом, как в макете, а не цифрой. + await expect(welcome.getByRole("heading")).toHaveText("Восемь шагов до первого ответа клиенту"); + // Карточка выезжает 320ms: снимок до конца анимации ловит полупрозрачность. + await settled(welcome); + await page.screenshot({ path: "test-results/onboarding-welcome.png" }); + + await welcome.getByRole("button", { name: "Начать настройку" }).click(); + const wizard = page.locator(".ob-steps"); + await expect(wizard).toBeVisible(); + // Владелец здесь ещё и администратор установки — значит все восемь шагов. + await expect(wizard.locator(".ob-rail-step")).toHaveCount(8); + await expect(wizard.locator(".ob-chip.is-accent")).toContainText("Шаг 1 из 8"); + await expect(wizard.getByRole("heading")).toHaveText("Подключите AI-провайдера"); + // Прогресс приходит фактами с сервера, а не нажатиями «Далее». + await expect(wizard.locator(".ob-rail-progress-row span")).toHaveText("Готово 5 из 8"); + await settled(wizard); + await page.screenshot({ path: "test-results/onboarding-steps.png" }); + + await wizard.getByRole("button", { name: "Далее" }).click(); + await expect(wizard.getByRole("heading")).toHaveText("Создайте AI-агента"); + await wizard.getByRole("button", { name: "Назад" }).click(); + await expect(wizard.getByRole("heading")).toHaveText("Подключите AI-провайдера"); + + // «Закрыть и настроить самому» записывает отказ на сервер, а не в браузер. + await wizard.getByRole("button", { name: "Закрыть и настроить самому" }).click(); + await expect(wizard).toHaveCount(0); + await expect.poll(() => actions).toEqual(["dismiss"]); + + // Закрытый визард оставляет пилюлю возврата: настроено ещё не всё. + const launcher = page.locator(".ob-launcher"); + await expect(launcher).toBeVisible(); + await expect(launcher).toContainText("5/8"); + await page.screenshot({ path: "test-results/onboarding-launcher.png" }); +}); + +test("администратору организации шаг про домен и почту не показывают", async ({ page }) => { + await login(page, ADMIN_IDENTITY, ONBOARDING_FRESH); + + await page.locator(".ob-welcome").getByRole("button", { name: "Начать настройку" }).click(); + const wizard = page.locator(".ob-steps"); + // Раздел «Платформа» — свойство установки, а не организации: шагов семь, и + // счётчик считает от семи. + await expect(wizard.locator(".ob-rail-step")).toHaveCount(7); + await expect(wizard.locator(".ob-rail-step")).not.toContainText(["Домен и почта"]); + await expect(wizard.locator(".ob-rail-progress-row span")).toHaveText("Готово 5 из 7"); + await expect(page.locator(".ob-steps")).toBeVisible(); +}); + +test("у семи шагов заголовок приветствия тоже числом-словом", async ({ page }) => { + await login(page, ADMIN_IDENTITY, ONBOARDING_FRESH); + + await expect(page.locator(".ob-welcome").getByRole("heading")).toHaveText("Семь шагов до первого ответа клиенту"); +}); + +test("«Показать где» затемняет приложение и подсвечивает пункт сайдбара", async ({ page }) => { + await login(page, OWNER_IDENTITY, ONBOARDING_FRESH); + + const wizard = page.locator(".ob-steps"); + await page.locator(".ob-welcome").getByRole("button", { name: "Начать настройку" }).click(); + // Второй шаг рассказывает про «Агентов» — их пункт тур и подсвечивает. + await wizard.getByRole("button", { name: "Далее" }).click(); + await wizard.getByRole("button", { name: "Показать где" }).click(); + + await expect(wizard).toHaveCount(0); + const callout = page.locator(".ob-tour-callout"); + await expect(callout).toBeVisible(); + await expect(callout).toContainText("Раздел «Агенты»"); + await expect(page.locator(".ob-tour-dim")).toHaveCount(4); + await settled(callout); + await page.screenshot({ path: "test-results/onboarding-tour.png" }); + + await callout.getByRole("button", { name: "Понятно, к шагам" }).click(); + await expect(page.locator(".ob-tour-callout")).toHaveCount(0); + await expect(wizard.getByRole("heading")).toHaveText("Создайте AI-агента"); +}); + +test("закрытый онбординг открывается заново ссылкой внизу субменю «Настроек»", async ({ page }) => { + await login(page, OWNER_IDENTITY, ONBOARDING_DISMISSED); + + // Визард закрыт и всё настроено: ни окна, ни пилюли. + await expect(page.locator(".ob-overlay")).toHaveCount(0); + await expect(page.locator(".ob-launcher")).toHaveCount(0); + + // Переход внутри приложения: перезагрузка страницы вернула бы форму входа — + // сессия в моках живёт только до неё. + await page.locator("nav.hub-nav").getByRole("button", { name: "Настройки", exact: true }).click(); + const link = page.locator(".settings-subnav-onboarding"); + await expect(link).toBeVisible(); + await expect(link).toContainText("Начало работы"); + await page.screenshot({ path: "test-results/onboarding-settings-link.png" }); + + await link.click(); + await expect(page.locator(".ob-steps")).toBeVisible(); +}); + +test("сотрудник онбординга не видит", async ({ page }) => { + await login(page, EMPLOYEE_IDENTITY, ONBOARDING_FRESH); + + await expect(page.locator(".ob-overlay")).toHaveCount(0); + await expect(page.locator(".ob-launcher")).toHaveCount(0); +}); + +test("последний шаг ведёт на финальный экран и записывает «пройден»", async ({ page }) => { + const actions: string[] = []; + await login(page, OWNER_IDENTITY, ONBOARDING_FRESH, actions); + + const wizard = page.locator(".ob-steps"); + await page.locator(".ob-welcome").getByRole("button", { name: "Начать настройку" }).click(); + // Навигация по рейке свободная: до последнего шага можно дойти одним кликом. + await wizard.locator(".ob-rail-step").last().click(); + await expect(wizard.getByRole("heading")).toHaveText("Напишите боту как клиент"); + + await wizard.getByRole("button", { name: "Завершить" }).click(); + const done = page.locator(".ob-done"); + await expect(done.getByRole("heading")).toHaveText("Всё готово — можно принимать клиентов"); + // Сводка честно показывает, что три шага так и не выполнены по факту. + await expect(done.locator(".ob-summary-item")).toHaveCount(8); + await expect(done.locator(".ob-summary-item.is-done")).toHaveCount(5); + await settled(done); + await page.screenshot({ path: "test-results/onboarding-done.png" }); + + await done.getByRole("button", { name: "Перейти в чат" }).click(); + await expect(page.locator(".ob-overlay")).toHaveCount(0); + await expect(page).toHaveURL(/\/chat$/); + await expect.poll(() => actions).toEqual(["complete"]); +}); + +test("подсветка тура встаёт ровно по элементу после смены экрана", async ({ page }) => { + await login(page, OWNER_IDENTITY, ONBOARDING_FRESH); + + const wizard = page.locator(".ob-steps"); + await page.locator(".ob-welcome").getByRole("button", { name: "Начать настройку" }).click(); + // Первый шаг ведёт в «Настройки → AI-провайдер». Новый маршрут въезжает + // анимацией `surface-enter` (translateY 6px): замер на первом кадре поставил + // бы рамку на 6px мимо строки, поэтому тур доводит его до остановки. + await wizard.getByRole("button", { name: "Показать где" }).click(); + + const callout = page.locator(".ob-tour-callout"); + await expect(callout).toContainText("Настройки → AI-провайдер"); + await settled(callout); + + const geometry = await page.evaluate(() => { + const box = (selector: string) => { + const rect = document.querySelector(selector)!.getBoundingClientRect(); + return [Math.round(rect.x), Math.round(rect.y), Math.round(rect.width), Math.round(rect.height)]; + }; + const root = box(".hub-shell"); + const target = box('[data-onboarding-target="settings-ai"]'); + const dim = [...document.querySelectorAll(".ob-tour-dim")].map((el) => { + const rect = el.getBoundingClientRect(); + return [Math.round(rect.x), Math.round(rect.y), Math.round(rect.width), Math.round(rect.height)]; + }); + return { root, target, spot: box(".ob-tour-spot"), dim }; + }); + + const [x, y, w, h] = geometry.target; + const [, , rootW, rootH] = geometry.root; + // Рамка — цель с отступом 6px со всех сторон. + expect(geometry.spot).toEqual([x - 6, y - 6, w + 12, h + 12]); + // Четыре затемнения смыкаются вокруг неё, не оставляя ни щели, ни просвета. + expect(geometry.dim).toEqual([ + [0, 0, rootW, y - 6], + [0, y + h + 6, rootW, rootH - (y + h + 6)], + [0, y - 6, x - 6, h + 12], + [x + w + 6, y - 6, rootW - (x + w + 6), h + 12], + ]); + + await page.screenshot({ path: "test-results/onboarding-tour-settings.png" }); +});