import { useState, useRef, useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Input } from '@/components/ui/input'; import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover'; import { Table } from 'lucide-react'; import { DirectoryTreeSelect, type DirectoryTreeNode } from '@/components/directories/DirectoryTreeSelect'; import { apiRequest } from '@/lib/queryClient'; import type { DataTableRow } from '@shared/schema'; interface TableFieldSelectProps { fieldId: number; fieldCode: string; placeholder?: string; options?: unknown; value: any; onChange: (value: string) => void; } export default function TableFieldSelect({ fieldId, fieldCode, placeholder, options, value, onChange, }: TableFieldSelectProps) { const opts = options && !Array.isArray(options) ? (options as Record) : {}; const tableId = opts.tableId as number | undefined; const displayColumn = (opts.displayColumn as string) || ''; const rowFilters = opts.filters as Record | undefined; const { data: tableData, isLoading, error } = useQuery<{ table: { rows: DataTableRow[]; columns: Array<{ name: string }>; treeColumnIndex: number | null; treeDisplayColumnIndex: number | null; treeColumns: number[] | null; treeDisplayColumns: number[] | null; }; }>({ queryKey: ['/api/directories', tableId], enabled: !!tableId, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, retry: false, }); const filtersStr = useMemo(() => (rowFilters ? JSON.stringify(rowFilters) : ''), [rowFilters]); const { data: treeData } = useQuery<{ columnIndex: number | null; treeColumns: number[]; treeDisplayColumns: number[]; nodes: DirectoryTreeNode[]; }>({ queryKey: ['/api/directories', tableId, 'rows', 'tree', filtersStr], queryFn: async () => { const params = new URLSearchParams(); if (filtersStr) params.set('filters', filtersStr); const res = await apiRequest('GET', `/api/directories/${tableId}/rows/tree?${params.toString()}`); return res.json(); }, enabled: !!tableId && !!tableData, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, retry: false, }); const [searchTerm, setSearchTerm] = useState(''); const [isOpen, setIsOpen] = useState(false); const inputRef = useRef(null); const table = tableData?.table; const rows = table?.rows || []; const nodes = treeData?.nodes || []; let displayColumnIndex = table?.columns?.findIndex((c) => c.name === displayColumn); if (displayColumnIndex === undefined || displayColumnIndex < 0) { displayColumnIndex = table?.treeDisplayColumnIndex ?? table?.treeDisplayColumns?.[0] ?? 0; } const sortedRows = useMemo(() => { return [...rows].sort((a, b) => { const av = a.values?.[displayColumnIndex!] || a.values?.[0] || ''; const bv = b.values?.[displayColumnIndex!] || b.values?.[0] || ''; return av.localeCompare(bv, 'ru'); }); }, [rows, displayColumnIndex]); const filtered = useMemo(() => { if (!searchTerm.trim()) return sortedRows; const term = searchTerm.toLowerCase(); return sortedRows.filter((r) => { const display = r.values?.[displayColumnIndex!] || r.values?.[0] || ''; return display.toLowerCase().includes(term); }); }, [sortedRows, searchTerm, displayColumnIndex]); const hasTree = nodes.some((n) => n.type === 'group'); if (!tableId) { return
Таблица не настроена
; } if (isLoading) { return ; } if (error) { return
Нет доступа к таблице
; } if (!table || !table.rows?.length) { return
Таблица пуста
; } const selectedRow = table.rows?.find((r) => r.id.toString() === value?.toString()); const selectedDisplay = selectedRow ? selectedRow.values[displayColumnIndex!] || selectedRow.values[0] || '' : ''; const handleSelect = (rowId: string) => { onChange(rowId); setSearchTerm(''); setIsOpen(false); }; return (
{ setSearchTerm(e.target.value); setIsOpen(true); }} onFocus={() => { setSearchTerm(''); setIsOpen(true); }} data-testid={`select-table-${fieldCode}`} />
e.preventDefault()} onInteractOutside={(e) => { if (inputRef.current && inputRef.current.contains(e.target as Node)) { e.preventDefault(); } }} >
{hasTree ? ( ) : (
  • { e.preventDefault(); handleSelect(''); }} > —
  • {filtered.map((row) => (
  • { e.preventDefault(); handleSelect(row.id.toString()); }} >
    {row.values[displayColumnIndex!] || row.values[0]} ))} {filtered.length === 0 && (
  • Нет совпадений
  • )} )} ); }