feat(auth): Phase 2 — audited-users UserPicker + Tier-3 Keycloak Admin lookup

This commit is contained in:
Александр Зимин
2026-05-26 12:01:09 +00:00
parent f1fcabc9da
commit a4ba8735d0
8 changed files with 142 additions and 122 deletions
@@ -3,16 +3,19 @@ import { useMemo, useState } from 'react'
import { CaretDownIcon, MagnifyingGlassIcon, UserIcon, XIcon } from '@phosphor-icons/react'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from 'cmdk'
import { Popover, PopoverTrigger, PopoverContent, FieldLabel, TextInput } from '@/ui'
import { useAllUsers, type UserListItem } from '@/api/queries'
import { useAuditedUsers, type UserListItem } from '@/api/queries'
import { cn } from '@/lib/utils'
/**
* Single-value user picker — cmdk-powered combobox для audit-фильтра
* «Пользователь». Заменяет ручной TextInput на typeahead по username/email/UUID.
*
* <p>Источник: GET /api/v1/admin/users (cached user_display table).
* INTERNAL+ scope требуется на backend; non-INTERNAL получит пустой
* список и падает на input fallback (поле остаётся редактируемым).
* <p>Источник: GET /api/v1/admin/users/audited — distinct user_id из audit_log
* JOIN с user_display_cache. Возвращает только тех, кто реально засветился в
* audit (не всех юзеров altum realm). INTERNAL+ scope требуется на backend;
* non-INTERNAL получит пустой список и падает на input fallback (поле
* остаётся редактируемым). Юзеры, которых ещё нет в user_display_cache,
* возвращаются с null username/name/email — здесь рендерится UUID-prefix.
*
* <p>UX: trigger показывает username (или UUID prefix как fallback);
* X сбрасывает selection inline. В popover'е — поиск по subname/email/UUID.
@@ -36,7 +39,7 @@ export type UserPickerProps = {
export function UserPicker({ value, onChange, label, placeholder, disabled }: UserPickerProps) {
const [open, setOpen] = useState(false)
const usersQ = useAllUsers()
const usersQ = useAuditedUsers()
// Fallback на TextInput если cache пустой или endpoint вернул 403/404 (юзер
// без INTERNAL scope, либо writer был только-что рестартован и cache не