From 75e44f5b26af1830eb1fa0eac1335b72dbdabe56 Mon Sep 17 00:00:00 2001 From: ERP-System Date: Mon, 8 Jun 2026 16:28:58 +0400 Subject: [PATCH] fix(data-table): preserve prior rows when multi-selecting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onRowSelectionChange computed the next selection from the render-time `rowSelection` closure, which could be stale by the time a second row was toggled. The updater then produced a selection containing only the latest row, and the persistedMap sync deleted the earlier one — so checking a second part unchecked the first, making multi-select behave as single-select. Track the latest selection in a ref and derive the updater from it, so each toggle builds on the current selection. Fixes adding multiple parts/services/ expense items in invoices, estimates, and job cards. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../shared/data-view/table-view/data-table.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/shared/data-view/table-view/data-table.tsx b/apps/dashboard/shared/data-view/table-view/data-table.tsx index 12b03ef..e847784 100644 --- a/apps/dashboard/shared/data-view/table-view/data-table.tsx +++ b/apps/dashboard/shared/data-view/table-view/data-table.tsx @@ -39,8 +39,13 @@ export function DataTable({ // Persisted map of id → original row data across all pages const persistedMap = useRef>(new Map()) - // Current-page selection state that TanStack Table controls + // Current-page selection state that TanStack Table controls. + // A ref mirrors the latest value so onRowSelectionChange never derives the + // next selection from a stale closure (which would drop previously-checked + // rows and make multi-select behave like single-select). const [rowSelection, setRowSelection] = useState({}) + const rowSelectionRef = useRef({}) + rowSelectionRef.current = rowSelection // When the page/data changes, restore selection state for the new page from the map useEffect(() => { @@ -50,6 +55,7 @@ export function DataTable({ const id = String((row as Record)[rowKeyStr]) if (persistedMap.current.has(id)) restored[id] = true }) + rowSelectionRef.current = restored setRowSelection(restored) }, [data]) // eslint-disable-line react-hooks/exhaustive-deps @@ -128,7 +134,10 @@ export function DataTable({ }) }, onRowSelectionChange: (updater) => { - const next = typeof updater === "function" ? updater(rowSelection) : updater + // Derive from the ref (always current), not the render-time closure, + // so toggling a second row preserves earlier selections. + const next = typeof updater === "function" ? updater(rowSelectionRef.current) : updater + rowSelectionRef.current = next setRowSelection(next) if (selection) { // Sync current page into the persisted map