SmokeE2ETest.contextLoadsAndCreatesRecord фильтрует audit_log по
action='CREATE' и ожидает row с businessKey. Мой backend #1 commit
сделал definitionCreated() писать action='CREATE' — конфликт с
record-level CREATE rows: filter подбирал dict-level row первой
(no businessKey → null assertion fail).
Fix: definitionCreated/definitionUpdated теперь пишут action
'DEFINITION_CREATE' / 'DEFINITION_UPDATE'. event_type остаётся
DEFINITION_CREATED/UPDATED (changelog query filter по event_type
IN список, не задеть).
SmokeE2ETest.contextLoadsAndCreatesRecord фильтрует audit_log по
action='CREATE' и ожидает row с businessKey. Мой backend #1 commit
сделал definitionCreated() писать action='CREATE' — конфликт с
record-level CREATE rows: filter подбирал dict-level row первой
(no businessKey → null assertion fail).
Fix: definitionCreated/definitionUpdated теперь пишут action
'DEFINITION_CREATE' / 'DEFINITION_UPDATE'. event_type остаётся
DEFINITION_CREATED/UPDATED (changelog query filter по event_type
IN список, не задеть).
Делёные 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.
Status table в pending-endpoints.md обновлён: endpoints #1, #2, #4, #5
landed в MR !95-!98 в session 2026-05-11. Workflow API (#3) deferred —
требует отдельной design discussion + sprint (state machine + RBAC pool
model parallel to record-level DraftController architecture).
Backend #2 из pending-endpoints brief. Frontend TimeTravelModal использует
этот endpoint для slider marks — каждая точка = bucket где минимум одна
запись validFrom попадает.
GET /api/v1/dictionaries/{name}/snapshots?from=...&to=...&granularity=day
Implementation:
- DictionaryRecordRepository.findSnapshotBuckets — native SQL date_trunc
GROUP BY bucket, ORDER BY bucket DESC. scope filter через text[] из CSV.
- SnapshotsService — gates dict existence/scope, validates granularity
(hour/day/week) + window, formats RU label (1 апр / сейчас), reverses
список чтобы oldest first (UI читает слева-направо).
- DictionaryChangelogController новый endpoint /snapshots.
Errors:
- 404 dictionary_not_found
- 400 invalid_granularity (not in {hour,day,week})
- 400 invalid_window (from >= to)
Frontend impact: TimeTravelModal slider marks переходит с recordTimestamps
derived data на реальный API. ~1h frontend swap.
Backend #1 из pending-endpoints brief. Frontend HistoryTabContent +
TimeTravelModal используют этот endpoint для отображения версий схемы
во времени с per-version property-level diff summary.
Implementation:
- AuditLogger.definitionCreated / definitionUpdated — пишет в audit_log
с event_type DEFINITION_CREATED / DEFINITION_UPDATED. payload_before/
after = JsonNode schema. record_id / business_key — NULL (dict-level event).
- DictionaryDefinitionService.create / updateSchema hook в audit logger
(autowired, null-safe для tests).
- AuditLogRepository.findByDictionaryIdAndEventTypes — query by dict_id +
event_type IN (...), ORDER BY event_time DESC, paginated.
- ChangelogService.findChangelog — собирает entries, computes top-level
property-key diff (added/removed/changed). Первая entry (newest) =
isCurrent + schemaVersion из DictionaryDefinition; старые entries
возвращают version=null (semver-в-аудит backfill — separate migration).
- DictionaryChangelogController — GET /dictionaries/{name}/changelog?limit=50
Errors:
- 404 dictionary_not_found когда dict missing OR scope hidden (consistent
с другими endpoints).
Tests +9: dict-not-found, scope-hidden, empty-audit, first-is-current,
diff-added/removed/changed/null-before/identical.
5 endpoints нужны admin-UI для завершения UI-фич:
1. GET /dictionaries/{name}/changelog — History tab + TimeTravel
2. GET /dictionaries/{name}/snapshots — TimeTravel slider marks
3. POST drafts/.../{review,decision,publish} — workflow state machine
4. PATCH /dictionaries/{name}/schema — Fields tab inline edit
5. GET /dictionaries/graph/outgoing — catalog → FK count column
Каждый endpoint содержит: purpose, route, query/body, response example,
errors, frontend integration estimate. Total frontend work after backend
ship — ~14h (1.5 sprint day).
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.
Per user feedback:
- "Справочники 37 шт. эта надпись в топбаре" — title с inline count
переехал в TopBar breadcrumb (новый Crumb.caption field, text-cap mute).
Catalog page больше не имеет h1 — start straight from toolbar.
- "фильтры и кнопки в баре таблицы. бар цветом" — toolbar (scope/bundle
pills + counter + Граф + + Создать) обёрнут в tinted `bg-surface-2`
rounded bar с border. Визуально отделяет controls от table chrome.
- "заголовки таблицы белый фон" — table thead was `bg-surface-2` (cream),
сливалось с toolbar bar. Стало `bg-surface` (white) — clean visual
separation toolbar (tinted) → table (white).
- "→ ссылается шрифт не тот" — InfoPanel section subhead "→ ссылается"
/ "← используют" был `text-cap` (Tektur uppercase 10.5px bold).
Прототип использует regular body Inter lowercase mute. Заменил на
`text-body text-mute`.
TopBar useBreadcrumb extended: для catalog route добавляет inline `caption`
field из useDictionaries() count. Editor route остаётся с mono `subtitle`
("name · v1.0.0"). Different fields rendered с different styles.
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.
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)
Per /Users/zimin/Downloads/Ordinis (4)/redesign/compact.html.
TopBar breadcrumb:
- Detail routes теперь показывают `← Справочники / Космические аппараты`
с back-arrow prefix на parent crumb (caption text, mute). Bold title +
inline mono subtitle "satellites · v1.0.0".
- Catalog route: simple "Dictionaries" title.
TopBar right cluster cleanup:
- Removed `Docs` link — diagnostic, не critical UX, моя кноп не нужна
для UI flow.
- Removed `VersionBadge` (commit + tag) — был noise рядом с продакшн
features. Build info всё ещё доступна в browser console / Docs.
- ThemeSwitch: tri-state (Light/Dark/System icons 3×24px) заменён на
2-button `Earth | Dark` с text labels (per prototype). `system`
preference остаётся в localStorage для existing users, но новый UI
не выставляет.
- LanguageSwitch: dual radio `RU | EN` заменён на single toggle button —
показывает текущий язык как label, click переключает на следующий.
Saves ~60px горизонтально, matches prototype.
EditorInfoPanel order:
- Action buttons (Экспорт / Time-travel / AOI) переехали из середины
panel (между metadata и Relations) в bottom (после Relations) —
per prototype. Info → meta → relations → actions flow.
- Action order: Экспорт первым (frequent), затем Time-travel, AOI последним
(rare для most dicts).
Per redesign prototype screenshot: каждая row имеет 3px цветной strip слева
по scope + filter chips заполняются scope-color когда active.
- DictRow: добавлен absolute-positioned `<span>` шириной 3px на левом краю
первой td (relative wrapper), background = SCOPE_DOT (PUBLIC=accent,
INTERNAL=warn, RESTRICTED=pink). pl-1 на контенте чтобы не наезжал.
- Row bg tint: для INTERNAL/RESTRICTED rows — subtle SCOPE_BG_TINT/30,
PUBLIC = plain white (default).
- SCOPE col chip: использует SCOPE_BG_TINT + SCOPE_TEXT вместо generic
Badge info — теперь PUBLIC chip orange, INTERNAL amber, RESTRICTED pink.
- Toolbar scope filter pills: active state = SCOPE_BG_TINT + SCOPE_TEXT +
border-transparent (filled in scope color). Inactive = outline.