{"id":21025,"date":"2026-08-12T14:37:33","date_gmt":"2026-08-12T14:37:33","guid":{"rendered":"https:\/\/jobsnocap.com\/?page_id=21025"},"modified":"2026-08-12T15:07:17","modified_gmt":"2026-08-12T15:07:17","slug":"dashboard","status":"publish","type":"page","link":"https:\/\/jobsnocap.com\/lo\/dashboard\/","title":{"rendered":"dashboard"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"21025\" class=\"elementor elementor-21025\" data-elementor-settings=\"{&quot;ha_cmc_init_switcher&quot;:&quot;no&quot;}\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f346b72 e-flex e-con-boxed e-con e-parent\" data-id=\"f346b72\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;_ha_eqh_enable&quot;:false}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d09061e elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"d09061e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>AI Dashboard Generator<\/title>\r\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\r\n    \r\n    <!-- Libraries for Exporting -->\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/html2pdf.js\/0.10.1\/html2pdf.bundle.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/gh\/gitbrent\/pptxgenjs@3.12.0\/libs\/jszip.min.js\"><\/script>\r\n    <script src=\"https:\/\/cdn.jsdelivr.net\/gh\/gitbrent\/pptxgenjs@3.12.0\/dist\/pptxgen.min.js\"><\/script>\r\n    \r\n    <link href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.0.0\/css\/all.min.css\" rel=\"stylesheet\">\r\n    <style>\r\n        .ui-option.selected { border-color: #2563eb; background-color: #eff6ff; }\r\n        .loader { border-top-color: #2563eb; -webkit-animation: spinner 1.5s linear infinite; animation: spinner 1.5s linear infinite; }\r\n        @keyframes spinner { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\r\n        .toggle-checkbox:checked { right: 0; border-color: #2563eb; }\r\n        .toggle-checkbox:checked + .toggle-label { background-color: #2563eb; }\r\n    <\/style>\r\n<\/head>\r\n<body class=\"bg-gray-50 text-gray-800 font-sans min-h-screen\">\r\n\r\n    <!-- Navbar -->\r\n    <nav class=\"bg-white shadow-sm border-b px-6 py-4 flex justify-between items-center relative z-50\">\r\n        <div class=\"flex items-center space-x-4\">\r\n            <h1 class=\"text-2xl font-bold text-blue-900\" id=\"app-title\">AI Dashboard Generator<\/h1>\r\n            <select id=\"lang-toggle\" onchange=\"toggleLanguage()\" class=\"bg-gray-100 border border-gray-300 text-gray-700 py-1 px-2 rounded focus:outline-none\">\r\n                <option value=\"en\">English<\/option>\r\n                <option value=\"lo\">\u0e9e\u0eb2\u0eaa\u0eb2\u0ea5\u0eb2\u0ea7 (Lao)<\/option>\r\n            <\/select>\r\n        <\/div>\r\n        <button onclick=\"openAdmin()\" class=\"text-gray-500 hover:text-gray-900 transition p-2 cursor-pointer\" title=\"Admin Settings\">\r\n            <i class=\"fas fa-cog text-2xl\"><\/i>\r\n        <\/button>\r\n    <\/nav>\r\n\r\n    <!-- Admin Modal -->\r\n    <div id=\"admin-modal\" class=\"hidden fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-[9999]\">\r\n        <div class=\"bg-white rounded-lg p-6 w-96 shadow-xl relative\">\r\n            <div class=\"flex justify-between items-center mb-4\">\r\n                <h2 class=\"text-xl font-bold\" id=\"admin-title\">Admin Settings<\/h2>\r\n                <button onclick=\"closeAdmin()\" class=\"text-gray-500 hover:text-red-500 cursor-pointer\"><i class=\"fas fa-times\"><\/i><\/button>\r\n            <\/div>\r\n            \r\n            <div id=\"auth-section\">\r\n                <p class=\"text-sm text-gray-600 mb-2\" id=\"admin-desc\">Enter password to configure shared system API key.<\/p>\r\n                <div class=\"relative mb-3\">\r\n                    <input type=\"password\" id=\"admin-pass\" placeholder=\"Password\" class=\"w-full border p-2 rounded focus:outline-blue-500 pr-10\">\r\n                    <button onclick=\"toggleVisibility('admin-pass', 'eye-icon-pass')\" class=\"absolute inset-y-0 right-0 px-3 flex items-center text-gray-500 hover:text-blue-600 cursor-pointer\">\r\n                        <i class=\"fas fa-eye\" id=\"eye-icon-pass\"><\/i>\r\n                    <\/button>\r\n                <\/div>\r\n                <button onclick=\"checkPassword()\" class=\"w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700\">Login<\/button>\r\n                <p id=\"auth-error\" class=\"text-red-500 text-sm mt-2 hidden\">Incorrect password.<\/p>\r\n            <\/div>\r\n\r\n            <div id=\"settings-section\" class=\"hidden\">\r\n                <p class=\"text-xs text-green-600 mb-2 font-medium\"><i class=\"fas fa-check-circle\"><\/i> Authenticated as Admin. Keys are saved globally for all general users.<\/p>\r\n                <label class=\"block text-sm font-medium text-gray-700 mb-1\">Google AI Studio API Key<\/label>\r\n                \r\n                <div class=\"relative mb-4\">\r\n                    <input type=\"password\" id=\"api-key\" placeholder=\"Enter new API key to update...\" class=\"w-full border p-2 rounded focus:outline-blue-500 pr-10\">\r\n                    <button onclick=\"toggleVisibility('api-key', 'eye-icon-api')\" class=\"absolute inset-y-0 right-0 px-3 flex items-center text-gray-500 hover:text-blue-600 cursor-pointer\">\r\n                        <i class=\"fas fa-eye\" id=\"eye-icon-api\"><\/i>\r\n                    <\/button>\r\n                <\/div>\r\n                \r\n                <button onclick=\"saveSettings()\" class=\"w-full bg-green-600 text-white p-2 rounded hover:bg-green-700\">Save System Key<\/button>\r\n                <button onclick=\"clearSettings()\" class=\"w-full bg-red-50 text-red-600 p-2 rounded hover:bg-red-100 mt-2 text-sm border border-red-200\">Reset to Default Key<\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Main Content -->\r\n    <main class=\"max-w-7xl mx-auto p-6 grid grid-cols-1 lg:grid-cols-3 gap-6 relative z-10\">\r\n        \r\n        <!-- Input Controls (Left Column) -->\r\n        <div class=\"lg:col-span-1 bg-white p-6 rounded-lg shadow-sm border h-fit\">\r\n            <h2 class=\"text-lg font-bold mb-4\" id=\"lbl-upload\">1. Data Source & Context<\/h2>\r\n            \r\n            <label class=\"block text-sm font-medium text-gray-700 mb-1\" id=\"lbl-files\">Upload Files (Images, PDF, Word, PPT, CSV)<\/label>\r\n            <input type=\"file\" id=\"file-upload\" accept=\".pdf,.png,.jpg,.jpeg,.csv,.docx,.pptx\" multiple class=\"block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 mb-4\"\/>\r\n            \r\n            <label class=\"block text-sm font-medium text-gray-700 mb-1\" id=\"lbl-prompt\">Prompt \/ Context<\/label>\r\n            <textarea id=\"user-prompt\" rows=\"3\" class=\"w-full border border-gray-300 rounded p-2 focus:outline-blue-500 mb-4\" placeholder=\"E.g., Analyze this report and create executive metrics...\"><\/textarea>\r\n\r\n            <!-- Explanations Toggle -->\r\n            <div class=\"flex items-center mb-6 p-3 bg-blue-50 rounded-lg border border-blue-100\">\r\n                <div class=\"relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in\">\r\n                    <input type=\"checkbox\" name=\"toggle\" id=\"toggle-explanation\" class=\"toggle-checkbox absolute block w-5 h-5 rounded-full bg-white border-4 appearance-none cursor-pointer border-gray-300 z-10 transition-transform duration-200 ease-in-out\"\/>\r\n                    <label for=\"toggle-explanation\" class=\"toggle-label block overflow-hidden h-5 rounded-full bg-gray-300 cursor-pointer transition-colors duration-200 ease-in-out\"><\/label>\r\n                <\/div>\r\n                <label for=\"toggle-explanation\" class=\"text-sm font-semibold text-blue-900 cursor-pointer\" id=\"lbl-explanation\">Include AI Explanations & Data Insights<\/label>\r\n            <\/div>\r\n\r\n            <h2 class=\"text-lg font-bold mb-4\" id=\"lbl-ui\">2. Select UI Components<\/h2>\r\n            <p class=\"text-xs text-gray-500 mb-3\" id=\"lbl-ui-desc\">Select components or leave blank for AI auto-selection.<\/p>\r\n            \r\n            <div class=\"grid grid-cols-2 gap-3 mb-6\" id=\"ui-grid\">\r\n                <!-- JS Populated -->\r\n            <\/div>\r\n\r\n            <button onclick=\"generateDashboard()\" id=\"btn-generate\" class=\"w-full bg-blue-900 text-white font-bold py-3 rounded-lg shadow hover:bg-blue-800 transition\">\r\n                Generate Dashboard\r\n            <\/button>\r\n            \r\n            <div id=\"loading\" class=\"hidden mt-4 flex items-center justify-center space-x-2\">\r\n                <div class=\"loader border-4 border-gray-200 rounded-full w-6 h-6\"><\/div>\r\n                <span class=\"text-gray-600 text-sm\" id=\"lbl-loading\">AI is analyzing documents...<\/span>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Output Dashboard (Right Column) -->\r\n        <div class=\"lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border min-h-[600px] flex flex-col\">\r\n            <div class=\"flex justify-between items-center mb-6 border-b pb-4\">\r\n                <h2 class=\"text-2xl font-bold text-gray-800\" id=\"dash-title\">Generated Dashboard<\/h2>\r\n                \r\n                <!-- Advanced Export Dropdown -->\r\n                <div class=\"relative inline-block text-left\">\r\n                    <button onclick=\"toggleExportMenu()\" class=\"text-sm bg-blue-50 text-blue-700 border border-blue-200 px-4 py-2 rounded shadow-sm hover:bg-blue-100 font-medium flex items-center transition cursor-pointer\">\r\n                        <i class=\"fas fa-file-export mr-2\"><\/i> Export <i class=\"fas fa-chevron-down ml-2 text-xs\"><\/i>\r\n                    <\/button>\r\n                    <div id=\"export-menu\" class=\"hidden absolute right-0 mt-2 w-48 rounded-md shadow-xl bg-white ring-1 ring-black ring-opacity-5 z-50 overflow-hidden\">\r\n                        <div class=\"py-1 flex flex-col\">\r\n                            <button onclick=\"exportPDF()\" class=\"px-4 py-3 text-sm text-gray-700 hover:bg-gray-100 flex items-center text-left\"><i class=\"fas fa-file-pdf text-red-500 mr-3 w-4\"><\/i> PDF Document<\/button>\r\n                            <button onclick=\"exportWord()\" class=\"px-4 py-3 text-sm text-gray-700 hover:bg-gray-100 flex items-center text-left\"><i class=\"fas fa-file-word text-blue-500 mr-3 w-4\"><\/i> Word Document<\/button>\r\n                            <button onclick=\"exportPPT()\" class=\"px-4 py-3 text-sm text-gray-700 hover:bg-gray-100 flex items-center text-left\"><i class=\"fas fa-file-powerpoint text-orange-500 mr-3 w-4\"><\/i> PowerPoint<\/button>\r\n                            <button onclick=\"exportHTML()\" class=\"px-4 py-3 text-sm text-gray-700 hover:bg-gray-100 flex items-center text-left\"><i class=\"fas fa-file-code text-gray-500 mr-3 w-4\"><\/i> Raw HTML<\/button>\r\n                            <div class=\"border-t border-gray-100 my-1\"><\/div>\r\n                            <button onclick=\"exportCanva()\" class=\"px-4 py-3 text-sm text-gray-700 hover:bg-purple-50 hover:text-purple-700 font-medium flex items-center text-left\"><i class=\"fas fa-paint-brush text-purple-500 mr-3 w-4\"><\/i> Edit in Canva<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div id=\"dashboard-container\" class=\"grid grid-cols-1 md:grid-cols-2 gap-6 flex-grow\">\r\n                <div class=\"col-span-full flex items-center justify-center text-gray-400 h-full text-center px-4\">\r\n                    <p id=\"dash-empty\">Upload files or input a prompt, pick your preferred UI layouts, and click Generate.<\/p>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n    <\/main>\r\n\r\n    <script>\r\n        const DEFAULT_API_KEY = \"AQ.Ab8RN6LiOF7U2mMwYPzNZVfa44CZGqNRGsH4Vi5Pyc80d9ATYg\";\r\n\r\n        \/\/ --- Localization Dictionary ---\r\n        const i18n = {\r\n            en: {\r\n                title: \"AI Dashboard Generator\",\r\n                upload: \"1. Data Source & Context\",\r\n                files: \"Upload Files (Images, PDF, Word, PPT, CSV)\",\r\n                prompt: \"Prompt \/ Context\",\r\n                promptPlaceholder: \"E.g., Analyze this report and create executive metrics...\",\r\n                ui: \"2. Select UI Components\",\r\n                uiDesc: \"Select components or leave blank for AI auto-selection.\",\r\n                generate: \"Generate Dashboard\",\r\n                loading: \"AI is analyzing documents...\",\r\n                dashTitle: \"Generated Dashboard\",\r\n                dashEmpty: \"Upload files or input a prompt, pick your preferred UI layouts, and click Generate.\",\r\n                explanation: \"Include AI Explanations & Data Insights\"\r\n            },\r\n            lo: {\r\n                title: \"\u0ea5\u0eb0\u0e9a\u0ebb\u0e9a\u0eaa\u0ec9\u0eb2\u0e87\u0ec1\u0e9c\u0e87\u0edc\u0ec9\u0eb2\u0e9b\u0eb1\u0e94 AI\",\r\n                upload: \"1. \u0ec1\u0eab\u0ebc\u0ec8\u0e87\u0e82\u0ecd\u0ec9\u0ea1\u0eb9\u0e99 & \u0e9a\u0ecd\u0ea5\u0eb4\u0e9a\u0ebb\u0e94\",\r\n                files: \"\u0ead\u0eb1\u0e9a\u0ec2\u0eab\u0ebc\u0e94\u0ec0\u0ead\u0e81\u0eb0\u0eaa\u0eb2\u0e99 (\u0eae\u0eb9\u0e9a, PDF, Word, PPT, CSV)\",\r\n                prompt: \"\u0e84\u0eb3\u0eaa\u0eb1\u0ec8\u0e87 \/ \u0e9a\u0ecd\u0ea5\u0eb4\u0e9a\u0ebb\u0e94\",\r\n                promptPlaceholder: \"\u0e95\u0ebb\u0ea7\u0ea2\u0ec8\u0eb2\u0e87: \u0ea7\u0eb4\u0ec0\u0e84\u0eb2\u0eb0\u0e9a\u0ebb\u0e94\u0ea5\u0eb2\u0e8d\u0e87\u0eb2\u0e99\u0e99\u0eb5\u0ec9 \u0ec1\u0ea5\u0eb0 \u0eaa\u0ec9\u0eb2\u0e87\u0e95\u0ebb\u0ea7\u0e8a\u0eb5\u0ec9\u0ea7\u0eb1\u0e94\u0eab\u0ebc\u0eb1\u0e81...\",\r\n                ui: \"2. \u0ec0\u0ea5\u0eb7\u0ead\u0e81\u0eae\u0eb9\u0e9a\u0ec1\u0e9a\u0e9a UI\",\r\n                uiDesc: \"\u0ec0\u0ea5\u0eb7\u0ead\u0e81\u0ead\u0ebb\u0e87\u0e9b\u0eb0\u0e81\u0ead\u0e9a \u0eab\u0ebc\u0eb7 \u0e9b\u0eb0\u0eab\u0ea7\u0ec8\u0eb2\u0e87\u0ec4\u0ea7\u0ec9\u0ec3\u0eab\u0ec9 AI \u0ec0\u0ea5\u0eb7\u0ead\u0e81\u0ec3\u0eab\u0ec9\u0ead\u0eb1\u0e94\u0e95\u0eb0\u0ec2\u0e99\u0ea1\u0eb1\u0e94.\",\r\n                generate: \"\u0eaa\u0ec9\u0eb2\u0e87\u0ec1\u0e9c\u0e87\u0edc\u0ec9\u0eb2\u0e9b\u0eb1\u0e94\",\r\n                loading: \"AI \u0e81\u0eb3\u0ea5\u0eb1\u0e87\u0ea7\u0eb4\u0ec0\u0e84\u0eb2\u0eb0\u0ec0\u0ead\u0e81\u0eb0\u0eaa\u0eb2\u0e99...\",\r\n                dashTitle: \"\u0ec1\u0e9c\u0e87\u0edc\u0ec9\u0eb2\u0e9b\u0eb1\u0e94\u0e97\u0eb5\u0ec8\u0eaa\u0ec9\u0eb2\u0e87\u0e82\u0eb6\u0ec9\u0e99\",\r\n                dashEmpty: \"\u0ead\u0eb1\u0e9a\u0ec2\u0eab\u0ebc\u0e94\u0ec0\u0ead\u0e81\u0eb0\u0eaa\u0eb2\u0e99 \u0eab\u0ebc\u0eb7 \u0ec3\u0eaa\u0ec8\u0e84\u0eb3\u0eaa\u0eb1\u0ec8\u0e87, \u0ec0\u0ea5\u0eb7\u0ead\u0e81\u0eae\u0eb9\u0e9a\u0ec1\u0e9a\u0e9a UI \u0ec1\u0ea5\u0eb0 \u0e81\u0ebb\u0e94\u0eaa\u0ec9\u0eb2\u0e87\u0ec1\u0e9c\u0e87\u0edc\u0ec9\u0eb2\u0e9b\u0eb1\u0e94.\",\r\n                explanation: \"\u0ea5\u0ea7\u0ea1\u0ec0\u0ead\u0ebb\u0eb2\u0e84\u0eb3\u0ead\u0eb0\u0e97\u0eb4\u0e9a\u0eb2\u0e8d \u0ec1\u0ea5\u0eb0 \u0e82\u0ecd\u0ec9\u0ea1\u0eb9\u0e99\u0ec0\u0e8a\u0eb5\u0e87\u0ea5\u0eb6\u0e81\u0e88\u0eb2\u0e81 AI\"\r\n            }\r\n        };\r\n\r\n        \/\/ --- 12 Stable UI Options Catalog ---\r\n        const uiOptions = [\r\n            { id: \"metric\", name: \"Metric Cards\", icon: \"fa-th-large\" },\r\n            { id: \"bar\", name: \"Bar Chart\", icon: \"fa-chart-bar\" },\r\n            { id: \"line\", name: \"Trend Chart\", icon: \"fa-chart-line\" },\r\n            { id: \"pie\", name: \"Pie Chart\", icon: \"fa-chart-pie\" },\r\n            { id: \"doughnut\", name: \"Doughnut Chart\", icon: \"fa-circle-notch\" },\r\n            { id: \"radar\", name: \"Radar Chart\", icon: \"fa-chart-area\" },\r\n            { id: \"table\", name: \"Comparison Table\", icon: \"fa-table\" },\r\n            { id: \"list\", name: \"Bullet List\", icon: \"fa-list-ul\" },\r\n            { id: \"timeline\", name: \"Timeline\", icon: \"fa-stream\" },\r\n            { id: \"quote\", name: \"Blockquote\", icon: \"fa-quote-left\" },\r\n            { id: \"split\", name: \"Split Screen View\", icon: \"fa-columns\" },\r\n            { id: \"text\", name: \"Impact Summary\", icon: \"fa-file-alt\" }\r\n        ];\r\n\r\n        let selectedUIs = [];\r\n        let currentLang = 'en';\r\n\r\n        \/\/ Initialize UI Grid directly\r\n        const uiGrid = document.getElementById('ui-grid');\r\n        uiOptions.forEach(opt => {\r\n            const div = document.createElement('div');\r\n            div.className = \"ui-option border rounded p-3 cursor-pointer flex flex-col items-center justify-center hover:bg-gray-50 transition text-center text-sm text-gray-700\";\r\n            div.innerHTML = `<i class=\"fas ${opt.icon} text-xl mb-2 text-blue-500\"><\/i><span>${opt.name}<\/span>`;\r\n            div.onclick = () => {\r\n                div.classList.toggle('selected');\r\n                if (selectedUIs.includes(opt.id)) {\r\n                    selectedUIs = selectedUIs.filter(i => i !== opt.id);\r\n                } else {\r\n                    selectedUIs.push(opt.id);\r\n                }\r\n            };\r\n            uiGrid.appendChild(div);\r\n        });\r\n\r\n        \/\/ Toggle Switch functionality visually\r\n        const toggleCheckbox = document.getElementById('toggle-explanation');\r\n        toggleCheckbox.addEventListener('change', function() {\r\n            if(this.checked) {\r\n                this.style.transform = 'translateX(100%)';\r\n            } else {\r\n                this.style.transform = 'translateX(0)';\r\n            }\r\n        });\r\n\r\n        \/\/ --- Export Menu Logic ---\r\n        function toggleExportMenu() {\r\n            document.getElementById('export-menu').classList.toggle('hidden');\r\n        }\r\n\r\n        document.addEventListener('click', function(event) {\r\n            const menu = document.getElementById('export-menu');\r\n            if(menu) {\r\n                const button = menu.previousElementSibling;\r\n                if (!menu.contains(event.target) && button && !button.contains(event.target)) {\r\n                    menu.classList.add('hidden');\r\n                }\r\n            }\r\n        });\r\n\r\n        function exportPDF() {\r\n            document.getElementById('export-menu').classList.add('hidden');\r\n            const element = document.getElementById('dashboard-container');\r\n            const opt = {\r\n                margin:       0.3,\r\n                filename:     'AI_Dashboard_Export.pdf',\r\n                image:        { type: 'jpeg', quality: 0.98 },\r\n                html2canvas:  { scale: 2, useCORS: true },\r\n                jsPDF:        { unit: 'in', format: 'letter', orientation: 'portrait' }\r\n            };\r\n            html2pdf().set(opt).from(element).save();\r\n        }\r\n\r\n        function exportWord() {\r\n            document.getElementById('export-menu').classList.add('hidden');\r\n            const content = document.getElementById('dashboard-container').innerHTML;\r\n            const header = \"<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http:\/\/www.w3.org\/TR\/REC-html40'><head><meta charset='utf-8'><title>Export HTML To Doc<\/title><\/head><body>\";\r\n            const footer = \"<\/body><\/html>\";\r\n            const sourceHTML = header + content + footer;\r\n            \r\n            const source = 'data:application\/vnd.ms-word;charset=utf-8,' + encodeURIComponent(sourceHTML);\r\n            const fileDownload = document.createElement(\"a\");\r\n            document.body.appendChild(fileDownload);\r\n            fileDownload.href = source;\r\n            fileDownload.download = 'AI_Dashboard_Export.doc';\r\n            fileDownload.click();\r\n            document.body.removeChild(fileDownload);\r\n        }\r\n\r\n        function exportPPT() {\r\n            document.getElementById('export-menu').classList.add('hidden');\r\n            let pptx = new PptxGenJS();\r\n            let slide = pptx.addSlide();\r\n            slide.addText(\"AI Generated Dashboard\", { x:1, y:1, w:'80%', h:1, fontSize:24, bold:true, color:'363636' });\r\n            slide.addText(\"Use 'Export as PDF' for visual snapshot exports.\", { x:1, y:2.5, w:'80%', h:1, fontSize:14, color:'3b82f6' });\r\n            pptx.writeFile({ fileName: \"AI_Dashboard_Export.pptx\" });\r\n        }\r\n\r\n        function exportHTML() {\r\n            document.getElementById('export-menu').classList.add('hidden');\r\n            const content = document.documentElement.outerHTML;\r\n            const blob = new Blob([content], { type: 'text\/html' });\r\n            const url = URL.createObjectURL(blob);\r\n            const link = document.createElement('a');\r\n            link.href = url;\r\n            link.download = 'AI_Dashboard_Source.html';\r\n            link.click();\r\n        }\r\n\r\n        function exportCanva() {\r\n            document.getElementById('export-menu').classList.add('hidden');\r\n            alert(\"To edit in Canva:\\n1. Use 'Export as PDF'.\\n2. Open Canva and upload your PDF.\");\r\n            window.open('https:\/\/www.canva.com', '_blank');\r\n        }\r\n\r\n        \/\/ --- Localization Logic ---\r\n        function toggleLanguage() {\r\n            currentLang = document.getElementById('lang-toggle').value;\r\n            const t = i18n[currentLang];\r\n            document.getElementById('app-title').innerText = t.title;\r\n            document.getElementById('lbl-upload').innerText = t.upload;\r\n            document.getElementById('lbl-files').innerText = t.files;\r\n            document.getElementById('lbl-prompt').innerText = t.prompt;\r\n            document.getElementById('user-prompt').placeholder = t.promptPlaceholder;\r\n            document.getElementById('lbl-ui').innerText = t.ui;\r\n            document.getElementById('lbl-ui-desc').innerText = t.uiDesc;\r\n            document.getElementById('btn-generate').innerText = t.generate;\r\n            document.getElementById('lbl-loading').innerText = t.loading;\r\n            document.getElementById('dash-title').innerText = t.dashTitle;\r\n            document.getElementById('lbl-explanation').innerText = t.explanation;\r\n            if (document.getElementById('dash-empty')) {\r\n                document.getElementById('dash-empty').innerText = t.dashEmpty;\r\n            }\r\n        }\r\n\r\n        \/\/ --- Admin \/ Settings Logic ---\r\n        function openAdmin() { document.getElementById('admin-modal').classList.remove('hidden'); }\r\n        function closeAdmin() { \r\n            document.getElementById('admin-modal').classList.add('hidden'); \r\n            document.getElementById('auth-section').classList.remove('hidden');\r\n            document.getElementById('settings-section').classList.add('hidden');\r\n            document.getElementById('admin-pass').value = '';\r\n            document.getElementById('api-key').value = ''; \r\n            document.getElementById('auth-error').classList.add('hidden');\r\n            document.getElementById('admin-pass').type = \"password\";\r\n            document.getElementById('eye-icon-pass').className = \"fas fa-eye\";\r\n            document.getElementById('api-key').type = \"password\";\r\n            document.getElementById('eye-icon-api').className = \"fas fa-eye\";\r\n        }\r\n        \r\n        function checkPassword() {\r\n            if (document.getElementById('admin-pass').value === '1243') {\r\n                document.getElementById('auth-section').classList.add('hidden');\r\n                document.getElementById('settings-section').classList.remove('hidden');\r\n                document.getElementById('api-key').value = ''; \r\n            } else {\r\n                document.getElementById('auth-error').classList.remove('hidden');\r\n            }\r\n        }\r\n\r\n        function toggleVisibility(inputId, iconId) {\r\n            const input = document.getElementById(inputId);\r\n            const icon = document.getElementById(iconId);\r\n            if (input.type === \"password\") {\r\n                input.type = \"text\";\r\n                icon.className = \"fas fa-eye-slash\";\r\n            } else {\r\n                input.type = \"password\";\r\n                icon.className = \"fas fa-eye\";\r\n            }\r\n        }\r\n\r\n        function saveSettings() {\r\n            const key = document.getElementById('api-key').value.trim();\r\n            if (key) {\r\n                localStorage.setItem('shared_system_gemini_api_key', key);\r\n                alert('System API key successfully updated!');\r\n            }\r\n            closeAdmin();\r\n        }\r\n\r\n        function clearSettings() {\r\n            localStorage.removeItem('shared_system_gemini_api_key');\r\n            alert('Custom API key cleared. Reverting to default API key.');\r\n            closeAdmin();\r\n        }\r\n\r\n        \/\/ --- File Conversion to Base64 ---\r\n        const fileToBase64 = (file) => new Promise((resolve, reject) => {\r\n            const reader = new FileReader();\r\n            reader.readAsDataURL(file);\r\n            reader.onload = () => resolve(reader.result.split(',')[1]);\r\n            reader.onerror = error => reject(error);\r\n        });\r\n\r\n        \/\/ --- Core Generation Logic ---\r\n        async function generateDashboard() {\r\n            const apiKey = localStorage.getItem('shared_system_gemini_api_key') || DEFAULT_API_KEY;\r\n            const promptText = document.getElementById('user-prompt').value;\r\n            const fileInput = document.getElementById('file-upload');\r\n            const requiresExplanation = document.getElementById('toggle-explanation').checked;\r\n            \r\n            document.getElementById('loading').classList.remove('hidden');\r\n            document.getElementById('btn-generate').disabled = true;\r\n            \r\n            try {\r\n                let parts = [];\r\n                let explanationInstruction = requiresExplanation ? \"\\nCRITICAL: Include explicit explanations of trends and featured numbers.\" : \"\";\r\n\r\n                const sysInstruction = `You are an expert data analyst and communications specialist. Process the provided text or files using OCR\/multimodal parsing. Return ONLY a valid JSON object. \r\n                Do NOT wrap the JSON inside markdown code tags like \\`\\`\\`json. \r\n                Language of output text must be strictly: ${currentLang === 'en' ? 'English' : 'Lao language script (\u0e9e\u0eb2\u0eaa\u0eb2\u0ea5\u0eb2\u0ea7)'}.\r\n                The output tone must be concise, logical, and impact-oriented.\r\n                Preferred UI widgets to include: ${selectedUIs.length > 0 ? selectedUIs.join(', ') : 'Choose the best components automatically'}. ${explanationInstruction}\r\n                \r\n                Strict JSON structure required:\r\n                {\r\n                    \"components\": [\r\n                        { \"type\": \"metric\", \"title\": \"Total Savings\", \"value\": \"$124k\", \"subtitle\": \"+12% increase\" },\r\n                        { \"type\": \"bar\", \"title\": \"Monthly Revenue\", \"labels\": [\"Jan\", \"Feb\"], \"data\": [40, 60] },\r\n                        { \"type\": \"line\", \"title\": \"Growth Trend\", \"labels\": [\"Q1\", \"Q2\"], \"data\": [10, 35] },\r\n                        { \"type\": \"pie\", \"title\": \"Market Share\", \"labels\": [\"Product A\", \"Product B\", \"Product C\"], \"data\": [50, 30, 20] },\r\n                        { \"type\": \"doughnut\", \"title\": \"Budget Allocation\", \"labels\": [\"HR\", \"Marketing\", \"Dev\"], \"data\": [40, 20, 40] },\r\n                        { \"type\": \"radar\", \"title\": \"Performance Metrics\", \"labels\": [\"Speed\", \"Cost\", \"Quality\", \"Reach\"], \"data\": [80, 60, 90, 70] },\r\n                        { \"type\": \"table\", \"title\": \"Comparison Table\", \"headers\": [\"Indicator\", \"Baseline\", \"Target\"], \"rows\": [[\"Row 1\", \"50\", \"100\"]] },\r\n                        { \"type\": \"list\", \"title\": \"Key Takeaways\", \"items\": [\"Item 1\", \"Item 2\", \"Item 3\"] },\r\n                        { \"type\": \"timeline\", \"title\": \"Project Phases\", \"steps\": [{\"date\": \"Q1 2026\", \"desc\": \"Planning\"}, {\"date\": \"Q2 2026\", \"desc\": \"Execution\"}] },\r\n                        { \"type\": \"quote\", \"title\": \"Client Feedback\", \"content\": \"This project exceeded our expectations in every way.\", \"author\": \"John Doe\" },\r\n                        { \"type\": \"text\", \"title\": \"Executive Summary\", \"content\": \"Detailed descriptive insights here...\" }\r\n                    ]\r\n                }`;\r\n                \r\n                parts.push({ text: sysInstruction + \"\\n\\nUser Context\/Instructions: \" + promptText });\r\n\r\n                if (fileInput.files.length > 0) {\r\n                    for (const file of fileInput.files) {\r\n                        if (file.name.endsWith('.csv') || file.name.endsWith('.txt')) {\r\n                            const text = await file.text();\r\n                            parts.push({ text: `File Content (${file.name}):\\n` + text });\r\n                        } else {\r\n                            const base64 = await fileToBase64(file);\r\n                            let mimeType = file.type || 'application\/octet-stream';\r\n                            if (file.name.endsWith('.pdf')) mimeType = 'application\/pdf';\r\n                            else if (file.name.endsWith('.docx')) mimeType = 'application\/vnd.openxmlformats-officedocument.wordprocessingml.document';\r\n                            else if (file.name.endsWith('.pptx')) mimeType = 'application\/vnd.openxmlformats-officedocument.presentationml.presentation';\r\n                            \r\n                            parts.push({\r\n                                inlineData: { mimeType: mimeType, data: base64 }\r\n                            });\r\n                        }\r\n                    }\r\n                }\r\n\r\n                const response = await fetch(`https:\/\/generativelanguage.googleapis.com\/v1beta\/models\/gemini-3.5-flash:generateContent?key=${apiKey}`, {\r\n                    method: 'POST',\r\n                    headers: { 'Content-Type': 'application\/json' },\r\n                    body: JSON.stringify({\r\n                        contents: [{ parts: parts }],\r\n                        generationConfig: { temperature: 0.2 }\r\n                    })\r\n                });\r\n\r\n                const data = await response.json();\r\n                if (data.error) throw new Error(data.error.message);\r\n                \r\n                let jsonStr = data.candidates[0].content.parts[0].text;\r\n                jsonStr = jsonStr.replace(\/```json\/g, '').replace(\/```\/g, '').trim();\r\n                const dashboardData = JSON.parse(jsonStr);\r\n\r\n                renderDashboard(dashboardData.components);\r\n\r\n            } catch (error) {\r\n                console.error(error);\r\n                alert(\"Generation error: \" + error.message);\r\n            } finally {\r\n                document.getElementById('loading').classList.add('hidden');\r\n                document.getElementById('btn-generate').disabled = false;\r\n            }\r\n        }\r\n\r\n        \/\/ --- Render UI Components Dynamically ---\r\n        function renderDashboard(components) {\r\n            const container = document.getElementById('dashboard-container');\r\n            container.innerHTML = ''; \r\n            const colorPalette = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#06b6d4'];\r\n\r\n            components.forEach((comp, index) => {\r\n                const wrapper = document.createElement('div');\r\n                \r\n                if (comp.type === 'metric') {\r\n                    wrapper.className = \"bg-white p-6 border rounded-xl shadow-sm text-center flex flex-col justify-center\";\r\n                    wrapper.innerHTML = `\r\n                        <h3 class=\"text-gray-500 text-sm font-medium mb-1\">${comp.title}<\/h3>\r\n                        <p class=\"text-3xl font-bold text-blue-900\">${comp.value}<\/p>\r\n                        ${comp.subtitle ? `<p class=\"text-xs text-green-600 mt-2 font-medium\"><i class=\"fas fa-arrow-up\"><\/i> ${comp.subtitle}<\/p>` : ''}\r\n                    `;\r\n                } \r\n                else if (['bar', 'line', 'pie', 'doughnut', 'radar'].includes(comp.type)) {\r\n                    wrapper.className = \"bg-white p-4 border rounded-xl shadow-sm col-span-full md:col-span-1\";\r\n                    wrapper.innerHTML = `<h3 class=\"font-bold text-gray-700 mb-3 text-sm\">${comp.title}<\/h3><div class=\"relative h-48\"><canvas id=\"chart-${index}\"><\/canvas><\/div>`;\r\n                    container.appendChild(wrapper);\r\n                    \r\n                    const isCircular = comp.type === 'pie' || comp.type === 'doughnut';\r\n                    const chartColors = isCircular ? colorPalette.slice(0, comp.data.length) : '#3b82f6';\r\n                    const chartBorder = isCircular ? '#ffffff' : '#1d4ed8';\r\n\r\n                    new Chart(document.getElementById(`chart-${index}`), {\r\n                        type: comp.type,\r\n                        data: {\r\n                            labels: comp.labels,\r\n                            datasets: [{\r\n                                label: comp.title,\r\n                                data: comp.data,\r\n                                backgroundColor: chartColors,\r\n                                borderColor: chartBorder,\r\n                                borderWidth: 1,\r\n                                borderRadius: comp.type === 'bar' ? 4 : 0\r\n                            }]\r\n                        },\r\n                        options: { \r\n                            responsive: true, \r\n                            maintainAspectRatio: false,\r\n                            plugins: {\r\n                                legend: { display: isCircular || comp.type === 'radar' }\r\n                            }\r\n                        }\r\n                    });\r\n                    return; \r\n                }\r\n                else if (comp.type === 'table') {\r\n                    wrapper.className = \"bg-white p-4 border rounded-xl shadow-sm col-span-full overflow-x-auto\";\r\n                    let headersHtml = comp.headers.map(h => `<th class=\"px-4 py-2 bg-gray-50 text-left text-xs font-semibold text-gray-600 uppercase\">${h}<\/th>`).join('');\r\n                    let rowsHtml = comp.rows.map(row => `<tr>` + row.map(cell => `<td class=\"px-4 py-2 border-t text-sm text-gray-700\">${cell}<\/td>`).join('') + `<\/tr>`).join('');\r\n                    \r\n                    wrapper.innerHTML = `\r\n                        <h3 class=\"font-bold text-gray-700 mb-3 text-sm\">${comp.title}<\/h3>\r\n                        <table class=\"min-w-full divide-y divide-gray-200\">\r\n                            <thead><tr>${headersHtml}<\/tr><\/thead>\r\n                            <tbody>${rowsHtml}<\/tbody>\r\n                        <\/table>\r\n                    `;\r\n                }\r\n                else if (comp.type === 'list') {\r\n                    wrapper.className = \"bg-white p-5 border rounded-xl shadow-sm col-span-full md:col-span-1\";\r\n                    let listHtml = comp.items.map(item => `<li class=\"mb-2 flex items-start\"><i class=\"fas fa-check-circle text-green-500 mt-1 mr-2 text-sm\"><\/i><span class=\"text-gray-700 text-sm\">${item}<\/span><\/li>`).join('');\r\n                    wrapper.innerHTML = `\r\n                        <h3 class=\"font-bold text-gray-700 mb-3 text-sm\">${comp.title}<\/h3>\r\n                        <ul class=\"list-none\">${listHtml}<\/ul>\r\n                    `;\r\n                }\r\n                else if (comp.type === 'timeline') {\r\n                    wrapper.className = \"bg-white p-5 border rounded-xl shadow-sm col-span-full\";\r\n                    let stepsHtml = comp.steps.map(step => `\r\n                        <div class=\"flex flex-col md:flex-row mb-4\">\r\n                            <div class=\"md:w-1\/4 text-sm font-bold text-blue-600\">${step.date}<\/div>\r\n                            <div class=\"md:w-3\/4 text-sm text-gray-700 border-l-2 border-blue-200 pl-4 relative\">\r\n                                <div class=\"absolute w-3 h-3 bg-blue-500 rounded-full -left-[7px] top-1\"><\/div>\r\n                                ${step.desc}\r\n                            <\/div>\r\n                        <\/div>\r\n                    `).join('');\r\n                    wrapper.innerHTML = `\r\n                        <h3 class=\"font-bold text-gray-700 mb-4 text-sm\">${comp.title}<\/h3>\r\n                        <div class=\"mt-2\">${stepsHtml}<\/div>\r\n                    `;\r\n                }\r\n                else if (comp.type === 'quote') {\r\n                    wrapper.className = \"bg-gradient-to-r from-blue-50 to-indigo-50 p-6 border-l-4 border-blue-500 rounded-r-xl shadow-sm col-span-full\";\r\n                    wrapper.innerHTML = `\r\n                        <i class=\"fas fa-quote-left text-3xl text-blue-200 mb-2\"><\/i>\r\n                        <p class=\"text-gray-800 text-lg italic mb-3\">\"${comp.content}\"<\/p>\r\n                        <p class=\"text-sm font-bold text-gray-600\">\u2014 ${comp.author || ''}<\/p>\r\n                    `;\r\n                }\r\n                else {\r\n                    wrapper.className = \"bg-blue-50 p-5 border border-blue-100 rounded-xl col-span-full shadow-sm\";\r\n                    wrapper.innerHTML = `\r\n                        <h3 class=\"font-bold text-blue-900 text-base mb-2\"><i class=\"fas fa-info-circle mr-2\"><\/i>${comp.title}<\/h3>\r\n                        <p class=\"text-gray-700 leading-relaxed text-sm whitespace-pre-line\">${comp.content}<\/p>\r\n                    `;\r\n                }\r\n\r\n                container.appendChild(wrapper);\r\n            });\r\n        }\r\n    <\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>AI Dashboard Generator AI Dashboard Generator English\u0e9e\u0eb2\u0eaa\u0eb2\u0ea5\u0eb2\u0ea7 (Lao) Admin Settings Enter password to configure shared system API key. Login Incorrect password. Authenticated as Admin. Keys are saved globally for all general users. Google AI Studio API Key Save System Key Reset to Default Key 1. Data Source &#038; Context Upload Files (Images, PDF, Word, PPT, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":186,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":"","_members_access_role":[],"_members_access_error":""},"class_list":["post-21025","page","type-page","status-publish","has-post-thumbnail","hentry"],"acf":[],"a3_pvc":{"activated":false,"total_views":0,"today_views":0},"_links":{"self":[{"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/pages\/21025","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/comments?post=21025"}],"version-history":[{"count":16,"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/pages\/21025\/revisions"}],"predecessor-version":[{"id":21041,"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/pages\/21025\/revisions\/21041"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/media\/186"}],"wp:attachment":[{"href":"https:\/\/jobsnocap.com\/lo\/wp-json\/wp\/v2\/media?parent=21025"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}