import { useMemo } from "react"; import { createColumnHelper, type ColumnDef, type RowSelectionState, } from "@tanstack/react-table"; import { AppDataTable, makeSelectionColumn, useAppTable, useSelectionAnchor, } from "@/client/components/table/AppDataTable"; import { IntentBadge, SortHeader, type SortDir, type SortField, } from "@/client/features/keywords/components"; import { formatNumber, scoreTierClass } from "@/client/features/keywords/utils"; import type { KeywordResearchRow } from "@/types/keywords"; import { EmptyFilterResults } from "./keywordResearchDesktopFilters"; type Props = { activeFilterCount: number; filteredRows: KeywordResearchRow[]; overviewKeyword: KeywordResearchRow | null; selectedRows: Set; setSelectedRows: (rows: Set) => void; sortDir: SortDir; sortField: SortField; toggleSort: (field: SortField) => void; resetFilters: () => void; handleRowClick: (row: KeywordResearchRow) => void; }; const keywordColumnHelper = createColumnHelper(); export function KeywordResearchDesktopTable({ activeFilterCount, filteredRows, overviewKeyword, selectedRows, setSelectedRows, sortDir, sortField, toggleSort, resetFilters, handleRowClick, }: Props) { const selectAnchorRef = useSelectionAnchor(); const rowSelection = useMemo( () => Object.fromEntries( [...selectedRows].map((keyword) => [keyword, true]), ) as RowSelectionState, [selectedRows], ); const columns = useMemo[]>( () => [ makeSelectionColumn(selectAnchorRef), keywordColumnHelper.accessor("keyword", { header: () => ( ), cell: ({ row }) => ( {row.original.keyword} ), meta: { headerClassName: "min-w-48 md:min-w-0", cellClassName: "min-w-48 md:min-w-0", }, }), keywordColumnHelper.accessor("searchVolume", { header: () => ( ), cell: ({ getValue }) => formatNumber(getValue()), meta: { headerClassName: "text-right", cellClassName: "whitespace-nowrap text-right tabular-nums text-base-content/70", }, }), keywordColumnHelper.accessor("cpc", { header: () => ( ), cell: ({ getValue }) => { const value = getValue(); return value == null ? "-" : value.toFixed(2); }, meta: { headerClassName: "text-right", cellClassName: "whitespace-nowrap text-right tabular-nums text-base-content/70", }, }), keywordColumnHelper.accessor("competition", { header: () => ( ), cell: ({ getValue }) => { const value = getValue(); return value == null ? "-" : value.toFixed(2); }, meta: { headerClassName: "text-right", cellClassName: "whitespace-nowrap text-right tabular-nums text-base-content/70", }, }), keywordColumnHelper.accessor("keywordDifficulty", { header: () => ( ), cell: ({ getValue }) => , meta: { headerClassName: "text-right", cellClassName: "text-right" }, }), keywordColumnHelper.accessor("intent", { header: "Intent", cell: ({ getValue }) => , meta: { headerClassName: "text-center", cellClassName: "whitespace-nowrap text-center", }, }), ], [selectAnchorRef, sortDir, sortField, toggleSort], ); const table = useAppTable({ data: filteredRows, columns, state: { rowSelection }, onRowSelectionChange: (updater) => { const next = typeof updater === "function" ? updater(rowSelection) : updater; setSelectedRows( new Set( Object.entries(next) .filter(([, selected]) => selected) .map(([keyword]) => keyword), ), ); }, getRowId: (row) => row.keyword, enableRowSelection: true, }); return (
{filteredRows.length === 0 ? ( ) : ( ({ className: `cursor-pointer border-b border-base-200 hover:bg-base-200/50 ${ overviewKeyword?.keyword === row.original.keyword ? "bg-primary/5 border-l-2 border-l-primary" : "" }`, onClick: () => handleRowClick(row.original), })} /> )}
); } function ScoreCell({ value }: { value: number | null }) { if (value == null) return null; const tierClass = scoreTierClass(value); return ( {value} ); }