## Purpose Поведение экрана свода 2 (`/svod/2`, вкладки «ССП» и «РФ»): загрузка строк страницами при прокрутке с ограниченным объёмом данных в памяти, поиск и колоночные фильтры через сервер, счётчик «Найдено N из M» и критерии производительности экрана (память, время до первых строк, шаг прокрутки). ## ADDED Requirements ### Requirement: Загрузка строк страницами при прокрутке После нажатия «Загрузить» или «Обновить» экран SHALL запрашивать с сервера только страницу строк с начала списка и варианты колоночных фильтров. Полная длина полосы прокрутки SHALL соответствовать общему числу строк по текущим условиям. Строки, которые ещё не пришли, SHALL показываться пустыми строками-заглушками той же высоты: без значений и без прочерков, которыми помечаются пустые данные. Когда видимая область доходит до незагруженных строк, экран SHALL их запросить и заменить заглушки данными, без сдвига прокрутки. В памяти страницы SHALL держаться строки не более чем фиксированного числа страниц вокруг видимой области, независимо от общего числа строк. Порядок действий пользователя (выбор года и ССП/РФ, пункт «Все ССП/РФ», кнопка «Загрузить») и подписи не меняются. #### Scenario: Первая загрузка «Все ССП/РФ» - **WHEN** пользователь выбирает «Все ССП/РФ» на вкладке «ССП» и нажимает «Загрузить» - **THEN** экран отправляет один запрос страницы с `offset=0` и один запрос вариантов фильтров, показывает первые строки, а высота прокрутки соответствует `total` строкам #### Scenario: Прокрутка в середину и в конец - **WHEN** пользователь перетаскивает полосу прокрутки в середину, а затем в самый конец списка - **THEN** в видимой области сначала могут быть заглушки, затем появляются строки с теми номерами по порядку, что и в полном списке, а последняя строка списка видна у нижнего края #### Scenario: Память не растёт при прокрутке до конца - **WHEN** пользователь прокручивает лист RF «все» до конца и обратно - **THEN** число строк с данными, которые экран держит в памяти, не превышает предела окна (число страниц окна × размер страницы) ### Requirement: Поиск и колоночные фильтры через сервер Изменение строки поиска (после существующей задержки ввода 300 мс) и изменение колоночного фильтра «Вид сметы», «Направление» или «ССП» SHALL перезапрашивать данные с сервера с начала списка, если по текущим году и ССП/РФ данные уже загружены. Если данные не загружены, перезапроса не происходит, как и сейчас. Варианты в колоночных фильтрах SHALL браться из ответа сервера с вариантами для текущих года и ССП/РФ. Ответ на устаревший запрос (после него условия уже поменялись) SHALL NOT попадать на экран. Как и сейчас, изменение поиска, года или ССП/РФ сбрасывает колоночные фильтры текущей вкладки. #### Scenario: Поиск - **WHEN** данные загружены и пользователь вводит «аренда» в поле поиска - **THEN** через 300 мс после последнего нажатия экран запрашивает первую страницу с этим поиском и показывает только найденные строки с начала списка #### Scenario: Быстрая смена условий - **WHEN** пользователь вводит поиск и, не дожидаясь ответа, выбирает колоночный фильтр «Направление» - **THEN** на экране остаются строки, отвечающие обоим условиям, а ответ на первый запрос не показывается #### Scenario: Варианты фильтра «ССП» - **WHEN** загружен лист с тремя организациями - **THEN** в фильтре «ССП» перечислены ровно эти три организации, и выбор одной из них оставляет только её строки ### Requirement: Счётчик «Найдено N из M» Когда задан поиск или хотя бы один колоночный фильтр и число строк по условиям N отличается от общего числа строк M по текущим году и ССП/РФ, экран SHALL показывать «Найдено N из M строк». N берётся из `total` ответа страницы, M — из ответа с вариантами фильтров. Числа форматируются с разделителями `ru-RU`. Иначе подпись не показывается. #### Scenario: Счётчик учитывает и поиск, и фильтры - **WHEN** загружено 5 563 строки, задан поиск и фильтр «Вид сметы», и по ним найдено 120 строк - **THEN** показано «Найдено 120 из 5 563 строк» ### Requirement: Вкладки и обновление У каждой вкладки («ССП», «РФ») SHALL сохраняться своё состояние (условия, загруженное окно строк, общее число строк). Возврат на вкладку показывает её строки без нового запроса, если её условия не менялись. «Обновить» SHALL перезапрашивать варианты фильтров и строки текущей вкладки с сервера. Смена года или ССП/РФ, как и сейчас, требует нажать «Загрузить». Свод 2 SHALL NOT сохранять полные ответы в общем кэше сводов. Поведение свода 1 не меняется. #### Scenario: Переключение вкладок - **WHEN** загружены «ССП» и «РФ», пользователь возвращается на «ССП» - **THEN** строки «ССП» видны сразу, без запроса страницы #### Scenario: Обновить - **WHEN** пользователь нажимает «Обновить» - **THEN** экран заново запрашивает варианты фильтров и первую страницу и показывает актуальные данные с начала списка ### Requirement: Неизменное отображение строк Колонки, их группы, порядок, ширины, закреплённые колонки, форматирование чисел и прочерков (`-` для нуля, `—` для пустого), единицы (руб./тыс./млн), бейджи вида сметы и ССП, «Перейти к задачам» SHALL выглядеть и работать так же, как до изменения. Отображение SHALL NOT зависеть от того, пришло ли значение `null` или поле отсутствует. #### Scenario: Визуальный паритет - **WHEN** на одних данных открыть GO «все» до и после изменения и сравнить первые экраны и три позиции горизонтальной прокрутки - **THEN** значения ячеек, прочерки и оформление совпадают ### Requirement: Ошибки загрузки Если не удалась первая страница или варианты фильтров, экран SHALL показывать, как и сейчас, сообщение об ошибке с кнопкой «Повторить». Если не удалась одна из следующих страниц, экран SHALL оставить на её месте заглушки, сообщить об ошибке всплывающим уведомлением и повторить запрос, когда эта область снова окажется в видимой зоне. #### Scenario: Ошибка догрузки - **WHEN** запрос страницы при прокрутке завершился ошибкой 500 - **THEN** уже показанные строки остаются, на месте страницы — заглушки, показано уведомление; при повторной прокрутке к этому месту запрос повторяется ### Requirement: Память страницы свода 2 На стенде замеров сценарий `perf/e2e/scenarios/svod2-memory.spec.ts` (роль dfip: GO «Все ССП/РФ» → «Загрузить» → вкладка RF «Все ССП/РФ» → «Загрузить» → вкладка GO, затем сборка мусора) SHALL показывать JS-кучу `heap_used_mb` в конечной точке: - на масштабе 1 — не больше 30 % значения «до»; - на масштабе 2 — не больше 1,25 значения «после» на масштабе 1, то есть куча не растёт вместе с объёмом данных. #### Scenario: Замер памяти до и после - **WHEN** сценарий выполнен на одних данных для сборки «до» (вершина AURORA-1842) и «после», на масштабах 1 и 2, по 3 повтора - **THEN** медиана `heap_used_mb` в конечной точке «после» на масштабе 1 не больше 0,30 × «до», а на масштабе 2 — не больше 1,25 × «после» на масштабе 1 ### Requirement: Время до первых строк Время от нажатия «Загрузить» при «Все ССП/РФ» до появления в таблице не менее 20 строк с данными (заглушки не считаются) SHALL быть не больше 50 % значения «до» для GO и RF на масштабах 1 и 2. #### Scenario: Замер времени до первых строк - **WHEN** `scroll.spec.ts` для `svod/svod2_GO` и `svod/svod2_RF` (роль dfip) пишет метрику `first_rows_ms` для сборок «до» и «после» на одних данных - **THEN** медиана `first_rows_ms` «после» не больше 0,5 × медианы «до» для каждого листа и масштаба ### Requirement: Шаг прокрутки не хуже AURORA-1842 Цена шага вертикальной прокрутки свода 2 (`main_thread_ms_per_step` в `scroll.spec.ts`, роль dfip, CPU 1×) SHALL быть не больше 1,10 значения «до» (вершина AURORA-1842, снятая тем же кодом сценария на тех же данных) для GO и RF на масштабе 1. Доля кадров с видимыми заглушками SHALL записываться рядом с ценой шага. #### Scenario: Замер шага прокрутки - **WHEN** `scroll.spec.ts` с `PERF_TARGETS='svod/svod2_*'`, `PERF_ROLES=dfip`, `--grep-invert filter` выполнен для «до» и «после» - **THEN** медиана `main_thread_ms_per_step` «после» не больше 1,10 × «до» для каждого листа, а в записи есть метрика доли кадров с заглушками