{
    "component": {
        "id": 29,
        "name": "Widget NRES",
        "code": "widget_nres",
        "content": "{{--\r\n  WIDGET NRES — Dashboard: Cuaca · APIMS · Amaran · Statistik · Kalendar · Top 10\r\n  ---------------------------------------------------------------------------\r\n  Data SEBENAR (bukan demo):\r\n    /widget/weather          → data.gov.my (MET Malaysia)\r\n    /widget/air-quality      → APIMS · DOE (proksi, kerana CORS)\r\n    /widget/weather-warning  → data.gov.my amaran cuaca\r\n    /widget/visitor-stats    → visitor_page_views (statistik lawatan)\r\n    /widget/calendar         → content_calendar\r\n    /widget/popular          → visitor_page_views (top 10)\r\n--}}\r\n<div class=\"container-xl\">\r\n    <section class=\"environment-section mb-5\" id=\"alam-sekitar\"\r\n        aria-label=\"Cuaca, kualiti udara dan statistik\"\r\n        data-weather=\"{{ url('/widget/weather') }}\"\r\n        data-air=\"{{ url('/widget/air-quality') }}\"\r\n        data-warning=\"{{ url('/widget/weather-warning') }}\"\r\n        data-stats=\"{{ url('/widget/visitor-stats') }}\">\r\n\r\n        <div class=\"weather-demo-label\">\r\n            Data langsung · MET Malaysia (cuaca) · JAS/DOE (IPU) · statistik lawatan laman\r\n        </div>\r\n\r\n        <div class=\"weather-dashboard\">\r\n\r\n            {{-- 1. CUACA --}}\r\n            <article class=\"weather-tile\">\r\n                <h2>CUACA HARI INI — <span id=\"wxPlace\">PUTRAJAYA</span></h2>\r\n                <p class=\"weather-source\">Sumber: MET Malaysia · data.gov.my</p>\r\n\r\n                <div class=\"weather-reading\">\r\n                    <span class=\"storm-icon\" id=\"wxIcon\" aria-hidden=\"true\">☀️</span>\r\n                    <div>\r\n                        <strong id=\"wxTemp\">–</strong>\r\n                        <span id=\"wxSummary\">Memuatkan…</span>\r\n                    </div>\r\n                </div>\r\n\r\n                <div class=\"weather-range\" id=\"wxRange\">H: – &nbsp; L: –</div>\r\n                <p class=\"weather-updated\" id=\"wxUpdated\">Memuatkan data…</p>\r\n            </article>\r\n\r\n            {{-- 2. INDEKS PENCEMARAN UDARA --}}\r\n            <article class=\"weather-tile\">\r\n                <h2>INDEKS PENCEMAR UDARA (APIMS)</h2>\r\n                <p class=\"weather-source\">Sumber: JAS · DOE (APIMS)</p>\r\n\r\n                <div class=\"air-gauge\">\r\n                    <svg viewBox=\"0 0 220 120\" aria-hidden=\"true\">\r\n                        <path d=\"M20 105a90 90 0 0 1 180 0\" fill=\"none\" stroke=\"#e5d039\" stroke-width=\"14\" />\r\n                        <path d=\"M20 105a90 90 0 0 1 67-87\" fill=\"none\" stroke=\"#54a96a\" stroke-width=\"14\" />\r\n                        <path d=\"M87 18a90 90 0 0 1 23-3\" fill=\"none\" stroke=\"#88b748\" stroke-width=\"14\" />\r\n                        <line id=\"airNeedle\" x1=\"110\" y1=\"105\" x2=\"110\" y2=\"32\" stroke=\"#14413a\" stroke-width=\"3\"\r\n                            stroke-linecap=\"round\" />\r\n                        <circle cx=\"110\" cy=\"105\" r=\"6\" fill=\"#14413a\" />\r\n                    </svg>\r\n\r\n                    <div>\r\n                        <strong id=\"airValue\">–</strong>\r\n                        <b id=\"airClass\">Memuatkan…</b>\r\n                    </div>\r\n                </div>\r\n\r\n                <p class=\"air-caption\" id=\"airCaption\">Memuatkan data IPU…</p>\r\n                <p class=\"weather-updated\" id=\"airUpdated\">Stesen –</p>\r\n            </article>\r\n\r\n            {{-- 3. AMARAN CUACA / JEREBU --}}\r\n            <article class=\"weather-tile\">\r\n                <h2>AMARAN CUACA / JEREBU SEMASA</h2>\r\n                <p class=\"weather-source\">Sumber: MET Malaysia · data.gov.my</p>\r\n\r\n                <span class=\"warning-check\" id=\"warnIcon\" aria-hidden=\"true\">✓</span>\r\n                <h3 id=\"warnTitle\">Memuatkan…</h3>\r\n                <p class=\"warning-copy\" id=\"warnText\">Menyemak status amaran semasa…</p>\r\n                <p class=\"weather-updated\" id=\"warnUpdated\"></p>\r\n                <a class=\"warning-link\" href=\"https://www.met.gov.my/\" target=\"_blank\" rel=\"noopener\">Lihat Peta Amaran ›</a>\r\n            </article>\r\n\r\n            {{-- 4. STATISTIK LAWATAN --}}\r\n            <article class=\"weather-tile\">\r\n                <h2>STATISTIK PERKHIDMATAN<br>ATAS TALIAN <span id=\"statYear\">2026</span></h2>\r\n                <p class=\"weather-source\">Sumber: statistik lawatan laman</p>\r\n\r\n                <strong class=\"transaction-total\" id=\"statTotal\">–</strong>\r\n                <span class=\"transaction-label\">Jumlah Lawatan Halaman</span>\r\n                <p class=\"transaction-growth\" id=\"statGrowth\"></p>\r\n\r\n                <div class=\"transaction-breakdown\">\r\n                    <div><span>Hari Ini</span><b id=\"statToday\">–</b></div>\r\n                    <div><span>Bulan Ini</span><b id=\"statMonth\">–</b></div>\r\n                    <div><span>Tahun Ini</span><b id=\"statYearTotal\">–</b></div>\r\n                </div>\r\n            </article>\r\n\r\n        </div>\r\n    </section>\r\n\r\n    {{-- ── KALENDAR NRES ─────────────────────────────────────────────── --}}\r\n    <section class=\"calendar-section mb-5\" id=\"kalendar\" data-calendar=\"{{ url('/widget/calendar') }}\">\r\n        <div class=\"section-heading\">\r\n            <div>\r\n                <div class=\"section-kicker\">PROGRAM &amp; AKTIVITI</div>\r\n                <h2>Kalendar NRES</h2>\r\n            </div>\r\n\r\n            <div class=\"calendar-controls\">\r\n                <button id=\"previousMonth\" type=\"button\" aria-label=\"Bulan sebelumnya\">‹</button>\r\n                <strong id=\"calendarMonth\">—</strong>\r\n                <button id=\"nextMonth\" type=\"button\" aria-label=\"Bulan seterusnya\">›</button>\r\n            </div>\r\n        </div>\r\n\r\n        <div class=\"calendar-layout\">\r\n            <div class=\"calendar-box\">\r\n                <div class=\"calendar-week\" aria-hidden=\"true\">\r\n                    <span>Isn</span><span>Sel</span><span>Rab</span><span>Kha</span><span>Jum</span><span>Sab</span><span>Ahd</span>\r\n                </div>\r\n\r\n                <div id=\"calendarDays\" class=\"calendar-days\"></div>\r\n\r\n                <p class=\"small text-body-secondary mt-3 mb-0\">\r\n                    ● Tarikh beraktiviti &nbsp; · &nbsp; Pilih tarikh untuk butiran\r\n                </p>\r\n            </div>\r\n\r\n            <div class=\"calendar-agenda\">\r\n                <h3 id=\"selectedDate\">—</h3>\r\n                <div id=\"dayEvents\" aria-live=\"polite\">\r\n                    <p class=\"small text-body-secondary\">Tiada aktiviti disenaraikan pada tarikh ini.</p>\r\n                </div>\r\n\r\n                <hr>\r\n\r\n                <h3>Senarai aktiviti bulan ini</h3>\r\n                <div id=\"monthEvents\">\r\n                    <p class=\"small text-body-secondary\">Tiada aktiviti disenaraikan untuk bulan ini.</p>\r\n                </div>\r\n            </div>\r\n        </div>\r\n    </section>\r\n\r\n    {{-- ── 10 HALAMAN PALING POPULAR ─────────────────────────────────── --}}\r\n    <section class=\"popular-section mb-5\" id=\"statistik\" data-popular=\"{{ url('/widget/popular') }}\">\r\n        <div class=\"section-heading\">\r\n            <div>\r\n                <div class=\"section-kicker\">STATISTIK PERKHIDMATAN DALAM TALIAN</div>\r\n                <h2>10 Halaman Paling Popular</h2>\r\n            </div>\r\n            <span class=\"top-badge\">TOP 10</span>\r\n        </div>\r\n\r\n        <ol id=\"popularPages\" class=\"popular-list\"></ol>\r\n        <p id=\"popularEmpty\" class=\"text-body-secondary\">Statistik halaman akan dipaparkan apabila data pelawat tersedia.</p>\r\n    </section>\r\n</div>\r\n\r\n<style>\r\n    /* ── label ─────────────────────────────────────────────────────── */\r\n    .weather-demo-label {\r\n        margin-bottom: 14px;\r\n        padding: 9px 14px;\r\n        border-radius: 10px;\r\n        background: #eef7f8;\r\n        color: #0c6a77;\r\n        font-size: .78rem;\r\n        font-weight: 600;\r\n    }\r\n\r\n    /* ── dashboard ─────────────────────────────────────────────────── */\r\n    .weather-dashboard {\r\n        display: grid;\r\n        grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));\r\n        gap: 16px;\r\n    }\r\n\r\n    .weather-tile {\r\n        display: flex;\r\n        flex-direction: column;\r\n        padding: 18px 20px;\r\n        border: 1px solid #dbe7e9;\r\n        border-radius: 16px;\r\n        background: #fff;\r\n        box-shadow: 0 10px 26px rgba(16, 62, 56, .06);\r\n    }\r\n\r\n    .weather-tile h2 {\r\n        margin: 0 0 4px;\r\n        color: #14413a;\r\n        font-size: .82rem;\r\n        font-weight: 800;\r\n        letter-spacing: .04em;\r\n        line-height: 1.35;\r\n        text-transform: uppercase;\r\n    }\r\n\r\n    .weather-source {\r\n        margin: 0 0 14px;\r\n        color: #8aa0a0;\r\n        font-size: .72rem;\r\n        font-weight: 600;\r\n    }\r\n\r\n    .weather-reading {\r\n        display: flex;\r\n        align-items: center;\r\n        gap: 14px;\r\n    }\r\n\r\n    .storm-icon {\r\n        font-size: 2.4rem;\r\n        line-height: 1;\r\n    }\r\n\r\n    .weather-reading strong {\r\n        display: block;\r\n        color: #11808f;\r\n        font-size: 1.9rem;\r\n        font-weight: 800;\r\n        line-height: 1.1;\r\n    }\r\n\r\n    .weather-reading span {\r\n        color: #5c6b6b;\r\n        font-size: .84rem;\r\n        font-weight: 600;\r\n    }\r\n\r\n    .weather-range {\r\n        margin-top: 12px;\r\n        padding: 8px 12px;\r\n        border-radius: 10px;\r\n        background: #f6fbfb;\r\n        color: #24443f;\r\n        font-size: .82rem;\r\n        font-weight: 700;\r\n    }\r\n\r\n    .weather-updated {\r\n        margin: auto 0 0;\r\n        padding-top: 12px;\r\n        color: #9aacab;\r\n        font-size: .72rem;\r\n    }\r\n\r\n    /* ── tolok IPU ─────────────────────────────────────────────────── */\r\n    .air-gauge {\r\n        display: flex;\r\n        align-items: center;\r\n        gap: 16px;\r\n    }\r\n\r\n    .air-gauge svg {\r\n        width: 132px;\r\n        height: 74px;\r\n        flex-shrink: 0;\r\n    }\r\n\r\n    .air-gauge strong {\r\n        display: block;\r\n        color: #14413a;\r\n        font-size: 1.9rem;\r\n        font-weight: 800;\r\n        line-height: 1.1;\r\n    }\r\n\r\n    .air-gauge b {\r\n        color: #11808f;\r\n        font-size: .84rem;\r\n        font-weight: 700;\r\n    }\r\n\r\n    .air-caption {\r\n        margin: 12px 0 0;\r\n        color: #5c6b6b;\r\n        font-size: .82rem;\r\n    }\r\n\r\n    /* ── amaran ────────────────────────────────────────────────────── */\r\n    .warning-check {\r\n        display: inline-flex;\r\n        align-items: center;\r\n        justify-content: center;\r\n        width: 46px;\r\n        height: 46px;\r\n        border-radius: 50%;\r\n        background: #e6f6ec;\r\n        color: #1f9d55;\r\n        font-size: 1.4rem;\r\n        font-weight: 800;\r\n    }\r\n\r\n    .warning-check.is-alert {\r\n        background: #fdeaea;\r\n        color: #d92d20;\r\n    }\r\n\r\n    .weather-tile h3 {\r\n        margin: 12px 0 6px;\r\n        color: #14413a;\r\n        font-size: 1rem;\r\n        font-weight: 800;\r\n    }\r\n\r\n    .warning-copy {\r\n        margin: 0;\r\n        color: #5c6b6b;\r\n        font-size: .84rem;\r\n        line-height: 1.5;\r\n    }\r\n\r\n    .warning-link {\r\n        margin-top: 12px;\r\n        color: #0c6a77;\r\n        font-size: .82rem;\r\n        font-weight: 700;\r\n        text-decoration: none;\r\n    }\r\n\r\n    .warning-link:hover {\r\n        text-decoration: underline;\r\n    }\r\n\r\n    /* ── statistik ─────────────────────────────────────────────────── */\r\n    .transaction-total {\r\n        display: block;\r\n        color: #11808f;\r\n        font-size: 1.9rem;\r\n        font-weight: 800;\r\n        line-height: 1.1;\r\n    }\r\n\r\n    .transaction-label {\r\n        color: #8aa0a0;\r\n        font-size: .76rem;\r\n        font-weight: 700;\r\n    }\r\n\r\n    .transaction-growth {\r\n        margin: 10px 0 0;\r\n        color: #5c6b6b;\r\n        font-size: .82rem;\r\n    }\r\n\r\n    .transaction-growth b {\r\n        color: #1f9d55;\r\n    }\r\n\r\n    .transaction-breakdown {\r\n        display: grid;\r\n        gap: 6px;\r\n        margin-top: 14px;\r\n    }\r\n\r\n    .transaction-breakdown div {\r\n        display: flex;\r\n        justify-content: space-between;\r\n        padding: 7px 10px;\r\n        border-radius: 9px;\r\n        background: #f6fbfb;\r\n        font-size: .8rem;\r\n    }\r\n\r\n    .transaction-breakdown span {\r\n        color: #4a6b66;\r\n        font-weight: 600;\r\n    }\r\n\r\n    .transaction-breakdown b {\r\n        color: #14413a;\r\n        font-weight: 800;\r\n    }\r\n\r\n    /* ── tajuk seksyen ─────────────────────────────────────────────── */\r\n    .section-heading {\r\n        display: flex;\r\n        flex-wrap: wrap;\r\n        align-items: flex-end;\r\n        justify-content: space-between;\r\n        gap: 12px;\r\n        margin-bottom: 16px;\r\n    }\r\n\r\n    .section-kicker {\r\n        color: #11808f;\r\n        font-size: .74rem;\r\n        font-weight: 800;\r\n        letter-spacing: .08em;\r\n    }\r\n\r\n    .section-heading h2 {\r\n        margin: 4px 0 0;\r\n        color: #14413a;\r\n        font-size: 1.35rem;\r\n        font-weight: 800;\r\n    }\r\n\r\n    /* ── kalendar ──────────────────────────────────────────────────── */\r\n    .calendar-section,\r\n    .popular-section {\r\n        padding: 22px 24px;\r\n        border: 1px solid #dbe7e9;\r\n        border-radius: 16px;\r\n        background: #fff;\r\n        box-shadow: 0 10px 26px rgba(16, 62, 56, .06);\r\n    }\r\n\r\n    .calendar-controls {\r\n        display: inline-flex;\r\n        align-items: center;\r\n        gap: 10px;\r\n    }\r\n\r\n    .calendar-controls button {\r\n        width: 34px;\r\n        height: 34px;\r\n        border: 1px solid #d9e6e7;\r\n        border-radius: 10px;\r\n        background: #f8fbfb;\r\n        color: #14413a;\r\n        font-size: 1.1rem;\r\n        font-weight: 800;\r\n        line-height: 1;\r\n        cursor: pointer;\r\n        transition: .18s;\r\n    }\r\n\r\n    .calendar-controls button:hover {\r\n        border-color: #11808f;\r\n        background: #eef7f8;\r\n    }\r\n\r\n    .calendar-controls strong {\r\n        min-width: 150px;\r\n        color: #14413a;\r\n        font-size: .95rem;\r\n        text-align: center;\r\n    }\r\n\r\n    .calendar-layout {\r\n        display: grid;\r\n        grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);\r\n        gap: 22px;\r\n    }\r\n\r\n    @media (max-width: 768px) {\r\n        .calendar-layout {\r\n            grid-template-columns: 1fr;\r\n        }\r\n    }\r\n\r\n    .calendar-week,\r\n    .calendar-days {\r\n        display: grid;\r\n        grid-template-columns: repeat(7, 1fr);\r\n        gap: 6px;\r\n    }\r\n\r\n    .calendar-week span {\r\n        padding: 6px 0;\r\n        color: #8aa0a0;\r\n        font-size: .72rem;\r\n        font-weight: 800;\r\n        text-align: center;\r\n        text-transform: uppercase;\r\n    }\r\n\r\n    .calendar-days button {\r\n        position: relative;\r\n        aspect-ratio: 1 / 1;\r\n        border: 1px solid #eef4f5;\r\n        border-radius: 10px;\r\n        background: #fff;\r\n        color: #24443f;\r\n        font-size: .84rem;\r\n        font-weight: 600;\r\n        cursor: pointer;\r\n        transition: .18s;\r\n    }\r\n\r\n    .calendar-days button:hover {\r\n        border-color: #11808f;\r\n        background: #eef7f8;\r\n    }\r\n\r\n    .calendar-days button.has-event {\r\n        border-color: #11808f;\r\n        background: #f2fbfb;\r\n        font-weight: 800;\r\n    }\r\n\r\n    .calendar-days button.has-event::after {\r\n        content: '';\r\n        position: absolute;\r\n        bottom: 6px;\r\n        left: 50%;\r\n        width: 6px;\r\n        height: 6px;\r\n        border-radius: 50%;\r\n        background: #11808f;\r\n        transform: translateX(-50%);\r\n    }\r\n\r\n    .calendar-days button.selected {\r\n        border-color: #14413a;\r\n        background: #14413a;\r\n        color: #fff;\r\n    }\r\n\r\n    .calendar-days button.selected::after {\r\n        background: #fff;\r\n    }\r\n\r\n    .calendar-days span {\r\n        aspect-ratio: 1 / 1;\r\n    }\r\n\r\n    .calendar-agenda h3 {\r\n        margin: 0 0 10px;\r\n        color: #14413a;\r\n        font-size: .98rem;\r\n        font-weight: 800;\r\n    }\r\n\r\n    .calendar-agenda hr {\r\n        margin: 18px 0;\r\n        border: 0;\r\n        border-top: 1px solid #eef4f5;\r\n    }\r\n\r\n    .cal-item {\r\n        margin-bottom: 10px;\r\n        padding: 11px 13px;\r\n        border-left: 3px solid #11808f;\r\n        border-radius: 0 10px 10px 0;\r\n        background: #f8fbfb;\r\n    }\r\n\r\n    .cal-item b {\r\n        display: block;\r\n        color: #14413a;\r\n        font-size: .86rem;\r\n        font-weight: 800;\r\n    }\r\n\r\n    .cal-item span {\r\n        display: block;\r\n        margin-top: 3px;\r\n        color: #7b8f8c;\r\n        font-size: .76rem;\r\n    }\r\n\r\n    .cal-tag {\r\n        display: inline-block;\r\n        margin-top: 6px;\r\n        padding: 2px 8px;\r\n        border-radius: 999px;\r\n        background: #e2f1f3;\r\n        color: #0c6a77;\r\n        font-size: .66rem;\r\n        font-weight: 800;\r\n        letter-spacing: .04em;\r\n    }\r\n\r\n    /* ── popular ───────────────────────────────────────────────────── */\r\n    .top-badge {\r\n        padding: 4px 12px;\r\n        border-radius: 999px;\r\n        background: #14413a;\r\n        color: #fff;\r\n        font-size: .7rem;\r\n        font-weight: 800;\r\n        letter-spacing: .06em;\r\n    }\r\n\r\n    .popular-list {\r\n        display: grid;\r\n        gap: 8px;\r\n        margin: 0;\r\n        padding: 0;\r\n        list-style: none;\r\n        counter-reset: pop;\r\n    }\r\n\r\n    .popular-list li {\r\n        display: flex;\r\n        align-items: center;\r\n        gap: 12px;\r\n        padding: 11px 14px;\r\n        border: 1px solid #eef4f5;\r\n        border-radius: 11px;\r\n        background: #fff;\r\n        counter-increment: pop;\r\n    }\r\n\r\n    .popular-list li::before {\r\n        content: counter(pop);\r\n        display: inline-flex;\r\n        align-items: center;\r\n        justify-content: center;\r\n        width: 26px;\r\n        height: 26px;\r\n        border-radius: 8px;\r\n        background: #eef7f8;\r\n        color: #0c6a77;\r\n        font-size: .76rem;\r\n        font-weight: 800;\r\n        flex-shrink: 0;\r\n    }\r\n\r\n    .popular-list li:nth-child(-n+3)::before {\r\n        background: #11808f;\r\n        color: #fff;\r\n    }\r\n\r\n    .popular-list a {\r\n        flex: 1;\r\n        color: #14413a;\r\n        font-size: .86rem;\r\n        font-weight: 700;\r\n        text-decoration: none;\r\n    }\r\n\r\n    .popular-list a:hover {\r\n        text-decoration: underline;\r\n    }\r\n\r\n    .popular-list em {\r\n        color: #7b8f8c;\r\n        font-size: .74rem;\r\n        font-style: normal;\r\n        font-weight: 700;\r\n    }\r\n</style>\r\n\r\n<script>\r\n    (function () {\r\n        'use strict';\r\n\r\n        var section = document.getElementById('alam-sekitar');\r\n\r\n        if (!section) return;\r\n\r\n        var API = {\r\n            weather: section.dataset.weather,\r\n            air: section.dataset.air,\r\n            warning: section.dataset.warning,\r\n            stats: section.dataset.stats\r\n        };\r\n\r\n        var LOC = 'Putrajaya';\r\n        var $ = function (id) { return document.getElementById(id); };\r\n\r\n        function fmt(n) {\r\n            return (n === null || n === undefined || isNaN(n)) ? '–' : Number(n).toLocaleString('en-MY');\r\n        }\r\n\r\n        function icon(text) {\r\n            var t = String(text || '').toLowerCase();\r\n\r\n            if (t.indexOf('ribut') > -1 || t.indexOf('petir') > -1) return '⛈️';\r\n            if (t.indexOf('hujan') > -1) return '🌧️';\r\n            if (t.indexOf('mendung') > -1 || t.indexOf('awan') > -1) return '☁️';\r\n            if (t.indexOf('jerebu') > -1) return '🌫️';\r\n\r\n            return '☀️';\r\n        }\r\n\r\n        /* ── 1. CUACA ─────────────────────────────────────────────── */\r\n        function loadWeather() {\r\n            fetch(API.weather + '?location=' + encodeURIComponent(LOC))\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    if (!d || !d.ok || !d.items || !d.items.length) throw new Error('no data');\r\n\r\n                    var it = d.items[0];\r\n\r\n                    $('wxPlace').textContent = (it.location || LOC).toUpperCase();\r\n                    $('wxIcon').textContent = icon(it.summary);\r\n                    $('wxTemp').textContent = (it.max_temp != null ? it.max_temp + '°C' : '–');\r\n                    $('wxSummary').textContent = (it.summary || '–') + (it.summary_when ? ' ' + it.summary_when : '');\r\n                    $('wxRange').innerHTML = 'H: ' + (it.max_temp != null ? it.max_temp + '°C' : '–') +\r\n                        ' &nbsp; L: ' + (it.min_temp != null ? it.min_temp + '°C' : '–');\r\n                    $('wxUpdated').textContent = 'Ramalan ' + (it.date || '') + ' · Pagi: ' + (it.morning || '–') +\r\n                        ' · Petang: ' + (it.afternoon || '–') + ' · Malam: ' + (it.night || '–');\r\n                })\r\n                .catch(function () {\r\n                    $('wxSummary').textContent = 'Data cuaca tidak tersedia';\r\n                    $('wxUpdated').textContent = '';\r\n                });\r\n        }\r\n\r\n        /* ── 2. INDEKS PENCEMARAN UDARA ───────────────────────────── */\r\n        var AIR_CAPTION = {\r\n            'Baik': 'Kualiti udara baik.',\r\n            'Sederhana': 'Kualiti udara boleh diterima.',\r\n            'Moderate': 'Kualiti udara boleh diterima.',\r\n            'Tidak Sihat': 'Kualiti udara tidak sihat — kurangkan aktiviti luar.',\r\n            'Unhealthy': 'Kualiti udara tidak sihat — kurangkan aktiviti luar.',\r\n            'Sangat Tidak Sihat': 'Kualiti udara sangat tidak sihat — elakkan aktiviti luar.',\r\n            'Very Unhealthy': 'Kualiti udara sangat tidak sihat — elakkan aktiviti luar.',\r\n            'Berbahaya': 'Kualiti udara berbahaya — kekal di dalam rumah.',\r\n            'Hazardous': 'Kualiti udara berbahaya — kekal di dalam rumah.'\r\n        };\r\n\r\n        function loadAir() {\r\n            fetch(API.air)\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    if (!d || !d.ok) throw new Error('no data');\r\n\r\n                    var val = d.api != null ? d.api : 0;\r\n\r\n                    $('airValue').textContent = d.api != null ? d.api : '–';\r\n                    $('airClass').textContent = d.category || '–';\r\n                    $('airCaption').textContent = AIR_CAPTION[d.category] || 'Status kualiti udara semasa.';\r\n                    $('airUpdated').textContent = 'Stesen ' + (d.station_id || '–') + ' · ' +\r\n                        (d.location || '') + (d.date_time ? ' · ' + String(d.date_time).replace('T', ' ') : '');\r\n\r\n                    var pct = Math.max(0, Math.min(val, 300)) / 300;\r\n                    var deg = -90 + (pct * 180);\r\n                    var rad = (deg - 90) * Math.PI / 180;\r\n                    var len = 72;\r\n\r\n                    $('airNeedle').setAttribute('x2', (110 + (len * Math.cos(rad))).toFixed(1));\r\n                    $('airNeedle').setAttribute('y2', (105 + (len * Math.sin(rad))).toFixed(1));\r\n                })\r\n                .catch(function () {\r\n                    $('airClass').textContent = 'Tidak tersedia';\r\n                    $('airCaption').textContent = 'Data IPU tidak tersedia buat masa ini.';\r\n                });\r\n        }\r\n\r\n        /* ── 3. AMARAN CUACA ──────────────────────────────────────── */\r\n        function loadWarning() {\r\n            fetch(API.warning)\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    if (!d || !d.ok) throw new Error('no data');\r\n\r\n                    var title = d.title || 'Tiada Nasihat';\r\n                    var calm = /tiada/i.test(title);\r\n\r\n                    $('warnTitle').textContent = title;\r\n                    $('warnIcon').textContent = calm ? '✓' : '!';\r\n                    $('warnIcon').classList.toggle('is-alert', !calm);\r\n                    $('warnText').textContent = calm\r\n                        ? 'Keadaan cuaca dan jerebu adalah normal di seluruh negara.'\r\n                        : String(d.text || '').replace(/SEKSYEN[^:]*:/g, '').substring(0, 260);\r\n                    $('warnUpdated').textContent = d.issued\r\n                        ? 'Dikeluarkan ' + String(d.issued).replace('T', ' ')\r\n                        : 'Status semasa';\r\n                })\r\n                .catch(function () {\r\n                    $('warnTitle').textContent = 'Tidak dapat disemak';\r\n                    $('warnText').textContent = 'Maklumat amaran tidak tersedia buat masa ini.';\r\n                });\r\n        }\r\n\r\n        /* ── 4. STATISTIK LAWATAN ─────────────────────────────────── */\r\n        function loadStats() {\r\n            fetch(API.stats)\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    if (!d || !d.ok) throw new Error('no data');\r\n\r\n                    $('statYear').textContent = d.year_label || '';\r\n                    $('statTotal').textContent = fmt(d.total);\r\n                    $('statToday').textContent = fmt(d.today);\r\n                    $('statMonth').textContent = fmt(d.month);\r\n                    $('statYearTotal').textContent = fmt(d.year);\r\n\r\n                    if (d.growth !== null && d.growth !== undefined) {\r\n                        var up = d.growth >= 0;\r\n\r\n                        $('statGrowth').innerHTML = '<b>' + (up ? '▲' : '▼') + ' ' +\r\n                            Math.abs(d.growth) + '%</b> berbanding ' + (d.year_label - 1);\r\n                    } else {\r\n                        $('statGrowth').innerHTML = '<b>' + fmt(d.visitors) + '</b> pelawat unik';\r\n                    }\r\n                })\r\n                .catch(function () {\r\n                    $('statTotal').textContent = '–';\r\n                    $('statGrowth').textContent = 'Statistik tidak tersedia';\r\n                });\r\n        }\r\n\r\n        /* ── 5. KALENDAR ──────────────────────────────────────────── */\r\n        var calSec = document.getElementById('kalendar');\r\n        var calUrl = calSec ? calSec.dataset.calendar : null;\r\n        var calState = { y: null, m: null, items: [], selected: null };\r\n\r\n        var BULAN = ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'];\r\n\r\n        function pad(n) { return (n < 10 ? '0' : '') + n; }\r\n\r\n        function iso(y, m, d) { return y + '-' + pad(m) + '-' + pad(d); }\r\n\r\n        function loadCalendar(y, m) {\r\n            if (!calUrl) return;\r\n\r\n            calState.y = y;\r\n            calState.m = m;\r\n            calState.selected = null;\r\n\r\n            $('calendarMonth').textContent = BULAN[m - 1] + ' ' + y;\r\n\r\n            fetch(calUrl + '?month=' + y + '-' + pad(m))\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    calState.items = (d && d.items) ? d.items : [];\r\n                    renderCalendar();\r\n                    renderMonthList();\r\n                })\r\n                .catch(function () {\r\n                    calState.items = [];\r\n                    renderCalendar();\r\n                    renderMonthList();\r\n                });\r\n        }\r\n\r\n        function eventsOn(dateStr) {\r\n            return calState.items.filter(function (it) {\r\n                var s = it.date;\r\n                var e = it.date_end || it.date;\r\n\r\n                return dateStr >= s && dateStr <= e;\r\n            });\r\n        }\r\n\r\n        function renderCalendar() {\r\n            var box = $('calendarDays');\r\n\r\n            if (!box) return;\r\n\r\n            var first = new Date(calState.y, calState.m - 1, 1);\r\n            var days = new Date(calState.y, calState.m, 0).getDate();\r\n\r\n            // Isnin = 0\r\n            var offset = (first.getDay() + 6) % 7;\r\n            var html = '';\r\n            var i;\r\n\r\n            for (i = 0; i < offset; i++) {\r\n                html += '<span></span>';\r\n            }\r\n\r\n            for (i = 1; i <= days; i++) {\r\n                var ds = iso(calState.y, calState.m, i);\r\n                var ev = eventsOn(ds);\r\n\r\n                html += '<button type=\"button\" data-date=\"' + ds + '\" aria-label=\"' + i + ' ' +\r\n                    BULAN[calState.m - 1] + ' ' + calState.y + '; ' + ev.length + ' aktiviti\"' +\r\n                    (ev.length ? ' class=\"has-event\"' : '') + '>' + i + '</button>';\r\n            }\r\n\r\n            box.innerHTML = html;\r\n        }\r\n\r\n        function calItemHtml(it) {\r\n            return '<div class=\"cal-item\"><b>' + (it.title || '(tanpa tajuk)') + '</b>' +\r\n                '<span>' + (it.time || '') + (it.location ? ' · ' + it.location : '') + '</span>' +\r\n                (it.category ? '<span class=\"cal-tag\">' + it.category + '</span>' : '') +\r\n                '</div>';\r\n        }\r\n\r\n        function selectDay(ds) {\r\n            calState.selected = ds;\r\n\r\n            var btns = document.querySelectorAll('#calendarDays button');\r\n\r\n            for (var i = 0; i < btns.length; i++) {\r\n                var on = btns[i].dataset.date === ds;\r\n\r\n                btns[i].classList.toggle('selected', on);\r\n                btns[i].setAttribute('aria-pressed', on ? 'true' : 'false');\r\n            }\r\n\r\n            var parts = ds.split('-');\r\n            $('selectedDate').textContent = parseInt(parts[2], 10) + ' ' + BULAN[parseInt(parts[1], 10) - 1] + ' ' + parts[0];\r\n\r\n            var ev = eventsOn(ds);\r\n\r\n            $('dayEvents').innerHTML = ev.length\r\n                ? ev.map(calItemHtml).join('')\r\n                : '<p class=\"small text-body-secondary\">Tiada aktiviti disenaraikan pada tarikh ini.</p>';\r\n        }\r\n\r\n        function renderMonthList() {\r\n            $('monthEvents').innerHTML = calState.items.length\r\n                ? calState.items.map(calItemHtml).join('')\r\n                : '<p class=\"small text-body-secondary\">Tiada aktiviti disenaraikan untuk bulan ini.</p>';\r\n        }\r\n\r\n        if (calSec && calUrl) {\r\n            var now = new Date();\r\n\r\n            loadCalendar(now.getFullYear(), now.getMonth() + 1);\r\n\r\n            $('previousMonth').addEventListener('click', function () {\r\n                var m = calState.m - 1;\r\n                var y = calState.y;\r\n\r\n                if (m < 1) { m = 12; y--; }\r\n\r\n                loadCalendar(y, m);\r\n            });\r\n\r\n            $('nextMonth').addEventListener('click', function () {\r\n                var m = calState.m + 1;\r\n                var y = calState.y;\r\n\r\n                if (m > 12) { m = 1; y++; }\r\n\r\n                loadCalendar(y, m);\r\n            });\r\n\r\n            $('calendarDays').addEventListener('click', function (e) {\r\n                var b = e.target.closest('button[data-date]');\r\n\r\n                if (b) selectDay(b.dataset.date);\r\n            });\r\n        }\r\n\r\n        /* ── 6. 10 HALAMAN PALING POPULAR ─────────────────────────── */\r\n        var popSec = document.getElementById('statistik');\r\n\r\n        function loadPopular() {\r\n            if (!popSec) return;\r\n\r\n            fetch(popSec.dataset.popular + '?limit=10')\r\n                .then(function (r) { return r.json(); })\r\n                .then(function (d) {\r\n                    var list = $('popularPages');\r\n                    var empty = $('popularEmpty');\r\n\r\n                    if (!d || !d.ok || !d.items || !d.items.length) {\r\n                        if (empty) empty.hidden = false;\r\n\r\n                        return;\r\n                    }\r\n\r\n                    if (empty) empty.hidden = true;\r\n\r\n                    list.innerHTML = d.items.map(function (it) {\r\n                        return '<li><a href=\"' + it.path + '\">' + it.path + '</a>' +\r\n                            '<em>' + fmt(it.total) + ' lawatan · ' + fmt(it.visitors) + ' pelawat</em></li>';\r\n                    }).join('');\r\n                })\r\n                .catch(function () {\r\n                    var empty = $('popularEmpty');\r\n\r\n                    if (empty) empty.hidden = false;\r\n                });\r\n        }\r\n\r\n        /* ── INIT ─────────────────────────────────────────────────── */\r\n        loadWeather();\r\n        loadAir();\r\n        loadWarning();\r\n        loadStats();\r\n        loadPopular();\r\n    })();\r\n</script>\r\n",
        "type": "PHP",
        "category": "Section",
        "status": 1,
        "created_at": "2026-11-01 01:01:32",
        "updated_at": "2026-11-01 16:41:11"
    },
    "pages": [
        {
            "id": 96,
            "page_fk": 22,
            "component_fk": 29,
            "sort": 5,
            "params": null,
            "created_at": null,
            "updated_at": null
        }
    ]
}