Initial commit - TPU Survey System with Google Forms & Certificate Generator

This commit is contained in:
pongpon pitisuk
2026-09-12 11:56:34 +07:00
commit 72eb7cd0c8
355 changed files with 117012 additions and 0 deletions
+804
View File
@@ -0,0 +1,804 @@
/**
* TPU Survey - Teacher Dashboard Logic
*/
let teacherSurveys = [];
async function initTeacher() {
const loggedIn = await Auth.check();
if (!loggedIn || !Auth.isTeacher()) {
if (Auth.isAdmin()) {
window.location.href = 'admin.html';
return;
}
window.location.href = 'index.html';
return;
}
document.getElementById('userName').textContent = Auth.user.full_name;
document.getElementById('userRole').textContent = 'อาจารย์ / เจ้าของโครงการ';
document.getElementById('userAvatar').textContent = Auth.user.full_name.charAt(0);
showTeacherView('mySurveys');
}
function showTeacherView(view) {
document.querySelectorAll('.view-section').forEach(s => s.classList.add('hidden'));
document.getElementById(`view-${view}`)?.classList.remove('hidden');
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
document.querySelector(`.nav-item[data-view="${view}"]`)?.classList.add('active');
const titles = { mySurveys: 'แบบสอบถามของฉัน', profile: 'ข้อมูลส่วนตัว' };
document.getElementById('pageTitle').textContent = titles[view] || '';
if (view === 'mySurveys') loadTeacherSurveys();
else if (view === 'profile') loadProfile();
}
async function loadTeacherSurveys() {
try {
const data = await API.get('surveys.php?action=list');
teacherSurveys = data.surveys || [];
renderTeacherSurveys();
} catch (err) {
Toast.error(err.message);
}
}
function renderTeacherSurveys() {
const container = document.getElementById('teacherSurveysList') || document.getElementById('teacherSurveysBody');
if (!container) return;
if (teacherSurveys.length === 0) {
const emptyHtml = `
<div class="card-tapi p-8 text-center bg-white rounded-lg border border-slate-200">
<i class="fas fa-clipboard-list text-4xl text-blue-300 mb-3 block"></i>
<p class="text-gray-600 font-bold">ยังไม่มีแบบสอบถาม กดปุ่ม "สร้างแบบสอบถาม" เพื่อเริ่มต้น</p>
</div>
`;
if (container.tagName === 'TBODY') {
container.innerHTML = `<tr><td colspan="5" class="text-center text-muted" style="padding:40px">ยังไม่มีแบบสอบถาม กดปุ่ม "สร้างแบบสอบถาม" เพื่อเริ่มต้น</td></tr>`;
} else {
container.innerHTML = emptyHtml;
}
return;
}
const statusMap = {
active: { label: 'เปิดใช้งาน', bg: 'bg-emerald-100 text-emerald-800 border-emerald-300' },
draft: { label: 'ฉบับร่าง', bg: 'bg-amber-100 text-amber-800 border-amber-300' },
closed: { label: 'ปิดแล้ว', bg: 'bg-rose-100 text-rose-800 border-rose-300' }
};
container.innerHTML = teacherSurveys.map(s => {
const st = statusMap[s.status] || { label: s.status || 'เปิดใช้งาน', bg: 'bg-emerald-100 text-emerald-800 border-emerald-300' };
return `
<div class="card-tapi p-3.5 rounded-xl bg-white flex flex-col lg:flex-row justify-between items-start lg:items-center gap-3 transition-all hover:shadow-md border border-slate-200">
<div class="flex flex-col sm:flex-row sm:items-center gap-2.5 flex-1 min-w-0">
<span class="px-2 py-0.5 text-[11px] font-extrabold rounded border whitespace-nowrap self-start sm:self-auto ${st.bg}">${st.label}</span>
<div class="space-y-0.5 flex-1 min-w-0">
<h4 class="font-bold text-xs sm:text-sm text-blue-950 truncate" title="${escapeHtml(s.title)}">
${escapeHtml(s.title)}
</h4>
<div class="flex items-center gap-3 text-[11px] text-gray-500 font-semibold flex-wrap">
<span><i class="fas fa-comments text-blue-600 mr-1"></i> ${s.response_count || 0} คำตอบ</span>
<span><i class="fas fa-calendar-alt text-amber-600 mr-1"></i> ${formatDate(s.created_at)}</span>
</div>
</div>
</div>
<div class="flex flex-wrap lg:flex-nowrap gap-1 items-center flex-shrink-0 w-full lg:w-auto overflow-x-auto pb-1 lg:pb-0">
<button onclick="teacherEditSurvey(${s.id})" class="bg-amber-400 hover:bg-amber-500 text-gray-900 border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="แก้ไขแบบสอบถาม">
✏️ แก้ไข
</button>
<button onclick="teacherDuplicateSurvey(${s.id})" class="bg-blue-600 hover:bg-blue-700 text-white border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="คัดลอกแบบสอบถาม">
📑 คัดลอก
</button>
<button onclick="teacherGenerateLink(${s.id})" class="bg-blue-900 hover:bg-blue-950 text-white border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="ลิงก์และ QR Code">
🔗 ลิงก์
</button>
<button onclick="showImportResponsesModal(${s.id}, '${escapeHtml(s.title)}')" class="bg-emerald-600 hover:bg-emerald-700 text-white border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="นำเข้าคำตอบจาก CSV">
📥 นำเข้าคำตอบ
</button>
<button onclick="teacherViewStats(${s.id})" class="bg-indigo-600 hover:bg-indigo-700 text-white border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="ผลสถิติ">
📊 สถิติ
</button>
<button onclick="teacherDeleteSurvey(${s.id}, '${escapeHtml(s.title)}')" class="bg-rose-600 hover:bg-rose-700 text-white border border-slate-800 px-2 py-1 rounded text-xs font-bold flex items-center gap-1 shadow-sm transition-all whitespace-nowrap active:scale-95" title="ลบแบบสอบถาม">
🗑️ ลบ
</button>
</div>
</div>
`;
}).join('');
}
async function teacherDeleteSurvey(id, title) {
if (!confirm(`คุณต้องการลบแบบสอบถาม "${title}" ใช่หรือไม่?`)) return;
try {
await API.get(`surveys.php?action=delete&id=${id}`);
Toast.success('ลบแบบสอบถามเรียบร้อยแล้ว');
loadTeacherSurveys();
} catch (err) {
Toast.error(err.message);
}
}
function showTeacherCreateModal() {
const html = `
<form id="teacherSurveyForm">
<div class="form-group">
<label class="form-label">ชื่อแบบสอบถาม *</label>
<input type="text" class="form-input" name="title" required placeholder="เช่น แบบประเมินความพึงพอใจโครงการ...">
</div>
<div class="form-group">
<label class="form-label">รายละเอียด</label>
<textarea class="form-input" name="description" placeholder="คำอธิบายเพิ่มเติม"></textarea>
</div>
</form>
`;
const footer = `
<button class="btn btn-secondary" onclick="Modal.hide('tCreateModal');document.getElementById('tCreateModal').remove();">ยกเลิก</button>
<button class="btn btn-primary" onclick="submitTeacherCreate()">สร้างแบบสอบถาม (ใช้ 3 ส่วนมาตรฐาน)</button>
`;
Modal.create('สร้างแบบสอบถามใหม่', html, footer);
document.querySelector('.modal-overlay:last-child').id = 'tCreateModal';
}
async function submitTeacherCreate() {
const form = document.getElementById('teacherSurveyForm');
const fd = new FormData(form);
const data = Object.fromEntries(fd);
if (!data.title) { Toast.warning('กรุณากรอกชื่อ'); return; }
// Pre-fill 3 sections
data.sections = [
{
section_number: 1,
title: 'ส่วนที่ ข้อมูลทั่วไป',
category: 'general',
questions: [
{ question_text: 'เพศ', question_type: 'radio', options: ['ชาย', 'หญิง'], is_required: 1 },
{ question_text: 'ช่วงอายุ', question_type: 'dropdown', options: ['18-25 ปี', '26-35 ปี', '36-45 ปี', '46-55 ปี', '56 ปีขึ้นไป'], is_required: 1 }
]
},
{
section_number: 2,
title: 'ส่วนที่ การประเมินความพึงพอใจ',
category: 'rating',
questions: [
{ question_text: 'ด้านเนื้อหาของการจัดโครงการ', question_type: 'radio', is_required: 1 },
{ question_text: 'ด้านวิทยากร / ผู้บรรยาย', question_type: 'radio', is_required: 1 }
]
},
{
section_number: 3,
title: 'ส่วนที่ ข้อเสนอแนะอื่นๆ',
category: 'suggestions',
questions: [
{ question_text: 'ข้อเสนอแนะเพิ่มเติม', question_type: 'text', is_required: 0 }
]
}
];
try {
const result = await API.post('surveys.php?action=create', data);
Toast.success('สร้างแบบสอบถามสำเร็จ');
Modal.hide('tCreateModal');
document.getElementById('tCreateModal').remove();
loadTeacherSurveys();
setTimeout(() => teacherEditSurvey(result.id), 500);
} catch (err) {
Toast.error(err.message);
}
}
function teacherEditSurvey(id) {
window.open(`edit.html?id=${id}`, '_blank');
}
function buildTeacherQEditor(si, qi, q = {}) {
const qType = q.question_type || 'radio';
const qText = q.question_text || '';
const qOptions = Array.isArray(q.options) ? q.options.join('\n') : '';
const showOptions = ['radio', 'checkbox', 'dropdown'].includes(qType);
const qDescription = q.description || '';
return `
<div class="question-builder">
<div class="qb-header">
<span class="qb-handle">⋮⋮</span>
<span class="text-sm fw-600 q-number-label">คำถาม</span>
<div class="qb-actions">
<button type="button" class="btn btn-ghost btn-sm" onclick="addTeacherQ(${si}, this.closest('.question-builder'))" title="แทรกคำถามใต้ข้อนี้">➕ แทรก</button>
<button type="button" class="btn btn-ghost btn-sm text-danger" onclick="removeTeacherQ(this, ${si})" title="ลบคำถาม">🗑️ ลบ</button>
</div>
</div>
<div class="form-group">
<label class="form-label">คำถาม / ข้อความ</label>
<input type="text" class="form-input q-text-input" value="${escapeHtml(qText)}" placeholder="พิมพ์คำถาม...">
</div>
<div class="form-group">
<label class="form-label">รายละเอียดเพิ่มเติม (คำใบ้/คำอธิบาย - ไม่บังคับ)</label>
<input type="text" class="form-input q-desc-input" value="${escapeHtml(qDescription)}" placeholder="เช่น กรุณาระบุเป็นภาษาไทย">
</div>
<div class="form-group">
<label class="form-label">ประเภทคำถาม</label>
<select class="form-input q-type-select" onchange="toggleTeacherOptions(this)">
<option value="radio" ${qType==='radio'?'selected':''}>📊 ระดับ 1-5</option>
<option value="text" ${qType==='text'?'selected':''}>📝 ข้อความ</option>
<option value="dropdown" ${qType==='dropdown'?'selected':''}>📋 Dropdown</option>
<option value="checkbox" ${qType==='checkbox'?'selected':''}>☑️ Checkbox</option>
</select>
</div>
<div class="form-group q-options-group" style="${showOptions ? '' : 'display:none;'}">
<label class="form-label">ตัวเลือก (1 รายการต่อบรรทัด)</label>
<textarea class="form-input q-options-input" placeholder="ตัวเลือกที่ 1\nตัวเลือกที่ 2" rows="3">${escapeHtml(qOptions)}</textarea>
<small class="text-muted">เว้นว่างสำหรับระดับ 1-5 (Radio)</small>
</div>
</div>
`;
}
function addTeacherSection() {
const container = document.getElementById('additionalTeacherSections');
if (!container) return;
const si = document.querySelectorAll('.card[data-section]').length;
const html = `
<div class="card mb-24" data-section="${si}">
<div class="card-header d-flex justify-between items-center">
<input type="text" class="form-input" style="font-weight:700;font-size:16px;border:none;padding:4px 0;background:transparent;"
value="ส่วนที่ ข้อมูลเพิ่มเติม" data-field="section-title-${si}">
<button type="button" class="btn btn-ghost btn-sm text-danger" onclick="this.closest('.card').remove()">🗑️ ลบส่วนนี้</button>
</div>
<div class="card-body">
<div id="t-questions-${si}"></div>
<button type="button" class="btn btn-secondary btn-sm mt-16" onclick="addTeacherQ(${si})">+ เพิ่มคำถาม</button>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
// Add one initial question to the new section
addTeacherQ(si);
}
function toggleTeacherOptions(select) {
const group = select.closest('.question-builder').querySelector('.q-options-group');
if (['radio', 'checkbox', 'dropdown'].includes(select.value)) {
group.style.display = 'block';
} else {
group.style.display = 'none';
}
}
function addTeacherQ(si, afterEl = null) {
const container = document.getElementById(`t-questions-${si}`);
if (!container) return;
// Create temporary div to parse HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = buildTeacherQEditor(si, 0);
const newQ = tempDiv.firstElementChild;
if (afterEl) {
afterEl.after(newQ);
} else {
container.appendChild(newQ);
}
reindexTeacherQuestions(si);
}
function removeTeacherQ(btn, si) {
if (!confirm('ยืนยันลบคำถามนี้?')) return;
btn.closest('.question-builder').remove();
reindexTeacherQuestions(si);
}
function reindexTeacherQuestions(si) {
const container = document.getElementById(`t-questions-${si}`);
if (!container) return;
container.querySelectorAll('.question-builder').forEach((qEl, idx) => {
const label = qEl.querySelector('.q-number-label');
if (label) label.textContent = `คำถาม`;
});
}
async function submitTeacherEdit() {
const form = document.getElementById('tEditForm');
const id = form.querySelector('[name="id"]').value;
const title = form.querySelector('[name="title"]').value;
const description = form.querySelector('[name="description"]').value;
const sections = [];
form.querySelectorAll('.card[data-section]').forEach((sectionEl, si) => {
const sTitleInput = sectionEl.querySelector('input[data-field^="section-title"]');
const sCategorySelect = sectionEl.querySelector('select[data-field^="section-category"]');
const sTitle = sTitleInput ? sTitleInput.value : `ส่วนที่ ${si+1}`;
const sCategory = sCategorySelect ? sCategorySelect.value : 'rating';
const questions = [];
sectionEl.querySelectorAll('.question-builder').forEach((qEl, qi) => {
const qTextInput = qEl.querySelector('.q-text-input');
const qDescInput = qEl.querySelector('.q-desc-input');
const qTypeSelect = qEl.querySelector('.q-type-select');
const qOptionsArea = qEl.querySelector('.q-options-input');
const qText = qTextInput ? qTextInput.value : '';
const qDescription = qDescInput ? qDescInput.value : '';
const qType = qTypeSelect ? qTypeSelect.value : 'radio';
const qOptionsRaw = qOptionsArea ? qOptionsArea.value : '';
const options = qOptionsRaw.split('\n').map(o => o.trim()).filter(o => o !== '');
if (qText.trim()) {
questions.push({
question_text: qText.trim(),
description: qDescription.trim() || null,
question_type: qType,
order_num: qi,
is_required: 1,
options: options.length > 0 ? options : null
});
}
});
sections.push({ section_number: si + 1, title: sTitle, category: sCategory, questions });
});
try {
await API.post('surveys.php?action=update', { id: parseInt(id), title, description, sections });
Toast.success('บันทึกสำเร็จ');
Modal.hide('tEditModal');
document.getElementById('tEditModal').remove();
loadTeacherSurveys();
} catch (err) {
Toast.error(err.message);
}
}
async function teacherDuplicateSurvey(id) {
if (!confirm('ยืนยันสร้างสำเนาแบบสอบถามนี้?')) return;
try {
await API.post('surveys.php?action=duplicate', { id });
Toast.success('คัดลอกสำเร็จ');
loadTeacherSurveys();
} catch (err) {
Toast.error(err.message);
}
}
async function teacherGenerateLink(id) {
try {
const data = await API.post('surveys.php?action=generateLink', { id });
const fullUrl = data.full_url || `${window.location.origin}/survey/respond.php?link=${data.unique_link}`;
// Find survey title
const survey = teacherSurveys.find(s => s.id == id);
const surveyTitle = survey ? survey.title : 'แบบสอบถาม';
const html = `
<div class="qr-link-container">
<div class="qr-link-section">
<div class="qr-link-label">🔗 ลิงก์แบบสอบถาม</div>
<div class="link-display">
<span class="flex-1" id="surveyLinkText">${fullUrl}</span>
<button class="copy-btn" onclick="copyTeacherLink()">📋 คัดลอก</button>
</div>
</div>
<div class="qr-code-section">
<div class="qr-link-label">📱 QR Code สำหรับตอบแบบสอบถาม</div>
<div class="qr-code-wrapper" id="qrCodeWrapper">
<div id="qrCodeContainer"></div>
</div>
<p class="qr-survey-title" id="qrSurveyTitle">${escapeHtml(surveyTitle)}</p>
<div class="qr-actions">
<button class="btn btn-primary btn-sm" onclick="teacherDownloadQR()">📥 ดาวน์โหลด QR</button>
<button class="btn btn-secondary btn-sm" onclick="teacherPrintQR()">🖨️ พิมพ์ QR</button>
</div>
</div>
<p class="mt-16 text-sm text-muted" style="text-align:center;">⚡ แบบสอบถามถูกเปิดใช้งานอัตโนมัติเมื่อสร้างลิงก์</p>
</div>
`;
Modal.create('🔗 ลิงก์และ QR Code', html, '', 'modal-lg');
// Generate QR Code using QRCodeJS
setTimeout(() => {
const container = document.getElementById('qrCodeContainer');
if (container && typeof QRCode !== 'undefined') {
new QRCode(container, {
text: fullUrl,
width: 280,
height: 280,
colorDark: '#1e3a8a',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
}
}, 100);
} catch (err) {
Toast.error(err.message);
}
}
function copyTeacherLink() {
const text = document.getElementById('surveyLinkText')?.textContent;
if (text) {
navigator.clipboard.writeText(text).then(() => Toast.success('คัดลอกแล้ว')).catch(() => {
const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove();
Toast.success('คัดลอกแล้ว');
});
}
}
function getTeacherQRImage() {
const container = document.getElementById('qrCodeContainer');
if (!container) return null;
return container.querySelector('canvas') || container.querySelector('img');
}
function teacherDownloadQR() {
const qrEl = getTeacherQRImage();
const title = document.getElementById('qrSurveyTitle')?.textContent || 'survey';
if (!qrEl) return;
let qrDataUrl;
if (qrEl.tagName === 'CANVAS') {
qrDataUrl = qrEl.toDataURL('image/png');
} else {
qrDataUrl = qrEl.src;
}
const exportCanvas = document.createElement('canvas');
const qrSize = 280;
const padding = 40;
const titleHeight = 60;
const footerHeight = 40;
exportCanvas.width = qrSize + padding * 2;
exportCanvas.height = qrSize + padding * 2 + titleHeight + footerHeight;
const ctx = exportCanvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
ctx.fillStyle = '#1a1a2e';
ctx.font = 'bold 16px sans-serif';
ctx.textAlign = 'center';
const maxTitleWidth = exportCanvas.width - 40;
let displayTitle = title;
if (ctx.measureText(displayTitle).width > maxTitleWidth) {
while (ctx.measureText(displayTitle + '...').width > maxTitleWidth && displayTitle.length > 0) {
displayTitle = displayTitle.slice(0, -1);
}
displayTitle += '...';
}
ctx.fillText(displayTitle, exportCanvas.width / 2, padding + 20);
const img = new Image();
img.onload = function() {
ctx.drawImage(img, padding, padding + titleHeight, qrSize, qrSize);
ctx.fillStyle = '#6b7280';
ctx.font = '12px sans-serif';
ctx.fillText('สแกนเพื่อตอบแบบสอบถาม — TPU Survey', exportCanvas.width / 2, exportCanvas.height - 16);
const link = document.createElement('a');
const safeName = title.replace(/[^a-zA-Z0-9ก-๙]/g, '_').substring(0, 50);
link.download = `QR_${safeName}.png`;
link.href = exportCanvas.toDataURL('image/png');
link.click();
Toast.success('ดาวน์โหลด QR Code สำเร็จ');
};
img.src = qrDataUrl;
}
function teacherPrintQR() {
const qrEl = getTeacherQRImage();
const title = document.getElementById('qrSurveyTitle')?.textContent || 'แบบสอบถาม';
const linkText = document.getElementById('surveyLinkText')?.textContent || '';
if (!qrEl) return;
let qrDataUrl;
if (qrEl.tagName === 'CANVAS') {
qrDataUrl = qrEl.toDataURL('image/png');
} else {
qrDataUrl = qrEl.src;
}
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<!DOCTYPE html><html><head><title>พิมพ์ QR Code</title>
<style>
body { font-family: 'Sarabun', sans-serif; text-align: center; padding: 40px; }
h2 { color: #1a1a2e; margin-bottom: 8px; }
.subtitle { color: #6b7280; font-size: 14px; margin-bottom: 24px; }
img { border: 2px solid #e5e7eb; border-radius: 12px; padding: 8px; }
.link { color: #6b7280; font-size: 11px; word-break: break-all; margin-top: 16px; }
.footer { margin-top: 32px; color: #9ca3af; font-size: 12px; }
</style></head>
<body>
<h2>${title}</h2>
<p class="subtitle">สแกน QR Code เพื่อตอบแบบสอบถาม</p>
<img src="${qrDataUrl}" width="300" height="300">
<p class="link">${linkText}</p>
<p class="footer">TPU Survey — มหาวิทยาลัยตาปี</p>
<script>window.onload = function() { window.print(); }<\/script>
</body></html>
`);
printWindow.document.close();
}
function teacherViewStats(surveyId) {
window.open(`stats.html?survey_id=${surveyId}`, '_blank');
}
// ============================================================
// Profile Management
// ============================================================
async function loadProfile() {
try {
const data = await API.get('profile.php?action=get');
const u = data.user;
// Fill the form
document.getElementById('profileCode').value = u.personnel_code || u.username || '-';
document.getElementById('profileName').value = u.full_name || '';
document.getElementById('profileEmail').value = u.email || '';
document.getElementById('profileSubject').value = u.subject_area || '';
document.getElementById('profileBranch').value = u.branch_code || '';
document.getElementById('profilePassword').value = '';
document.getElementById('profilePasswordConfirm').value = '';
// Fill the sidebar card
document.getElementById('profileDisplayName').textContent = u.full_name || '-';
document.getElementById('profileDisplayRole').textContent = u.role === 'admin' ? 'ผู้ดูแลระบบ' : 'อาจารย์ / เจ้าของโครงการ';
document.getElementById('profileAvatar').textContent = (u.full_name || 'T').charAt(0).toUpperCase();
document.getElementById('profilePersonnelCode').textContent = u.personnel_code || u.username || '-';
const badge = document.getElementById('profileBadge');
if (badge) {
badge.textContent = u.role === 'admin' ? '👑 ผู้ดูแลระบบ' : '🎓 อาจารย์';
badge.className = `user-card-badge ${u.role}`;
}
} catch (err) {
Toast.error(err.message);
}
}
async function saveProfile() {
const fullName = document.getElementById('profileName').value.trim();
const email = document.getElementById('profileEmail').value.trim();
const subjectArea = document.getElementById('profileSubject').value.trim();
const branchCode = document.getElementById('profileBranch').value.trim();
const password = document.getElementById('profilePassword').value;
const passwordConfirm = document.getElementById('profilePasswordConfirm').value;
if (!fullName) {
Toast.warning('กรุณากรอกชื่อ-นามสกุล');
return;
}
if (password && password.length < 4) {
Toast.warning('รหัสผ่านต้องมีอย่างน้อย 4 ตัวอักษร');
return;
}
if (password && password !== passwordConfirm) {
Toast.warning('รหัสผ่านไม่ตรงกัน');
return;
}
const payload = {
full_name: fullName,
email: email,
subject_area: subjectArea,
branch_code: branchCode
};
if (password) {
payload.password = password;
}
try {
await API.post('profile.php?action=update', payload);
Toast.success('บันทึกข้อมูลส่วนตัวสำเร็จ');
// Update sidebar display
document.getElementById('userName').textContent = fullName;
document.getElementById('userAvatar').textContent = fullName.charAt(0).toUpperCase();
// Reload profile card
loadProfile();
} catch (err) {
Toast.error(err.message);
}
}
// ============================================================
// Google Forms Import Modals & Functions
// ============================================================
function showImportGoogleFormModal() {
const html = `
<div style="margin-bottom:16px;">
<div style="display:flex;gap:12px;margin-bottom:16px;border-bottom:1px solid #e5e7eb;padding-bottom:10px;">
<button id="tabUrlBtn" type="button" class="btn btn-sm btn-primary" onclick="switchImportTab('url')">🌐 วาง URL / Link</button>
<button id="tabJsonBtn" type="button" class="btn btn-sm btn-secondary" onclick="switchImportTab('json')">📄 อัปโหลดไฟล์ JSON</button>
</div>
<!-- Tab 1: URL -->
<div id="importTabUrl">
<div class="form-group">
<label class="form-label">Google Form URL / Link *</label>
<input type="url" class="form-input" id="googleFormUrl" placeholder="https://docs.google.com/forms/d/e/1FAIpQLS.../viewform">
<small class="text-muted mt-8" style="display:block;">วางลิงก์แบบสอบถาม Google Forms ที่เปิดสาธารณะ (Public)</small>
</div>
</div>
<!-- Tab 2: JSON -->
<div id="importTabJson" style="display:none;">
<div class="form-group">
<label class="form-label">เลือกไฟล์ JSON โครงสร้างฟอร์ม *</label>
<input type="file" class="form-input" id="googleFormJsonFile" accept=".json">
</div>
<div class="form-group">
<label class="form-label">หรือวางข้อมูล JSON ตรงนี้</label>
<textarea class="form-input" id="googleFormJsonText" rows="4" placeholder='{"title": "แบบสอบถาม...", "sections": [...]}'></textarea>
</div>
</div>
</div>
`;
const footer = `
<button class="btn btn-secondary" onclick="Modal.hide('importGFormModal');document.getElementById('importGFormModal')?.remove();">ยกเลิก</button>
<button class="btn btn-primary" id="btnSubmitImportGForm" onclick="submitImportGoogleForm()">📥 นำเข้าแบบสอบถาม</button>
`;
const modalId = Modal.create('📥 นำเข้าแบบสอบถามจาก Google Forms', html, footer);
const modalEl = document.getElementById(modalId);
if (modalEl) modalEl.id = 'importGFormModal';
}
function switchImportTab(tab) {
const urlTab = document.getElementById('importTabUrl');
const jsonTab = document.getElementById('importTabJson');
const urlBtn = document.getElementById('tabUrlBtn');
const jsonBtn = document.getElementById('tabJsonBtn');
if (tab === 'url') {
if (urlTab) urlTab.style.display = 'block';
if (jsonTab) jsonTab.style.display = 'none';
if (urlBtn) urlBtn.className = 'btn btn-sm btn-primary';
if (jsonBtn) jsonBtn.className = 'btn btn-sm btn-secondary';
} else {
if (urlTab) urlTab.style.display = 'none';
if (jsonTab) jsonTab.style.display = 'block';
if (urlBtn) urlBtn.className = 'btn btn-sm btn-secondary';
if (jsonBtn) jsonBtn.className = 'btn btn-sm btn-primary';
}
}
async function submitImportGoogleForm() {
const urlTab = document.getElementById('importTabUrl');
const btn = document.getElementById('btnSubmitImportGForm');
if (btn) {
btn.disabled = true;
btn.textContent = '⏳ กำลังนำเข้าข้อมูล...';
}
try {
if (urlTab && urlTab.style.display !== 'none') {
const url = document.getElementById('googleFormUrl')?.value?.trim();
if (!url) {
Toast.warning('กรุณาระบุ Google Form URL');
if (btn) { btn.disabled = false; btn.textContent = '📥 นำเข้าแบบสอบถาม'; }
return;
}
const res = await API.post('import_google_form.php?action=importFromUrl', { url });
Toast.success(res.message || 'นำเข้าแบบสอบถามสำเร็จ!');
Modal.hide('importGFormModal');
document.getElementById('importGFormModal')?.remove();
if (typeof loadSurveys === 'function') loadSurveys();
if (typeof loadTeacherSurveys === 'function') loadTeacherSurveys();
} else {
const fileInput = document.getElementById('googleFormJsonFile');
const jsonText = document.getElementById('googleFormJsonText')?.value?.trim();
let surveyData = null;
if (fileInput && fileInput.files.length > 0) {
const text = await fileInput.files[0].text();
surveyData = JSON.parse(text);
} else if (jsonText) {
surveyData = JSON.parse(jsonText);
}
if (!surveyData) {
Toast.warning('กรุณาเลือกไฟล์ JSON หรือระบุข้อความ JSON');
if (btn) { btn.disabled = false; btn.textContent = '📥 นำเข้าแบบสอบถาม'; }
return;
}
const res = await API.post('import_google_form.php?action=importFromJson', { survey_data: surveyData });
Toast.success(res.message || 'นำเข้าแบบสอบถามสำเร็จ!');
Modal.hide('importGFormModal');
document.getElementById('importGFormModal')?.remove();
if (typeof loadSurveys === 'function') loadSurveys();
if (typeof loadTeacherSurveys === 'function') loadTeacherSurveys();
}
} catch (err) {
Toast.error(err.message || 'เกิดข้อผิดพลาดในการนำเข้าแบบสอบถาม');
} finally {
if (btn) {
btn.disabled = false;
btn.textContent = '📥 นำเข้าแบบสอบถาม';
}
}
}
function showImportResponsesModal(surveyId, surveyTitle) {
const html = `
<form id="importRespForm">
<input type="hidden" name="survey_id" value="${surveyId}">
<div class="form-group">
<label class="form-label">แบบสอบถาม:</label>
<div style="font-weight:600;font-size:15px;color:var(--navy-600);">${escapeHtml(surveyTitle)}</div>
</div>
<div class="form-group">
<label class="form-label">เลือกไฟล์ CSV ข้อมูลคำตอบจาก Google Forms *</label>
<input type="file" class="form-input" name="csv_file" accept=".csv" required>
<small class="text-muted mt-8" style="display:block;">ไฟล์ .csv ที่ Export มาจาก Google Sheets / Google Forms (แถวแรกเป็นชื่อคำถาม)</small>
</div>
</form>
`;
const footer = `
<button class="btn btn-secondary" onclick="Modal.hide('importRespModal');document.getElementById('importRespModal')?.remove();">ยกเลิก</button>
<button class="btn btn-primary" id="btnSubmitImportResp" onclick="submitImportResponses(${surveyId})">📥 นำเข้าคำตอบลงระบบ</button>
`;
const modalId = Modal.create('📥 นำเข้าคำตอบจาก Google Forms (CSV)', html, footer);
const modalEl = document.getElementById(modalId);
if (modalEl) modalEl.id = 'importRespModal';
}
async function submitImportResponses(surveyId) {
const form = document.getElementById('importRespForm');
const fileInput = form?.querySelector('input[name="csv_file"]');
if (!fileInput || fileInput.files.length === 0) {
Toast.warning('กรุณาเลือกไฟล์ CSV คำตอบ');
return;
}
const btn = document.getElementById('btnSubmitImportResp');
if (btn) {
btn.disabled = true;
btn.textContent = '⏳ กำลังนำเข้าคำตอบ...';
}
const fd = new FormData(form);
try {
const res = await API.upload('import_responses.php', fd);
Toast.success(res.message || 'นำเข้าคำตอบสำเร็จ!');
Modal.hide('importRespModal');
document.getElementById('importRespModal')?.remove();
if (typeof loadSurveys === 'function') loadSurveys();
if (typeof loadTeacherSurveys === 'function') loadTeacherSurveys();
} catch (err) {
Toast.error(err.message || 'เกิดข้อผิดพลาดในการนำเข้าคำตอบ');
} finally {
if (btn) {
btn.disabled = false;
btn.textContent = '📥 นำเข้าคำตอบลงระบบ';
}
}
}
// Global Window bindings
window.showImportGoogleFormModal = showImportGoogleFormModal;
window.switchImportTab = switchImportTab;
window.submitImportGoogleForm = submitImportGoogleForm;
window.showImportResponsesModal = showImportResponsesModal;
window.submitImportResponses = submitImportResponses;
document.addEventListener('DOMContentLoaded', initTeacher);