Files
call-center/ui/supervisor/index.html
T
Hermes Agent 1ad4e1ec6e feat: surface L2 agent pool and escalations on supervisor screen
- routing-service: GET /escalations (list recent escalation attempts)
- supervisor UI: new panel showing the real agent pool (status, level,
  tenant, skills, calls handled) fed by GET /agents, with a form to add
  operators to the pool
- supervisor UI: new live escalation feed (AI->L2 handoffs, status,
  assigned operator), auto-refreshed every 5s alongside live calls
2026-08-29 13:28:14 +05:00

315 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KonturCC Супервизор</title>
<link rel="icon" href="/operator/assets/favicon.svg" type="image/svg+xml" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/operator/assets/styles.css" />
</head>
<body>
<div class="app-shell">
<aside class="sidebar">
<div class="brand">
<div class="brand-mark">K</div>
<div>
<div class="brand-title">KonturCC</div>
<div class="brand-sub">Операционный контроль</div>
</div>
</div>
<nav class="nav-menu">
<div class="nav-group">
<div class="nav-label">Рабочие зоны</div>
<a class="nav-link" data-shell="operator" data-roles="admin,supervisor,operator,analyst" href="/operator"><span class="nav-bullet"></span>Оператор</a>
<a class="nav-link active" data-shell="supervisor" data-roles="admin,supervisor" href="/supervisor"><span class="nav-bullet"></span>Супервизор</a>
<a class="nav-link" data-shell="analyst" data-roles="admin,supervisor,analyst" href="/analyst"><span class="nav-bullet"></span>Аналитика</a>
<a class="nav-link" data-shell="sales" data-roles="admin,supervisor,operator,analyst" href="/sales"><span class="nav-bullet"></span>Продажи</a>
<a class="nav-link" data-shell="admin" data-roles="admin" href="/admin"><span class="nav-bullet"></span>Администрирование</a>
</div>
<div class="nav-group">
<div class="nav-label">Блоки</div>
<a class="nav-link" href="#realtime"><span class="nav-bullet"></span>Оперативный срез</a>
<a class="nav-link" href="#liveCalls"><span class="nav-bullet"></span>Живые звонки</a>
<a class="nav-link" href="#agents"><span class="nav-bullet"></span>Агенты</a>
<a class="nav-link" href="#callRoutingAgents"><span class="nav-bullet"></span>Пул L2 (звонки)</a>
<a class="nav-link" href="#escalations"><span class="nav-bullet"></span>Эскалации</a>
<a class="nav-link" href="#queues"><span class="nav-bullet"></span>Очереди</a>
<a class="nav-link" href="#knowledge"><span class="nav-bullet"></span>База знаний</a>
</div>
<div class="nav-group">
<div class="nav-label">Записи</div>
<a class="nav-link" href="#recordings"><span class="nav-bullet"></span>Записи</a>
</div>
</nav>
<div class="sidebar-footer">
<strong>Экран супервизора</strong>
<p>Сводка по агентам, очередям и KPI без захода в операторский экран.</p>
<button id="logoutBtn" class="btn ghost" type="button">Выйти</button>
</div>
</aside>
<section class="workspace">
<header class="workspace-topbar reveal">
<div class="search-box">
<input class="app-search" placeholder="Найти агента, очередь или KPI..." />
</div>
<div class="topbar-actions">
<div class="status-pill" id="gatewayStatus">Шлюз: проверка...</div>
<button class="icon-button" type="button" aria-label="Уведомления"></button>
<div class="profile-chip">
<div class="avatar-badge">SV</div>
<div class="profile-meta">
<div class="profile-name" id="profileName">Экран супервизора</div>
<div class="profile-role" id="profileRole">Операционный контроль</div>
</div>
</div>
</div>
</header>
<main class="layout">
<section class="page-hero reveal">
<p class="eyebrow">Операционный контур</p>
<h1>Пульт супервизора</h1>
<p class="hint">Операционный обзор: состояние команды, очередей и ключевых KPI.</p>
</section>
<section class="panel auth reveal" id="authPanel">
<h2>Сессия</h2>
<div class="row">
<label>Пользователь</label>
<input id="sessionUser" value="supervisor" />
</div>
<div class="row">
<label>Роль</label>
<select id="sessionRole">
<option value="supervisor" selected>Супервизор</option>
<option value="admin">Администратор</option>
<option value="operator">Оператор</option>
<option value="analyst">Аналитик</option>
</select>
</div>
<div class="row">
<label>Пароль</label>
<input id="sessionPassword" type="password" value="sup12345" />
</div>
<div class="actions">
<button id="loginBtn" class="btn">Войти</button>
<button id="corporateLoginBtn" class="btn ghost" type="button">Корпоративный вход</button>
<button id="refreshBtn" class="btn ghost">Обновить все</button>
</div>
<p class="hint" id="sessionInfo">Токен: отсутствует</p>
</section>
<section class="grid">
<article class="panel reveal" id="realtime">
<h2>Оперативный срез</h2>
<div class="actions compact-actions">
<button id="loadRealtimeBtn" class="btn ghost">Обновить срез</button>
<button id="loadKpiBtn" class="btn ghost">Обновить KPI</button>
</div>
<p class="hint">Этот блок нужен для оперативной проверки и технической диагностики на экране супервизора.</p>
<div id="realtimeSummary" class="summary-grid"></div>
<details class="details-block">
<summary>Технический ответ оперативного среза</summary>
<pre id="realtimeOutput" class="output"></pre>
</details>
<div id="kpiSummary" class="summary-grid"></div>
<details class="details-block">
<summary>Технический ответ KPI</summary>
<pre id="kpiOutput" class="output"></pre>
</details>
</article>
<article class="panel reveal" id="liveCalls">
<h2>Живые звонки Asterisk</h2>
<div class="actions compact-actions">
<button id="loadLiveCallsBtn" class="btn ghost">Обновить звонки</button>
</div>
<p class="hint">Показываются последние звонки, пришедшие от Asterisk.</p>
<ul id="liveCallsList" class="list"></ul>
</article>
<article class="panel reveal" id="agents">
<h2>Агенты</h2>
<div class="inline-form split-two">
<input id="agentId" placeholder="ID агента" value="supervisor_demo_agent" />
<select id="agentState">
<option value="READY">Готовы</option>
<option value="BUSY">Заняты</option>
<option value="BREAK">BREAK</option>
<option value="OFFLINE">OFFLINE</option>
</select>
</div>
<div class="inline-form compact">
<input id="agentQueueId" placeholder="Очередь" value="line2" />
<button id="setAgentBtn" class="btn">Обновить статус</button>
</div>
<div class="inline-form compact">
<button id="loadAgentsBtn" class="btn ghost">Показать агентов</button>
</div>
<ul id="agentsList" class="list"></ul>
</article>
<article class="panel reveal" id="callRoutingAgents">
<h2>Пул операторов L2 (звонки)</h2>
<p class="hint">Реальный пул для эскалации звонков с Voice AI (L1) на живых операторов +7 747 645 6048. Отдельно от демо-статусов в блоке «Агенты» выше — здесь показан фактический пул Routing Engine.</p>
<div class="actions compact-actions">
<button id="loadCallAgentsBtn" class="btn ghost">Обновить пул</button>
</div>
<div id="callAgentsSummary" class="summary-grid"></div>
<ul id="callAgentsList" class="list"></ul>
<details class="details-block">
<summary>Добавить оператора в пул</summary>
<div class="inline-form split-two">
<input id="newAgentExtension" placeholder="Внутренний номер (напр. 2001)" />
<input id="newAgentDisplayName" placeholder="Имя оператора" />
</div>
<div class="inline-form split-two">
<select id="newAgentLevel">
<option value="L2" selected>L2 — оператор</option>
<option value="L3">L3 — технический специалист</option>
</select>
<input id="newAgentTenant" placeholder="Tenant ID" value="konturai" />
</div>
<div class="inline-form compact">
<input id="newAgentSkills" placeholder="Навыки через запятую (необязательно)" />
<button id="createCallAgentBtn" class="btn">Добавить в пул</button>
</div>
</details>
</article>
<article class="panel reveal" id="escalations">
<h2>Эскалации AI → оператор</h2>
<p class="hint">Живая лента: какие звонки Voice AI передал(-ает) на L2 и кто из операторов их принял.</p>
<div class="actions compact-actions">
<button id="loadEscalationsBtn" class="btn ghost">Обновить эскалации</button>
</div>
<div id="escalationsSummary" class="summary-grid"></div>
<ul id="escalationsList" class="list"></ul>
</article>
<article class="panel reveal" id="queues">
<h2>Очередь</h2>
<div class="inline-form split-two">
<input id="queueMetricId" placeholder="ID очереди" value="line2" />
<input id="queueInQueue" placeholder="В очереди" type="number" value="2" />
</div>
<div class="inline-form compact">
<input id="queueAvgWait" placeholder="Среднее ожидание (сек)" type="number" value="18" />
<button id="saveQueueMetricsBtn" class="btn">Обновить очередь</button>
</div>
<p class="hint">Используйте это как быстрый операционный контроль для демонстрационного среза.</p>
<pre id="queueMetricsOutput" class="output"></pre>
</article>
<article class="panel reveal panel-span-full" id="knowledge">
<h2>База знаний</h2>
<p class="hint">Супервизор может вручную создать категорию, добавить статью и сразу обновить базу знаний для Voice AI.</p>
<div id="kbSummary" class="summary-grid"></div>
<div class="inline-form split-two">
<input id="kbCategoryName" placeholder="Новая категория" />
<input id="kbCategoryDescription" placeholder="Описание категории" />
</div>
<div class="inline-form compact">
<button id="createKbCategoryBtn" class="btn">Добавить категорию</button>
<button id="loadKbBtn" class="btn ghost" type="button">Обновить базу знаний</button>
</div>
<div class="inline-form split-two">
<select id="kbArticleCategory"></select>
<select id="kbArticleLanguage">
<option value="ru">RU</option>
<option value="kz">KZ</option>
</select>
</div>
<div class="inline-form compact">
<input id="kbArticleGroupId" placeholder="ID группы статей (связка RU/KZ)" />
</div>
<div class="inline-form split-two">
<input id="kbArticleTitle" placeholder="Заголовок статьи" />
</div>
<div class="inline-form compact">
<input id="kbArticleTags" placeholder="Теги через запятую" />
</div>
<div class="row">
<label for="kbArticleBody">Текст статьи</label>
<textarea id="kbArticleBody" rows="6" placeholder="Например: график работы, адреса, частые вопросы, правила маршрутизации..."></textarea>
</div>
<div class="actions compact-actions">
<button id="saveKbArticleBtn" class="btn">Сохранить статью</button>
<button id="resetKbArticleBtn" class="btn ghost" type="button">Новая статья</button>
</div>
<p class="hint" id="kbFormHint">Создайте новую статью или выберите существующую для редактирования.</p>
<div class="inline-form split-two">
<input id="kbSearchQuery" placeholder="Поиск по базе знаний" />
<button id="searchKbBtn" class="btn ghost" type="button">Искать</button>
</div>
<div class="inline-form split-two">
<div>
<h3>Категории</h3>
<ul id="kbCategoriesList" class="list"></ul>
</div>
<div>
<h3>Статьи</h3>
<ul id="kbArticlesList" class="list"></ul>
</div>
</div>
<details class="details-block">
<summary>Предпросмотр выбранной статьи</summary>
<pre id="kbArticlePreview" class="output"></pre>
</details>
</article>
<article class="panel reveal">
<h2>Лог действий</h2>
<p class="hint">Сверху всегда последнее действие супервизора.</p>
<pre id="eventLog" class="output tall"></pre>
</article>
<article class="panel reveal" id="recordings">
<h2>Записи</h2>
<div class="inline-form compact">
<input id="recordingEventId" placeholder="ID voice_event / обращения / звонка" />
<button id="importRecordingBtn" class="btn">Импорт / поиск</button>
</div>
<div class="inline-form split-two">
<input id="recordingCallId" placeholder="Фильтр по ID звонка" />
<input id="recordingInteractionId" placeholder="Фильтр по ID обращения" />
</div>
<div class="inline-form compact">
<select id="recordingStatus">
<option value="">Все</option>
<option value="ready">Готова</option>
<option value="archived">В архиве</option>
<option value="missing">Отсутствует</option>
</select>
<button id="loadRecordingsBtn" class="btn ghost">Показать записи</button>
<button id="archiveRecordingBtn" class="btn ghost" type="button">Архивировать</button>
</div>
<p class="hint">Для Asterisk запись обычно импортируется автоматически после завершения звонка.</p>
<ul id="recordingsList" class="list"></ul>
<div class="recording-detail">
<div class="hint" id="recordingSelectionHint">Выберите запись для предпросмотра.</div>
<pre id="recordingMetaOutput" class="output"></pre>
<audio id="recordingPlayer" controls preload="none"></audio>
<button id="downloadRecordingBtn" class="btn ghost" type="button">Скачать</button>
</div>
</article>
</section>
</main>
</section>
</div>
<script src="/supervisor/assets/app.js?v=track34-supervisor-ru-polish"></script>
</body>
</html>