function vietnameseToEnglishName(name) { if (!name) return ""; // 1. Normalize Unicode + remove accents let noAccent = name.normalize("NFD") .replace(/[\u0300-\u036f]/g, "") // remove diacritic marks .replace(/đ/g, "d").replace(/Đ/g, "D"); // 2. Trim + remove multiple spaces noAccent = noAccent.trim().replace(/\s+/g, " "); // 3. Capitalize each word let englishName = noAccent .split(" ") .map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) .join(" "); return englishName; } let currentStudentData = null; // Show error modal function showError(message) { const errorModal = document.createElement('div'); errorModal.className = 'modal fade'; errorModal.innerHTML = ` `; document.body.appendChild(errorModal); const modal = new bootstrap.Modal(errorModal); modal.show(); // Remove modal from DOM after it's hidden errorModal.addEventListener('hidden.bs.modal', () => { document.body.removeChild(errorModal); }); } // Show success modal function showSuccess(message) { const successModal = document.createElement('div'); successModal.className = 'modal fade'; successModal.innerHTML = ` `; document.body.appendChild(successModal); const modal = new bootstrap.Modal(successModal); modal.show(); // Remove modal from DOM after it's hidden successModal.addEventListener('hidden.bs.modal', () => { document.body.removeChild(successModal); }); } // Load student info by SBD document.getElementById('loadInfoBtn').addEventListener('click', async function() { const sbd = document.getElementById('cccd').value.trim(); if (!sbd) { showError('Vui lòng nhập số báo danh'); return; } const loadBtn = this; // Show loading state loadBtn.disabled = true; loadBtn.innerHTML = 'Đang tải...'; const spinner = loadBtn.querySelector('.loading-spinner'); if(spinner) spinner.style.display = 'inline-block'; try { const response = await fetch('/api/candidate/' + sbd); const data = await response.json(); if (response.ok && data.success) { currentStudentData = data.candidate; fillFormData(data.candidate); document.getElementById('studentInfo').style.display = 'block'; // Scroll to form document.getElementById('studentInfo').scrollIntoView({ behavior: 'smooth' }); } else { showError(data.message || 'Không tìm thấy thông tin sinh viên với số báo danh này'); document.getElementById('studentInfo').style.display = 'none'; } } catch (error) { showError('Có lỗi xảy ra khi tải thông tin'); console.error(error); } finally { // Reset button state loadBtn.disabled = false; if(spinner) spinner.style.display = 'none'; loadBtn.innerHTML = 'Tải thông tin'; } }); function fillFormData(candidate) { // Fill display fields (with _display suffix to avoid form submission) document.getElementById('hoten_display').value = candidate.hoten; document.getElementById('gioitinh_display').value = candidate.gioitinh; // Format date for display const date = new Date(candidate.ngaysinh); const formattedDate = date.toLocaleDateString('vi-VN'); document.getElementById('ngaysinh_display').value = formattedDate; document.getElementById('dantoc_display').value = candidate.dantoc; document.getElementById('cccd_display').value = candidate.cccd; document.getElementById('dienthoai_display').value = candidate.dienthoai; document.getElementById('email_display').value = candidate.email; // Convert enum values to display text // const phuongthucText = candidate.phuongthuc === 'hocba' ? 'Học bạ' : 'Điểm xét tuyển THPT'; const nganhText = candidate.nganh;// === 'luathoc' ? 'Luật học' : 'Luật kinh tế'; // document.getElementById('phuongthuc_display').value = phuongthucText; document.getElementById('nganh_display').value = nganhText; // Show current image status const imageStatusDiv = document.getElementById('imageStatus'); if (candidate.filename) { imageStatusDiv.innerHTML = ` Đã có ảnh Xem ảnh hiện tại `; } else { imageStatusDiv.innerHTML = 'Chưa có ảnh'; } var options = { text: makeVietQRContent({bankId: "970405", accountId: "4000337338339", amount: "9400000", description: "K49_" + vietnameseToEnglishName(candidate.hoten) + "_" + candidate.cccd}), width: 200, height: 200, colorDark : "#000000", colorLight : "#ffffff", dotScale: 1, logo:"https://hul.edu.vn/bundles/web/assets/images/logo.png", // Relative address, relative to `easy.easy.qrcode.min.js` logoWidth:36, // widht. default is automatic width logoHeight:36, // height. default is automatic height logoBackgroundColor:'#fffff', // Logo backgroud color, Invalid when `logBgTransparent` is true; default is '#ffffff' logoBackgroundTransparent:false, // Whether use transparent image, default is false }; document.getElementById("qr_vietqr").innerHTML = ""; document.getElementById("noidungchuyenkhoan").innerHTML = "K49_" + vietnameseToEnglishName(candidate.hoten) + "_" + candidate.cccd; var qrcode = new QRCode(document.getElementById("qr_vietqr"), options); } // Load completed registrations function loadStudents() { fetch('/api/students') .then(response => response.json()) .then(data => { const container = document.getElementById('studentsList'); if (data.length === 0) { container.innerHTML = '

Chưa có sinh viên nào đăng ký

'; return; } let html = '
'; html += ''; html += ''; html += ''; data.forEach(student => { html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
SBDHọ tênGiới tínhNgày sinhEmailNgànhẢnhNgày đăng ký
' + student.sbd + '' + student.hoten + '' + student.gioitinh + '' + new Date(student.ngaysinh).toLocaleDateString('vi-VN') + '' + student.email + '' + (student.nganh === 'luathoc' ? 'Luật học' : 'Luật kinh tế') + ''; if (student.filename) { html += ''; html += '
Xem ảnh'; } else { html += 'Chưa có'; } html += '
' + new Date(student.created_at).toLocaleDateString('vi-VN') + '
'; container.innerHTML = html; }) .catch(error => { console.error('Error loading students:', error); showError('Có lỗi xảy ra khi tải danh sách sinh viên'); }); } // Form submission document.getElementById('studentForm').addEventListener('submit', function(e) { e.preventDefault(); if (!currentStudentData) { showError('Vui lòng tải thông tin sinh viên trước'); return; } // File upload is optional - can update without changing image const fileInput = document.getElementById('filename'); if (!fileInput.files || !fileInput.files[0]) { showError('Vui lòng chọn ảnh để tải lên'); return; } // Submit form const formData = new FormData(this); fetch('/submit', { method: 'POST', body: formData }) .then(response => { if (response.redirected) { // Successful submission - redirect occurred showSuccess('Cập nhật ảnh thành công!'); // loadStudents(); // Refresh the student list // Reload the student info to show updated image status setTimeout(() => { document.getElementById('loadInfoBtn').click(); }, 1000); } else if (response.ok) { return response.json(); } else { return response.json().then(data => { throw new Error(data.message || 'Có lỗi xảy ra'); }); } }) .then(data => { if (data && !data.success) { showError(data.message || 'Có lỗi xảy ra khi cập nhật ảnh'); } }) .catch(error => { console.error('Error:', error); showError(error.message || 'Có lỗi xảy ra khi cập nhật ảnh. Vui lòng thử lại.'); }); }); // Allow loading info when pressing Enter in SBD field document.getElementById('cccd').addEventListener('keypress', function(e) { if (e.key === 'Enter') { e.preventDefault(); document.getElementById('loadInfoBtn').click(); } }); // Load students on page load // loadStudents(); // Check URL parameters for success message const urlParams = new URLSearchParams(window.location.search); if (urlParams.get('success') === '1') { showSuccess('Cập nhật ảnh thành công!'); // Clean URL window.history.replaceState({}, document.title, window.location.pathname); } ///////////////////////////////////// function crc16(str) { let crc = 0xffff; for (let i = 0; i < str.length; i++) { crc ^= str.charCodeAt(i) << 8; for (let j = 0; j < 8; j++) { if (crc & 0x8000) { crc = (crc << 1) ^ 0x1021; } else { crc = crc << 1; } } } crc = crc & 0xffff; return crc.toString(16).padStart(4, "0").toUpperCase(); } const pad2zero = (num) => num.toString().padStart(2, "0"); function makeVietQRContent(input) { let v = "000201010212" + "38" + pad2zero(input.bankId.length + input.accountId.length + 38) + "0010A000000727" + "01" + pad2zero(input.bankId.length + input.accountId.length + 8) + "00" + pad2zero(input.bankId.length) + input.bankId + "01" + pad2zero(input.accountId.length) + input.accountId; v += "0208QRIBFTTA"; v += "5303704"; if (input.amount) { const amountStr = input.amount.toString(); v += "54" + pad2zero(amountStr.length) + amountStr; } v += "5802VN"; if (input.description) { v += "62" + pad2zero(input.description.length + 4) + "08" + pad2zero(input.description.length) + input.description; } v += "6304"; v += crc16(v); return v; }