Initial commit - TPU Survey System with Google Forms & Certificate Generator
This commit is contained in:
+294
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* TPU Survey - Public Survey Response Logic
|
||||
* รองรับ Section -> Heading -> Question
|
||||
*/
|
||||
|
||||
let currentSurvey = null;
|
||||
|
||||
async function initSurvey() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const link = params.get('link');
|
||||
|
||||
if (!link) {
|
||||
showError('ไม่พบลิงก์แบบสอบถาม');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await fetch(`/survey/api/surveys.php?action=getByLink&link=${encodeURIComponent(link)}`).then(r => r.json());
|
||||
|
||||
if (data.error) {
|
||||
showError(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
currentSurvey = data.survey;
|
||||
renderSurvey();
|
||||
} catch (err) {
|
||||
showError('ไม่สามารถโหลดแบบสอบถามได้');
|
||||
}
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
document.getElementById('surveyContent').innerHTML = `
|
||||
<div class="empty-state" style="padding:80px 20px;">
|
||||
<div class="empty-icon">😔</div>
|
||||
<h3>${msg}</h3>
|
||||
<p class="text-muted mt-8">ลิงก์อาจไม่ถูกต้องหรือแบบสอบถามถูกปิดแล้ว</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderSurvey() {
|
||||
const container = document.getElementById('surveyContent');
|
||||
|
||||
let html = `
|
||||
<div class="survey-header" style="text-align:center;">
|
||||
<img src="pic/logo.png" alt="TPU Logo" style="display: block; margin-left: auto; margin-right: auto; margin-bottom: 16px; height: 80px; width: auto;">
|
||||
<h1>${escapeHtml(currentSurvey.title)}</h1>
|
||||
${currentSurvey.description ? `<p>${escapeHtml(currentSurvey.description)}</p>` : ''}
|
||||
</div>
|
||||
<form id="surveyResponseForm" onsubmit="submitSurveyResponse(event)">
|
||||
${currentSurvey.has_certificate == 1 ? `
|
||||
<!-- ส่วนข้อมูลสำหรับรับเกียรติบัตร -->
|
||||
<div class="survey-section" style="border-top: 4px solid var(--primary-color, #2563eb);">
|
||||
<div class="survey-section-header">
|
||||
<h2 style="font-size:1.2rem; font-weight:700; color:#1e3a8a;">🎓 ข้อมูลสำหรับรับเกียรติบัตร (ข้อมูลจำเป็น)</h2>
|
||||
<p style="color: #4b5563; font-size:0.9rem; margin-top:4px;">กรุณาระบุชื่อ-นามสกุลจริง และอีเมลมหาวิทยาลัยตาปี หรือ Gmail เพื่อระบบจะทำการส่งไฟล์เกียรติบัตรผ่านทางอีเมลโดยอัตโนมัติ</p>
|
||||
</div>
|
||||
|
||||
<div class="survey-question" style="margin-top:16px;">
|
||||
<div class="q-label" style="font-weight:700;">
|
||||
<span>ชื่อ-นามสกุลจริง (ภาษาไทย เช่น นายสมชาย ใจดี)</span>
|
||||
<span class="required">*</span>
|
||||
</div>
|
||||
<input type="text" class="form-input" id="respondentName" placeholder="กรอกชื่อ-นามสกุลจริง เช่น นายสมชาย ใจดี" required style="width: 100%; max-width: 500px; margin-top: 8px;">
|
||||
</div>
|
||||
|
||||
<div class="survey-question" style="margin-top:16px;">
|
||||
<div class="q-label" style="font-weight:700;">
|
||||
<span>อีเมลสำหรับรับเกียรติบัตร (มหาวิทยาลัยตาปี หรือ Gmail)</span>
|
||||
<span class="required">*</span>
|
||||
</div>
|
||||
<input type="email" class="form-input" id="respondentEmail" placeholder="เช่น name@gmail.com หรือ 651234567@student.tapee.ac.th" required
|
||||
pattern="[a-zA-Z0-9._%+-]+@((student\\.)?tapee\\.ac\\.th|gmail\\.com|gmil\\.com)"
|
||||
title="กรุณาใช้อีเมลมหาวิทยาลัยตาปี หรือ Gmail เท่านั้น"
|
||||
style="width: 100%; max-width: 500px; margin-top: 8px;">
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
|
||||
|
||||
(currentSurvey.sections || []).forEach(section => {
|
||||
const cleanSectionTitle = section.title.replace(/^ส่วนที่\s*[\d๑-๙]+\s*/, '').replace(/^\s*[\d๑-๙]+[\.\)\s\-:\u00A0]*\s*/, '');
|
||||
html += `
|
||||
<div class="survey-section">
|
||||
<div class="survey-section-header">
|
||||
<h2>${escapeHtml(cleanSectionTitle)}</h2>
|
||||
${section.description ? `<p>${escapeHtml(section.description)}</p>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Render headings and their questions
|
||||
(section.headings || []).forEach(heading => {
|
||||
const cleanHeadingTitle = heading.title.replace(/^\s*[\d๑-๙]+[\.\)\s\-:\u00A0]*\s*/, '');
|
||||
html += `
|
||||
<div class="survey-heading" style="margin:24px 0 16px; padding:16px 20px; background:linear-gradient(135deg, rgba(37,99,235,0.06), rgba(37,99,235,0.10)); border-left:4px solid var(--primary-color, #2563eb); border-radius:0 12px 12px 0;">
|
||||
<h3 style="margin:0; font-size:1.1rem; font-weight:700; color:#1e3a8a;">📑 ${escapeHtml(cleanHeadingTitle)}</h3>
|
||||
${heading.description ? `<p style="margin:4px 0 0; font-size:0.9rem; color:#475569;">${escapeHtml(heading.description)}</p>` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
(heading.questions || []).forEach(q => {
|
||||
html += renderQuestion(q);
|
||||
});
|
||||
});
|
||||
|
||||
// Render loose questions (not in any heading)
|
||||
(section.questions || []).forEach(q => {
|
||||
html += renderQuestion(q);
|
||||
});
|
||||
html += `</div>`;
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="text-center mt-32">
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary btn-lg" style="min-width:200px;">
|
||||
📤 ส่งแบบสอบถาม
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderQuestion(q) {
|
||||
const cleanText = q.question_text.replace(/^\s*[\d๑-๙]+[\.\)\s\-:\u00A0]*\s*/, '');
|
||||
let html = `
|
||||
<div class="survey-question">
|
||||
<div class="q-label">
|
||||
<span>${escapeHtml(cleanText)}</span>
|
||||
${q.is_required ? '<span class="required">*</span>' : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (q.description) {
|
||||
html += `<p style="font-size:0.85rem; color:#000000; margin:-4px 0 10px 0;">${escapeHtml(q.description)}</p>`;
|
||||
}
|
||||
|
||||
if (q.question_type === 'radio') {
|
||||
if (q.options && q.options.length > 0) {
|
||||
html += `<div class="form-radio-group" style="flex-direction:column; align-items:flex-start;">`;
|
||||
q.options.forEach(opt => {
|
||||
html += `
|
||||
<label class="radio-option" style="width:100%; border:none; padding:8px 0;">
|
||||
<input type="radio" name="q_${q.id}" value="${escapeHtml(opt)}" ${q.is_required ? 'required' : ''}>
|
||||
<span>${escapeHtml(opt)}</span>
|
||||
</label>
|
||||
`;
|
||||
});
|
||||
html += `</div>`;
|
||||
} else {
|
||||
html += `<div class="rating-scale">`;
|
||||
const labels = ['น้อยที่สุด', 'น้อย', 'ปานกลาง', 'มาก', 'มากที่สุด'];
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
html += `
|
||||
<label>
|
||||
<input type="radio" name="q_${q.id}" value="${i}" ${q.is_required ? 'required' : ''}>
|
||||
<span class="rating-number">${i}</span>
|
||||
<span class="rating-label">${labels[i-1]}</span>
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
html += `</div>`;
|
||||
}
|
||||
} else if (q.question_type === 'text') {
|
||||
html += `<textarea class="form-input" name="q_${q.id}" placeholder="กรอกความคิดเห็น..." rows="3" ${q.is_required ? 'required' : ''}></textarea>`;
|
||||
} else if (q.question_type === 'dropdown') {
|
||||
const options = q.options || [];
|
||||
html += `<select class="form-input" name="q_${q.id}" ${q.is_required ? 'required' : ''}>`;
|
||||
html += `<option value="">-- กรุณาเลือก --</option>`;
|
||||
options.forEach(opt => {
|
||||
const val = typeof opt === 'object' ? opt.value : opt;
|
||||
const label = typeof opt === 'object' ? opt.label : opt;
|
||||
html += `<option value="${escapeHtml(val)}">${escapeHtml(label)}</option>`;
|
||||
});
|
||||
html += `</select>`;
|
||||
} else if (q.question_type === 'checkbox') {
|
||||
const options = q.options || [];
|
||||
html += `<div class="form-checkbox-group">`;
|
||||
options.forEach((opt, oi) => {
|
||||
const val = typeof opt === 'object' ? opt.value : opt;
|
||||
const label = typeof opt === 'object' ? opt.label : opt;
|
||||
html += `
|
||||
<label class="checkbox-option">
|
||||
<input type="checkbox" name="q_${q.id}_${oi}" value="${escapeHtml(val)}">
|
||||
<span>${escapeHtml(label)}</span>
|
||||
</label>
|
||||
`;
|
||||
});
|
||||
html += `</div>`;
|
||||
}
|
||||
|
||||
html += `</div>`;
|
||||
return html;
|
||||
}
|
||||
|
||||
// Collect all questions flat for submission
|
||||
function getAllQuestions() {
|
||||
const questions = [];
|
||||
(currentSurvey.sections || []).forEach(section => {
|
||||
(section.headings || []).forEach(heading => {
|
||||
(heading.questions || []).forEach(q => questions.push(q));
|
||||
});
|
||||
(section.questions || []).forEach(q => questions.push(q));
|
||||
});
|
||||
return questions;
|
||||
}
|
||||
|
||||
async function submitSurveyResponse(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const btn = document.getElementById('submitBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'กำลังส่ง...';
|
||||
|
||||
const form = document.getElementById('surveyResponseForm');
|
||||
const answers = [];
|
||||
const allQuestions = getAllQuestions();
|
||||
|
||||
allQuestions.forEach(q => {
|
||||
if (q.question_type === 'checkbox') {
|
||||
const values = [];
|
||||
form.querySelectorAll(`[name^="q_${q.id}_"]`).forEach(cb => {
|
||||
if (cb.checked) values.push(cb.value);
|
||||
});
|
||||
answers.push({ question_id: q.id, value: values.join(', ') });
|
||||
} else {
|
||||
const el = form.querySelector(`[name="q_${q.id}"]`);
|
||||
if (el) {
|
||||
if (el.type === 'radio') {
|
||||
const checked = form.querySelector(`[name="q_${q.id}"]:checked`);
|
||||
answers.push({ question_id: q.id, value: checked ? checked.value : '' });
|
||||
} else {
|
||||
answers.push({ question_id: q.id, value: el.value });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Get respondent information
|
||||
const nameEl = document.getElementById('respondentName');
|
||||
const emailEl = document.getElementById('respondentEmail');
|
||||
const respondentName = nameEl ? nameEl.value.trim() : '';
|
||||
const respondentEmail = emailEl ? emailEl.value.trim() : '';
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
try {
|
||||
const res = await fetch('/survey/api/responses.php?action=submit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
survey_link: params.get('link'),
|
||||
answers: answers,
|
||||
respondent_info: {
|
||||
name: respondentName,
|
||||
email: respondentEmail
|
||||
}
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
document.getElementById('surveyContent').innerHTML = `
|
||||
<div class="success-page" style="min-height:auto;padding:60px 20px;">
|
||||
<div style="text-align:center;">
|
||||
<img src="pic/logo.png" alt="TPU Logo" style="display: block; margin-left: auto; margin-right: auto; margin-bottom: 24px; height: 80px; width: auto;">
|
||||
<div class="success-icon">✓</div>
|
||||
<h2 style="font-size:24px;margin-bottom:12px;">ส่งแบบสอบถามสำเร็จ!</h2>
|
||||
<p class="text-muted">ขอบคุณที่สละเวลาตอบแบบสอบถาม</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
throw new Error(data.error || 'เกิดข้อผิดพลาด');
|
||||
}
|
||||
} catch (err) {
|
||||
alert('เกิดข้อผิดพลาด: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = '📤 ส่งแบบสอบถาม';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initSurvey);
|
||||
Reference in New Issue
Block a user