feat(modules): batch module import in admin, module usage in journal
This commit is contained in:
+62
-21
@@ -5,7 +5,10 @@ const placeholderSrc = preview.src;
|
||||
const removePhotoBtn = document.getElementById('removePhotoBtn');
|
||||
const groupInput = document.getElementById('groupInput');
|
||||
const moduleInput = document.getElementById('moduleInput');
|
||||
const moduleInputList = document.getElementById('moduleInputList');
|
||||
const moduleField = document.getElementById('moduleField');
|
||||
const moduleNames = new Map();
|
||||
let moduleSearchTimer = null;
|
||||
const toast = document.getElementById('toast');
|
||||
const camVideo = document.getElementById('camVideo');
|
||||
const camCanvas = document.getElementById('camCanvas');
|
||||
@@ -134,22 +137,46 @@ if (params.get('nameInput')) {
|
||||
field.innerHTML = `<label>Фамилия и имя</label><div class="fixed-name" id="fixedName">${escName(studentName)}</div>`;
|
||||
}
|
||||
|
||||
function renderModuleOptions(list) {
|
||||
list.forEach(m => moduleNames.set(m.name, m.id));
|
||||
moduleInputList.innerHTML = '';
|
||||
list.forEach(m => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m.name;
|
||||
moduleInputList.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchModules(q, limit) {
|
||||
const p = new URLSearchParams();
|
||||
if (q) p.set('search', q);
|
||||
p.set('limit', String(limit));
|
||||
try {
|
||||
const res = await fetch(`${API}/api/modules?${p}`);
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
return data.modules || [];
|
||||
} catch (e) { return []; }
|
||||
}
|
||||
|
||||
async function resolveModuleId(name) {
|
||||
const n = (name || '').trim();
|
||||
if (!n) return null;
|
||||
if (moduleNames.has(n)) return moduleNames.get(n);
|
||||
const list = await fetchModules(n, 5);
|
||||
renderModuleOptions(list);
|
||||
const lower = n.toLowerCase();
|
||||
const exact = list.find(m => m.name.toLowerCase() === lower);
|
||||
return exact ? exact.id : null;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const mRes = await fetch(`${API}/api/modules`);
|
||||
if (mRes.ok) {
|
||||
const mData = await mRes.json();
|
||||
const list = mData.modules || [];
|
||||
if (list.length) {
|
||||
list.forEach(m => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m.id;
|
||||
opt.textContent = m.lessons_count > 0 ? `${m.name} · ${m.lessons_count} зан.` : m.name;
|
||||
moduleInput.appendChild(opt);
|
||||
});
|
||||
moduleInput.required = true;
|
||||
moduleField.style.display = '';
|
||||
}
|
||||
const list = await fetchModules('', 50);
|
||||
if (list.length) {
|
||||
renderModuleOptions(list);
|
||||
moduleInput.required = true;
|
||||
moduleField.style.display = '';
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
const res = await fetch(`${API}/api/students`);
|
||||
@@ -238,11 +265,15 @@ form.addEventListener('submit', async (e) => {
|
||||
setTimeout(() => toast.classList.remove('show', 'error'), 3200);
|
||||
return;
|
||||
}
|
||||
if (moduleField.style.display !== 'none' && !moduleInput.value) {
|
||||
toast.textContent = 'Выберите тему модуля';
|
||||
toast.classList.add('show', 'error');
|
||||
setTimeout(() => toast.classList.remove('show', 'error'), 3200);
|
||||
return;
|
||||
let moduleId = '';
|
||||
if (moduleField.style.display !== 'none') {
|
||||
moduleId = await resolveModuleId(moduleInput.value);
|
||||
if (!moduleId) {
|
||||
toast.textContent = 'Выберите тему модуля из списка подсказок';
|
||||
toast.classList.add('show', 'error');
|
||||
setTimeout(() => toast.classList.remove('show', 'error'), 3200);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const btn = document.getElementById('submitBtn');
|
||||
btn.disabled = true;
|
||||
@@ -253,7 +284,7 @@ form.addEventListener('submit', async (e) => {
|
||||
selectedFiles.forEach(f => fd.append('files', f, f.name));
|
||||
fd.append('student_name', studentName || document.getElementById('nameInput').value);
|
||||
fd.append('group_id', groupInput.value);
|
||||
fd.append('module_id', moduleInput.value);
|
||||
fd.append('module_id', moduleId || '');
|
||||
fd.append('description', document.getElementById('descInput').value);
|
||||
fd.append('website', document.getElementById('hpWebsite').value);
|
||||
|
||||
@@ -285,4 +316,14 @@ document.getElementById('openCamBtn').addEventListener('click', openCam);
|
||||
filesClearBtn.addEventListener('click', clearFiles);
|
||||
document.getElementById('filesBtn').addEventListener('click', () => document.getElementById('filesInput').click());
|
||||
document.getElementById('cancelCamBtn').addEventListener('click', closeCam);
|
||||
document.getElementById('captureBtn').addEventListener('click', capture);
|
||||
document.getElementById('captureBtn').addEventListener('click', capture);
|
||||
moduleInput.addEventListener('input', () => {
|
||||
clearTimeout(moduleSearchTimer);
|
||||
const q = moduleInput.value.trim();
|
||||
moduleSearchTimer = setTimeout(async () => {
|
||||
renderModuleOptions(await fetchModules(q, 20));
|
||||
}, 250);
|
||||
});
|
||||
moduleInput.addEventListener('focus', async () => {
|
||||
if (!moduleInput.value.trim()) renderModuleOptions(await fetchModules('', 50));
|
||||
});
|
||||
Reference in New Issue
Block a user