diff --git a/ordinis-admin-ui/src/components/layout/Sidebar.tsx b/ordinis-admin-ui/src/components/layout/Sidebar.tsx index 8d9d67c..be0ac80 100644 --- a/ordinis-admin-ui/src/components/layout/Sidebar.tsx +++ b/ordinis-admin-ui/src/components/layout/Sidebar.tsx @@ -2,7 +2,6 @@ import { useState } from 'react' import { Link, useLocation } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' import { - Home, Database, FileText, ClipboardList, @@ -112,12 +111,14 @@ export function Sidebar() { webhooks.data?.filter((w) => w.active).length ?? 0 // Flat list per redesign/compact.html — no section labels, /graph removed - // (now reachable via catalog toolbar "Граф ⇄" button). Order: Главная → - // Справочники → Мои черновики → На ревью → Outbox → Webhooks → Аудит → Поиск. + // (now reachable via catalog toolbar "Граф ⇄" button). QA bug #8: «Главная» + // удалён — он вёл на `/` который сразу redirect'нет на /dictionaries; в + // сайдбаре подсветка слетала (active state на /dictionaries, не на /). + // Logo сверху уже даёт home affordance — duplicate nav item только сбивал. + // Order: Справочники → Мои черновики → На ревью → Outbox → Webhooks → Аудит → Поиск. const sections: NavSection[] = [ { items: [ - { to: '/', label: t('nav.home'), icon: Home }, { to: '/dictionaries', label: t('nav.dictionaries'), @@ -484,11 +485,11 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () => const webhooksActive = webhooks.data?.filter((w) => w.active).length ?? 0 - // Flat list per redesign — same as desktop Sidebar above. + // Flat list per redesign — same as desktop Sidebar above. QA bug #8: + // «Главная» удалён (redirect → /dictionaries сбивал active state). const sections: NavSection[] = [ { items: [ - { to: '/', label: t('nav.home'), icon: Home }, { to: '/dictionaries', label: t('nav.dictionaries'), diff --git a/ordinis-admin-ui/src/i18n.ts b/ordinis-admin-ui/src/i18n.ts index 44a1d0a..4a3f38d 100644 --- a/ordinis-admin-ui/src/i18n.ts +++ b/ordinis-admin-ui/src/i18n.ts @@ -352,8 +352,19 @@ i18n 'dict.list.col.version': 'Версия', 'dict.list.col.locales': 'Локали', 'dict.list.col.records': 'Записи', + // Compact row table (catalog) — column headers per redesign/compact.html. + // QA bug #5: defaultValue inline в коде падал на ru-text при EN locale; + // выносим в i18n чтобы переключение языка реально влияло на заголовки. + 'dict.col.title': 'Название', + 'dict.col.id': 'id', + 'dict.col.bundle': 'bundle', + 'dict.col.records': 'записей', + 'dict.col.updated': 'изменён', + 'dict.col.outgoingFk': 'Ссылается (исходящие FK)', + 'dict.col.incomingFk': 'Используют (входящие FK)', // List view (handoff D) 'dict.list.bundle.all': 'все', + 'dict.list.graph': 'Граф', 'dict.list.deps.only': 'Только со связями', 'dict.list.records.short': 'справочн.', 'dict.list.fk.references': '→ ссылается', @@ -485,7 +496,7 @@ i18n 'schema.bundle': 'Bundle', 'schema.bundleHint': 'Обычно cuod', 'schema.template.label': 'Создать по шаблону', - 'schema.template.hint': 'Скопирует схему, scope и locale выбранного словаря. Имя оставьте уникальное; версия сбросится на 1.0.0.', + 'schema.template.hint': 'Скопирует схему, scope и локали выбранного словаря. Укажите уникальное имя — версия сбросится на 1.0.0.', 'schema.template.placeholder': '— без шаблона —', 'schema.version': 'Версия схемы', 'schema.supportedLocales': 'Поддерживаемые локали', @@ -1209,6 +1220,15 @@ i18n 'dict.list.col.version': 'Version', 'dict.list.col.locales': 'Locales', 'dict.list.col.records': 'Records', + // Compact row table (catalog) — column headers EN. + 'dict.col.title': 'Name', + 'dict.col.id': 'id', + 'dict.col.bundle': 'bundle', + 'dict.col.records': 'records', + 'dict.col.updated': 'updated', + 'dict.col.outgoingFk': 'References (outgoing FK)', + 'dict.col.incomingFk': 'Used by (incoming FK)', + 'dict.list.graph': 'Graph', // List view (handoff D) 'dict.list.bundle.all': 'all', 'dict.list.deps.only': 'Only with links', diff --git a/ordinis-admin-ui/src/routes/dictionaries.$name.tsx b/ordinis-admin-ui/src/routes/dictionaries.$name.tsx index 830d391..6fc57e1 100644 --- a/ordinis-admin-ui/src/routes/dictionaries.$name.tsx +++ b/ordinis-admin-ui/src/routes/dictionaries.$name.tsx @@ -800,8 +800,14 @@ function DictionaryDetail() { Info panel — sticky слева с dict metadata + relations. На + * gap-3 — closer panels per user feedback ("ближе расположены"). + * QA bug #1: 1fr → minmax(0,1fr) — без minmax(0,…) grid track имеет + * implicit min-content (auto), что позволяет records table вырасти + * шире viewport. После закрытия Radix Dialog снимается body padding- + * right (~15px), 1fr пересчитывается и ломает layout (исчезает + * первая колонка под overflow-hidden card). minmax(0,1fr) фиксирует + * нижнюю границу 0 → overflow-x-auto в Table primitive работает. */} +
{detailQuery.data && ( + * page chrome. + * QA bug #1: overflow-hidden убран — клиппил records table при ширине + * контента больше колонки. Table primitive сам имеет overflow-x-auto; + * tabs ниже получают свой scroll контейнер. Card rounding продолжает + * работать через border-radius на самом div'е. */} +
{/* Editor tabs per handoff Stage 3.4 + redesign: * - counts в labels (Записи 127, Связи 6, Поля 12) @@ -1189,7 +1199,7 @@ function DictionaryDetail() { ) : null}
- + {recordDisplayName( r.data, detailQuery.data?.defaultLocale ?? 'ru-RU', @@ -1981,32 +1991,42 @@ function EditorTabBar({ { id: 'events', label: t('editor.tab.events', { defaultValue: 'События' }) }, { id: 'history', label: t('editor.tab.history', { defaultValue: 'История' }) }, ] + // QA bug #3: на 1075px tabs (6 шт.) + actions (3 кнопки) переполняли flex + // row → "+ Создать" обрезалось до "+ Соз". Решение: actions выносим в + // отдельный shrink-0 контейнер, tabs кладём в overflow-x-auto wrapper + // с min-w-0 → tabs скроллятся горизонтально если cramped, actions всегда + // видны полностью. whitespace-nowrap на tab buttons чтобы текст не + // переносился в кружок счётчика. return ( -
- {tabs.map((tab) => { - const isActive = active === tab.id - const count = counts?.[tab.id] - return ( - - ) - })} - {actions &&
{actions}
} +
+
+ {tabs.map((tab) => { + const isActive = active === tab.id + const count = counts?.[tab.id] + return ( + + ) + })} +
+ {actions && ( +
{actions}
+ )}
) } diff --git a/ordinis-admin-ui/src/routes/dictionaries.index.tsx b/ordinis-admin-ui/src/routes/dictionaries.index.tsx index ab7297f..1d0a495 100644 --- a/ordinis-admin-ui/src/routes/dictionaries.index.tsx +++ b/ordinis-admin-ui/src/routes/dictionaries.index.tsx @@ -483,9 +483,12 @@ function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) { sort.key === key ? (sort.dir === 'asc' ? ' ↑' : ' ↓') : '' return ( - // No outer rounded/border wrapper — outer panel handles that (toolbar + table - // в одной card). Just table with white header per user feedback. - + // QA bug #4: оборачиваем в overflow-x-auto wrapper чтобы при PUBLIC+INTERNAL + // с extra columns table не клипал name'ы под parent overflow-hidden card. + // table:min-w[760px] — нижняя граница ширины (sum min-content всех колонок), + // ниже неё включается horizontal scroll. +
+
toggleSort('title')} active={sort.key === 'title'}> @@ -530,6 +533,7 @@ function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) { ))}
+
) } @@ -623,8 +627,10 @@ function DictRow({ d, t }: { d: DictionaryDefinition; t: TFunc }) { onKeyDown={handleKey} className="relative border-b border-line-2 last:border-b-0 cursor-pointer bg-surface hover:bg-surface-2 focus-visible:outline-none focus-visible:bg-accent-bg/30 transition-colors" > - {/* name + subtitle (description short) + scope-color left stripe */} - + {/* name + subtitle (description short) + scope-color left stripe. + * QA bug #4: min-w-[220px] — обеспечивает что name column не схлопывается + * до букв. При недостатке ширины table получает horizontal scroll. */} + {/* 3px scope stripe — absolutely positioned на левом краю row. * td имеет relative чтобы before работало в table context. */} , DialogContentProps >(function DialogContent({ className, children, size = 'md', hideClose, ...props }, ref) { + // duration-150 (вместо 200) — анимация закрытия укладывается в один frame + // на 60Hz без видимого "застывания" между state=closed и unmount. QA bug #2: + // "модал становится полупрозрачным, но остаётся отрисованным в течение + // секунд" — на slow CPU 200ms ощущается как несколько кадров; cutoff 150ms + // совпадает с tw-animate-css дефолтами и убирает рассинхрон визуала/DOM. return (