Top-left dark-glass input на странице /graph. Substring match
case-insensitive по name + displayName (admin помнит либо
technical id `antenna`, либо русское название `Антенны`).
UX:
- Matched nodes остаются в full opacity, остальные dim'ятся (0.25) —
как при hover-focus. Force-layout не трогаем (positions стабильны).
- При единственном match → matched node получает focused border
(orange accent + glow) — легко найти результат.
- Edges светятся только когда оба конца — match. Иначе dim.
- Counter chip `N из M` под input'ом + `ничего не найдено` для empty.
- Esc clears query и снимает focus. ✕-кнопка тоже clears (focus
остаётся на input).
- Search dominates над hover dim — non-matched nodes остаются dim
даже если hover их затрагивает.
i18n: graph.search.placeholder / .empty / .count для ru-RU и en-US.
Force-simulation не пересчитываем при изменении query — это намеренно.
Иначе nodes прыгали бы по экрану на каждый символ, ломая spatial
recognition (юзер уже привык где какой node лежит).
Восемь дефектов из 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).
Last item из QA report backlog'а. Banner + empty-state hint показывают
aggregate count, но не помогают find конкретные записи которые скоро
изменятся. Badge per row даёт O(1) visual scan.
Backend (read-api):
- ScheduledRecordsSummary.scheduledBusinessKeys: List<String> (capped 500)
- Repo method findScheduledBusinessKeys (DISTINCT subquery)
- Single fetch вместо N+1 per-row queries
Frontend:
- scheduledByKey Set<string> построен из summary
- Badge variant=info «Запланировано» рядом с businessKey badge'ом
- Title attribute с full RU/EN tooltip
- Не блокирует layout — gap-1.5 с pendingReview badge'ем (могут быть оба)
i18n keys для ru/en — short label «Запланировано» / «Scheduled».
QA report cleanup (после P1/P2 ship'нутых в v2.31.4):
- **BUG-009**: Excel/SQL export форматы получили явный «скоро» badge поверх
opacity-only disabled state. Title attribute оставлен.
- **BUG-010**: «Доступ актора» (audit scope column) переименован в «Scope» —
компактнее, не переносится на 2 строки.
- **BUG-011**: Все audit table headers получили title attribute → hover
tooltip когда text truncate'ится («ИЗМЕНЕН…» → full «Изменения»).
- **UX-001**: Scope chips в /dictionaries теперь показывают ✓ glyph когда
selected → визуально clear что multi-select. Aria-label расширен:
«Фильтр по области (можно выбрать несколько)».
- **UX-002**: Breadcrumb segments получили title attribute → hover показывает
full label когда truncate'ится в narrow viewport.
- **UX-003**: При смене вкладки в редакторе справочника (records → relations
и т.п.) URL params q/scopes сбрасываются — раньше search фильтр оставался
применённым после возврата на records.
- **UX-004**: TimeTravel slider boundary метки получили 2-digit year:
«05.05.26» вместо «05.05» — disambiguates конец декабря / начало января.
Title attribute exposes full локальную дату.
- **UX-009**: «Удалить» button в RecordDrawer переименован в «Закрыть запись»
— same action как row context menu «Закрыть» (bitemporal close, не truly
delete). Consistent labeling across UI.
Fixes UX bug: пользователь создаёт запись с validFrom на будущую дату
(e.g. 18.05), открывает catalog 15.05 — пустая таблица, никаких хинтов
о том что запись существует.
Backend (read-api):
- New endpoint GET /api/v1/{dict}/records/scheduled-summary
- Returns {count, nearestValidFrom} для записей с validFrom > now AND
validTo > now в текущем scope view
- Single-aggregate JPA query (COUNT + MIN), respect scope filter same way
as listActive — INTERNAL scheduled не учитывается для PUBLIC consumer'а
Frontend:
- New useScheduledSummary hook + ScheduledRecordsSummary type
- Empty-state в dictionaries.$name now shows: "Запланировано N, ближайшая
на DD.MM [Перейти к этой дате]" с CTA который set'ает time-travel на
nearestValidFrom
- i18n keys для ru/en plural forms
Связан с MR !219 (TimeTravelPicker 30-day future window) — теперь slider
позволяет дойти до даты записи, и empty-state hint позволяет узнать что
запись существует.
User feedback (screenshot): 'Approve' остался английским в RU локали +
'Отозвать' улетал на отдельную строку справа из-за ml-auto.
Fix:
- i18n.ts: 'workflow.schemaDraft.actions.approve' default 'Approve' →
'Одобрить'. EN остался 'Approve'.
- SchemaDraftDrawer.tsx: убрал ml-auto на withdraw button. Раньше это
force pushed его в конец строки → flex-wrap отправлял на новую строку
правым краем. Сейчас withdraw сидит сразу после reject в той же
flow-row, с danger color для визуальной distinction. На узком экране
всё ещё wrap'нется естественно (без 'плавающего' alignment).
Sidebar:
- Outbox: бэйдж = outbox.pending (события queued, не отосланы). DLQ
отдельно — alarming но не actionable из бэйджа, видно на /outbox.
- Webhooks: бэйдж = count активных subscriptions (w.active === true).
User feedback: 'вебхуки активные есть а цифры с бэйджем нет'.
Audit log column header:
- 'Scope' → 'Доступ актора' (ru) / 'Actor scope' (en)
- Backend audit_log хранит userScope (claim из JWT актора), НЕ scope
изменённого ресурса. User confusion: 'правки в public были а тут
RESTRICTED' — потому что у пользователя scope-привилегия RESTRICTED.
Уточнённая label убирает путаницу.
- TODO: backend feature — добавить dictionaryScope в AuditEntry чтобы
показать обе ('Актор: RESTRICTED → Ресурс: PUBLIC' — двусторонний
audit trail для compliance).
User explicitly: "ThemeSwitch не трогай". Реверт my 2-button Earth|Dark
переделку обратно на 3-icon Sun/Moon/Monitor segmented control (по
original 3bfa7ca).
Также:
- schema.action.create RU: "Создать справочник" → "Создать"
- schema.action.create EN: "Create dictionary" → "Create"
(per prototype catalog button — короче чтобы не растягивать toolbar)