CoolFace
Apppublic

Wisetsut/integer-web-app

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
index.html812 linesDownload Raw Back to root
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>