Merge branch 'chore/a11y-sweep' into 'main'
chore(ui): a11y touch-targets sweep + useUserDisplay JSDoc refresh See merge request 2-6/2-6-4/terravault/ordinis!278
This commit is contained in:
@@ -382,7 +382,11 @@ function SidebarFooter({
|
|||||||
}
|
}
|
||||||
title={collapsed ? t('sidebar.expand', { defaultValue: 'Развернуть' }) : t('sidebar.collapse', { defaultValue: 'Свернуть' })}
|
title={collapsed ? t('sidebar.expand', { defaultValue: 'Развернуть' }) : t('sidebar.collapse', { defaultValue: 'Свернуть' })}
|
||||||
className={cn(
|
className={cn(
|
||||||
'border-t border-line shrink-0 h-8 flex items-center gap-2 text-cap text-mute hover:text-ink hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
// h-11 = 44px WCAG AAA touch target per DESIGN.md §13 (был h-8/32px,
|
||||||
|
// отмечено в /design-review TODOS.md P1-3). Desktop sidebar только
|
||||||
|
// (hidden lg:flex) — mobile использует burger из TopBar. h-11 не
|
||||||
|
// ломает вертикальный rhythm и matches TopBar h-11.
|
||||||
|
'border-t border-line shrink-0 h-11 flex items-center gap-2 text-cap text-mute hover:text-ink hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
||||||
collapsed ? 'justify-center' : 'px-4',
|
collapsed ? 'justify-center' : 'px-4',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ export function WhatsNewButton() {
|
|||||||
aria-label={label}
|
aria-label={label}
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative size-7 rounded-md text-ink-2 hover:text-ink hover:bg-surface-2',
|
// size-11 = 44×44 WCAG AAA touch target per DESIGN.md §13 (aligned
|
||||||
|
// с другими TopBar icon buttons — Bell, ThemeSwitch). Icon size=14
|
||||||
|
// остаётся, wrapper увеличен для тактильной площади на mobile.
|
||||||
|
'relative size-11 rounded-md text-ink-2 hover:text-ink hover:bg-surface-2',
|
||||||
'inline-flex items-center justify-center transition-colors',
|
'inline-flex items-center justify-center transition-colors',
|
||||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,15 +3,20 @@ import { useAuth } from '@/auth/useAuth'
|
|||||||
import { apiClient } from '@/api/client'
|
import { apiClient } from '@/api/client'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup user display info по UUID (JWT sub). Backed by backend in-memory
|
* Lookup user display info по UUID (JWT sub). Backed by backend trio:
|
||||||
* cache (заполняется на каждом authenticated request через
|
* hot ConcurrentHashMap → Postgres user_display_cache → Keycloak Admin
|
||||||
* JwtUserCaptureFilter). Returns null если sub не в кэше — frontend
|
* REST (Tier 3 on-demand). См. UserDisplayService.find в backend.
|
||||||
* fallback'нёт на short UUID.
|
|
||||||
*
|
*
|
||||||
* <p>Stale-while-revalidate: TanStack Query кэширует 5 минут, refetch
|
* <p>Stale-while-revalidate: TanStack Query кэширует 5 минут, refetch
|
||||||
* on focus. Cheap — endpoint = O(1) hash lookup, no DB hit.
|
* on focus. Cheap — hot cache hit = O(1), даже tier 3 KC lookup
|
||||||
|
* ~10-50ms на cold sub.
|
||||||
*
|
*
|
||||||
* <p>404 → данных нет; не считаем error (через retry: false + handle null).
|
* <p>404 → данных нет (sub неизвестен realm'у); не считаем error
|
||||||
|
* (через retry: false + handle null). Frontend fallback'нёт на em-dash
|
||||||
|
* placeholder (см. useUserDisplay).
|
||||||
|
*
|
||||||
|
* <p>Note: JwtUserCaptureFilter был удалён в Phase 2 — cache теперь
|
||||||
|
* lazy-populated через первое чтение на cache miss (tier 3).
|
||||||
*/
|
*/
|
||||||
type UserDisplayInfo = {
|
type UserDisplayInfo = {
|
||||||
sub: string
|
sub: string
|
||||||
@@ -67,10 +72,12 @@ const useResolveUserDisplay = (uuid: string | null | undefined) =>
|
|||||||
* <li><b>null/undefined uuid</b> → {@code "anonymous"}.</li>
|
* <li><b>null/undefined uuid</b> → {@code "anonymous"}.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Backend endpoint: {@code GET /api/v1/admin/users/{sub}/display} — backed
|
* <p>Backend endpoint: {@code GET /api/v1/admin/users/{sub}/display} —
|
||||||
* by in-memory cache (заполняется JwtUserCaptureFilter на каждом authenticated
|
* backed by three-tier cache (hot ConcurrentHashMap → Postgres
|
||||||
* request). Если backend pod restart'нулся, кэш пуст — postepenно заполняется
|
* user_display_cache → Keycloak Admin REST on-demand). Cold pod
|
||||||
* обратно. Frontend gracefully fallback'ит на short UUID.
|
* стартует с пустого hot cache, но DB persists — sub'ы будут резолвиться
|
||||||
|
* сразу. Новые юзеры (никогда не появлялись в audit'е) — резолв через
|
||||||
|
* tier 3 KC Admin lookup на первый запрос.
|
||||||
*
|
*
|
||||||
* <p>Используется везде где UI показывает actor: AUTHOR в таблицах
|
* <p>Используется везде где UI показывает actor: AUTHOR в таблицах
|
||||||
* /reviews (record + schema), USER в /audit log, AUTHOR в schema draft
|
* /reviews (record + schema), USER в /audit log, AUTHOR в schema draft
|
||||||
|
|||||||
Reference in New Issue
Block a user