6.7 KiB
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.
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 ⏳ MR !189 pending
Closed in MR !189 — all 5 fixes in one MR:
- Records empty state — icon + description + audit link
- Reject reason inline validation — aria-invalid + role=alert
- SchemaDraftDrawer empty state — QueryErrorState для errors + dashed block для no-draft
- Approve/reject success flash — 600ms green/pink band before close
- 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 ownaria-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.tsxnear 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 needsh-11instead ofh-10.
- A11y-4: UserCell raw UUID fallback ugly when fetch fails.
77ec2cc8shown literally in AUTHOR column when the user-display lookup 401s (anon) or returns empty. Fall back tot('user.unknown')("Неизвестный пользователь") instead of UUID slice. - A11y-5: No
aria-liveon tab panel. Switching Records → Schemas → Mine doesn't announce content change to screen readers. Addaria-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 hasstyle="outline: none"inline that defeats default focus ring. Verify:focus-visiblestyling 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-prosefor 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 ⏳ MR !190 pending
Closed in MR !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 + EmailChannelUserDisplayPortSPI in notifications module (anti-corruption layer)NotificationsUserDisplayBridgewires port → UserDisplayService in ordinis-app (avoids circular dep)MeNotificationsControllerGET /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:
useMyNotificationsquery (30s refetch + background poll for badge)useMarkNotificationRead+useMarkAllNotificationsReadmutationsNotificationsDrawerright-side drawer with per-row mark-read + footer mark-allNotificationsBellreplaces 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.