From eca0e526673dfd4ec0038c3d083ee0b7edec90b8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 13 Sep 2026 03:59:10 +0300 Subject: [PATCH] =?UTF-8?q?:recycle:=20refactor(ui):=20=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=20=D1=81=D0=B5=D0=BB=D0=B5=D0=BA=D1=82=20=D0=B8=20=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D0=BD=20=D1=81=D0=BA=D0=B8=D0=BD=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=B5=D0=B9=20=D0=BD=D0=B0=20=D0=B2=D1=81=D1=91=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Карточка агента рисовала свои выпадающие селекты: «Группа» и «Провайдер» жили на собственной вёрстке и своём CSS, а рядом, в модалке создания агента, стоял общий SelectField. Два стандарта одного элемента успели разойтись в высоте, подложке, размере текста, подписи и фокусе. Общий SelectField научился тому, из-за чего карточка писала свой: метке перед значением (точка группы), невалидному состоянию и режиму без прав правки, где значение показывается текстом. Форм у него две — обычный селект и бокс, который держит рамку, когда внутрь нужно поставить точку или текст вместо контрола. Карточка перешла на него, локальный скин удалён, кольцо фокуса кадра G2 сохранено. Геометрия базлайна v2 (поле 36px на подложке n-10, текст 13.5px, подпись 12.5/600) стала дефолтом shared/form-controls.css. До этого дефолтом были 38px на surface-card, а базлайн добирали поверх профиль, настройки и модалка создания агента — вместе с карточкой четыре копии одного поля. Копии сняты, мобильные 44px тоже общие. Заодно шеврон встал по центру поля: его отступ считался от прежних 38px. Справочник дизайн-системы обновлён следом, чтобы не описывать геометрию, которой в приложении больше нет. Co-Authored-By: Claude Opus 5 --- .../src/features/agents/AgentDetailPage.tsx | 58 ++++----- .../src/features/agents/styles.css | 41 +----- .../src/features/profile/styles.css | 24 +--- .../src/features/settings/styles.css | 26 +--- apps/internal-ui/src/shared/form-controls.css | 119 ++++++++++++++---- apps/internal-ui/src/shared/form-controls.tsx | 38 +++++- .../design-system/assets/component-forms.css | 80 ++++++++++-- design/design-system/components.html | 3 + 8 files changed, 228 insertions(+), 161 deletions(-) diff --git a/apps/internal-ui/src/features/agents/AgentDetailPage.tsx b/apps/internal-ui/src/features/agents/AgentDetailPage.tsx index 8105a70..b28fa29 100644 --- a/apps/internal-ui/src/features/agents/AgentDetailPage.tsx +++ b/apps/internal-ui/src/features/agents/AgentDetailPage.tsx @@ -5,6 +5,7 @@ import { LANGUAGES } from "@chatballs/shared"; import { api, ApiError } from "../../api/client"; import { ChannelGlyph } from "../../shared/badges"; +import { SelectField } from "../../shared/form-controls"; import { Icon } from "../../shared/icons"; import { EmptyState, ErrorScreen, LoadingState } from "../../shared/ui"; import { BackLink, Button, CopyButton } from "../../shared/ui-controls"; @@ -509,25 +510,16 @@ function AssignmentCard({ card, groups, canManage, busy, apply }: { ? setName(event.target.value)} onBlur={() => { if (dirty) void apply({ name: name.trim() }); }} /> : {card.name}} - + } + disabled={busy} + label={t("common.group")} + readOnly={!canManage} + readOnlyText={card.groupName ?? t("common.no_group")} + value={card.groupId === null ? "" : String(card.groupId)} + onChange={(next) => void apply({ groupId: next ? Number(next) : null })} + options={[["", t("common.no_group")], ...groups.map((group) => [String(group.id), group.name] as [string, string])]} + /> ); @@ -558,24 +550,16 @@ function ModelCard({ card, providers, canManage, busy, apply }: {

{t("common.model")}

{t("ai.provider_key_lives_under_settings")}

- + void apply({ providerIntegrationId: next ? Number(next) : null })} + options={[["", t("ai.not_selected")], ...providers.map((item) => [String(item.id), item.name] as [string, string])]} + />