diff --git a/ordinis-admin-ui/src/components/audit/UserPicker.tsx b/ordinis-admin-ui/src/components/audit/UserPicker.tsx index f0da2f9..d6a0f17 100644 --- a/ordinis-admin-ui/src/components/audit/UserPicker.tsx +++ b/ordinis-admin-ui/src/components/audit/UserPicker.tsx @@ -2,7 +2,7 @@ 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 { Popover, PopoverTrigger, PopoverContent, FieldLabel, TextInput } from '@/ui' import { useAllUsers, type UserListItem } from '@/api/queries' import { cn } from '@/lib/utils' @@ -38,11 +38,32 @@ export function UserPicker({ value, onChange, label, placeholder, disabled }: Us const [open, setOpen] = useState(false) const usersQ = useAllUsers() + // Fallback на TextInput если cache пустой или endpoint вернул 403/404 (юзер + // без INTERNAL scope, либо writer был только-что рестартован и cache не + // прогрелся). Без этого user-фильтр становится недоступен — раньше тут был + // TextInput, не лишаем юзера возможности вписать UUID руками. + const usersList = usersQ.data ?? [] + const fallbackToTextInput = + !usersQ.isLoading && !usersQ.isError && usersList.length === 0 + const usersBySub = useMemo(() => { const m = new Map() - for (const u of usersQ.data ?? []) m.set(u.sub, u) + for (const u of usersList) m.set(u.sub, u) return m - }, [usersQ.data]) + }, [usersList]) + + if (fallbackToTextInput) { + return ( + onChange(e.target.value || undefined)} + placeholder="UUID или username" + hint="Список юзеров пуст — введите вручную" + disabled={disabled} + /> + ) + } const selected = value ? usersBySub.get(value) : undefined const triggerLabel = useMemo(() => {