Merge branch 'Redesign' of https://gitea.zuev.company/Zuev/magistr into Redesign
This commit is contained in:
@@ -503,6 +503,38 @@
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.quality-rule-issues,
|
||||
.quality-rule-detail-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.quality-rule-issues > span,
|
||||
.quality-rule-detail-list > span {
|
||||
padding: 0.22rem 0.45rem;
|
||||
color: var(--text-secondary);
|
||||
background: color-mix(in srgb, var(--quality-amber) 9%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--quality-amber) 24%, var(--bg-card-border));
|
||||
border-radius: 999px;
|
||||
font-size: 0.66rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.quality-rule-detail-list {
|
||||
display: grid;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.quality-rule-detail-list > span {
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.quality-edit-rule {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.quality-penalty {
|
||||
color: var(--quality-red);
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
|
||||
@@ -186,7 +186,7 @@ function renderProblems() {
|
||||
`).join('')}</span>
|
||||
<small>
|
||||
Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}
|
||||
· ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))} снижают оценку
|
||||
· затронуто ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))}
|
||||
· суммарный штраф
|
||||
</small>
|
||||
</span>
|
||||
@@ -238,7 +238,7 @@ function renderRuleDetails(problem) {
|
||||
<div class="quality-recommendations-head">
|
||||
<span class="quality-kicker">Правило #${escapeHtml(String(problem.scheduleRuleId || '—'))}</span>
|
||||
<h3>${escapeHtml(problem.subjectName || problem.title)}</h3>
|
||||
<p>${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))} снижают оценку; суммарный штраф −${escapeHtml(String(problem.penalty))}.</p>
|
||||
<p>Затронуто ${escapeHtml(String(affectedLessonCount))} ${escapeHtml(lessonWord(affectedLessonCount))}; суммарный штраф −${escapeHtml(String(problem.penalty))}.</p>
|
||||
</div>
|
||||
<div class="quality-rule-detail-list">
|
||||
${issueLabels.map(label => `<span>${escapeHtml(label)}</span>`).join('')}
|
||||
|
||||
@@ -491,12 +491,25 @@ export async function initSchedule() {
|
||||
}
|
||||
renderRules();
|
||||
renderVisualSchedule();
|
||||
openRequestedRule();
|
||||
} catch (error) {
|
||||
rulesTbody.innerHTML = `<tr><td colspan="7" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
|
||||
setVisualState('Ошибка загрузки правил расписания');
|
||||
}
|
||||
}
|
||||
|
||||
function openRequestedRule() {
|
||||
const requestedRuleId = localStorage.getItem('magistr.schedule.openRuleId');
|
||||
if (!requestedRuleId) return;
|
||||
localStorage.removeItem('magistr.schedule.openRuleId');
|
||||
const rule = rules.find(item => String(item.id) === requestedRuleId);
|
||||
if (!rule) {
|
||||
showAlert('schedule-rule-alert', 'Не удалось найти выбранное правило в этом расписании.', 'error');
|
||||
return;
|
||||
}
|
||||
fillRuleForm(rule);
|
||||
}
|
||||
|
||||
function renderRules() {
|
||||
if (!rules.length) {
|
||||
rulesTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Правила расписания не созданы</td></tr>';
|
||||
@@ -2079,6 +2092,13 @@ export async function initSchedule() {
|
||||
return new Date(year, month - 1, day);
|
||||
}
|
||||
|
||||
function localIsoDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function compactWeekRanges(weeks, maxWeeks, parity) {
|
||||
const uniqueWeeks = [...new Set((weeks || []).map(Number).filter(Boolean))].sort((a, b) => a - b);
|
||||
if (!uniqueWeeks.length) return '';
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<h2 id="quality-lab-title">Качество видно до перестановок</h2>
|
||||
<p>
|
||||
Анализатор находит окна, перегруженные дни, неудачные аудитории и нарушения
|
||||
пожеланий. Черновик можно оценить до публикации, не показывая промежуточное
|
||||
расписание студентам и преподавателям.
|
||||
пожеланий. Все проблемные занятия объединяются по правилу, а их штрафы
|
||||
суммируются. Черновик можно оценить до публикации.
|
||||
</p>
|
||||
</div>
|
||||
<div class="quality-run-panel">
|
||||
@@ -55,8 +55,8 @@
|
||||
<div class="quality-problems-column">
|
||||
<div class="quality-section-heading">
|
||||
<div>
|
||||
<span class="quality-kicker">Карта неудобств</span>
|
||||
<h3>Что снижает оценку</h3>
|
||||
<span class="quality-kicker">Карта правил</span>
|
||||
<h3>Правила, снижающие оценку</h3>
|
||||
</div>
|
||||
<span class="quality-problem-total" id="quality-problem-total"></span>
|
||||
</div>
|
||||
@@ -89,8 +89,8 @@
|
||||
<aside class="quality-recommendations" id="quality-recommendations" aria-live="polite">
|
||||
<div class="quality-recommendations-empty">
|
||||
<span class="quality-recommendations-mark">↗</span>
|
||||
<h3>Локальные улучшения</h3>
|
||||
<p>Выберите проблему, чтобы проверить свободные слоты и аудитории.</p>
|
||||
<h3>Работа с правилом</h3>
|
||||
<p>Выберите правило, чтобы увидеть суммарное влияние и открыть его в конструкторе.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
@@ -17,7 +17,7 @@ test('оценка качества получает понятный диапа
|
||||
|
||||
test('фильтр проблем одновременно учитывает важность и категорию', () => {
|
||||
const problems = [
|
||||
{ type: 'GROUP_GAP', severity: 'HIGH' },
|
||||
{ type: 'GROUP_GAP', problemTypes: ['GROUP_GAP', 'ROOM_OVERSIZED'], severity: 'HIGH' },
|
||||
{ type: 'ROOM_OVERSIZED', severity: 'LOW' },
|
||||
{ type: 'TEACHER_SOFT_UNWANTED', severity: 'HIGH' }
|
||||
];
|
||||
@@ -28,7 +28,7 @@ test('фильтр проблем одновременно учитывает в
|
||||
);
|
||||
assert.deepEqual(
|
||||
filterQualityProblems(problems, '', 'ROOM'),
|
||||
[problems[1]]
|
||||
[problems[0], problems[1]]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -47,6 +47,8 @@ test('метрики и вкладка анализа подключены к SP
|
||||
assert.doesNotMatch(capabilities, /DEPARTMENT:[\s\S]*schedule-quality/);
|
||||
assert.match(main, /'schedule-quality':[\s\S]*schedule-quality\.js/);
|
||||
assert.match(index, /data-tab="schedule-quality"/);
|
||||
assert.match(view, /Локальные улучшения/);
|
||||
assert.match(script, /Применить после подтверждения/);
|
||||
assert.match(view, /Правила, снижающие оценку/);
|
||||
assert.match(script, /Изменить правило/);
|
||||
assert.match(script, /magistr\.schedule\.openRuleId/);
|
||||
assert.doesNotMatch(script, /quality\/recommendations/);
|
||||
});
|
||||
|
||||
@@ -48,6 +48,8 @@ test('контур версий подключён к SPA, а конструкт
|
||||
assert.match(constructor, /scheduleVersionId:\s*Number\(scheduleVersionSelect\.value\)/);
|
||||
assert.match(constructor, /basedOnVersionId:\s*Number\(base\.id\)/);
|
||||
assert.match(constructor, /magistr\.schedule\.openVersionId/);
|
||||
assert.match(constructor, /magistr\.schedule\.openRuleId/);
|
||||
assert.match(constructor, /function localIsoDate\(date\)/);
|
||||
assert.match(constructor, /data-slot-id/);
|
||||
assert.match(view, /id="schedule-version-create-draft"/);
|
||||
assert.match(quality, /versionId=.*encodeURIComponent\(versionId\)/);
|
||||
|
||||
Reference in New Issue
Block a user