0a24a66a2e
registry: карта GROUP (Вопросы / Класс и ученики / Библиотека / Курсы и шаблоны / Геймификация / Контент / Тесты и активность / Профиль), проброшена в byRole.group. permissions.js: вкладка «Доступ · роли» рендерит права секциями по группам, у каждой — «включить все / выключить все» (с подтверждением, если в группе есть requireConfirmOff). Карточка вынесена в permCard(). Тест: definitions содержат group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
164 lines
8.4 KiB
JavaScript
164 lines
8.4 KiB
JavaScript
'use strict';
|
|
/* admin → permissions section (role-based teacher/student permissions) */
|
|
(function () {
|
|
'use strict';
|
|
let inited = false;
|
|
let _permData = null;
|
|
|
|
async function load() {
|
|
try {
|
|
_permData = await LS.getPermissions();
|
|
renderPermissions();
|
|
} catch(e) {
|
|
document.getElementById('perm-teacher').innerHTML =
|
|
`<p style="color:var(--danger);font-size:13px">Ошибка загрузки: ${esc(e.message)}</p>`;
|
|
}
|
|
}
|
|
|
|
function permCard(role, def, en, labelOf) {
|
|
const enabled = en[def.key];
|
|
const reqs = def.requires || [];
|
|
const unmet = reqs.filter(r => !en[r]);
|
|
const blocked = unmet.length > 0; // зависимость не выполнена → право неактивно
|
|
const effective = enabled && !blocked;
|
|
const isModified = (enabled ? 1 : 0) !== def.default;
|
|
const modDot = isModified
|
|
? `<span class="perm-modified-dot" title="Отличается от значения по умолчанию"></span>`
|
|
: '';
|
|
const reqNote = reqs.length
|
|
? `<div class="perm-desc" style="margin-top:3px;color:${blocked ? 'var(--danger,#dc2626)' : 'var(--muted)'}">${blocked ? 'Требует: ' + unmet.map(r => esc(labelOf[r] || r)).join(', ') : 'Зависит от: ' + reqs.map(r => esc(labelOf[r] || r)).join(', ')}</div>`
|
|
: '';
|
|
return `
|
|
<div class="perm-card${effective ? ' enabled' : ''}" id="perm-card-${role}-${def.key.replace('.','_')}" style="${blocked ? 'opacity:.65' : ''}">
|
|
<div class="perm-info">
|
|
<div class="perm-label">${esc(def.label)}${modDot}</div>
|
|
<div class="perm-desc">${esc(def.desc)}</div>
|
|
${reqNote}
|
|
</div>
|
|
<label class="perm-toggle" title="${blocked ? 'Сначала включите зависимость' : (enabled ? 'Выключить' : 'Включить')}">
|
|
<input type="checkbox" ${enabled ? 'checked' : ''} ${blocked ? 'disabled' : ''}
|
|
onchange="togglePermission('${esc(role)}','${esc(def.key)}',this.checked,this)">
|
|
<span class="perm-track"></span>
|
|
<span class="perm-thumb"></span>
|
|
</label>
|
|
</div>`;
|
|
}
|
|
|
|
function renderPermissions() {
|
|
if (!_permData) return;
|
|
const { permissions, definitions } = _permData;
|
|
['teacher', 'student'].forEach(role => {
|
|
const container = document.getElementById('perm-' + role);
|
|
const defs = definitions.filter(d => d.role === role);
|
|
const en = {}, labelOf = {};
|
|
defs.forEach(d => { en[d.key] = permissions[role]?.[d.key] ?? d.default; labelOf[d.key] = d.label; });
|
|
// группировка по def.group (порядок групп — по первому появлению)
|
|
const order = [], byGroup = {};
|
|
defs.forEach(d => { const g = d.group || 'Прочее'; if (!byGroup[g]) { byGroup[g] = []; order.push(g); } byGroup[g].push(d); });
|
|
const grpBtn = (g, on) => `<button onclick="togglePermGroup('${esc(role)}','${esc(g)}',${on})" style="padding:3px 9px;border:1px solid var(--border);border-radius:7px;background:transparent;color:var(--text-3);cursor:pointer;font-family:inherit;font-size:11.5px">${on ? 'включить все' : 'выключить все'}</button>`;
|
|
container.style.display = 'block'; // секции групп блоками (контейнер сам — .perm-grid)
|
|
container.innerHTML = order.map(g => `
|
|
<div class="perm-group" data-group="${esc(g)}" style="margin-bottom:14px">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin:4px 0 8px">
|
|
<div style="font-size:11.5px;font-weight:700;color:var(--text-3);text-transform:uppercase;letter-spacing:.05em">${esc(g)}</div>
|
|
<div style="display:flex;gap:6px">${grpBtn(g, true)}${grpBtn(g, false)}</div>
|
|
</div>
|
|
<div class="perm-grid">${byGroup[g].map(def => permCard(role, def, en, labelOf)).join('')}</div>
|
|
</div>`).join('');
|
|
});
|
|
}
|
|
|
|
async function togglePermGroup(role, group, enable) {
|
|
const defs = (_permData.definitions || []).filter(d => d.role === role && (d.group || 'Прочее') === group);
|
|
if (!defs.length) return;
|
|
if (!enable && defs.some(d => d.requireConfirmOff)) {
|
|
const roleLabel = role === 'teacher' ? 'Учитель' : 'Ученик';
|
|
const ok = await LS.confirm(
|
|
`Выключить все права группы «${group}» для роли «${roleLabel}»? Затронет всех пользователей роли.`,
|
|
{ title: 'Подтвердите выключение группы', confirmText: 'Выключить все' }
|
|
);
|
|
if (!ok) return;
|
|
}
|
|
try {
|
|
await Promise.all(defs.map(d => LS.setPermission(role, d.key, enable)));
|
|
if (!_permData.permissions[role]) _permData.permissions[role] = {};
|
|
defs.forEach(d => { _permData.permissions[role][d.key] = enable; });
|
|
renderPermissions();
|
|
LS.toast(enable ? `Группа «${group}» включена` : `Группа «${group}» выключена`, 'success');
|
|
} catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); load(); }
|
|
}
|
|
|
|
async function togglePermission(role, key, enabled, checkbox) {
|
|
if (!enabled) {
|
|
const def = (_permData.definitions || []).find(d => d.role === role && d.key === key);
|
|
if (def && def.requireConfirmOff) {
|
|
const roleLabel = role === 'teacher' ? 'Учитель' : 'Ученик';
|
|
const ok = await LS.confirm(
|
|
`Выключение «${def.label}» затронет всех пользователей роли «${roleLabel}». Они потеряют доступ. Продолжить?`,
|
|
{ title: 'Подтвердите выключение права', confirmText: 'Выключить' }
|
|
);
|
|
if (!ok) { checkbox.checked = true; return; }
|
|
}
|
|
}
|
|
checkbox.disabled = true;
|
|
try {
|
|
await LS.setPermission(role, key, enabled);
|
|
if (!_permData.permissions[role]) _permData.permissions[role] = {};
|
|
_permData.permissions[role][key] = enabled;
|
|
const safeKey = key.replace('.', '_');
|
|
const card = document.getElementById(`perm-card-${role}-${safeKey}`);
|
|
if (card) card.classList.toggle('enabled', enabled);
|
|
// Re-render to refresh the modified-dot indicator across all cards.
|
|
renderPermissions();
|
|
LS.toast(enabled ? 'Право включено' : 'Право отключено', 'success');
|
|
} catch(e) {
|
|
checkbox.checked = !enabled;
|
|
LS.toast('Ошибка: ' + e.message, 'error');
|
|
} finally {
|
|
checkbox.disabled = false;
|
|
}
|
|
}
|
|
|
|
function filterPermissions(query) {
|
|
const q = (query || '').trim().toLowerCase();
|
|
['teacher', 'student'].forEach(role => {
|
|
const block = document.querySelector(`#perm-${role}`)?.closest('.perm-role-block');
|
|
const cards = document.querySelectorAll(`#perm-${role} .perm-card`);
|
|
let visibleCount = 0;
|
|
cards.forEach(card => {
|
|
const label = (card.querySelector('.perm-label')?.textContent || '').toLowerCase();
|
|
const desc = (card.querySelector('.perm-desc')?.textContent || '').toLowerCase();
|
|
const show = !q || label.includes(q) || desc.includes(q);
|
|
card.style.display = show ? '' : 'none';
|
|
if (show) visibleCount++;
|
|
});
|
|
if (block) block.style.display = visibleCount === 0 ? 'none' : '';
|
|
});
|
|
}
|
|
|
|
async function loadPermLog() {
|
|
const box = document.getElementById('perm-log');
|
|
if (!box) return;
|
|
box.innerHTML = '<p style="color:var(--muted);font-size:12px">Загрузка…</p>';
|
|
try {
|
|
const log = await LS.permissionsLog();
|
|
box.innerHTML = log.length
|
|
? log.map(e => `<div style="font-size:12.5px;padding:5px 0;border-top:1px solid var(--border-h,#eee)"><b>${esc(e.actor)}</b> ${esc(e.text)} <span style="color:var(--muted)">· ${esc((e.at || '').replace('T', ' ').slice(0, 16))}</span></div>`).join('')
|
|
: '<p style="color:var(--muted);font-size:12px">Изменений пока нет.</p>';
|
|
} catch (e) {
|
|
box.innerHTML = `<p style="color:var(--danger);font-size:12px">Ошибка: ${esc(e.message)}</p>`;
|
|
}
|
|
}
|
|
|
|
window.togglePermission = togglePermission;
|
|
window.togglePermGroup = togglePermGroup;
|
|
window.filterPermissions = filterPermissions;
|
|
window.loadPermLog = loadPermLog;
|
|
|
|
window.AdminSections = window.AdminSections || {};
|
|
window.AdminSections.permissions = {
|
|
init: async () => { if (inited) return; inited = true; await load(); },
|
|
reload: load,
|
|
};
|
|
})();
|