Варианты: Исходная V1 Что менялось
НАВИГАТОР ОБРАЗОВАТЕЛЬНЫХ ПРОГРАММ

Основные изменения

Форма подбора

Логика и подсказки:

  • Подсказка о том, что русский язык обязателен и нужно минимум 3 экзамена
  • При выборе любого предмета «Русский язык» отмечается автоматически
  • В режиме калькулятора баллов русский язык визуально выделен жёлтой рамкой с меткой «обяз.»
  • Выпадающий список специальностей открывается сразу при клике на поле поиска
  • Если пользователь ввёл текст, но ничего не выбрал и кликнул в сторону — поле очищается
  • Поиск работает по отдельным словам: запрос «тика теле» найдёт «Информатика и телекоммуникации»
  • Совпавшие фрагменты подсвечиваются оранжевым прямо в списке результатов

Визуальные улучшения и анимации:

  • Кнопка «Получить консультацию» с pulse-анимацией и SVG-иконкой для быстрой связи с приёмной комиссией
  • Предметы ЕГЭ — chip-кнопки с плавной анимацией выделения при выборе
  • Переключение режимов (Подбор по ЕГЭ / Калькулятор / ВИ без ЕГЭ) — мгновенное, без перезагрузки
  • Счётчик найденных программ в кнопке «Поиск» обновляется в реальном времени с пульсацией при изменении
  • Декоративные SVG-элементы в заголовках факультетов и блоках фильтра

AJAX без перезагрузки:

  • Нажатие «Поиск» обновляет список программ через AJAX — страница не перезагружается и не прыгает наверх
  • При изменении любого фильтра мгновенно обновляется предварительный счётчик программ (без полной перезагрузки результатов)
  • Плавная анимация обновления: блок результатов затемняется с размытием на время загрузки, затем появляется с новыми данными
  • Позиция скролла сохраняется — пользователь остаётся там, где был. Раньше страница «улетала» в начало, а после этого скроллилась к результатам
  • Убрана повторная отправка формы по F5 — больше не появляется раздражающее окно «Подтвердить повторную отправку данных формы»
  • Спиннер загрузки встроен в кнопку «Поиск» — заменяет число на время запроса, докручивает минимум один оборот, после завершения число возвращается с пульсацией
  • Факультеты отсортированы по алфавиту (А→Я) — как в выпадающем списке выбора факультета

Известное ограничение

При обновлении страницы (F5) выбранные фильтры сбрасываются, так как состояние хранится только в DOM. Для решения необходимо сохранять параметры фильтров в GET-параметрах URL — это также важно для SEO: поисковые системы смогут индексировать страницы с конкретными комбинациями фильтров как отдельные входные точки.

Обновлённый футер

Адаптирован для мобильных устройств — на экранах до 960px контент выстраивается в один столбец.

Альтернативный дизайн стартовой страницы

Создан вариант V1 стартовой страницы в формате «Compact Grid» — все блоки собраны в компактную сетку из двух колонок. В hero-блоке появилась карусель фотографий университета и иконки для быстрой связи через мессенджеры. Доступен по адресу /applicant/v1.

Панель переключения между вариантами отображается на всех страницах сайта.

Исправления

критично

Устранение N+1 запросов — первая загрузка ускорена в 10+ раз

Проблема

При первом открытии страницы программ (когда кэш пуст) загрузка занимала более 1 секунды. Причина — сервис ProfileStructureService выполнял отдельный SQL-запрос к таблице profile_admin_data на каждую из 6409 строк таблицы program_subjects. Итого — до 6409 SQL-запросов при каждом холодном старте.

Решение

Все записи ProfileAdminData теперь предзагружаются одним SQL-запросом перед циклом и передаются в метод appendEduForm() как lookup-коллекция. Вместо обращения к базе на каждой итерации — мгновенный поиск по ключу в памяти.

Результат

Метрика Было Стало
SQL-запросов (холодный кэш) 6 409 1
Время построения структуры > 1 сек ~73 мс
критично

Повторные загрузки всей таблицы при каждой комбинации фильтров

Проблема

Для каждой уникальной комбинации фильтров (уровень образования + предметы ЕГЭ + формы обучения) создавался отдельный ключ кэша. Если пользователь менял хотя бы один фильтр — контроллер снова загружал все 6409 строк из базы, заново строил иерархическую структуру и сохранял под новым ключом. При активном использовании фильтров — десятки полных загрузок таблицы за сессию.

Решение

Полная структура теперь кэшируется один раз под единым ключом. Фильтрация по уровню, предметам и формам обучения выполняется из кэшированной структуры в памяти — без обращений к базе данных.

Результат

Сценарий Было Стало
Холодный кэш (первый запрос) 6409 строк из БД 6409 строк из БД (123 мс, один раз)
Горячий кэш, без фильтров 0 SQL, из кэша 0 SQL, 3 мс
Горячий кэш + фильтр по ЕГЭ снова 6409 строк из БД 0 SQL, 4 мс
Каждая новая комбинация фильтров снова 6409 строк из БД 0 SQL, фильтрация из кэша
fix

Мигание шрифтов при загрузке (FOUT)

Проблема

При загрузке страницы (особенно при Ctrl+F5) текст сначала отображался системным шрифтом, а через мгновение «перескакивал» на кастомный Helvetica Neue Cyr — это создавало визуальное «дёрганье» всей страницы.

Решение

Шрифты добавлены как ассеты Vite и подключены через <link rel="preload"> в layout. Браузер начинает скачивать шрифты параллельно с CSS, не дожидаясь парсинга @font-face. URL в preload совпадает с URL из CSS — браузер делает один запрос, а не два.

Результат

К моменту первого рендера шрифты уже в кэше — страница отображается сразу с правильной типографикой, без моргания.

fix

Двойное подключение скриптов и стилей

Проблема

В основном layout-файле (default.blade.php) директива @vite вызывалась дважды — один раз безусловно в <head>, второй раз внутри условного блока проверки манифеста. В результате браузер получал:

  • Два одинаковых <link rel="stylesheet"> — CSS применялся повторно, вызывая пересчёт стилей (re-layout / re-style)
  • Два <script type="module"> — JS-код инициализировался дважды, обработчики событий навешивались повторно
  • Дублированные <link rel="preload"> и <link rel="modulepreload"> — лишние сетевые запросы

Визуально это проявлялось как «моргание» при переключении между страницами — стили применялись, сбрасывались и применялись снова.

Решение

Удалён безусловный вызов @vite — оставлен единственный внутри условного блока с проверкой manifest.json. Также отключена клиентская фильтрация факультетов при клике по чекбоксу (до нажатия «Поиск»), которая дублировала серверную логику — список программ менялся мгновенно при клике, не дожидаясь отправки формы.

Результат

Каждый ассет (app.css, app.js, header.js) загружается ровно один раз. Устранено моргание при переходах, уменьшен объём сетевых запросов, обработчики событий не дублируются.

fix

Выделение текста при двойном клике на фильтры

При двойном клике на кнопки предметов ЕГЭ или форм обучения браузер выделял текст внутри кнопки. Добавлен select-none — теперь быстрые клики по фильтрам не вызывают случайное выделение текста.

fix

Адаптивная вёрстка для планшетов и мобильных

Проблема

На мобильных экранах (375px) контент был зажат в фиксированные 290px — по бокам оставались огромные пустые поля. На планшетах (768px) страница программ показывала мобильный фильтр вместо десктопного, карточки шли в одну узкую колонку. Футер был полностью скрыт на экранах меньше 1440px.

Решение

  • Все фиксированные мобильные ширины (290px, 277px, 295px) заменены на calc(100vw - 2rem)
  • Страница программ: мобильный фильтр скрывается с 700px (чтобы планшеты в портретном режиме ~706px показывали десктопную форму), карточки в 2 колонки с 700px, 3 колонки с 1440px
  • Заголовки факультетов: увеличен шрифт (16px мобильный, 20px планшет), картинки скрыты до 960px
  • Футер: убрано скрытие на экранах меньше 1440px, добавлена раскладка в один столбец для мобильных
fix

HTML-валидация по W3C — исправлены ошибки на всех страницах

Проблемы

  • lang="en" на всех страницах — поисковые системы и скринридеры считали сайт англоязычным
  • Атрибут for на элементе <button> — допустим только на <label>
  • Дублированные атрибуты id="burger-icon" и id="close-icon" — каждый <img> содержал id дважды
  • 56 изображений <img> без атрибута alt — ошибка доступности (accessibility)
  • Пустой атрибут type="" на кнопке «Очистить выбор» — невалидное значение

Решение

  • locale в config/app.php изменён на ru — теперь <html lang="ru">
  • Убран невалидный for="menu-toggle" с кнопок бургер-меню
  • Удалены дублированные id на иконках бургер-меню
  • Добавлен alt="" на все декоративные <img> (Vector.svg, стрелки, hash.svg)
  • Пустой type="" заменён на type="button"

Яндекс.Метрика — исправлено

Код счётчика Яндекс.Метрики содержал две ошибки HTML-валидации:

  • Сломанные кавычки в <noscript>: в атрибуте src тега <img> использовался символ &quot; (HTML-сущность кавычки) вместо обычной кавычки ". Из-за этого браузер воспринимал style="position:absolute; left:-9999px;" не как атрибут стиля, а как часть URL картинки. W3C-валидатор сообщал: «Bad value for attribute src — Illegal character in path segment», «Attribute position:absolute; not allowed on element img».
  • Блок <noscript> стоял в <head>: по спецификации HTML5 элемент <noscript> в <head> может содержать только <link>, <style> и <meta> — но не <div> и <img>. Валидатор выдавал: «Bad start tag div in noscript in head», что каскадно ломало весь документ («Stray end tag head», «Start tag body seen but an element of the same type was already open»).

Что было сделано:

  • Кавычки &quot; заменены на обычные " — атрибуты src и style теперь парсятся корректно
  • Весь блок метрики (<script> + <noscript>) перенесён из <head> в конец <body> (перед </body>) — это допустимое и рекомендуемое размещение