feat(modules): soft delete with restore and active filter

This commit is contained in:
dev
2026-09-18 20:04:02 +03:00
parent d3dd922e32
commit d7d4cc1133
8 changed files with 57 additions and 15 deletions
+4
View File
@@ -21,6 +21,10 @@ const ACTION_LABELS = {
'student.update': 'Изменён ученик',
'student.delete': 'Удалён ученик',
'student.batch-group': 'Ученики перенесены в группу',
'module.create': 'Создан модуль',
'module.update': 'Изменён модуль',
'module.delete': 'Модуль отключён',
'module.restore': 'Модуль включён',
'entry.update': 'Изменена запись',
'entry.photo.update': 'Изменено фото записи',
'entry.photo.set_main': 'Главное фото записи',
+1
View File
@@ -151,6 +151,7 @@ async function fetchModules(q, limit) {
const p = new URLSearchParams();
if (q) p.set('search', q);
p.set('limit', String(limit));
p.set('active', '1');
try {
const res = await fetch(`${API}/api/modules?${p}`);
if (!res.ok) return [];
+1
View File
@@ -239,6 +239,7 @@ async function fetchModules(q, limit) {
const p = new URLSearchParams();
if (q) p.set('search', q);
p.set('limit', String(limit));
p.set('active', '1');
const res = await fetch(`${API}/api/modules?${p}`, { headers: hdr() });
if (!res.ok) return [];
const data = await res.json().catch(() => ({}));
+26 -10
View File
@@ -32,14 +32,17 @@ function renderModules(total) {
list.innerHTML = '<div class="empty">Модулей пока нет. Нажмите «Добавить модуль».</div>';
} else {
list.innerHTML = modulesList.map(m => `
<div class="item module-item" data-id="${m.id}">
<div class="item module-item${m.is_active ? '' : ' module-off'}" data-id="${m.id}">
<div class="module-info">
<div class="module-name">${esc(m.name)}</div>
<div class="module-name">${esc(m.name)}${m.is_active ? '' : ' <span class="module-badge">Отключён</span>'}</div>
<div class="module-meta">${m.lessons_count} ${mPlural(m.lessons_count, 'занятие', 'занятия', 'занятий')} · ${m.entries_count} ${mPlural(m.entries_count, 'запись', 'записи', 'записей')}</div>
</div>
<div class="student-actions">
<button class="btn-link" type="button" data-edit-module="${m.id}">Изменить</button>
<button class="btn-del" type="button" data-del-module="${m.id}" title="Удалить">&times;</button>
${m.is_active
? `<button class="btn-link" type="button" data-edit-module="${m.id}">Изменить</button>
<button class="btn-link danger" type="button" data-off-module="${m.id}">Отключить</button>`
: `<button class="btn-link" type="button" data-on-module="${m.id}">Включить</button>
<button class="btn-link" type="button" data-edit-module="${m.id}">Изменить</button>`}
</div>
</div>
`).join('');
@@ -75,15 +78,26 @@ function closeModuleModal() {
document.getElementById('moduleModal').classList.remove('open');
}
async function deleteModule(id) {
async function disableModule(id) {
const m = modulesList.find(x => x.id === id);
const msg = m && m.entries_count > 0
? `Удалить модуль «${m.name}»? У ${m.entries_count} связанных записей тема будет очищена.`
: 'Удалить этот модуль?';
? `Отключить модуль «${m.name}»? Он станет недоступен для выбора, у ${m.entries_count} связанных записей тема сохранится.`
: `Отключить модуль${m ? ` «${m.name}»` : ''}? Он станет недоступен для выбора.`;
if (!confirm(msg)) return;
const res = await fetch(`${API}/api/modules/${id}`, { method: 'DELETE', headers: hdr() });
if (res.ok) {
showToast('Модуль удалён');
showToast('Модуль отключён');
loadModules();
} else {
const err = await res.json().catch(() => ({}));
alert(err.error || 'Ошибка');
}
}
async function enableModule(id) {
const res = await fetch(`${API}/api/modules/${id}/restore`, { method: 'PUT', headers: hdr() });
if (res.ok) {
showToast('Модуль включён');
loadModules();
} else {
const err = await res.json().catch(() => ({}));
@@ -202,8 +216,10 @@ document.getElementById('moduleSearch').addEventListener('input', () => {
document.getElementById('moduleList').addEventListener('click', e => {
const ed = e.target.closest('[data-edit-module]');
if (ed) { openModuleModal(parseInt(ed.dataset.editModule, 10)); return; }
const dl = e.target.closest('[data-del-module]');
if (dl) deleteModule(parseInt(dl.dataset.delModule, 10));
const off = e.target.closest('[data-off-module]');
if (off) { disableModule(parseInt(off.dataset.offModule, 10)); return; }
const on = e.target.closest('[data-on-module]');
if (on) enableModule(parseInt(on.dataset.onModule, 10));
});
(async () => {