From 1dc7e796129355964535fa53cfe808324680d4b3 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 10 Jul 2026 13:46:43 +0300 Subject: [PATCH] =?UTF-8?q?chore(ai):=20=D1=83=D0=BF=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=B4=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D1=87=D0=B0=D1=82=D0=B0=20(fr?= =?UTF-8?q?ontend=20+=20docs)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Удалён фронтенд-only статический прототип тестового чата (features/ai/test-chat/) и все точки его подключения: роут aiTestChat, таб поднавигации, кнопки в заголовках AI-агента/release и таблице агентов, CSS-классы. Мёртвая ветка логики статусов релиза (параметр tested) убрана, тексты про тест-чат заменены на нейтральные. Baseline-прототип Тестовый чат.dc.html удалён; упразднение AI-04 зафиксировано в SPEC-HUB-0001, SPEC-HUB-0004, SPEC-HUB-0005. --- .../src/features/ai/AgentsTable.tsx | 6 +- .../src/features/ai/AiAgentsPage.tsx | 5 +- apps/internal-ui/src/features/ai/AiSubnav.tsx | 1 - .../ai/detail/AiAgentDetailHeader.tsx | 4 - .../features/ai/detail/AiAgentDetailPage.tsx | 5 +- .../ai/release/ProductAIReleaseHeader.tsx | 4 - .../ai/release/ProductAIReleasePage.tsx | 5 +- .../src/features/ai/release/model.ts | 8 +- .../src/features/ai/release/styles.css | 10 - apps/internal-ui/src/features/ai/styles.css | 7 - .../features/ai/test-chat/AiTestChatPage.tsx | 19 -- .../ai/test-chat/TestChatDiagnostics.tsx | 52 ----- .../features/ai/test-chat/TestChatSidebar.tsx | 56 ----- .../ai/test-chat/TestConversation.tsx | 97 -------- .../features/ai/test-chat/conversation.css | 201 ---------------- .../src/features/ai/test-chat/diagnostics.css | 155 ------------- .../src/features/ai/test-chat/model.ts | 18 -- .../src/features/ai/test-chat/styles.css | 167 -------------- apps/internal-ui/src/layout/Shell.tsx | 2 +- .../src/layout/ShellRouteContent.tsx | 8 +- apps/internal-ui/src/router.ts | 2 - apps/internal-ui/src/routes.ts | 1 - apps/internal-ui/src/styles.css | 3 - apps/internal-ui/src/types.ts | 2 +- design/baseline/Тестовый чат.dc.html | 218 ------------------ 25 files changed, 14 insertions(+), 1042 deletions(-) delete mode 100644 apps/internal-ui/src/features/ai/test-chat/AiTestChatPage.tsx delete mode 100644 apps/internal-ui/src/features/ai/test-chat/TestChatDiagnostics.tsx delete mode 100644 apps/internal-ui/src/features/ai/test-chat/TestChatSidebar.tsx delete mode 100644 apps/internal-ui/src/features/ai/test-chat/TestConversation.tsx delete mode 100644 apps/internal-ui/src/features/ai/test-chat/conversation.css delete mode 100644 apps/internal-ui/src/features/ai/test-chat/diagnostics.css delete mode 100644 apps/internal-ui/src/features/ai/test-chat/model.ts delete mode 100644 apps/internal-ui/src/features/ai/test-chat/styles.css delete mode 100644 design/baseline/Тестовый чат.dc.html diff --git a/apps/internal-ui/src/features/ai/AgentsTable.tsx b/apps/internal-ui/src/features/ai/AgentsTable.tsx index 8738e71..f3fde19 100644 --- a/apps/internal-ui/src/features/ai/AgentsTable.tsx +++ b/apps/internal-ui/src/features/ai/AgentsTable.tsx @@ -1,9 +1,8 @@ import { Dropdown } from "antd"; -import type { RouteKey } from "../../types"; import { Icon } from "../../shared/icons"; import { StatusPill } from "../../shared/ui"; -import { Button, MonoLink, ToneBadge } from "../../shared/ui-controls"; +import { MonoLink, ToneBadge } from "../../shared/ui-controls"; import { productAccent } from "../../shared/utils"; import { publishedRelease, releaseName, type AiAgent, type AiRelease } from "./model"; @@ -13,7 +12,6 @@ export function AgentsTable({ menuId, setMenuId, toggleActive, - setRoute, openAgent, openRelease, }: { @@ -22,7 +20,6 @@ export function AgentsTable({ menuId: number | null; setMenuId: (id: number | null) => void; toggleActive: (agent: AiAgent) => void; - setRoute: (route: RouteKey) => void; openAgent: (agentId: number) => void; openRelease: (releaseId: number) => void; }) { @@ -84,7 +81,6 @@ export function AgentsTable({ —
- void; setRoute: (route: RouteKey) => void; openAgentCreate: (productCode: string | null) => void; openAgent: (agentId: number) => void; openRelease: (releaseId: number) => void }) { +export function AiAgentsPage({ agents, releases, reload, openAgentCreate, openAgent, openRelease }: { agents: AiAgent[]; releases: AiRelease[]; reload: () => void; openAgentCreate: (productCode: string | null) => void; openAgent: (agentId: number) => void; openRelease: (releaseId: number) => void }) { const [menuId, setMenuId] = useState(null); async function toggleActive(agent: AiAgent) { @@ -22,7 +21,7 @@ export function AiAgentsPage({ agents, releases, reload, setRoute, openAgentCrea
openAgentCreate(null)}>Создать агента} /> - +
); } diff --git a/apps/internal-ui/src/features/ai/AiSubnav.tsx b/apps/internal-ui/src/features/ai/AiSubnav.tsx index 73059bf..70f46d4 100644 --- a/apps/internal-ui/src/features/ai/AiSubnav.tsx +++ b/apps/internal-ui/src/features/ai/AiSubnav.tsx @@ -3,7 +3,6 @@ import { UnderlineTabs } from "../../shared/ui-controls"; const ITEMS: Array<{ key: RouteKey; label: string; disabled?: boolean }> = [ { key: "aiAgents", label: "AI-агенты" }, - { key: "aiTestChat", label: "Тестовый чат" }, { key: "aiUsage", label: "Использование AI", disabled: true }, ]; diff --git a/apps/internal-ui/src/features/ai/detail/AiAgentDetailHeader.tsx b/apps/internal-ui/src/features/ai/detail/AiAgentDetailHeader.tsx index 9dd8dcb..3e39bff 100644 --- a/apps/internal-ui/src/features/ai/detail/AiAgentDetailHeader.tsx +++ b/apps/internal-ui/src/features/ai/detail/AiAgentDetailHeader.tsx @@ -2,20 +2,17 @@ import { Icon } from "../../../shared/icons"; import { StatusPill } from "../../../shared/ui"; import { Button } from "../../../shared/ui-controls"; import { productAccent } from "../../../shared/utils"; -import type { RouteKey } from "../../../types"; import { dailyBudget, publishedRelease, releaseDisplayName, type AiAgentDetail, type AiReleaseFull } from "./model"; export function AiAgentDetailHeader({ agent, releases, - setRoute, openRelease, createRelease, onEditChannel, }: { agent: AiAgentDetail; releases: AiReleaseFull[]; - setRoute: (route: RouteKey) => void; openRelease: (releaseId: number) => void; createRelease: () => void; onEditChannel: () => void; @@ -40,7 +37,6 @@ export function AiAgentDetailHeader({
-
diff --git a/apps/internal-ui/src/features/ai/detail/AiAgentDetailPage.tsx b/apps/internal-ui/src/features/ai/detail/AiAgentDetailPage.tsx index 459744c..11aca0d 100644 --- a/apps/internal-ui/src/features/ai/detail/AiAgentDetailPage.tsx +++ b/apps/internal-ui/src/features/ai/detail/AiAgentDetailPage.tsx @@ -4,7 +4,6 @@ import { api } from "../../../api/client"; import { Icon } from "../../../shared/icons"; import { EmptyState, LoadingState } from "../../../shared/ui"; import { Button, UnderlineTabs } from "../../../shared/ui-controls"; -import type { RouteKey } from "../../../types"; import { AiAgentDetailHeader } from "./AiAgentDetailHeader"; import { AgentEditForm } from "./AgentEditForm"; import { ChannelEditForm } from "./ChannelEditForm"; @@ -16,7 +15,7 @@ import { AiAgentReleasesTab } from "./AiAgentReleasesTab"; import { agentTabs, type AgentTab, type AiReleaseFull } from "./model"; import { useAiAgentDetail } from "./useAiAgentDetail"; -export function AiAgentDetailPage({ agentId, setRoute, openRelease, onAgentLoaded }: { agentId: number | null; setRoute: (route: RouteKey) => void; openRelease: (releaseId: number) => void; onAgentLoaded: (name: string | null) => void }) { +export function AiAgentDetailPage({ agentId, openRelease, onAgentLoaded }: { agentId: number | null; openRelease: (releaseId: number) => void; onAgentLoaded: (name: string | null) => void }) { const { agent, releases, knowledge, prompts, loading, error, reload } = useAiAgentDetail(agentId); const [tab, setTab] = useState("overview"); const [editOpen, setEditOpen] = useState(false); @@ -50,7 +49,7 @@ export function AiAgentDetailPage({ agentId, setRoute, openRelease, onAgentLoade return (
- setChannelEdit(true)} /> + setChannelEdit(true)} /> {(tab === "instructions" || tab === "knowledge") && (
diff --git a/apps/internal-ui/src/features/ai/release/ProductAIReleaseHeader.tsx b/apps/internal-ui/src/features/ai/release/ProductAIReleaseHeader.tsx index 48a3fc4..aadd185 100644 --- a/apps/internal-ui/src/features/ai/release/ProductAIReleaseHeader.tsx +++ b/apps/internal-ui/src/features/ai/release/ProductAIReleaseHeader.tsx @@ -1,6 +1,5 @@ import { Icon } from "../../../shared/icons"; import { Button } from "../../../shared/ui-controls"; -import type { RouteKey } from "../../../types"; import type { AiReleaseFull } from "../detail/model"; import { formatDateTime, releaseLabel, releaseState, type ReleaseCheck } from "./model"; @@ -8,13 +7,11 @@ export function ProductAIReleaseHeader({ canPublish, checks, release, - setRoute, startPublish, }: { canPublish: boolean; checks: ReleaseCheck[]; release: AiReleaseFull; - setRoute: (route: RouteKey) => void; startPublish: () => void; }) { const state = releaseState(checks, release.status === "PUBLISHED"); @@ -38,7 +35,6 @@ export function ProductAIReleaseHeader({
- diff --git a/apps/internal-ui/src/features/ai/release/ProductAIReleasePage.tsx b/apps/internal-ui/src/features/ai/release/ProductAIReleasePage.tsx index 0bf55d8..875deb6 100644 --- a/apps/internal-ui/src/features/ai/release/ProductAIReleasePage.tsx +++ b/apps/internal-ui/src/features/ai/release/ProductAIReleasePage.tsx @@ -2,7 +2,6 @@ import { useEffect, useMemo, useState } from "react"; import { api } from "../../../api/client"; import { EmptyState, LoadingState } from "../../../shared/ui"; -import type { RouteKey } from "../../../types"; import { ProductAIReleaseHeader } from "./ProductAIReleaseHeader"; import { PublishReleaseModal } from "./PublishReleaseModal"; import { ReleaseComposition } from "./ReleaseComposition"; @@ -13,11 +12,9 @@ import { useProductAIRelease } from "./useProductAIRelease"; export function ProductAIReleasePage({ onReleaseLoaded, releaseId, - setRoute, }: { onReleaseLoaded: (name: string | null) => void; releaseId: number | null; - setRoute: (route: RouteKey) => void; }) { const { release, releases, knowledge, prompts, loading, error, reload } = useProductAIRelease(releaseId); const [modalOpen, setModalOpen] = useState(false); @@ -54,7 +51,7 @@ export function ProductAIReleasePage({ return (
- setModalOpen(true)} /> + setModalOpen(true)} />
diff --git a/apps/internal-ui/src/features/ai/release/model.ts b/apps/internal-ui/src/features/ai/release/model.ts index 1ef383e..a5f95fa 100644 --- a/apps/internal-ui/src/features/ai/release/model.ts +++ b/apps/internal-ui/src/features/ai/release/model.ts @@ -79,20 +79,18 @@ export function canPublishRelease(release: AiReleaseFull, checks: ReleaseCheck[] return release.status === "DRAFT" && checks.every((check) => check.tone === "ok"); } -export function validationBanner(checks: ReleaseCheck[], tested = true) { +export function validationBanner(checks: ReleaseCheck[]) { const summary = checksSummary(checks); if (summary.tone === "error") return { title: "Найдены ошибки валидации", note: "Исправьте отмеченные пункты в составе версии, затем перезапустите проверку.", tone: "error" as const }; if (summary.tone === "warn") return { title: "Черновик версии неполный", note: "Заполните обязательные разделы, чтобы перейти к проверке.", tone: "warn" as const }; - if (!tested) return { title: "Готово к тестированию", note: "Проверки пройдены. Запустите тест-чат перед публикацией.", tone: "ok" as const }; - return { title: "Готово к публикации", note: "Все проверки пройдены, протестировано в тест-чате. Можно публиковать.", tone: "ok" as const }; + return { title: "Готово к публикации", note: "Все проверки пройдены. Можно публиковать.", tone: "ok" as const }; } -export function releaseState(checks: ReleaseCheck[], published: boolean, tested = true): { className: string; label: string } { +export function releaseState(checks: ReleaseCheck[], published: boolean): { className: string; label: string } { const summary = checksSummary(checks); if (published) return { className: "published", label: "Опубликован" }; if (summary.tone === "error") return { className: "errors", label: "Ошибки валидации" }; if (summary.tone === "warn") return { className: "incomplete", label: "Неполный черновик" }; - if (!tested) return { className: "ready-test", label: "Готов к тестированию" }; return { className: "ready-publish", label: "Готов к публикации" }; } diff --git a/apps/internal-ui/src/features/ai/release/styles.css b/apps/internal-ui/src/features/ai/release/styles.css index 5194c6c..d39d36c 100644 --- a/apps/internal-ui/src/features/ai/release/styles.css +++ b/apps/internal-ui/src/features/ai/release/styles.css @@ -86,16 +86,6 @@ background: #52c41a; } -.release-status-badge.ready-test { - color: #0958d9; - background: #e6f4ff; - border-color: #91caff; -} - -.release-status-badge.ready-test span { - background: #1677ff; -} - .release-status-badge.incomplete { color: #8c8c8c; background: #f5f5f5; diff --git a/apps/internal-ui/src/features/ai/styles.css b/apps/internal-ui/src/features/ai/styles.css index 4a2e3c7..0ed486a 100644 --- a/apps/internal-ui/src/features/ai/styles.css +++ b/apps/internal-ui/src/features/ai/styles.css @@ -101,13 +101,6 @@ gap: 7px; } -.ai-test-chat-btn { - height: 32px; - padding: 0 12px; - font-size: 12.5px; - white-space: nowrap; -} - .ai-table-footer { display: flex; align-items: center; diff --git a/apps/internal-ui/src/features/ai/test-chat/AiTestChatPage.tsx b/apps/internal-ui/src/features/ai/test-chat/AiTestChatPage.tsx deleted file mode 100644 index 71440b7..0000000 --- a/apps/internal-ui/src/features/ai/test-chat/AiTestChatPage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useState } from "react"; - -import type { TestChannel } from "./model"; -import { TestChatDiagnostics } from "./TestChatDiagnostics"; -import { TestChatSidebar } from "./TestChatSidebar"; -import { TestConversation } from "./TestConversation"; - -export function AiTestChatPage() { - const [channel, setChannel] = useState("MAX"); - const [scenario, setScenario] = useState("box"); - - return ( -
- - - -
- ); -} diff --git a/apps/internal-ui/src/features/ai/test-chat/TestChatDiagnostics.tsx b/apps/internal-ui/src/features/ai/test-chat/TestChatDiagnostics.tsx deleted file mode 100644 index 99eaa18..0000000 --- a/apps/internal-ui/src/features/ai/test-chat/TestChatDiagnostics.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Icon } from "../../../shared/icons"; -import { diagnosticChunks } from "./model"; - -export function TestChatDiagnostics() { - return ( -
-
-

Диагностика

- Последний ответ AI -
-
-
-
Tokens1 284in 940 · out 344
-
Cost₽0,42latency 3,1с
-
- - ИСПОЛЬЗОВАННЫЕ ФРАГМЕНТЫ -
- {diagnosticChunks.map((chunk) => ( -
-
{chunk.source}{chunk.score}
-

{chunk.text}

-
- ))} -
- - ВЫЗВАННЫЕ ИНСТРУМЕНТЫ -
- - -
- - ПЕРЕДАЧА ОПЕРАТОРУ -
Передачи оператору не было — сценарий в рамках агента.
-
-
- ); -} - -function PanelTitle({ children }: { children: string }) { - return
{children}
; -} - -function ToolCall({ meta, name, time }: { meta: string; name: string; time: string }) { - return ( -
- -
{name}{meta}
- {time} -
- ); -} diff --git a/apps/internal-ui/src/features/ai/test-chat/TestChatSidebar.tsx b/apps/internal-ui/src/features/ai/test-chat/TestChatSidebar.tsx deleted file mode 100644 index e0f698f..0000000 --- a/apps/internal-ui/src/features/ai/test-chat/TestChatSidebar.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Icon } from "../../../shared/icons"; -import { testChannels, testScenarios, type TestChannel } from "./model"; - -export function TestChatSidebar({ - channel, - scenario, - setChannel, - setScenario, -}: { - channel: TestChannel; - scenario: string; - setChannel: (channel: TestChannel) => void; - setScenario: (scenario: string) => void; -}) { - return ( -
-
-
ЧЕРНОВИК ВЕРСИИ
- -
- - FirePage Sales · gpt-4o-mini · temp 0.4 -
- -
КАНАЛ СИМУЛЯЦИИ
-
- {testChannels.map((item) => ( - - ))} -
- -
ГОТОВЫЕ СЦЕНАРИИ
-
- {testScenarios.map((item) => ( - - ))} -
-
-
- -
-
- ); -} diff --git a/apps/internal-ui/src/features/ai/test-chat/TestConversation.tsx b/apps/internal-ui/src/features/ai/test-chat/TestConversation.tsx deleted file mode 100644 index d4cac3a..0000000 --- a/apps/internal-ui/src/features/ai/test-chat/TestConversation.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { useLayoutEffect, useRef, useState, type ChangeEvent, type ReactNode } from "react"; - -import { Icon } from "../../../shared/icons"; -import type { TestChannel } from "./model"; - -const channelLabel: Record = { - MAX: "MAX", - TG: "Telegram", - WEB: "Web Chat", -}; - -export function TestConversation({ channel }: { channel: TestChannel }) { - const [message, setMessage] = useState(""); - const textareaRef = useRef(null); - - const resizeMessageInput = () => { - const textarea = textareaRef.current; - if (!textarea) return; - const styles = window.getComputedStyle(textarea); - const lineHeight = Number.parseFloat(styles.lineHeight); - const paddingTop = Number.parseFloat(styles.paddingTop); - const paddingBottom = Number.parseFloat(styles.paddingBottom); - const maxHeight = lineHeight * 12 + paddingTop + paddingBottom; - - textarea.style.height = "auto"; - const nextHeight = Math.min(textarea.scrollHeight, maxHeight); - textarea.style.height = `${nextHeight}px`; - textarea.style.overflowY = textarea.scrollHeight > maxHeight ? "auto" : "hidden"; - }; - - useLayoutEffect(() => { - resizeMessageInput(); - }, [message]); - - const handleMessageChange = (event: ChangeEvent) => { - setMessage(event.target.value); - }; - - return ( -
-
-
- -
- Test conversation - Симуляция клиента · сообщений: 4 -
-
- Не создаёт Contact, Order или Payment -
- -
-
Сценарий: «Покупка коробки» · канал {channelLabel[channel]}
- - - Здравствуйте! FirePage — это готовый нишевой сайт под ключ. Коробка стоит ₽4 900 единоразово. Дополнительно можно подключить годовую поддержку за ₽1 470. Под визитку отлично подходит — оформить? - - - - Отлично! Подготовил оформление коробки FirePage за ₽4 900. Вызываю оформление покупки — в реальном диалоге клиент получил бы ссылку на оплату. - -
- -
-
- - -
-
-
- - - -
-

Диагностика

Последний ответ AI
-
- - -
-
Tokens
1 284
in 940 · out 344
-
Cost
₽0,42
latency 3,1с
-
- - -
ИСПОЛЬЗОВАННЫЕ CHUNKS
-
- -
-
{{ c.source }}{{ c.score }}
-
{{ c.text }}
-
-
-
- - -
ВЫЗВАННЫЕ TOOLS
-
-
get_offers
product=firepage → 2 offers
120мс
-
create_checkout
offer=OFR-FP-BOX · ₽4 900 (симуляция)
88мс
-
- - -
HANDOFF
-
Передачи оператору не было — сценарий в рамках агента.
- -
-
- -
-
-
- - - -