chore: shadcn DataTable proof-of-concept (2 consumers) - #132
Open
iacapuca wants to merge 2 commits into
Open
Conversation
Introduces a shadcn/ui-style generic DataTable alongside the existing monolithic <Table>, and migrates two simple consumers to prove the pattern. The rest continue using the monolithic component until a follow-up PR. Stacks on top of PR #131 (TanStack Table v8 migration) — which is required for @tanstack/react-table to exist. Infrastructure (all new files): - components.json: shadcn CLI config at repo root - app/lib/utils.ts: cn() helper (clsx + tailwind-merge) - app/components/ui/table.tsx: unstyled Table primitives (Table/Header/Body/Row/Head/Cell/Caption) as thin Tailwind wrappers, no CSS-variable theming — uses concrete classes that match the existing project aesthetic - app/components/ui/data-table.tsx: generic DataTable<TData, TValue> with sort + pagination + optional rowClassName. Consumers own the title/header UI above it and write typed ColumnDef<TData> lists. Deps added: class-variance-authority, clsx, tailwind-merge (3 packages total). Consumer migrations (proof of concept): - app/components/Samu/SamuClientSide.tsx: cities list table now uses DataTable with a typed CityRow interface. Title moved out of the table into an <h3> above it. - app/components/ExecucaoCicloviaria/CityContent.tsx: PDC structures table now uses DataTable with a typed PdcRelation interface. Same pattern — title moved outside. Intentionally not touched: - The monolithic app/components/Commom/Table/Table.tsx stays in place. The other 6 consumers still use it. Follow-up PRs can migrate them individually and delete the monolithic component once all consumers are off it. Verification: typecheck count unchanged (301 pre-existing errors, zero introduced); build succeeds in 8.4s. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lish Extends the POC DataTable with an optional render-prop toolbar that exposes the TanStack table instance, then wires up a dropdown filter on the PDC structures table (filter by Tipologia Prevista) as a real example of the shadcn "consumer-owned filter UI" pattern. DataTable additions: - getFacetedRowModel + getFacetedUniqueValues row models registered so consumers can populate dropdowns from the actual data. - ColumnFilters state wired into the useReactTable instance. - New `toolbar?: (table) => ReactNode` prop rendered above the table's bordered container. - Sort button styling: focus ring moved to focus-visible (mouse click no longer paints the blue ring), chevron icon wrapped in a fixed w-4/h-4 span to prevent sub-pixel layout shift on sort state changes. Consumer (CityContent PDC structures table): - Adds a typed dropdown filter on the pdc_typology column. Options come from column.getFacetedUniqueValues(), so the list is always in sync with whatever typologies the Atlas API returns — no hardcoded list. - Replaces the `as string` assertion on getFilterValue() with a typeof narrow; drops `as PdcRelation[]` on the data prop (any is structurally assignable). The remaining `v is string` is a proper type predicate, not an assertion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a shadcn/ui-style generic
<DataTable>alongside the existing monolithic<Table>, and migrates two simple consumers to prove the pattern works end-to-end. The remaining six consumers stay on the monolithic component for now — this PR is a beachhead, not a full conversion.Why
Answering the "why are there so many `any`s in Table.tsx" question: because the monolithic component is generic-shaped but not generic-parameterized. shadcn's recipe — typed primitives + consumer-owned
useReactTablecall — puts the generic parameter at the consumer level, where it actually has meaning. That's the pattern this POC sets up.Infrastructure (new)
components.json— shadcn CLI config, root of repo. Aliases configured for~/components,~/lib,~/components/ui.app/lib/utils.ts— the standardcn()helper (clsx+tailwind-merge).app/components/ui/table.tsx— unstyled Table primitives:Table,TableHeader,TableBody,TableFooter,TableRow,TableHead,TableCell,TableCaption. Thin `div`/`table`/`tr`/`td`… wrappers with Tailwind classes. No CSS-variable theming — uses concrete Tailwind classes that match the existing project aesthetic. (Adopting shadcn's full HSL-based theming is a bigger migration we can tackle separately.)app/components/ui/data-table.tsx— generic<DataTable<TData, TValue>>with sorting + pagination + optionalrowClassName(row). ~150 lines total. Consumers pass typedColumnDef<TData>[]and get type safety all the way down.Deps added:
class-variance-authority,clsx,tailwind-merge(3 packages).Proof-of-concept consumer migrations
app/components/Samu/SamuClientSide.tsx(cities list)<Table>with inline untypedcolumns={[…]}<DataTable columns={citiesTableColumns} data={…} />with a typedCityRowinterfaceapp/components/ExecucaoCicloviaria/CityContent.tsx(PDC structures)<Table>with inline untypedcolumns={[…]}+showFilters={true}<DataTable columns={pdcColumns} data={…} />with a typedPdcRelationinterfaceBoth consumers now:
<h3>above the DataTable (composition, not a prop).row.original.*— no moreany.<input>above the DataTable withtable.getColumn("x").setFilterValue(...).Explicitly not touched
app/components/Commom/Table/Table.tsxstays in place. The other six consumers still use it.CountsTable,CountingComparisionTable,IdecicloTable,ViasRankingTable,dados.viasinseguras.\$slugsinistros table, anddados.loa.index— each a separate follow-up PR. The LOA page will be the hardest (filterType pills, allColumns expansion, classifyAction row coloring, column-sum headers).Selectprimitives yet).Verification
npm install— 3 new packagesnpm run typecheck— 301 pre-existing errors on base; zero new errors introducednpm run build— clean in 8.4sTest plan
/dados/samu— scroll to "Lista completa das cidades" table. Sort headers should work (click Ranking, click Município), pagination buttons should appear if > 10 rows. No filter UI (expected)./dados/execucaocicloviaria→ select a city → scroll to "Estruturas do PDC para {city}" table. Same checks.Follow-up roadmap
Once this proves out, the staged migration:
dados.viasinseguras.\$slug(inline sinistros),CountsTable,CountingComparisionTable— ~1-2 hours eachIdecicloTable,ViasRankingTable— feature-richer, custom filter components to re-homedados.loa.index— half a day on its ownCommom/Table/Table.tsx+TableFilters.tsxTotal remaining work: ~1.5-2 days spread across 3-4 PRs.
🤖 Generated with Claude Code