From 95a61b9ab6d440b9408ac7b782d26176aeafaed1 Mon Sep 17 00:00:00 2001 From: Lince Mathew Date: Mon, 26 Oct 2026 19:46:18 +0530 Subject: [PATCH 1/9] Implement Table Row Linking LiveReview Pre-Commit Check: ran (iter:1, coverage:0%) --- ui/src/components/DataTable/ClientTable.tsx | 26 ++++++++++++++++---- ui/src/pages/Reviews/Reviews.tsx | 27 +++++++++++++++------ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/ui/src/components/DataTable/ClientTable.tsx b/ui/src/components/DataTable/ClientTable.tsx index ee4d4347..14a55dec 100644 --- a/ui/src/components/DataTable/ClientTable.tsx +++ b/ui/src/components/DataTable/ClientTable.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { flexRender, Table } from '@tanstack/react-table'; import { motion, AnimatePresence } from 'framer-motion'; +import { Link } from 'react-router-dom'; import { Button, Icons, Spinner } from '../UIPrimitives'; import { DataTablePaginationControls, DataTableEmptyState } from './DataTable'; @@ -33,6 +34,9 @@ export interface ClientTableProps { * activation to each row when set; omit for pages that only expose * actions via explicit buttons. */ onRowClick?: (row: TData) => void; + /** Makes each cell a real link to this URL, so right-click "Open in new tab" + * and middle/Ctrl-click work. Columns with their own links opt out via meta.rowLink = false. */ + getRowHref?: (row: TData) => string; /** Override for the pagination footer's "total rows" count. Needed for * server-side (manualPagination/manualFiltering) tables, where the * table instance only ever holds one page's worth of data - @@ -61,6 +65,7 @@ export function ClientTable({ empty, pageSizeOptions, onRowClick, + getRowHref, manualTotal, }: ClientTableProps) { // Show loading until the first rows arrive, even when filters keep isEmpty false. @@ -162,11 +167,22 @@ export function ClientTable({ } tabIndex={onRowClick ? 0 : undefined} > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} + {row.getVisibleCells().map((cell) => { + const content = flexRender(cell.column.columnDef.cell, cell.getContext()); + const meta = cell.column.columnDef.meta as { rowLink?: boolean } | undefined; + return ( + + {getRowHref && meta?.rowLink !== false ? ( + // Stop the row's onClick so it doesn't navigate a second time. + e.stopPropagation()} className="block text-inherit"> + {content} + + ) : ( + content + )} + + ); + })} ))} diff --git a/ui/src/pages/Reviews/Reviews.tsx b/ui/src/pages/Reviews/Reviews.tsx index bcc7e727..959bec9f 100644 --- a/ui/src/pages/Reviews/Reviews.tsx +++ b/ui/src/pages/Reviews/Reviews.tsx @@ -292,6 +292,8 @@ const Reviews: React.FC = () => { { id: 'review', accessorFn: (review) => getPrimaryTitle(review), + // Links its own title/PR line so "Open PR/MR" isn't nested in a link. + meta: { rowLink: false }, // Searches title/repo/branch/URL/author together, same combined // free-text search the old top search bar offered (it searched // "repositories or URLs"; author is included too since it's visible @@ -365,14 +367,18 @@ const Reviews: React.FC = () => { return (
- + e.stopPropagation()} + className="min-w-0 truncate text-white font-semibold" + > {displayTitle} - + {review.prMrUrl && ( { )}
{metaChips.length > 0 && ( -
+ e.stopPropagation()} + className="block min-w-0 text-sm text-slate-400 truncate" + > {metaChips.join(' · ')} -
+ )}
); @@ -644,6 +654,7 @@ const Reviews: React.FC = () => { id: 'details', enableSorting: false, enableColumnFilter: false, + meta: { rowLink: false }, header: () => ( Details @@ -651,12 +662,11 @@ const Reviews: React.FC = () => { ), cell: ({ row }) => (