feat(prep): тумблер «готовится к ЦТ» на странице персональных учеников
my-students.html: колонка «ЦТ» с тумблером у каждого ученика (teacher_students вне классов). Бэкенд уже поддерживал (canManageStudent включает teacher_students); статус грузится per-student через LS.prepStudentTracks, переключение — prepSetStudent/prepUnsetStudent. togglePrep на window (скрипт-модуль). Иконки — SVG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,11 +66,17 @@
|
|||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
}
|
}
|
||||||
.ms-row {
|
.ms-row {
|
||||||
display:grid; grid-template-columns: 36px 1.5fr 2fr 1fr auto auto;
|
display:grid; grid-template-columns: 36px 1.5fr 2fr 1fr auto auto auto;
|
||||||
gap:14px; padding:14px 20px; align-items:center;
|
gap:14px; padding:14px 20px; align-items:center;
|
||||||
border-bottom:1px solid var(--border);
|
border-bottom:1px solid var(--border);
|
||||||
transition:background .12s;
|
transition:background .12s;
|
||||||
}
|
}
|
||||||
|
.prep-toggle { display:inline-flex; align-items:center; gap:5px; padding:4px 9px; border-radius:999px;
|
||||||
|
border:1px solid var(--border); background:var(--surface); cursor:pointer; font-size:.74rem;
|
||||||
|
font-weight:600; color:var(--text-3); transition:all .15s; white-space:nowrap; }
|
||||||
|
.prep-toggle svg { width:13px; height:13px; }
|
||||||
|
.prep-toggle:hover { border-color:var(--violet); color:var(--violet); }
|
||||||
|
.prep-toggle.on { background:rgba(155,93,229,.12); border-color:var(--violet); color:var(--violet); }
|
||||||
.ms-row:last-child { border-bottom:none; }
|
.ms-row:last-child { border-bottom:none; }
|
||||||
.ms-row:hover { background:rgba(155,93,229,.04); }
|
.ms-row:hover { background:rgba(155,93,229,.04); }
|
||||||
.ms-row.head {
|
.ms-row.head {
|
||||||
@@ -110,7 +116,7 @@
|
|||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
.ms-row { grid-template-columns: 36px 1fr auto; gap:10px; }
|
.ms-row { grid-template-columns: 36px 1fr auto; gap:10px; }
|
||||||
.ms-row > :nth-child(3), .ms-row > :nth-child(4) { display:none; }
|
.ms-row > :nth-child(3), .ms-row > :nth-child(4), .ms-row > :nth-child(5), .ms-row > :nth-child(6) { display:none; }
|
||||||
.ms-row.head { display:none; }
|
.ms-row.head { display:none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -193,11 +199,46 @@
|
|||||||
|
|
||||||
let students = [];
|
let students = [];
|
||||||
|
|
||||||
|
/* Флаг «готовится к ЦТ» (трек ct-math открывает карточки + курс + пробники). */
|
||||||
|
const PREP_TRACK = 'ct-math';
|
||||||
|
let _prep = {}; // { studentId: true|false }
|
||||||
|
|
||||||
|
function prepCellHtml(id) {
|
||||||
|
const on = _prep[id] === true;
|
||||||
|
return `<button class="prep-toggle${on ? ' on' : ''}" onclick="togglePrep(${id})" title="${on ? 'Готовится к ЦТ — нажмите, чтобы снять' : 'Отметить «готовится к ЦТ»'}">
|
||||||
|
${on
|
||||||
|
? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>ЦТ'
|
||||||
|
: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/></svg>нет'}</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPrepAll() {
|
||||||
|
if (!students.length) return;
|
||||||
|
const results = await Promise.all(students.map(s =>
|
||||||
|
LS.prepStudentTracks(s.id)
|
||||||
|
.then(r => ({ id: s.id, on: (r.tracks || []).includes(PREP_TRACK) }))
|
||||||
|
.catch(() => ({ id: s.id, on: false }))));
|
||||||
|
_prep = {};
|
||||||
|
results.forEach(r => { _prep[r.id] = r.on; });
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.togglePrep = async function (id) {
|
||||||
|
const on = _prep[id] === true;
|
||||||
|
try {
|
||||||
|
if (on) await LS.prepUnsetStudent(id, PREP_TRACK);
|
||||||
|
else await LS.prepSetStudent(id, PREP_TRACK);
|
||||||
|
_prep[id] = !on;
|
||||||
|
render();
|
||||||
|
if (LS.toast) LS.toast(on ? 'Снято' : 'Готовится к ЦТ', 'success');
|
||||||
|
} catch (e) { if (LS.toast) LS.toast(e.message || 'Ошибка', 'error'); else alert(e.message || 'Ошибка'); }
|
||||||
|
};
|
||||||
|
|
||||||
async function loadStudents() {
|
async function loadStudents() {
|
||||||
try {
|
try {
|
||||||
const r = await LS.api('/api/teacher-students');
|
const r = await LS.api('/api/teacher-students');
|
||||||
students = r.students || [];
|
students = r.students || [];
|
||||||
render();
|
render();
|
||||||
|
loadPrepAll();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
document.getElementById('ms-list-container').innerHTML = `<div class="ms-empty">Ошибка: ${esc(e.message)}</div>`;
|
document.getElementById('ms-list-container').innerHTML = `<div class="ms-empty">Ошибка: ${esc(e.message)}</div>`;
|
||||||
}
|
}
|
||||||
@@ -222,6 +263,7 @@
|
|||||||
<div>Email</div>
|
<div>Email</div>
|
||||||
<div>Заданий</div>
|
<div>Заданий</div>
|
||||||
<div>Добавлен</div>
|
<div>Добавлен</div>
|
||||||
|
<div title="Готовится к ЦТ — открывает карточки, курс и пробники ЦТ">ЦТ</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
${students.map(s => `
|
${students.map(s => `
|
||||||
@@ -234,6 +276,7 @@
|
|||||||
<div class="ms-email">${esc(s.email)}</div>
|
<div class="ms-email">${esc(s.email)}</div>
|
||||||
<div class="ms-meta"><b style="color:var(--text)">${s.assignment_count || 0}</b></div>
|
<div class="ms-meta"><b style="color:var(--text)">${s.assignment_count || 0}</b></div>
|
||||||
<div class="ms-meta">${fmtDate(s.added_at)}</div>
|
<div class="ms-meta">${fmtDate(s.added_at)}</div>
|
||||||
|
<div>${prepCellHtml(s.id)}</div>
|
||||||
<div style="display:flex;gap:6px">
|
<div style="display:flex;gap:6px">
|
||||||
<button class="ms-btn" onclick="showProfile(${s.id}, '${esc(s.name).replace(/'/g, "\\'")}')" title="Профиль для Квантика (слабые темы)">
|
<button class="ms-btn" onclick="showProfile(${s.id}, '${esc(s.name).replace(/'/g, "\\'")}')" title="Профиль для Квантика (слабые темы)">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"/></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"/></svg>
|
||||||
|
|||||||
Reference in New Issue
Block a user