You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
WantedTable and UpcomingTable both decoded stable row-id keys as array indices (issues[parseInt(key)]), so selection was always empty. The identical bug shipped in two files because there was no shared implementation to fix once. Both now carry a hand-written comment explaining the invariant — a comment is not a seam.
Filters and pagination
SeriesFilters.tsx renders filters, SeriesTable.tsx:60–75 owns the nuqs parser definitions (including a hand-written sortParser with a custom delimiter at :55), and SeriesTable.tsx:127 applies them. SeriesListPage.tsx recomputes mangaCount/comicCount from the full array — a fourth place that knows ContentType === "manga". #290 moved filter ownership into SeriesTable; #293 then had to reset pagination when switching views; #292 added the URL/shadow-state sync at :95–104.
Action bars
Three separate ones: queue/BulkActionBar.tsx (72), import/ImportBulkActions.tsx (114), and a third rendered inline at SeriesTable.tsx:412–420. #290's stated goal was "unify table action bar" — it unified the toolbar, not the bulk bar.
Implementation absorbs selection decoding, the page-clamp rule, sort state, and the nuqs URL-sync dance.
Explicitly a hook, not a schema engine.#308 deleted 7,447 lines / 65 files of a schema-driven table system that was unreachable, @ts-nocheck'd, and inside the eslint ignore list — an over-general abstraction built without callers. The lesson is to share the state, not to generate the tables.
The URL-sync dance becomes a tested invariant instead of a bug fix
leverage: one interface, 5 tables
Deletion test
Passes. Keep DataTable.tsx — a renderer is correctly shallow by design.
Test gap
13 test files for 105 components. IssuesTable.tsx (834 lines) and UpcomingTable.tsx have no direct tests. Every existing table test is a post-hoc regression pin, one per incident. The hook's interface becomes the test surface for all five.
From the architecture review, candidate 8 (Strong). Independent of the backend candidates — can run in parallel.
Problem
Five tables, each with its own state machine. This area has the highest defect density in the repo — #290, #292, #293, #307 are all table-state fixes.
series/SeriesTable.tsxrow.ComicID(:257)Object.keys(rowSelection).filter(...)(:156)useStateseries/IssuesTable.tsxObject.keys(rowSelection).map(...)(:187)initialState.paginationimport/ImportTable.tsxgetImportGroupRowId(:512)emitSelectionChangequeue/WantedTable.tsxrow.IssueID(:200):190)queue/UpcomingTable.tsxrow.IssueID(:223):213)The shared layer is a renderer only —
data-table/DataTable.tsx(105 lines) takes an already-builtTanstackTable<TData>. All state is per-table.How #307 happened, twice
WantedTableandUpcomingTableboth decoded stable row-id keys as array indices (issues[parseInt(key)]), so selection was always empty. The identical bug shipped in two files because there was no shared implementation to fix once. Both now carry a hand-written comment explaining the invariant — a comment is not a seam.Filters and pagination
SeriesFilters.tsxrenders filters,SeriesTable.tsx:60–75owns the nuqs parser definitions (including a hand-writtensortParserwith a custom delimiter at:55), andSeriesTable.tsx:127applies them.SeriesListPage.tsxrecomputesmangaCount/comicCountfrom the full array — a fourth place that knowsContentType === "manga". #290 moved filter ownership intoSeriesTable; #293 then had to reset pagination when switching views; #292 added the URL/shadow-state sync at:95–104.Action bars
Three separate ones:
queue/BulkActionBar.tsx(72),import/ImportBulkActions.tsx(114), and a third rendered inline atSeriesTable.tsx:412–420. #290's stated goal was "unify table action bar" — it unified the toolbar, not the bulk bar.Proposed deepening
A
useTableState<T>hook. Interface:Implementation absorbs selection decoding, the page-clamp rule, sort state, and the nuqs URL-sync dance.
Explicitly a hook, not a schema engine. #308 deleted 7,447 lines / 65 files of a schema-driven table system that was unreachable,
@ts-nocheck'd, and inside the eslint ignore list — an over-general abstraction built without callers. The lesson is to share the state, not to generate the tables.Wins
Deletion test
Passes. Keep
DataTable.tsx— a renderer is correctly shallow by design.Test gap
13 test files for 105 components.
IssuesTable.tsx(834 lines) andUpcomingTable.tsxhave no direct tests. Every existing table test is a post-hoc regression pin, one per incident. The hook's interface becomes the test surface for all five.From the architecture review, candidate 8 (Strong). Independent of the backend candidates — can run in parallel.