{"id":799,"date":"2026-07-08T10:44:40","date_gmt":"2026-07-08T03:44:40","guid":{"rendered":"https:\/\/phrae1.go.th\/sites\/?page_id=799"},"modified":"2026-07-08T11:49:59","modified_gmt":"2026-07-08T04:49:59","slug":"%e0%b8%82%e0%b9%89%e0%b8%ad%e0%b8%a1%e0%b8%b9%e0%b8%a5%e0%b8%aa%e0%b8%b2%e0%b8%a3%e0%b8%aa%e0%b8%99%e0%b9%80%e0%b8%97%e0%b8%a8%e0%b9%82%e0%b8%a3%e0%b8%87%e0%b9%80%e0%b8%a3%e0%b8%b5%e0%b8%a2%e0%b8%99","status":"publish","type":"page","link":"https:\/\/phrae1.go.th\/sites\/bigdata\/%e0%b8%82%e0%b9%89%e0%b8%ad%e0%b8%a1%e0%b8%b9%e0%b8%a5%e0%b8%aa%e0%b8%b2%e0%b8%a3%e0%b8%aa%e0%b8%99%e0%b9%80%e0%b8%97%e0%b8%a8%e0%b9%82%e0%b8%a3%e0%b8%87%e0%b9%80%e0%b8%a3%e0%b8%b5%e0%b8%a2%e0%b8%99\/","title":{"rendered":"\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e32\u0e23\u0e2a\u0e19\u0e40\u0e17\u0e28\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"th\" class=\"h-full\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Google Sheets Dynamic Dashboard (Educational Edition)<\/title>\n    <!-- Google Fonts for Thai & English -->\n    <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@300;400;500;600;700&#038;family=Mitr:wght@300;400;500;600&#038;display=swap\" rel=\"stylesheet\">\n    \n    <!-- Tailwind CSS -->\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    \n    <!-- ApexCharts for modern charts -->\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/apexcharts\"><\/script>\n    \n    <!-- Lucide Icons -->\n    <script src=\"https:\/\/unpkg.com\/lucide@latest\"><\/script>\n    \n    <!-- PapaParse for high-performance CSV Parsing -->\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/PapaParse\/5.4.1\/papaparse.min.js\"><\/script>\n\n    <script>\n        tailwind.config = {\n            darkMode: 'class',\n            theme: {\n                extend: {\n                    fontFamily: {\n                        sans: ['Inter', 'Mitr', 'sans-serif'],\n                    },\n                    colors: {\n                        brand: {\n                            50: '#eef2ff',\n                            100: '#e0e7ff',\n                            500: '#6366f1',\n                            600: '#4f46e5',\n                            700: '#4338ca',\n                            900: '#312e81',\n                        }\n                    }\n                }\n            }\n        }\n    <\/script>\n    <style>\n        .custom-scrollbar::-webkit-scrollbar {\n            width: 6px;\n            height: 6px;\n        }\n        .custom-scrollbar::-webkit-scrollbar-track {\n            background: transparent;\n        }\n        .custom-scrollbar::-webkit-scrollbar-thumb {\n            background: #cbd5e1;\n            border-radius: 4px;\n        }\n        .dark .custom-scrollbar::-webkit-scrollbar-thumb {\n            background: #475569;\n        }\n    <\/style>\n<\/head>\n<body class=\"bg-slate-50 dark:bg-slate-900 text-slate-800 dark:text-slate-100 font-sans transition-colors duration-200 min-h-screen flex flex-col\">\n\n    <!-- Top Navigation Bar -->\n    <header class=\"bg-white dark:bg-slate-800 border-b border-slate-200 dark:border-slate-700 sticky top-0 z-40 shadow-sm transition-colors\">\n        <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between\">\n            <div class=\"flex items-center gap-3\">\n                <div class=\"bg-emerald-500 text-white p-2 rounded-xl shadow-md shadow-emerald-500\/20\">\n                    <i data-lucide=\"graduation-cap\" class=\"w-6 h-6\"><\/i>\n                <\/div>\n                <div>\n                    <h1 class=\"font-bold text-lg leading-tight text-slate-900 dark:text-white\">\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e16\u0e32\u0e19\u0e28\u0e36\u0e01\u0e29\u0e32\u0e43\u0e19\u0e2a\u0e31\u0e07\u0e01\u0e31\u0e14 \u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1<\/h1>\n                    <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e23\u0e30\u0e1a\u0e1a\u0e23\u0e32\u0e22\u0e07\u0e32\u0e19\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e16\u0e32\u0e19\u0e28\u0e36\u0e01\u0e29\u0e32\u0e41\u0e25\u0e30\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\u0e2d\u0e31\u0e08\u0e09\u0e23\u0e34\u0e22\u0e30<\/p>\n                <\/div>\n            <\/div>\n\n            <!-- Header Actions -->\n            <div class=\"flex items-center gap-2 sm:gap-4\">\n                <!-- Status Badge -->\n                <div id=\"connectionStatus\" class=\"hidden sm:flex items-center gap-2 px-3 py-1 rounded-full text-xs font-medium bg-amber-50 text-amber-700 dark:bg-amber-950\/40 dark:text-amber-400 border border-amber-200 dark:border-amber-900\/50\">\n                    <span class=\"w-2 h-2 rounded-full bg-amber-500 animate-pulse\"><\/span>\n                    <span id=\"statusText\">\u0e01\u0e33\u0e25\u0e31\u0e07\u0e42\u0e2b\u0e25\u0e14\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25&#8230;<\/span>\n                <\/div>\n                \n                <!-- Theme Toggle -->\n                <button id=\"themeToggle\" class=\"p-2 text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition\" title=\"\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19\u0e18\u0e35\u0e21\">\n                    <i data-lucide=\"moon\" class=\"w-5 h-5 dark:hidden\"><\/i>\n                    <i data-lucide=\"sun\" class=\"w-5 h-5 hidden dark:block\"><\/i>\n                <\/button>\n            <\/div>\n        <\/div>\n    <\/header>\n\n    <!-- Main Content Grid -->\n    <main class=\"flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 lg:p-8 space-y-6\">\n\n        <!-- Custom Notification Toast Area -->\n        <div id=\"toast\" class=\"hidden transform translate-y-2 opacity-0 transition-all duration-300 fixed bottom-5 right-5 z-50 bg-slate-900 text-white dark:bg-white dark:text-slate-900 px-4 py-3 rounded-xl shadow-2xl flex items-center gap-3 border border-slate-800 dark:border-slate-200\">\n            <span id=\"toastIcon\"><\/span>\n            <p id=\"toastMessage\" class=\"text-sm font-medium\"><\/p>\n        <\/div>\n\n        <!-- Simplified Data Filtering & Analysis Panel -->\n        <section id=\"mappingPanel\" class=\"hidden bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm space-y-5\">\n            <div class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2 pb-3 border-b border-slate-100 dark:border-slate-700\">\n                <div class=\"flex items-center gap-2\">\n                    <i data-lucide=\"sliders-horizontal\" class=\"text-brand-500 w-5 h-5\"><\/i>\n                    <h3 class=\"font-bold text-slate-950 dark:text-white\">\u0e15\u0e31\u0e27\u0e01\u0e23\u0e2d\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e41\u0e25\u0e30\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32\u0e01\u0e32\u0e23\u0e27\u0e34\u0e40\u0e04\u0e23\u0e32\u0e30\u0e2b\u0e4c\u0e40\u0e0a\u0e34\u0e07\u0e25\u0e36\u0e01<\/h3>\n                <\/div>\n                <div class=\"flex items-center gap-2\">\n                    <span class=\"text-xs text-slate-500 dark:text-slate-400 font-medium\">\u0e21\u0e34\u0e15\u0e34\u0e08\u0e33\u0e41\u0e19\u0e01\u0e43\u0e19\u0e01\u0e23\u0e32\u0e1f (\u0e41\u0e01\u0e19 X):<\/span>\n                    <select id=\"selectGroupBy\" onchange=\"processAndRenderAll()\" class=\"px-2.5 py-1 bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg text-xs font-semibold focus:ring-brand-500 focus:border-brand-500\">\n                        <!-- Dynamically filled with options such as \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\", \"\u0e15\u0e33\u0e1a\u0e25\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\" -->\n                    <\/select>\n                <\/div>\n            <\/div>\n            \n            <!-- Simple filter Grid of 5 Key Educational Fields -->\n            <div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4\">\n                <!-- Amphoe Filter (\u0e2d\u0e33\u0e40\u0e20\u0e2d) -->\n                <div class=\"space-y-1.5\">\n                    <label class=\"block text-xs font-semibold text-slate-500 dark:text-slate-400 flex items-center gap-1\">\n                        <i data-lucide=\"map-pin\" class=\"w-3 h-3 text-brand-500\"><\/i> \u0e2d\u0e33\u0e40\u0e20\u0e2d\n                    <\/label>\n                    <select id=\"selectAmphoe\" onchange=\"processAndRenderAll()\" class=\"w-full px-3 py-2 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-sm focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50\">\n                        <option value=\"-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --\">&#8212; \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 &#8212;<\/option>\n                    <\/select>\n                <\/div>\n\n                <!-- Tambon Filter (\u0e15\u0e33\u0e1a\u0e25) -->\n                <div class=\"space-y-1.5\">\n                    <label class=\"block text-xs font-semibold text-slate-500 dark:text-slate-400 flex items-center gap-1\">\n                        <i data-lucide=\"navigation\" class=\"w-3 h-3 text-brand-500\"><\/i> \u0e15\u0e33\u0e1a\u0e25\n                    <\/label>\n                    <select id=\"selectTambon\" onchange=\"processAndRenderAll()\" class=\"w-full px-3 py-2 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-sm focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50\">\n                        <option value=\"-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --\">&#8212; \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 &#8212;<\/option>\n                    <\/select>\n                <\/div>\n\n                <!-- School Group Filter (\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22) -->\n                <div class=\"space-y-1.5\">\n                    <label class=\"block text-xs font-semibold text-slate-500 dark:text-slate-400 flex items-center gap-1\">\n                        <i data-lucide=\"network\" class=\"w-3 h-3 text-brand-500\"><\/i> \u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\n                    <\/label>\n                    <select id=\"selectGroup\" onchange=\"processAndRenderAll()\" class=\"w-full px-3 py-2 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-sm focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50\">\n                        <option value=\"-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --\">&#8212; \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 &#8212;<\/option>\n                    <\/select>\n                <\/div>\n\n                <!-- Type Filter (\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19) -->\n                <div class=\"space-y-1.5\">\n                    <label class=\"block text-xs font-semibold text-slate-500 dark:text-slate-400 flex items-center gap-1\">\n                        <i data-lucide=\"bookmark\" class=\"w-3 h-3 text-brand-500\"><\/i> \u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\n                    <\/label>\n                    <select id=\"selectType\" onchange=\"processAndRenderAll()\" class=\"w-full px-3 py-2 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-sm focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50\">\n                        <option value=\"-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --\">&#8212; \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 &#8212;<\/option>\n                    <\/select>\n                <\/div>\n\n                <!-- School Size Filter (\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19) -->\n                <div class=\"space-y-1.5\">\n                    <label class=\"block text-xs font-semibold text-slate-500 dark:text-slate-400 flex items-center gap-1\">\n                        <i data-lucide=\"layout-grid\" class=\"w-3 h-3 text-brand-500\"><\/i> \u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\n                    <\/label>\n                    <select id=\"selectSize\" onchange=\"processAndRenderAll()\" class=\"w-full px-3 py-2 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-sm focus:ring-brand-500 focus:border-brand-500 disabled:opacity-50\">\n                        <option value=\"-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --\">&#8212; \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 &#8212;<\/option>\n                    <\/select>\n                <\/div>\n            <\/div>\n        <\/section>\n\n        <!-- Core Education KPIs Summary Row -->\n        <section class=\"grid grid-cols-1 md:grid-cols-3 gap-5\">\n            <!-- Total Schools Card -->\n            <div class=\"bg-gradient-to-br from-indigo-500 to-indigo-600 text-white rounded-2xl p-6 shadow-md shadow-indigo-500\/10 flex items-center justify-between transition hover:scale-[1.01]\">\n                <div class=\"space-y-1\">\n                    <p class=\"text-xs font-semibold text-indigo-100 uppercase tracking-wider\">\u0e08\u0e33\u0e19\u0e27\u0e19\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e27\u0e21<\/p>\n                    <h4 id=\"kpiTotalSchools\" class=\"text-3xl font-extrabold\">&#8211;<\/h4>\n                    <p class=\"text-xs text-indigo-200 flex items-center gap-1\">\n                        <i data-lucide=\"check-circle\" class=\"w-3.5 h-3.5\"><\/i> \u0e15\u0e23\u0e27\u0e08\u0e08\u0e31\u0e1a\u0e04\u0e27\u0e32\u0e21\u0e0b\u0e49\u0e33\u0e0b\u0e49\u0e2d\u0e19\u0e42\u0e14\u0e22\u0e2d\u0e31\u0e15\u0e42\u0e19\u0e21\u0e31\u0e15\u0e34\n                    <\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-white\/10 text-white rounded-2xl\">\n                    <i data-lucide=\"school\" class=\"w-8 h-8\"><\/i>\n                <\/div>\n            <\/div>\n\n            <!-- Total Students Card -->\n            <div class=\"bg-gradient-to-br from-emerald-500 to-emerald-600 text-white rounded-2xl p-6 shadow-md shadow-emerald-500\/10 flex items-center justify-between transition hover:scale-[1.01]\">\n                <div class=\"space-y-1\">\n                    <p class=\"text-xs font-semibold text-emerald-100 uppercase tracking-wider\">\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14<\/p>\n                    <h4 id=\"kpiTotalStudents\" class=\"text-3xl font-extrabold\">8,497<\/h4>\n                    <p class=\"text-xs text-emerald-200\">\u0e04\u0e19 (\u0e15\u0e32\u0e21\u0e02\u0e2d\u0e1a\u0e40\u0e02\u0e15\u0e15\u0e31\u0e27\u0e01\u0e23\u0e2d\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25)<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-white\/10 text-white rounded-2xl\">\n                    <i data-lucide=\"users\" class=\"w-8 h-8\"><\/i>\n                <\/div>\n            <\/div>\n\n            <!-- Total Classrooms Card -->\n            <div class=\"bg-gradient-to-br from-amber-500 to-amber-600 text-white rounded-2xl p-6 shadow-md shadow-amber-500\/10 flex items-center justify-between transition hover:scale-[1.01]\">\n                <div class=\"space-y-1\">\n                    <p class=\"text-xs font-semibold text-amber-100 uppercase tracking-wider\">\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e27\u0e21<\/p>\n                    <h4 id=\"kpiTotalClassrooms\" class=\"text-3xl font-extrabold\">&#8211;<\/h4>\n                    <p class=\"text-xs text-amber-200\">\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e1b\u0e0f\u0e34\u0e1a\u0e31\u0e15\u0e34\u0e01\u0e32\u0e23<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-white\/10 text-white rounded-2xl\">\n                    <i data-lucide=\"presentation\" class=\"w-8 h-8\"><\/i>\n                <\/div>\n            <\/div>\n        <\/section>\n\n        <!-- General KPI Overviews -->\n        <section class=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n            <!-- Total Records -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm flex items-center justify-between\">\n                <div class=\"space-y-1\">\n                    <p class=\"text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider\">\u0e08\u0e33\u0e19\u0e27\u0e19\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14<\/p>\n                    <h4 id=\"kpiTotalRecords\" class=\"text-2xl font-bold text-slate-900 dark:text-white\">&#8211;<\/h4>\n                    <p id=\"kpiTotalRecordsSub\" class=\"text-xs text-emerald-500 flex items-center gap-0.5\"><i data-lucide=\"arrow-up-right\" class=\"w-3.5 h-3.5\"><\/i> \u0e15\u0e32\u0e23\u0e32\u0e07\u0e41\u0e16\u0e27\u0e2a\u0e21\u0e1a\u0e39\u0e23\u0e13\u0e4c<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-blue-50 dark:bg-blue-950\/50 text-blue-500 dark:text-blue-400 rounded-xl\">\n                    <i data-lucide=\"database\" class=\"w-6 h-6\"><\/i>\n                <\/div>\n            <\/div>\n\n            <!-- Sum of Selected Metric -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm flex items-center justify-between\">\n                <div class=\"space-y-1\">\n                    <p id=\"kpiSumLabel\" class=\"text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider\">\u0e1c\u0e25\u0e23\u0e27\u0e21\u0e23\u0e27\u0e21<\/p>\n                    <h4 id=\"kpiSum\" class=\"text-2xl font-bold text-slate-900 dark:text-white\">&#8211;<\/h4>\n                    <p class=\"text-xs text-slate-400 dark:text-slate-500\">\u0e1c\u0e25\u0e2a\u0e30\u0e2a\u0e21\u0e02\u0e2d\u0e07\u0e15\u0e31\u0e27\u0e0a\u0e35\u0e49\u0e27\u0e31\u0e14\u0e2b\u0e25\u0e31\u0e01<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-emerald-50 dark:bg-emerald-950\/50 text-emerald-500 dark:text-emerald-400 rounded-xl\">\n                    <i data-lucide=\"sigma\" class=\"w-6 h-6\"><\/i>\n                <\/div>\n            <\/div>\n\n            <!-- Average of Selected Metric -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm flex items-center justify-between\">\n                <div class=\"space-y-1\">\n                    <p id=\"kpiAvgLabel\" class=\"text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider\">\u0e04\u0e48\u0e32\u0e40\u0e09\u0e25\u0e35\u0e48\u0e22\u0e15\u0e31\u0e27\u0e0a\u0e35\u0e49\u0e27\u0e31\u0e14<\/p>\n                    <h4 id=\"kpiAvg\" class=\"text-2xl font-bold text-slate-900 dark:text-white\">&#8211;<\/h4>\n                    <p class=\"text-xs text-slate-400 dark:text-slate-500\">\u0e04\u0e33\u0e19\u0e27\u0e13\u0e40\u0e0a\u0e34\u0e07\u0e04\u0e13\u0e34\u0e15\u0e28\u0e32\u0e2a\u0e15\u0e23\u0e4c\u0e40\u0e09\u0e25\u0e35\u0e48\u0e22<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-purple-50 dark:bg-purple-950\/50 text-purple-500 dark:text-purple-400 rounded-xl\">\n                    <i data-lucide=\"calculator\" class=\"w-6 h-6\"><\/i>\n                <\/div>\n            <\/div>\n\n            <!-- Count of Unique Categories -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm flex items-center justify-between\">\n                <div class=\"space-y-1\">\n                    <p id=\"kpiUniqueLabel\" class=\"text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider\">\u0e01\u0e25\u0e38\u0e48\u0e21\u0e22\u0e48\u0e2d\u0e22\u0e17\u0e35\u0e48\u0e04\u0e31\u0e14\u0e41\u0e22\u0e01<\/p>\n                    <h4 id=\"kpiUnique\" class=\"text-2xl font-bold text-slate-900 dark:text-white\">&#8211;<\/h4>\n                    <p class=\"text-xs text-slate-400 dark:text-slate-500\">\u0e08\u0e33\u0e19\u0e27\u0e19\u0e01\u0e25\u0e38\u0e48\u0e21\u0e15\u0e31\u0e27\u0e41\u0e1b\u0e23\u0e17\u0e35\u0e48\u0e27\u0e34\u0e40\u0e04\u0e23\u0e32\u0e30\u0e2b\u0e4c\u0e1e\u0e1a<\/p>\n                <\/div>\n                <div class=\"p-3.5 bg-amber-50 dark:bg-amber-950\/50 text-amber-500 dark:text-amber-400 rounded-xl\">\n                    <i data-lucide=\"tags\" class=\"w-6 h-6\"><\/i>\n                <\/div>\n            <\/div>\n        <\/section>\n\n        <!-- Charts Visual Panel -->\n        <section class=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n            <!-- Core Distribution Chart (Bar) -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm lg:col-span-2 space-y-4\">\n                <div class=\"flex items-center justify-between\">\n                    <div>\n                        <h3 class=\"font-bold text-slate-950 dark:text-white text-md\">\u0e1c\u0e25\u0e01\u0e32\u0e23\u0e27\u0e34\u0e40\u0e04\u0e23\u0e32\u0e30\u0e2b\u0e4c\u0e40\u0e1b\u0e23\u0e35\u0e22\u0e1a\u0e40\u0e17\u0e35\u0e22\u0e1a\u0e15\u0e32\u0e21\u0e01\u0e25\u0e38\u0e48\u0e21\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25<\/h3>\n                        <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e08\u0e33\u0e41\u0e19\u0e01\u0e40\u0e1b\u0e23\u0e35\u0e22\u0e1a\u0e40\u0e17\u0e35\u0e22\u0e1a\u0e2a\u0e16\u0e34\u0e15\u0e34\u0e40\u0e0a\u0e34\u0e07\u0e1b\u0e23\u0e34\u0e21\u0e32\u0e13\u0e02\u0e2d\u0e07\u0e15\u0e31\u0e27\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e41\u0e01\u0e19\u0e08\u0e33\u0e41\u0e19\u0e01\u0e2b\u0e25\u0e31\u0e01<\/p>\n                    <\/div>\n                    <div class=\"bg-slate-100 dark:bg-slate-700 rounded-lg p-1 flex\">\n                        <button onclick=\"changeChartType('bar')\" id=\"btnBarChart\" class=\"px-2 py-1 bg-white dark:bg-slate-600 rounded text-xs font-medium shadow-sm transition\">\u0e41\u0e19\u0e27\u0e15\u0e31\u0e49\u0e07<\/button>\n                        <button onclick=\"changeChartType('horizontalBar')\" id=\"btnHorizontalBarChart\" class=\"px-2 py-1 text-slate-600 dark:text-slate-300 rounded text-xs font-medium transition\">\u0e41\u0e19\u0e27\u0e19\u0e2d\u0e19<\/button>\n                    <\/div>\n                <\/div>\n                <div class=\"min-h-[350px] w-full\" id=\"barChartContainer\">\n                    <div id=\"mainBarChart\"><\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Distribution Ratio (Donut) -->\n            <div class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm space-y-4\">\n                <div>\n                    <h3 class=\"font-bold text-slate-950 dark:text-white text-md\">\u0e2a\u0e31\u0e14\u0e2a\u0e48\u0e27\u0e19\u0e42\u0e04\u0e23\u0e07\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e1b\u0e23\u0e34\u0e21\u0e32\u0e13<\/h3>\n                    <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e04\u0e33\u0e19\u0e27\u0e13\u0e2d\u0e31\u0e15\u0e23\u0e32\u0e2a\u0e48\u0e27\u0e19\u0e23\u0e49\u0e2d\u0e22\u0e25\u0e30\u0e02\u0e2d\u0e07\u0e41\u0e15\u0e48\u0e25\u0e30\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48<\/p>\n                <\/div>\n                <div class=\"min-h-[350px] flex items-center justify-center\">\n                    <div id=\"pieChart\" class=\"w-full\"><\/div>\n                <\/div>\n            <\/div>\n\n            <!-- Time-Series Analytics (Line Chart - Dynamically shown if Date column exists) -->\n            <div id=\"timeSeriesPanel\" class=\"hidden bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm lg:col-span-3 space-y-4\">\n                <div class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2\">\n                    <div>\n                        <h3 class=\"font-bold text-slate-950 dark:text-white text-md\">\u0e41\u0e19\u0e27\u0e42\u0e19\u0e49\u0e21\u0e08\u0e33\u0e41\u0e19\u0e01\u0e15\u0e32\u0e21\u0e40\u0e2a\u0e49\u0e19\u0e40\u0e27\u0e25\u0e32\u0e41\u0e25\u0e30\u0e44\u0e17\u0e21\u0e4c\u0e44\u0e25\u0e19\u0e4c<\/h3>\n                        <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e41\u0e2a\u0e14\u0e07\u0e1c\u0e25\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e1e\u0e25\u0e47\u0e2d\u0e15\u0e15\u0e32\u0e21\u0e25\u0e33\u0e14\u0e31\u0e1a\u0e40\u0e2b\u0e15\u0e38\u0e01\u0e32\u0e23\u0e13\u0e4c\u0e40\u0e0a\u0e34\u0e07\u0e40\u0e27\u0e25\u0e32<\/p>\n                    <\/div>\n                    <div class=\"flex items-center gap-2\">\n                        <label class=\"text-xs text-slate-400\">\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e40\u0e27\u0e25\u0e32:<\/label>\n                        <select id=\"selectDateCol\" onchange=\"processAndRenderAll()\" class=\"px-3 py-1 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-lg text-xs\"><\/select>\n                    <\/div>\n                <\/div>\n                <div id=\"timeSeriesChart\"><\/div>\n            <\/div>\n        <\/section>\n\n        <!-- Raw Data Inspector -->\n        <section class=\"bg-white dark:bg-slate-800 rounded-2xl border border-slate-200 dark:border-slate-700 p-5 shadow-sm space-y-4\">\n            <div class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-2 border-b border-slate-100 dark:border-slate-700\">\n                <div>\n                    <h3 class=\"font-bold text-slate-950 dark:text-white text-md\">\u0e15\u0e32\u0e23\u0e32\u0e07\u0e2a\u0e37\u0e1a\u0e04\u0e49\u0e19\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e14\u0e34\u0e1a\u0e2a\u0e16\u0e32\u0e19\u0e28\u0e36\u0e01\u0e29\u0e32<\/h3>\n                    <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e41\u0e2a\u0e14\u0e07\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e41\u0e25\u0e30\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14\u0e08\u0e32\u0e01\u0e2a\u0e40\u0e1b\u0e23\u0e14\u0e0a\u0e35\u0e15 (\u0e41\u0e2a\u0e14\u0e07\u0e1c\u0e25\u0e15\u0e31\u0e27\u0e40\u0e25\u0e02\u0e41\u0e1a\u0e1a\u0e44\u0e21\u0e48\u0e21\u0e35\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e2b\u0e21\u0e32\u0e22\u0e08\u0e38\u0e25\u0e20\u0e32\u0e04\u0e04\u0e31\u0e48\u0e19)<\/p>\n                <\/div>\n                \n                <div class=\"flex flex-col sm:flex-row items-center gap-2 w-full sm:w-auto\">\n                    <div class=\"relative w-full sm:w-64\">\n                        <input type=\"text\" id=\"searchTableInput\" oninput=\"filterTable()\" \n                            class=\"w-full pl-9 pr-4 py-1.5 bg-slate-50 dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded-xl text-xs focus:ring-brand-500 focus:border-brand-500\" \n                            placeholder=\"\u0e04\u0e49\u0e19\u0e2b\u0e32\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e43\u0e19\u0e15\u0e32\u0e23\u0e32\u0e07...\">\n                        <div class=\"absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-slate-400\">\n                            <i data-lucide=\"search\" class=\"w-3.5 h-3.5\"><\/i>\n                        <\/div>\n                    <\/div>\n                    <button onclick=\"downloadCSV()\" class=\"w-full sm:w-auto px-3.5 py-1.5 border border-slate-300 dark:border-slate-700 text-slate-700 dark:text-slate-300 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-900 text-xs font-semibold flex items-center justify-center gap-1.5 transition\">\n                        <i data-lucide=\"download\" class=\"w-3.5 h-3.5\"><\/i> \u0e2a\u0e48\u0e07\u0e2d\u0e2d\u0e01 CSV\n                    <\/button>\n                <\/div>\n            <\/div>\n\n            <!-- Table Container -->\n            <div class=\"overflow-x-auto custom-scrollbar border border-slate-150 dark:border-slate-700 rounded-xl\">\n                <table class=\"w-full text-left border-collapse\">\n                    <thead>\n                        <tr id=\"tableHeader\" class=\"bg-slate-50 dark:bg-slate-900\/50 border-b border-slate-200 dark:border-slate-700 text-xs font-bold text-slate-700 dark:text-slate-300\">\n                            <!-- Headers dynamically injected -->\n                        <\/tr>\n                    <\/thead>\n                    <tbody id=\"tableBody\" class=\"text-xs divide-y divide-slate-100 dark:divide-slate-800 text-slate-600 dark:text-slate-400\">\n                        <!-- Rows dynamically injected -->\n                    <\/tbody>\n                <\/table>\n            <\/div>\n\n            <!-- Pagination & Metrics Summary -->\n            <div class=\"flex flex-col sm:flex-row items-center justify-between gap-3 text-xs text-slate-500 dark:text-slate-400 pt-2\">\n                <div id=\"tableCounter\">\u0e01\u0e33\u0e25\u0e31\u0e07\u0e04\u0e33\u0e19\u0e27\u0e13\u0e41\u0e16\u0e27\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25&#8230;<\/div>\n                <div class=\"flex items-center gap-2\">\n                    <button onclick=\"changePage(-1)\" id=\"btnPrevPage\" class=\"p-1.5 border border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-700 rounded-lg disabled:opacity-40 transition\">\n                        <i data-lucide=\"chevron-left\" class=\"w-4 h-4\"><\/i>\n                    <\/button>\n                    <span id=\"pageIndicator\" class=\"font-semibold\">\u0e2b\u0e19\u0e49\u0e32 1 \/ 1<\/span>\n                    <button onclick=\"changePage(1)\" id=\"btnNextPage\" class=\"p-1.5 border border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-700 rounded-lg disabled:opacity-40 transition\">\n                        <i data-lucide=\"chevron-right\" class=\"w-4 h-4\"><\/i>\n                    <\/button>\n                <\/div>\n            <\/div>\n        <\/section>\n    <\/main>\n\n    <!-- Footer -->\n    <footer class=\"bg-white dark:bg-slate-800 border-t border-slate-200 dark:border-slate-700 py-6 mt-12 transition-colors\">\n        <div class=\"max-w-7xl mx-auto px-4 text-center space-y-2\">\n            <p class=\"text-xs text-slate-500 dark:text-slate-400\">\u0e1e\u0e31\u0e12\u0e19\u0e32\u0e14\u0e49\u0e27\u0e22 \ud83d\udc96 \u0e23\u0e30\u0e1a\u0e1a\u0e1b\u0e23\u0e30\u0e21\u0e27\u0e25\u0e1c\u0e25\u0e01\u0e32\u0e23\u0e27\u0e34\u0e40\u0e04\u0e23\u0e32\u0e30\u0e2b\u0e4c\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2d\u0e31\u0e08\u0e09\u0e23\u0e34\u0e22\u0e30\u0e2d\u0e31\u0e15\u0e42\u0e19\u0e21\u0e31\u0e15\u0e34<\/p>\n            <p class=\"text-[10px] text-slate-400 dark:text-slate-500\">\u0e40\u0e27\u0e2d\u0e23\u0e4c\u0e0a\u0e31\u0e19 4.2 (Educational Update) \u2022 \u0e0b\u0e34\u0e07\u0e04\u0e4c\u0e01\u0e31\u0e1a\u0e2a\u0e40\u0e1b\u0e23\u0e14\u0e0a\u0e35\u0e15\u0e01\u0e32\u0e23\u0e40\u0e23\u0e35\u0e22\u0e19\u0e01\u0e32\u0e23\u0e2a\u0e2d\u0e19\u0e2d\u0e31\u0e15\u0e42\u0e19\u0e21\u0e31\u0e15\u0e34<\/p>\n        <\/div>\n    <\/footer>\n\n    <script>\n        \/\/ Global State Variables\n        const TARGET_SHEET_URL = \"https:\/\/docs.google.com\/spreadsheets\/d\/1FzJ4iaRZ5QBY1bDPITS0zB_4ISm1nJDB\/edit?usp=sharing&ouid=100668498222356052151&rtpof=true&sd=true\";\n        let rawData = [];\n        let filteredData = [];\n        let columnsList = [];\n        let tableCurrentPage = 1;\n        const tableRowsPerPage = 10;\n        let chartInstances = {};\n        let currentChartType = 'bar'; \/\/ bar, horizontalBar\n\n        \/\/ Educational Rich Mock Dataset calibrated to sum up to exactly 8,497 students\n        const mockData = [\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e40\u0e21\u0e37\u0e2d\u0e07\u0e41\u0e1e\u0e23\u0e48\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e43\u0e19\u0e40\u0e27\u0e35\u0e22\u0e07\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e1e\u0e0d\u0e32\u0e1e\u0e25\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e27\u0e31\u0e14\u0e40\u0e21\u0e18\u0e31\u0e07\u0e01\u0e23\u0e32\u0e27\u0e32\u0e2a (\u0e40\u0e17\u0e28\u0e23\u0e31\u0e10\u0e23\u0e32\u0e29\u0e0e\u0e23\u0e4c\u0e19\u0e38\u0e01\u0e39\u0e25)\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e43\u0e2b\u0e0d\u0e48\u0e1e\u0e34\u0e40\u0e28\u0e29\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 24, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 1820, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 65},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e40\u0e21\u0e37\u0e2d\u0e07\u0e41\u0e1e\u0e23\u0e48\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e40\u0e2b\u0e21\u0e37\u0e2d\u0e07\u0e2b\u0e21\u0e49\u0e2d\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e1e\u0e0d\u0e32\u0e1e\u0e25\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e0a\u0e38\u0e21\u0e0a\u0e19\u0e1a\u0e49\u0e32\u0e19\u0e40\u0e2b\u0e21\u0e37\u0e2d\u0e07\u0e2b\u0e21\u0e49\u0e2d\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e01\u0e25\u0e32\u0e07\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 12, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 960, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 32},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e23\u0e49\u0e2d\u0e07\u0e01\u0e27\u0e32\u0e07\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e23\u0e49\u0e2d\u0e07\u0e40\u0e02\u0e47\u0e21\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e0a\u0e48\u0e2d\u0e41\u0e2e\u0e19\u0e32\u0e08\u0e31\u0e01\u0e23\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e44\u0e17\u0e22\u0e23\u0e31\u0e10\u0e27\u0e34\u0e17\u0e22\u0e32 31\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e43\u0e2b\u0e0d\u0e48\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 18, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 1720, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 55},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e23\u0e49\u0e2d\u0e07\u0e01\u0e27\u0e32\u0e07\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e17\u0e38\u0e48\u0e07\u0e19\u0e49\u0e32\u0e27\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e0a\u0e48\u0e2d\u0e41\u0e2e\u0e19\u0e32\u0e08\u0e31\u0e01\u0e23\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e1a\u0e49\u0e32\u0e19\u0e17\u0e38\u0e48\u0e07\u0e19\u0e49\u0e32\u0e27\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e01\u0e25\u0e32\u0e07\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 8, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 860, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 22},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e2a\u0e2d\u0e07\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e1a\u0e49\u0e32\u0e19\u0e2b\u0e19\u0e38\u0e19\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e22\u0e21\u0e40\u0e2b\u0e19\u0e37\u0e2d\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e2d\u0e19\u0e38\u0e1a\u0e32\u0e25\u0e1a\u0e49\u0e32\u0e19\u0e2b\u0e19\u0e38\u0e19\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e43\u0e2b\u0e0d\u0e48\u0e1e\u0e34\u0e40\u0e28\u0e29\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 22, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 1380, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 58},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e2a\u0e2d\u0e07\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e2a\u0e30\u0e40\u0e2d\u0e35\u0e22\u0e1a\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e22\u0e21\u0e40\u0e2b\u0e19\u0e37\u0e2d\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e1a\u0e49\u0e32\u0e19\u0e2a\u0e30\u0e40\u0e2d\u0e35\u0e22\u0e1a\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e02\u0e22\u0e32\u0e22\u0e42\u0e2d\u0e01\u0e32\u0e2a\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e43\u0e2b\u0e0d\u0e48\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 14, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 1120, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 40},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e2b\u0e19\u0e2d\u0e07\u0e21\u0e48\u0e27\u0e07\u0e44\u0e02\u0e48\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e2b\u0e19\u0e2d\u0e07\u0e21\u0e48\u0e27\u0e07\u0e44\u0e02\u0e48\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e0a\u0e48\u0e2d\u0e41\u0e2e\u0e19\u0e32\u0e08\u0e31\u0e01\u0e23\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e2d\u0e19\u0e38\u0e1a\u0e32\u0e25\u0e2b\u0e19\u0e2d\u0e07\u0e21\u0e48\u0e27\u0e07\u0e44\u0e02\u0e48\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e2a\u0e32\u0e21\u0e31\u0e0d\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e40\u0e25\u0e47\u0e01\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 6, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 380, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 14},\n            {\"\u0e1b\u0e35\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\": \"2569\", \"\u0e40\u0e02\u0e15\u0e1e\u0e37\u0e49\u0e19\u0e17\u0e35\u0e48\": \"\u0e2a\u0e1e\u0e1b.\u0e41\u0e1e\u0e23\u0e48 \u0e40\u0e02\u0e15 1\", \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\": \"\u0e2b\u0e19\u0e2d\u0e07\u0e21\u0e48\u0e27\u0e07\u0e44\u0e02\u0e48\", \"\u0e15\u0e33\u0e1a\u0e25\": \"\u0e19\u0e49\u0e33\u0e23\u0e31\u0e14\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\": \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e1e\u0e0d\u0e32\u0e1e\u0e25\", \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e1a\u0e49\u0e32\u0e19\u0e19\u0e49\u0e33\u0e23\u0e31\u0e14\", \"\u0e2a\u0e16\u0e32\u0e19\u0e30\": \"\u0e1b\u0e01\u0e15\u0e34\", \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\": \"\u0e02\u0e22\u0e32\u0e22\u0e42\u0e2d\u0e01\u0e32\u0e2a\", \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": \"\u0e02\u0e19\u0e32\u0e14\u0e40\u0e25\u0e47\u0e01\", \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\": 8, \"num_obec_student\": 54010125, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\": 257, \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e04\u0e23\u0e39\": 12}\n        ];\n\n        window.onload = function() {\n            lucide.createIcons();\n            initTheme();\n\n            \/\/ Load initial Mock Data first to make UI beautiful, then try to fetch Google Sheet automatically\n            loadMockData();\n            \n            \/\/ Trigger load from the prefilled URL\n            setTimeout(() => {\n                loadGoogleSheetData();\n            }, 800);\n        }\n\n        \/\/ Theme Toggle Functionality\n        function initTheme() {\n            if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {\n                document.documentElement.classList.add('dark');\n            } else {\n                document.documentElement.classList.remove('dark');\n            }\n\n            document.getElementById('themeToggle').addEventListener('click', function() {\n                if (document.documentElement.classList.contains('dark')) {\n                    document.documentElement.classList.remove('dark');\n                    localStorage.setItem('color-theme', 'light');\n                } else {\n                    document.documentElement.classList.add('dark');\n                    localStorage.setItem('color-theme', 'dark');\n                }\n            });\n        }\n\n        \/\/ Show Toast Notification\n        function showNotification(message, type = 'success') {\n            const toast = document.getElementById('toast');\n            const toastMessage = document.getElementById('toastMessage');\n            const toastIcon = document.getElementById('toastIcon');\n\n            toastMessage.textContent = message;\n            \n            if (type === 'success') {\n                toastIcon.innerHTML = `<i data-lucide=\"check-circle-2\" class=\"w-5 h-5 text-emerald-500\"><\/i>`;\n            } else if (type === 'error') {\n                toastIcon.innerHTML = `<i data-lucide=\"alert-triangle\" class=\"w-5 h-5 text-rose-500\"><\/i>`;\n            } else {\n                toastIcon.innerHTML = `<i data-lucide=\"info\" class=\"w-5 h-5 text-blue-500\"><\/i>`;\n            }\n            lucide.createIcons();\n\n            toast.classList.remove('hidden', 'translate-y-2', 'opacity-0');\n            toast.classList.add('flex', 'translate-y-0', 'opacity-100');\n\n            setTimeout(() => {\n                toast.classList.add('translate-y-2', 'opacity-0');\n                setTimeout(() => toast.classList.add('hidden'), 300);\n            }, 4000);\n        }\n\n        \/\/ Helper to convert standard Google Sheet Edit URL to clean CSV Export Endpoint\n        function convertUrlToCsvEndpoint(url) {\n            if (!url) return '';\n            const matches = url.match(\/\\\/d\\\/([a-zA-Z0-9-_]+)\/);\n            if (matches && matches[1]) {\n                const sheetId = matches[1];\n                const gidMatches = url.match(\/[#&]gid=([0-9]+)\/);\n                const gidParam = gidMatches ? `&gid=${gidMatches[1]}` : '';\n                return `https:\/\/docs.google.com\/spreadsheets\/d\/${sheetId}\/export?format=csv${gidParam}`;\n            }\n            return url;\n        }\n\n        \/\/ Fetch & Parse Function from hardcoded URL\n        function loadGoogleSheetData() {\n            const statusText = document.getElementById('statusText');\n            const connectionStatus = document.getElementById('connectionStatus');\n            connectionStatus.classList.remove('hidden');\n            statusText.textContent = \"\u0e01\u0e33\u0e25\u0e31\u0e07\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e15\u0e48\u0e2d\u0e01\u0e31\u0e1a Google Sheets...\";\n\n            const csvExportUrl = convertUrlToCsvEndpoint(TARGET_SHEET_URL);\n\n            fetch(csvExportUrl, { cache: 'no-store' })\n                .then(response => {\n                    if (!response.ok) {\n                        throw new Error(`HTTP Error Status: ${response.status}`);\n                    }\n                    return response.text();\n                })\n                .then(csvText => {\n                    Papa.parse(csvText, {\n                        header: true,\n                        skipEmptyLines: true,\n                        dynamicTyping: true,\n                        complete: function(results) {\n                            if (results.data && results.data.length > 0) {\n                                rawData = results.data;\n                                showNotification('\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e15\u0e48\u0e2d\u0e2a\u0e40\u0e1b\u0e23\u0e14\u0e0a\u0e35\u0e15\u0e41\u0e25\u0e30\u0e14\u0e36\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e33\u0e40\u0e23\u0e47\u0e08!', 'success');\n                                updateStatusBadge(true, \"\u0e0b\u0e34\u0e07\u0e04\u0e4c\u0e01\u0e31\u0e1a Google Sheets \u0e2a\u0e33\u0e40\u0e23\u0e47\u0e08\");\n                                initDashboardStructure();\n                            } else {\n                                throw new Error(\"\u0e44\u0e21\u0e48\u0e1e\u0e1a\u0e41\u0e16\u0e27\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e43\u0e14\u0e46 \u0e43\u0e19\u0e2a\u0e40\u0e1b\u0e23\u0e14\u0e0a\u0e35\u0e15\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\");\n                            }\n                        },\n                        error: function(err) {\n                            throw new Error(`\u0e01\u0e32\u0e23\u0e41\u0e1b\u0e25\u0e07\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a CSV \u0e1c\u0e34\u0e14\u0e1e\u0e25\u0e32\u0e14: ${err.message}`);\n                        }\n                    });\n                })\n                .catch(error => {\n                    console.error(\"Fetch Error Detail:\", error);\n                    showNotification('\u0e43\u0e0a\u0e49\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e08\u0e33\u0e25\u0e2d\u0e07\u0e17\u0e32\u0e07\u0e01\u0e32\u0e23\u0e28\u0e36\u0e01\u0e29\u0e32\u0e04\u0e38\u0e13\u0e20\u0e32\u0e1e\u0e2a\u0e39\u0e07\u0e41\u0e25\u0e30\u0e2a\u0e31\u0e14\u0e2a\u0e48\u0e27\u0e19\u0e08\u0e23\u0e34\u0e07', 'info');\n                    updateStatusBadge(false, \"\u0e43\u0e0a\u0e49\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e01\u0e32\u0e23\u0e2a\u0e32\u0e18\u0e34\u0e15\");\n                    loadMockData();\n                });\n        }\n\n        \/\/ Load Default Mock Data\n        function loadMockData() {\n            rawData = JSON.parse(JSON.stringify(mockData));\n            updateStatusBadge(false, \"\u0e43\u0e0a\u0e49\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e01\u0e32\u0e23\u0e2a\u0e32\u0e18\u0e34\u0e15\");\n            initDashboardStructure();\n        }\n\n        function updateStatusBadge(isSynced, message) {\n            const connectionStatus = document.getElementById('connectionStatus');\n            const statusText = document.getElementById('statusText');\n            connectionStatus.classList.remove('hidden');\n            \n            if (isSynced) {\n                connectionStatus.className = \"flex items-center gap-2 px-3 py-1 rounded-full text-xs font-medium bg-emerald-50 text-emerald-700 dark:bg-emerald-950\/40 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-900\/50\";\n                statusText.innerHTML = `<span class=\"w-2 h-2 rounded-full bg-emerald-500\"><\/span> ${message}`;\n            } else {\n                connectionStatus.className = \"flex items-center gap-2 px-3 py-1 rounded-full text-xs font-medium bg-amber-50 text-amber-700 dark:bg-amber-950\/40 dark:text-amber-400 border border-amber-200 dark:border-amber-900\/50\";\n                statusText.innerHTML = `<span class=\"w-2 h-2 rounded-full bg-amber-500 animate-pulse\"><\/span> ${message}`;\n            }\n        }\n\n        \/\/ Helper to search column header containing keyword\n        function findColumnKey(keyword) {\n            if (!columnsList || columnsList.length === 0) return null;\n            return columnsList.find(col => col.toLowerCase().includes(keyword.toLowerCase()));\n        }\n\n        \/\/ Initialize mapping, selectors and show control panel\n        function initDashboardStructure() {\n            if (rawData.length === 0) return;\n\n            columnsList = Object.keys(rawData[0]).filter(header => header.trim() !== \"\");\n            \n            if (columnsList.length === 0) {\n                showNotification('\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e43\u0e19\u0e15\u0e32\u0e23\u0e32\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e44\u0e21\u0e48\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07', 'error');\n                return;\n            }\n\n            document.getElementById('mappingPanel').classList.remove('hidden');\n\n            \/\/ Find valid column keywords\n            const amphoeKey = findColumnKey(\"\u0e2d\u0e33\u0e40\u0e20\u0e2d\");\n            const tambonKey = findColumnKey(\"\u0e15\u0e33\u0e1a\u0e25\");\n            const groupKey = findColumnKey(\"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\") || findColumnKey(\"\u0e01\u0e25\u0e38\u0e48\u0e21\") || findColumnKey(\"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\");\n            const typeKey = findColumnKey(\"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\");\n            const sizeKey = findColumnKey(\"\u0e02\u0e19\u0e32\u0e14\");\n\n            \/\/ Setup simplified dimension selectors for Chart Grouping\n            const selectGroupBy = document.getElementById('selectGroupBy');\n            selectGroupBy.innerHTML = '';\n            \n            \/\/ We want to offer beautiful dimension categories\n            const dimensionTargets = [\n                { keyword: \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\", label: \"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\" },\n                { keyword: \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\", label: \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\" },\n                { keyword: \"\u0e15\u0e33\u0e1a\u0e25\", label: \"\u0e15\u0e33\u0e1a\u0e25\" },\n                { keyword: \"\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\", label: \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\" },\n                { keyword: \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\", label: \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\" },\n                { keyword: \"\u0e02\u0e19\u0e32\u0e14\", label: \"\u0e02\u0e19\u0e32\u0e14\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\" }\n            ];\n\n            let activeDimensions = 0;\n            dimensionTargets.forEach(target => {\n                const actualKey = findColumnKey(target.keyword);\n                if (actualKey) {\n                    const opt = document.createElement('option');\n                    opt.value = actualKey;\n                    opt.textContent = target.label;\n                    selectGroupBy.appendChild(opt);\n                    activeDimensions++;\n                }\n            });\n\n            \/\/ If no match, add whatever categorical is available\n            if (activeDimensions === 0 && columnsList.length > 0) {\n                const opt = document.createElement('option');\n                opt.value = columnsList[0];\n                opt.textContent = columnsList[0];\n                selectGroupBy.appendChild(opt);\n            }\n\n            \/\/ Populate the 5 Educational Filter Dropdowns Dynamically\n            setupDropdownFilter(\"selectAmphoe\", amphoeKey);\n            setupDropdownFilter(\"selectTambon\", tambonKey);\n            setupDropdownFilter(\"selectGroup\", groupKey);\n            setupDropdownFilter(\"selectType\", typeKey);\n            setupDropdownFilter(\"selectSize\", sizeKey);\n\n            \/\/ Auto-detect Date series column\n            let dateCols = columnsList.filter(col => {\n                let val = rawData[0][col];\n                return val && (\n                    String(val).match(\/^\\d{4}-\\d{2}-\\d{2}\/) || \n                    String(val).match(\/^\\d{1,2}[\\\/\\-]\\d{1,2}[\\\/\\-]\\d{4}\/)\n                );\n            });\n\n            const dateSelect = document.getElementById('selectDateCol');\n            populateDropdown('selectDateCol', columnsList, dateCols.length > 0 ? dateCols[0] : '');\n            if (dateCols.length > 0) {\n                document.getElementById('timeSeriesPanel').classList.remove('hidden');\n            } else {\n                document.getElementById('timeSeriesPanel').classList.add('hidden');\n            }\n\n            processAndRenderAll();\n        }\n\n        \/\/ Robust dropdown populator and binder\n        function setupDropdownFilter(elementId, actualColKey) {\n            const selectEl = document.getElementById(elementId);\n            selectEl.innerHTML = '';\n\n            if (!actualColKey) {\n                const opt = document.createElement('option');\n                opt.value = '-- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e19\u0e35\u0e49 --';\n                opt.textContent = '-- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e19\u0e35\u0e49 --';\n                selectEl.appendChild(opt);\n                selectEl.disabled = true;\n                return;\n            }\n\n            selectEl.disabled = false;\n            \n            \/\/ Get unique sorted values\n            const uniqueVals = [...new Set(rawData.map(row => row[actualColKey]))]\n                .filter(val => val !== null && val !== undefined && String(val).trim() !== \"\")\n                .sort();\n\n            const optAll = document.createElement('option');\n            optAll.value = '-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --';\n            optAll.textContent = '-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --';\n            selectEl.appendChild(optAll);\n\n            uniqueVals.forEach(val => {\n                const opt = document.createElement('option');\n                opt.value = val;\n                opt.textContent = val;\n                selectEl.appendChild(opt);\n            });\n        }\n\n        \/\/ Dropdown filler helper\n        function populateDropdown(elemId, items, selectedValue) {\n            const select = document.getElementById(elemId);\n            if(!select) return;\n            select.innerHTML = '';\n            items.forEach(item => {\n                const opt = document.createElement('option');\n                opt.value = item;\n                opt.textContent = item;\n                if (item === selectedValue) {\n                    opt.selected = true;\n                }\n                select.appendChild(opt);\n            });\n        }\n\n        \/\/ Safe Row filtering check\n        function matchFilter(row, colKeyword, selectId) {\n            const selectEl = document.getElementById(selectId);\n            if (!selectEl || selectEl.disabled) return true;\n\n            const selectedValue = selectEl.value;\n            if (selectedValue === '-- \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 --' || selectedValue === '-- \u0e44\u0e21\u0e48\u0e21\u0e35\u0e04\u0e2d\u0e25\u0e31\u0e21\u0e19\u0e4c\u0e19\u0e35\u0e49 --' || !selectedValue) {\n                return true;\n            }\n\n            const actualColKey = findColumnKey(colKeyword);\n            if (!actualColKey) return true;\n\n            return String(row[actualColKey]) === String(selectedValue);\n        }\n\n        \/\/ Process data based on active parameters and filter constraints\n        function processAndRenderAll() {\n            if (rawData.length === 0) return;\n\n            const groupByCol = document.getElementById('selectGroupBy').value;\n            \n            \/\/ We default to \"\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\" as metric if available, otherwise \"\u0e08\u0e33\u0e19\u0e27\u0e19\" or the first numeric column\n            const metricCol = columnsList.find(col => col.includes(\"\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\") || col.includes(\"\u0e08\u0e33\u0e19\u0e27\u0e19\") || col.toLowerCase().includes(\"student\")) || \n                              columnsList.find(col => typeof rawData[0][col] === 'number') || \n                              columnsList[0];\n\n            const dateCol = document.getElementById('selectDateCol').value;\n\n            \/\/ Apply the 5 Educational Filters together\n            filteredData = rawData.filter(row => {\n                return matchFilter(row, \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\", \"selectAmphoe\") &&\n                       matchFilter(row, \"\u0e15\u0e33\u0e1a\u0e25\", \"selectTambon\") &&\n                       matchFilter(row, \"\u0e01\u0e25\u0e38\u0e48\u0e21\", \"selectGroup\") &&\n                       matchFilter(row, \"\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\", \"selectType\") &&\n                       matchFilter(row, \"\u0e02\u0e19\u0e32\u0e14\", \"selectSize\");\n            });\n\n            \/\/ Calculate basic KPIs\n            const totalRecords = filteredData.length;\n            document.getElementById('kpiTotalRecords').textContent = totalRecords.toLocaleString('th-TH');\n\n            let sumValue = 0;\n            let uniqueCategories = new Set();\n\n            filteredData.forEach(row => {\n                const rawNum = row[metricCol];\n                const numericVal = typeof rawNum === 'number' ? rawNum : parseFloat(String(rawNum).replace(\/[^\\d.-]\/g, ''));\n                if (!isNaN(numericVal)) {\n                    sumValue += numericVal;\n                }\n\n                if (row[groupByCol] !== undefined && row[groupByCol] !== null && row[groupByCol] !== \"\") {\n                    uniqueCategories.add(row[groupByCol]);\n                }\n            });\n\n            const avgValue = totalRecords > 0 ? (sumValue \/ totalRecords) : 0;\n\n            \/\/ Update KPI visuals\n            document.getElementById('kpiSumLabel').textContent = `\u0e1c\u0e25\u0e23\u0e27\u0e21 (${metricCol})`;\n            document.getElementById('kpiSum').textContent = sumValue.toLocaleString('th-TH', { maximumFractionDigits: 2 });\n            \n            document.getElementById('kpiAvgLabel').textContent = `\u0e04\u0e48\u0e32\u0e40\u0e09\u0e25\u0e35\u0e48\u0e22 (${metricCol})`;\n            document.getElementById('kpiAvg').textContent = avgValue.toLocaleString('th-TH', { maximumFractionDigits: 2 });\n            \n            document.getElementById('kpiUniqueLabel').textContent = `${groupByCol}`;\n            document.getElementById('kpiUnique').textContent = uniqueCategories.size.toLocaleString('th-TH');\n\n\n            \/\/ --- COMPUTE ADVANCED EDUCATIONAL SUMMARIES ---\n            \n            \/\/ 1. Total Schools (\u0e08\u0e33\u0e19\u0e27\u0e19\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14)\n            let schoolCol = columnsList.find(col => col.includes(\"\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\") || col.toLowerCase().includes(\"school\"));\n            let totalSchoolsCount = 0;\n            if (schoolCol) {\n                const uniqueSchools = new Set(filteredData.map(row => row[schoolCol]).filter(val => val !== null && val !== undefined && val !== \"\"));\n                totalSchoolsCount = uniqueSchools.size;\n            } else {\n                totalSchoolsCount = uniqueCategories.size;\n            }\n\n            \/\/ 2. Total Students (\u0e08\u0e33\u0e19\u0e27\u0e19\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e27\u0e21)\n            let studentCol = columnsList.find(col => col.includes(\"\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\") || col.toLowerCase().includes(\"student\"));\n            let totalStudentsSum = 0;\n            if (studentCol) {\n                filteredData.forEach(row => {\n                    const rawNum = row[studentCol];\n                    const numericVal = typeof rawNum === 'number' ? rawNum : parseFloat(String(rawNum).replace(\/[^\\d.-]\/g, ''));\n                    if (!isNaN(numericVal)) {\n                        totalStudentsSum += numericVal;\n                    }\n                });\n            } else {\n                if (metricCol.includes(\"\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\") || metricCol.toLowerCase().includes(\"student\")) {\n                    totalStudentsSum = sumValue;\n                }\n            }\n\n            \/\/ 3. Total Classrooms (\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e27\u0e21)\n            let classroomCol = columnsList.find(col => col.includes(\"\u0e2b\u0e49\u0e2d\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\") || col.includes(\"\u0e2b\u0e49\u0e2d\u0e07\") || col.toLowerCase().includes(\"classroom\") || col.toLowerCase().includes(\"class\"));\n            let totalClassroomsSum = 0;\n            if (classroomCol) {\n                filteredData.forEach(row => {\n                    const rawNum = row[classroomCol];\n                    const numericVal = typeof rawNum === 'number' ? rawNum : parseFloat(String(rawNum).replace(\/[^\\d.-]\/g, ''));\n                    if (!isNaN(numericVal)) {\n                        totalClassroomsSum += numericVal;\n                    }\n                });\n            }\n\n            \/\/ Render educational KPI cards\n            document.getElementById('kpiTotalSchools').textContent = totalSchoolsCount > 0 ? totalSchoolsCount.toLocaleString('th-TH') : '0';\n            document.getElementById('kpiTotalStudents').textContent = totalStudentsSum > 0 ? totalStudentsSum.toLocaleString('th-TH') : '-';\n            document.getElementById('kpiTotalClassrooms').textContent = totalClassroomsSum > 0 ? totalClassroomsSum.toLocaleString('th-TH') : '-';\n\n            \/\/ --- END EDUCATIONAL SUMMARIES ---\n\n\n            \/\/ Render Visual Charts\n            renderAggregatedCharts(groupByCol, metricCol);\n\n            \/\/ Render Time Series Chart if available\n            if (dateCol) {\n                renderTimeSeriesChart(dateCol, metricCol);\n            }\n\n            tableCurrentPage = 1;\n            renderInspectorTable();\n        }\n\n        \/\/ Render Bar, Horizontal Bar, and Donut dynamic charts\n        function renderAggregatedCharts(groupByCol, metricCol) {\n            const aggregates = {};\n            filteredData.forEach(row => {\n                const groupKey = row[groupByCol] !== null && row[groupByCol] !== undefined ? String(row[groupByCol]) : '\u0e2d\u0e37\u0e48\u0e19\u0e46';\n                const rawNum = row[metricCol];\n                const numericVal = typeof rawNum === 'number' ? rawNum : parseFloat(String(rawNum).replace(\/[^\\d.-]\/g, ''));\n                \n                if (!isNaN(numericVal)) {\n                    aggregates[groupKey] = (aggregates[groupKey] || 0) + numericVal;\n                }\n            });\n\n            const sortedKeys = Object.keys(aggregates).sort((a, b) => aggregates[b] - aggregates[a]);\n            const sortedValues = sortedKeys.map(key => parseFloat(aggregates[key].toFixed(2)));\n\n            const isDark = document.documentElement.classList.contains('dark');\n            const themeColor = isDark ? '#6366f1' : '#4f46e5';\n            const labelColor = isDark ? '#94a3b8' : '#64748b';\n\n            if (chartInstances.mainBar) {\n                chartInstances.mainBar.destroy();\n            }\n            if (chartInstances.pie) {\n                chartInstances.pie.destroy();\n            }\n\n            const barOptions = {\n                chart: {\n                    type: currentChartType === 'horizontalBar' ? 'bar' : 'bar',\n                    height: 350,\n                    fontFamily: 'Mitr, Inter, sans-serif',\n                    toolbar: { show: false }\n                },\n                series: [{\n                    name: metricCol,\n                    data: sortedValues\n                }],\n                colors: [themeColor],\n                plotOptions: {\n                    bar: {\n                        borderRadius: 6,\n                        columnWidth: '50%',\n                        horizontal: currentChartType === 'horizontalBar',\n                        distributed: false\n                    }\n                },\n                dataLabels: {\n                    enabled: true,\n                    formatter: function (val) {\n                        return val.toLocaleString('th-TH');\n                    },\n                    style: {\n                        fontSize: '10px',\n                        colors: ['#fff']\n                    }\n                },\n                xaxis: {\n                    categories: sortedKeys,\n                    labels: {\n                        style: { colors: labelColor }\n                    }\n                },\n                yaxis: {\n                    labels: {\n                        style: { colors: labelColor },\n                        formatter: function (val) {\n                            return val.toLocaleString('th-TH');\n                        }\n                    }\n                },\n                grid: {\n                    borderColor: isDark ? '#334155' : '#f1f5f9'\n                },\n                tooltip: {\n                    theme: isDark ? 'dark' : 'light'\n                }\n            };\n\n            chartInstances.mainBar = new ApexCharts(document.querySelector(\"#mainBarChart\"), barOptions);\n            chartInstances.mainBar.render();\n\n            const maxDonutCategories = 8;\n            let donutKeys = [...sortedKeys];\n            let donutValues = [...sortedValues];\n\n            if (donutKeys.length > maxDonutCategories) {\n                const topKeys = donutKeys.slice(0, maxDonutCategories);\n                const topValues = donutValues.slice(0, maxDonutCategories);\n                const otherSum = donutValues.slice(maxDonutCategories).reduce((a, b) => a + b, 0);\n                \n                topKeys.push('\u0e2d\u0e37\u0e48\u0e19\u0e46');\n                topValues.push(parseFloat(otherSum.toFixed(2)));\n                \n                donutKeys = topKeys;\n                donutValues = topValues;\n            }\n\n            const pieOptions = {\n                chart: {\n                    type: 'donut',\n                    height: 350,\n                    fontFamily: 'Mitr, Inter, sans-serif'\n                },\n                series: donutValues,\n                labels: donutKeys,\n                legend: {\n                    position: 'bottom',\n                    labels: { colors: labelColor }\n                },\n                dataLabels: {\n                    enabled: true,\n                    style: { fontSize: '10px' }\n                },\n                plotOptions: {\n                    pie: {\n                        donut: {\n                            size: '65%',\n                            labels: {\n                                show: true,\n                                name: {\n                                    show: true,\n                                    fontSize: '14px',\n                                    color: labelColor\n                                },\n                                value: {\n                                    show: true,\n                                    fontSize: '18px',\n                                    fontWeight: 'bold',\n                                    color: isDark ? '#ffffff' : '#0f172a',\n                                    formatter: function (val) {\n                                        return parseFloat(val).toLocaleString('th-TH');\n                                    }\n                                },\n                                total: {\n                                    show: true,\n                                    label: '\u0e2a\u0e31\u0e14\u0e2a\u0e48\u0e27\u0e19\u0e23\u0e27\u0e21',\n                                    color: labelColor,\n                                    formatter: function (w) {\n                                        const sum = w.globals.seriesTotals.reduce((a, b) => a + b, 0);\n                                        return sum.toLocaleString('th-TH', { maximumFractionDigits: 1 });\n                                    }\n                                }\n                            }\n                        }\n                    }\n                },\n                tooltip: {\n                    theme: isDark ? 'dark' : 'light'\n                }\n            };\n\n            chartInstances.pie = new ApexCharts(document.querySelector(\"#pieChart\"), pieOptions);\n            chartInstances.pie.render();\n        }\n\n        \/\/ Change between normal vertical bar and horizontal layout\n        function changeChartType(type) {\n            currentChartType = type;\n            \n            const btnBar = document.getElementById('btnBarChart');\n            const btnHBar = document.getElementById('btnHorizontalBarChart');\n            \n            if (type === 'bar') {\n                btnBar.className = \"px-2 py-1 bg-white dark:bg-slate-600 rounded text-xs font-medium shadow-sm transition\";\n                btnHBar.className = \"px-2 py-1 text-slate-600 dark:text-slate-300 rounded text-xs font-medium transition\";\n            } else {\n                btnHBar.className = \"px-2 py-1 bg-white dark:bg-slate-600 rounded text-xs font-medium shadow-sm transition\";\n                btnBar.className = \"px-2 py-1 text-slate-600 dark:text-slate-300 rounded text-xs font-medium transition\";\n            }\n\n            const groupByCol = document.getElementById('selectGroupBy').value;\n            const metricCol = columnsList.find(col => col.includes(\"\u0e19\u0e31\u0e01\u0e40\u0e23\u0e35\u0e22\u0e19\") || col.includes(\"\u0e08\u0e33\u0e19\u0e27\u0e19\") || col.toLowerCase().includes(\"student\")) || columnsList[0];\n            renderAggregatedCharts(groupByCol, metricCol);\n        }\n\n        \/\/ Render Trend\/Line over time series chart\n        function renderTimeSeriesChart(dateCol, metricCol) {\n            const aggregates = {};\n            filteredData.forEach(row => {\n                const dateKey = row[dateCol] !== null && row[dateCol] !== undefined ? String(row[dateCol]).trim() : '';\n                if (!dateKey) return;\n\n                const rawNum = row[metricCol];\n                const numericVal = typeof rawNum === 'number' ? rawNum : parseFloat(String(rawNum).replace(\/[^\\d.-]\/g, ''));\n                \n                if (!isNaN(numericVal)) {\n                    aggregates[dateKey] = (aggregates[dateKey] || 0) + numericVal;\n                }\n            });\n\n            const sortedDates = Object.keys(aggregates).sort((a, b) => {\n                return new Date(a) - new Date(b);\n            });\n            const values = sortedDates.map(date => parseFloat(aggregates[date].toFixed(2)));\n\n            const isDark = document.documentElement.classList.contains('dark');\n            const labelColor = isDark ? '#94a3b8' : '#64748b';\n\n            if (chartInstances.timeSeries) {\n                chartInstances.timeSeries.destroy();\n            }\n\n            const options = {\n                chart: {\n                    type: 'area',\n                    height: 250,\n                    fontFamily: 'Mitr, Inter, sans-serif',\n                    toolbar: { show: false }\n                },\n                stroke: {\n                    curve: 'smooth',\n                    width: 3\n                },\n                colors: ['#10b981'],\n                fill: {\n                    type: 'gradient',\n                    gradient: {\n                        shadeIntensity: 1,\n                        opacityFrom: 0.45,\n                        opacityTo: 0.05,\n                        stops: [0, 100]\n                    }\n                },\n                series: [{\n                    name: `\u0e22\u0e2d\u0e14\u0e23\u0e27\u0e21 ${metricCol}`,\n                    data: values\n                }],\n                xaxis: {\n                    categories: sortedDates,\n                    labels: {\n                        style: { colors: labelColor }\n                    }\n                },\n                yaxis: {\n                    labels: {\n                        style: { colors: labelColor },\n                        formatter: function (val) {\n                            return val.toLocaleString('th-TH');\n                        }\n                    }\n                },\n                grid: {\n                    borderColor: isDark ? '#334155' : '#f1f5f9'\n                },\n                tooltip: {\n                    theme: isDark ? 'dark' : 'light'\n                }\n            };\n\n            chartInstances.timeSeries = new ApexCharts(document.querySelector(\"#timeSeriesChart\"), options);\n            chartInstances.timeSeries.render();\n        }\n\n        \/\/ Local Filter and Interactive Raw Data Table Search\n        let searchedData = [];\n\n        function filterTable() {\n            const query = document.getElementById('searchTableInput').value.toLowerCase().trim();\n            \n            if (!query) {\n                searchedData = [...filteredData];\n            } else {\n                searchedData = filteredData.filter(row => {\n                    return Object.values(row).some(val => \n                        String(val).toLowerCase().includes(query)\n                    );\n                });\n            }\n\n            tableCurrentPage = 1;\n            renderInspectorTable(false);\n        }\n\n        \/\/ Paginate and display columns dynamically (Commas are removed from numerical cells, \"\u0e23\u0e2b\u0e31\u0e2a obec\" is excluded)\n        function renderInspectorTable(updateSearchList = true) {\n            if (updateSearchList) {\n                const query = document.getElementById('searchTableInput').value.toLowerCase().trim();\n                searchedData = query ? filteredData.filter(row => {\n                    return Object.values(row).some(val => String(val).toLowerCase().includes(query));\n                }) : [...filteredData];\n            }\n\n            \/\/ Exclude OBEC ID from dynamic table display\n            let tableColumns = columnsList.filter(col => {\n                const normalized = col.trim().toLowerCase().replace(\/\\s+\/g, '');\n                return normalized !== \"\u0e23\u0e2b\u0e31\u0e2aobec\" && normalized !== \"\u0e23\u0e2b\u0e31\u0e2ao-bec\";\n            });\n\n            \/\/ Dynamically ensure the specific sequence: \"\u0e2d\u0e33\u0e40\u0e20\u0e2d\", \"\u0e15\u0e33\u0e1a\u0e25\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\"\n            \/\/ We search for matching keys in the current dataset\n            const targetGeoOrder = [\"\u0e2d\u0e33\u0e40\u0e20\u0e2d\", \"\u0e15\u0e33\u0e1a\u0e25\", \"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\"];\n            const foundGeoCols = [];\n\n            targetGeoOrder.forEach(target => {\n                const match = tableColumns.find(col => col.includes(target));\n                if (match) {\n                    foundGeoCols.push(match);\n                }\n            });\n\n            if (foundGeoCols.length > 0) {\n                \/\/ Remove those identified geographic columns from their original positions\n                const nonGeoCols = tableColumns.filter(col => !foundGeoCols.includes(col));\n                \/\/ Find where the first geographical column was originally positioned\n                let insertionIndex = tableColumns.findIndex(col => \n                    col.includes(\"\u0e2d\u0e33\u0e40\u0e20\u0e2d\") || col.includes(\"\u0e15\u0e33\u0e1a\u0e25\") || col.includes(\"\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\") || col.includes(\"\u0e01\u0e25\u0e38\u0e48\u0e21\")\n                );\n                if (insertionIndex === -1) insertionIndex = 0;\n\n                \/\/ Re-stitch columns so \"\u0e2d\u0e33\u0e40\u0e20\u0e2d -> \u0e15\u0e33\u0e1a\u0e25 -> \u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e04\u0e23\u0e37\u0e2d\u0e02\u0e48\u0e32\u0e22\" appear sequentially\n                tableColumns = [\n                    ...nonGeoCols.slice(0, insertionIndex),\n                    ...foundGeoCols,\n                    ...nonGeoCols.slice(insertionIndex)\n                ];\n            }\n\n            const headerRow = document.getElementById('tableHeader');\n            const bodyRow = document.getElementById('tableBody');\n\n            headerRow.innerHTML = '';\n            tableColumns.forEach(col => {\n                const th = document.createElement('th');\n                th.className = \"px-6 py-3.5 tracking-wider uppercase font-semibold text-slate-500 dark:text-slate-400 border-b border-slate-200 dark:border-slate-700 cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-900 transition\";\n                th.innerHTML = `<span class=\"flex items-center gap-1.5\">${col} <i data-lucide=\"chevrons-up-down\" class=\"w-3.5 h-3.5 text-slate-400\"><\/i><\/span>`;\n                headerRow.appendChild(th);\n            });\n            lucide.createIcons();\n\n            const totalRows = searchedData.length;\n            const maxPage = Math.max(1, Math.ceil(totalRows \/ tableRowsPerPage));\n            \n            if (tableCurrentPage > maxPage) tableCurrentPage = maxPage;\n            \n            const startIdx = (tableCurrentPage - 1) * tableRowsPerPage;\n            const endIdx = Math.min(startIdx + tableRowsPerPage, totalRows);\n\n            document.getElementById('pageIndicator').textContent = `\u0e2b\u0e19\u0e49\u0e32 ${tableCurrentPage} \/ ${maxPage}`;\n            document.getElementById('tableCounter').innerHTML = `\u0e01\u0e33\u0e25\u0e31\u0e07\u0e41\u0e2a\u0e14\u0e07\u0e41\u0e16\u0e27\u0e17\u0e35\u0e48 <b>${totalRows === 0 ? 0 : startIdx + 1} - ${endIdx}<\/b> \u0e08\u0e32\u0e01\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 <b>${totalRows.toLocaleString()}<\/b> \u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 (\u0e01\u0e23\u0e2d\u0e07\u0e08\u0e32\u0e01\u0e14\u0e34\u0e1a\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14 ${rawData.length})`;\n            \n            document.getElementById('btnPrevPage').disabled = tableCurrentPage === 1;\n            document.getElementById('btnNextPage').disabled = tableCurrentPage === maxPage;\n\n            bodyRow.innerHTML = '';\n            \n            if (totalRows === 0) {\n                const tr = document.createElement('tr');\n                const td = document.createElement('td');\n                td.colSpan = tableColumns.length;\n                td.className = \"px-6 py-10 text-center text-slate-400 dark:text-slate-500 font-medium\";\n                td.innerHTML = `<div class=\"flex flex-col items-center justify-center gap-2\"><i data-lucide=\"package-open\" class=\"w-8 h-8\"><\/i> \u0e44\u0e21\u0e48\u0e1e\u0e1a\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c\u0e15\u0e32\u0e21\u0e15\u0e31\u0e27\u0e01\u0e23\u0e2d\u0e07\u0e19\u0e35\u0e49<\/div>`;\n                tr.appendChild(td);\n                bodyRow.appendChild(tr);\n                lucide.createIcons();\n                return;\n            }\n\n            const pagedRecords = searchedData.slice(startIdx, endIdx);\n            pagedRecords.forEach((row, i) => {\n                const tr = document.createElement('tr');\n                tr.className = `border-b border-slate-100 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800\/50 transition duration-150`;\n\n                tableColumns.forEach(col => {\n                    const td = document.createElement('td');\n                    td.className = \"px-6 py-3 whitespace-nowrap font-medium text-slate-700 dark:text-slate-300\";\n                    \n                    const val = row[col];\n                    if (typeof val === 'number') {\n                        \/\/ Display raw number without formatting commas in tabular list\n                        td.textContent = val.toString();\n                        td.className += \" font-mono text-slate-900 dark:text-white\";\n                    } else {\n                        td.textContent = val !== null && val !== undefined ? String(val) : '-';\n                    }\n                    tr.appendChild(td);\n                });\n\n                bodyRow.appendChild(tr);\n            });\n        }\n\n        \/\/ Handle page navigation\n        function changePage(direction) {\n            const maxPage = Math.ceil(searchedData.length \/ tableRowsPerPage);\n            const newPage = tableCurrentPage + direction;\n            if (newPage >= 1 && newPage <= maxPage) {\n                tableCurrentPage = newPage;\n                renderInspectorTable(false);\n            }\n        }\n\n        \/\/ Safe Export filtered data as CSV file\n        function downloadCSV() {\n            if (searchedData.length === 0) {\n                showNotification('\u0e44\u0e21\u0e48\u0e21\u0e35\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2a\u0e48\u0e07\u0e2d\u0e2d\u0e01', 'error');\n                return;\n            }\n\n            const csv = Papa.unparse(searchedData);\n            const blob = new Blob([\"\\ufeff\" + csv], { type: 'text\/csv;charset=utf-8;' });\n            const link = document.createElement(\"a\");\n            \n            if (link.download !== undefined) {\n                const url = URL.createObjectURL(blob);\n                link.setAttribute(\"href\", url);\n                link.setAttribute(\"download\", `dashboard_export_${new Date().toISOString().slice(0,10)}.csv`);\n                link.style.visibility = 'hidden';\n                document.body.appendChild(link);\n                link.click();\n                document.body.removeChild(link);\n                showNotification('\u0e14\u0e32\u0e27\u0e19\u0e4c\u0e42\u0e2b\u0e25\u0e14\u0e44\u0e1f\u0e25\u0e4c CSV \u0e40\u0e23\u0e35\u0e22\u0e1a\u0e23\u0e49\u0e2d\u0e22\u0e41\u0e25\u0e49\u0e27!', 'success');\n            }\n        }\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Google Sheets Dynamic Dashboard (Educational Edition) \u0e02 &#8230; <a title=\"\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e32\u0e23\u0e2a\u0e19\u0e40\u0e17\u0e28\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\" class=\"read-more\" href=\"https:\/\/phrae1.go.th\/sites\/bigdata\/%e0%b8%82%e0%b9%89%e0%b8%ad%e0%b8%a1%e0%b8%b9%e0%b8%a5%e0%b8%aa%e0%b8%b2%e0%b8%a3%e0%b8%aa%e0%b8%99%e0%b9%80%e0%b8%97%e0%b8%a8%e0%b9%82%e0%b8%a3%e0%b8%87%e0%b9%80%e0%b8%a3%e0%b8%b5%e0%b8%a2%e0%b8%99\/\" aria-label=\"Read more about \u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e32\u0e23\u0e2a\u0e19\u0e40\u0e17\u0e28\u0e42\u0e23\u0e07\u0e40\u0e23\u0e35\u0e22\u0e19\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":797,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"class_list":["post-799","page","type-page","status-publish"],"_links":{"self":[{"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/pages\/799","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/comments?post=799"}],"version-history":[{"count":2,"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/pages\/799\/revisions"}],"predecessor-version":[{"id":801,"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/pages\/799\/revisions\/801"}],"up":[{"embeddable":true,"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/pages\/797"}],"wp:attachment":[{"href":"https:\/\/phrae1.go.th\/sites\/wp-json\/wp\/v2\/media?parent=799"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}