import { useState, useMemo, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { getRouteApi } from '@tanstack/react-router' import { flexRender, getCoreRowModel, useReactTable, type SortingState, type VisibilityState, } from '@tanstack/react-table' import { useMediaQuery } from '@/hooks' import { useTranslation } from 'react-i18next' import { cn } from '@/lib/utils' import { useTableUrlState } from '@/hooks/use-table-url-state' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { DataTableToolbar, TableSkeleton, TableEmpty, MobileCardList, } from '@/components/data-table' import { DataTablePagination } from '@/components/data-table/pagination' import { PageFooterPortal } from '@/components/layout' import { getModels, searchModels, getVendors } from '../api' import { DEFAULT_PAGE_SIZE, getModelStatusOptions, getSyncStatusOptions, } from '../constants' import { modelsQueryKeys, vendorsQueryKeys } from '../lib' import { DataTableBulkActions } from './data-table-bulk-actions' import { useModelsColumns } from './models-columns' import { useModels } from './models-provider' const route = getRouteApi('/_authenticated/models/$section') export function ModelsTable() { const { t } = useTranslation() const { selectedVendor } = useModels() const isMobile = useMediaQuery('(max-width: 640px)') // Table state const [sorting, setSorting] = useState([]) const [columnVisibility, setColumnVisibility] = useState({ description: false, bound_channels: false, quota_types: false, }) const [rowSelection, setRowSelection] = useState({}) // URL state management const { globalFilter, onGlobalFilterChange, columnFilters, onColumnFiltersChange, pagination, onPaginationChange, ensurePageInRange, } = useTableUrlState({ search: route.useSearch(), navigate: route.useNavigate(), pagination: { defaultPage: 1, defaultPageSize: isMobile ? 10 : DEFAULT_PAGE_SIZE, }, globalFilter: { enabled: true, key: 'filter' }, columnFilters: [ { columnId: 'status', searchKey: 'status', type: 'array' }, { columnId: 'vendor_id', searchKey: 'vendor', type: 'array' }, { columnId: 'sync_official', searchKey: 'sync', type: 'array' }, ], }) // Extract filters from column filters const statusFilter = (columnFilters.find((f) => f.id === 'status')?.value as string[]) || [] const vendorFilter = (columnFilters.find((f) => f.id === 'vendor_id')?.value as string[]) || [] const syncFilter = (columnFilters.find((f) => f.id === 'sync_official')?.value as string[]) || [] // Fetch vendors for filter const { data: vendorsData } = useQuery({ queryKey: vendorsQueryKeys.list(), queryFn: () => getVendors({ page_size: 1000 }), }) const vendors = useMemo( () => vendorsData?.data?.items || [], [vendorsData?.data?.items] ) const vendorOptions = useMemo(() => { return vendors.map((v) => ({ label: v.name, value: String(v.id), })) }, [vendors]) // Determine whether to use search or regular list API const shouldSearch = Boolean(globalFilter?.trim()) // Apply selected vendor from context or filter const activeVendorFilter = selectedVendor || (vendorFilter.length > 0 && !vendorFilter.includes('all') ? vendorFilter[0] : undefined) // Fetch models data // eslint-disable-next-line @tanstack/query/exhaustive-deps const { data, isLoading, isFetching } = useQuery({ queryKey: modelsQueryKeys.list({ keyword: globalFilter, vendor: activeVendorFilter, status: statusFilter.length > 0 && !statusFilter.includes('all') ? statusFilter[0] : undefined, sync_official: syncFilter.length > 0 && !syncFilter.includes('all') ? syncFilter[0] : undefined, p: pagination.pageIndex + 1, page_size: pagination.pageSize, }), queryFn: async () => { if (shouldSearch || activeVendorFilter) { return searchModels({ keyword: globalFilter, vendor: activeVendorFilter, status: statusFilter.length > 0 && !statusFilter.includes('all') ? statusFilter[0] : undefined, sync_official: syncFilter.length > 0 && !syncFilter.includes('all') ? syncFilter[0] : undefined, p: pagination.pageIndex + 1, page_size: pagination.pageSize, }) } else { return getModels({ status: statusFilter.length > 0 && !statusFilter.includes('all') ? statusFilter[0] : undefined, sync_official: syncFilter.length > 0 && !syncFilter.includes('all') ? syncFilter[0] : undefined, p: pagination.pageIndex + 1, page_size: pagination.pageSize, }) } }, placeholderData: (previousData) => previousData, }) const models = data?.data?.items || [] const totalCount = data?.data?.total || 0 const vendorCounts = data?.data?.vendor_counts // Columns configuration const columns = useModelsColumns(vendors) // React Table instance const table = useReactTable({ data: models, columns, pageCount: Math.ceil(totalCount / pagination.pageSize), state: { sorting, columnFilters, columnVisibility, rowSelection, pagination, globalFilter, }, enableRowSelection: true, onRowSelectionChange: setRowSelection, onSortingChange: setSorting, onColumnFiltersChange, onColumnVisibilityChange: setColumnVisibility, onPaginationChange, onGlobalFilterChange, getCoreRowModel: getCoreRowModel(), manualPagination: true, manualSorting: true, manualFiltering: true, }) // Ensure page is in range when total count changes const pageCount = table.getPageCount() useEffect(() => { ensurePageInRange(pageCount) }, [pageCount, ensurePageInRange]) // Prepare filter options const vendorFilterOptions = [ { label: `${t('All Vendors')}${vendorCounts?.all ? ` (${vendorCounts.all})` : ''}`, value: 'all', }, ...vendorOptions.map((option) => ({ label: `${option.label}${vendorCounts?.[option.value] ? ` (${vendorCounts[option.value]})` : ''}`, value: option.value, })), ] return ( <>
{isMobile ? ( ) : ( <>
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )} ))} ))} {isLoading ? ( ) : table.getRowModel().rows.length === 0 ? ( ) : ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ))} )) )}
)}
} /> ) }