feat(permissions): B6 — массовая выдача права классу (личный оверрайд всем ученикам)
POST /api/permissions/class/:id/bulk { permission, enabled } (admin, явный
requireRole) — выставляет user_permissions всем ученикам класса (1/0/null=сброс),
точечный token_version bump каждому. Валидация: только студенческие ключи.
Клиент LS.setClassPermission. В админке «Доступ · роли» — блок «Массово по
классу»: выбор класса → у каждого права «включить/выключить всем / сбросить».
Тест: оверрайд всем + сброс + отклонение teacher-ключа. Backend 221 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,17 +4,66 @@
|
||||
'use strict';
|
||||
let inited = false;
|
||||
let _permData = null;
|
||||
let _bulkTargets = null; // { classes:[{id,name,students:[]}] }
|
||||
let _bulkClassId = null;
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
_permData = await LS.getPermissions();
|
||||
renderPermissions();
|
||||
loadBulk();
|
||||
} catch(e) {
|
||||
document.getElementById('perm-teacher').innerHTML =
|
||||
`<p style="color:var(--danger);font-size:13px">Ошибка загрузки: ${esc(e.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Массово по классу ── */
|
||||
async function loadBulk() {
|
||||
const root = document.getElementById('perm-bulk');
|
||||
if (!root) return;
|
||||
try { _bulkTargets = await LS.accessTargets(); }
|
||||
catch { _bulkTargets = { classes: [] }; }
|
||||
renderBulk();
|
||||
}
|
||||
function renderBulk() {
|
||||
const root = document.getElementById('perm-bulk');
|
||||
if (!root || !_permData) return;
|
||||
const classes = (_bulkTargets && _bulkTargets.classes) || [];
|
||||
const studentDefs = (_permData.definitions || []).filter(d => d.role === 'student');
|
||||
const opts = classes.map(c => `<option value="${c.id}" ${_bulkClassId === c.id ? 'selected' : ''}>${esc(c.name)} (${(c.students || []).length})</option>`).join('');
|
||||
const cls = classes.find(c => c.id === _bulkClassId);
|
||||
const btn = (key, val, label, bg) => `<button onclick="bulkPerm('${esc(key)}',${val})" style="padding:3px 9px;border:1px solid var(--border);border-radius:7px;background:${bg};color:var(--text-3);cursor:pointer;font-family:inherit;font-size:11.5px">${label}</button>`;
|
||||
const rows = (cls ? studentDefs : []).map(d => `
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 0;border-top:1px solid var(--border-h,#eee)">
|
||||
<span style="font-size:13px;color:var(--text-1)">${esc(d.label)}</span>
|
||||
<span style="display:inline-flex;gap:6px">${btn(d.key, 1, 'включить всем', 'transparent')}${btn(d.key, 0, 'выключить всем', 'transparent')}${btn(d.key, 'null', 'сбросить', 'var(--border-h,#eee)')}</span>
|
||||
</div>`).join('');
|
||||
root.innerHTML = `
|
||||
<div style="font-weight:600;margin-bottom:6px">Массово по классу</div>
|
||||
<div style="font-size:12px;color:var(--muted);margin-bottom:10px">Выставить личное правило сразу всем ученикам класса (переопределяет роль). «Сбросить» — вернуть наследование роли.</div>
|
||||
<select onchange="selBulkClass(this.value)" style="padding:8px 12px;border:1.5px solid var(--border);border-radius:9px;font-family:inherit;font-size:0.9rem;min-width:220px">
|
||||
<option value="">— выберите класс —</option>${opts}
|
||||
</select>
|
||||
<div style="margin-top:10px">${cls ? (rows || '<p style="color:var(--muted);font-size:12px">Нет студенческих прав.</p>') : '<p style="color:var(--muted);font-size:12px">Выберите класс.</p>'}</div>`;
|
||||
}
|
||||
function selBulkClass(v) { _bulkClassId = v ? Number(v) : null; renderBulk(); }
|
||||
async function bulkPerm(permission, enabled) {
|
||||
if (!_bulkClassId) return;
|
||||
if (enabled === 'null') enabled = null;
|
||||
const cls = ((_bulkTargets && _bulkTargets.classes) || []).find(c => c.id === _bulkClassId);
|
||||
const clsName = cls ? cls.name : ('#' + _bulkClassId);
|
||||
if (enabled === 0) {
|
||||
const ok = await LS.confirm(`Выключить это право всем ученикам класса «${clsName}»?`,
|
||||
{ title: 'Подтвердите', confirmText: 'Выключить всем' });
|
||||
if (!ok) return;
|
||||
}
|
||||
try {
|
||||
const r = await LS.setClassPermission(_bulkClassId, permission, enabled);
|
||||
LS.toast(`Готово: затронуто учеников — ${r.affected}`, 'success');
|
||||
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
|
||||
}
|
||||
|
||||
function permCard(role, def, en, labelOf) {
|
||||
const enabled = en[def.key];
|
||||
const reqs = def.requires || [];
|
||||
@@ -154,6 +203,8 @@
|
||||
window.togglePermGroup = togglePermGroup;
|
||||
window.filterPermissions = filterPermissions;
|
||||
window.loadPermLog = loadPermLog;
|
||||
window.selBulkClass = selBulkClass;
|
||||
window.bulkPerm = bulkPerm;
|
||||
|
||||
window.AdminSections = window.AdminSections || {};
|
||||
window.AdminSections.permissions = {
|
||||
|
||||
Reference in New Issue
Block a user