Commit Graph

158 Commits

Author SHA1 Message Date
Александр Зимин ea729dc901 Merge branch 'fix/catalog-toolbar-bar-topbar-count' into 'main'
fix(catalog): title→TopBar + toolbar→tinted bar + table white + font fix

See merge request 2-6/2-6-4/terravault/ordinis!86
2026-05-11 17:06:23 +00:00
Zimin A.N. d77f040a5c fix(catalog): title→TopBar + toolbar→tinted bar + table white + font fix
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.
2026-05-11 20:06:18 +03:00
Александр Зимин d4348d4d26 Merge branch 'fix/table-rows-white-stronger-hover' into 'main'
fix(table): white row bg + stronger hover

See merge request 2-6/2-6-4/terravault/ordinis!85
2026-05-11 17:00:45 +00:00
Zimin A.N. a27e9ec901 fix(table): white row bg + stronger hover per user feedback
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.
2026-05-11 20:00:40 +03:00
Александр Зимин 1b382cb338 Merge branch 'fix/topbar-restore-full-cluster' into 'main'
fix(topbar): restore full right cluster (Docs/VersionBadge/LangSwitch)

See merge request 2-6/2-6-4/terravault/ordinis!84
2026-05-11 16:56:06 +00:00
Zimin A.N. 38f7dcee3e fix(topbar): restore full right cluster per user preference
User explicitly: "Руководство · dev · 19a3731b · ☀️🌙🖥 · RU EN · zimin.an · ↩
это оставь". Revert MR !81 cluster-minimization — diagnostic info нужна
для prod debugging.

Restored:
- Docs link (/docs/) — opens admin docs portal
- VersionBadge (channel + commit short SHA) — shows local/dev/v1.x.x · hash
- LanguageSwitch (dual RU | EN radio) — keeps both visible vs single-toggle
- (ThemeSwitch + AuthBadge остаются как были)
2026-05-11 19:55:52 +03:00
Zimin A.N. 6627fd918d fix(topbar): revert ThemeSwitch to 3-icon original + shorten create button
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)
2026-05-11 19:50:41 +03:00
Александр Зимин 1102e7b8ec Merge branch 'feat/scope-badge-timetravel-label' into 'main'
feat(editor): scope-colored InfoPanel badge + Time-travel label

See merge request 2-6/2-6-4/terravault/ordinis!82
2026-05-11 16:46:52 +00:00
Zimin A.N. e580a04007 feat(editor): scope-colored InfoPanel badge + Time-travel label
Per redesign (4) prototype.

- InfoPanel scope badge: было `<Badge variant="info">` (generic light bg).
  Стало scope-color filled chip: PUBLIC→accent-bg, INTERNAL→warn-bg,
  RESTRICTED→pink-bg. Matches table SCOPE col + filter pills.
- i18n: timeTravel.button RU "Просмотр на дату" → "Time-travel…"; EN
  "View as of date" → "Time-travel…". User confirmed: "Time-travel label
  оставь" — единый label везде, без локализации (бренд-слово как редизайн).
2026-05-11 19:46:33 +03:00
Александр Зимин a9394ea363 Merge branch 'feat/topbar-cleanup-infopanel-order' into 'main'
feat(topbar): breadcrumb back-arrow + cleaner right cluster + InfoPanel order

See merge request 2-6/2-6-4/terravault/ordinis!81
2026-05-11 16:43:21 +00:00
Zimin A.N. ac1ca8d035 feat(topbar): match redesign prototype — breadcrumb back-arrow + cleaner right cluster
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).
2026-05-11 19:43:05 +03:00
Александр Зимин 4219a6f96d Merge branch 'feat/catalog-scope-color-rows' into 'main'
feat(catalog): scope-colored row stripes + filter pills + chips

See merge request 2-6/2-6-4/terravault/ordinis!80
2026-05-11 16:35:57 +00:00
Zimin A.N. bc737820d6 feat(catalog): scope-colored row stripes + filter pills + chips
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.
2026-05-11 19:35:43 +03:00
Zimin A.N. 180a5920fe feat(editor): Связи tab 2-col split per redesign prototype
Per /Users/zimin/Downloads/Ordinis (4)/redesign/screens/04-editor-links.png.

Replaced 3-col hub view (left rail + center dict card + right rail) с simple
2-col grid. Central dict card стал избыточен — все его info уже в InfoPanel
слева, дублирование тратило место и attention.

New layout:
┌────────────────────────────┬────────────────────────────┐
│ → ССЫЛАЕТСЯ НА (N)         │ ← ССЫЛАЮТСЯ НА НАС (M)     │
│ ┌──────────────────────┐   │ ┌──────────────────────┐   │
│ │ {target} {count}     │   │ │ {source} {count}     │   │
│ │ {req|opt} {.field}   │   │ │ {крит} {.field}      │   │
│ └──────────────────────┘   │ ├──────────────────────┤   │
│                            │ │ Warning N ссылок     │   │
│                            │ └──────────────────────┘   │
└────────────────────────────┴────────────────────────────┘

Each outgoing row: target_dict (link accent) | recordCount | required/optional
chip | source_field (mono right).
Each incoming row: source_dict (link) | activeRecordsInSourceDict | onClose
policy chip (BLOCK=критично pink, CASCADE=warn amber) | .source_field.

Footer warning в incoming panel когда totalDeps > 100 — "удаление сложно".
2026-05-11 19:29:37 +03:00
Александр Зимин 19a3731ba9 Merge branch 'feat/catalog-relative-time-flat-sidebar' into 'main'
feat(layout): catalog relative time + chevron + flat sidebar per redesign (4)

See merge request 2-6/2-6-4/terravault/ordinis!78
2026-05-11 16:27:18 +00:00
Zimin A.N. 7e33456d70 feat(layout): catalog relative time + chevron + flat sidebar per redesign (4)
Per /Users/zimin/Downloads/Ordinis (4)/redesign/compact.html (бэкграунд
прототипа уже #faf9f5 — match'ит наш neutral default).

Catalog table:
- ИЗМЕНЁН column показывает relative time: Xс / Xмин / Xч / Xд / Xмес / Xг.
  Прошлая абсолютная дата (05/06/2026) затирала scale ("5 минут vs 3 месяца —
  визуально одинаково далеко"). Прототип использует 2ч, 3д, 1мес.
- Chevron `›` column добавлен в конец каждой строки — affordance что row
  кликабелен (открывает editor).

Sidebar:
- Flatten: убраны section labels (workflow / admin). Все nav items в одном
  списке per redesign: Главная → Справочники → Мои черновики → На ревью →
  Outbox → Webhooks → Аудит → Поиск.
- /graph удалён из sidebar — теперь reachable только через catalog toolbar
  "Граф ⇄" button (single entry point, no duplication).
- Применено в обоих Sidebar (desktop) и MobileSidebar (drawer).

Cleanup: убран unused GitBranch icon import.
2026-05-11 19:26:56 +03:00
Александр Зимин b39e0500de Merge branch 'feat/editor-tabs-redesign' into 'main'
feat(editor): Fields / Events / History tabs match redesign prototype

See merge request 2-6/2-6-4/terravault/ordinis!77
2026-05-11 16:21:17 +00:00
Zimin A.N. cbe21dbc52 feat(editor): Fields / Events / History tabs match redesign prototype
Per redesign/screens/{05-fields,07-events,08-history}.png:

Fields tab (Поля):
- # numbered col (01, 02, ...) — mono mute, tabular-nums
- ПОЛЕ — bold accent (was plain mono)
- ТИП — inline FK render: `fk → target_dict`, array as `string[]`,
  date/date-time format names, enum, native types
- REQUIRED / UNIQUE / I18N / INDEX — checkmark (green) or — dash
- INDEX heuristic: x-unique || x-references → indexed (backend doesn't
  expose explicit flag yet)
- ОПИСАНИЕ truncate с max-w-[320px]

Events tab (События):
- Filter chips above table: все N / webhooks / publish / review / edit /
  export (только показываем те которые есть, кроме «все»)
- "Полный лог →" link справа в chips row
- Table cols: Время (5 sec precision) / Тип (colored chip) / Статус
  (● dot + ok/fail) / Куда·Кто (user) / Сообщение (businessKey · action)
- Type chips colored: webhook=accent, publish=green, review=warn,
  export=surface-2, edit=accent-bg/50

History tab (История):
- HEAD row: ● filled dot + v{version} + HEAD chip (dark) + change summary
  (+N ~0 −0 placeholder until backend changelog endpoint появится)
- Description + updatedAt date
- Backend-pending notice card: "/api/v1/dictionaries/{name}/changelog pending"

Plus: + import { cn } from '@/lib/utils' для новых tabs
2026-05-11 19:20:57 +03:00
Александр Зимин a0b10e50ec Merge branch 'feat/conflict-diff-modal' into 'main'
feat(record): conflict diff modal на 409 save

See merge request 2-6/2-6-4/terravault/ordinis!76
2026-05-11 16:13:57 +00:00
Zimin A.N. 16da694d8c feat(record): conflict diff modal на 409 save (handoff line 444)
Когда PUT /records/{key} возвращает 409 (optimistic-lock fail — другой
пользователь сохранил тот же record между нашим load и submit'ом),
открываем ConflictDiffModal.

Component:
- Header: 'Конфликт сохранения · {DICT}' + business key
- Diff body: refetch server state via useRecordRaw, иди по union ключей
  local + server, оставь только отличающиеся. Per-row: field name (mono)
  + ваше значение (accent-bg) + серверное (surface-2)
- Footer 3 actions:
  - Отмена: close, оставить editor open
  - Загрузить серверное: discard local, refetch records, close editor
  - Перезаписать: re-submit current payload (если опять 409 → новый diff)
- Backend-friendly: не требует server diff endpoint, client считает diff
  client-side через JSON.stringify equality

Route wiring:
- onError handler distinguishes generic 409 (open diff modal) от
  специальных кодов: x_references_blocked_by_dependents,
  x_references_cascade_required, draft_required — те идут в свои dialogs
- Conflict state ({payload, businessKey}) capture'ит local data чтобы
  показать в diff даже после reset формы
2026-05-11 19:13:30 +03:00
semantic-release-bot 258ba50d3e chore(release): v1.3.0 [skip ci]
## [1.3.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v1.2.2...v1.3.0) (2026-05-11)

###  Features

* **admin-ui:** editor 6-tab layout (Stage 3.4) ([640c890](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/640c890d0bb35117ee32420f11d4960c24aeabb8))
* **admin-ui:** editor 6-tab layout (Stage 3.4) ([0ddba87](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/0ddba871f7cbc62ac49a72144cfc9c98dfc861d4))
* **catalog:** row-based table per handoff prototype design/compact.html ([f43ce55](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/f43ce5563a7f6ddffb494b1879247c4c582b4567)), closes [#2](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/2)
* **drawer:** all-sections layout с chips strip per handoff prototype ([155c70e](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/155c70e23cc0e38deb10953c176ff3835712f415)), closes [#5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/5)
* **drawer:** sticky toolbar API per handoff Screen 3 line 279 ([d4000dd](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/d4000ddd3d93facfeb4f61270f132cf5fe16974c)), closes [#5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/5)
* **editor:** + Экспорт button в InfoPanel per redesign dark theme screenshot ([e15a391](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/e15a3916bfcd89f61cc63ea5f5472474bad98f9b))
* **editor:** ExportModal + TimeTravelModal + InfoPanel merge + fixes ([5a2220d](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/5a2220d77966efff26ddac12b2d408cdf5696f5f)), closes [#fbf8ee](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/fbf8ee) [#faf9f5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/faf9f5)
* **editor:** keyboard shortcuts j/k/Enter/n per handoff Screen 2 ([a09c411](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/a09c4113c1b8331590cc2e368dec680bcb4d1e04))
* **editor:** left info panel + 3-col layout per handoff prototype ([a57cf52](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/a57cf520a428b5d72966818967327fa714f05728))
* **editor:** match redesign prototype layout — TopBar title + tab counts + InfoPanel actions ([ec99010](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/ec99010697e8a2687d99ee01e2dc4751a62a8346))
* **editor:** Stage 3.4 polish — Events embedded + JSON syntax highlight ([f0392af](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/f0392af0e37a06f784cb18558f8fb79c1b8bce24))
* **handoff:** WorkflowBanner + table a11y + responsive + toast styling ([0acba4c](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/0acba4c073507951b9dbdd191ff0a4ad0ed8252a))
* **mobile:** Sidebar drawer <1024px + Modal/Sheet fullscreen <880px ([7a3b31b](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/7a3b31b9578ef280e25c88f2657a62bf7272f216))
* **record:** RecordDrawer slide-over per Stage 3.5 handoff (Phase A) ([750395d](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/750395da09dc11a4048744417933864df147708d)), closes [#5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/5)
* **sidebar:** desktop collapse mode 220px → 56px per handoff prototype ([69a86b2](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/69a86b238c3baa2c6cf44f8d6b9a15bcc59664b5))
* **theme:** sync color tokens с redesign ui-kit + add claude-light theme ([42dab24](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/42dab24d964533d6ce600e5d5c73f6ff2b8e87ce)), closes [#fbf8ee](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/fbf8ee) [#d4a07a](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/d4a07a) [#faf9f5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/faf9f5) [#d97757](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/d97757)
* **ui:** drop @nstart/ui dependency (Stage 3.9) ([5007e0f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/5007e0f4eb730454c9883942fa3c59c6416f0637))
* wire drawer toolbar filters + records table responsive hiding ([889207f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/889207f62901820fcb3e908fd576cd0878013945))

### 🐛 Fixes

* **admin-ui:** sidebar font sizes per handoff spec ([f72aac6](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/f72aac63986e3ba30717139f852ebbf5fe26bc23))
* **admin-ui:** sidebar fonts per handoff ([0297745](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/0297745d139ae7ebb9076c61e83d6a2d53a6e275))
* **admin-ui:** UpdateBanner generic message ([dc0002f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/dc0002f379f96e7d22746f3fc2b87e7b456475f5))
* **fonts:** align all text sizes to handoff spec via Tailwind theme overrides ([71432e9](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/71432e9ae7006bad3bc38cf89ce82c6596e07c81))
* **fonts:** explicit font-family on title utilities + cascade conflicts ([f0dbc20](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/f0dbc20f62c7b3b0c9f0150247fb3b14007f267d))
* **fonts:** semantic typography utilities per handoff (7 roles) ([b949127](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/b94912789f7d7be93def334aeb3377dd0dad409d))
* **release:** drop invalid tarballDir: [secure] config ([090bff5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/090bff56ce2aa9153617dc11a623005fa0c88918))
* **table+layout:** TableHead alias to THEAD wrapper + kill horizontal scroll ([787cea9](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/787cea9db172b3f111c357129af45183ce461efa))
* **theme:** de-saturate light bg — убрать жёлтый Earth-cream tone ([e5572d5](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/e5572d534640953f86fed7eecefe0210d49c3925)), closes [#fbf8ee](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/fbf8ee) [#fbf8ee](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/fbf8ee) [#fafaf8](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/fafaf8) [#f4eedc](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/f4eedc) [#f2f1ee](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/f2f1ee) [#e3dccb](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/e3dccb) [#e4e1dc](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/e4e1dc) [#efe9d8](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/efe9d8) [#ecebe7](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/ecebe7) [#b05a2e](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/b05a2e) [#3a2818](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/issues/3a2818)
* **ui:** 3 user-reported issues ([abdfaa8](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/abdfaa8fcc2da6fba8bab6d73f43498e4f77f5a5))
* **ui:** tailwind-merge стрипал text-body — buttons ушли в 16px inherited ([9e2d985](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/9e2d985485f60f8333ed5d81de7cd6bcd5009bad))
* **version:** bump to 1.3.1 and show 8-char commit hash ([780de70](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/780de70fc5cf91c3904a8dc417a09514e3ddf584))
* **version:** channel-based label, no semver fallback for branch builds ([3223a5c](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/3223a5ced74956efa85cf1a9d4be0d51a17b2297))
* **version:** detect CI build via CI_COMMIT_SHORT_SHA, not process.env.CI ([b6af155](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/b6af155da256a395db6757c911e1e3e3c50cac9a))
* **version:** read CI_COMMIT_TAG as source of truth, package.json as fallback ([e62633d](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/e62633d903c063540d521c4b063795aade1b6a14))

###  Reverts

* **timetravel:** keep 'Time-travel…' label ([36fb70f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/36fb70fc00a62f93687142293c318b231b915a07))
2026-05-11 16:11:52 +00:00
Zimin A.N. ad7c51d93f feat(catalog): toolbar layout per redesign prototype
Match handoff screenshot:
- Title "Справочники" + inline "N шт." count caption (no description text)
- Remove page-level SearchInput (TopBar already has global search ⌘K)
- Reorder toolbar to single row:
  [scope pills PUBLIC|INTERNAL|RESTRICTED]
  | divider |
  [bundle pills все|cuod|geo|i18n|core]
  | spacer |
  N/M counter
  [Граф ⇄] (→ /graph)
  [+ Создать] (moved here from PageHeader actions)
- Scope/bundle pills are bordered chips с ring-1 для active state
- Drop deps-only toggle (not in redesign)
- Drop scopeCounts useMemo (counts moved out of chip labels per design)
2026-05-11 19:09:39 +03:00
Zimin A.N. 5a2220d779 feat(editor): ExportModal + TimeTravelModal + InfoPanel merge + fixes
UI redesign matching prototype (Ordinis (3) dump):
- ExportModal: format (CSV/JSON/Excel/SQL) + scope (all/filtered/selected) +
  column toggles + encoding/delimiter + preview filename per redesign line
  1059-1092. CSV uses backend mutation, JSON client-side from filtered rows,
  Excel/SQL disabled (backend pending).
- TimeTravelModal: full-page compare СЕЙЧАС vs ТОЧКА ВО ВРЕМЕНИ + quick
  presets (сейчас/неделя/месяц/год/при создании) + step toggle версия/день
  + slider + tabs (Что изменилось / Записи на момент / Структура и поля) +
  "Открыть как readonly" CTA. Replaces inline TimeTravelPicker.
- InfoPanel ← используют merge: rich rows с field path + active records
  count + onClose policy chip (BLOCK/WARN/CASCADE). Inline
  DictionaryDependentsPanel above records table removed (one source of truth).
- Sidebar logo: match redesign/ui-kit.html .brand SVG (outer ring + inner
  faded ring + orbit dot offset right cx=22.5). ORDINIS Tektur tracking 0.22em
  + MDM smaller mute 0.32em.
- Bg neutralized: #fbf8ee Earth cream → #faf9f5 warm neutral default
  (per user preference, Earth cream still described in comment как opt-in).
- WorkflowBanner: Case 1 Live (approvalRequired=false) returns null —
  permanent "Опубликовано" banner = noise, status already visible в
  InfoPanel scope/version/updatedAt. Banner показывается только когда
  есть actionable state.

Critical bug fixes:
- fix(CascadeConfirmDialog): infinite render loop "Maximum update depth
  exceeded". useEffect had cascadeMut (TanStack Query mutation wrapper) в
  deps — wrapper identity changes every render → effect fires → reset() →
  store update → render → loop. Extract stable `reset` reference перед
  useEffect. Симптомы у user: создание/редактирование записи не работали.
- fix(semantic-release): add missing peer dep
  conventional-changelog-conventionalcommits (9.3.1). semantic-release 22+
  больше не bundles preset. Без неё release job падал с
  "Cannot find module 'conventional-changelog-conventionalcommits'".
2026-05-11 19:01:57 +03:00
Zimin A.N. e15a3916bf feat(editor): + Экспорт button в InfoPanel per redesign dark theme screenshot
User dropped /Users/zimin/Downloads/Ordinis (3) — full project dump.
Dark theme editor screenshot (uploads/draw-4b771386...png) показывает
INFO PANEL c двумя buttons:
  - ↓ Экспорт…
  - ⟲ Time-travel…

У нас был Time-travel + AOI — добавил Экспорт как third button.

Implementation:
  - EditorInfoPanel.actions.onExport + exportPending props
  - DownloadSimpleIcon кнопка 'Экспорт…'
  - Wired к useBulkExportRecords mutation:
    * Selection не empty -> export selected
    * Selection empty -> export all filtered records
  - disabled когда mutation pending, label 'Экспорт…'

Project dump reviewed:
  - design_handoff_dictionary_catalog — старый handoff, всё уже реализовано
    (List view MR !62, Hub MR earlier, Graph earlier)
  - patches/ — @nstart/ui patches, moot (мы dropped @nstart/ui MR !52)
  - redesign/ — уже синхронизированы colors MR !71, layout MR !69
  - uploads/ — user-provided screenshots, main delta = Export button
2026-05-11 18:03:52 +03:00
Zimin A.N. 36fb70fc00 revert(timetravel): keep 'Time-travel…' label
User feedback: 'Time-travel label такое себе Time-travel… оставь'.
Reverting MR !70 part of rename ('Таймлайн' → 'Time-travel…').

Per handoff prototype line 299 это и был 'Time-travel…' изначально —
надо было не трогать.
2026-05-11 17:56:38 +03:00
Zimin A.N. 42dab24d96 feat(theme): sync color tokens с redesign ui-kit + add claude-light theme
Reverted MR !66 de-yellow — turns out redesign prototype intentionally
использует жёлтый Earth cream (#fbf8ee) как primary bg. User confusion
was с предыдущим build'ом до MR !66, в ui-kit все таки cream.

Added tokens per redesign/ui-kit.html prototype:

Scope tints:
  --bg-public, --bg-internal, --bg-tenant (для scope chip backgrounds)
Workflow states:
  --bg-draft, --bg-live, --bg-deprec (для workflow banner backgrounds)
Alert variants:
  --al-info-bg/bd, --al-warn-bg/bd, --al-err-bg/bd, --al-ok-bg/bd
Shadows:
  --shadow-l, --shadow-m, --shadow-toast, --shadow-drawer (consistent)
Toast cap color:
  --toast-cap #d4a07a (orange-cream per handoff Screen 4)
Avatars:
  --avt-pink-bg, --avt-green-bg (semantic state avatar backgrounds)
Purple:
  --color-purple для future use

New theme '[data-theme=claude-light]':
  Warm cream neutral (#faf9f5) — для users которые не любят saturated Earth.
  Accent navy → coral orange (#d97757) per Claude branding.
  Full token set включая scope/workflow/alert variants.

Dark theme получил те же дополнительные tokens (bg-public/internal/tenant,
al-info-bg/bd, shadows, toast-cap, avt-bg) — consistency с light tier.

User может через ThemeSwitch / manual data-theme attr выбрать:
  default     — Earth cream (handoff design intent)
  claude-light — neutral warm
  dark        — Claude-style dark

Bundle: +~1KB CSS. Tests: 116 pass.
2026-05-11 17:53:45 +03:00
Zimin A.N. abdfaa8fcc fix(ui): 3 user-reported issues
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
2026-05-11 17:49:57 +03:00
Zimin A.N. ec99010697 feat(editor): match redesign prototype layout — TopBar title + tab counts + InfoPanel actions
Per /Users/zimin/Downloads/redesign/screens/03-editor-records.png редизайн
prototype editor layout отличается от current MVP:

1. TopBar absorbs editor title + version (was в PageHeader)
2. PageHeader удалён — title и breadcrumb теперь только в TopBar
3. Tab labels получили counts (Записи 127, Связи 6, Поля 12)
4. Action buttons распределены per role:
   - Time-travel + AOI → InfoPanel bottom (close к data context)
   - Schema edit + +Запись → tab toolbar right (close к tab actions)

Changes:

src/components/layout/TopBar.tsx
  - Breadcrumb получил subtitle slot — mono '<name> · v<version>'
  - useBreadcrumb fetches dict detail для editor route '/dictionaries/$name'
  - Render: '← Справочники / Космические аппараты satellites · v1.0.0'

src/api/queries.ts
  - useDictionaryDetail принимает name: string | undefined + enabled flag
  - Guard для editor breadcrumb когда route не matches dict

src/routes/dictionaries.$name.tsx
  - Removed <PageHeader> — title/breadcrumb теперь в TopBar
  - EditorInfoPanel получает actions prop:
    onTimeTravel + timeTravelActive + onAoi + aoiActive
  - EditorTabBar получает counts (per-tab badge) + actions slot
    (right-aligned Schema + +Запись buttons)
  - extractOutgoingFkCount helper для relations count

src/components/editor/EditorInfoPanel.tsx
  - actions prop с Time-travel + AOI buttons
  - Rendered в panel выше Relations section, между metadata и FK list
  - aria-pressed когда active (time-travel или AOI с filter)

src/components/editor/EditorTabBar (inline in route)
  - counts prop: Partial<Record<EditorTab, number>>
  - actions prop: ReactNode для right-aligned cluster
  - inline count badge — text-mono text-mute next to label

Tests: 116 pass. TS strict clean.
2026-05-11 17:45:06 +03:00
Zimin A.N. 69a86b238c feat(sidebar): desktop collapse mode 220px → 56px per handoff prototype
Handoff prototype показывает «‹ Свернуть» button внизу sidebar — toggle
desktop rail в icon-only 56px. Persists в localStorage ord-sidebar-collapsed.

Sidebar component:
  - State collapsed (localStorage 'ord-sidebar-collapsed' = '1')
  - <aside width> transitions между w-[220px] и w-[56px] (200ms ease)

SidebarContent (DRY с MobileSidebar) получил props:
  - collapsed?: boolean (default false)
  - onToggleCollapsed?: () => void (undefined hides button — mobile drawer)

Когда collapsed:
  - Logo: 'ORDINIS' → 'O' (single char, centered)
  - Section labels (text-cap headers) — hidden
  - Nav links: icon-only, centered, icon size 18px (vs 15px expanded);
    title attribute с label для hover tooltip
  - Badges (records count, pending) — hidden (нет места)
  - User block: avatar only, name/email hidden; title attribute с full name
  - Toggle button bottom: '‹ Свернуть' → '›' (icon-only)

Mobile (MobileSidebar Sheet) НЕ получает collapse — там Sheet даёт full
nav text всегда, close через × header.

A11y: aria-label / title для всех collapsed buttons. Click handler
sets localStorage + state — survives F5.

Tests: 116 pass. TS strict clean.
2026-05-11 17:13:06 +03:00
Zimin A.N. 889207f629 feat: wire drawer toolbar filters + records table responsive hiding
Closing 2 handoff gaps:

1) DRAWER TOOLBAR FILTERS (handoff Screen 3 line 279)
SchemaDrivenForm получил три new optional props:
  - searchFilter (string) — case-insensitive substring против field key + label
  - onlyFilled (boolean) — hide fields с empty data[key]
    (null/undefined/''/false/0/empty-array/empty-object)
  - onCounterChange (visible, total) — surface field counter parent'у

Filter применяется поверх bucketing — все три buckets (identity/description/
extra) фильтруются. UI рендерит filteredBuckets.X.map(...) вместо raw bucket'ов.

В sections layout (drawer use case) если section после filter empty —
sticky header не рендерится. Tabs layout backward-compat (filter применяется
но visually invisible если user в другом tab'е).

dictionaries.$name.tsx callsite:
  - State: drawerSearch, drawerOnlyFilled, drawerCount {visible, total}
  - Reset on drawer close
  - RecordDrawer.toolbar prop wired только в edit mode (create — все поля
    empty, 'только заполненные' даст 0/0)
  - SchemaDrivenForm receives все три filter props

Result: user может typing 'mass' в drawer search → видит только mass_kg /
data_mass / etc.; toggle 'только заполненные' → hide unused optional fields.

2) RECORDS TABLE RESPONSIVE COLUMN HIDING (handoff line 466)
Records table в dictionaries.$name.tsx применяет hideBelow API из MR !59:
  - businessKey: всегда visible
  - name/code: hideBelow='md' (768px)
  - extra schema columns: hideBelow='lg' (1024px)
  - scope badge: hideBelow='sm' (640px)
  - validFrom: hideBelow='lg'
  - actions: всегда visible

На mobile 375px только businessKey + actions visible — chrome остаётся
usable. Tablet 768+ adds name/scope. Desktop 1024+ adds schema columns.

Tests: 116 pass.
2026-05-11 17:10:26 +03:00
Zimin A.N. e5572d5346 fix(theme): de-saturate light bg — убрать жёлтый Earth-cream tone
Пользователь: 'этот желтый фон давай уберем'. Светлый bg #fbf8ee
выглядел слишком cream/yellow на staging — особенно на больших areas
страницы (список справочников, hover states).

Color token changes (light theme только; dark не трогаем):
  --color-bg:        #fbf8ee → #fafaf8  (off-white neutral)
  --color-surface-2: #f4eedc → #f2f1ee  (subtle warm grey hover/stripe)
  --color-line:      #e3dccb → #e4e1dc  (de-saturated border)
  --color-line-2:    #efe9d8 → #ecebe7  (subtle inner border)

Brand accents preserved:
  --color-accent: #b05a2e (warm orange-brown) — unchanged
  --color-navy:   #3a2818 (deep brown) — unchanged
  --color-warn / pink / green BG — unchanged (semantic states)

Result: cleaner light theme, professional reading surface. Accent
color по-прежнему даёт warmth для brand identity, но page bg
больше не tints всё жёлтым.
2026-05-11 17:01:54 +03:00
Zimin A.N. 090bff56ce fix(release): drop invalid tarballDir: false config
@semantic-release/npm падал с EINVALIDTARBALLDIR — tarballDir option
должен быть String OR omitted. У нас был false (boolean), что invalid.

Поскольку npmPublish: false (мы НЕ публикуем pkg в npm registry —
private repo), tarballDir не нужен вовсе. Удалили — default behavior
(npm pack идёт в temp dir после bump, но не used при npmPublish:false).

После этого fix semantic-release должен пройти normally:
  - analyze conventional commits с момента v1.2.2
  - bump version в admin-ui/package.json
  - generate CHANGELOG.md
  - commit + tag vX.Y.Z [skip ci]
  - create GitLab Release object

Минимум tag будет v1.3.0 (мы накопили 10+ feat: с момента v1.2.2).
2026-05-11 16:57:19 +03:00
Zimin A.N. 155c70e23c feat(drawer): all-sections layout с chips strip per handoff prototype
Handoff Screen 3 RecordDrawer показывает sections подряд с chips strip
для quick jump, не tabs (currently SchemaDrivenForm has 3 tabs).

SchemaDrivenForm:
  - New optional prop layout: 'tabs' (default, legacy) | 'sections'
  - В sections mode:
    * Section chips strip на верху (sticky top-0) вместо Tabs
    * Chip = bucket name + field count в Mono
    * Click chip → scrollIntoView к section
    * Все 3 buckets рендерятся подряд (visible одновременно)
    * Каждая section получает sticky h3 cap header + scroll-mt-12
    * z-indexed sticky: chips strip = 10, section headers = 5
  - В tabs mode: backward-compat без изменений

RecordDrawer callsite (dictionaries.$name.tsx):
  - Передаёт layout='sections' в SchemaDrivenForm (create + edit)
  - Существующий tabs API остаётся для test'ов которые используют default

Layout matches handoff Screen 3 prototype:
  [chips strip sticky]
  [section: Основное]
   field grid 2-col
  [section: Описание (i18n)]
   localized fields
  [section: Дополнительно]
   optional fields grid

Future Phase B refactor: backend x-section schema metadata (open question
#5) разблокирует per-property sections (вместо bucketing). Текущий
fallback bucketing (identity/description/extra) достаточен для MVP.

Tests: 116 pass (test использует default 'tabs' layout, не разломан).
2026-05-11 16:37:38 +03:00
Zimin A.N. a57cf520a4 feat(editor): left info panel + 3-col layout per handoff prototype
Handoff prototype design/compact.html показывает editor как 3-col layout:
[Sidebar 220 (global nav)] [InfoPanel 220 (dict metadata)] [main content].
Текущая implementation была 2-col (nav + everything).

NEW src/components/editor/EditorInfoPanel.tsx:
  - Scope badge (PUBLIC/INTERNAL/RESTRICTED) — top
  - ОПИСАНИЕ section — caption + dict.description body
  - Metadata 2x2 grid: Bundle / Версия / Записей / Локали
    (mono text-ink-2, cap labels)
  - СВЯЗИ section с двумя группами:
    → ссылается  outgoing FK list (name links к /dictionaries/$name)
    ← используют incoming refBy list (deduplicated sourceDict names)
  - sticky lg:top-2 self-start чтобы panel оставалась видна при scroll
  - lg:max-h-[calc(100vh-7rem)] overflow-y-auto если metadata высокий

src/routes/dictionaries.$name.tsx:
  - Обернул содержимое (banner + tabs + tab content) в:
    <div className='grid grid-cols-1 lg:grid-cols-[220px_1fr] gap-4 lg:gap-6'>
      <EditorInfoPanel ... />
      <div>{banner + tabs + content}</div>
    </div>
  - На <lg переключается в stacked (info above content) — single column
    благодаря grid-cols-1.

PageHeader остаётся выше grid (full-width breadcrumb + title + actions).
Action buttons (AOI / Time-travel / Schema edit / Создать) живут в
PageHeader.actions slot — handoff prototype показывает их в info panel,
но рядом с title лучше discoverable. Может уйти в info panel в future
refactor.

Tests: 116 pass. TS strict clean.
2026-05-11 16:33:40 +03:00
Zimin A.N. f43ce5563a feat(catalog): row-based table per handoff prototype design/compact.html
Refactor каталога с card grid → compact row table. Handoff README line
46 явно говорит 'clicking any dictionary row in the catalog' — наша cards
implementation diverged от прототипа.

What's new:
- DictionaryListTable — single flat table (bundle стал column, не группа)
- DictRow — compact tr с scope dot prefix + title + truncated desc
- 8 columns per prototype: name / id / bundle / scope / records / → / ← / updated
- Responsive column hiding via hideBelow-style breakpoints:
  - id: hidden <md
  - bundle: hidden <lg
  - scope: hidden <sm
  - records: hidden <md
  - → ←: hidden <lg
  - updated: hidden <xl
- На mobile <640px показано только name column (с inline scope dot и desc)
- Row click → navigate к editor; Enter/Space keyboard accessible (role=link)

Removed:
- DictCard component (card layout)
- FkChip component (FK chips на cards — теперь just counts)
- SCOPE_STRIP (vertical 3px strip — заменён на inline scope dot)
- groupByBundle usage (bundle стал column, не group section)
- MAX_REFBY_CHIPS const

Note: outgoing FK count (→) пока показывает '—' — требует schemaJson per-dict
fetch (40+ requests). Batch endpoint GET /dictionaries/graph (handoff open
question #2) разблокирует это. Текущий incoming refBy через useDictionary-
Dependents работает per row.

Tests: 116 pass. TS strict clean.
2026-05-11 16:30:31 +03:00
Zimin A.N. d4000ddd3d feat(drawer): sticky toolbar API per handoff Screen 3 line 279
RecordDrawer теперь принимает optional toolbar prop:
  - search (string) — field name filter
  - onlyFilled (boolean) — show only filled fields toggle
  - counterVisible/Total (number) — 'N / M полей' caption

Sticky position между header и body, h=44 per handoff style. Toolbar
rendered только если caller передал. SchemaDrivenForm callsite (записать
filter в каждое FieldRenderer) — separate follow-up, требует refactor
form internals (currently 3 buckets identity/description/extra).

Section anchor chips + wide-mode ToC rail — deferred до backend
x-section schema metadata (CEO open question #5 в handoff).
2026-05-11 16:02:11 +03:00
Zimin A.N. a09c4113c1 feat(editor): keyboard shortcuts j/k/Enter/n per handoff Screen 2
Handoff line 438: 'Pressing j/k moves selected row; Enter opens drawer;
Esc closes; n opens new-record drawer; / focuses search.'

Implementation:
  - rowCursor state — index в visibleRecords (-1 = no focus)
  - j или ArrowDown → cursor++ (clamped)
  - k или ArrowUp → cursor-- (clamped)
  - Enter → setEdit({kind:'edit', record}) когда cursor valid
  - n → setEdit({kind:'create'}) когда canMutate
  - Esc — handled Radix Dialog natively через RecordDrawer
  - / — handled TopBar (⌘K equivalent, focuses global search input)

Guards:
  - Skip когда target inside input/textarea/contentEditable (no conflict с typing)
  - Skip когда drawer/modal open (edit.kind !== 'closed' — focus в portal)
  - Cursor reset на page/filter change (stale index безопасности)

Visual: active row получает bg-accent-bg/40 outline accent для clarity.
2026-05-11 16:00:42 +03:00
Zimin A.N. 0acba4c073 feat(handoff): WorkflowBanner + table a11y + responsive + toast styling
Closing handoff gaps выявленных при полном audit'е README.md vs current
implementation. См. screen-by-screen mapping ниже.

== Screen 2 — Editor: WorkflowBanner (NEW) ==
Required by handoff (line 238-242) но не существовал. Inline status row выше
tab bar — live/review/info variants. Backend не имеет explicit dict-level
workflow state (draft → review → live transition), используем proxy:
  - approvalRequired=false      → 'Live' (green-bg + green left edge)
  - approvalRequired=true + 0   → 'Approval enabled' (accent-bg + accent edge)
  - approvalRequired=true + N   → 'На ревью N' (warn-bg + warn edge), link к /reviews

Backend extension future: добавить explicit workflowState к DictionaryDetail
+ buttons для transitions (request-review / approve / publish). Сейчас banner
дает минимум — visual workflow signal без UI mutation buttons.

== Table accessibility per handoff line 476 ==
TableHeaderCell:
  - scope='col' (a11y screen reader requirement)
  - aria-sort='ascending|descending|none' когда sortable prop true
  - New sortable + sortDirection props (API ready, не используется пока)

== 560px responsive per handoff line 466 ==
TableHeaderCell + TableCell получили optional hideBelow='sm|md|lg' prop:
  hideBelow='sm' → hidden <640px (table-cell ≥sm)
  hideBelow='md' → hidden <768px
  hideBelow='lg' → hidden <1024px

Callsites могут пометить secondary columns (created_by, updated_at, etc.)
hideBelow='md' для clean mobile experience. Текущие routes ещё не используют
prop — API ready для incremental adoption.

== Toast styling per handoff line 302-305 ==
Sonner toast — раньше surface bg + ink text. Handoff:
  - Navy bg + on-accent text (warm cream-orange feeling)
  - 4s auto-dismiss
  - Variant accent через left border 4px:
    success → green / error → pink / info → accent / warning → warn
  - cap-style title (но используем text-body для нативности, не overkill)
  - 13px font-sans

richColors disabled — наши tokens вместо sonner defaults.

Files:
  - NEW src/components/editor/WorkflowBanner.tsx
  - src/routes/dictionaries.$name.tsx (wire banner above tab bar)
  - src/ui/components/table.tsx (a11y + hideBelow API)
  - src/ui/components/toaster.tsx (navy variant + variant borders)
  - src/ui/index.ts (TableCellProps export)

Tests: 116 pass. TS strict clean.
2026-05-11 15:58:12 +03:00
Zimin A.N. 787cea9db1 fix(table+layout): TableHead alias to THEAD wrapper + kill horizontal scroll
CRITICAL bugs reported by user — table выглядела кривой, контент уезжал
за экран с горизонтальным скроллом.

Bug 1 — invalid HTML в Table:
  Callsites используют <TableHead><TableRow><TableHeaderCell>... ожидая
  нstart-style THEAD wrapper. Но мой alias делал TableHead = TableHeaderCell
  (TH cell). Получалось <th><tr><th>...</th></tr></th> — invalid nested,
  browser parser hoist'ил TableRow наружу, создавая phantom 509px column
  слева от данных. Колонки выглядели misaligned, content смещён вправо.

  Fix: TableHead → THEAD wrapper (alias TableHeader). TableHeaderCell
  остаётся отдельным TH cell. Source semantically correct, browser parses
  валидно, phantom column исчез.

Bug 2 — horizontal overflow:
  Scope strip border на верху detail-страницы использовал
  '-mt-6 -mx-4 sm:-mx-6 lg:-mx-8'. На lg viewport main padding только
  sm:px-6 (24px), strip negative margins -mx-8 (32px) → 8px overflow each
  side → body.scrollWidth > viewport → horizontal scrollbar.

  Fix:
  - Strip negative margins только до -mx-6 (соответствует main padding)
  - main получил overflow-x-clip + max-w-7xl wrapper получил min-w-0
    (защита от любых future overflow children)

Verified в preview viewport 1440 + 375: body.scrollWidth === viewport,
no h-scroll.
2026-05-11 15:49:27 +03:00
Zimin A.N. 9e2d985485 fix(ui): tailwind-merge стрипал text-body — buttons ушли в 16px inherited
CRITICAL bug — на staging buttons рендерились в 16px вместо handoff
workhorse 13px. UpdateBanner secondary buttons показывали invisible
текст (text-ink overrode by inherited text-on-accent от parent banner).

Root cause:
  buttonVariants base = 'rounded-md text-body font-medium ...'
  primary variant     = 'bg-navy text-on-accent ...'

tailwind-merge default config pattern-matches `text-*` как ОДНУ группу
(color). Видя `text-body text-on-accent` он считал это конфликтом двух
colors → keep last → text-on-accent остаётся, text-body стрипается.
Result: button теряет font-size → inherit body 16px.

Fix: extendTailwindMerge регистрирует наши custom semantic typography
utilities (text-body/cell/mono/cap/title-*) в группе font-size (отдельной
от colors). twMerge теперь знает `text-body text-on-accent` это size +
color, обе сохраняются.

Also: text-mono/text-cap в font-family группе (они baked Mono/Tektur)
— fix конфликт с font-mono/font-display tokens если кто-то их случайно
combined.

Verified в preview: 'Sign in' button class теперь содержит text-body,
computed fontSize: 13px (вместо 16px).

Plus CI: release job image node:22-alpine → repo.nstart.cloud/library/
node:22-alpine (Docker Hub rate-limited, mirror используется во всех
других jobs).
2026-05-11 15:45:27 +03:00
Zimin A.N. 7a3b31b957 feat(mobile): Sidebar drawer <1024px + Modal/Sheet fullscreen <880px
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
2026-05-11 15:39:23 +03:00
Zimin A.N. b6af155da2 fix(version): detect CI build via CI_COMMIT_SHORT_SHA, not process.env.CI
Staging показывал badge 'local · {commit}' вместо ожидаемого 'dev'.
Причина: Dockerfile не пробрасывает process.env.CI через ARG. Vite
видит только что было передано через --build-arg в .gitlab-ci.yml
(GIT_COMMIT/GIT_TAG → CI_COMMIT_SHORT_SHA/CI_COMMIT_TAG).

Fix: использовать CI_COMMIT_SHORT_SHA как primary CI detect — он всегда
проходит через Dockerfile ARG (см. ordinis-admin-ui/Dockerfile). Это
canonical GitLab CI signal — always set в любом pipeline.

process.env.CI остаётся как secondary check для других CI систем
(local CI runners, GitHub Actions если когда-то) где CI_COMMIT_SHORT_SHA
не set но CI=true.
2026-05-11 15:29:58 +03:00
Zimin A.N. f0392af0e3 feat(editor): Stage 3.4 polish — Events embedded + JSON syntax highlight
Per handoff Stage 3.4 polish list (Records/Relations/Fields/JSON/Events/
History tabs).

Events tab — раньше был redirect к /audit?dict=name. Теперь embedded:
- useAudit({dictionaryName, size: 20}) → last 20 entries
- 4-col compact table: time / action / businessKey / user
- Empty state когда нет событий
- Bottom link → /audit с filter для full diff view

JSON tab — раньше был plain <pre>. Теперь:
- Regex-based syntax highlight (keys=accent, strings=green, numbers=warn,
  booleans=pink, null=mute italic)
- Copy-to-clipboard button с visual feedback
- Bundle cost: 0 (нет Monaco). Если потребуется EDIT — Monaco lazy
  через React.lazy в Stage 3.x followup.

Fields tab — пока read-only table остаётся. Inline edit требует backend
PATCH /dictionaries/{name}/schema endpoint; deferred.

History tab — placeholder remains. Требует backend changelog endpoint;
deferred.

TS strict + 116 tests pass.
2026-05-11 15:28:29 +03:00
Zimin A.N. 5007e0f4eb feat(ui): drop @nstart/ui dependency (Stage 3.9)
Все font cascade bugs из последних MR были вызваны @nstart/ui pollution
(Tektur inheritance от Card parent'ов, Onest font в Button, font-primary
class overrides). Решили обрезать раньше plan'а.

What changed:

New primitives (8 файлов в src/ui/components/):
  - panel.tsx           — container с border/surface
  - field.tsx           — FieldLabel/Hint/Error typography helpers
  - text-input.tsx      — Input + label/hint/error composite
  - table.tsx           — Table/TableHeader/Body/Row/HeaderCell/Cell/Empty
  - date-picker.tsx     — native <input type=date> wrapper с label/hint/error
  - multi-select.tsx    — Radix Popover + inline checkboxes
  - single-select.tsx   — native <select> с label/hint/error chrome
  - language-switch.tsx — segmented control в Tektur uppercase

Compatibility shims (existing components extended):
  - checkbox.tsx        — + label/description/error/onChange (nstart-compat
                          synthetic event), wrap в <label> когда есть chrome
  - modal.tsx           — + panelClassName / bodyClassName props
  - form-actions.tsx    — + align prop (start/end/between)
  - tabs-simple.tsx     — TabItem.count alias for .trailing (nstart-compat)
  - table TableHead     — alias TableHeaderCell с align prop

Barrel rewrite (src/ui/index.ts):
  - Удалён 'export * from @nstart/ui'
  - Explicit exports для всех 45+ нужных символов
  - SelectOption / MultiSelectOption / LanguageOption теперь {id, label}
    с optional 'value' alias — nstart-compat без массового codemod call-sites

styles.css cleanup:
  - Removed @import @nstart/ui/styles/{fonts,theme}.css
  - Removed @source nstart/dist directive (Tailwind v4 scan)
  - Removed --text-sm: 13px override (был костыль для @nstart/ui dist)
  - Legacy color tokens (--color-carbon/ultramarain/orbit/regolith/asteroid)
    sсодержали fallback mappings — оставлены, но callsites больше не используют

Codemod: 9 файлов конвертированы legacy colors → handoff tokens
  carbon → ink, ultramarain → accent, orbit → warn, regolith → line, asteroid → mute

main.tsx:
  - NStartUiProvider → TooltipProvider (Radix)
  - DatePickerProvider убран (наш DatePicker native, no provider needed)

routes/dictionaries.$name.tsx:
  - Removed useRef-based <input>.indeterminate hack
  - Radix Checkbox принимает checked='indeterminate' declaratively

package.json: -@nstart/ui (was 0.1.3)

Bundle impact:
  before: vendor-nstart-ui chunk = 1228 KB
  after:  no nstart chunk, всё в vendor-misc (+~70 KB Radix wrappers)
  net savings: ~1.15 MB minified, ~440 KB gzip

Tests: 116 pass (1 fixed: tab role from 'button' → 'tab', Radix semantically correct).
TS strict: clean.
Browser-verified (preview): 7 utilities computed correctly, nstart bundle absent.
2026-05-11 15:17:37 +03:00
Zimin A.N. f0dbc20f62 fix(fonts): explicit font-family on title utilities + cascade conflicts
Реальные измерения через 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.
2026-05-11 14:58:32 +03:00
Zimin A.N. 6805c70a6b ci: semantic-release automation for tag/changelog (Option D)
Закрывает эту тему окончательно. После merge в main CI анализирует
conventional commits с последнего tag'а, бампит package.json + CHANGELOG.md
и пушит git tag vX.Y.Z. Tag pipeline далее собирает images и ждёт
manual gate на trigger-deploy-prod.

Что добавил:
- ordinis-admin-ui/.releaserc.json — config (commit-analyzer, release-notes,
  changelog, npm[no-publish], git, gitlab). branches: ['main']. Маппинг
  conventional-commit types к bump levels.
- ordinis-admin-ui/package.json — devDeps: semantic-release@24.2 + 6 plugins.
- .gitlab-ci.yml — new 'release' stage + release job:
  - needs: trigger-deploy-staging (release ТОЛЬКО после зелёного staging)
  - image: node:22-alpine + pnpm@9.15.4
  - GL_TOKEN из CI variable GITLAB_TOKEN (semantic-release/gitlab требует
    именно GL_TOKEN env name)
  - allow_failure: true (нет релизных commits = OK, не блокирует pipeline)
- RELEASE.md — operator docs:
  - One-time setup: создать Project Access Token + GITLAB_TOKEN CI var
  - Commit conventions table (feat→minor, fix→patch, breaking→major)
  - Skip release (use chore: type или [skip ci])
  - Dry-run command для local validation

Prod safety: trigger-deploy-prod остаётся manual (when: manual). Автоматизация
ТОЛЬКО tag-создания, не deploy. Tag → CI runs → human кликает Deploy.

ACTION REQUIRED от user (one-time):
  1. GitLab → Settings → Access Tokens → создать Project Access Token
     scopes: write_repository + api, role: Maintainer
  2. GitLab → Settings → CI/CD → Variables → GITLAB_TOKEN (Masked, Protected)

Без этого release job будет fail'ить (но pipeline не покраснеет — allow_failure).
2026-05-11 14:51:03 +03:00
Zimin A.N. 3223a5ced7 fix(version): channel-based label, no semver fallback for branch builds
Stale package.json создавал ложное впечатление rollback'а: staging
показывал "v1.2.0" хотя последний tag — v1.2.2 (package.json не bump'ился
со времён v1.2.0 release).

Vote A — branch builds НЕ используют package.json как version label.
Vite определяет channel:

  serverTag != none → что прод/staging RUNS
  clientTag         → что build'ил CI tag pipeline (CI_COMMIT_TAG set)
  CI без tag        → 'dev' literal (staging / branch CI build)
  локально          → 'local' literal (pnpm dev)

Detection через новый __APP_CI__ define из process.env.CI (GitLab сетает
CI=true). Local pnpm dev: CI undefined → channel='local'.

package.json.version всё ещё инжектится как __APP_VERSION__ и видна в
tooltip badge'а как 'floor vX.Y.Z' — не как primary label.

Files:
  - vite.config.ts: + APP_CI from process.env.CI
  - src/vite-env.d.ts: + __APP_CI__ declare
  - src/components/version/useAppVersion.ts: + clientCI
  - src/components/version/VersionBadge.tsx: channel logic вместо fallback

Verify all three modes:
  local         → 'local · {commit}'
  CI branch     → 'dev · {commit}'
  CI tag v1.3.1 → 'v1.3.1 · {commit}'

Tests: 116 pass.
2026-05-11 14:45:04 +03:00
Zimin A.N. 750395da09 feat(record): RecordDrawer slide-over per Stage 3.5 handoff (Phase A)
Заменяет Modal-based record create/edit на right slide-over drawer per
design_handoff_ordinis_mdm/README.md Screen 3.

What's в Phase A (drawer shell + sticky chrome):

- src/components/record/RecordDrawer.tsx (NEW)
  - Two widths: 520px default / 880px wide, toggle через ‹/› button.
    Persists в localStorage 'ord-drawer-wide'.
  - Sticky header: caption (cap style) + recordId mono + width-toggle +
    history button (edit mode) + ×.
  - Sticky footer: [Удалить] (danger, edit mode) · 'не сохранено · N полей'
    caption · [Отмена] · [Сохранить] (drives form submit через form attr).
  - Built на shadcn Sheet primitive + Radix Dialog. Override sheetVariants
    inline для transition-able max-width (variant size не toggle'ится
    smoothly без animate prop).
  - aria-labelledby wires DialogTitle к содержимому header.
  - Mobile <880px: w-full fullscreen (per handoff responsive table).

- src/components/form/SchemaDrivenForm.tsx
  - New optional props: formId, onDirtyChange.
  - Когда formId передан — внутренний FormActions footer прячется (drawer
    footer driving submit через <button form={formId} type=submit>).
  - Dirty-count считается из RHF formState.dirtyFields (top-level + data.*).
  - useEffect surfaces count к parent — drawer footer caption updates live.

- src/routes/dictionaries.$name.tsx
  - Modal create/edit -> RecordDrawer.
  - State: + recordDirtyCount (reset на close).
  - onDelete wires к existing close-confirm flow (kind: 'close-confirm').
  - onHistory wires к existing setHistoryKey RecordHistoryDrawer.

- src/i18n.ts
  - + form.delete, form.unsavedNFields_one/few/many/other (i18next plural rules).
  - + drawer.{collapse,expand,close,captionEdit,captionCreate}.

Phase B (TODO Stage 3.5b — separate MR):
- Section anchor chips (sticky strip)
- Wide-mode left ToC rail (180px)
- Sticky toolbar: search + 'только заполненные' toggle + counter
- Requires x-section schema metadata в backend properties (CEO open question #5 в handoff)

Tests: 8 files, 116 tests pass. TS strict clean.
2026-05-11 14:39:38 +03:00
Zimin A.N. b94912789f fix(fonts): semantic typography utilities per handoff (7 roles)
Заменяет blanket override из MR !45 на типизированную type scale per
design_handoff_ordinis_mdm/README.md "Scale" section.

Семь semantic @utility в styles.css:
  text-title-xl  22/600   — modal title, section header
  text-title-lg  17/600   — page title в editor
  text-title-md  15/600   — dictionary card title
  text-body      13/400   — workhorse: body, buttons, tabs, inputs
  text-cell      12.5/500 — table cell text
  text-mono      11/500   — Mono: IDs, dates, FK refs (font-mono baked in)
  text-cap       10.5/600 — Tektur UPPERCASE caption (font/uppercase baked in)

Audit phases:
  P1: добавил 7 утилит, font/uppercase/tracking baked где надо
  P2: 43 deterministic codemods (font-mono+text-2xs/[11px] → text-mono,
      [15/17/22]px+font-semibold → text-title-*, [12.5]px → text-cell,
      [10.5]px+uppercase+tracking → text-cap)
  P3: 64 text-sm → text-body (handoff workhorse), 84 text-2xs context-aware
      (TableCell → text-cell, bare → text-cell, плюс cleanup caps в backticks
      template literals который Phase 2 пропустил), 25 text-xs (6 → text-mono
      когда с font-mono, 19 → text-cell), 8 titles text-base/lg → text-title-*
  P4: убрал --text-2xs override (no users), оставил --text-sm: 13px scoped
      к @nstart/ui passthrough (см. comment в styles.css — убирается в Stage 3.9)

Stats:
  text-body: 69 | text-cell: 99 | text-mono: 50 | text-cap: 42
  text-title-xl: 5 | text-title-lg: 5 | text-title-md: 7
  text-sm/text-2xs/text-xs в src/: 0 (только в styles.css комментариях)

Поведение всех 277 typography usages теперь явно соответствует handoff —
каждое место осознанно выбрано под роль, не плажирующий override.
2026-05-11 14:31:35 +03:00
Zimin A.N. e62633d903 fix(version): read CI_COMMIT_TAG as source of truth, package.json as fallback
Previously: vite read package.json.version only, requiring manual bump
before each tag pipeline (manual sync ломалось — MR !44 пример костыля).

Now: vite reads CI_COMMIT_TAG (passed via Dockerfile ARG GIT_TAG from
.gitlab-ci.yml .docker_base) → strips 'v' prefix → exposes как __APP_TAG__.
'none' sentinel filtered → empty string → fallback к package.json.

VersionBadge priority chain:
  1. serverTag (что прод RUNS, через /api/v1/version)
  2. clientTag (что build'илось на tag pipeline)
  3. v${clientVersion} (package.json fallback floor для local / branch)

Dockerfile: add ARG GIT_TAG=none, ENV CI_COMMIT_TAG=$GIT_TAG (mirrors
существующий pattern с GIT_COMMIT → CI_COMMIT_SHORT_SHA).

Revert package.json 1.3.1 → 1.2.0 (manual bump из MR !44). Версия теперь
follows tag, не наоборот.
2026-05-11 14:19:50 +03:00