Commit Graph

311 Commits

Author SHA1 Message Date
Александр Зимин 90dd7f8c5d Merge branch 'feat/keyboard-shortcuts' into 'main'
feat(editor): keyboard shortcuts j/k/Enter/n per handoff

See merge request 2-6/2-6-4/terravault/ordinis!60
2026-05-11 13:00:50 +00: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
Александр Зимин e52a42ab14 Merge branch 'feat/workflow-banner-table-a11y' into 'main'
feat(handoff): WorkflowBanner + table a11y/responsive + toast styling

See merge request 2-6/2-6-4/terravault/ordinis!59
2026-05-11 12:58:20 +00: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
Александр Зимин 5883adc05d Merge branch 'fix/table-thead-and-horizontal-overflow' into 'main'
fix(table+layout): TableHead = THEAD wrapper + kill horizontal scroll

See merge request 2-6/2-6-4/terravault/ordinis!58
2026-05-11 12:49:35 +00: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
Александр Зимин d9670a9cae Merge branch 'fix/tailwind-merge-custom-utilities' into 'main'
fix(ui): tailwind-merge config + CI image mirror

See merge request 2-6/2-6-4/terravault/ordinis!57
2026-05-11 12:47:50 +00: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
Александр Зимин 1d978d2be8 Merge branch 'feat/mobile-responsive' into 'main'
feat(mobile): Sidebar drawer <1024px + Modal/Sheet fullscreen <880px

See merge request 2-6/2-6-4/terravault/ordinis!56
2026-05-11 12:39:31 +00: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
Александр Зимин 706c31b2d7 Merge branch 'fix/ci-detection-via-short-sha' into 'main'
fix(version): CI detect via CI_COMMIT_SHORT_SHA

See merge request 2-6/2-6-4/terravault/ordinis!55
2026-05-11 12:30:06 +00: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
Александр Зимин 24e621ced4 Merge branch 'feat/stage-3.4-events-json-polish' into 'main'
feat(editor): Stage 3.4 polish — Events embedded + JSON highlight

See merge request 2-6/2-6-4/terravault/ordinis!54
2026-05-11 12:28:37 +00: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
Александр Зимин 27bcdf5ca0 Merge branch 'ci/semantic-release-auth-fix' into 'main'
ci(release): fix git auth + fail-fast on missing GITLAB_TOKEN

See merge request 2-6/2-6-4/terravault/ordinis!53
2026-05-11 12:22:05 +00:00
Zimin A.N. 1e93d74420 ci(release): fix git auth + fail-fast when GITLAB_TOKEN missing
semantic-release провалился с 'HTTP Basic: Access denied' потому что:

1. CI default git remote использует CI_JOB_TOKEN scope — не может push'ить
   tags/commits в protected refs (main branch).
2. GITLAB_TOKEN ещё не настроен в Settings → CI/CD → Variables.
3. Без explicit git remote rewrite semantic-release/git plugin падает
   с cryptic 'Authentication failed' вместо понятного 'token missing'.

Fixes:
- before_script: fail-fast если GITLAB_TOKEN пустой — печатает setup
  инструкцию (write_repository + api scopes, Masked + Protected flags)
  и exit 0. Pipeline не краснеет (allow_failure: true).
- git remote set-url origin https://gitlab-ci-token:${GITLAB_TOKEN}@... —
  переопределяет default remote чтобы git push мог работать с
  Project Access Token'ом.
- git config user.email/name — semantic-release commit author.

После настройки GITLAB_TOKEN в GitLab UI следующий push в main создаст
первый авто-tag (v1.3.0 minimum по conventional commits с момента v1.2.2).
2026-05-11 15:19:22 +03:00
Александр Зимин 73d8fa585a Merge branch 'feat/stage-3.9-drop-nstart-ui' into 'main'
feat(ui): drop @nstart/ui dependency (Stage 3.9)

See merge request 2-6/2-6-4/terravault/ordinis!52
2026-05-11 12:17:56 +00: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
Александр Зимин eac43cc5a3 Merge branch 'fix/font-cascade-handoff' into 'main'
fix(fonts): explicit font-family + cascade conflicts (browser-verified)

See merge request 2-6/2-6-4/terravault/ordinis!51
2026-05-11 11:58:47 +00: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
Александр Зимин f12b7e0ced Merge branch 'ci/semantic-release-automation' into 'main'
ci: semantic-release automation for tag/CHANGELOG (Option D)

See merge request 2-6/2-6-4/terravault/ordinis!50
2026-05-11 11:51:27 +00: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
Александр Зимин d5c2e27e27 Merge branch 'fix/version-channel-label' into 'main'
fix(version): channel-based label (dev/local), no stale package.json fallback

See merge request 2-6/2-6-4/terravault/ordinis!49
2026-05-11 11:45:18 +00: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
Александр Зимин 7f6ffd0d11 Merge branch 'feat/stage-3.5a-record-drawer' into 'main'
feat(record): Stage 3.5a RecordDrawer slide-over (Phase A)

See merge request 2-6/2-6-4/terravault/ordinis!48
2026-05-11 11:39:57 +00: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
Александр Зимин 2d3125f544 Merge branch 'fix/v1.3.1-fonts-semantic-audit' into 'main'
fix(fonts): semantic typography utilities per handoff (7 roles)

See merge request 2-6/2-6-4/terravault/ordinis!47
2026-05-11 11:31:55 +00: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
Александр Зимин 715b009e00 Merge branch 'fix/v1.3.1-version-from-ci-tag' into 'main'
fix(version): CI_COMMIT_TAG as source of truth, no more manual bumps

See merge request 2-6/2-6-4/terravault/ordinis!46
2026-05-11 11:20:12 +00: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
Александр Зимин c11b00a81d Merge branch 'fix/v1.3.1-fonts-handoff' into 'main'
fix(fonts): align all text sizes to handoff spec via Tailwind theme

See merge request 2-6/2-6-4/terravault/ordinis!45
2026-05-11 11:13:25 +00:00
Zimin A.N. 71432e9ae7 fix(fonts): align all text sizes to handoff spec via Tailwind theme overrides
Глобальные правки через @theme в styles.css вместо 200+ точечных правок:

- text-sm: 14px -> 13px (handoff workhorse — body, buttons, tabs)
- text-2xs: undefined -> 11px (handoff IDs, mono meta — было невидимо)
- @utility text-cap: 10.5px Tektur uppercase 0.10em (handoff caps)

Codemod (30 sites): text-2xs + uppercase + tracking-[0.10em] -> text-cap.
Sidebar section labels consolidated с explicit text-[10.5px] на text-cap.

Покрывает 64 text-sm + 148 text-2xs автоматически. Table cells (12.5px)
и page/modal titles (17px/22px) остаются explicit text-[Npx] arbitrary
values по месту — нет slots в Tailwind scale.
2026-05-11 14:12:57 +03:00
Александр Зимин f2db543be4 Merge branch 'fix/v1.3.1-version-badge' into 'main'
fix(version): bump to 1.3.1 and show 8-char commit hash

See merge request 2-6/2-6-4/terravault/ordinis!44
2026-05-11 11:09:31 +00:00
Zimin A.N. 780de70fc5 fix(version): bump to 1.3.1 and show 8-char commit hash
VersionBadge показывал 1.2.0 (stale package.json) и truncate коммита на
7 символов. Пользователь ожидал v1.3.1 · 4c7fa8d7 (full 8-char short).

- package.json: 1.2.0 -> 1.3.1
- VersionBadge: substring(0, 7) -> substring(0, 8)
- vite.config: git rev-parse --short=8 для consistency с CI_COMMIT_SHORT_SHA
2026-05-11 14:09:03 +03:00
Александр Зимин 4c7fa8d724 Merge branch 'fix/v1.3.1-fonts-sidebar' into 'main'
fix(admin-ui): sidebar fonts per handoff

See merge request 2-6/2-6-4/terravault/ordinis!43
2026-05-11 10:56:46 +00:00
Александр Зимин 0297745d13 fix(admin-ui): sidebar fonts per handoff 2026-05-11 10:56:46 +00:00
Zimin A.N. f72aac6398 fix(admin-ui): sidebar font sizes per handoff spec
- Section labels (WORKFLOW / АДМИНИСТРИРОВАНИЕ): text-2xs (12px) → 10.5px
  Tektur per handoff caption spec. "Администрирование" не вылазит за
  220px sidebar.
- Nav items: text-sm (14px) → text-[13px] handoff body workhorse size.
- Badge counters: text-2xs (12px) → text-[11px] (mono IDs spec).

Partial — full font audit pending (Stage 3.x font sweep). См. nsi-hot.md.
2026-05-11 13:56:38 +03:00
Александр Зимин c2ce7a0afb Merge branch 'feat/v1.3.0-editor-tabs' into 'main'
feat(admin-ui): editor 6-tab layout (Stage 3.4)

See merge request 2-6/2-6-4/terravault/ordinis!42
2026-05-11 10:54:33 +00:00
Александр Зимин 640c890d0b feat(admin-ui): editor 6-tab layout (Stage 3.4) 2026-05-11 10:54:33 +00:00
Zimin A.N. 0ddba871f7 feat(admin-ui): editor 6-tab layout (Stage 3.4)
Dictionary editor route теперь имеет TabBar (handoff design Stage 3.4):
  Records | Relations | Fields | JSON | Events | History

URL-synced через ?tab= (default: records). Backward-compat: старый
?view=hub auto-remap'нется в ?tab=relations при mount'е.

Tab contents:
- records (default): существующий records table + filters + edit modal
  flow — без изменений, обёрнут в conditional render
- relations: DictionaryHubView (был раньше при view=hub)
- fields: schema properties summary — name/type/required/unique/i18n/FK/desc
- json: pretty-printed schemaJson read-only
- events: link к /audit с pre-filled dict filter
- history: placeholder (schema version timeline — backend endpoint pending)

EditorTabBar — inline component с handoff styling (border-b border-line +
active accent border + font-semibold), -mb-px overlap для clean look.

Hub view's neighbor cards теперь search={tab:'relations'} вместо
view='hub' (consistent с new param).

DictionaryDependentsPanel рендерится только в Records tab — auto Hub view
в Relations tab уже включает dependents через свой layout.

Tests: 116/116 PASS, build green, TS clean.
2026-05-11 13:54:26 +03:00
Александр Зимин 5ffa8d3c86 Merge branch 'fix/v1.2.3-banner-generic' into 'main'
fix(admin-ui): UpdateBanner generic message

See merge request 2-6/2-6-4/terravault/ordinis!41
2026-05-11 10:39:35 +00:00
Александр Зимин dc0002f379 fix(admin-ui): UpdateBanner generic message 2026-05-11 10:39:35 +00:00
Александр Зимин d7be927a7a Merge branch 'fix/v1.2.2-update-banner-none' into 'main'
fix(admin-ui): UpdateBanner none-version + tokens

See merge request 2-6/2-6-4/terravault/ordinis!40
2026-05-11 10:29:45 +00:00
Александр Зимин 4122aa1fc6 fix(admin-ui): UpdateBanner none-version + tokens 2026-05-11 10:29:45 +00:00
Александр Зимин 7566bd50a9 Merge branch 'fix/v1.2.1-version-display-search' into 'main'
fix(admin-ui): version display + global search submit

See merge request 2-6/2-6-4/terravault/ordinis!39
2026-05-11 10:18:22 +00:00
Александр Зимин 580529b977 fix(admin-ui): version display + global search submit 2026-05-11 10:18:22 +00:00
Александр Зимин 4959dcf30b Merge branch 'feat/v1.2.3-textarea-and-prep' into 'main'
feat(admin-ui): shadcn TextArea (Stage 3.8a)

See merge request 2-6/2-6-4/terravault/ordinis!38
2026-05-11 09:13:10 +00:00
Александр Зимин 649b7c4244 feat(admin-ui): shadcn TextArea (Stage 3.8a) 2026-05-11 09:13:10 +00:00
Александр Зимин 117136be84 Merge branch 'feat/v1.2.2-modals-toast-timetravel' into 'main'
feat(admin-ui): sonner Toast + TimeTravel polish (Stage 3.6+3.7)

See merge request 2-6/2-6-4/terravault/ordinis!37
2026-05-11 09:11:21 +00:00
Александр Зимин e2f1c3527b feat(admin-ui): sonner Toast + TimeTravel polish (Stage 3.6+3.7) 2026-05-11 09:11:21 +00:00