app/message-table.tsx (203 lines of code) (raw):

"use client"; import { useState } from "react"; import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, useReactTable, ExpandedState, getFilteredRowModel, Column, RowData, } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; declare module "@tanstack/react-table" { interface ColumnMeta<TData extends RowData, TValue> { filterVariant?: "text" | "checkbox"; } } interface HideMessageCheckboxProps<TData, Value> { column: Column<TData, Value>; hideMessages: boolean; setHideMessages: (shouldHide: boolean) => any; impressionsThreshold?: string; } interface MessageTableProps<TData, TValue> { columns: ColumnDef<TData, TValue>[]; data: TData[]; defaultExpanded?: boolean; canHideMessages?: boolean; impressionsThreshold?: string; } function HideMessageCheckbox<TData, Value>({ column, hideMessages, setHideMessages, impressionsThreshold, }: HideMessageCheckboxProps<TData, Value>) { // XXX fix assertion that impressionsThreshold won't be undefined here return ( <div className="flex items-center gap-x-1"> <Checkbox className="border-slate-400" id="hide" onCheckedChange={() => { if (!hideMessages) { column.setFilterValue(parseInt(impressionsThreshold!)); } else { column.setFilterValue(null); } setHideMessages(!hideMessages); }} /> <label htmlFor="hide" className="text-3xs font-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > Hide messages with fewer than {impressionsThreshold || "1000"}{" "} impressions </label> </div> ); } export function MessageTable<TData, TValue>({ columns, data, defaultExpanded, canHideMessages, impressionsThreshold, }: MessageTableProps<TData, TValue>) { // Tables will start collapsed if defaultExpanded is undefined const [expanded, setExpanded] = useState<ExpandedState>( defaultExpanded || {}, ); const table = useReactTable({ data, columns, state: { expanded, }, onExpandedChange: setExpanded, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSubRows: (originalRow: any) => originalRow.branches, getExpandedRowModel: getExpandedRowModel(), filterFromLeafRows: true, }); function getRowSpanForCell(cell: any) { // XXX is an experiment & the dates column // if (cell.row.original.recipe && cell.column.id == 'dates') { // return cell.row.original.recipe.branches.length // } return 1; } return ( <div className="rounded-md border"> <Table> <TableHeader className="sticky top-36"> {table.getHeaderGroups().map((headerGroup) => ( <TableRow key={headerGroup.id}> {headerGroup.headers.map((header) => { return ( <TableHead className="py-4 bg-stone-100 text-slate-400" key={header.id} > {header.isPlaceholder ? null : ( <div className="flex flex-col gap-y-1"> {flexRender( header.column.columnDef.header, header.getContext(), )} {header.column.getCanFilter() ? ( <Filter column={header.column} impressionsThreshold={impressionsThreshold} canHideMessages={canHideMessages} /> ) : null} </div> )} </TableHead> ); })} </TableRow> ))} </TableHeader> <TableBody> {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( <TableRow key={row.id} data-state={row.getIsSelected() && "selected"} > {row.getVisibleCells().map((cell) => { // if ((cell.row.original as any).isBranch) // return ( <></> ); return ( <TableCell className="py-2" key={cell.id} rowSpan={getRowSpanForCell(cell)} > {flexRender( cell.column.columnDef.cell, cell.getContext(), )} </TableCell> ); })} </TableRow> )) ) : ( <TableRow> <TableCell colSpan={columns.length} className="h-24 text-center"> No results. </TableCell> </TableRow> )} </TableBody> </Table> </div> ); } function Filter<TData, TValue>({ column, impressionsThreshold, canHideMessages, }: { column: Column<TData, TValue>; impressionsThreshold?: string; canHideMessages?: boolean; }) { const { filterVariant } = column.columnDef.meta ?? {}; const [hideMessages, setHideMessages] = useState(false); if (filterVariant === "text") { return ( <div> <input type="text" value={(column.getFilterValue() ?? "") as string} onChange={(e) => column.setFilterValue(e.target.value)} placeholder={`Search...`} className="w-full border border-slate-400 font-medium p-1 text-2xs rounded text-primary placeholder:font-light" /> </div> ); } else if (filterVariant === "checkbox" && canHideMessages) { return ( <HideMessageCheckbox column={column} hideMessages={hideMessages} setHideMessages={setHideMessages} impressionsThreshold={impressionsThreshold} /> ); } else { return <></>; } }