Commit Graph

393 Commits

Author SHA1 Message Date
Александр Зимин fb64ca4350 refactor(reviews): interaction state polish — 5 UX fixes 2026-05-14 14:13:57 +00:00
semantic-release-bot 513b57b6b5 chore(release): v2.26.0
## [2.26.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.25.1...v2.26.0) (2026-05-14)

###  Features

* **reviews:** "Мои" tab — maker self-tracking with status filter ([be58b61](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/be58b6191383e0cecd9c2a06ef99b1835ecd3dce))
2026-05-14 14:11:18 +00:00
Александр Зимин be58b61913 feat(reviews): "Мои" tab — maker self-tracking with status filter 2026-05-14 14:06:30 +00:00
semantic-release-bot a6a07428ed chore(release): v2.25.1
## [2.25.1](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.25.0...v2.25.1) (2026-05-14)

### ♻️ Refactoring

* **approval-ux:** eng-review fixes — extract helpers, add tests, register i18n ([4c29f4e](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/4c29f4e7cbab6be552603f7791f1de35a882cb71))
2026-05-14 13:57:00 +00:00
Александр Зимин 4c29f4e7cb refactor(approval-ux): eng-review fixes — extract helpers, add tests, register i18n 2026-05-14 13:52:12 +00:00
semantic-release-bot 31ad140a02 chore(release): v2.25.0
## [2.25.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.24.0...v2.25.0) (2026-05-14)

###  Features

* **approval-ux:** inline draft CTA + reviewer diff summary + tightened drawer ([fe7f9cd](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/fe7f9cd01f8f2150ecffdc300e260fedb7f8a1cf))
2026-05-14 13:04:42 +00:00
Andrei Zimin fe7f9cd01f feat(approval-ux): inline draft CTA + reviewer diff summary + tightened drawer
Acts on the two "build now" items from the design review of the approval
workflow UX (other five went to TODOS.md). Plus the TODOS.md file itself,
authored during the same review.

1. **Inline "Создать черновик с моими изменениями" CTA**
   (DictionaryEditorDialog + CreateSchemaDraftModal)

   Was: when a maker edited an approval-required dict, Save disabled and a
   warning told them to "close the editor and click 'Создать черновик схемы'
   on the dict page." Three friction steps and a hunt for a button.

   Now: the warning Alert carries an action button — "Создать черновик с
   моими изменениями" — that opens CreateSchemaDraftModal in-place with the
   maker's in-progress schemaJson pre-loaded. They add a reason and submit.
   No retyping, no hunt.

   Implementation:
   - CreateSchemaDraftModal accepts optional `initialProposedSchema` prop.
     Seed order: edit-mode draft → caller pre-fill → live HEAD.
   - DictionaryEditorDialog mounts the modal inline and triggers it from the
     Alert action. Closes the editor on successful create.

2. **Diff summary chips in ReviewDrawer**

   Was: two raw JSON dumps side by side. Reviewer had to eyeball-diff long
   records to find the actual edit.

   Now: a row of summary chips above the panes — "+N added, −M removed,
   ~K changed" with the field names inline. Computed shallow client-side
   (shallowDiffSummary helper) — ordinis records are flat-ish at the top
   level, no need for a backend diff endpoint. Skipped for CLOSE op (no
   proposed) and when both sides are null.

3. **Tightened drawer header + trimmed empty-state copy**

   Was: single flex-wrap line with four spans (badge + bk + maker + ts) —
   visually busy. Plus a verbose empty-state description: "Когда maker
   отправит schema draft на ревью, он появится здесь."

   Now: two-row layout (identity row on top, provenance row below) so the
   eye lands on Badge + BK first. Empty-state copy shortened to "Очередь
   пуста" / "Схемы на ревью появятся здесь."

Other five gaps (Мои tab, full state-coverage pass, DESIGN.md, a11y audit,
maker notifications) recorded in TODOS.md.
2026-05-14 15:56:50 +03:00
semantic-release-bot bdae52ff28 chore(release): v2.24.0
## [2.24.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.23.1...v2.24.0) (2026-05-14)

###  Features

* **drafts:** audit + outbox для record-draft lifecycle (audit findings) ([0003fb6](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/0003fb68019a39a515f5046df3f039dc3a48f79c))

### 🐛 Fixes

* **approval:** allow non-breaking schema changes inline ([d338c52](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/d338c52c63876d68fabdd5133d6b2e834829a9f9))
* **approval:** close 2 more bypass paths from security audit ([4e09027](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/4e0902706e73e3860eab2618676389aca230e64c))
* **ui:** DictionaryEditorDialog disables Save when draft workflow needed ([898cb34](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/898cb345ec462f2b322a96b3cf5cbc830e4cc079))
2026-05-14 12:52:02 +00:00
Александр Зимин 898cb345ec fix(ui): DictionaryEditorDialog disables Save when draft workflow needed 2026-05-14 12:33:43 +00:00
semantic-release-bot ce3b495561 chore(release): v2.23.1
## [2.23.1](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.23.0...v2.23.1) (2026-05-14)

### 🐛 Fixes

* **schema:** enforce approvalRequired on PUT /{name} + PATCH /{name}/schema ([018ebff](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/018ebff2072952828f6b5e7dbd6b19f204e0432a))
2026-05-14 12:15:02 +00:00
semantic-release-bot 7202265da1 chore(release): v2.23.0
## [2.23.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.22.1...v2.23.0) (2026-05-14)

###  Features

* **user-display:** KeycloakAdminUserResolver — Phase 2 on-demand lookup ([83caf9c](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/83caf9c3c80e947dc3ad4ff46a194903192ca1f3))
* **user-display:** persistent DB cache + Keycloak resolver hook (Phase 1+2 wiring) ([ec0c74a](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/ec0c74afdf627eab43310c987f009a7a3a1a89d4))

### 🐛 Fixes

* **ui:** UserCell в changelog/events/history — ещё 6 мест ([455ca2f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/455ca2ff264f4955b5c5b1259c11ce4c3fdd48ce))
2026-05-14 12:00:15 +00:00
Александр Зимин a73549d38d Merge branch 'feat/user-display-persistent-cache' into 'main'
feat(user-display): persistent DB cache + Keycloak Admin lookup (Phase 1+2)

See merge request 2-6/2-6-4/terravault/ordinis!180
2026-05-14 11:52:20 +00:00
Andrei Zimin 455ca2ff26 fix(ui): UserCell в changelog/events/history — ещё 6 мест
После !178 (record drawer + RecordHistoryDrawer + webhook detail) ещё
осталось 6 точек где раньше рендерился UUID. Все они в "истории
словаря" и tab "События":

1. ChangelogDiffModal:57 — header "автор: <name>" в diff modal.
   Было: `{data.publishedBy.name}` (тип ChangelogPublisher.name —
   docstring буквально говорит "Backend пока эхает sub").
   Стало: <UserCell uuid={data.publishedBy.sub} />.

2. TimeTravelModal:686 — список изменений, имя автора рядом с датой.
3. TimeTravelModal:898 — RIGHT panel "История этой версии", dt/dd
   pair с автором.
4. TimeTravelModal ComparePanel:414 — "· {author}" suffix,
   author там DictionaryDetail.updatedBy (тоже UUID).

5. dictionaries.$name.tsx:2263 (events tab, EventsTabContent row) —
   `{r.userId ?? 'anonymous'}` raw. Сменил на UserCell для не-null
   userId, anonymous-fallback оставил без UserCell (нечего lookup'ать).

6. dictionaries.$name.tsx:2399 (history tab, HistoryTabContent entry)
   — `{entry.publishedBy.name}` raw.

Всё через тот же useUserDisplay hook что и в !177 — backend
endpoint /admin/users/{sub}/display + JwtUserCaptureFilter cache.
Если sub нет в cache, UserCell fallback'ит на shortened UUID
(текущее поведение из !177).

ChangelogPublisher type comment в src/api/client.ts уже намекал что
.name это placeholder — useUserDisplay теперь реально резолвит.
2026-05-14 14:33:20 +03:00
semantic-release-bot b5920e4d5f chore(release): v2.22.1
## [2.22.1](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.22.0...v2.22.1) (2026-05-14)

### 🐛 Fixes

* **ui:** wrap remaining UUIDs into UserCell — 3 missed spots ([93b2676](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/93b267699435d36c3bac9c465f400b60d6fcaa65))
2026-05-14 11:13:36 +00:00
Andrei Zimin 93b2676994 fix(ui): wrap remaining UUIDs into UserCell — 3 missed spots
MR !177 added /admin/users/{sub}/display + UserCell + useUserDisplay,
but only some renderers were migrated. Three places still rendered
raw UUID strings:

1. reviews.tsx:466 — record draft drawer ("Согласование draft" header)
   showed `{draft.makerId}` raw. Tables on the same page already use
   <UserCell uuid={d.makerId} />. Verified via prod staging:
   soloviev.da approved a record draft today and the drawer header
   showed the maker as "77ec2cc8-98e3-4d70-8037-94038fcbde67" instead
   of the username.
2. RecordHistoryDrawer.tsx:110 — version history `updatedBy` raw.
3. webhooks.$id.tsx:235 — webhook subscription `createdBy` raw.

All three switched to <UserCell uuid={...} />, matching the existing
pattern. Added the import where missing. Wrapped each in
inline-flex so the UserCell sits next to the label cleanly.

No backend / API changes — same /admin/users/{sub}/display endpoint
populated by JwtUserCaptureFilter (MR !177).
2026-05-14 14:07:02 +03:00
semantic-release-bot 969395e00d chore(release): v2.22.0
## [2.22.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.21.0...v2.22.0) (2026-05-14)

###  Features

* **admin:** scope-gated audit и outbox endpoints ([da17930](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/da17930def89b2fea51be1b8e45b4e5fabe752d1))
* **approval:** enforce per-dictionary approvalMinRole (Phase 2) ([5187845](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/5187845047ba20a6306f5dce8a65f1d4889811fa))
* **records:** close / bulk-close через draft когда approvalRequired ([d66db60](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/d66db60b8496a218501ebade77ff68cc232849d4))
* **users:** /admin/users/{sub}/display endpoint + UserCell uses it ([be90077](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/be90077520607998078a3df5bb5fbe24f1a1bf6e))

### 🐛 Fixes

* **reviews:** friendly error в ReviewDrawer вместо AxiosError ([9436068](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/94360681b6d6412d9d0ae4c5dd4b69600274a034))

### 📝 Docs

* **rbac:** обновить RBAC sections после Phase 1d + Phase 2 ([c41e345](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/c41e34545b7a23e24c24196c06a7889724f63338))
2026-05-14 10:37:30 +00:00
Александр Зимин 95c2b46dd3 Merge branch 'feat/user-display-endpoint' into 'main'
feat(users): /admin/users/{sub}/display endpoint + UserCell uses it

See merge request 2-6/2-6-4/terravault/ordinis!177
2026-05-13 16:42:46 +00:00
Александр Зимин be90077520 feat(users): /admin/users/{sub}/display endpoint + UserCell uses it 2026-05-13 16:42:46 +00:00
Александр Зимин c8d90930e3 Merge branch 'feat/phase2-approval-min-role-enforcement' into 'main'
feat(approval): enforce per-dictionary approvalMinRole (Phase 2)

See merge request 2-6/2-6-4/terravault/ordinis!172
2026-05-13 16:42:09 +00:00
Александр Зимин 5187845047 feat(approval): enforce per-dictionary approvalMinRole (Phase 2) 2026-05-13 16:42:09 +00:00
Александр Зимин 6188062c34 Merge branch 'feat/close-via-draft-when-approval-required' into 'main'
feat(records): close / bulk-close через draft когда approvalRequired

See merge request 2-6/2-6-4/terravault/ordinis!175
2026-05-13 16:41:09 +00:00
Александр Зимин d66db60b84 feat(records): close / bulk-close через draft когда approvalRequired 2026-05-13 16:41:09 +00:00
Александр Зимин 94360681b6 fix(reviews): friendly error в ReviewDrawer вместо AxiosError 2026-05-13 16:41:05 +00:00
semantic-release-bot 0e165f69ca chore(release): v2.21.0
## [2.21.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.20.0...v2.21.0) (2026-05-13)

###  Features

* **records:** auto-route create/edit через draft когда approvalRequired ([6eb56cf](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/6eb56cffe4383d6d488fcd0a17f710b83b0a7585))
2026-05-13 15:34:51 +00:00
Александр Зимин 6eb56cffe4 feat(records): auto-route create/edit через draft когда approvalRequired 2026-05-13 15:29:11 +00:00
semantic-release-bot daa4b284c9 chore(release): v2.20.0
## [2.20.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.19.2...v2.20.0) (2026-05-13)

###  Features

* **my-drafts:** schema drafts тоже в "Мои черновики" ([f096b57](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/f096b5746d256220f569616813b8ec886b27247d))
2026-05-13 12:54:56 +00:00
Александр Зимин f096b5746d feat(my-drafts): schema drafts тоже в "Мои черновики" 2026-05-13 12:45:45 +00:00
semantic-release-bot 4af79669f8 chore(release): v2.19.2
## [2.19.2](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.19.1...v2.19.2) (2026-05-13)

### 🐛 Fixes

* **auth:** read roles из access_token, не id_token (sidebar reviews hidden bug) ([b6ee703](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/b6ee703577eb868b4dc452b64b87e3ba779b3596))
2026-05-13 11:45:51 +00:00
Александр Зимин b6ee703577 fix(auth): read roles из access_token, не id_token (sidebar reviews hidden bug) 2026-05-13 11:39:45 +00:00
semantic-release-bot a25a997f34 chore(release): v2.19.1
## [2.19.1](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.19.0...v2.19.1) (2026-05-13)

### 🐛 Fixes

* **webhook:** scope_filter Hibernate ordinal bug + UI toggle для active ([d4ee30a](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/d4ee30a7dd09119b5d38c50b5dda212b5b3e8f88))
2026-05-13 11:20:20 +00:00
Александр Зимин b1b66bf6a8 Merge branch 'fix/webhook-scope-filter-enum-storage' into 'main'
fix(webhook): scope_filter Hibernate ordinal bug + UI toggle active

See merge request 2-6/2-6-4/terravault/ordinis!167
2026-05-13 11:05:09 +00:00
semantic-release-bot e03743b250 chore(release): v2.19.0
## [2.19.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.18.0...v2.19.0) (2026-05-13)

###  Features

* **sidebar:** role-based visibility для /reviews ([5fb1658](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/5fb1658880ad62cc641a939ae9344ec93ca594ad))

### 🐛 Fixes

* **auth:** @Autowired на WorkflowRoles main ctor ([4fe7f1e](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/4fe7f1e800b4ebe304a8011f5fc2cbb0bab67f40))
2026-05-13 11:04:11 +00:00
Zimin A.N. d4ee30a7dd fix(webhook): scope_filter Hibernate ordinal bug + UI toggle для active
Два бага в /webhooks (user report):

1. **500 при выборе scope в форме**

POST /admin/webhooks/subscriptions с scopeFilter:["PUBLIC"] возвращал 500.

Root cause: WebhookSubscription.scopeFilter был List<DataScope> с
@JdbcTypeCode(SqlTypes.ARRAY). Hibernate сериализовал enum в TEXT[] как
ordinal (е.g. {0} для PUBLIC), а DB CHECK constraint
chk_webhook_scope_filter ожидает строковые имена. Insert падал
DataIntegrityViolationException → 500.

Fix: field теперь List<String> (raw enum names). Getter/setter
конвертируют в/из DataScope — API surface (DTO, service signatures,
WebhookDispatcher) не меняется.

2. **Нет toggle active/inactive в UI**

useUpdateWebhook hook был, но не использовался в UI. Status показывался
только read-only badge'ом. Невозможно деактивировать webhook через UI.

Fix: добавлен toggle button рядом с Status badge на /webhooks/:id.
Sends PUT с inverted active flag (backend treats PUT как full replace,
поэтому пересобираем full payload).

i18n: 'webhooks.action.activate' / 'deactivate' default labels (RU).
2026-05-13 14:02:35 +03:00
Zimin A.N. 5fb1658880 feat(sidebar): role-based visibility для /reviews
Phase 1d enhancement: 'Согласования' пункт сайдбара скрыт если у user'а
нет approver/reviewer ролей. Раньше показывался любому authenticated —
оператор без reviewer прав видел queue, кликал и получал пустую
страницу (или 403 если бы пробовал approve).

Visibility rules:
- 'Мои черновики' — auth (maker может быть любой)
- 'Согласования' — auth + (ordinis:schema:reviewer OR ordinis:record:reviewer
  OR ordinis:admin super-role) — иначе hidden
- 'Outbox' / 'Webhooks' / 'Аудит' — auth (admin-y но backend не gated
  отдельной ролью)

Новый NavItem.reviewerOnly flag — фильтр в visibleSections учитывает
оба flag'а (authRequired + reviewerOnly).

Применено и к desktop Sidebar и к MobileSidebar (DRY refactor — отдельный
task).
2026-05-13 13:52:40 +03:00
semantic-release-bot e183ff1d6c chore(release): v2.18.0
## [2.18.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.17.0...v2.18.0) (2026-05-13)

###  Features

* **auth:** WorkflowRoles configurable claim path + ordinis:admin super-role ([36c06ae](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/36c06ae1da52ebf515eaa5227c942f8343071188))
2026-05-13 10:29:04 +00:00
Zimin A.N. 36c06ae1da feat(auth): WorkflowRoles configurable claim path + ordinis:admin super-role
User report: token has roles в resource_access.ordinis.roles (client-scoped),
backend WorkflowRoles читал hardcoded realm_access.roles → forbidden_role
для всех reviewer actions.

Backend:
- WorkflowRoles: depends OrdinisAuthProperties → reads via rolesClaim path
  (же что ScopeContext). Default realm_access.roles, на staging/prod env
  var ORDINIS_AUTH_ROLES_CLAIM=resource_access.ordinis.roles (уже wired
  в helm helper).
- Nested claim path resolver — mirror ScopeContext.readClaimByPath
- New super-role ADMIN ('ordinis:admin') — holder автоматически проходит
  любой workflow check без явного назначения отдельных ролей. Convenience
  для small teams.

Frontend (usePermissions.ts):
- tokenRoles() читает оба claim path (realm_access.roles +
  resource_access.ordinis.roles) и объединяет — UI gate работает
  независимо от backend config
- hasRole() recognizes 'ordinis:admin' как super-role override
- ROLE_ADMIN exported

Docs:
- docs/operator/rbac.md: добавлен ordinis:admin row, JWT claim path note,
  Admin profile updated (super-role vs composite explanation)
2026-05-13 13:17:54 +03:00
semantic-release-bot 031a582167 chore(release): v2.17.0
## [2.17.0](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.16.4...v2.17.0) (2026-05-13)

###  Features

* **auth:** Phase 1d — backend role enforcement для workflow ([24aa94d](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/24aa94d13007b7d2d9716a6afb7fdbc54a32d8ba))

### ♻️ Refactoring

* **auth:** domain-specific role names (consistent convention) ([c5b00ff](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/c5b00ff62314942f6b2cee09993d9ddd8648e8e2))

### 📝 Docs

* **rbac:** добавить матрицу realm-ролей Keycloak ([1dba947](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/1dba9476db036d4f5688d409aae4c39b5b5a441c))
2026-05-13 10:06:16 +00:00
Zimin A.N. c5b00ff623 refactor(auth): domain-specific role names (consistent convention)
User feedback: универсальный 'ordinis:approver' отступал от convention
'ordinis:<domain>:<action>'. Меняю на domain-specific:

WorkflowRoles constants:
- APPROVER → SCHEMA_REVIEWER ('ordinis:schema:reviewer')
- PUBLISHER → SCHEMA_PUBLISHER ('ordinis:schema:publisher')
- Новая RECORD_REVIEWER ('ordinis:record:reviewer')

Applied:
- SchemaDraftService.decide() → require SCHEMA_REVIEWER
- SchemaDraftService.publish() → require SCHEMA_PUBLISHER
- DraftService.approve() / .reject() → require RECORD_REVIEWER

Frontend usePermissions.ts:
- ROLE_APPROVER + ROLE_PUBLISHER → ROLE_SCHEMA_REVIEWER +
  ROLE_SCHEMA_PUBLISHER + ROLE_RECORD_REVIEWER
- Новый hook useCanReviewRecord() для record draft actions

Keycloak setup (updated):
- ordinis:schema:reviewer
- ordinis:schema:publisher
- ordinis:record:reviewer
(Composite role 'ordinis:admin' = all три + scope roles — для удобства.)
2026-05-13 12:44:23 +03:00
Zimin A.N. 24aa94d130 feat(auth): Phase 1d — backend role enforcement для workflow
User explicitly requested 'Phase 1d пора'. До этого MR'а backend гейтил
только maker-checker (self_approve_forbidden), frontend hooks возвращали
auth.isAuthenticated.

Реализация:

Backend WorkflowRoles (ordinis-rest-api/auth/WorkflowRoles.java):
- Component reads realm_access.roles из JWT claim
- Constants: ordinis:approver (decide/approve/reject), ordinis:publisher (publish)
- requireRole(role) throws 403 forbidden_role если missing
- Defensive parse: malformed/missing realm_access → empty roles
- 9 unit tests cover authenticated + anonymous + malformed JWT

Applied gates:
- SchemaDraftService.decide() — require ordinis:approver
- SchemaDraftService.publish() — require ordinis:publisher
- DraftService.approve() — require ordinis:approver
- DraftService.reject() — require ordinis:approver

WorkflowRoles is Optional в test ctors → unit tests без auth setup
не ломаются. Все existing tests должны проходить (legacy ctor overloads
сохранены).

Frontend usePermissions.ts:
- Stub return auth.isAuthenticated заменён на real role decode из
  auth.user.profile.realm_access.roles
- ROLE_APPROVER + ROLE_PUBLISHER constants exported
- Defensive parsing — graceful с missing/malformed claims
- useCanCreateSchemaDraft остаётся permissive (любой auth — maker'ом
  может быть anyone, scope ACL заведует видимостью)

Naming convention — colon-separated (consistent с ordinis:client:*
scope ролями), aligned с MR !161.

MIGRATION (BREAKING без правильной Keycloak setup):
1. Keycloak realm 'nstart' → Realm Roles → Create:
   - ordinis:approver
   - ordinis:publisher
2. Users → assign roles:
   - reviewer/approver users → ordinis:approver
   - publisher users → ordinis:approver + ordinis:publisher
3. После refresh JWT (logout/login) роли появляются в realm_access.roles

Без этого все decide/publish операции вернут 403 forbidden_role.
2026-05-13 12:36:53 +03:00
Zimin A.N. 1281654d32 chore(auth): rename schema workflow roles к colon-convention
Project uses Altum-style colon-separated realm roles:
- ordinis:client:public / internal / restricted (scope ACL, уже active)

Schema workflow roles (Phase 1d TODO) приводим к той же convention:
- ordinis:schema:reviewer (был ordinis-schema-reviewer)
- ordinis:schema:publisher
- ordinis:schema:maker

Изменения в usePermissions.ts:
- JSDoc обновлён с новой convention
- Constants ROLE_SCHEMA_* для discoverable rename'а в Phase 1d
- TODO comments указывают на constants, не bare strings

Phase 1d (когда подключится backend role check):
1. Uncomment role check в hooks
2. Backend SchemaDraftService.decide() — require role
2026-05-13 12:27:33 +03:00
semantic-release-bot dc556d12c8 chore(release): v2.16.4
## [2.16.4](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.16.3...v2.16.4) (2026-05-13)

### 🐛 Fixes

* **ui:** shared UserCell для display UUID actor'ов везде ([e6294ce](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/e6294ce79cc07c6f57c8d45e81a92507e01bb97b))
* **ui:** кнопки в schema draft drawer — RU label + flow row ([6ee88a8](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/6ee88a86bd32cfaa34ceb8dee140c00e629172b8))
2026-05-13 08:53:35 +00:00
Zimin A.N. 6ee88a86bd fix(ui): кнопки в schema draft drawer — RU label + flow row
User feedback (screenshot): 'Approve' остался английским в RU локали +
'Отозвать' улетал на отдельную строку справа из-за ml-auto.

Fix:
- i18n.ts: 'workflow.schemaDraft.actions.approve' default 'Approve' →
  'Одобрить'. EN остался 'Approve'.
- SchemaDraftDrawer.tsx: убрал ml-auto на withdraw button. Раньше это
  force pushed его в конец строки → flex-wrap отправлял на новую строку
  правым краем. Сейчас withdraw сидит сразу после reject в той же
  flow-row, с danger color для визуальной distinction. На узком экране
  всё ещё wrap'нется естественно (без 'плавающего' alignment).
2026-05-13 11:45:29 +03:00
Zimin A.N. e6294ce79c fix(ui): shared UserCell для display UUID actor'ов везде
Browser QA на staging показал что UUID actor'а render'ится raw во многих
местах кроме drawer (MR !158 — там был inline fix):
- /reviews Records tab — column AUTHOR
- /reviews Schemas tab — column AUTHOR
- /audit log — column USER

77ec2cc8-98e3-4d70-8037-94038fcbde67 — нечитаемо для оператора. Тот же
паттерн что я делал в MR !158, теперь shared helper:

src/lib/useUserDisplay.tsx:
- useUserDisplay(uuid) hook — returns { display, isMe, fullId }
- UserCell component — readonly cell с title=fullId tooltip
- Match current user (JWT sub claim) → 'Я • <preferred_username>'
- Other user → short UUID (8 chars) + full в title
- null/undefined → 'anonymous'

Применено:
- reviews.tsx:334,625 — records + schemas table AUTHOR cells
- audit.tsx:476 — USER cell
- SchemaDraftDrawer.tsx — refactor (был inline в MR !158 → теперь shared)

TODO Phase 1d: backend endpoint /admin/users/{sub}/display чтобы и для
не-current-user отображать username.
2026-05-13 11:43:04 +03:00
semantic-release-bot 38764648bf chore(release): v2.16.3
## [2.16.3](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.16.2...v2.16.3) (2026-05-13)

### 🐛 Fixes

* **sidebar,audit:** badges для Outbox + Webhooks, clarify audit scope label ([aec2cdd](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/aec2cdd644b8d6bf1846f0d26d1485ef669f5f98))
* **types:** SchemaReviewQueuePage.total → totalElements (match backend) ([aecd65e](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/aecd65e7d57a6853bf0744571da246d6db236071))
2026-05-13 08:10:21 +00:00
Zimin A.N. aec2cdd644 fix(sidebar,audit): badges для Outbox + Webhooks, clarify audit scope label
Sidebar:
- Outbox: бэйдж = outbox.pending (события queued, не отосланы). DLQ
  отдельно — alarming но не actionable из бэйджа, видно на /outbox.
- Webhooks: бэйдж = count активных subscriptions (w.active === true).
  User feedback: 'вебхуки активные есть а цифры с бэйджем нет'.

Audit log column header:
- 'Scope' → 'Доступ актора' (ru) / 'Actor scope' (en)
- Backend audit_log хранит userScope (claim из JWT актора), НЕ scope
  изменённого ресурса. User confusion: 'правки в public были а тут
  RESTRICTED' — потому что у пользователя scope-привилегия RESTRICTED.
  Уточнённая label убирает путаницу.
- TODO: backend feature — добавить dictionaryScope в AuditEntry чтобы
  показать обе ('Актор: RESTRICTED → Ресурс: PUBLIC' — двусторонний
  audit trail для compliance).
2026-05-13 11:03:47 +03:00
Zimin A.N. aecd65e7d5 fix(types): SchemaReviewQueuePage.total → totalElements (match backend)
User reported бэйдж 'Согласований' в sidebar показывал 0 несмотря на 1
pending schema draft. Root cause:

Backend сериализует Spring Page<SchemaDraft> как:
{items, page, size, totalElements, totalPages}

Но TS type SchemaReviewQueuePage объявлял поле как 'total' (mismatch
именования). Consumers читали data?.total → undefined → fallback 0:

- useReviewsBadgeCount (queries.ts:650) — sidebar badge падал на 0
- reviews.tsx:61 — header count в Schemas tab (но fallback на
  items.length скрывал bug в UI)
- reviews.tsx:593-594 — schema queue subtitle 'N drafts ждут ревью'

Fix:
- client.ts SchemaReviewQueuePage: total → totalElements + totalPages
- 3 consumer'а обновлены на .totalElements
- Backend контракт не меняется — это чисто frontend type alignment

После prod deploy v2.16.3 sidebar badge заработает корректно.
2026-05-13 10:58:48 +03:00
semantic-release-bot 894fcea81b chore(release): v2.16.2
## [2.16.2](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/compare/v2.16.1...v2.16.2) (2026-05-13)

### 🐛 Fixes

* **schema-draft:** автор display + gate Отозвать только maker'у ([fbf978a](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/fbf978aa397b66a309fafb35caa611e3f73164b8))
* **sidebar:** подключить reviews badge к real count ([252939f](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/commit/252939f63a9c997b94603aefa43b9bae287f3eb3))
2026-05-13 07:46:14 +00:00
Александр Зимин 441bbedadf Merge branch 'fix/schema-draft-author-display-and-withdraw-gate' into 'main'
fix(schema-draft): author display + gate Отозвать только maker'у

See merge request 2-6/2-6-4/terravault/ordinis!158
2026-05-13 07:39:59 +00:00
Zimin A.N. fbf978aa39 fix(schema-draft): автор display + gate Отозвать только maker'у
Два бага в SchemaDraftDrawer (видны на review pending):

1. АВТОР показывал raw UUID (77ec2cc8-98e3-4d70-8037-...). Резолвим:
   - Если maker = current user (JWT sub claim) → 'Я • <preferred_username>'
   - Иначе → short UUID (первые 8 символов) с full в title attribute
   - Стилизация: 'Я' — text-ink (читаемая), чужой — font-mono text-mute
   - TODO Phase 1d: backend endpoint для resolve чужих sub → display name

2. Кнопка 'Отозвать' показывалась всем юзерам, не только автору.
   - Backend всё равно 403'нет non-maker withdraw — но UI confusion:
     reviewer видел кнопку которая всегда fail'ит.
   - Gate: показываем только когда auth.user.profile.sub === draft.makerId.
   - Reviewer теперь видит чистый action row (Approve / Запросить правки /
     Отклонить), без danger-button мимо контекста.

Реальный случай: см. user screenshot на v2.16.x — Reviewer modal с UUID
автора и кнопкой 'Отозвать' справа внизу.
2026-05-13 10:33:02 +03:00
Zimin A.N. 252939f63a fix(sidebar): подключить reviews badge к real count
Бэйдж 'На ревью' в sidebar (desktop + mobile) был захардкожен в 0 —
TODO comment ждал cheap /drafts/pending/count endpoint. На самом деле
existing /admin/reviews/pending и /admin/schema-reviews/pending уже
возвращают paged response с totalElements/total в metadata — request
size=1 даёт нужный counter с минимальным payload (~1 строка + 4
metadata field'а).

Новый hook useReviewsBadgeCount():
- Параллельно дёргает обе queue (record + schema)
- Суммирует totalElements (record) + total (schema)
- Полл каждые 60s (lighter чем 30s queue refresh — badge не critical)
- Gated на auth.isAuthenticated (anonymous не должен дёргать admin endpoint'ы)

Sidebar.tsx (оба варианта — desktop + mobile MobileSidebar):
- reviewsCount: 0 → reviews.total
2026-05-13 10:08:57 +03:00