Wisetsut/integer-web-app
0
1<!DOCTYPE html>2<html lang="th">3 4<head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"66db22c1c81167fc5ee7553f"}};</script>5 <meta charset="UTF-8">6 <meta name="viewport" content="width=device-width, initial-scale=1.0">7 8 <!--9 ╔══════════════════════════════════════════════════════════════════════╗10 ║ 📐 Math Web App Hub — index.html Template ║11 ║ โครงการอบรม "Math x AI Creative Studio" ║12 ║ คณะวิทยาศาสตร์และเทคโนโลยี มหาวิทยาลัยราชภัฏพระนครศรีอยุธยา ║13 ║ 16–17 พฤษภาคม 2569 ║14 ╠══════════════════════════════════════════════════════════════════════╣15 ║ 📌 วิธีใช้ไฟล์นี้ ║16 ║ 1. แก้ไข "ส่วนที่ 1 — ข้อมูลครู" ด้านล่าง ║17 ║ 2. แก้ไข "ส่วนที่ 2 — รายการ Web App" เพิ่ม app ของตัวเอง ║18 ║ 3. แก้ไข "ส่วนที่ 3 — ลิงก์ภายนอก" ใส่ Dify URL ของตัวเอง ║19 ║ 4. บันทึกไฟล์เป็นชื่อ index.html ║20 ║ 5. Deploy บน Hugging Face Spaces (ดูคำแนะนำด้านล่าง) ║21 ╠══════════════════════════════════════════════════════════════════════╣22 ║ 🚀 วิธี Deploy บน Hugging Face Spaces (Static HTML) ║23 ║ 1. ไปที่ huggingface.co → New Space ║24 ║ 2. SDK: เลือก "Static" (ไม่ต้องใช้ Python!) ║25 ║ 3. อัปโหลดไฟล์: index.html (ไฟล์นี้) + ไฟล์ app ทั้งหมด ║26 ║ 4. Space จะ deploy ให้อัตโนมัติใน ~30 วินาที ║27 ║ 5. URL ของคุณ: username.hf.space/spacename ║28 ╚══════════════════════════════════════════════════════════════════════╝29 -->30 31 <!-- ชื่อแท็บในเบราว์เซอร์ — เปลี่ยนให้ตรงกับชื่อครูและวิชา -->32 <title>📐 Math Web App Hub — ห้องเรียนคณิตศาสตร์ของฉัน</title>33 34 <!-- ─────────── CDN Libraries ─────────── -->35 <!-- Tailwind CSS: utility-first CSS framework ทำให้เขียน style ได้เร็ว -->36 <script src="https://cdn.tailwindcss.com"></script>37 38 <!-- Google Font: Sarabun รองรับภาษาไทยสวยงาม -->39 <link40 href="https://fonts.googleapis.com/css2?family=Sarabun:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap"41 rel="stylesheet">42 43 <!-- Font Awesome: ไลบรารีไอคอน ใช้ class เช่น fa-solid fa-chart-line -->44 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">45 46 <!-- KaTeX: แสดงสูตรคณิตศาสตร์ LaTeX ได้สวยงาม ($x^2$, $\frac{a}{b}$) -->47 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">48 <script src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"></script>49 <script src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.min.js"></script>50 51 <style>52 /*53 * ══════════════════════════════════════════════════════54 * CSS VARIABLES — เปลี่ยนสีธีมทั้งหมดที่นี่ที่เดียว55 * ไม่จำเป็นต้องแก้ไขส่วนอื่นใน CSS เลย56 * ══════════════════════════════════════════════════════57 */58 :root {59 /* สีหลัก — เปลี่ยนได้เป็นสีของโรงเรียน/วิชาตัวเอง */60 --primary: #1d4ed8;61 /* น้ำเงินเข้ม (header, ปุ่ม) */62 --primary-mid: #2563eb;63 /* น้ำเงินกลาง */64 --primary-light: #eff6ff;65 /* น้ำเงินอ่อนมาก (พื้นหลัง badge) */66 --accent: #f59e0b;67 /* ทอง (เน้นสำคัญ) */68 --accent-dark: #b45309;69 /* ทองเข้ม */70 71 /* สีพื้นหลังและข้อความ */72 --bg: #f1f5f9;73 /* เทาอ่อน (พื้นหลัง page) */74 --card-bg: #ffffff;75 /* ขาว (การ์ด) */76 --text: #0f172a;77 /* เกือบดำ (ข้อความหลัก) */78 --text-muted: #64748b;79 /* เทา (ข้อความรอง) */80 --border: #e2e8f0;81 /* ขอบ */82 83 /* Tier Badges */84 --tier1-bg: #dcfce7;85 --tier1-text: #166534;86 /* เขียว */87 --tier2-bg: #fef9c3;88 --tier2-text: #854d0e;89 /* เหลือง */90 --tier3-bg: #ede9fe;91 --tier3-text: #5b21b6;92 /* ม่วง */93 }94 95 /* ══ Global ══ */96 *,97 *::before,98 *::after {99 box-sizing: border-box;100 }101 102 body {103 font-family: 'Sarabun', 'Segoe UI', sans-serif;104 background: var(--bg);105 color: var(--text);106 margin: 0;107 min-height: 100vh;108 display: flex;109 flex-direction: column;110 }111 112 /* ══ Header: gradient เข้มแบบมืออาชีพ ══ */113 .hero {114 background: linear-gradient(135deg, #0f172a 0%, #1d4ed8 60%, #2563eb 100%);115 position: relative;116 overflow: hidden;117 }118 119 /* วงกลม decoration ใน header */120 .hero::before {121 content: '';122 position: absolute;123 width: 400px;124 height: 400px;125 border-radius: 50%;126 background: rgba(255, 255, 255, 0.04);127 top: -120px;128 right: -80px;129 pointer-events: none;130 }131 132 .hero::after {133 content: '';134 position: absolute;135 width: 250px;136 height: 250px;137 border-radius: 50%;138 background: rgba(245, 158, 11, 0.08);139 bottom: -60px;140 left: 10%;141 pointer-events: none;142 }143 144 /* ══ Workshop badge ใน header ══ */145 .ws-badge {146 background: rgba(255, 255, 255, 0.12);147 border: 1px solid rgba(255, 255, 255, 0.2);148 color: #bfdbfe;149 font-size: 0.78rem;150 font-weight: 600;151 padding: 0.3rem 0.85rem;152 border-radius: 9999px;153 display: inline-flex;154 align-items: center;155 gap: 0.4rem;156 backdrop-filter: blur(4px);157 }158 159 /* ══ App Counter chip ══ */160 .count-chip {161 background: rgba(255, 255, 255, 0.15);162 border: 1px solid rgba(255, 255, 255, 0.25);163 color: white;164 font-weight: 700;165 font-size: 0.9rem;166 padding: 0.45rem 1.1rem;167 border-radius: 9999px;168 display: inline-flex;169 align-items: center;170 gap: 0.5rem;171 backdrop-filter: blur(4px);172 }173 174 /* ══ การ์ด Web App ══ */175 .app-card {176 background: var(--card-bg);177 border-radius: 1rem;178 border: 2px solid var(--border);179 padding: 1.5rem;180 display: flex;181 flex-direction: column;182 transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;183 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);184 }185 186 .app-card:hover {187 transform: translateY(-6px);188 box-shadow: 0 16px 32px -8px rgba(0, 0, 0, 0.12);189 }190 191 .app-card[data-tier="Tier 1"]:hover {192 border-color: #4ade80;193 }194 195 .app-card[data-tier="Tier 2"]:hover {196 border-color: #facc15;197 }198 199 .app-card[data-tier="Tier 3"]:hover {200 border-color: #a78bfa;201 }202 203 /* ══ Icon circle บนการ์ด ══ */204 .card-icon {205 width: 3.5rem;206 height: 3.5rem;207 border-radius: 0.875rem;208 display: flex;209 align-items: center;210 justify-content: center;211 font-size: 1.5rem;212 margin: 0 auto 1rem;213 }214 215 .card-icon.blue {216 background: #dbeafe;217 color: #1d4ed8;218 }219 220 .card-icon.green {221 background: #dcfce7;222 color: #15803d;223 }224 225 .card-icon.purple {226 background: #ede9fe;227 color: #7c3aed;228 }229 230 .card-icon.amber {231 background: #fef3c7;232 color: #b45309;233 }234 235 .card-icon.rose {236 background: #ffe4e6;237 color: #be123c;238 }239 240 .card-icon.teal {241 background: #ccfbf1;242 color: #0f766e;243 }244 245 /* ══ Tier badge ══ */246 .tier-badge {247 font-size: 0.72rem;248 font-weight: 700;249 padding: 0.25rem 0.7rem;250 border-radius: 9999px;251 text-transform: uppercase;252 letter-spacing: 0.5px;253 }254 255 .tier-badge.t1 {256 background: var(--tier1-bg);257 color: var(--tier1-text);258 }259 260 .tier-badge.t2 {261 background: var(--tier2-bg);262 color: var(--tier2-text);263 }264 265 .tier-badge.t3 {266 background: var(--tier3-bg);267 color: var(--tier3-text);268 }269 270 /* ══ Math badge (แอปที่ใช้ KaTeX/MathLive) ══ */271 .math-badge {272 font-size: 0.7rem;273 font-weight: 600;274 padding: 0.2rem 0.55rem;275 border-radius: 9999px;276 background: #fdf4ff;277 color: #7e22ce;278 border: 1px solid #e9d5ff;279 }280 281 /* ══ ปุ่ม Primary ══ */282 .btn-open {283 background: var(--primary);284 color: white;285 padding: 0.55rem 1.1rem;286 border-radius: 0.625rem;287 font-weight: 700;288 font-size: 0.9rem;289 text-decoration: none;290 display: flex;291 align-items: center;292 justify-content: center;293 gap: 0.4rem;294 transition: background 0.2s, transform 0.15s;295 border: none;296 cursor: pointer;297 }298 299 .btn-open:hover {300 background: var(--primary-mid);301 transform: scale(1.02);302 }303 304 /* ══ ปุ่ม Copy Link (เส้นประ) ══ */305 .btn-copy-link {306 font-size: 0.78rem;307 color: var(--text-muted);308 border: 1.5px dashed var(--border);309 border-radius: 0.5rem;310 padding: 0.4rem;311 cursor: pointer;312 background: transparent;313 transition: color 0.2s, border-color 0.2s;314 width: 100%;315 display: flex;316 align-items: center;317 justify-content: center;318 gap: 0.35rem;319 }320 321 .btn-copy-link:hover {322 color: var(--primary);323 border-color: #93c5fd;324 }325 326 /* ══ Filter buttons ══ */327 .filter-btn {328 padding: 0.4rem 1rem;329 border-radius: 9999px;330 font-size: 0.82rem;331 font-weight: 600;332 cursor: pointer;333 border: none;334 transition: background 0.2s, color 0.2s;335 }336 337 .filter-btn.active,338 .filter-btn:hover {339 background: var(--primary);340 color: white;341 }342 343 /* ══ Grid การ์ด ══ */344 .card-grid {345 display: grid;346 grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));347 gap: 1.25rem;348 }349 350 /* ══ Deploy tip box ══ */351 .tip-box {352 background: #fefce8;353 border: 1.5px solid #fde047;354 border-radius: 0.75rem;355 padding: 1rem 1.25rem;356 font-size: 0.875rem;357 }358 359 /* ══ Footer ══ */360 .site-footer {361 background: linear-gradient(135deg, #0f172a, #1e293b);362 color: #cbd5e1;363 }364 365 /* ══ KaTeX override: ให้สมการในคำอธิบายการ์ดมีขนาดเหมาะสม ══ */366 .app-card .katex {367 font-size: 1em;368 }369 370 @media (max-width: 640px) {371 .card-grid {372 grid-template-columns: 1fr;373 }374 }375 </style>376</head>377 378<body>379 380 <!-- ═══════════════════════════════════════════381 HEADER — ส่วนหัวของหน้า382 ไม่ต้องแก้ HTML ส่วนนี้383 ข้อมูลจะถูก inject ด้วย JavaScript384 ═══════════════════════════════════════════ -->385 <header class="hero text-white py-10 px-6">386 <div class="max-w-5xl mx-auto relative z-10">387 388 <!-- แถวบน: Workshop badge + App counter -->389 <div class="flex flex-wrap items-center justify-between gap-3 mb-6">390 <span class="ws-badge">391 <i class="fa-solid fa-atom"></i>392 Math x AI Creative Studio · ARU 2569393 </span>394 <!-- id="appCountDisplay" ถูก inject ด้วย updateAppCount() -->395 <span id="appCountDisplay" class="count-chip">396 <i class="fa-solid fa-layer-group"></i>397 กำลังโหลด...398 </span>399 </div>400 401 <!-- ชื่อหน้า + ข้อมูลครู -->402 <h1 class="text-3xl md:text-4xl font-bold tracking-tight mb-2">403 📐 Math Web App Hub404 </h1>405 <!-- id="teacherDisplay" ถูก inject ด้วย renderTeacherInfo() -->406 <p id="teacherDisplay" class="text-blue-200 text-lg mb-4"></p>407 408 <!-- คำอธิบายหน้า -->409 <p class="text-blue-200 max-w-2xl text-base leading-relaxed">410 ✨ แหล่งรวมสื่อการเรียนรู้คณิตศาสตร์แบบ Interactive สร้างสรรค์ด้วย AI และ Math APIs411 <span class="opacity-70">— กดการ์ดเพื่อเปิดใช้งานได้ทันที</span>412 </p>413 </div>414 </header>415 416 <!-- ═══════════════════════════════════════════417 MAIN — เนื้อหาหลัก418 ═══════════════════════════════════════════ -->419 <main class="flex-grow max-w-5xl mx-auto px-4 py-8 w-full">420 421 <!-- ── Filter Bar ── -->422 <div class="flex flex-wrap items-center justify-between gap-3 mb-6">423 <h2 class="text-xl font-bold text-gray-800 flex items-center gap-2">424 <i class="fa-solid fa-grid-2 text-blue-500"></i>425 สื่อการเรียนรู้ของฉัน426 </h2>427 <div class="flex gap-2 flex-wrap">428 <!-- ปุ่มกรองตาม Tier — filterApps() จะถูกเรียกเมื่อกด -->429 <button onclick="filterApps('all')" class="filter-btn active" id="btn-all">ทั้งหมด</button>430 <button onclick="filterApps('Tier 1')" class="filter-btn bg-green-100 text-green-800" id="btn-t1">🟢431 Tier 1</button>432 <button onclick="filterApps('Tier 2')" class="filter-btn bg-yellow-100 text-yellow-800" id="btn-t2">🟡433 Tier 2</button>434 <button onclick="filterApps('Tier 3')" class="filter-btn bg-purple-100 text-purple-800" id="btn-t3">🟣435 Tier 3</button>436 </div>437 </div>438 439 <!-- ── Grid การ์ด Web App ──440 การ์ดทั้งหมดถูกสร้างโดย createAppCard() + renderCards()441 ไม่ต้องแก้ HTML ส่วนนี้ เพิ่ม app ใน webApps[] แทน -->442 <div id="appGrid" class="card-grid"></div>443 444 <!-- ── ข้อความเมื่อไม่มี App ที่ตรง filter ── -->445 <div id="noAppMsg" class="hidden text-center py-16 text-gray-400">446 <i class="fa-regular fa-folder-open text-5xl mb-4 block"></i>447 <p class="text-lg font-semibold">ยังไม่มีสื่อการเรียนรู้ใน Tier นี้</p>448 <p class="text-sm mt-1">ลองสร้าง Web App ใหม่แล้วเพิ่มในรายการ webApps[]</p>449 </div>450 451 <!-- ── Deploy Tip Box ── -->452 <div class="tip-box mt-10 flex gap-3 items-start">453 <i class="fa-brands fa-hugging-face text-2xl text-yellow-500 mt-0.5 shrink-0"></i>454 <div>455 <p class="font-bold text-yellow-800 mb-1">💡 วิธี Deploy บน Hugging Face Spaces</p>456 <ol class="text-yellow-900 text-sm list-decimal list-inside space-y-1 leading-relaxed">457 <li>ไปที่ <a href="https://huggingface.co" target="_blank"458 class="underline font-semibold">huggingface.co</a> → New Space → SDK:459 <strong>Static</strong></li>460 <li>อัปโหลดไฟล์: <code class="bg-yellow-100 px-1 rounded">index.html</code> (ไฟล์นี้) + ไฟล์ app461 ทั้งหมดไว้ในโฟลเดอร์เดียวกัน</li>462 <li>Spaces จะ build อัตโนมัติใน ~30 วินาที ไม่ต้องติดตั้งอะไร!</li>463 <li>URL ของคุณ: <code class="bg-yellow-100 px-1 rounded">your-username.hf.space/space-name</code>464 </li>465 </ol>466 </div>467 </div>468 469 </main>470 471 <!-- ═══════════════════════════════════════════472 FOOTER — ส่วนท้ายของหน้า473 ═══════════════════════════════════════════ -->474 <footer class="site-footer py-8 px-6 mt-auto">475 <div class="max-w-5xl mx-auto">476 477 <!-- แถวปุ่ม: Chatbot + Sheet + QR -->478 <div class="flex flex-col md:flex-row justify-between items-center gap-4 mb-6">479 <div>480 <p class="font-bold text-white text-base">🤖 AI Chatbot ผู้ช่วยสอน</p>481 <p class="text-slate-400 text-sm mt-0.5">สแกน QR หรือคลิกปุ่มเพื่อถามคำถามคณิตศาสตร์</p>482 </div>483 <div class="flex gap-3 flex-wrap justify-center">484 <!-- ปุ่มเปิด Dify Chatbot — URL ตั้งใน externalLinks.difyChatbotUrl -->485 <button onclick="openDifyChatbot()"486 class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-5 rounded-full transition flex items-center gap-2 text-sm shadow-md">487 <i class="fa-solid fa-comment-dots"></i> ถาม AI ผู้ช่วย488 </button>489 <!-- ปุ่ม Google Sheet — URL ตั้งใน externalLinks.googleSheetUrl -->490 <a id="sheetLink" href="#" target="_blank"491 class="bg-emerald-600 hover:bg-emerald-700 text-white font-semibold py-2 px-5 rounded-full transition flex items-center gap-2 text-sm shadow-md">492 <i class="fa-solid fa-table-cells"></i> ดูข้อมูลนักเรียน493 </a>494 <!-- ปุ่ม QR Generator — URL ตั้งใน externalLinks.qrGeneratorPage -->495 <a id="qrLink" href="#" target="_blank"496 class="bg-amber-500 hover:bg-amber-600 text-white font-semibold py-2 px-5 rounded-full transition flex items-center gap-2 text-sm shadow-md">497 <i class="fa-solid fa-qrcode"></i> QR Code498 </a>499 </div>500 </div>501 502 <!-- Credit line -->503 <div class="border-t border-slate-700 pt-4 text-center text-slate-500 text-xs">504 <p>สร้างด้วย ❤️ ในโครงการอบรม <strong class="text-slate-300">Math x AI Creative Studio</strong>505 — คณะวิทยาศาสตร์และเทคโนโลยี มหาวิทยาลัยราชภัฏพระนครศรีอยุธยา</p>506 <p class="mt-1">507 Powered by508 <span class="text-blue-400">KaTeX</span> ·509 <span class="text-purple-400">MathLive</span> ·510 <span class="text-green-400">Gemini</span> ·511 <span class="text-pink-400">Dify</span> ·512 <span class="text-yellow-400">Hugging Face Spaces</span>513 </p>514 </div>515 </div>516 </footer>517 518 <!-- ═══════════════════════════════════════════519 JAVASCRIPT — ตรรกะทั้งหมดของหน้านี้520 ═══════════════════════════════════════════ -->521 <script>522 /*523 * ╔══════════════════════════════════════════════════════════════╗524 * ║ ✏️ ส่วนที่ 1 — ข้อมูลครู (แก้ไขที่นี่เท่านั้น) ║525 * ╚══════════════════════════════════════════════════════════════╝526 *527 * กรอกข้อมูลของคุณในวงเล็บด้านล่าง528 * ชื่อจะแสดงใน Header ของหน้า529 */530 const teacherInfo = {531 name: "ครูสมหมาย รักสงบ", // ← ใส่ชื่อ-นามสกุลของคุณ532 subject: "คณิตศาสตร์ ม.6 · โรงเรียนวิทยาบำรุง", // ← ใส่วิชาและโรงเรียน533 avatar: "👩🏫" // ← เปลี่ยน emoji หรือ URL รูปโปรไฟล์534 };535 536 /*537 * ╔══════════════════════════════════════════════════════════════╗538 * ║ ✏️ ส่วนที่ 2 — รายการ Web App ของคุณ ║539 * ╠══════════════════════════════════════════════════════════════╣540 * ║ ║541 * ║ เพิ่มการ์ด Web App โดยเพิ่ม object ในรายการด้านล่าง ║542 * ║ แต่ละ object มี field ดังนี้: ║543 * ║ ║544 * ║ title ชื่อ Web App (ใส่ emoji ด้านหน้าได้) ║545 * ║ description คำอธิบายสั้นๆ (ใส่ $สมการ$ LaTeX ได้!) ║546 * ║ url ชื่อไฟล์ .html หรือ URL เต็ม ║547 * ║ tier "Tier 1" | "Tier 2" | "Tier 3" ║548 * ║ icon Font Awesome class เช่น "fa-chart-line" ║549 * ║ ดูไอคอนทั้งหมดที่ fontawesome.com/icons ║550 * ║ color "blue" | "green" | "purple" | "amber" ║551 * ║ | "rose" | "teal" ║552 * ║ hasMath true = แอปใช้ KaTeX/MathLive (แสดง badge 📐) ║553 * ║ false = ไม่มี (ละไว้ก็ได้) ║554 * ║ ║555 * ╠══════════════════════════════════════════════════════════════╣556 * ║ ตัวอย่างการเพิ่ม App ใหม่: ║557 * ║ { ║558 * ║ title: "📏 ห้องทดลองเรขาคณิต", ║559 * ║ description: "สำรวจสมบัติสามเหลี่ยมด้วย GeoGebra", ║560 * ║ url: "geometry_lab.html", ║561 * ║ tier: "Tier 2", ║562 * ║ icon: "fa-draw-polygon", ║563 * ║ color: "teal", ║564 * ║ hasMath: true ║565 * ║ }, ║566 * ╚══════════════════════════════════════════════════════════════╝567 */568 const webApps = [569 570 /* ── ตัวอย่างที่ 1: Tier 1 ── */571 {572 title: "โลกของจำนวนเต็ม",573 description: "เรียนรู้เรื่องจำนวนเต็มแบบง่าย ๆ จากเรื่องใกล้ตัว",574 url: "integer.html", // ← เปลี่ยนเป็นชื่อไฟล์จริงของคุณ575 tier: "บทที่ 1",576 icon: "fa-chart-line",577 color: "blue",578 hasMath: true579 },580 581 /* ── ตัวอย่างที่ 2: Tier 2 ── */582 {583 title: "📐 ห้องเรียนจำนวนเต็ม",584 description: "ลากจุดสามเหลี่ยมใน GeoGebra ค้นพบว่า $\\alpha+\\beta+\\gamma=180°$ เสมอ + AI อธิบาย Why",585 url: "integerwai.html", // ← เปลี่ยนเป็นชื่อไฟล์จริงของคุณ586 tier: "บทที่ 2",587 icon: "fa-draw-polygon",588 color: "green",589 hasMath: true590 },591 592 /* ── ตัวอย่างที่ 3: Tier 2 ── */593 {594 title: "🎲 ทดสอบ pisa แบบง่ายๆ",595 description: "ทำข้อสอบ pisa กับนักสืบเบาะแส $\\frac{1}{2}+\\frac{1}{4}=?$",596 url: "pisa.html", // ← เปลี่ยนเป็นชื่อไฟล์จริงของคุณ597 tier: "บทที่ 3",598 icon: "fa-puzzle-piece",599 color: "amber",600 hasMath: true601 },602 603 /* ── ตัวอย่างที่ 4: Tier 3 ── */604 /*605 {606 title: "✏️ ลากแก้สมการเชิงเส้น",607 description: "ลากพจน์ใน Graspable Math แก้ $3x+7=2x-5$ — AI ตรวจคำตอบ + วิเคราะห์ข้อผิดพลาด",608 url: "tier3_equation.html", // ← เปลี่ยนเป็นชื่อไฟล์จริงของคุณ609 tier: "Tier 3",610 icon: "fa-equals",611 color: "purple",612 hasMath: true613 }614 615 /*616 * ── เพิ่ม App ของคุณที่นี่ ──617 * ลบ // ด้านหน้า แล้วกรอกข้อมูล:618 *619 * ,{620 * title: "🔢 ชื่อ App ของฉัน",621 * description: "คำอธิบายสั้นๆ",622 * url: "my_app.html",623 * tier: "Tier 1",624 * icon: "fa-star",625 * color: "teal",626 * hasMath: false627 * }628 */629 ];630 631 /*632 * ╔══════════════════════════════════════════════════════════════╗633 * ║ ✏️ ส่วนที่ 3 — ลิงก์ภายนอก (แก้ไขที่นี่) ║634 * ╠══════════════════════════════════════════════════════════════╣635 * ║ difyChatbotUrl URL ของ Dify Chatbot ที่ Embed ║636 * ║ ไปที่ Dify → Overview → "Share" → Copy URL ║637 * ║ รูปแบบ: https://udify.app/chatbot/TOKEN ║638 * ║ ║639 * ║ googleSheetUrl URL Google Sheet ที่เก็บข้อมูลนักเรียน ║640 * ║ เปิด Sheet → คัดลอก URL จาก address bar ║641 * ║ ║642 * ║ qrGeneratorPage ไฟล์หน้า QR Generator ของคุณ ║643 * ║ หรือใส่ URL ภายนอก เช่น qrco.de ║644 * ╚══════════════════════════════════════════════════════════════╝645 */646 const externalLinks = {647 difyChatbotUrl: "https://udify.app/chatbot/YOUR_TOKEN_HERE", // ← ใส่ Dify URL648 googleSheetUrl: "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID", // ← ใส่ Sheet URL649 qrGeneratorPage: "qr_generator.html" // ← หรือ URL ภายนอก650 };651 652 /*653 * ══════════════════════════════════════════════════════════════654 * ⚙️ โค้ดด้านล่าง — ไม่ต้องแก้ไข655 * (Engine สำหรับสร้างการ์ด, filter, นับ, copy link)656 * ══════════════════════════════════════════════════════════════657 */658 659 /* ── สร้าง HTML สำหรับการ์ด 1 ใบ ── */660 function createAppCard(app) {661 // แมป tier → class tier badge662 const tierClass = { 'Tier 1': 't1', 'Tier 2': 't2', 'Tier 3': 't3' }[app.tier] || 't1';663 664 // Math badge (แสดงเฉพาะ app ที่ hasMath = true)665 const mathBadge = app.hasMath666 ? `<span class="math-badge">📐 Math Rendering</span>`667 : '';668 669 return `670 <div class="app-card" data-tier="${app.tier}">671 <!-- ไอคอนวงกลม -->672 <div class="card-icon ${app.color || 'blue'}">673 <i class="fa-solid ${app.icon}"></i>674 </div>675 676 <!-- Badges (Tier + Math) -->677 <div class="flex justify-center gap-2 mb-3 flex-wrap">678 <span class="tier-badge ${tierClass}">${app.tier}</span>679 ${mathBadge}680 </div>681 682 <!-- ชื่อ App -->683 <h3 class="text-base font-bold text-gray-800 mb-2 text-center leading-snug">684 ${app.title}685 </h3>686 687 <!-- คำอธิบาย — KaTeX จะ render $...$ อัตโนมัติ -->688 <p class="text-gray-500 text-sm flex-grow mb-4 text-center leading-relaxed card-desc">689 ${app.description}690 </p>691 692 <!-- ปุ่มเปิดใช้งาน -->693 <a href="${app.url}" target="_blank" class="btn-open mb-2">694 <i class="fa-solid fa-arrow-up-right-from-square"></i>695 เปิดใช้งาน696 </a>697 698 <!-- ปุ่ม Copy Link -->699 <button onclick="copyLink('${app.url}', this)" class="btn-copy-link">700 <i class="fa-regular fa-copy"></i> คัดลอกลิงก์701 </button>702 </div>703 `;704 }705 706 /* ── แสดงการ์ดตาม filter ── */707 function renderCards(filter = 'all') {708 const grid = document.getElementById('appGrid');709 const noMsg = document.getElementById('noAppMsg');710 const apps = filter === 'all' ? webApps : webApps.filter(a => a.tier === filter);711 712 if (apps.length === 0) {713 grid.innerHTML = '';714 noMsg.classList.remove('hidden');715 } else {716 noMsg.classList.add('hidden');717 grid.innerHTML = apps.map(createAppCard).join('');718 719 // Render LaTeX ใน description ทุกการ์ดที่เพิ่งสร้าง720 grid.querySelectorAll('.card-desc').forEach(el => {721 renderMathInElement(el, {722 delimiters: [723 { left: '$$', right: '$$', display: true },724 { left: '$', right: '$', display: false }725 ],726 throwOnError: false727 });728 });729 }730 updateAppCount();731 }732 733 /* ── กรองตาม Tier ── */734 function filterApps(tier) {735 renderCards(tier);736 737 // อัปเดต active state ของ filter buttons738 ['all', 'Tier 1', 'Tier 2', 'Tier 3'].forEach((t, i) => {739 const ids = ['btn-all', 'btn-t1', 'btn-t2', 'btn-t3'];740 const btn = document.getElementById(ids[i]);741 if (!btn) return;742 if (t === tier) {743 btn.classList.add('active', 'bg-blue-600', 'text-white');744 btn.classList.remove('bg-green-100', 'bg-yellow-100', 'bg-purple-100',745 'text-green-800', 'text-yellow-800', 'text-purple-800');746 } else {747 btn.classList.remove('active', 'bg-blue-600', 'text-white');748 }749 });750 }751 752 /* ── อัปเดตจำนวน App ใน Header ── */753 function updateAppCount() {754 document.getElementById('appCountDisplay').innerHTML =755 `<i class="fa-solid fa-layer-group"></i> ${webApps.length} Web App`;756 }757 758 /* ── Render ข้อมูลครูใน Header ── */759 function renderTeacherInfo() {760 document.getElementById('teacherDisplay').innerHTML =761 `<span class="text-2xl mr-1">${teacherInfo.avatar}</span>762 <strong>${teacherInfo.name}</strong> — ${teacherInfo.subject}`;763 }764 765 /* ── ตั้งค่าลิงก์ภายนอก ── */766 function setupExternalLinks() {767 // Dify popup (กว้าง 420px เหมือน chat widget)768 window.openDifyChatbot = () =>769 window.open(externalLinks.difyChatbotUrl, '_blank', 'width=430,height=640,resizable=yes');770 771 const sheetEl = document.getElementById('sheetLink');772 if (sheetEl) sheetEl.href = externalLinks.googleSheetUrl;773 774 const qrEl = document.getElementById('qrLink');775 if (qrEl) qrEl.href = externalLinks.qrGeneratorPage;776 }777 778 /* ── Copy URL ของ App ── */779 function copyLink(url, btn) {780 // ถ้า url เป็น path สัมพัทธ์ ให้เติม origin ด้านหน้า781 const full = url.startsWith('http') ? url : `${location.origin}${location.pathname.replace(/[^/]*$/, '')}${url}`;782 navigator.clipboard.writeText(full).then(() => {783 const orig = btn.innerHTML;784 btn.innerHTML = '<i class="fa-solid fa-check"></i> คัดลอกแล้ว!';785 btn.style.color = '#16a34a';786 btn.style.borderColor = '#86efac';787 setTimeout(() => {788 btn.innerHTML = orig;789 btn.style.color = '';790 btn.style.borderColor = '';791 }, 1800);792 }).catch(() => {793 alert('ไม่สามารถคัดลอกอัตโนมัติได้ กรุณาคัดลอก URL ด้วยตนเอง:\n' + full);794 });795 }796 797 /* ── เริ่มต้นเมื่อ DOM โหลดเสร็จ ── */798 document.addEventListener('DOMContentLoaded', () => {799 renderTeacherInfo();800 renderCards('all');801 setupExternalLinks();802 803 // Log สำหรับ debug เปิด DevTools (F12) แล้วดูที่ Console804 console.log('%c📐 Math Web App Hub พร้อมใช้งาน', 'color:#2563eb;font-weight:bold;font-size:14px');805 console.log('📊 จำนวน Web App:', webApps.length);806 console.log('👩🏫 ครู:', teacherInfo.name);807 console.log('💡 เพิ่ม App ใหม่ได้ใน webApps[] ใน JavaScript');808 });809 </script>810 811</body>812</html>