<!doctype html>
<html lang="en" >
<head>
<style>
.tooltip {
--bs-tooltip-bg: #bdc7d7 !important;
--bs-tooltip-color: #000 !important;
}
</style>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dashboard SGN</title>
<!--favicon-->
<link rel="icon" href="{{ asset('frontend/img/logo.png') }}" type="image/png">
<!-- loader -->
<link href="{{ asset('new_template/css/pace.min.css') }}" rel="stylesheet">
<script src="{{ asset('new_template/js/pace.min.js') }}"></script>
<!--plugins-->
<link href="{{ asset('new_template/plugins/perfect-scrollbar/css/perfect-scrollbar.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/plugins/metismenu/metisMenu.min.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/plugins/metismenu/mm-vertical.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/plugins/simplebar/css/simplebar.css') }}" rel="stylesheet">
<!--bootstrap css-->
<link href="{{ asset('new_template/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/plugins/datatable/css/dataTables.bootstrap5.min.css') }}" rel="stylesheet"/>
<link rel="stylesheet" href="{{ asset('new_template/css/extra-icons.css') }}">
<link rel="stylesheet" href="{{ asset('new_template/css/flatpickr.min.css') }}">
<link href="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons+Outlined" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"/>
<!--main css-->
<link href="{{ asset('new_template/css/bootstrap-extended.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/main.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/dark-theme.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/blue-theme.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/semi-dark.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/bordered-theme.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/sass/responsive.css') }}" rel="stylesheet">
<link href="{{ asset('new_template/css/custom-new-template.css') }}" rel="stylesheet">
<link rel="stylesheet" href="{{ asset('frontend/select2/select2.css') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
{# core dulu #}
<script src="{{ asset('new_template/js/jquery.min.js') }}"></script>
<script src="{{ asset('new_template/js/bootstrap.bundle.min.js') }}"></script>
{# plugins layout #}
<script src="{{ asset('new_template/plugins/metismenu/metisMenu.min.js') }}"></script>
<script src="{{ asset('new_template/plugins/datatable/js/jquery.dataTables.min.js') }}"></script>
<script src="{{ asset('new_template/plugins/datatable/js/dataTables.bootstrap5.min.js') }}"></script>
<script src="{{ asset('new_template/plugins/perfect-scrollbar/js/perfect-scrollbar.js') }}"></script>
<script src="{{ asset('new_template/plugins/simplebar/js/simplebar.min.js') }}"></script>
{# plugin opsional #}
<script src="{{ asset('new_template/plugins/apexchart/apexcharts.min.js') }}"></script>
<script src="{{ asset('new_template/plugins/peity/jquery.peity.min.js') }}"></script>
<script src="{{ asset('new_template/js/flatpickr.js') }}"></script>
<script src="{{ asset('frontend/select2/select2.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack-all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack-all.min.js"></script>
<script src="{{ asset('frontend/assets/plugins/leaflet/leaflet.js') }}"></script>
<script src="{{ asset('frontend/assets/plugins/leaflet/air.js') }}"></script>
<script src="{{ asset('frontend/select2/select2.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
{# js tema umum #}
<script src="{{ asset('new_template/js/main.js') }}"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
</head>
<body>
<!--start header-->
{% include 'backend/base/_header.html.twig' %}
<!--end top header-->
<!--start sidebar-->
{% include 'backend/base/_sidebar.html.twig' %}
<!--end sidebar-->
<div class="modal fade" id="modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content"></div>
</div>
</div>
<!--start main wrapper-->
<main class="main-wrapper">
<div class="main-content">
<!--breadcrumb-->
<div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
{{ kmj_breadcrumb()|raw }}
</div>
<!--end breadcrumb-->
<div class="row">
{% block body %}
{% endblock %}
</div>
</div>
</main>
<!--end main wrapper-->
<!--start switcher-->
{% include 'backend/base/_theme_switcher.html.twig' %}
<!--start switcher-->
{# Safe initializations #}
<script>
(function (win, doc, $) {
function initMetis() {
var el = doc.getElementById('sidenav');
if (!el || !win.MetisMenu) return;
try { if (el.metisMenuObj && el.metisMenuObj.dispose) el.metisMenuObj.dispose(); } catch(e){}
el.metisMenuObj = new win.MetisMenu(el);
}
function initPS() {
if (!win.PerfectScrollbar) return;
var nodes = doc.querySelectorAll('.user-list, [data-ps], [data-simplebar="true"]');
nodes.forEach(function (n) { try { new win.PerfectScrollbar(n); } catch(e){} });
}
function initPeity() {
if (!$ || !$.fn || !$.fn.peity) return;
$('.data-attributes span').peity('donut');
}
doc.addEventListener('DOMContentLoaded', function(){ initMetis(); initPS(); initPeity(); });
win.addEventListener('load', function(){ initMetis(); initPS(); initPeity(); });
})(window, document, window.jQuery);
</script>
{# Modal loader + dispatch modal:content-ready #}
<script>
(function () {
const modalEl = document.getElementById('modal');
if (!modalEl) return;
document.addEventListener('click', function (ev) {
const trigger = ev.target.closest(
'.btn-modal,' +
'[data-bs-toggle="modal"][data-bs-target="#modal"],' +
'[data-toggle="modal"][data-target="#modal"]'
);
if (!trigger) return;
ev.preventDefault();
const url = trigger.getAttribute('href') || trigger.dataset.url || trigger.getAttribute('data-url');
const content = modalEl.querySelector('.modal-content');
// placeholder loading
content.innerHTML = '<div class="modal-body p-5 text-center">Loading…</div>';
// tampilkan modal
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show();
if (!url) return;
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
.then(r => r.text())
.then(html => {
content.innerHTML = html;
// normalisasi tombol close lama → BS5
content.querySelectorAll('[data-dismiss="modal"]').forEach(function (el) {
el.setAttribute('data-bs-dismiss', 'modal');
el.removeAttribute('data-dismiss');
});
content.querySelectorAll('.close').forEach(function (oldBtn) {
if (oldBtn.tagName === 'BUTTON') {
oldBtn.classList.remove('close');
oldBtn.classList.add('btn-close');
oldBtn.setAttribute('data-bs-dismiss', 'modal');
oldBtn.setAttribute('aria-label', 'Close');
const x = oldBtn.querySelector('span[aria-hidden="true"]');
if (x) x.remove();
} else {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn-close';
btn.setAttribute('data-bs-dismiss', 'modal');
btn.setAttribute('aria-label', 'Close');
oldBtn.replaceWith(btn);
}
});
// Re-init Select2 (scoped ke modal)
if (window.jQuery && jQuery.fn.select2) {
jQuery(content).find('.select12').select2({
allowClear: true,
width: 'resolve',
dropdownParent: jQuery(modalEl)
});
}
content.dispatchEvent(new CustomEvent('modal:content-ready', { bubbles: true }));
})
.catch(() => {
content.innerHTML = '<div class="modal-body text-danger p-4">Gagal memuat konten.</div>';
});
});
// dukung penutup modal lama
modalEl.addEventListener('click', function (e) {
const legacyClose = e.target.closest('[data-dismiss="modal"], .close');
if (!legacyClose) return;
const inst = bootstrap.Modal.getInstance(modalEl) || bootstrap.Modal.getOrCreateInstance(modalEl);
inst.hide();
});
})();
</script>
<script>
document.addEventListener("DOMContentLoaded", function(){
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
});
</script>
<script>
// inject URL dari Symfony ke JS global (kalau dipakai di explore-data.js)
window.EXPLORE_ROUTES = {
getColumn: "{{ path('explore_data_get_column') }}",
getColumnTgrafik: "{{ path('t_grafik_get_column') }}"
};
</script>
{# <script>
$(".datepicker").flatpickr();
$(".time-picker").flatpickr({
enableTime: true,
noCalendar: true,
dateFormat: "Y-m-d H:i",
});
$(".date-time").flatpickr({
enableTime: true,
dateFormat: "Y-m-d H:i",
});
$(".date-format").flatpickr({
altInput: true,
altFormat: "F j, Y",
dateFormat: "Y-m-d",
});
$(".date-range").flatpickr({
mode: "range",
altInput: true,
altFormat: "F j, Y",
dateFormat: "Y-m-d",
});
$(".date-inline").flatpickr({
inline: true,
altInput: true,
altFormat: "F j, Y",
dateFormat: "Y-m-d",
});
</script> #}
<script>
let __postingDates = null;
function getPostingDates() {
if (__postingDates) return Promise.resolve(__postingDates);
return fetch('{{ path('dashboard_dates') }}')
.then(r => r.json())
.then(d => (__postingDates = d))
.catch(() => (__postingDates = []));
}
function initScopedPickers(rootEl) {
const root = rootEl || document;
// kalau root berada di dalam modal → pakai modal sebagai appendTo
// kalau bukan modal → jangan set appendTo (biarkan default)
const modalEl = (root instanceof Element) ? root.closest('.modal') : null;
const useAppendTo = !!modalEl;
// helper utk menyisipkan appendTo hanya jika di modal
const withAppend = (opts) => useAppendTo ? Object.assign(opts, { appendTo: modalEl }) : opts;
// DATE ONLY
root.querySelectorAll('.datepicker').forEach(el => {
if (el._flatpickr) return;
getPostingDates().then(dates => {
flatpickr(el, withAppend({
dateFormat: 'Y-m-d',
onDayCreate: function(dObj, dStr, fp, dayElem) {
const ymd = dayElem.dateObj.toLocaleDateString('en-CA');
if (Array.isArray(dates) && dates.includes(ymd)) {
dayElem.classList.add('has-data');
dayElem.title = 'Ada data posting';
}
}
}));
});
});
// TIME ONLY
root.querySelectorAll('.time-picker').forEach(el => {
if (el._flatpickr) return;
flatpickr(el, withAppend({
enableTime: true,
noCalendar: true,
dateFormat: 'Y-m-d H:i'
}));
});
// DATE TIME
root.querySelectorAll('.date-time').forEach(el => {
if (el._flatpickr) return;
flatpickr(el, withAppend({
enableTime: true,
dateFormat: 'Y-m-d H:i'
}));
});
// DATE with alt display
root.querySelectorAll('.date-format').forEach(el => {
if (el._flatpickr) return;
flatpickr(el, withAppend({
altInput: true,
altFormat: 'F j, Y',
dateFormat: 'Y-m-d'
}));
});
// RANGE
root.querySelectorAll('.date-range').forEach(el => {
if (el._flatpickr) return;
flatpickr(el, withAppend({
mode: 'range',
altInput: true,
altFormat: 'F j, Y',
dateFormat: 'Y-m-d'
}));
});
// INLINE
root.querySelectorAll('.date-inline').forEach(el => {
if (el._flatpickr) return;
flatpickr(el, withAppend({
inline: true,
altInput: true,
altFormat: 'F j, Y',
dateFormat: 'Y-m-d'
}));
});
}
// INIT DI HALAMAN UTAMA
document.addEventListener('DOMContentLoaded', function() {
initScopedPickers(document);
});
const globalModal = document.getElementById('modal');
if (globalModal) {
globalModal.addEventListener('modal:content-ready', function (ev) {
const content = ev.target; // .modal-content
initScopedPickers(content);
});
}
</script>
{# Script spesifik aplikasi #}
<script src="{{ asset('new_template/js/explore-data.js') }}"></script>
<script src="{{ asset('new_template/js/t-grafik.js') }}"></script>
<script src="{{ asset('new_template/js/modal-new.js') }}"></script>
{% block page_scripts %}{% endblock %}
</body>
</html>