Suparak/integer-web-app-demo
0
1<!DOCTYPE html>
2<html lang="th">
3
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>แบบทดสอบวัดทักษะการคิดวิเคราะห์ (PISA Style)</title>
8 <link rel="preconnect" href="https://fonts.googleapis.com">
9 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10 <link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap"
11 rel="stylesheet">
12
13 <style>
14 :root {
15 --primary-color: #10B981;
16 /* เปลี่ยนเป็นสีเขียว Emerald */
17 --primary-hover: #059669;
18 /* สีเขียวเข้มเมื่อโฮเวอร์ */
19 --bg-color: #F0FDF4;
20 /* พื้นหลังสีเขียวมิ้นต์อ่อนๆ */
21 --card-bg: #FFFFFF;
22 /* การ์ดสีขาว */
23 --text-main: #1F2937;
24 --text-muted: #4B5563;
25 --border-color: #A7F3D0;
26 /* ขอบสีเขียวอ่อน */
27
28 /* Domain Colors - ปรับให้เข้ากับโทนธรรมชาติ */
29 --tag-math: #E0F2FE;
30 --tag-math-text: #0369A1;
31 --tag-read: #FEF3C7;
32 --tag-read-text: #B45309;
33 --tag-sci: #F3E8FF;
34 --tag-sci-text: #7E22CE;
35 --tag-digital: #FFEDD5;
36 --tag-digital-text: #C2410C;
37 }
38
39 * {
40 box-sizing: border-box;
41 margin: 0;
42 padding: 0;
43 }
44
45 body {
46 font-family: 'Sarabun', sans-serif;
47 background-color: var(--bg-color);
48 color: var(--text-main);
49 display: flex;
50 justify-content: center;
51 align-items: center;
52 min-height: 100vh;
53 padding: 20px;
54 }
55
56 .app-container {
57 background-color: var(--card-bg);
58 width: 100%;
59 max-width: 600px;
60 border-radius: 16px;
61 box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
62 overflow: hidden;
63 display: flex;
64 flex-direction: column;
65 }
66
67 /* Header & Progress */
68 .header {
69 padding: 24px 24px 0 24px;
70 }
71
72 .progress-info {
73 display: flex;
74 justify-content: space-between;
75 align-items: center;
76 margin-bottom: 12px;
77 font-weight: 600;
78 color: var(--text-muted);
79 font-size: 0.9rem;
80 }
81
82 .progress-bar-bg {
83 width: 100%;
84 height: 8px;
85 background-color: var(--border-color);
86 border-radius: 4px;
87 overflow: hidden;
88 }
89
90 .progress-bar-fill {
91 height: 100%;
92 background-color: var(--primary-color);
93 border-radius: 4px;
94 transition: width 0.4s ease;
95 }
96
97 /* Question Area */
98 .question-container {
99 padding: 24px;
100 flex-grow: 1;
101 }
102
103 .domain-tag {
104 display: inline-block;
105 padding: 6px 12px;
106 border-radius: 20px;
107 font-size: 0.85rem;
108 font-weight: 600;
109 margin-bottom: 16px;
110 }
111
112 .tag-math {
113 background-color: var(--tag-math);
114 color: var(--tag-math-text);
115 }
116
117 .tag-read {
118 background-color: var(--tag-read);
119 color: var(--tag-read-text);
120 }
121
122 .tag-sci {
123 background-color: var(--tag-sci);
124 color: var(--tag-sci-text);
125 }
126
127 .tag-digital {
128 background-color: var(--tag-digital);
129 color: var(--tag-digital-text);
130 }
131
132 .scenario-box {
133 background-color: #F8FAFC;
134 border-left: 4px solid var(--border-color);
135 padding: 16px;
136 border-radius: 4px 8px 8px 4px;
137 margin-bottom: 20px;
138 font-size: 1rem;
139 line-height: 1.6;
140 color: var(--text-main);
141 }
142
143 .question-text {
144 font-size: 1.15rem;
145 font-weight: 600;
146 margin-bottom: 20px;
147 line-height: 1.5;
148 }
149
150 /* Options */
151 .options-grid {
152 display: flex;
153 flex-direction: column;
154 gap: 12px;
155 }
156
157 .option-btn {
158 background-color: transparent;
159 border: 2px solid var(--border-color);
160 padding: 16px;
161 border-radius: 12px;
162 text-align: left;
163 font-family: inherit;
164 font-size: 1rem;
165 cursor: pointer;
166 transition: all 0.2s ease;
167 color: var(--text-main);
168 display: flex;
169 align-items: center;
170 }
171
172 .option-btn:hover {
173 border-color: #34D399;
174 /* กรอบเขียวเวลาเอาเมาส์ชี้ */
175 background-color: #ECFDF5;
176 }
177
178 .option-btn.selected {
179 border-color: var(--primary-color);
180 background-color: #D1FAE5;
181 /* พื้นหลังเขียวอ่อนตอนเลือก */
182 font-weight: 600;
183 box-shadow: 0 2px 4px rgba(16, 185, 129, 0.15);
184 }
185
186 .option-letter {
187 display: inline-flex;
188 justify-content: center;
189 align-items: center;
190 width: 28px;
191 height: 28px;
192 background-color: var(--border-color);
193 border-radius: 50%;
194 margin-right: 12px;
195 font-weight: bold;
196 font-size: 0.9rem;
197 transition: all 0.2s ease;
198 }
199
200 .option-btn.selected .option-letter {
201 background-color: var(--primary-color);
202 color: white;
203 }
204
205 /* Footer & Navigation */
206 .footer {
207 padding: 20px 24px;
208 border-top: 1px solid var(--border-color);
209 display: flex;
210 justify-content: space-between;
211 align-items: center;
212 }
213
214 .nav-btn {
215 padding: 10px 20px;
216 border: none;
217 border-radius: 8px;
218 font-family: inherit;
219 font-size: 1rem;
220 font-weight: 600;
221 cursor: pointer;
222 transition: background-color 0.2s ease;
223 }
224
225 .btn-prev {
226 background-color: white;
227 color: var(--text-muted);
228 border: 1px solid var(--border-color);
229 }
230
231 .btn-prev:hover:not(:disabled) {
232 background-color: var(--bg-color);
233 color: var(--text-main);
234 }
235
236 .btn-next {
237 background-color: var(--primary-color);
238 color: white;
239 }
240
241 .btn-next:hover:not(:disabled) {
242 background-color: var(--primary-hover);
243 }
244
245 .nav-btn:disabled {
246 opacity: 0.5;
247 cursor: not-allowed;
248 }
249
250 /* Result Screen */
251 .result-container {
252 padding: 40px 24px;
253 text-align: center;
254 display: none;
255 }
256
257 .score-circle {
258 width: 120px;
259 height: 120px;
260 border-radius: 50%;
261 background-color: #ECFDF5;
262 /* วงกลมพื้นหลังเขียวอ่อน */
263 border: 8px solid var(--primary-color);
264 display: flex;
265 justify-content: center;
266 align-items: center;
267 margin: 0 auto 24px auto;
268 font-size: 2.5rem;
269 font-weight: 700;
270 color: var(--primary-color);
271 }
272
273 .feedback-text {
274 font-size: 1.2rem;
275 margin-bottom: 12px;
276 font-weight: 600;
277 }
278
279 .feedback-desc {
280 color: var(--text-muted);
281 line-height: 1.6;
282 margin-bottom: 30px;
283 }
284
285 .btn-retry {
286 background-color: var(--primary-color);
287 color: white;
288 padding: 12px 24px;
289 border: none;
290 border-radius: 8px;
291 font-family: inherit;
292 font-size: 1rem;
293 font-weight: 600;
294 cursor: pointer;
295 width: 100%;
296 }
297
298 /* Responsive */
299 @media (max-width: 480px) {
300 .app-container {
301 min-height: 90vh;
302 }
303
304 .question-text {
305 font-size: 1.05rem;
306 }
307
308 .option-btn {
309 padding: 12px;
310 }
311
312 .footer {
313 padding: 16px;
314 }
315 }
316 </style>
317</head>
318
319<body>
320
321 <div class="app-container" id="app">
322 <!-- Header -->
323 <div class="header" id="header">
324 <div class="progress-info">
325 <span>📝 ข้อสอบ PISA Style 🌱</span>
326 <span id="progress-text">🎯 ข้อ 1 จาก 5</span>
327 </div>
328 <div class="progress-bar-bg">
329 <div class="progress-bar-fill" id="progress-fill" style="width: 20%;"></div>
330 </div>
331 </div>
332
333 <!-- Question View -->
334 <div class="question-container" id="question-view">
335 <div id="domain-tag" class="domain-tag tag-math">🔢 คณิตศาสตร์</div>
336 <div class="scenario-box" id="scenario-text">
337 โหลดเนื้อหาสถานการณ์...
338 </div>
339 <h2 class="question-text" id="question-text">
340 โหลดคำถาม...
341 </h2>
342 <div class="options-grid" id="options-container">
343 <!-- ตัวเลือกจะถูกสร้างด้วย JS -->
344 </div>
345 </div>
346
347 <!-- Footer Navigation -->
348 <div class="footer" id="footer">
349 <button class="nav-btn btn-prev" id="btn-prev" onclick="prevQuestion()" disabled>◀ 🍃 ข้อก่อนหน้า</button>
350 <button class="nav-btn btn-next" id="btn-next" onclick="nextQuestion()">ข้อถัดไป 🌿 ▶</button>
351 </div>
352
353 <!-- Result View -->
354 <div class="result-container" id="result-view">
355 <div class="score-circle">
356 <span id="final-score">0</span>/5
357 </div>
358 <h2 class="feedback-text" id="feedback-title">ประมวลผลคะแนน...</h2>
359 <p class="feedback-desc" id="feedback-desc">รายละเอียดคำแนะนำ</p>
360 <button class="btn-retry" onclick="resetQuiz()">ทำข้อสอบอีกครั้ง</button>
361 </div>
362 </div>
363
364 <!-- JavaScript สำหรับควบคุมระบบข้อสอบ -->
365 <script>
366 // โครงสร้างข้อมูลข้อสอบ
367 const quizData = [
368 {
369 id: 1,
370 domain: "🔢 คณิตศาสตร์",
371 tagClass: "tag-math",
372 scenario: "ป้ายโปรโมชันหน้าร้านกาแฟแห่งหนึ่งระบุว่า \"พิเศษ! ซื้อแก้วที่ 2 ลดทันที 50%\" โดยกาแฟแก้วแรกราคาปกติ 80 บาท และกาแฟแก้วที่สองราคาปกติ 80 บาทเท่ากัน",
373 question: "หากมานีต้องการซื้อกาแฟ 2 แก้วตามโปรโมชันนี้ เธอจะต้องจ่ายเงินทั้งหมดเท่าไร?",
374 options: ["80 บาท", "120 บาท", "140 บาท", "160 บาท"],
375 correctAnswer: 1 // ตัวเลือก B (index 1) : 80 + 40 = 120
376 },
377 {
378 id: 2,
379 domain: "📖 การอ่าน",
380 tagClass: "tag-read",
381 scenario: "ประกาศเทศบาลเมือง: \"เพื่อลดปัญหาขยะพลาสติกและรักษาสิ่งแวดล้อม ตั้งแต่วันที่ 1 เดือนหน้าเป็นต้นไป ห้างสรรพสินค้าและร้านสะดวกซื้อในเขตเทศบาล จะงดให้บริการถุงพลาสติกฟรี หากลูกค้าต้องการถุงพลาสติกจะต้องชำระเงินเพิ่มใบละ 2 บาท\"",
382 question: "จุดประสงค์หลักที่แท้จริงของประกาศฉบับนี้คือข้อใด?",
383 options: ["เพื่อเพิ่มรายได้พิเศษให้กับร้านสะดวกซื้อ", "เพื่อกระตุ้นให้ประชาชนพกถุงผ้ามาเอง", "เพื่อนำเงิน 2 บาทไปจัดตั้งกองทุนกำจัดขยะ", "เพื่อบังคับให้ทุกคนต้องซื้อถุงพลาสติก"],
384 correctAnswer: 1 // ตัวเลือก B (index 1) : เพื่อกระตุ้นพฤติกรรมลดการใช้พลาสติก
385 },
386 {
387 id: 3,
388 domain: "🔬 วิทยาศาสตร์",
389 tagClass: "tag-sci",
390 scenario: "นักเรียนทดลองต้มน้ำในบีกเกอร์และบันทึกอุณหภูมิทุกๆ 2 นาที ได้ผลดังนี้<br><br>นาทีที่ 0: 25°C<br>นาทีที่ 2: 45°C<br>นาทีที่ 4: 65°C<br>นาทีที่ 6: 85°C",
391 question: "หากทดลองต้มน้ำต่อไปเรื่อยๆ ที่ระดับความดันบรรยากาศปกติ (น้ำเดือดที่ 100°C) อุณหภูมิของน้ำในนาทีที่ 8 ควรจะเป็นเท่าไร?",
392 options: ["95°C", "100°C", "105°C", "125°C"],
393 correctAnswer: 1 // ตัวเลือก B (index 1) : 100°C เพราะน้ำไม่สามารถร้อนเกินจุดเดือดที่ความดันปกติได้ (PISA Trick)
394 },
395 {
396 id: 4,
397 domain: "📊 คณิตศาสตร์",
398 tagClass: "tag-math",
399 scenario: "พนักงานตรวจสอบคุณภาพสินค้าทำการสุ่มตรวจสินค้า 100 ชิ้นจากสายการผลิต พบว่ามีสินค้าไม่ได้มาตรฐานจำนวน 5 ชิ้น",
400 question: "หากในวันนั้นโรงงานผลิตสินค้าออกมาทั้งหมด 2,000 ชิ้น คาดว่าจะมีสินค้าที่ไม่ได้มาตรฐานประมาณกี่ชิ้น?",
401 options: ["50 ชิ้น", "100 ชิ้น", "150 ชิ้น", "200 ชิ้น"],
402 correctAnswer: 1 // ตัวเลือก B (index 1) : (5/100) * 2000 = 100
403 },
404 {
405 id: 5,
406 domain: "💻 Digital Literacy",
407 tagClass: "tag-digital",
408 scenario: "นักเรียนได้รับข้อความส่งต่อในแอปพลิเคชันแชทว่า \"ด่วน! ข่าวดีทางการแพทย์ ดื่มน้ำมะนาวอุ่นผสมโซดาทุกเช้า ช่วยฆ่าเซลล์มะเร็งได้ 100% แชร์ต่อให้คนที่คุณรักด่วน!\"",
409 question: "สิ่งแรกที่นักเรียนควรทำเพื่อประเมินความน่าเชื่อถือของข้อความนี้คือข้อใด?",
410 options: ["ส่งต่อให้เพื่อนในกลุ่มห้องเรียนเพื่อถามความคิดเห็น", "ทดลองดื่มด้วยตนเองเป็นเวลา 1 สัปดาห์เพื่อดูผลลัพธ์", "ค้นหาข้อมูลยืนยันจากเว็บไซต์ของกระทรวงสาธารณสุขหรือสถาบันมะเร็ง", "ตรวจสอบดูว่าคนที่ส่งข้อความมาให้นั้นเป็นคนรู้จักที่ไว้ใจได้หรือไม่"],
411 correctAnswer: 2 // ตัวเลือก C (index 2) : ตรวจสอบจากแหล่งข้อมูลที่น่าเชื่อถืออย่างเป็นทางการ
412 }
413 ];
414
415 // ตัวแปรสถานะที่สำคัญ (currentQuestion สามารถให้ Dify เข้าถึงได้)
416 let currentQuestion = 0;
417 let userAnswers = new Array(quizData.length).fill(null);
418
419 // อ้างอิง DOM Elements
420 const headerEl = document.getElementById('header');
421 const footerEl = document.getElementById('footer');
422 const questionView = document.getElementById('question-view');
423 const resultView = document.getElementById('result-view');
424 const progressText = document.getElementById('progress-text');
425 const progressFill = document.getElementById('progress-fill');
426
427 const domainTag = document.getElementById('domain-tag');
428 const scenarioText = document.getElementById('scenario-text');
429 const questionText = document.getElementById('question-text');
430 const optionsContainer = document.getElementById('options-container');
431
432 const btnPrev = document.getElementById('btn-prev');
433 const btnNext = document.getElementById('btn-next');
434
435 // ฟังก์ชันแสดงผลข้อสอบตาม Index ปัจจุบัน
436 function renderQuestion() {
437 const data = quizData[currentQuestion];
438
439 // อัปเดต Progress
440 progressText.innerText = `🎯 ข้อ ${currentQuestion + 1} จาก ${quizData.length}`;
441 progressFill.style.width = `${((currentQuestion + 1) / quizData.length) * 100}%`;
442
443 // อัปเดตเนื้อหา
444 domainTag.className = `domain-tag ${data.tagClass}`;
445 domainTag.innerText = data.domain;
446 scenarioText.innerHTML = data.scenario;
447 questionText.innerText = data.question;
448
449 // สร้างตัวเลือก
450 optionsContainer.innerHTML = '';
451 const labels = ['A', 'B', 'C', 'D'];
452
453 data.options.forEach((optText, index) => {
454 const btn = document.createElement('button');
455 btn.className = `option-btn ${userAnswers[currentQuestion] === index ? 'selected' : ''}`;
456 btn.onclick = () => selectOption(index);
457
458 btn.innerHTML = `
459 <span class="option-letter">${labels[index]}</span>
460 <span>${optText}</span>
461 `;
462 optionsContainer.appendChild(btn);
463 });
464
465 // จัดการปุ่มนำทาง
466 btnPrev.disabled = currentQuestion === 0;
467
468 if (currentQuestion === quizData.length - 1) {
469 btnNext.innerText = 'ส่งคำตอบ / ดูผลลัพธ์ ✨';
470 btnNext.style.backgroundColor = '#059669'; // Green for submit
471 } else {
472 btnNext.innerText = 'ข้อถัดไป 🌿 ▶';
473 btnNext.style.backgroundColor = 'var(--primary-color)';
474 }
475 }
476
477 // ฟังก์ชันเมื่อผู้ใช้คลิกเลือกคำตอบ
478 function selectOption(selectedIndex) {
479 userAnswers[currentQuestion] = selectedIndex;
480
481 // อัปเดต UI ทันที
482 const optionBtns = optionsContainer.children;
483 for (let i = 0; i < optionBtns.length; i++) {
484 if (i === selectedIndex) {
485 optionBtns[i].classList.add('selected');
486 } else {
487 optionBtns[i].classList.remove('selected');
488 }
489 }
490 }
491
492 // ไปข้อถัดไป หรือ แสดงผลลัพธ์
493 function nextQuestion() {
494 // ตรวจสอบว่าตอบคำถามหรือยัง
495 if (userAnswers[currentQuestion] === null) {
496 alert("กรุณาเลือกคำตอบอย่างน้อย 1 ตัวเลือกก่อนไปข้อถัดไป");
497 return;
498 }
499
500 if (currentQuestion < quizData.length - 1) {
501 currentQuestion++;
502 renderQuestion();
503 } else {
504 showResults();
505 }
506 }
507
508 // ย้อนกลับไปข้อก่อนหน้า
509 function prevQuestion() {
510 if (currentQuestion > 0) {
511 currentQuestion--;
512 renderQuestion();
513 }
514 }
515
516 // ฟังก์ชันคำนวณคะแนนและแสดงผลลัพธ์
517 function showResults() {
518 headerEl.style.display = 'none';
519 questionView.style.display = 'none';
520 footerEl.style.display = 'none';
521 resultView.style.display = 'block';
522
523 let score = 0;
524 userAnswers.forEach((ans, index) => {
525 if (ans === quizData[index].correctAnswer) {
526 score++;
527 }
528 });
529
530 document.getElementById('final-score').innerText = score;
531
532 const feedbackTitle = document.getElementById('feedback-title');
533 const feedbackDesc = document.getElementById('feedback-desc');
534
535 if (score === 5) {
536 feedbackTitle.innerText = 'ยอดเยี่ยมมาก! 🏆🌟';
537 feedbackDesc.innerText = 'ทักษะการคิดวิเคราะห์ของคุณอยู่ในระดับสูงมาก! 🧠✨ คุณสามารถวิเคราะห์ข้อมูลจากหลากหลายสถานการณ์ได้อย่างแม่นยำ 🎯';
538 } else if (score >= 3) {
539 feedbackTitle.innerText = 'ทำได้ดี! 👍✨';
540 feedbackDesc.innerText = 'คุณมีความเข้าใจพื้นฐานและทักษะการวิเคราะห์ที่ดี 🌿 แต่อาจต้องฝึกฝนการสังเกตจุดเล็กๆ น้อยๆ ในข้อมูลที่ซับซ้อนขึ้นอีกนิดนะ 🔍';
541 } else {
542 feedbackTitle.innerText = 'พยายามได้ดี! 💪🌱';
543 feedbackDesc.innerText = 'ข้อสอบ PISA จะเน้นการนำความรู้ไปใช้ในสถานการณ์จริง 💡 แนะนำให้ฝึกอ่านและวิเคราะห์ข้อมูลจากข่าวสารรอบตัวให้มากขึ้นนะ สู้ๆ! ✌️';
544 }
545 }
546
547 // เริ่มทำข้อสอบใหม่
548 function resetQuiz() {
549 currentQuestion = 0;
550 userAnswers = new Array(quizData.length).fill(null);
551
552 headerEl.style.display = 'block';
553 questionView.style.display = 'block';
554 footerEl.style.display = 'flex';
555 resultView.style.display = 'none';
556
557 renderQuestion();
558 }
559
560 // เริ่มต้นแอป
561 renderQuestion();
562 </script>
563
564 <script>
565 window.difyChatbotConfig = {
566 token: 'APlBRFntPxGRUIOd',
567 inputs: {
568 // You can define the inputs from the Start node here
569 // key is the variable name
570 // e.g.
571 // name: "NAME"
572 },
573 systemVariables: {
574 // user_id: 'YOU CAN DEFINE USER ID HERE',
575 // conversation_id: 'YOU CAN DEFINE CONVERSATION ID HERE, IT MUST BE A VALID UUID',
576 },
577 userVariables: {
578 // avatar_url: 'YOU CAN DEFINE USER AVATAR URL HERE',
579 // name: 'YOU CAN DEFINE USER NAME HERE',
580 },
581 }
582</script>
583<script
584 src="https://udify.app/embed.min.js"
585 id="APlBRFntPxGRUIOd"
586 defer>
587</script>
588<style>
589 #dify-chatbot-bubble-button {
590 background-color: #1C64F2 !important;
591 }
592 #dify-chatbot-bubble-window {
593 width: 24rem !important;
594 height: 40rem !important;
595 }
596</style>
597</body>
598
599</html>