const API = location.origin; const form = document.getElementById('form'); const preview = document.getElementById('preview'); const placeholderSrc = preview.src; const removePhotoBtn = document.getElementById('removePhotoBtn'); const groupInput = document.getElementById('groupInput'); const toast = document.getElementById('toast'); const camVideo = document.getElementById('camVideo'); const camCanvas = document.getElementById('camCanvas'); const camModal = document.getElementById('camModal'); let capturedBlob = null; let stream = null; const descInput = document.getElementById('descInput'); const filesInput = document.getElementById('filesInput'); const filesList = document.getElementById('filesList'); const filesClearBtn = document.getElementById('filesClearBtn'); let selectedFiles = []; function humanSize(b) { if (b < 1024) return b + ' Б'; if (b < 1048576) return (b / 1024).toFixed(1) + ' КБ'; return (b / 1048576).toFixed(1) + ' МБ'; } function renderFiles() { filesList.innerHTML = ''; selectedFiles.forEach((f, i) => { const row = document.createElement('div'); row.className = 'file-row'; const name = document.createElement('span'); name.className = 'f-name'; name.textContent = f.name; name.title = f.name; const size = document.createElement('span'); size.className = 'f-size'; size.textContent = humanSize(f.size); const del = document.createElement('button'); del.type = 'button'; del.textContent = '✕'; del.addEventListener('click', () => removeFile(i)); row.append(name, size, del); filesList.appendChild(row); }); filesClearBtn.classList.toggle('show', selectedFiles.length > 0); } function removeFile(i) { selectedFiles.splice(i, 1); renderFiles(); } function clearFiles() { selectedFiles = []; filesInput.value = ''; renderFiles(); } filesInput.addEventListener('change', () => { const keep = 10 - selectedFiles.length; if (keep <= 0) { alert('Можно прикрепить не более 10 файлов'); filesInput.value = ''; return; } selectedFiles = selectedFiles.concat([...filesInput.files].slice(0, keep)); if ([...filesInput.files].length > keep) alert('Можно прикрепить не более 10 файлов'); filesInput.value = ''; renderFiles(); }); const params = new URLSearchParams(location.search); let studentName = ''; function escName(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } if (params.get('nameInput')) { studentName = params.get('nameInput'); document.getElementById('nameInput').value = studentName; const field = document.getElementById('nameField'); field.innerHTML = `
${escName(studentName)}
`; } (async () => { const res = await fetch(`${API}/api/students`); const students = await res.json(); const me = studentName ? students.find(s => s.name === studentName) : null; if (me && me.group_id) { groupInput.innerHTML = ``; groupInput.value = String(me.group_id); } else { const groupsRes = await fetch(`${API}/api/groups/active`); const groups = await groupsRes.json(); groups.forEach(g => { const opt = document.createElement('option'); opt.value = g.id; opt.textContent = g.name; groupInput.appendChild(opt); }); if (!groups.length) { groupInput.innerHTML = ''; } } try { const fRes = await fetch(`${API}/api/public-settings`); const f = await fRes.json(); if (f.footer_left) document.getElementById('footerLeft').textContent = f.footer_left; if (f.footer_right) document.getElementById('footerRight').textContent = f.footer_right; } catch (e) { /* ignore */ } })(); function showPreview(src) { preview.src = src; preview.classList.add('has-photo'); removePhotoBtn.classList.add('show'); } function removePhoto() { preview.src = placeholderSrc; preview.classList.remove('has-photo'); removePhotoBtn.classList.remove('show'); capturedBlob = null; } async function openCam() { try { stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 } } }); camVideo.srcObject = stream; camModal.classList.add('open'); } catch (e) { alert('Не удалось открыть камеру: ' + e.message); } } function closeCam() { camModal.classList.remove('open'); if (stream) { stream.getTracks().forEach(t => t.stop()); stream = null; } } function capture() { camCanvas.width = camVideo.videoWidth; camCanvas.height = camVideo.videoHeight; const ctx = camCanvas.getContext('2d'); ctx.translate(camCanvas.width, 0); ctx.scale(-1, 1); ctx.drawImage(camVideo, 0, 0); camCanvas.toBlob(blob => { capturedBlob = blob; showPreview(URL.createObjectURL(blob)); closeCam(); }, 'image/jpeg', 0.85); } form.addEventListener('submit', async (e) => { e.preventDefault(); const btn = document.getElementById('submitBtn'); btn.disabled = true; btn.textContent = 'Отправка...'; const fd = new FormData(); if (capturedBlob) fd.append('photo', capturedBlob, 'photo.jpg'); 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('description', document.getElementById('descInput').value); fd.append('website', document.getElementById('hpWebsite').value); try { const res = await fetch(`${API}/api/entries`, { method: 'POST', body: fd }); if (res.status === 429) { const e = await res.json().catch(() => ({})); toast.textContent = e.error || 'Уже ответили: подождите'; toast.classList.add('show', 'error'); setTimeout(() => toast.classList.remove('show', 'error'), 3200); return; } if (!res.ok) throw new Error(await res.text()); form.reset(); removePhoto(); clearFiles(); toast.textContent = 'Готово!'; toast.classList.remove('error'); toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 2500); } catch (err) { alert('Ошибка: ' + err.message); } finally { btn.disabled = false; btn.textContent = 'Отправить'; } }); removePhotoBtn.addEventListener('click', removePhoto); 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);