'use client'; import { useState, useMemo } from 'react'; import { useReactTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel, ColumnDef, flexRender, SortingState, } from '@tanstack/react-table'; import { ArrowUpDown, ArrowUp, ArrowDown, ExternalLink, ChevronLeft, ChevronRight } from 'lucide-react'; import type { IssueItem, PaginatedIssues } from '../types'; import { DOMAIN_LABELS, INTENSITY_LABELS } from '../types'; import { IssueDetailModal } from './IssueDetailModal'; interface IssuesTableProps { issues: PaginatedIssues; onPageChange?: (page: number) => void; } // Priority badge color based on score const getPriorityColor = (score: number): string => { if (score >= 0.8) return 'bg-red-100 text-red-800 border-red-300'; if (score >= 0.5) return 'bg-orange-100 text-orange-800 border-orange-300'; if (score >= 0.3) return 'bg-yellow-100 text-yellow-800 border-yellow-300'; return 'bg-gray-100 text-gray-800 border-gray-300'; }; // State badge color const getStateColor = (state: string): string => { switch (state) { case 'open': return 'bg-red-100 text-red-800 border-red-300'; case 'in_progress': return 'bg-blue-100 text-blue-800 border-blue-300'; case 'resolved': return 'bg-green-100 text-green-800 border-green-300'; default: return 'bg-gray-100 text-gray-800 border-gray-300'; } }; /** * Issues table with TanStack Table. * Click rows to open drill-down modal. */ export function IssuesTable({ issues, onPageChange }: IssuesTableProps) { const [sorting, setSorting] = useState([ { id: 'priority_score', desc: true }, ]); const [selectedIssue, setSelectedIssue] = useState(null); const columns = useMemo[]>( () => [ { accessorKey: 'primary_subcode', header: ({ column }) => ( ), cell: ({ row }) => (
{row.original.subcode_name || row.original.primary_subcode} {row.original.primary_subcode}
), }, { accessorKey: 'domain', header: 'Domain', cell: ({ row }) => ( {DOMAIN_LABELS[row.original.domain] || row.original.domain} ), }, { accessorKey: 'default_owner', header: 'Owner', cell: ({ row }) => ( {row.original.default_owner || 'Unassigned'} ), }, { accessorKey: 'state', header: 'State', cell: ({ row }) => ( {row.original.state.toUpperCase()} ), }, { accessorKey: 'priority_score', header: ({ column }) => ( ), cell: ({ row }) => ( {(row.original.priority_score * 100).toFixed(0)}% ), }, { accessorKey: 'span_count', header: 'Spans', cell: ({ row }) => ( {row.original.span_count} ), }, { accessorKey: 'max_intensity', header: 'Intensity', cell: ({ row }) => ( {row.original.max_intensity ? INTENSITY_LABELS[row.original.max_intensity] || row.original.max_intensity : '-'} ), }, { id: 'actions', header: '', cell: ({ row }) => ( ), }, ], [] ); const table = useReactTable({ data: issues.items, columns, state: { sorting }, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize: 10 }, }, }); const totalPages = Math.ceil(issues.total / issues.page_size); return (

Issues

{issues.total} total issues - Click row for details

{issues.items.length === 0 ? (
No issues found
) : ( <>
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( setSelectedIssue(row.original)} > {row.getVisibleCells().map((cell) => ( ))} ))}
{header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )}
{flexRender( cell.column.columnDef.cell, cell.getContext() )}
{/* Pagination */}
Page {issues.page} of {totalPages} ({issues.total} issues)
)} {/* Detail Modal */} {selectedIssue && ( setSelectedIssue(null)} /> )}
); }