diff --git a/analysis_results.md b/analysis_results.md
new file mode 100644
index 0000000..44c82ff
--- /dev/null
+++ b/analysis_results.md
@@ -0,0 +1,288 @@
+# Анализ структуры вкладок и предложения по UX/UI оптимизации системы Magistr
+
+В данном документе представлен детальный анализ текущей структуры интерфейса административной панели (SPA) и настроек системы Magistr. Выявлены пересекающиеся области функционала, сформулированы предложения по их объединению, переносу в настройки или удалению, а также предложены новые функциональные модули и технические решения для оптимизации UX/UI и производительности.
+
+---
+
+## 1. Анализ текущей структуры и доступности вкладок
+
+На данный момент в основном SPA-интерфейсе администратора (`/admin/`) зарегистрировано **13 вкладок**, а в дополнительном SPA настроек (`/admin/settings/`) — **2 вкладки**.
+
+В зависимости от роли пользователя, сайдбар фильтруется следующим образом:
+
+| Вкладка (ID) | Описание | Доступность по ролям |
+| :--- | :--- | :--- |
+| `users` | CRUD пользователей и архивирование | `ADMIN` |
+| `groups` | CRUD групп, подгрупп и назначений календарей | `ADMIN` |
+| `edu-forms` | Справочник форм обучения | `ADMIN` |
+| `profiles` | Управление профилями обучения специальностей | `ADMIN` |
+| `equipments` | Каталог оборудования | `ADMIN`, `EDUCATION_OFFICE` |
+| `classrooms` | Список аудиторий с привязкой оборудования | `ADMIN`, `EDUCATION_OFFICE` |
+| `subjects` | CRUD всех дисциплин и привязка преподавателей | `ADMIN` |
+| `department-workspace` | Рабочая область кафедры (дисциплины, импорт, нагрузка) | `ADMIN`, `DEPARTMENT` |
+| `schedule-view` | Просмотр расписаний (read-only) | `ADMIN`, `EDUCATION_OFFICE`, `DEPARTMENT`, `SCHEDULE_VIEWER` |
+| `schedule` | Конструктор правил динамического расписания | `ADMIN`, `EDUCATION_OFFICE` |
+| `academic-calendar` | Календарные графики и Excel-редактор сетки | `ADMIN`, `EDUCATION_OFFICE` |
+| `auditorium-workload` | Матрицы и таблицы загруженности | `ADMIN`, `EDUCATION_OFFICE` |
+| `database` | Управление тенантами (БД клиентов) | `ADMIN` |
+| **Settings** (SPA) | Общие настройки, Управление временными слотами | `ADMIN`, `EDUCATION_OFFICE` (ссылка в подвале) |
+
+---
+
+## 2. Пересечения функционала и проблемные зоны
+
+### 2.1. Избыточное дробление академической структуры
+Для описания структуры обучения сейчас используются четыре отдельные вкладки:
+1. `departments-data` (Создание кафедры/специальности)
+2. `profiles` (Профили обучения специальностей)
+3. `edu-forms` (Формы обучения)
+4. `groups` (В блоке создания группы выбираются форма, кафедра, специальность и профиль)
+
+**В чём проблема:**
+* **Разрыв контекста:** Чтобы завести новую специальность с профилями, администратору сначала нужно пойти во вкладку `departments-data`, создать специальность, затем перейти во вкладку `profiles`, выбрать в селекте эту специальность и добавить профили.
+* **Перегрузка меню:** 13 вкладок в сайдбаре для роли `ADMIN` — это слишком много. Пользователю трудно ориентироваться визуально.
+
+### 2.2. Дублирование и раздвоение управления дисциплинами
+Управление дисциплинами разнесено между вкладками `subjects` (для администратора) и `department-workspace` (для кафедры).
+
+**В чём проблема:**
+* Администратор во вкладке `subjects` видит плоскую таблицу всех дисциплин всех кафедр, но не имеет удобных инструментов фильтрации по кафедре.
+* Форма создания новой дисциплины во вкладке `subjects` требует ввода **числового ID кафедры вручную** (поле `new-subject-department` типа `number`). Это критическая UX-ошибка: администратор не должен помнить числовые ID БД наизусть.
+* Привязка преподавателей к дисциплинам (`teacher_subjects`) находится на вкладке `subjects`. При этом сама нагрузка преподавателей и список преподавателей кафедры отображаются на вкладке `department-workspace`. Логичнее было бы видеть, какие дисциплины ведёт преподаватель, непосредственно при работе с его нагрузкой или в едином справочнике преподавателей/кафедры.
+
+### 2.3. Низкая частота использования справочника оборудования (`equipments`)
+Оборудование существует только как подчиненная сущность для аудиторий (чтобы указать, что в аудитории 101 есть 20 ПК и 1 проектор).
+
+**В чём проблема:**
+* Выделение целой вкладки под простой список («Проектор», «ПК») не оправдано. Список оборудования меняется раз в несколько лет.
+* При этом привязка оборудования к аудиториям происходит на вкладке `classrooms`.
+
+### 2.4. Перегруженность вкладки групп (`groups`)
+На вкладке групп на одном экране выводятся сразу три тяжелые формы и три таблицы:
+1. Создание/редактирование групп + таблица групп.
+2. Создание подгрупп для лабораторных + таблица подгрупп.
+3. Назначение календарного графика группе + таблица назначений.
+
+**В чём проблема:**
+* Экран перегружен элементами управления.
+* Для работы с подгруппами или назначениями графиков конкретной группы пользователю приходится выбирать эту группу из длинного выпадающего списка в отдельной форме, вместо того чтобы выполнять действия в контексте строки этой группы в таблице.
+
+### 2.5. Нахождение системной вкладки «База данных» в общем меню расписания
+Вкладка `database` отвечает за управление тенантами (добавление новых вузов-клиентов, создание для них БД и запуск Flyway миграций).
+
+**В чём проблема:**
+* Это технический функционал уровня Super-Admin / DevOps. Он не имеет отношения к ежедневному планированию расписания. Нахождение его в одном ряду с расписанием и группами нарушает логику интерфейса.
+
+---
+
+## 3. Рекомендации по оптимизации структуры (Что убрать, объединить и перенести)
+
+```mermaid
+graph TD
+ subgraph Было (13 вкладок в меню ADMIN)
+ direction TB
+ T1[database]
+ T2[edu-forms]
+ T3[departments-data]
+ T4[profiles]
+ T5[equipments]
+ T6[classrooms]
+ T7[groups]
+ T8[users]
+ T9[subjects]
+ T10[department-workspace]
+ T11[schedule]
+ T12[schedule-view]
+ T13[academic-calendar]
+ T14[auditorium-workload]
+ end
+
+ subgraph Стало (Сгруппированное меню + настройки)
+ direction TB
+ subgraph Основное меню SPA (Группы вкладок)
+ direction TB
+ DASH[1. Дашборд / Главная]
+
+ subgraph Модуль: Расписание
+ SCH_ED[2. Конструктор расписания]
+ SCH_VI[3. Просмотр расписаний]
+ CAL[4. Календарный график]
+ WORK[5. Загруженность]
+ end
+
+ subgraph Модуль: Структура
+ UNIV[6. Структура вуза Кафедры, Специальности, Профили]
+ GRP[7. Группы и подгруппы]
+ SUBJ[8. Дисциплины]
+ end
+
+ subgraph Модуль: Ресурсы
+ CLASS[9. Аудиторный фонд Аудитории + Оборудование]
+ end
+
+ USR[10. Пользователи]
+ end
+
+ subgraph SPA Настройки (Настройки системы)
+ direction TB
+ SET_GEN[Общие настройки]
+ SET_TIME[Временные слоты сетка]
+ SET_FORM[Формы обучения из edu-forms]
+ SET_DB[Базы данных / Тенанты из database]
+ end
+ end
+
+ T1 -->|Перенести| SET_DB
+ T2 -->|Перенести| SET_FORM
+ T3 -->|Объединить| UNIV
+ T4 -->|Объединить| UNIV
+ T5 -->|Объединить| CLASS
+ T6 -->|Объединить| CLASS
+ T7 -->|Оптимизировать| GRP
+ T8 -->|Оставить| USR
+ T9 -->|Оптимизировать| SUBJ
+ T10 -->|Сохранить для роли Кафедра| SUBJ
+ T11 -->|Оставить| SCH_ED
+ T12 -->|Оставить| SCH_VI
+ T13 -->|Оставить| CAL
+ T14 -->|Оставить| WORK
+```
+
+### 3.1. Перенос в настройки (`/admin/settings/`)
+1. **База данных (`database`)**: Перенести в SPA настроек в раздел «Управление тенантами». Доступ должен быть только у роли `ADMIN`.
+2. **Формы обучения (`edu-forms`)**: Перенести в настройки. Данный справочник заполняется один раз и не требует оперативного доступа из главного меню.
+
+### 3.2. Объединение вкладок
+1. **Объединить Кафедры, Специальности и Профили** в одну вкладку **«Структура вуза»** (`university-structure`):
+ * Интерфейс делится на две вкладки внутри страницы: **«Кафедры»** и **«Специальности»**.
+ * Управление профилями должно происходить прямо внутри списка специальностей. Например, в строке специальности в таблице добавляется кнопка «Профили», открывающая список профилей этой специальности в раскрывающейся строке (accordion) или модальном окне.
+2. **Объединить Аудитории (`classrooms`) и Оборудование (`equipments`)** в одну вкладку **«Аудиторный фонд»**:
+ * Основной экран показывает список аудиторий.
+ * Рядом располагается кнопка «Управление каталогом оборудования», которая открывает модальное окно с простым списком (добавить/удалить тип оборудования). Это избавит от необходимости держать под это отдельную вкладку в сайдбаре.
+
+### 3.3. Реорганизация вкладки групп (`groups`)
+* **Оставить на вкладке только список групп** с возможностью фильтрации по курсу, кафедре и форме обучения.
+* **Вынести подгруппы и назначения календарей из глобальных форм:**
+ * В строке таблицы для каждой группы добавить действия:
+ * `[Подгруппы]` — открывает компактное модальное окно для быстрого создания/редактирования подгрупп (лабораторных) именно для этой группы.
+ * `[Календарь]` — открывает модальное окно назначения календарного учебного графика на учебные годы для этой группы.
+ * Это уберет лишние тяжелые формы с главной страницы и исключит необходимость выбора группы из селектов вручную.
+
+### 3.4. Оптимизация Дисциплин (`subjects`) и Кабинета кафедры
+* **Исправить форму добавления дисциплины:** Заменить ручной числовой ввод ID кафедры (`new-subject-department`) на стандартный выпадающий список (выбор из загруженных кафедр).
+* **Связать привязку преподавателей с дисциплинами:** Вместо двух разрозненных таблиц на вкладке `subjects` («Все дисциплины» и «Привязки преподавателей»), объединить их:
+ * В строке дисциплины выводить список привязанных преподавателей (в виде чипов/badge).
+ * По клику на кнопку «Преподаватели» в строке дисциплины открывать модальное окно привязки/отвязки преподавателей для этой конкретной дисциплины.
+
+---
+
+## 4. Что необходимо добавить (Новый функционал)
+
+### 4.1. Главная страница / Дашборд (`dashboard`)
+При входе в систему администратор и сотрудники учебного отдела должны видеть не сырые таблицы пользователей или расписания, а высокоуровневую аналитическую панель:
+* **Информационные карточки (Метрики):**
+ * Всего активных групп / студентов.
+ * Количество задействованных преподавателей.
+ * Процент занятости аудиторного фонда на текущий день.
+ * Количество запланированных пар на сегодня.
+* **Мониторинг расписания в реальном времени:**
+ * Список идущих прямо сейчас пар (текущий временной слот).
+ * Ближайшие свободные аудитории.
+* **Быстрый переход:** Кнопки быстрого создания правила расписания, добавления переноса/замены.
+
+### 4.2. Центр предупреждений и конфликтов (Red Zone)
+В соответствии с планируемой бизнес-логикой (раздел 5 `docs/BUSINESS_LOGIC.md`), система должна автоматически валидировать расписание и предупреждать о коллизиях.
+Необходима отдельная вкладка или виджет на дашборде **«Конфликты расписания»**:
+* **Критические ошибки (Блокирующие генерацию):**
+ * *Накладки преподавателей:* Преподаватель назначен в один и тот же временной слот в разные аудитории / к разным группам (кроме потоковых лекций).
+ * *Накладки аудиторий:* Две разные пары стоят в одной аудитории в один слот.
+* **Предупреждения (Мягкие проверки):**
+ * *Превышение вместимости:* Численность группы (или сумма подгрупп при совмещенных занятиях) превышает физическую вместимость аудитории.
+ * *Недостаток оборудования:* Дисциплина (например, лабораторная по химии) требует определенного оборудования, которого нет в назначенной аудитории.
+ * *Превышение нагрузки:* Преподаватель превысил недельный лимит часов.
+
+---
+
+## 5. Концепция новой структуры меню (Сайдбара)
+
+Для уменьшения визуального шума сайдбар предлагается сделать **двухуровневым** (сворачиваемые группы) или четко разделить визуальными разделителями:
+
+```
+[ Логотип Magistr ]
+---------------------------
+👤 Кабинет кафедры <-- (Показывается только для роли DEPARTMENT)
+📈 Дашборд <-- (Новый главный экран для ADMIN / EDU_OFFICE)
+
+📁 РАСПИСАНИЕ
+ 🗓 Конструктор правил
+ 🗓 Точечные изменения <-- (Выделенный функционал оверрайдов/переносов)
+ 📅 Календарный график
+ 📊 Загруженность
+ 🔍 Просмотр расписаний
+
+🗂 СУБЪЕКТЫ И РЕСУРСЫ
+ 🏫 Аудиторный фонд <-- (Объединенные Аудитории + Оборудование)
+ 🎓 Группы обучения <-- (Группы + модалки подгрупп/календарей)
+ 🏛 Структура вуза <-- (Кафедры + Специальности + Профили)
+ 📚 Дисциплины <-- (Дисциплины + модалки преподавателей)
+
+⚙️ СИСТЕМА
+ 👥 Пользователи
+ ⚙️ Настройки <-- (Переход в настройки SPA: слоты, БД, формы обучения)
+---------------------------
+[ Профиль / Выход ]
+```
+
+---
+
+## 6. Идеи по оптимизации фронтенда и взаимодействия с API
+
+### 6.1. Кэширование справочников на клиенте
+На данный момент при переключении почти каждой вкладки заново запрашиваются списки кафедр, специальностей, учебных годов и т.д.
+* **Решение:** Реализовать простой кэш-менеджер во фронтенд-клиенте (`api.js` или отдельный `cache.js`):
+ ```javascript
+ const cache = new Map();
+ export async function getCachedData(key, fetchPromise) {
+ if (!cache.has(key)) {
+ cache.set(key, await fetchPromise);
+ }
+ return cache.get(key);
+ }
+ export function clearCache(key) {
+ if (key) cache.delete(key);
+ else cache.clear();
+ }
+ ```
+ Это позволит избежать десятков дублирующих запросов при переключении между вкладками `groups`, `subjects`, `schedule`.
+
+### 6.2. Ленивая инициализация вкладок (Lazy Loading)
+Сейчас в `main.js` импортируются все JS-модули вкладок одновременно при загрузке страницы:
+```javascript
+import { initUsers } from './views/users.js';
+import { initGroups } from './views/groups.js';
+// ...еще 10 импортов
+```
+* **Решение:** Перейти на динамический импорт модулей инициализации при переходе на конкретную вкладку:
+ ```javascript
+ async function switchTab(tab) {
+ // ...
+ const viewModule = await import(`./views/${tab}.js`);
+ viewModule.init();
+ // ...
+ }
+ ```
+ Это уменьшит размер начального JS-бандла и ускорит первую загрузку страницы.
+
+### 6.3. Единый реестр кастомных выпадающих списков
+В проекте реализованы кастомные селекты (выпадающие списки с поддержкой поиска и чекбоксов). При динамической подгрузке HTML-шаблонов часто требуется их повторная инициализация.
+* **Решение:** Создать глобальный шину событий (Event Bus) или MutationObserver, который автоматически подхватывает элементы с классами `.custom-select` или атрибутами `data-select` и инициализирует их без вызова ручного кода `initAllCustomDropdowns()` в каждом отдельном скрипте представления.
+
+---
+
+## Резюме
+
+Предложенные изменения позволят:
+1. Сократить количество пунктов основного меню с **13** до **8-9** логически сгруппированных разделов.
+2. Исключить ручной ввод ID (заменив на селекты) и разгрузить перегруженные интерфейсы (выносом подгрупп и назначений в контекстные модальные окна групп).
+3. Повысить отзывчивость интерфейса за счет клиентского кэширования справочников и ленивой загрузки JS-модулей.
+4. Добавить полноценный Дашборд и Центр предупреждений о конфликтах, что выведет аналитические возможности системы на уровень премиум-класса.
diff --git a/backend/src/main/java/com/magistr/app/dto/RenderedLessonDto.java b/backend/src/main/java/com/magistr/app/dto/RenderedLessonDto.java
index b449fae..3fee5f1 100644
--- a/backend/src/main/java/com/magistr/app/dto/RenderedLessonDto.java
+++ b/backend/src/main/java/com/magistr/app/dto/RenderedLessonDto.java
@@ -36,6 +36,7 @@ public record RenderedLessonDto(
String activityType,
Integer lessonTypeAcademicHours,
Integer consumedLessonTypeAcademicHoursBeforeLesson,
- Integer remainingLessonTypeAcademicHoursAfterLesson
+ Integer remainingLessonTypeAcademicHoursAfterLesson,
+ com.magistr.app.model.ScheduleParity ruleParity
) {
}
diff --git a/backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java b/backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java
index c0215a1..772cb32 100644
--- a/backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java
+++ b/backend/src/main/java/com/magistr/app/service/ScheduleGeneratorService.java
@@ -406,7 +406,8 @@ public class ScheduleGeneratorService {
"Т",
lessonTypeAcademicHours,
consumedBeforeLesson,
- remainingAfterLesson
+ remainingAfterLesson,
+ slot.getParity()
);
}
diff --git a/backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java b/backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java
index f226564..89ffb85 100644
--- a/backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java
+++ b/backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java
@@ -136,7 +136,8 @@ public class ScheduleQueryService {
lesson.activityType(),
lesson.lessonTypeAcademicHours(),
lesson.consumedLessonTypeAcademicHoursBeforeLesson(),
- lesson.remainingLessonTypeAcademicHoursAfterLesson()
+ lesson.remainingLessonTypeAcademicHoursAfterLesson(),
+ lesson.ruleParity()
);
}
@@ -273,7 +274,8 @@ public class ScheduleQueryService {
source.activityType(),
source.lessonTypeAcademicHours(),
source.consumedLessonTypeAcademicHoursBeforeLesson(),
- source.remainingLessonTypeAcademicHoursAfterLesson()
+ source.remainingLessonTypeAcademicHoursAfterLesson(),
+ source.ruleParity()
);
}
}
diff --git a/frontend/admin/css/auditorium-workload.css b/frontend/admin/css/auditorium-workload.css
index c3cb0e9..e7ada53 100644
--- a/frontend/admin/css/auditorium-workload.css
+++ b/frontend/admin/css/auditorium-workload.css
@@ -576,6 +576,19 @@
gap: 1rem;
}
+.schedule-view-control-card {
+ position: relative;
+ z-index: 10;
+}
+.schedule-view-results-card {
+ position: relative;
+ z-index: 5;
+}
+.schedule-view-table-card {
+ position: relative;
+ z-index: 1;
+}
+
.schedule-view-control-card,
.schedule-view-results-card,
.schedule-view-table-card {
diff --git a/frontend/admin/css/components.css b/frontend/admin/css/components.css
index 22d67bb..c731669 100755
--- a/frontend/admin/css/components.css
+++ b/frontend/admin/css/components.css
@@ -1317,8 +1317,12 @@ input[type="number"] {
}
.select-layer-active {
- position: relative;
- z-index: 300;
+ position: relative !important;
+ z-index: 10001 !important;
+}
+
+.custom-select-wrapper.open {
+ z-index: 10002 !important;
}
.custom-select-trigger {
@@ -1338,9 +1342,11 @@ input[type="number"] {
}
.filter-row .custom-select-trigger {
+ height: 38px;
+ box-sizing: border-box;
padding: 0.45rem 1rem;
font-size: 0.85rem;
- border-color: transparent;
+ border-color: var(--bg-card-border);
}
.custom-select-trigger:hover {
@@ -1416,6 +1422,30 @@ input[type="number"] {
transform: translateY(0) scale(1);
}
+.custom-select-search-container {
+ padding: 0.25rem 0.5rem 0.5rem 0.5rem;
+ border-bottom: 1px solid var(--bg-card-border);
+ margin-bottom: 0.35rem;
+}
+
+.custom-select-search-input {
+ width: 100%;
+ box-sizing: border-box;
+ padding: 0.5rem 0.75rem;
+ background: var(--bg-input);
+ border: 1px solid var(--bg-card-border);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-size: 0.85rem;
+ outline: none;
+ transition: all var(--transition);
+}
+
+.custom-select-search-input:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 2px var(--accent-glow);
+}
+
/* Custom Scrollbar for Dropdown */
.custom-select-menu::-webkit-scrollbar {
width: 6px;
@@ -1517,7 +1547,8 @@ input[type="number"] {
.filter-row {
display: flex;
align-items: center;
- gap: 0.5rem;
+ gap: 0.75rem;
+ flex-wrap: wrap;
}
.filter-row label {
@@ -1530,12 +1561,15 @@ input[type="number"] {
}
.filter-row input {
- padding: 0.45rem 2rem 0.45rem 0.7rem;
+ height: 38px;
+ box-sizing: border-box;
+ padding: 0.45rem 0.75rem;
background: var(--bg-input);
- border: 1px solid transparent;
+ border: 1px solid var(--bg-card-border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 0.85rem;
+ outline: none;
transition: background var(--transition), border-color var(--transition), box-shadow var(--transition);
}
@@ -2238,4 +2272,16 @@ tbody tr:hover {
position: fixed;
top: 1.25rem;
right: 1.25rem;
+}
+
+.lesson-weeks-badge {
+ font-size: 0.72rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ background: var(--bg-input);
+ border: 1px solid var(--bg-card-border);
+ border-radius: 4px;
+ padding: 0.05rem 0.35rem;
+ white-space: nowrap;
+ line-height: 1.2;
}
\ No newline at end of file
diff --git a/frontend/admin/index.html b/frontend/admin/index.html
index 99d71b2..aebbf5d 100755
--- a/frontend/admin/index.html
+++ b/frontend/admin/index.html
@@ -16,6 +16,19 @@
+
\ No newline at end of file
diff --git a/frontend/admin/views/university-structure.html b/frontend/admin/views/university-structure.html
new file mode 100644
index 0000000..2a44ebc
--- /dev/null
+++ b/frontend/admin/views/university-structure.html
@@ -0,0 +1,197 @@
+
+