From 61b0dc2416a926035ccc6e56c236887af760bfea Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 1 Oct 2026 13:11:16 +0300 Subject: [PATCH] =?UTF-8?q?:sparkles:=20feat(webchat):=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BD=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B9=D0=BA=D0=B8=20=D0=BE=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...azdel-oformlenie-v-nastroykah-podklyuch.md | 30 +++++ .../integrations/WebIntegrationPage.tsx | 4 +- .../appearance/AppearancePreview.tsx | 30 +++++ .../integrations/appearance/ColorSettings.tsx | 41 +++++++ .../integrations/appearance/CssSettings.tsx | 13 +++ .../integrations/appearance/IconSettings.tsx | 63 ++++++++++ .../appearance/LauncherSettings.tsx | 30 +++++ .../appearance/WebIntegrationAppearance.tsx | 38 ++++++ .../integrations/appearance/WidgetIcon.tsx | 7 ++ .../integrations/appearance/model.test.ts | 32 +++++ .../features/integrations/appearance/model.ts | 53 +++++++++ .../integrations/appearance/preview.css | 56 +++++++++ .../integrations/appearance/settings.css | 110 ++++++++++++++++++ .../integrations/appearance/useAppearance.ts | 57 +++++++++ .../src/features/integrations/model.ts | 2 + apps/internal-ui/src/i18n/en.ts | 2 + apps/internal-ui/src/i18n/ru.ts | 2 + .../src/i18n/widget-appearance.en.ts | 47 ++++++++ .../src/i18n/widget-appearance.ru.ts | 45 +++++++ apps/web-chat/src/BotIcon.tsx | 31 +---- packages/ui/src/BotIcon.tsx | 25 ++++ packages/ui/src/index.ts | 1 + 22 files changed, 689 insertions(+), 30 deletions(-) create mode 100644 .skaro/tasks/T-023-razdel-oformlenie-v-nastroykah-podklyuch.md create mode 100644 apps/internal-ui/src/features/integrations/appearance/AppearancePreview.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/ColorSettings.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/CssSettings.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/IconSettings.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/LauncherSettings.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/WebIntegrationAppearance.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/WidgetIcon.tsx create mode 100644 apps/internal-ui/src/features/integrations/appearance/model.test.ts create mode 100644 apps/internal-ui/src/features/integrations/appearance/model.ts create mode 100644 apps/internal-ui/src/features/integrations/appearance/preview.css create mode 100644 apps/internal-ui/src/features/integrations/appearance/settings.css create mode 100644 apps/internal-ui/src/features/integrations/appearance/useAppearance.ts create mode 100644 apps/internal-ui/src/i18n/widget-appearance.en.ts create mode 100644 apps/internal-ui/src/i18n/widget-appearance.ru.ts create mode 100644 packages/ui/src/BotIcon.tsx diff --git a/.skaro/tasks/T-023-razdel-oformlenie-v-nastroykah-podklyuch.md b/.skaro/tasks/T-023-razdel-oformlenie-v-nastroykah-podklyuch.md new file mode 100644 index 0000000..d0721d1 --- /dev/null +++ b/.skaro/tasks/T-023-razdel-oformlenie-v-nastroykah-podklyuch.md @@ -0,0 +1,30 @@ +--- +id: T-023 +title: Раздел «Оформление» в настройках подключения +milestone: M04 +status: done +depends_on: + - T-008 + - T-019 + - T-020 +order: 5 +spec: "0021" +created: 2026-09-29 +branch: skaro/T-023-razdel-oformlenie-v-nastroykah +--- + +## Цель + +Администратор настраивает вид виджета в разделе W3 с живым предпросмотром, ровно как в макете (R-2, R-10). + +## Критерии приёмки + +- [x] 8 пресетов цвета и «Свой HEX» с подсказкой контраста: ≥4.5:1 — ок, меньше — предупреждение, неверный HEX — подсказка формата; сохранение не запрещено +- [x] Загрузка иконок кнопки и шапки, переключатели положения, размера и формы +- [x] Поле своего CSS со ссылкой «Классы виджета» и кнопкой «Сбросить» +- [x] Предпросмотр справа меняется сразу; цвета пресетов — данные, а не сырые hex в стилях компонентов +- [x] Все тексты через t() в ru и en, вид совпадает с кадром W3, файлы в пределах NO GOD FILES + +## Итог + +Ветка перенесена на main 52524287, четыре конфликта разрешены с сохранением раздела «Данные с сайта», счетчика полей, типов и обоих словарей. Результат зафиксирован переписанным коммитом 1184c6d7; рабочая папка чистая, main является предком HEAD. После разрешения конфликтов прошли сборка internal-ui, 20 связанных тестов и браузерная проверка всех трех разделов на реальном backend. diff --git a/apps/internal-ui/src/features/integrations/WebIntegrationPage.tsx b/apps/internal-ui/src/features/integrations/WebIntegrationPage.tsx index a635948..afde9ae 100644 --- a/apps/internal-ui/src/features/integrations/WebIntegrationPage.tsx +++ b/apps/internal-ui/src/features/integrations/WebIntegrationPage.tsx @@ -10,6 +10,7 @@ import { WebIntegrationBasics } from "./WebIntegrationBasics"; import { WebIntegrationHeader } from "./WebIntegrationHeader"; import { useWebIntegration } from "./useWebIntegration"; import { SiteFieldsSection } from "./site-fields/SiteFieldsSection"; +import { WebIntegrationAppearance } from "./appearance/WebIntegrationAppearance"; type Section = "basics" | "fields" | "form" | "look" | "danger"; @@ -17,7 +18,7 @@ const sections: SectionMenuItem
[] = [ { key: "basics", label: t("portals.basics"), icon: "settings" }, { key: "fields", label: t("settings.site_data"), icon: "code" }, { key: "form", label: t("settings.pre_chat_form"), icon: "doc", disabled: true }, - { key: "look", label: t("common.appearance"), icon: "paint", disabled: true, divider: true }, + { key: "look", label: t("common.appearance"), icon: "paint", divider: true }, { key: "danger", label: t("settings.delete_connection"), icon: "trash", danger: true }, ]; @@ -55,6 +56,7 @@ export function WebIntegrationPage({ integrationId, onOpenSettings, onBack }: { + {section === "look" && } {confirmingDelete && setConfirmingDelete(false)} onDeleted={onBack} />} diff --git a/apps/internal-ui/src/features/integrations/appearance/AppearancePreview.tsx b/apps/internal-ui/src/features/integrations/appearance/AppearancePreview.tsx new file mode 100644 index 0000000..bee4346 --- /dev/null +++ b/apps/internal-ui/src/features/integrations/appearance/AppearancePreview.tsx @@ -0,0 +1,30 @@ +import { t } from "../../../i18n"; +import { SHAPE_RADIUS, type WidgetAppearance } from "./model"; +import { WidgetIcon } from "./WidgetIcon"; +import "./preview.css"; + +/** Упрощённая копия из W3; демонстрационные реплики принадлежат макету. */ +export function AppearancePreview({ appearance }: { appearance: WidgetAppearance }) { + const side = { [appearance.launcherPosition]: 16 }; + return ; +} diff --git a/apps/internal-ui/src/features/integrations/appearance/ColorSettings.tsx b/apps/internal-ui/src/features/integrations/appearance/ColorSettings.tsx new file mode 100644 index 0000000..058145e --- /dev/null +++ b/apps/internal-ui/src/features/integrations/appearance/ColorSettings.tsx @@ -0,0 +1,41 @@ +import type { CSSProperties } from "react"; + +import { fmt, t } from "../../../i18n"; +import { Icon } from "../../../shared/icons"; +import { COLOR_PRESETS, validHex, whiteContrast } from "./model"; + +export function ColorSettings({ accent, customHex, custom, onPreset, onHex }: { + accent: string; customHex: string; custom: boolean; + onPreset: (accent: string) => void; onHex: (hex: string) => void; +}) { + const invalid = custom && !validHex(customHex); + const ratio = whiteContrast(accent); + const note = invalid ? t("widget_appearance.hex_format") : t(ratio >= 4.5 + ? "widget_appearance.contrast_ok" : "widget_appearance.contrast_warning", { + ratio: fmt.number(ratio, { minimumFractionDigits: 1, maximumFractionDigits: 1 }), + }); + + return ( +
+ {t("widget_appearance.color")} +
+ {COLOR_PRESETS.map(({ hex, label }) => ( + + ))} +
+
+ + {note} +
+
+ ); +} diff --git a/apps/internal-ui/src/features/integrations/appearance/CssSettings.tsx b/apps/internal-ui/src/features/integrations/appearance/CssSettings.tsx new file mode 100644 index 0000000..5615976 --- /dev/null +++ b/apps/internal-ui/src/features/integrations/appearance/CssSettings.tsx @@ -0,0 +1,13 @@ +import { t } from "../../../i18n"; +import { helpArticleUrl } from "../../../shared/help"; + +export function CssSettings({ value, onChange }: { value: string; onChange: (css: string) => void }) { + return
+
+ + {t("widget_appearance.classes")} +
+