Commit a9c12a2
feat(tables): add export, import column creation, infinite row pagination (#4373)
* feat(tables): add export, import column creation, infinite row pagination
- Add `/api/table/[tableId]/export` route streaming CSV/JSON downloads
- Rename `/import-csv` route to `/import` and extend to auto-create new
columns from unmapped CSV headers via `createColumns` form field
- Switch table view to `useInfiniteQuery` so tables larger than 1000
rows fully load; reconcile created rows into the paginated cache so
"New row" past 1000 no longer reverts on invalidate
- Wire scroll-driven prefetch (600px from bottom) and pre-drain pages
before append to keep new-row position consistent
- Polish import-csv dialog flow and add Export action to header
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(table): route boundary validation through Zod contracts
Switch the table query hooks and the import/export routes to the
codebase's contract-based request pattern so the API validation audit
and boundary policy ratchet pass.
- `hooks/queries/tables.ts` now calls every endpoint via
`requestJson(contract, ...)`; the only remaining raw `fetch` calls are
the streaming export download and the multipart CSV upload, both
annotated `boundary-raw-fetch:`.
- The import and export routes parse `params`, the `format` query, and
the multipart form fields with shared schemas from
`@/lib/api/contracts/tables`. Two new contract schemas
(`csvImportCreateColumnsSchema`, `tableExportFormatSchema`) cover the
fields specific to these routes.
- Bumps the audit baseline by one route to account for the new export
endpoint.
* fix(table): address PR bot review
- Move the `isAppendingRowRef` reset into the create-row mutation's
`onSettled` callback. The previous `try/finally` cleared the guard
immediately after `mutate()` returned, before the request completed,
so a rapid second click on "New row" could fire a duplicate create.
- Drop the unused `addTableColumns` wrapper from `lib/table/service`.
The CSV import flow only ever uses the transaction-bound
`addTableColumnsWithTx`; the standalone wrapper was dead code.
* fix(table): re-throw on infinite-query fetch error in append-row drain
`useInfiniteQuery.fetchNextPage()` resolves (rather than rejects) when a
page request fails — the resolved value carries `status: 'error'` while
`hasNextPage` still reflects the last successful page. The drain loop in
`handleAppendRow` relied on a thrown error to bail, so a failed mid-drain
fetch could spin indefinitely and leave the append guard stuck on.
Re-throw inside `fetchNextPageWrapped` when the result is an error so the
caller's `try/catch` runs as intended.
* fix(table): import TABLE_LIMITS from constants to keep server code out of client bundle
The client hook `use-table-data.ts` was importing `TABLE_LIMITS` as a
value from the `@/lib/table` barrel, which transitively pulls in
`service.ts` and the `postgres` driver. Turbopack then tried to bundle
`fs`, `net`, `tls`, and `perf_hooks` into the client component graph and
the production build failed.
Import `TABLE_LIMITS` directly from `@/lib/table/constants` (a pure
constants module) and keep the type imports against the barrel.
* fix(table): run batch unique check inside import transaction
`checkBatchUniqueConstraintsDb` queried the global `db` connection, so
inside a single import transaction (one tx wrapping all batches) the
constraint lookup couldn't see uncommitted rows from prior batches —
duplicates that crossed `CSV_MAX_BATCH_SIZE` boundaries slipped through.
Accept an optional executor and pass `trx` from `batchInsertRowsWithTx`
so the lookup observes the in-flight transaction state.
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 47208e0 commit a9c12a2
16 files changed
Lines changed: 1142 additions & 267 deletions
File tree
- apps/sim
- app
- api/table/[tableId]
- export
- import
- workspace/[workspaceId]
- home/components/mothership-view/components/resource-content
- tables
- [tableId]
- components/table
- hooks
- components
- import-csv-dialog
- hooks/queries
- lib
- api/contracts
- table
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
0 commit comments