From 1b8a1a44ad4800d8f68dd5d02fae72b86efba07c Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 29 Oct 2026 13:21:51 +0400 Subject: [PATCH] =?UTF-8?q?fix(svod2):=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20?= =?UTF-8?q?=D1=81=D0=BC=D0=B5=D0=BD=D1=8B=20=D1=83=D1=81=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D0=B9=20=D1=81=D1=82=D0=B0=D1=80=D1=8B=D0=B5=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20=D0=BD=D0=B5=20=D1=80=D0=B8?= =?UTF-8?q?=D1=81=D1=83=D1=8E=D1=82=D1=81=D1=8F,=20=D0=BF=D0=BE=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B5=D1=82=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20(=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D1=81=D1=8B=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7?= =?UTF-8?q?=D0=BA=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- web/src/pages/Svod2Page/svod2Paging.js | 8 ++++++++ web/src/pages/Svod2Page/svod2Paging.test.js | 16 ++++++++++++++++ web/src/pages/Svod2Page/useSvod2Pages.js | 10 ++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/web/src/pages/Svod2Page/svod2Paging.js b/web/src/pages/Svod2Page/svod2Paging.js index 6e76155..a7318a0 100644 --- a/web/src/pages/Svod2Page/svod2Paging.js +++ b/web/src/pages/Svod2Page/svod2Paging.js @@ -48,6 +48,14 @@ export function buildWindowRows(pages, pageCache, total) { return result; } +const NO_PAGES = Object.freeze(new Map()); + +// Страницы, которые можно показывать: только поколения, чьи данные уже пришли. После смены условий (поиск, фильтры, +// «Обновить») поколение вырастает сразу, а первый ответ нового приходит позже: до него все строки окна — заглушки. +export function getCurrentPages({ pages, dataGeneration, generation }) { + return dataGeneration === generation ? pages : NO_PAGES; +} + export function getVirtualizerPadding({ windowStart, windowEnd, total, rowHeight }) { return { paddingStart: windowStart * rowHeight, diff --git a/web/src/pages/Svod2Page/svod2Paging.test.js b/web/src/pages/Svod2Page/svod2Paging.test.js index 1e44844..bd60d39 100644 --- a/web/src/pages/Svod2Page/svod2Paging.test.js +++ b/web/src/pages/Svod2Page/svod2Paging.test.js @@ -8,6 +8,7 @@ import { buildWindowRows, createSheetPagingState, facetsToMaps, + getCurrentPages, getMissingPages, getPageCount, getSkeletonWidth, @@ -328,3 +329,18 @@ test('первая страница при смене условий: запас assert.equal(shouldFetchFirstPageFallback({ candidate: 37, total: 22419 }), false); assert.equal(shouldFetchFirstPageFallback({ candidate: 75, total: 22419 }), true); }); + +test('смена поколения условий: пока нет ответа нового поколения, старые страницы не показываются (все строки окна — заглушки)', () => { + const pages = new Map([[0, [{ data: 1 }]]]); + assert.equal(getCurrentPages({ pages, dataGeneration: 3, generation: 3 }), pages); + const stale = getCurrentPages({ pages, dataGeneration: 3, generation: 4 }); + assert.equal(stale.size, 0); + const rows = buildWindowRows([0], stale, 3); + assert.deepEqual( + rows.map((r) => isPlaceholderRow(r)), + [true, true, true], + ); +}); +test('до первой загрузки (поколение 0) страниц нет и показывать нечего', () => { + assert.equal(getCurrentPages({ pages: new Map(), dataGeneration: 0, generation: 0 }).size, 0); +}); diff --git a/web/src/pages/Svod2Page/useSvod2Pages.js b/web/src/pages/Svod2Page/useSvod2Pages.js index 2c02353..201575f 100644 --- a/web/src/pages/Svod2Page/useSvod2Pages.js +++ b/web/src/pages/Svod2Page/useSvod2Pages.js @@ -11,6 +11,7 @@ import { buildWindowRows, createSheetPagingState, facetsToMaps, + getCurrentPages, getMissingPages, getPageCount, getVirtualizerPadding, @@ -300,8 +301,13 @@ export function useSvod2Pages({ sheet, year, orgIds, queryKey, search, columnFil const visiblePage = getVisiblePage({ scrollTop: scrollPage * PAGE_SIZE * rowHeight, rowHeight, total: sheetState.total }); const windowPages = useMemo(() => getWindowPages(visiblePage, sheetState.total), [visiblePage, sheetState.total]); const windowRows = useMemo( - () => buildWindowRows(windowPages, sheetState.pages, sheetState.total), - [windowPages, sheetState.pages, sheetState.total], + () => + buildWindowRows( + windowPages, + getCurrentPages({ pages: sheetState.pages, dataGeneration: sheetState.dataGeneration, generation: sheetState.generation }), + sheetState.total, + ), + [windowPages, sheetState.pages, sheetState.dataGeneration, sheetState.generation, sheetState.total], ); const padding = useMemo(() => { if (!hasCurrentData) return NO_PADDING;