Два бага в SchemaDraftDrawer (видны на review pending):
1. АВТОР показывал raw UUID (77ec2cc8-98e3-4d70-8037-...). Резолвим:
- Если maker = current user (JWT sub claim) → 'Я • <preferred_username>'
- Иначе → short UUID (первые 8 символов) с full в title attribute
- Стилизация: 'Я' — text-ink (читаемая), чужой — font-mono text-mute
- TODO Phase 1d: backend endpoint для resolve чужих sub → display name
2. Кнопка 'Отозвать' показывалась всем юзерам, не только автору.
- Backend всё равно 403'нет non-maker withdraw — но UI confusion:
reviewer видел кнопку которая всегда fail'ит.
- Gate: показываем только когда auth.user.profile.sub === draft.makerId.
- Reviewer теперь видит чистый action row (Approve / Запросить правки /
Отклонить), без danger-button мимо контекста.
Реальный случай: см. user screenshot на v2.16.x — Reviewer modal с UUID
автора и кнопкой 'Отозвать' справа внизу.
Бэйдж 'На ревью' в sidebar (desktop + mobile) был захардкожен в 0 —
TODO comment ждал cheap /drafts/pending/count endpoint. На самом деле
existing /admin/reviews/pending и /admin/schema-reviews/pending уже
возвращают paged response с totalElements/total в metadata — request
size=1 даёт нужный counter с минимальным payload (~1 строка + 4
metadata field'а).
Новый hook useReviewsBadgeCount():
- Параллельно дёргает обе queue (record + schema)
- Суммирует totalElements (record) + total (schema)
- Полл каждые 60s (lighter чем 30s queue refresh — badge не critical)
- Gated на auth.isAuthenticated (anonymous не должен дёргать admin endpoint'ы)
Sidebar.tsx (оба варианта — desktop + mobile MobileSidebar):
- reviewsCount: 0 → reviews.total
Per user feedback: «по кнопке что нового должно открываться модальное
окно с изменениями».
UpdateBanner кнопка «Что нового» (т9n key `updateBanner.docs`) ранее
открывала `/docs/` в новой вкладке — generic documentation page, не
contextual для «что именно в этом обновлении».
Теперь — открывает `WhatsNewModal` (тот же modal что показывает 🎁
button в TopBar) — public changelog с release notes сразу.
## Refactor
- **`WhatsNewModal.tsx` (NEW)** — extracted controlled-mode modal с
release notes. Props: `open`, `onClose`. Internal logic читает
changelog, tracks lastSeen в localStorage, на open mark'ает latest
как seen.
- **`WhatsNewButton.tsx`** — refactored: trigger button + use
`WhatsNewModal`. Поведение unchanged (🎁 icon с unread badge → click → modal).
- **`UpdateBanner.tsx`** — replaced `window.open(DOCS_URL)` на
`setWhatsNewOpen(true)` + render `<WhatsNewModal>`. DOCS_URL constant
removed (больше не используется).
## UX intent
Когда update detected:
1. User видит orange banner «Доступна новая версия v2.16.0»
2. Hover «Что нового» button → opens modal
3. User читает release notes (что добавлено, исправлено) ДО reload
4. Click «Обновить» → reload
Раньше «Что нового» вёл к generic docs page — user не знал ЧТО
изменилось, только что update есть. Modal даёт structured answer.
## Test plan
- [x] `pnpm tsc --noEmit` clean
- [x] `pnpm build` clean
- [x] `pnpm vitest run useChangelog.test` — 11/11 pass (parser logic
unchanged)
- [ ] Manual: симулировать update banner (mock useAppVersion
`updateAvailable=true`), click «Что нового» → modal opens
- [ ] Manual: 🎁 button в TopBar также opens same modal
- [ ] Manual: close modal → red dot disappears на 🎁 button
(localStorage updated)
Two improvements per user feedback ('по клику открывается редактирование и
в развернутом виде слева навигация'):
## 1. Row click → edit drawer
TableRow в /dictionaries/$name теперь имеет onClick={setEdit({kind:'edit',
record:r})} когда canMutate. cursor-pointer + bubbling guards:
- Checkbox cell и Actions cell имеют onClick stopPropagation чтобы
внутренние controls не открывали drawer случайно.
- Anonymous users (no canMutate) — row не clickable (read-only, no drawer).
## 2. Wide-mode left rail в drawer
SchemaDrivenForm в sections layout теперь умеет ResizeObserver-based detection
своей ширины. Когда контейнер ≥720px (drawer wide mode = 880px) → render'ит
180px sticky left rail с section list + per-section field counts. Click rail
item → smooth scroll к section anchor. Иначе chip strip сверху, как раньше.
Container-based (не viewport-based) чтобы narrow drawer на wide viewport не
рендерил rail (520px не fits 180+1fr grid).
Native <select> для FK references показывал label '<code> — <localized name>'
(например 'OPT_MIDRES_MS — Оптический мульти-резолюционный мульти-спектральный')
— текст бьётся на chevron в trigger area из-за тонкого contract'а browser-
specific overflow handling (наезжают per user screenshot 2026-05-11).
Fix:
- buildOption() обрезает name до MAX_FK_LABEL_CHARS=28 (code intact) +
'…' suffix когда combined превышает limit
- select сам получает 'truncate overflow-hidden whitespace-nowrap text-ellipsis'
как защита если browser native truncate не сработал
- pr-10 → pr-9 (38px) — точно совпадает с right-3 chevron position (12px)
+ ~16px icon + 8px gap, не overlap'aет.
stroke=5 в viewBox 256 при 28px render = 5*28/256 = 0.55px (subpixel,
почти невидимо). stroke=12 даёт ~1.31px real — чёткая линия. Заодно
переключил с --color-line-2 на --color-line чтобы немного контрастнее
на bg-surface.
Делёные design team 2026-05-11 в Downloads/files:
- ordinis-mark.svg — двойные орбиты + 6 dots + gradient core
(DeepBlue #185FA5 → SkyBlue #378ADD), brand spec
- ordinis-favicon.svg — minimal 32px variant
- ordinis-appicon.svg — 1024px PWA icon с dark rounded tile
- ordinis-lockup-light/dark.svg — horizontal lockup с wordmark
(для landing/splash/marketing — пока не используется но
хранится в public для future use)
Changes:
- public/favicon.svg → minimal 32px variant
- Sidebar logo block: inline ordinis-mark с двойными орбитами +
gradient core. Wordmark 'ORDINIS MDM' остаётся в Tektur.
- index.html: theme-color #185FA5 (был #120a8f), apple-touch-icon
link на appicon, mask-icon color match.
Note: SVG colors hardcoded в Sidebar inline для full design fidelity
(brand assets specifically defined gradient). currentColor не подходит
т.к. brand требует точные DeepBlue/SkyBlue/LightBlue tokens.
CSS transform-box default для SVG = view-box (transform-origin в viewBox
user coords). С transform-box: fill-box origin становится относительным
bounding-box каждой группы (whirl + whirl-rev — arc bbox смещён). Арки
летали по неправильной траектории. Прототип HTML rely on default — copy.
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.
Per user feedback ('вот такого не должно быть, лучше лоадер'):
- /dictionaries — full-page LoadingBlock (size lg, centered min-h 60vh)
пока useDictionaries query loading. Раньше size=md без centering
показывался в углу — а на пустой странице это смотрелось куце.
- /dictionaries/$name — same gate на detailQuery.isLoading. Без этого
юзер видел partial render: пустую sidebar info panel, узкую records
колонку (extra columns ещё не computed из schema), битую табличную
toolbar. Теперь — централизованный GlobeLoader пока схема не дойдёт.
Radix Dialog warns when DialogContent отсутствует accessible name/description.
RecordDrawer (Sheet) уже имел Title, добавили sr-only Description.
ConflictDiffModal/ExportModal/TimeTravelModal — кастомные visible headers,
теперь имеют sr-only Title + Description для screen readers + чтобы
заглушить dev warnings.
- TopBar: right cluster `md:ml-0 ml-auto` → `ml-auto` always. Без TopBar
search в центре уже нечего push'ить вправо через form, right cluster
должен сам прижиматься к правому краю. User: "в топбаре руководство
и далее по правой стороне".
- Catalog toolbar: scope chips / bundle pills / Граф button — все на
h-8 (было py-1 + inline text variable height). + inline-flex
items-center чтобы baseline консистентный с input/Граф/+Создать.
User: "высоту элементов в баре таблиц выровняй".
User clarification after MR !91:
- "Search из TopBar убран — все верно" — TopBar search должен быть удалён.
- "TopBar search вернулся. убери его" — MR !91 ошибочно его вернул.
- "два крестика баг в топбаре был а ты и в таблице убрал его" — двойной X
was в TopBar (native + custom); в catalog был только custom X на
старом input. После refactor catalog input получился без X совсем —
fix: добавил кастомный clear button когда q непустой.
Changes:
- TopBar: removed form + SearchInput + handlers (handleSearchInput /
Submit / Clear / inputRef / useState). Removed useEffect ⌘K shortcut
(без search input nothing to focus). Right cluster: Docs +
VersionBadge + NotificationsBell. ml-auto на right cluster чтобы
push'ить вправо без search в центре.
- Catalog input: добавлен absolute-positioned X (✕) справа input'а
когда `search.q` непустой. `pr-7` на input чтобы текст не наезжал.
Clear → `setSearch({ q: undefined })`. Single X confirmed visually.
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 появится.
Per user feedback:
- "hub view" — restore старый 3-col layout с central focused dict card +
outgoing FK rail слева + refBy rail справа. Был simpler 2-col split,
но user previously сказал "Связи и вкладка связи была удобнее" (старый
hub более информативный + центральная карта показывает context).
Revert к версии 5007e0f (pre-2-col-redesign).
- "aoi там где нет полей гео лишний" — InfoPanel AOI button теперь скрыт
если schema dict не имеет geo properties. Heuristic: property name
matches /geom|location|bbox|polygon|coordinates|geo/i OR format=geojson|wkt.
Edge case: если active aoi уже set → button показывается чтобы clear.
Per user feedback:
- "поле Бизнес-ключ выделен цветом" — businessKey column TableCell теперь
`text-mono text-accent` (было plain text-mono). Matches prototype 03 где
RES-P-1, KAN-V-3, SNT-1A показаны в accent orange — primary identifier
для каждой row.
- "Экспорт CSV в bulk bar лишний как повтор Экспорт кнопки в InfoPanel" —
убрал Export button из BulkSelectionToolbar. ExportModal pre-picks
scope=selected когда selection.size > 0, поэтому workflow:
select rows → click "↓ Экспорт..." в InfoPanel → modal с pre-selected
scope. Бэк endpoint тот же, UX cleaner — один путь к export'у.
- Removed handleBulkExport callback + DownloadSimpleIcon import (теперь
unused).
Per user feedback: "словари в своих панелях то как у нас было ранее".
Per redesign prototype editor view (Ordinis(4) compact.html):
- InfoPanel: was floating sidebar aside без borders → теперь
`rounded-lg border border-line bg-surface p-4` — visible bordered card
containing PUBLIC chip + meta + Связи + actions.
- Main editor area: tabs + toolbar + tab content (records/relations/
fields/json/events/history) обёрнуты в `rounded-lg border border-line
bg-surface overflow-hidden` panel.
- WorkflowBanner и AOI banner остаются outside panels (status chrome).
Visual containment отделяет editor chrome от page background, делает
очевидным где info ends / table starts. Matches prototype Screen 03-08.