1. DateRangePicker popover. PopoverContent имел default w-72 (288px),
а DayPicker с numberOfMonths={2} требует ~560px. Из-за этого второй
месяц переносился под первый, popover вылазил поверх таблицы аудита
и кнопок «Сбросить/Применить». Override на p-3 w-auto max-w-[min(95vw,640px)]
— два месяца теперь side-by-side, popover floats как должно.
2. Панель фильтров /audit. Было: 3-col grid с 4 input'ами + DateRange
span'ит 2 cols на отдельной строке. Стало: на xl (≥1280px) все 5
полей inline в одну строку (lg → 3 cols, sm → 2, mobile → 1).
Padding p-4 space-y-3 → p-3 space-y-2. Убран border-t на action row
— визуальный шум.
Изменения только на /audit (date-range-picker — shared компонент,
но используется только в audit-фильтрах сейчас; рендер других мест
не страдает — popover просто шире, w-auto не ломает узкий контент).
1. reviews.tsx: ReviewDrawer звал useLiveRecord для ЛЮБОГО черновика,
включая CREATE. У CREATE-черновика live-записи нет по определению →
GET /dictionaries/{id}/records/{key} → 404 (красный шум в консоли на
каждый CREATE-draft). Теперь для CREATE запрос не запускается, блок
«текущая версия» показывает «нет текущей версии».
2. modal.tsx: Modal рендерил DialogTitle только при наличии prop title.
Без title Radix писал в консоль «DialogContent requires a DialogTitle»
+ warning про aria-describedby. Теперь Modal всегда рендерит DialogTitle
(sr-only fallback) и гасит aria-describedby когда описания нет.
Восемь дефектов из QA-отчёта одним MR.
#1 — Records-table теряла первую колонку после закрытия Create-modal.
Корень: lg:grid-cols-[220px_1fr] без minmax(0,…) — 1fr имел implicit
min-content (=max-content), позволяя table вырасти шире viewport.
После закрытия Radix Dialog снималось body padding-right (~15px), grid
пересчитывался и клиппил колонку под overflow-hidden card.
Fix: grid-cols-[220px_minmax(0,1fr)] + убран overflow-hidden с card
(Table primitive имеет свой overflow-x-auto).
#2 — Esc закрывал modal с задержкой (визуал/DOM рассинхрон).
duration-200 на slow CPU ощущался как несколько кадров между state=closed
и unmount. Снижено до duration-150 — совпадает с tw-animate-css дефолтами.
#3 — «+ Создать» обрезалось до «+ Соз» на ~1075px на detail-странице.
EditorTabBar: 6 tabs + 3 кнопки actions в одном flex row без shrink-0
давили друг друга. Решение: tabs в overflow-x-auto wrapper с min-w-0,
actions в shrink-0 контейнере — tabs скроллятся, actions всегда видны
полностью. scrollbar скрыт визуально (height-консистентность).
#4 — Обрезка названий в records-table при PUBLIC+INTERNAL.
Добавлен div.overflow-x-auto вокруг table + min-w-[760px] на table +
min-w-[180px] max-w-[320px] truncate на name cell. Catalog table также
получает min-w-[220px] на name column.
#5 — Заголовки колонок и кнопка «Граф» не локализованы на EN.
defaultValue:'Название' inline в коде падал на ru-text при EN locale.
Вынесены в i18n: dict.col.title/id/bundle/records/updated/outgoingFk/
incomingFk + dict.list.graph для обеих локалей.
#6 — Modal backdrop почти невидим.
bg-black/40 + 1px blur → bg-black/55 + 2px blur. Заметно темнее при
сохранении читабельности фона, фокус на modal.
#7 — Неестественная подсказка в форме «Создать»:
«Имя оставьте уникальное; версия сбросится на 1.0.0» →
«Укажите уникальное имя — версия сбросится на 1.0.0».
#8 — «Главная» в сайдбаре сбивала active state.
Клик на «Главную» вёл на `/` → redirect на /dictionaries → подсветка
уезжала на «Справочники». Logo сверху уже даёт home affordance, поэтому
duplicate nav item удалён (desktop + mobile).
Per user — GlobeLoader должен показывать реальные континенты (Asia/Africa/
Australia outline) с rotating orthographic projection, как в прототипе
Downloads/Globe loader/Globe Loader.html. Прошлая версия использовала
faked ellipses graticule — выглядело как абстрактные кольца, не как глобус.
Implementation:
- d3-geo.geoOrthographic + d3.geoPath для projection math
- topojson-client.feature для распаковки world-atlas land-110m
- dynamic import обоих deps + JSON — не блокирует main bundle, lazy chunk
- requestAnimationFrame loop пересчитывает d-attr на каждом кадре
(8s per revolution per прототип, tilt -15° lean)
- LAND_CACHE module-level — повторные mount'ы не re-fetch
- CSS .globe-sphere rotation удалена — d3 projection делает работу,
CSS transform distort'ила бы land mass (нет projection math)
Bundle hit: ~55kb (land-110m.json) + ~15kb gzipped (d3-geo + topojson) —
lazy chunk, fetched один раз и cached.
Follow-up к MR !90 per user feedback iterations:
1) "в поиске по справочникам два крестика" — fixed в SearchInput:
`type="search"` (native browser X) → `type="text"`. Custom onClear X
остаётся единственным. Catalog input тоже type="text".
2) "блин два крестика было на топбар поиске а тут норм все. верни" —
restore TopBar search input. Context-aware behavior сохранён:
- На /dictionaries → live filter каталога через URL ?q=
- На остальных routes → submit (Enter) → /search records search
Single X confirmed by visual test.
3) "свитч темы можно тоже в профиль перенести" — ThemeSwitch переехал в
Sidebar footer (рядом с RU/EN button). Right row в footer: lang button
слева, theme switch (3 icons sun/moon/monitor) справа через ml-auto.
4) "руководство и версию оставить в топбар чтобы подсвечивать новинки" —
Docs + VersionBadge stays in TopBar. Туда же добавлена
NotificationsBell (placeholder Bell icon) — broadcast slot для
webhook errors / draft reviews / system notices когда backend
notifications stream появится.
User: "строки таблиц белые / ховер на них более чёткий а то еле видно".
Прошлый hover был bg-surface-2/40 (40% opacity на cream/yellow page bg) —
почти неотличим от default. Под page bg #faf9f5 + 40% opacity surface-2
тёмное пятно еле проступало. На long тяжело отследить какая row под cursor.
Изменения:
- DictRow (catalog table): убран subtle scope-color bg tint (был
warn-bg/30, pink-bg/30 — еле виден на page bg). Row всегда `bg-surface`
(white). Scope visually conveyed через 3px left-stripe + colored SCOPE
chip. Hover full strength `bg-surface-2` (no /40 opacity).
- FieldsTabContent + EventsTabContent rows: same change — bg-surface +
hover:bg-surface-2.
- Shared TableRow component (ui/components/table.tsx): bg-surface +
hover:bg-surface-2 — applies к records table, audit, и любым другим
TableRow callsites.
1) MODAL OFF-SCREEN (Schema/AOI/Confirm dialogs)
DialogContent had `inset-0 w-full h-full` + `min-[880px]:inset-auto`
конфликтовал — inset-0 winning по CSS specificity, modal приклеивался
к левому краю viewport вместо центра. User screenshot показал Schema edit
выходящим за левую границу экрана.
Fix: remove inset-0 fullscreen pattern. Use simple centered modal —
left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 + w-[95vw] max-h-[95vh]
overflow-y-auto. На mobile <880px max-w из size variant даёт ширину 95vw
(natural fit). На desktop max-w ограничивает до 440/560/720/880/920px.
2) LOGO 'ORDINIS MDM' С CIRCLE ICON (per redesign prototype)
Sidebar logo: 'ORDINIS' → circle icon (accent orbit ring + dot) +
'ORDINIS' bold + 'MDM' mute subtitle (Tektur). SVG inline — нет external
dep.
Collapsed mode: только circle icon, no text (uses Tektur ORDINIS dropped).
3) TIMELINE BUTTON LABEL
InfoPanel button 'Time-travel…' → 'Таймлайн' (per user feedback). Behaviour
не меняется — клик открывает picker. Inline таймлайн visualization
отложен (большой refactor, нужен product decision).
Verified:
- Dialog modal центрируется на десктоп
- Logo рендерится с svg + text
- Tests: 116 pass
Per design_handoff_ordinis_mdm/README.md responsive table (line 464-465):
- ≤1024px: Sidebar becomes overlay drawer (hamburger в TopBar)
- ≤880px: Drawers и modals fullscreen (100vw × 100dvh, no border-radius)
Implementation:
src/components/layout/Sidebar.tsx
- Extracted SidebarContent (logo + nav + user block) для DRY
- Desktop <Sidebar /> wraps SidebarContent в hidden lg:flex aside
- New <MobileSidebar open onClose /> — Sheet с тем же содержимым,
260px wide, slides from left
- SidebarLink принимает onClick → drawer auto-close при navigate
src/components/layout/TopBar.tsx
- Optional onMenuClick prop — рендерит hamburger button (Menu icon
из lucide-react) с lg:hidden
- VersionBadge скрыт <md (768px) — diagnostic info, не critical UX
src/routes/__root.tsx
- useState mobileNavOpen, передаём в MobileSidebar + TopBar
src/ui/components/dialog.tsx
- SIZE_CLASSES получили prefix (arbitrary Tailwind v4
breakpoint per handoff exact 880px). На mobile <880px modal становится
fullscreen (inset-0 w-full h-full no rounded), ≥880px — centered с
translate + max-w из SIZE_CLASSES.
src/ui/components/sheet.tsx
- size variants: sm:max-w-* → min-[880px]:max-w-*. На mobile <880px
sheet берёт w-full (fullscreen drawer per handoff).
src/components/record/RecordDrawer.tsx
- max-width переключатель 520/880 теперь применяется только ≥880px.
На mobile drawer всегда fullscreen.
src/ui/components/language-switch.tsx
- Display fallback: opt.short → opt.label → uppercase id.
Previously показывал id ('ru-RU', 'en-US') когда short не был задан —
теперь использует label ('RU', 'EN') как fallback.
Browser-verified в preview viewport 375×812:
- Hamburger ☰ visible, click открывает Sheet с nav items
- Backdrop fade + slide-from-left animation
- TopBar fits в 375px (RU/EN compact, Sign in visible)
- LoadingBlock + Alert text-wrap correctly
Реальные измерения через DevTools на staging выявили 3 бага после MR !47:
Bug 1: text-title-md/lg/xl inherited Tektur от @nstart/ui Card parent.
Handoff: 'Inter for all headings'. Computed: Tektur.
Fix: добавил font-family: var(--font-sans) explicit на text-title-*
и text-body/text-cell. После Stage 3.9 (nstart gone) можно убрать —
body inherit будет достаточно.
Bug 2: text-cap + font-mono cohabit (catalog scope chips, time-travel
presets) → Tailwind v4 cascade order: font-mono идёт после text-cap
в bundled CSS → JetBrains Mono перебивает Tektur. text-cap baked
font-display, font-mono token redundant.
Fix: codemod strip font-mono где есть text-cap (4 sites). Computed
font-family теперь правильно Tektur.
Bug 3: @nstart/ui PageHeader рендерил h1 'DICTIONARIES' в Tektur
36px/700 uppercase (font-primary text-4xl). Handoff page title:
17px/600 Inter normal case. Наш src/ui/components/page-header.tsx
написан но не экспортирован через barrel — все routes резолвили
PageHeader к nstart версии.
Fix: добавил 'export { PageHeader }' override в src/ui/index.ts.
Все 7 PageHeader usages (catalog/search/reviews/webhooks/outbox/
my-drafts/graph) теперь получат handoff-conformant heading.
Bug 4 (small): catalog counter '37/37' использовал text-cap но это
numeric value — handoff role text-mono (11px JetBrains Mono).
Fix: text-cap → text-mono + tabular-nums для grid alignment.
Verified via injected test elements + CDP computed styles:
text-title-xl: 22/600 Inter
text-title-lg: 17/600 Inter
text-title-md: 15/600 Inter
text-body: 13/400 Inter
text-cell: 12.5/500 Inter
text-mono: 11/500 JetBrains Mono
text-cap: 10.5/600 Tektur
Все 7 utilities точно matches handoff scale.
Files: 4 modified. TS strict + 116 tests pass.