From f736d548df0f03a1539195258b1af39c1d15fdf6 Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Fri, 2 Oct 2026 10:10:55 -0500 Subject: [PATCH 1/2] fix(data-views): handle record merging --- .../frontend/js_src/lib/components/DataViews/index.tsx | 10 ++++++++++ .../js_src/lib/components/QueryBuilder/Results.tsx | 4 +++- .../lib/components/QueryBuilder/ResultsWrapper.tsx | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx b/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx index 14caa258c3b..8e74270772c 100644 --- a/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx @@ -200,6 +200,15 @@ function LoadedDataViewFromTable({ restoreScrollTopRef.current = resultsScrollRef.current.scrollTop; setRefreshToken((token) => token + 1); }, []); + const handleMerged = React.useCallback((): void => { + /* + * Merging removes the selected records. Clear the preview before the + * refreshed results arrive so it does not try to load deleted records. + */ + setSelectedIds([]); + setSelectedIndex(0); + handleRefresh(); + }, [handleRefresh]); const handleCloseQueryEditor = (): void => setQueryData(undefined); const handleOpenQueryEditor = (): void => { setIsQueryDirty(false); @@ -282,6 +291,7 @@ function LoadedDataViewFromTable({ createRecordSet={undefined} extraButtons={undefined} onReRun={handleRefresh} + onMerged={handleMerged} onSortChange={(newFields): void => { setRuntimeFields(unParseQueryFields(table.name, newFields)); setQueryRunCount((count) => count + 1); diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx index 4425b871b31..fee74343e59 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx @@ -91,6 +91,7 @@ export type QueryResultsProps = { readonly tableClassName?: string; readonly selectedRows: GetSet>; readonly onResults?: (results: RA) => void; + readonly onMerged?: () => void; readonly scrollRef?: React.MutableRefObject; readonly restoreScrollTopRef?: React.MutableRefObject; readonly refreshToken?: number; @@ -113,6 +114,7 @@ export function QueryResults(props: QueryResultsProps): JSX.Element { onSelected: handleSelected, onSortChange: handleSortChange, onReRun: handleReRun, + onMerged: handleMerged, createRecordSet, extraButtons, containerClassName = '', @@ -498,7 +500,7 @@ export function QueryResults(props: QueryResultsProps): JSX.Element { selectedRows={selectedRows} table={table} onDeleted={handleDelete} - onMerged={handleReRun} + onMerged={handleMerged ?? handleReRun} /> ) : undefined} {hasToolPermission('recordSets', 'create') && totalCount !== 0 ? ( diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx index d235b929fdf..22ebd14c47e 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx @@ -35,6 +35,7 @@ export function QueryResultsWrapper({ onSelected: handleSelected, onResults: handleResults, onReRun: handleReRun, + onMerged: handleMerged, refreshToken, splitPane, splitHorizontal, @@ -69,6 +70,7 @@ export function QueryResultsWrapper({ createRecordSet={createRecordSet} extraButtons={extraButtons} onReRun={handleReRun} + onMerged={handleMerged} onSelected={handleSelected} refreshToken={refreshToken} /> @@ -109,6 +111,7 @@ type ResultsProps = { readonly containerClassName?: string; readonly tableClassName?: string; readonly onResults?: (results: RA) => void; + readonly onMerged?: () => void; readonly scrollRef?: React.MutableRefObject; readonly restoreScrollTopRef?: React.MutableRefObject; readonly resultsRef?: React.MutableRefObject< @@ -177,6 +180,7 @@ export function useQueryResultsWrapper({ tableClassName, onSortChange: handleSortChange, onResults: handleResults, + onMerged: handleMerged, selectedRows: [selectedRows, setSelectedRows], scrollRef, restoreScrollTopRef, @@ -260,6 +264,7 @@ export function useQueryResultsWrapper({ fieldSpecs, initialData, onResults: handleResults, + onMerged: handleMerged, scrollRef, restoreScrollTopRef, sortConfig: queryFields @@ -297,6 +302,7 @@ export function useQueryResultsWrapper({ queryResource, queryRunCount, recordSetId, + handleMerged, ]); return props === undefined From d9dc7042d4209d8d763910efc92824dd357fffc4 Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:08:21 -0500 Subject: [PATCH 2/2] fix(data-views): handle deleted records --- .../frontend/js_src/lib/components/DataViews/index.tsx | 4 ++++ .../js_src/lib/components/QueryBuilder/Results.tsx | 7 ++++++- .../js_src/lib/components/QueryBuilder/ResultsWrapper.tsx | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx b/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx index 8e74270772c..c8b28396e5c 100644 --- a/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx +++ b/specifyweb/frontend/js_src/lib/components/DataViews/index.tsx @@ -291,6 +291,10 @@ function LoadedDataViewFromTable({ createRecordSet={undefined} extraButtons={undefined} onReRun={handleRefresh} + onDeleted={(): void => { + setSelectedIds([]); + setSelectedIndex(0); + }} onMerged={handleMerged} onSortChange={(newFields): void => { setRuntimeFields(unParseQueryFields(table.name, newFields)); diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx index fee74343e59..d673aade458 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/Results.tsx @@ -91,6 +91,7 @@ export type QueryResultsProps = { readonly tableClassName?: string; readonly selectedRows: GetSet>; readonly onResults?: (results: RA) => void; + readonly onDeleted?: (recordId: number) => void; readonly onMerged?: () => void; readonly scrollRef?: React.MutableRefObject; readonly restoreScrollTopRef?: React.MutableRefObject; @@ -121,6 +122,7 @@ export function QueryResults(props: QueryResultsProps): JSX.Element { tableClassName = '', selectedRows: [selectedRows, setSelectedRows], onResults: handleResults, + onDeleted: handleDeleted, scrollRef, restoreScrollTopRef, refreshToken, @@ -499,7 +501,10 @@ export function QueryResults(props: QueryResultsProps): JSX.Element { { + handleDelete(recordId); + handleDeleted?.(recordId); + }} onMerged={handleMerged ?? handleReRun} /> ) : undefined} diff --git a/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx b/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx index 22ebd14c47e..fbb3aaea887 100644 --- a/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx +++ b/specifyweb/frontend/js_src/lib/components/QueryBuilder/ResultsWrapper.tsx @@ -34,6 +34,7 @@ export function QueryResultsWrapper({ extraButtons, onSelected: handleSelected, onResults: handleResults, + onDeleted: handleDeleted, onReRun: handleReRun, onMerged: handleMerged, refreshToken, @@ -67,6 +68,7 @@ export function QueryResultsWrapper({ ) => void; + readonly onDeleted?: (recordId: number) => void; readonly onMerged?: () => void; readonly scrollRef?: React.MutableRefObject; readonly restoreScrollTopRef?: React.MutableRefObject;