import * as React from 'react' 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 } from '@/ui' import { useAllUsers, type UserListItem } from '@/api/queries' import { cn } from '@/lib/utils' /** * Single-value user picker — cmdk-powered combobox для audit-фильтра * «Пользователь». Заменяет ручной TextInput на typeahead по username/email/UUID. * *
Источник: GET /api/v1/admin/users (cached user_display table). * INTERNAL+ scope требуется на backend; non-INTERNAL получит пустой * список и падает на input fallback (поле остаётся редактируемым). * *
UX: trigger показывает username (или UUID prefix как fallback);
* X сбрасывает selection inline. В popover'е — поиск по subname/email/UUID.
*/
const labelFor = (u: UserListItem): string =>
u.preferredUsername || u.name || `${u.sub.slice(0, 8)}…`
const secondaryFor = (u: UserListItem): string | null => {
if (u.email && u.email !== u.preferredUsername) return u.email
return null
}
export type UserPickerProps = {
value: string | undefined
onChange: (sub: string | undefined) => void
label?: React.ReactNode
placeholder?: string
disabled?: boolean
}
export function UserPicker({ value, onChange, label, placeholder, disabled }: UserPickerProps) {
const [open, setOpen] = useState(false)
const usersQ = useAllUsers()
const usersBySub = useMemo(() => {
const m = new Map