{"id":16879,"date":"2026-05-04T06:34:17","date_gmt":"2026-05-04T06:34:17","guid":{"rendered":"https:\/\/palapaservicecenter.com\/?page_id=16879"},"modified":"2026-05-05T09:54:06","modified_gmt":"2026-05-05T09:54:06","slug":"live-update","status":"publish","type":"page","link":"https:\/\/palapaservicecenter.com\/en\/live-update\/","title":{"rendered":"Live Update Unit Masuk"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"16879\" class=\"elementor elementor-16879\">\n\t\t\t\t<div class=\"elementor-element elementor-element-900c4cf e-con-full e-flex e-con e-parent\" data-id=\"900c4cf\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-95c61a7 elementor-widget__width-inherit elementor-widget elementor-widget-shortcode\" data-id=\"95c61a7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\r\n    <style>\r\n        .ps-wrapper { font-family: \"Inter\", sans-serif; color: #334155; box-sizing: border-box; }\r\n        .ps-search-form { display: flex; align-items: center; background: #fff; padding: 4px 4px 4px 15px; border-radius: 50px; border: 1px solid #e2e8f0; box-shadow: 0 4px 12px rgba(0,0,0,0.02); margin-bottom: 15px; }\r\n        .ps-input { flex: 1; border: none !important; outline: none !important; font-size: 1rem; background: transparent; width: 100%; }\r\n        .ps-btn { background: #0056b3; color: #fff; border: none; border-radius: 40px; padding: 10px 25px; font-size: 0.95rem; font-weight: 700; cursor: pointer; transition: 0.3s; white-space: nowrap; }\r\n        .ps-btn:hover { background: #004494; transform: translateY(-1px); }\r\n        .ps-btn:disabled { background: #94a3b8; cursor: not-allowed; transform: none; }\r\n        \r\n        .ps-results-wrapper { display: none; }\r\n        .ps-results-wrapper.active { display: flex; animation: fadeIn 0.4s ease; }\r\n        .ps-result-card { background: #fff; border: 1px solid #e2e8f0; border-radius: 16px; padding: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.03); display: flex; flex-direction: column; }\r\n        .ps-highlight-box { font-size: 0.95rem; font-weight: 600; line-height: 1.5; color: #1e293b; }\r\n        .ps-highlight-box b { color: #0056b3; background: #eff6ff; padding: 2px 6px; border-radius: 4px; }\r\n        .ps-diagnosa-box { background: #fffbeb; border-left: 4px solid #f59e0b; padding: 10px 12px; border-radius: 6px; font-size: 0.85rem; line-height: 1.4; margin-bottom: 10px;}\r\n        .ps-sop-box { background: #f8fafc; border-left: 4px solid #3b82f6; padding: 10px 12px; border-radius: 6px; font-size: 0.8rem; line-height: 1.4; color: #475569; margin-bottom: 0;}\r\n        .ps-section-title { font-size: 0.75rem; font-weight: 800; color: #94a3b8; text-transform: uppercase; margin-bottom: 12px; flex-shrink: 0; }\r\n        \r\n        .brand-btn { cursor: pointer; transition: 0.2s; background: var(--brand-bg,#eff6ff); border: 1px solid var(--brand-border,#bfdbfe); color: var(--brand-color,#1e3a8a); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; margin-bottom: 5px; }\r\n        .brand-btn.active { background: var(--brand-color,#3b82f6)!important; color: #fff!important; }\r\n        \r\n        .ps-custom-scroll::-webkit-scrollbar { height: 6px; width: 6px; }\r\n        .ps-custom-scroll::-webkit-scrollbar-track { background: transparent; border-radius: 4px; }\r\n        .ps-custom-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }\r\n        .ps-custom-scroll::-webkit-scrollbar-thumb:hover { background: #94a3b8; }\r\n        \r\n        @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }\r\n        @keyframes pulseGreen { 0% { box-shadow: 0 0 0 0 rgba(16,185,129,0.4); } 70% { box-shadow: 0 0 0 6px rgba(16,185,129,0); } 100% { box-shadow: 0 0 0 0 rgba(16,185,129,0); } }\r\n\r\n        \/* =================================================================\r\n           NEW DASHBOARD 2x2 GRID (Berlaku untuk Kiosk & Desktop)\r\n           ================================================================= *\/\r\n        .ps-dashboard-grid { \r\n            display: grid; \r\n            grid-template-columns: 35fr 65fr; \/* 2 Kolom: Kiri (Total+Tren) 35%, Kanan (Filter+Bar) 65% *\/\r\n            gap: 15px; \r\n            flex: 1; \r\n            min-height: 0; \r\n            width: 100%; \r\n        }\r\n        .ps-dash-col { \r\n            display: flex; \r\n            flex-direction: column; \r\n            gap: 15px; \r\n            min-height: 0; \r\n        }\r\n        .ps-scroll-area { \r\n            flex: 1; \r\n            overflow-y: auto !important; \r\n            background: #fafbfc; \r\n            border-radius: 12px; \r\n            padding: 10px; \r\n            border: 1px solid #f1f5f9; \r\n            min-height: 0; \r\n        }\r\n        .ps-chart-main { \r\n            flex: 1; \r\n            position: relative; \r\n            min-height: 0; \r\n            width: 100%; \r\n        }\r\n\r\n        \/* MODE PUBLIC (Berdiri sendiri tidak dibatasi 90vh) *\/\r\n        .ps-wrapper.mode-public { width: 100%; max-width: 1200px; margin: 0 auto; padding: 10px 0; background: transparent; }\r\n        .ps-wrapper.mode-public .ps-results-wrapper { display: flex; flex-direction: column; }\r\n        .ps-wrapper.mode-public .ps-dashboard-grid { grid-template-columns: 1fr; } \/* Vertikal di layar kecil *\/\r\n        .ps-wrapper.mode-public .ps-chart-main { height: 350px; flex: none; }\r\n        .ps-wrapper.mode-public .ps-scroll-area { max-height: 500px; flex: none; }\r\n\r\n        @media (min-width: 992px) {\r\n            .ps-wrapper.mode-public .ps-dashboard-grid { grid-template-columns: 35fr 65fr; } \/* 2 Kolom di layar besar *\/\r\n        }\r\n\r\n        \/* =================================================================\r\n           MODE KIOSK (STRICT 90VH)\r\n           ================================================================= *\/\r\n        .ps-wrapper.mode-kiosk { \r\n            position: relative; \r\n            left: 50%; \r\n            transform: translateX(-50%); \r\n            width: 100vw !important; \r\n            max-width: 100vw !important; \r\n            \r\n            \/* DIBUAT FIX MAX DI 90VH AGAR TIDAK MELAR *\/\r\n            height: 90vh !important; \r\n            max-height: 90vh !important; \r\n            \r\n            padding: 1.5vw; \r\n            background: #f8fafc; \r\n            display: flex; \r\n            flex-direction: column; \r\n            overflow: hidden; \r\n        }\r\n        .ps-wrapper.mode-kiosk .ps-results-wrapper { \r\n            flex: 1; \r\n            display: flex;\r\n            flex-direction: column; \r\n            min-height: 0; \r\n        }\r\n\r\n        \/* KIOSK HEADER & CAROUSEL UNIT *\/\r\n        .ps-kiosk-top-row { flex: 0 0 auto; display: flex; align-items: center; gap: 15px; margin-bottom: 15px; width: 100%; transition: opacity 0.5s ease; }\r\n        .ps-kiosk-inline-badge { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; background: #0f172a; color: #fff; padding: 12px 20px; border-radius: 12px; border: 1px solid #1e293b; box-shadow: 0 4px 12px rgba(0,0,0,0.1); white-space: nowrap; }\r\n        .ps-carousel-container { flex: 1; display: flex; gap: 10px; overflow-x: auto; padding: 5px; scroll-behavior: smooth; -ms-overflow-style: none; scrollbar-width: none; }\r\n        .ps-carousel-container::-webkit-scrollbar { display: none; }\r\n        .ps-kiosk-card { flex: 0 0 auto; min-width: 140px; max-width: 190px; background: #fff; border: 1px solid #e2e8f0; border-radius: 10px; padding: 10px 12px; cursor: pointer; transition: 0.3s; position: relative; box-shadow: 0 2px 5px rgba(0,0,0,0.02); overflow: hidden; }\r\n        .ps-kiosk-card.active { border-color: #3b82f6; background: #eff6ff; box-shadow: 0 4px 12px rgba(59,130,246,0.15); transform: translateY(-2px); }\r\n        .ps-kiosk-card .cat-label { font-size: 0.6rem; font-weight: 800; color: #94a3b8; text-transform: uppercase; margin-bottom: 3px; }\r\n        .ps-kiosk-card .unit-name { font-size: 0.85rem; font-weight: 700; color: #1e293b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\r\n        .ps-kiosk-card.active .cat-label { color: #3b82f6; }\r\n        .ps-kiosk-card.active .unit-name { color: #1d4ed8; }\r\n        .ps-kiosk-card.disabled { opacity: 0.6; cursor: not-allowed; background: #f8fafc; border-color: #cbd5e1; }\r\n        .ps-kiosk-card.disabled .unit-name { text-decoration: line-through; color: #94a3b8; }\r\n        .ps-kiosk-status { font-size: 0.85rem; font-weight: 600; color: #64748b; margin: auto 0; padding-left: 10px; }\r\n        .ps-live-dot { display: inline-block; width: 10px; height: 10px; background: #10b981; border-radius: 50%; box-shadow: 0 0 10px #10b981; animation: pulseGreen 1.5s infinite; }\r\n        .ps-live-text { font-size: 0.85rem; font-weight: 800; color: #10b981; letter-spacing: 0.5px; }\r\n\r\n        \/* TAMPILAN 6 BANNER KIOSK *\/\r\n        .ps-kiosk-banner-wrapper { \r\n            display: none; \r\n            flex: 1; \r\n            width: 100%; \r\n            border-radius: 16px; \r\n            overflow: hidden; \r\n            box-shadow: 0 4px 25px rgba(0,0,0,0.05); \r\n            animation: fadeIn 0.8s ease; \r\n            background: transparent; \r\n            position: relative;\r\n        }\r\n        \r\n        #ps-kiosk-banner-img {\r\n            width: 100%;\r\n            height: 100%;\r\n            object-fit: contain; \r\n            opacity: 0.9;\r\n            transition: opacity 0.5s ease-in-out;\r\n        }\r\n\r\n        \/* RESPONSIVE KHUSUS KIOSK LAYAR KECIL *\/\r\n        @media (max-width:992px){\r\n            .ps-wrapper.mode-kiosk { height: auto !important; min-height: 100vh; overflow-y: auto; }\r\n            .ps-dashboard-grid { grid-template-columns: 1fr !important; }\r\n            .ps-wrapper.mode-kiosk .ps-chart-main { min-height: 250px; flex: none; }\r\n            .ps-wrapper.mode-kiosk .ps-scroll-area { min-height: 300px; flex: none; }\r\n            .ps-kiosk-top-row{flex-direction:column;gap:10px;align-items:stretch;}\r\n            .ps-kiosk-inline-badge{justify-content:center;}\r\n            .ps-search-form{flex-direction:column;padding:12px;border-radius:16px;gap:10px}\r\n            .ps-input{padding:8px 5px;text-align:center;}\r\n            .ps-btn{width:100%;border-radius:12px;}\r\n        }\r\n    <\/style>\r\n\r\n    <div class=\"ps-wrapper mode-kiosk\">\r\n        \r\n                    <div id=\"ps-kiosk-header\" class=\"ps-kiosk-top-row\">\r\n                <div class=\"ps-kiosk-inline-badge\">\r\n                    <span class=\"ps-live-dot\"><\/span>\r\n                    <span id=\"ps-kiosk-live-text\" class=\"ps-live-text\">10 UNIT TERBARU MASUK<\/span>\r\n                <\/div>\r\n                <div id=\"ps-kiosk-status\" class=\"ps-kiosk-status\">\ud83d\udce1 Sinkronisasi database Palapa AI...<\/div>\r\n                <div id=\"ps-kiosk-carousel\" class=\"ps-carousel-container\" style=\"display:none;\"><\/div>\r\n            <\/div>\r\n            \r\n            <div id=\"ps-kiosk-banner\" class=\"ps-kiosk-banner-wrapper\">\r\n                <img decoding=\"async\" id=\"ps-kiosk-banner-img\" src=\"\" alt=\"Promo Palapa\">\r\n            <\/div>\r\n            \r\n        \r\n        <div id=\"ps-loader\" style=\"display:none; text-align:center; padding: 20px; color: #0056b3; font-weight: bold; font-size: 0.85rem;\">\ud83d\udd0d Memetakan Hierarki Data...<\/div>\r\n        \r\n        <div id=\"ps-results-container\" class=\"ps-results-wrapper\">\r\n\t\t<div id=\"ps-results-container\" class=\"ps-results-wrapper\"><\/div>\r\n        \r\n        \r\n\t\t<\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n    let browserRAMCache = {};\r\n\r\n    \r\n            \/\/ --- LOGIKA KHUSUS MODE KIOSK ---\r\n        let kioskItems = [];\r\n        let activeKioskIndex = -1;\r\n        let radarPollingTimer = null;\r\n        let kioskWaitTimer = null; \r\n        let lastItemsString = '';\r\n        let isShowingBanner = false; \r\n        \r\n        \/\/ 6 GAMBAR BANNER KIOSK (Gambar 1 bulan di urutan PERTAMA)\r\n        const kioskBannerImages = [\r\n            \"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Slide-16_9-2-1.png\",\r\n            \"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Slide-16_9-3.png\",\r\n            \"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Slide-16_9-4.png\",\r\n            \"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Slide-16_9-5.png\",\r\n            \"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Slide-16_9-6.png\",\r\n\t\t\t\"https:\/\/palapaservicecenter.com\/wp-content\/uploads\/2026\/05\/Desktop-Informasi-kebijakan-pengambilan-unit-servis-maksimal-1-bulan-di-Palapa-Service-Center-1.png\"\r\n        ];\r\n        let currentBannerIdx = 0;\r\n        let bannerSlideTimer = null;\r\n\r\n        document.addEventListener(\"DOMContentLoaded\", function() {\r\n            console.log(\"Palapa Kiosk Initialize: OK\");\r\n            fetchKioskRadar();\r\n            radarPollingTimer = setInterval(fetchKioskRadar, 20000); \r\n        });\r\n\r\n        function fetchKioskRadar() {\r\n            let fd = new URLSearchParams(); fd.append('action', 'ps_live_radar');\r\n            fetch('https:\/\/palapaservicecenter.com\/wp-admin\/admin-ajax.php', { method: 'POST', body: fd })\r\n            .then(r => r.json())\r\n            .then(res => {\r\n                if(res.success && res.data && res.data.items && res.data.items.length > 0) {\r\n                    let currentString = res.data.items.map(i => i.id).join(',');\r\n                    if(currentString !== lastItemsString) {\r\n                        lastItemsString = currentString;\r\n                        kioskItems = res.data.items;\r\n                        renderKioskCarousel();\r\n                        \r\n                        if (!isShowingBanner && activeKioskIndex === -1) {\r\n                            let firstValidIndex = kioskItems.findIndex(i => i.is_valid);\r\n                            if(firstValidIndex !== -1) {\r\n                                activeKioskIndex = firstValidIndex;\r\n                                triggerKioskCard(activeKioskIndex);\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }).catch(err => console.log(\"Radar Polling Error\", err));\r\n        }\r\n\r\n        function renderKioskCarousel() {\r\n            const container = document.getElementById('ps-kiosk-carousel');\r\n            const status = document.getElementById('ps-kiosk-status');\r\n            status.style.display = 'none'; container.style.display = 'flex'; container.innerHTML = '';\r\n            \r\n            kioskItems.forEach((item, index) => {\r\n                let card = document.createElement('div');\r\n                card.className = 'ps-kiosk-card' + (!item.is_valid ? ' disabled' : '');\r\n                card.id = 'kiosk-card-' + index;\r\n                let labelHtml = !item.is_valid ? `<span style=\"color:#ef4444;\">\ud83d\udeab ${item.jenis}<\/span>` : `${item.jenis}`;\r\n                let unitNameStr = (item.kw || '(TIDAK DITULIS)').toUpperCase();\r\n                card.innerHTML = `<div class=\"cat-label\">${labelHtml}<\/div><div class=\"unit-name\" title=\"${unitNameStr}\">${unitNameStr}<\/div>`;\r\n                \r\n                if(item.is_valid) {\r\n                    card.addEventListener('click', function() {\r\n                        if (isShowingBanner) endBannerMode(); \r\n                        activeKioskIndex = index;\r\n                        triggerKioskCard(activeKioskIndex);\r\n                    });\r\n                }\r\n                container.appendChild(card);\r\n            });\r\n        }\r\n\r\n        function triggerKioskCard(index) {\r\n            clearTimeout(kioskWaitTimer); \r\n            document.querySelectorAll('.ps-kiosk-card').forEach(el => el.classList.remove('active'));\r\n            \r\n            let activeCard = document.getElementById('kiosk-card-' + index);\r\n            let container = document.getElementById('ps-kiosk-carousel');\r\n            \r\n            if(activeCard && container) {\r\n                activeCard.classList.add('active');\r\n                let cardRect = activeCard.getBoundingClientRect();\r\n                let containerRect = container.getBoundingClientRect();\r\n                let targetScroll = container.scrollLeft + (cardRect.left - containerRect.left);\r\n                container.scrollTo({ left: targetScroll - 12, behavior: 'smooth' });\r\n            }\r\n            \r\n            if(kioskItems[index] && kioskItems[index].is_valid) {\r\n                console.log(`Menampilkan Data Unit ke-${index + 1} dari total ${kioskItems.length}`);\r\n                performSearch((kioskItems[index].kw || ''), true); \r\n            }\r\n        }\r\n\r\n        function startBannerMode() {\r\n            console.log(\">>> MENGAKTIFKAN SLIDESHOW BANNER KIOSK <<<\");\r\n            isShowingBanner = true;\r\n            \r\n            clearTimeout(kioskWaitTimer);\r\n            clearInterval(bannerSlideTimer);\r\n            \r\n            document.querySelectorAll('.ps-kiosk-card').forEach(el => el.classList.remove('active'));\r\n            let dataContainer = document.getElementById('ps-results-container');\r\n            dataContainer.classList.remove('active');\r\n            dataContainer.style.setProperty('display', 'none', 'important');\r\n            \r\n            let bannerContainer = document.getElementById('ps-kiosk-banner');\r\n            bannerContainer.style.setProperty('display', 'flex', 'important');\r\n            document.getElementById('ps-kiosk-live-text').innerText = \"INFORMASI LAYANAN\";\r\n            \r\n            currentBannerIdx = 0;\r\n            let bannerImgEl = document.getElementById('ps-kiosk-banner-img');\r\n            bannerImgEl.src = kioskBannerImages[currentBannerIdx];\r\n            bannerImgEl.style.opacity = 0.9;\r\n            \r\n            bannerSlideTimer = setInterval(() => {\r\n                currentBannerIdx++;\r\n                \r\n                if (currentBannerIdx < kioskBannerImages.length) {\r\n                    console.log(`Mengganti ke Slide Banner ${currentBannerIdx + 1}`);\r\n                    bannerImgEl.style.opacity = 0;\r\n                    setTimeout(() => {\r\n                        bannerImgEl.src = kioskBannerImages[currentBannerIdx];\r\n                        bannerImgEl.style.opacity = 0.9;\r\n                    }, 500); \r\n                } else {\r\n                    clearInterval(bannerSlideTimer);\r\n                    startSmartCountdown(); \r\n                }\r\n            }, 8000); \r\n        }\r\n\r\n        function endBannerMode() {\r\n            console.log(\"<<< MENGAKHIRI SLIDESHOW BANNER, KEMBALI KE DATA UNIT <<<\");\r\n            isShowingBanner = false;\r\n            clearInterval(bannerSlideTimer); \r\n            \r\n            document.getElementById('ps-kiosk-banner').style.setProperty('display', 'none', 'important');\r\n            document.getElementById('ps-results-container').style.setProperty('display', 'flex', 'important');\r\n            document.getElementById('ps-kiosk-live-text').innerText = \"10 UNIT TERBARU MASUK\";\r\n        }\r\n\r\n        function startSmartCountdown() {\r\n            clearTimeout(kioskWaitTimer);\r\n            kioskWaitTimer = setTimeout(() => {\r\n                if (isShowingBanner) {\r\n                    endBannerMode();\r\n                    let firstValidIndex = kioskItems.findIndex(i => i.is_valid);\r\n                    if(firstValidIndex !== -1) {\r\n                        activeKioskIndex = firstValidIndex;\r\n                        triggerKioskCard(activeKioskIndex);\r\n                    }\r\n                    return;\r\n                }\r\n\r\n                if(kioskItems.length > 0) {\r\n                    let nextIndex = activeKioskIndex + 1;\r\n                    if (nextIndex >= kioskItems.length) {\r\n                        startBannerMode();\r\n                        return;\r\n                    }\r\n\r\n                    let attempts = 0;\r\n                    while(nextIndex < kioskItems.length && !kioskItems[nextIndex].is_valid && attempts < kioskItems.length) {\r\n                        nextIndex++; attempts++;\r\n                    }\r\n\r\n                    if (nextIndex < kioskItems.length && kioskItems[nextIndex].is_valid) {\r\n                        activeKioskIndex = nextIndex;\r\n                        triggerKioskCard(activeKioskIndex);\r\n                    } else {\r\n                        startBannerMode();\r\n                    }\r\n                }\r\n            }, 10000);\r\n        }\r\n    \r\n    function performSearch(kw, isFromKiosk = false) {\r\n        if (!kw || kw.trim() === '') {\r\n             if(isFromKiosk) startSmartCountdown();             return;\r\n        }\r\n        \r\n        let container = document.getElementById('ps-results-container');\r\n        let loader = document.getElementById('ps-loader');\r\n        let safeCacheKey = kw.trim().toLowerCase();\r\n\r\n        \r\n        if (browserRAMCache[safeCacheKey]) {\r\n            renderDataToScreen(browserRAMCache[safeCacheKey], kw);\r\n             if(isFromKiosk) startSmartCountdown();             return; \r\n        }\r\n\r\n        loader.style.display = 'block'; container.classList.remove('active');\r\n\r\n        let fd = new URLSearchParams(); fd.append('action', 'ps_ajax_search'); fd.append('kw', kw);\r\n        fetch('https:\/\/palapaservicecenter.com\/wp-admin\/admin-ajax.php', { method: 'POST', body: fd })\r\n        .then(r => r.json()).then(res => {\r\n            if(res.success) {\r\n                browserRAMCache[safeCacheKey] = res.data; renderDataToScreen(res.data, kw);\r\n            } else {\r\n                loader.style.display = 'none'; \r\n                            }\r\n        }).catch(err => console.log(\"Search error\", err))\r\n        .finally(() => {\r\n             if(isFromKiosk) startSmartCountdown();         });\r\n    }\r\n\r\n    const highlightRowPlugin = {\r\n        id: 'highlightRow',\r\n        beforeDraw: (chart) => {\r\n            const ctx = chart.ctx; const dataset = chart.data.datasets[0]; const meta = chart.getDatasetMeta(0);\r\n            if (!meta.hidden && dataset && dataset.data.length > 0) {\r\n                ctx.save();\r\n                dataset.backgroundColor.forEach((color, index) => {\r\n                    if (color === '#f59e0b') {\r\n                        const bar = meta.data[index];\r\n                        if (bar) {\r\n                            const yPos = bar.y; const height = bar.height + 10; \r\n                            ctx.fillStyle = 'rgba(245, 158, 11, 0.15)'; \r\n                            ctx.fillRect(chart.chartArea.left, yPos - (height \/ 2), chart.chartArea.right - chart.chartArea.left, height);\r\n                        }\r\n                    }\r\n                });\r\n                ctx.restore();\r\n            }\r\n        }\r\n    };\r\n\r\n    let lineChart = null, barChart = null; let serverData = null; let currentFilterMerek = null; let highlightKeywords = [];\r\n    const brandPalette = ['#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4', '#f43f5e', '#84cc16', '#64748b', '#d946ef', '#14b8a6', '#4f46e5', '#ea580c', '#65a30d'];\r\n    let colorMap = {};\r\n\r\n    function hexToRgba(hex, alpha) {\r\n        hex = hex.replace('#', ''); var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 16); var g = parseInt(hex.length == 3 ? hex.slice(1, 2).repeat(2) : hex.slice(2, 4), 16); var b = parseInt(hex.length == 3 ? hex.slice(2, 3).repeat(2) : hex.slice(4, 6), 16);\r\n        return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';\r\n    }\r\n\r\n    \/\/ =================================================================\r\n    \/\/ UPDATE: HTML GENERATOR KE LAYOUT 2x2 KOLOM (ANTI-MELAR)\r\n    \/\/ =================================================================\r\n    function renderDataToScreen(data, kw) {\r\n        let container = document.getElementById('ps-results-container'); let loader = document.getElementById('ps-loader');\r\n        loader.style.display = 'none'; \r\n                \r\n        currentFilterMerek = null; highlightKeywords = [];\r\n        \r\n        if (!data.found) {\r\n            if (data.reject_msg) { container.innerHTML = `<div class='ps-result-card' style='background:#fef2f2; border: 1px solid #fca5a5; color: #991b1b; text-align:left; font-size: 0.95rem; line-height: 1.5;'>${data.reject_msg}<\/div>`; } \r\n            else { container.innerHTML = \"<div class='ps-result-card' style='text-align:center;'>Data spesifik tidak ditemukan.<\/div>\"; }\r\n            container.classList.add('active'); return;\r\n        }\r\n        \r\n        serverData = data; let colorIdx = 0;\r\n        serverData.list_broad.forEach(item => {\r\n            let m = (item.merek || '').toUpperCase(); if (!colorMap[m]) { colorMap[m] = brandPalette[colorIdx % brandPalette.length]; colorIdx++; }\r\n        });\r\n\r\n        let exactBrands = [...new Set(serverData.list_exact.map(item => (item.merek || '').toUpperCase()))]; \r\n        if (exactBrands.length === 1 && exactBrands[0] !== 'TANPA MEREK') { currentFilterMerek = exactBrands[0]; }\r\n\r\n        let upperWords = serverData.clean_words_array.map(w => (w || '').toUpperCase()); \r\n        highlightKeywords = upperWords.filter(w => {\r\n            let isKategori = serverData.nama_kategori_induk.includes(w); let isMerek = currentFilterMerek && currentFilterMerek.includes(w);\r\n            return !isKategori && !isMerek; \r\n        });\r\n\r\n        let html_diagnosa = serverData.diagnosa ? `<div class='ps-diagnosa-box'><b>\ud83e\ude7a Diagnosa AI:<\/b> ${serverData.diagnosa}<\/div>` : '';\r\n        let kwSafe = (serverData.clean_kw || '(TIDAK DITULIS)');\r\n        let html_teks = `<div class='ps-highlight-box' style='margin-bottom:0;'>Total <b>${kwSafe}<\/b> yang ditangani sejauh ini adalah <b>${serverData.total_spesifik.toLocaleString('id-ID')} unit<\/b>.<\/div>`;\r\n        let html_sop_disclaimer = `<div class='ps-sop-box'>${serverData.sop_msg}<\/div>`;\r\n\r\n        let html_filters_merek = \"\";\r\n        if (serverData.merek_list.length > 0) {\r\n            html_filters_merek += `<div class='ps-custom-scroll' style='display:flex; flex-wrap:wrap; gap:6px; max-height:40vh; overflow-y:auto; padding-right:5px; margin-bottom: 10px; flex-shrink:0;'>`;\r\n            serverData.merek_list.forEach(m => {\r\n                let mName = m.nama || 'TANPA MEREK'; let bColor = colorMap[mName.toUpperCase()] || '#3b82f6';\r\n                let pct = ((m.total \/ serverData.total_broad) * 100).toFixed(1); let activeClass = (mName.toUpperCase() === currentFilterMerek) ? 'active' : '';\r\n                html_filters_merek += `<div class='brand-btn filter-merek-btn ${activeClass}' style='--brand-color: ${bColor}; --brand-bg: ${hexToRgba(bColor, 0.1)}; --brand-border: ${hexToRgba(bColor, 0.3)};' data-val='${mName.toUpperCase()}'><b>${mName}<\/b>: ${pct}% (${m.total})<\/div>`;\r\n            });\r\n            html_filters_merek += `<\/div>`;\r\n        }\r\n\r\n        let brandTitleText = currentFilterMerek ? currentFilterMerek : '';\r\n        \r\n        container.innerHTML = `\r\n            <div class=\"ps-dashboard-grid\">\r\n                <div class=\"ps-dash-col\">\r\n                    <div class='ps-result-card' style='padding: 15px 20px; flex-shrink: 0;'>\r\n                        ${html_teks}\r\n                        <div style=\"margin-top: 10px; display: flex; flex-direction: column; gap: 8px;\">\r\n                            ${html_diagnosa}${html_sop_disclaimer}\r\n                        <\/div>\r\n                    <\/div>\r\n                    <div class='ps-result-card' style='padding: 15px; display: flex; flex-direction: column; flex: 1; min-height: 0;'>\r\n                        <div class='ps-section-title' style='margin-bottom:10px;'>TREN KESELURUHAN ${serverData.nama_kategori_induk} (12 BULAN):<\/div>\r\n                        <div class='ps-chart-main'><canvas id='psLineChart'><\/canvas><\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div class=\"ps-dash-col\">\r\n                    <div class='ps-result-card' style='padding: 15px 20px; display: flex; flex-direction: column; flex: 1; min-height: 0;'>\r\n                        ${html_filters_merek !== '' ? html_filters_merek : ''}\r\n                        <div class='ps-section-title' id='bar-chart-title' style='margin-bottom:10px; margin-top: 5px; flex-shrink: 0;'>KOMPOSISI SPESIFIK PERANGKAT ${brandTitleText} (TOP 20):<\/div>\r\n                        <div class='ps-scroll-area' style='flex: 1; position: relative; min-height: 0;'><div id='psBarCanvasContainer' style='position:relative; width:100%; height:100%;'><canvas id='psBarChart'><\/canvas><\/div><\/div>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n        `;\r\n        container.classList.add('active');\r\n        \r\n        if (lineChart) lineChart.destroy();\r\n        let sumRecent = serverData.line_global.reduce((a, b) => a + Number(b), 0); let currentRun = (serverData.total_broad - sumRecent) > 0 ? (serverData.total_broad - sumRecent) : 0;\r\n        let cumulativeData = serverData.line_global.map(v => { currentRun += Number(v); return currentRun; });\r\n        let minVal = Math.min(...cumulativeData); let maxVal = Math.max(...cumulativeData); let variance = maxVal - minVal;\r\n        if (variance === 0) variance = maxVal * 0.1; \r\n        let suggestedMin = Math.max(0, minVal - (variance * 0.5)); let suggestedMax = maxVal + (variance * 0.2);\r\n\r\n        lineChart = new Chart(document.getElementById('psLineChart'), {\r\n            type: 'line',\r\n            data: { labels: serverData.labels, datasets: [{ label: 'Total Unit', data: cumulativeData, borderColor: '#3b82f6', tension: 0.4, fill: true, backgroundColor: 'rgba(59,130,246,0.1)' }] },\r\n            options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, animation: { duration: 800, easing: 'easeOutQuart' }, scales: { y: { min: suggestedMin, max: suggestedMax, ticks: { maxTicksLimit: 8, callback: function(value) { return Math.floor(value).toLocaleString('id-ID'); } } } } }\r\n        });\r\n\r\n        if (barChart) barChart.destroy();\r\n        barChart = new Chart(document.getElementById('psBarChart'), {\r\n            type: 'bar',\r\n            data: { labels: [], datasets: [{ data: [], borderRadius: 4, barThickness: 12, maxBarThickness: 15 }] },\r\n            options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, animation: { duration: 500 }, scales: { x: { ticks: { font: { size: 10 } } }, y: { ticks: { autoSkip: false, font: { size: 10 } } } } },\r\n            plugins: [highlightRowPlugin] \r\n        });\r\n\r\n        updateBarChartOnly();\r\n\r\n        document.querySelectorAll('.filter-merek-btn').forEach(b => {\r\n            b.addEventListener('click', function() {\r\n                let val = this.getAttribute('data-val');\r\n                if (currentFilterMerek === val) { currentFilterMerek = null; document.querySelectorAll('.filter-merek-btn').forEach(btn => btn.classList.remove('active')); } \r\n                else { currentFilterMerek = val; document.querySelectorAll('.filter-merek-btn').forEach(btn => btn.classList.remove('active')); this.classList.add('active'); }\r\n                updateBarChartOnly(); \r\n            });\r\n        });\r\n    }\r\n\r\n    function updateBarChartOnly() {\r\n        let filteredList = serverData.list_broad;\r\n        if (currentFilterMerek) { filteredList = serverData.list_broad.filter(i => (i.merek || '').toUpperCase() === currentFilterMerek); }\r\n        let exactMatchesInBroad = [];\r\n        filteredList.forEach(item => {\r\n            let typeName = (item.namatipe || '').toUpperCase();\r\n            if (serverData.list_exact.some(ex => (ex.namatipe || '').toUpperCase() === typeName) && highlightKeywords.length > 0) { exactMatchesInBroad.push(item); }\r\n        });\r\n\r\n        let top20 = filteredList.slice(0, 20); let finalBarData = [...top20];\r\n        exactMatchesInBroad.forEach(exactItem => {\r\n            if (!top20.some(topItem => (topItem.namatipe || '') === (exactItem.namatipe || '') && (topItem.merek || '') === (exactItem.merek || ''))) { finalBarData.push(exactItem); }\r\n        });\r\n\r\n        barChart.data.labels = finalBarData.map(i => {\r\n            let m = (i.merek && i.merek !== 'TANPA MEREK') ? i.merek.toUpperCase() + ' ' : ''; let t = i.namatipe ? i.namatipe : '(TIPE TIDAK DITULIS)'; let fullText = m + t;\r\n            return fullText.length > 30 ? fullText.substring(0, 30) + '...' : fullText; \r\n        });\r\n        \r\n        barChart.data.datasets[0].data = finalBarData.map(i => i.total);\r\n        barChart.data.datasets[0].backgroundColor = finalBarData.map(item => {\r\n            let typeName = (item.namatipe || '').toUpperCase();\r\n            if (serverData.list_exact.some(ex => (ex.namatipe || '').toUpperCase() === typeName) && highlightKeywords.length > 0) return '#f59e0b'; \r\n            return colorMap[(item.merek || '').toUpperCase()] || '#10b981';\r\n        });\r\n        barChart.update();\r\n\r\n        let titleEl = document.getElementById('bar-chart-title');\r\n        if(titleEl) { let brandText = currentFilterMerek ? currentFilterMerek : ''; titleEl.innerText = `KOMPOSISI SPESIFIK PERANGKAT ${brandText} (TOP 20):`; }\r\n        let dynamicHeight = Math.max(finalBarData.length * 30, 150);\r\n        document.getElementById('psBarCanvasContainer').style.height = dynamicHeight + 'px'; document.getElementById('psBarCanvasContainer').style.width = '100%'; \r\n    }\r\n    <\/script>\r\n    <\/div>\n\t\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":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-16879","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/pages\/16879","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/comments?post=16879"}],"version-history":[{"count":28,"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/pages\/16879\/revisions"}],"predecessor-version":[{"id":17101,"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/pages\/16879\/revisions\/17101"}],"wp:attachment":[{"href":"https:\/\/palapaservicecenter.com\/en\/wp-json\/wp\/v2\/media?parent=16879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}