# TODOS

Design debt and follow-ups surfaced by `/plan-design-review` of the approval
workflow UX (2026-05-14). Each entry is self-contained: pick one up and read
the **Context** to understand the motivation without re-running the review.

## Session summary 2026-05-14 — all original TODOs closed

| # | Title | Status | Artifact |
|---|---|---|---|
| 1 | "Мои" tab on /reviews | ✅ shipped | MR !188 (v2.25.1) |
| 2 | Interaction state polish (5 fixes) | ✅ shipped | MR !189 (v2.26.0) |
| 5 | DESIGN.md baseline | ✅ shipped | `DESIGN.md` v0 |
| 6 | A11y audit on staging | ✅ findings captured | 10 issues P0–P3 (fix loop = follow-up polish MR) |
| 7 | Notifications: toast + email + bell | ✅ shipped | MR !190 (v2.26.x) |

**New follow-up:** TODO 6 punch list → groupy fix MR. ~15 LOC each except touch-target
sweep across TopBar + Sidebar + Tabs components.

---

## 1. `/reviews` "Мои" tab — maker visibility ✅ SHIPPED MR !188

Closed via MR !188 (2026-05-14). Final shape: flat mixed timeline (records
+ schemas together sorted DESC), filter chips All/Pending/WIP/Decided with
per-bucket counts, ReviewDrawer status-aware (read-only decision banner for
terminal states, Withdraw for own-pending). 13 unit tests on classifiers + 32
i18n keys. Schema rows link to /dictionaries/$name (existing drawer there);
record rows reuse ReviewDrawer with status-gated footer.

---

## 2. Interaction state pass — reviews page polish ✅ SHIPPED MR !189

Closed in [MR !189](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/-/merge_requests/189) — all 5 fixes in one MR:
1. Records empty state — icon + description + audit link
2. Reject reason inline validation — aria-invalid + role=alert
3. SchemaDraftDrawer empty state — QueryErrorState для errors + dashed block для no-draft
4. Approve/reject success flash — 600ms green/pink band before close
5. Bulk failed reasons humanized — extracted `humanizeBulkReason()` lib + 7 unit tests

---

## 5. DESIGN.md for the admin UI ✅ SHIPPED v0 baseline

Authored as `DESIGN.md` at repo root (2026-05-14). 12 sections covering:
voice & tone, color tokens (3 themes), typography, spacing/layout, component
primitives, badge variant mapping (operations / record status / schema status /
notifications), code/diff visual language, interaction states, term glossary,
RBAC, AI slop anti-patterns, open items для future /design-consultation sprint.

Full /design-consultation skill run deferred — v0 captures observed patterns
as fact-based source of truth; later sprint can refine policy gaps listed in
Section 12 (responsive breakpoints, motion durations, loading state taxonomy,
etc.).

---

## 6. A11y audit post-deploy via `/design-review` ⏳ Punch list captured 2026-05-14

Ran focused live audit on https://ordinis.k8s.265.nstart.cloud/reviews (staging
v2.25.x, anonymous + read-only state). Findings captured below; fix loop deferred
to standalone polish MR.

### P0 — Severe

- **A11y-1: Bell badge "9+" lacks contextual aria-label.** Screen reader reads
  literal "9+" without "unread notifications" context. The `<span>` carrying
  the digit needs its own `aria-label={t('notifications.unreadCount', {count})}`.
  TopBar.tsx NotificationsBell. (Already addressed in MR !190 — verify in deploy.)
- **A11y-2: i18n leak — "СВЕРНУТЬ" hardcoded RU.** When EN locale active
  (sidebar reads Home/Dictionaries/Search), sidebar collapse button at bottom
  still shows "‹ СВЕРНУТЬ". Either missing translation key or hardcoded label.
  Find in `Sidebar.tsx` near collapse handler.

### P1 — High

- **A11y-3: Touch targets below WCAG 44×44 minimum.** Measured on staging:
  - Bell button: 28×28
  - Theme switches (Light/Dark/System): 28×26
  - Lang switch button "EN": 37×28
  - Sidebar nav items: ~32px tall
  - Tabs: 78×40 (Records) and 112×40 (Schemas) — height below 44
  - "Open →" action link in schema queue row: ~50×18 (text link only)
  Mobile/touch use is uncomfortable. Add `min-h-[44px]` to icon buttons in
  TopBar + Sidebar footer. Tab height needs `h-11` instead of `h-10`.
- **A11y-4: UserCell raw UUID fallback ugly when fetch fails.** `77ec2cc8` 
  shown literally in AUTHOR column when the user-display lookup 401s (anon)
  or returns empty. Fall back to `t('user.unknown')` ("Неизвестный пользователь")
  instead of UUID slice.
- **A11y-5: No `aria-live` on tab panel.** Switching Records → Schemas → Mine
  doesn't announce content change to screen readers. Add
  `aria-live="polite"` on the panel container or rely on Radix `<Tabs.Content>`
  which already manages focus.

### P2 — Medium

- **A11y-6: Radix Tabs inline `style="outline:none"`.** Tablist has
  `style="outline: none"` inline that defeats default focus ring. Verify
  `:focus-visible` styling kicks in for keyboard nav — if not, remove the
  inline override or add a Tailwind focus ring.
- **A11y-7: Anonymous Mine tab handling.** Need to verify what happens for
  unauthenticated user clicking Mine — should redirect to /sign-in or render
  "Sign in to see your drafts" empty state (current behavior likely error
  toast or blank query).
- **A11y-8: Description text has no max-width.** "Pending drafts from makers
  awaiting…" body copy stretches to viewport edge on wide screens (>1440px).
  Apply `max-w-prose` for readable measure (45-75 chars).

### P3 — Polish

- **A11y-9: Column header tracking-wide ALL CAPS** ("DICTIONARY", "FROM
  VERSION", "AUTHOR") creates uneven scan rhythm against lowercase data
  rows. Verify against design tokens — likely intentional cap-style but
  worth a visual review at narrow widths.
- **A11y-10: "Open →" link should be Button.** It's a text Link in Action
  column; better as a `Button variant="ghost"` to align with the
  existing Records tab "Review" CTA pattern.

### Status

Punch list compiled. Fix loop deferred — group into separate "a11y polish"
MR after current notifications work (!190) lands. Most fixes are 2-5 LOC
each except touch-target sweep which touches 5+ components.

---

## 7. Notifications service: draft decision toast + email ✅ SHIPPED MR !190

Closed in [MR !190](https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/-/merge_requests/190).

**Backend** (ordinis-notifications + ordinis-rest-api + ordinis-app):
- `NotificationsDispatcher` — mirrors WebhookDispatcher pattern, reads outbox
  events of type RecordDraftApproved/Rejected/Withdrawn/Submitted, fan-outs
  via IdempotencyGuard + EmailChannel
- `UserDisplayPort` SPI in notifications module (anti-corruption layer)
- `NotificationsUserDisplayBridge` wires port → UserDisplayService in
  ordinis-app (avoids circular dep)
- `MeNotificationsController` GET /api/v1/me/notifications + POST /{id}/read +
  /read-all
- Migration 0024 `notification_read_state(user_id, notification_id, read_at)`
- 5 unit tests (mock-maker-subclass for Java 25 compat)

**Frontend**:
- `useMyNotifications` query (30s refetch + background poll for badge)
- `useMarkNotificationRead` + `useMarkAllNotificationsRead` mutations
- `NotificationsDrawer` right-side drawer with per-row mark-read + footer
  mark-all
- `NotificationsBell` replaces placeholder — unread badge (capped "9+") +
  delta-toast on poll-detected new arrivals when drawer closed
- 14 ru-RU + 14 en-US i18n keys

**Email templates** already existed (draft_submitted, draft_decision,
draft_withdrawn) in notifications_{ru,en}.properties — reused as-is.
