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 += 'SBD | Họ tên | Giới tính | Ngày sinh | ';
html += 'Email | Ngành | Ảnh | Ngày đăng ký |
';
data.forEach(student => {
html += '';
html += '' + student.sbd + ' | ';
html += '' + student.hoten + ' | ';
html += '' + student.gioitinh + ' | ';
html += '' + new Date(student.ngaysinh).toLocaleDateString('vi-VN') + ' | ';
html += '' + student.email + ' | ';
html += '' + (student.nganh === 'luathoc' ? 'Luật học' : 'Luật kinh tế') + ' | ';
html += '';
if (student.filename) {
html += 'Có';
html += ' Xem ảnh';
} else {
html += 'Chưa có';
}
html += ' | ';
html += '' + new Date(student.created_at).toLocaleDateString('vi-VN') + ' | ';
html += '
';
});
html += '
';
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;
}