templates/backend/base/base.html.twig line 1

Open in your IDE?
  1. <!doctype html>
  2. <html lang="en" >
  3.     <head>
  4.     <style>
  5.    .tooltip {
  6.       --bs-tooltip-bg: #bdc7d7 !important;
  7.       --bs-tooltip-color: #000 !important;
  8.     }
  9.     </style>
  10.         <meta charset="utf-8">
  11.         <meta name="viewport" content="width=device-width, initial-scale=1">
  12.         <title>Dashboard SGN</title>
  13.         <!--favicon-->
  14.         <link rel="icon" href="{{ asset('frontend/img/logo.png') }}" type="image/png">
  15.         <!-- loader -->
  16.         <link href="{{ asset('new_template/css/pace.min.css') }}" rel="stylesheet">
  17.         <script src="{{ asset('new_template/js/pace.min.js') }}"></script>
  18.         <!--plugins-->
  19.         <link href="{{ asset('new_template/plugins/perfect-scrollbar/css/perfect-scrollbar.css') }}" rel="stylesheet"> 
  20.     <link href="{{ asset('new_template/plugins/metismenu/metisMenu.min.css') }}" rel="stylesheet">
  21.         <link href="{{ asset('new_template/plugins/metismenu/mm-vertical.css') }}" rel="stylesheet">
  22.         <link href="{{ asset('new_template/plugins/simplebar/css/simplebar.css') }}" rel="stylesheet">
  23.         <!--bootstrap css-->
  24.         <link href="{{ asset('new_template/css/bootstrap.min.css') }}" rel="stylesheet">
  25.         <link href="{{ asset('new_template/plugins/datatable/css/dataTables.bootstrap5.min.css') }}" rel="stylesheet"/>
  26.     <link rel="stylesheet" href="{{ asset('new_template/css/extra-icons.css') }}">
  27.     <link rel="stylesheet" href="{{ asset('new_template/css/flatpickr.min.css') }}">
  28.     <link href="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack.min.css" rel="stylesheet"/>
  29.         <link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
  30.         <link href="https://fonts.googleapis.com/css?family=Material+Icons+Outlined" rel="stylesheet">
  31.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"/>
  32.         <!--main css-->
  33.         <link href="{{ asset('new_template/css/bootstrap-extended.css') }}" rel="stylesheet">
  34.         <link href="{{ asset('new_template/sass/main.css') }}" rel="stylesheet">
  35.         <link href="{{ asset('new_template/sass/dark-theme.css') }}" rel="stylesheet">
  36.         <link href="{{ asset('new_template/sass/blue-theme.css') }}" rel="stylesheet">
  37.         <link href="{{ asset('new_template/sass/semi-dark.css') }}" rel="stylesheet">
  38.         <link href="{{ asset('new_template/sass/bordered-theme.css') }}" rel="stylesheet">
  39.         <link href="{{ asset('new_template/sass/responsive.css') }}" rel="stylesheet">
  40.         <link href="{{ asset('new_template/css/custom-new-template.css') }}" rel="stylesheet">
  41.     <link rel="stylesheet" href="{{ asset('frontend/select2/select2.css') }}">
  42.     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
  43.       
  44.     {# core dulu #}
  45.         <script src="{{ asset('new_template/js/jquery.min.js') }}"></script>
  46.         <script src="{{ asset('new_template/js/bootstrap.bundle.min.js') }}"></script>
  47.         {# plugins layout #}
  48.         <script src="{{ asset('new_template/plugins/metismenu/metisMenu.min.js') }}"></script>
  49.         <script src="{{ asset('new_template/plugins/datatable/js/jquery.dataTables.min.js') }}"></script>
  50.         <script src="{{ asset('new_template/plugins/datatable/js/dataTables.bootstrap5.min.js') }}"></script>
  51.         <script src="{{ asset('new_template/plugins/perfect-scrollbar/js/perfect-scrollbar.js') }}"></script>
  52.         <script src="{{ asset('new_template/plugins/simplebar/js/simplebar.min.js') }}"></script>
  53.         {# plugin opsional #}
  54.         <script src="{{ asset('new_template/plugins/apexchart/apexcharts.min.js') }}"></script>
  55.         <script src="{{ asset('new_template/plugins/peity/jquery.peity.min.js') }}"></script>
  56.         <script src="{{ asset('new_template/js/flatpickr.js') }}"></script>
  57.     <script src="{{ asset('frontend/select2/select2.js') }}"></script>
  58.     <script src="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack-all.min.js"></script>
  59.     <script src="https://cdn.jsdelivr.net/npm/gridstack@9.2.2/dist/gridstack-all.min.js"></script>
  60.     <script src="{{ asset('frontend/assets/plugins/leaflet/leaflet.js') }}"></script>
  61.     <script src="{{ asset('frontend/assets/plugins/leaflet/air.js') }}"></script>
  62.     <script src="{{ asset('frontend/select2/select2.min.js') }}"></script>
  63.     <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
  64.         {# js tema umum #}
  65.         <script src="{{ asset('new_template/js/main.js') }}"></script>
  66.     <script src="https://code.highcharts.com/highcharts.js"></script>
  67.     <script src="https://code.highcharts.com/modules/exporting.js"></script>
  68.     <script src="https://code.highcharts.com/modules/export-data.js"></script>
  69.     <script src="https://code.highcharts.com/modules/accessibility.js"></script>
  70.     </head>
  71.     <body>
  72.         <!--start header-->
  73.         {% include 'backend/base/_header.html.twig' %}
  74.         <!--end top header-->
  75.         <!--start sidebar-->
  76.         {% include 'backend/base/_sidebar.html.twig' %}
  77.         <!--end sidebar-->
  78.     <div class="modal fade" id="modal" tabindex="-1" aria-hidden="true">
  79.         <div class="modal-dialog modal-lg modal-dialog-scrollable">
  80.             <div class="modal-content"></div>
  81.         </div>
  82.     </div>
  83.         <!--start main wrapper-->
  84.             <main class="main-wrapper"> 
  85.         <div class="main-content">
  86.           <!--breadcrumb-->
  87.           <div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
  88.             {{ kmj_breadcrumb()|raw }}
  89.           </div>
  90.           <!--end breadcrumb-->
  91.           <div class="row"> 
  92.             {% block body %}
  93.             {% endblock %}
  94.           </div>
  95.         </div>
  96.             </main>
  97.             <!--end main wrapper-->
  98.             <!--start switcher-->
  99.             {% include 'backend/base/_theme_switcher.html.twig' %}
  100.             <!--start switcher-->
  101.             {# Safe initializations #}
  102.       <script>
  103.         (function (win, doc, $) {
  104.           function initMetis() {
  105.             var el = doc.getElementById('sidenav');
  106.             if (!el || !win.MetisMenu) return;
  107.             try { if (el.metisMenuObj && el.metisMenuObj.dispose) el.metisMenuObj.dispose(); } catch(e){}
  108.             el.metisMenuObj = new win.MetisMenu(el);
  109.           }
  110.           function initPS() {
  111.             if (!win.PerfectScrollbar) return;
  112.             var nodes = doc.querySelectorAll('.user-list, [data-ps], [data-simplebar="true"]');
  113.             nodes.forEach(function (n) { try { new win.PerfectScrollbar(n); } catch(e){} });
  114.           }
  115.           function initPeity() {
  116.             if (!$ || !$.fn || !$.fn.peity) return;
  117.             $('.data-attributes span').peity('donut');
  118.           }
  119.           doc.addEventListener('DOMContentLoaded', function(){ initMetis(); initPS(); initPeity(); });
  120.           win.addEventListener('load', function(){ initMetis(); initPS(); initPeity(); });
  121.         })(window, document, window.jQuery);
  122.       </script>
  123.       {# Modal loader + dispatch modal:content-ready #}
  124.       <script>
  125.         (function () {
  126.           const modalEl = document.getElementById('modal');
  127.           if (!modalEl) return;
  128.           document.addEventListener('click', function (ev) {
  129.             const trigger = ev.target.closest(
  130.               '.btn-modal,' +
  131.               '[data-bs-toggle="modal"][data-bs-target="#modal"],' +
  132.               '[data-toggle="modal"][data-target="#modal"]'
  133.             );
  134.             if (!trigger) return;
  135.             ev.preventDefault();
  136.             const url = trigger.getAttribute('href') || trigger.dataset.url || trigger.getAttribute('data-url');
  137.             const content = modalEl.querySelector('.modal-content');
  138.             // placeholder loading
  139.             content.innerHTML = '<div class="modal-body p-5 text-center">Loading…</div>';
  140.             // tampilkan modal
  141.             const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
  142.             modal.show();
  143.             if (!url) return;
  144.             fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
  145.               .then(r => r.text())
  146.               .then(html => {
  147.                 content.innerHTML = html;
  148.                 // normalisasi tombol close lama → BS5
  149.                 content.querySelectorAll('[data-dismiss="modal"]').forEach(function (el) {
  150.                   el.setAttribute('data-bs-dismiss', 'modal');
  151.                   el.removeAttribute('data-dismiss');
  152.                 });
  153.                 content.querySelectorAll('.close').forEach(function (oldBtn) {
  154.                   if (oldBtn.tagName === 'BUTTON') {
  155.                     oldBtn.classList.remove('close');
  156.                     oldBtn.classList.add('btn-close');
  157.                     oldBtn.setAttribute('data-bs-dismiss', 'modal');
  158.                     oldBtn.setAttribute('aria-label', 'Close');
  159.                     const x = oldBtn.querySelector('span[aria-hidden="true"]');
  160.                     if (x) x.remove();
  161.                   } else {
  162.                     const btn = document.createElement('button');
  163.                     btn.type = 'button';
  164.                     btn.className = 'btn-close';
  165.                     btn.setAttribute('data-bs-dismiss', 'modal');
  166.                     btn.setAttribute('aria-label', 'Close');
  167.                     oldBtn.replaceWith(btn);
  168.                   }
  169.                 });
  170.                 // Re-init Select2 (scoped ke modal)
  171.                 if (window.jQuery && jQuery.fn.select2) {
  172.                   jQuery(content).find('.select12').select2({
  173.                     allowClear: true,
  174.                     width: 'resolve',
  175.                     dropdownParent: jQuery(modalEl)
  176.                   });
  177.                 }
  178.                 content.dispatchEvent(new CustomEvent('modal:content-ready', { bubbles: true }));
  179.               })
  180.               .catch(() => {
  181.                 content.innerHTML = '<div class="modal-body text-danger p-4">Gagal memuat konten.</div>';
  182.               });
  183.           });
  184.           // dukung penutup modal lama
  185.           modalEl.addEventListener('click', function (e) {
  186.             const legacyClose = e.target.closest('[data-dismiss="modal"], .close');
  187.             if (!legacyClose) return;
  188.             const inst = bootstrap.Modal.getInstance(modalEl) || bootstrap.Modal.getOrCreateInstance(modalEl);
  189.             inst.hide();
  190.           });
  191.         })();
  192.       </script>
  193.       <script>
  194.         document.addEventListener("DOMContentLoaded", function(){
  195.           var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
  196.           var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
  197.             return new bootstrap.Tooltip(tooltipTriggerEl)
  198.           })
  199.         });
  200.       </script>
  201.       <script>
  202.         // inject URL dari Symfony ke JS global (kalau dipakai di explore-data.js)
  203.         window.EXPLORE_ROUTES = {
  204.           getColumn: "{{ path('explore_data_get_column') }}",
  205.           getColumnTgrafik: "{{ path('t_grafik_get_column') }}"
  206.         };
  207.       </script>
  208.       {# <script>
  209.           $(".datepicker").flatpickr();
  210.           $(".time-picker").flatpickr({
  211.               enableTime: true,
  212.               noCalendar: true,
  213.               dateFormat: "Y-m-d H:i",
  214.             });
  215.           $(".date-time").flatpickr({
  216.               enableTime: true,
  217.               dateFormat: "Y-m-d H:i",
  218.           });
  219.           $(".date-format").flatpickr({
  220.             altInput: true,
  221.             altFormat: "F j, Y",
  222.             dateFormat: "Y-m-d",
  223.           });
  224.           $(".date-range").flatpickr({
  225.             mode: "range",
  226.             altInput: true,
  227.             altFormat: "F j, Y",
  228.             dateFormat: "Y-m-d",
  229.           });
  230.           $(".date-inline").flatpickr({
  231.             inline: true,
  232.             altInput: true,
  233.             altFormat: "F j, Y",
  234.             dateFormat: "Y-m-d",
  235.           });
  236.         </script> #}
  237.       <script>
  238.         let __postingDates = null;
  239.         function getPostingDates() {
  240.           if (__postingDates) return Promise.resolve(__postingDates);
  241.           return fetch('{{ path('dashboard_dates') }}')
  242.             .then(r => r.json())
  243.             .then(d => (__postingDates = d))
  244.             .catch(() => (__postingDates = []));
  245.         }
  246.         function initScopedPickers(rootEl) {
  247.           const root = rootEl || document;
  248.           // kalau root berada di dalam modal → pakai modal sebagai appendTo
  249.           // kalau bukan modal → jangan set appendTo (biarkan default)
  250.           const modalEl = (root instanceof Element) ? root.closest('.modal') : null;
  251.           const useAppendTo = !!modalEl;
  252.           // helper utk menyisipkan appendTo hanya jika di modal
  253.           const withAppend = (opts) => useAppendTo ? Object.assign(opts, { appendTo: modalEl }) : opts;
  254.           // DATE ONLY
  255.           root.querySelectorAll('.datepicker').forEach(el => {
  256.             if (el._flatpickr) return;
  257.             getPostingDates().then(dates => {
  258.               flatpickr(el, withAppend({
  259.                 dateFormat: 'Y-m-d',
  260.                 onDayCreate: function(dObj, dStr, fp, dayElem) {
  261.                   const ymd = dayElem.dateObj.toLocaleDateString('en-CA');
  262.                   if (Array.isArray(dates) && dates.includes(ymd)) {
  263.                     dayElem.classList.add('has-data');
  264.                     dayElem.title = 'Ada data posting';
  265.                   }
  266.                 }
  267.               }));
  268.             });
  269.           });
  270.           // TIME ONLY
  271.           root.querySelectorAll('.time-picker').forEach(el => {
  272.             if (el._flatpickr) return;
  273.             flatpickr(el, withAppend({
  274.               enableTime: true,
  275.               noCalendar: true,
  276.               dateFormat: 'Y-m-d H:i'
  277.             }));
  278.           });
  279.           // DATE TIME
  280.           root.querySelectorAll('.date-time').forEach(el => {
  281.             if (el._flatpickr) return;
  282.             flatpickr(el, withAppend({
  283.               enableTime: true,
  284.               dateFormat: 'Y-m-d H:i'
  285.             }));
  286.           });
  287.           // DATE with alt display
  288.           root.querySelectorAll('.date-format').forEach(el => {
  289.             if (el._flatpickr) return;
  290.             flatpickr(el, withAppend({
  291.               altInput: true,
  292.               altFormat: 'F j, Y',
  293.               dateFormat: 'Y-m-d'
  294.             }));
  295.           });
  296.           // RANGE
  297.           root.querySelectorAll('.date-range').forEach(el => {
  298.             if (el._flatpickr) return;
  299.             flatpickr(el, withAppend({
  300.               mode: 'range',
  301.               altInput: true,
  302.               altFormat: 'F j, Y',
  303.               dateFormat: 'Y-m-d'
  304.             }));
  305.           });
  306.           // INLINE
  307.           root.querySelectorAll('.date-inline').forEach(el => {
  308.             if (el._flatpickr) return;
  309.             flatpickr(el, withAppend({
  310.               inline: true,
  311.               altInput: true,
  312.               altFormat: 'F j, Y',
  313.               dateFormat: 'Y-m-d'
  314.             }));
  315.           });
  316.         }
  317.         // INIT DI HALAMAN UTAMA
  318.         document.addEventListener('DOMContentLoaded', function() {
  319.           initScopedPickers(document);
  320.         });
  321.         const globalModal = document.getElementById('modal');
  322.         if (globalModal) {
  323.           globalModal.addEventListener('modal:content-ready', function (ev) {
  324.             const content = ev.target; // .modal-content
  325.             initScopedPickers(content);
  326.           });
  327.         }
  328.       </script>
  329.       {# Script spesifik aplikasi #}
  330.       <script src="{{ asset('new_template/js/explore-data.js') }}"></script>
  331.       <script src="{{ asset('new_template/js/t-grafik.js') }}"></script>
  332.       <script src="{{ asset('new_template/js/modal-new.js') }}"></script>
  333.       {% block page_scripts %}{% endblock %}
  334.     </body>
  335. </html>