wip: student profile/report
This commit is contained in:
+96
-7
@@ -2,6 +2,7 @@ let modulesList = [];
|
||||
let mPage = 1;
|
||||
const M_PAGE_SIZE = 25;
|
||||
let mSearchTimer = null;
|
||||
let modulePhotoDeleteFlag = false;
|
||||
|
||||
function mPlural(n, one, few, many) {
|
||||
n = Math.abs(n) % 100; const n1 = n % 10;
|
||||
@@ -33,6 +34,7 @@ function renderModules(total) {
|
||||
} else {
|
||||
list.innerHTML = modulesList.map(m => `
|
||||
<div class="item module-item${m.is_active ? '' : ' module-off'}" data-id="${m.id}">
|
||||
${m.photo_path ? `<img class="module-thumb" src="${thumbSrc(m.photo_path)}" alt="" data-img="${esc(API + m.photo_path)}">` : ''}
|
||||
<div class="module-info">
|
||||
<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>
|
||||
@@ -50,6 +52,26 @@ function renderModules(total) {
|
||||
renderPager(document.getElementById('pager'), mPage, Math.max(1, Math.ceil(total / M_PAGE_SIZE)), p => { mPage = p; loadModules(); });
|
||||
}
|
||||
|
||||
function resetModulePhoto(m) {
|
||||
modulePhotoDeleteFlag = false;
|
||||
const fileInput = document.getElementById('modulePhotoFile');
|
||||
const preview = document.getElementById('modulePhotoPreview');
|
||||
const delBtn = document.getElementById('modulePhotoDeleteBtn');
|
||||
const note = document.getElementById('modulePhotoNote');
|
||||
fileInput.value = '';
|
||||
if (m && m.photo_path) {
|
||||
preview.src = thumbSrc(m.photo_path);
|
||||
preview.style.display = 'block';
|
||||
delBtn.style.display = 'inline-block';
|
||||
note.textContent = 'Загрузка новой картинки заменит текущую';
|
||||
} else {
|
||||
preview.removeAttribute('src');
|
||||
preview.style.display = 'none';
|
||||
delBtn.style.display = 'none';
|
||||
note.textContent = 'jpg, png, gif, webp, avif, heic и др., до 10 МБ · или вставьте из буфера (Ctrl+V)';
|
||||
}
|
||||
}
|
||||
|
||||
function openModuleModal(id = null) {
|
||||
const modal = document.getElementById('moduleModal');
|
||||
const form = document.getElementById('moduleForm');
|
||||
@@ -66,14 +88,62 @@ function openModuleModal(id = null) {
|
||||
form.name.value = m.name;
|
||||
document.getElementById('moduleLessons').value = String(m.lessons_count);
|
||||
document.getElementById('moduleId').value = m.id;
|
||||
resetModulePhoto(m);
|
||||
} else {
|
||||
title.textContent = 'Создать модуль';
|
||||
submitBtn.textContent = 'Создать';
|
||||
resetModulePhoto(null);
|
||||
}
|
||||
modal.classList.add('open');
|
||||
setTimeout(() => form.name.focus(), 100);
|
||||
}
|
||||
|
||||
function previewModulePhotoFile() {
|
||||
const file = document.getElementById('modulePhotoFile').files[0];
|
||||
const preview = document.getElementById('modulePhotoPreview');
|
||||
const delBtn = document.getElementById('modulePhotoDeleteBtn');
|
||||
const note = document.getElementById('modulePhotoNote');
|
||||
if (!file) return;
|
||||
modulePhotoDeleteFlag = false;
|
||||
preview.src = URL.createObjectURL(file);
|
||||
preview.style.display = 'block';
|
||||
delBtn.style.display = 'none';
|
||||
note.textContent = 'Новая картинка будет сохранена после нажатия «Сохранить»';
|
||||
}
|
||||
|
||||
function removeModulePhoto() {
|
||||
const preview = document.getElementById('modulePhotoPreview');
|
||||
const delBtn = document.getElementById('modulePhotoDeleteBtn');
|
||||
const note = document.getElementById('modulePhotoNote');
|
||||
if (!confirm('Удалить картинку модуля?')) return;
|
||||
modulePhotoDeleteFlag = true;
|
||||
document.getElementById('modulePhotoFile').value = '';
|
||||
preview.removeAttribute('src');
|
||||
preview.style.display = 'none';
|
||||
delBtn.style.display = 'none';
|
||||
note.textContent = 'Картинка будет удалена после нажатия «Сохранить»';
|
||||
}
|
||||
|
||||
function pasteModulePhotoFile(e) {
|
||||
const modal = document.getElementById('moduleModal');
|
||||
if (!modal.classList.contains('open')) return;
|
||||
const items = e.clipboardData && e.clipboardData.items;
|
||||
if (!items) return;
|
||||
for (const item of items) {
|
||||
if (item.type && item.type.startsWith('image/')) {
|
||||
const file = item.getAsFile();
|
||||
if (!file) continue;
|
||||
e.preventDefault();
|
||||
const dt = new DataTransfer();
|
||||
dt.items.add(file);
|
||||
const input = document.getElementById('modulePhotoFile');
|
||||
input.files = dt.files;
|
||||
previewModulePhotoFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeModuleModal() {
|
||||
document.getElementById('moduleModal').classList.remove('open');
|
||||
}
|
||||
@@ -118,14 +188,30 @@ document.getElementById('moduleForm').addEventListener('submit', async (e) => {
|
||||
headers: hdrJson(),
|
||||
body: JSON.stringify({ name, lessons_count })
|
||||
});
|
||||
if (res.ok) {
|
||||
showToast(id ? 'Модуль обновлён' : 'Модуль создан');
|
||||
closeModuleModal();
|
||||
loadModules();
|
||||
} else {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
alert(err.error || 'Ошибка');
|
||||
if (!res.ok) { const err = await res.json().catch(() => ({})); alert(err.error || 'Ошибка'); return; }
|
||||
const moduleRow = await res.json();
|
||||
const mid = moduleRow.id;
|
||||
const fileInput = document.getElementById('modulePhotoFile');
|
||||
if (modulePhotoDeleteFlag) {
|
||||
const delRes = await fetch(`${API}/api/modules/${mid}/photo`, { method: 'DELETE', headers: hdr() });
|
||||
if (!delRes.ok) {
|
||||
const err = await delRes.json().catch(() => ({}));
|
||||
alert(err.error || 'Ошибка удаления картинки');
|
||||
}
|
||||
}
|
||||
const file = fileInput.files[0];
|
||||
if (file) {
|
||||
const fd = new FormData();
|
||||
fd.append('photo', file);
|
||||
const upRes = await fetch(`${API}/api/modules/${mid}/photo`, { method: 'POST', headers: hdr(), body: fd });
|
||||
if (!upRes.ok) {
|
||||
const err = await upRes.json().catch(() => ({}));
|
||||
alert(err.error || 'Ошибка загрузки картинки');
|
||||
}
|
||||
}
|
||||
showToast(id ? 'Модуль обновлён' : 'Модуль создан');
|
||||
closeModuleModal();
|
||||
loadModules();
|
||||
});
|
||||
|
||||
function parseBatchModules(raw) {
|
||||
@@ -208,6 +294,9 @@ document.getElementById('batchModuleModal').addEventListener('click', e => { if
|
||||
document.getElementById('addModuleBtn').addEventListener('click', () => openModuleModal());
|
||||
document.getElementById('moduleCancelBtn').addEventListener('click', closeModuleModal);
|
||||
document.getElementById('moduleModal').addEventListener('click', e => { if (e.target === e.currentTarget) closeModuleModal(); });
|
||||
document.getElementById('modulePhotoFile').addEventListener('change', previewModulePhotoFile);
|
||||
document.getElementById('modulePhotoDeleteBtn').addEventListener('click', removeModulePhoto);
|
||||
document.addEventListener('paste', pasteModulePhotoFile);
|
||||
document.getElementById('moduleSearch').addEventListener('input', () => {
|
||||
clearTimeout(mSearchTimer);
|
||||
mSearchTimer = setTimeout(() => { mPage = 1; loadModules(); }, 300);
|
||||
|
||||
Reference in New Issue
Block a user