681 lines
19 KiB
JavaScript
681 lines
19 KiB
JavaScript
/**
|
||
* Template Name: Invent
|
||
* Template URL: https://bootstrapmade.com/invent-bootstrap-business-template/
|
||
* Updated: May 12 2025 with Bootstrap v5.3.6
|
||
* Author: BootstrapMade.com
|
||
* License: https://bootstrapmade.com/license/
|
||
*/
|
||
|
||
(function() {
|
||
"use strict";
|
||
|
||
/**
|
||
* Apply .scrolled class to the body as the page is scrolled down
|
||
*/
|
||
function toggleScrolled() {
|
||
const selectBody = document.querySelector('body');
|
||
const selectHeader = document.querySelector('#header');
|
||
if (!selectHeader.classList.contains('scroll-up-sticky') && !selectHeader.classList.contains('sticky-top') && !selectHeader.classList.contains('fixed-top')) return;
|
||
window.scrollY > 100 ? selectBody.classList.add('scrolled') : selectBody.classList.remove('scrolled');
|
||
}
|
||
|
||
document.addEventListener('scroll', toggleScrolled);
|
||
window.addEventListener('load', toggleScrolled);
|
||
|
||
/**
|
||
* Mobile nav toggle
|
||
*/
|
||
const mobileNavToggleBtn = document.querySelector('.mobile-nav-toggle');
|
||
|
||
function mobileNavToogle() {
|
||
document.querySelector('body').classList.toggle('mobile-nav-active');
|
||
mobileNavToggleBtn.classList.toggle('bi-list');
|
||
mobileNavToggleBtn.classList.toggle('bi-x');
|
||
}
|
||
if (mobileNavToggleBtn) {
|
||
mobileNavToggleBtn.addEventListener('click', mobileNavToogle);
|
||
}
|
||
|
||
// ============================
|
||
// Sidebar Toggle
|
||
// ============================
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const toggleBtns = document.querySelectorAll('.toggle-sidebar-btn');
|
||
if (toggleBtns.length > 0) {
|
||
toggleBtns.forEach(btn => {
|
||
btn.addEventListener('click', function () {
|
||
document.body.classList.toggle('toggle-sidebar');
|
||
});
|
||
});
|
||
} else {
|
||
console.warn('⚠️ .toggle-sidebar-btn tidak ditemukan di DOM.');
|
||
}
|
||
// 2. Line Chart Implementation
|
||
const chartElement = document.querySelector("#reportsChart");
|
||
if (!chartElement) {
|
||
console.error("❌ Element #reportsChart not found");
|
||
return;
|
||
}
|
||
|
||
// Get chart data from data attribute
|
||
const chartDataJson = chartElement.getAttribute('data-chart-data');
|
||
let chartData = [];
|
||
|
||
try {
|
||
chartData = JSON.parse(chartDataJson || '[]');
|
||
console.log("📊 Chart data loaded:", chartData);
|
||
} catch (e) {
|
||
console.error("❌ Error parsing chart data:", e);
|
||
chartData = [0, 0, 0, 0];
|
||
}
|
||
|
||
// Check if ApexCharts is loaded
|
||
if (typeof ApexCharts === 'undefined') {
|
||
console.error("❌ ApexCharts library not loaded");
|
||
return;
|
||
}
|
||
|
||
// Create and render chart
|
||
try {
|
||
const chart = new ApexCharts(chartElement, {
|
||
series: [{
|
||
name: 'Jumlah Data',
|
||
data: chartData,
|
||
}],
|
||
chart: {
|
||
height: 350,
|
||
type: 'line',
|
||
toolbar: {
|
||
show: true
|
||
},
|
||
zoom: {
|
||
enabled: true
|
||
},
|
||
events: {
|
||
mounted: function(chartContext, config) {
|
||
console.log("✅ Chart mounted successfully");
|
||
// Hide fallback when chart is rendered
|
||
const fallback = chartElement.querySelector('.chart-fallback');
|
||
if (fallback) {
|
||
fallback.style.display = 'none';
|
||
}
|
||
},
|
||
error: function(error) {
|
||
console.error("❌ Chart error:", error);
|
||
}
|
||
}
|
||
},
|
||
dataLabels: {
|
||
enabled: false
|
||
},
|
||
stroke: {
|
||
curve: 'smooth',
|
||
width: 3
|
||
},
|
||
colors: ['#4154f1', '#2eca6a', '#ff771d', '#ff0000'],
|
||
grid: {
|
||
row: {
|
||
colors: ['#f3f3f3', 'transparent'],
|
||
opacity: 0.5
|
||
},
|
||
},
|
||
markers: {
|
||
size: 5,
|
||
colors: ['#4154f1'],
|
||
strokeColors: '#fff',
|
||
strokeWidth: 2
|
||
},
|
||
xaxis: {
|
||
categories: ['Guru', 'Jurusan', 'Fasilitas', 'Ekstrakurikuler'],
|
||
labels: {
|
||
style: {
|
||
fontSize: '14px',
|
||
fontWeight: 'bold'
|
||
}
|
||
}
|
||
},
|
||
yaxis: {
|
||
min: 0,
|
||
title: {
|
||
text: 'Jumlah',
|
||
style: {
|
||
fontSize: '14px',
|
||
fontWeight: 'bold'
|
||
}
|
||
},
|
||
labels: {
|
||
style: {
|
||
fontSize: '12px'
|
||
}
|
||
}
|
||
},
|
||
tooltip: {
|
||
y: {
|
||
formatter: function(value) {
|
||
return value + " data";
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Render chart after a small delay to ensure DOM is ready
|
||
setTimeout(() => {
|
||
chart.render();
|
||
console.log("🔄 Chart rendering started");
|
||
}, 100);
|
||
|
||
} catch (error) {
|
||
console.error("❌ Error creating chart:", error);
|
||
}
|
||
|
||
|
||
});
|
||
/**
|
||
* Hide mobile nav on same-page/hash links
|
||
*/
|
||
document.querySelectorAll('#navmenu a').forEach(navmenu => {
|
||
navmenu.addEventListener('click', () => {
|
||
if (document.querySelector('.mobile-nav-active')) {
|
||
mobileNavToogle();
|
||
}
|
||
});
|
||
});
|
||
|
||
/**
|
||
* Toggle mobile nav dropdowns
|
||
*/
|
||
document.querySelectorAll('.navmenu .toggle-dropdown').forEach(navmenu => {
|
||
navmenu.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
this.parentNode.classList.toggle('active');
|
||
this.parentNode.nextElementSibling.classList.toggle('dropdown-active');
|
||
e.stopImmediatePropagation();
|
||
});
|
||
});
|
||
|
||
/**
|
||
* Preloader
|
||
*/
|
||
const preloader = document.querySelector('#preloader');
|
||
if (preloader) {
|
||
window.addEventListener('load', () => {
|
||
preloader.remove();
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Scroll top button
|
||
*/
|
||
let scrollTop = document.querySelector('.scroll-top');
|
||
|
||
function toggleScrollTop() {
|
||
if (scrollTop) {
|
||
window.scrollY > 100 ? scrollTop.classList.add('active') : scrollTop.classList.remove('active');
|
||
}
|
||
}
|
||
scrollTop.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
window.scrollTo({
|
||
top: 0,
|
||
behavior: 'smooth'
|
||
});
|
||
});
|
||
|
||
window.addEventListener('load', toggleScrollTop);
|
||
document.addEventListener('scroll', toggleScrollTop);
|
||
|
||
/**
|
||
* Animation on scroll function and init
|
||
*/
|
||
function aosInit() {
|
||
AOS.init({
|
||
duration: 600,
|
||
easing: 'ease-in-out',
|
||
once: true,
|
||
mirror: false
|
||
});
|
||
}
|
||
window.addEventListener('load', aosInit);
|
||
|
||
/**
|
||
* Initiate glightbox
|
||
*/
|
||
const glightbox = GLightbox({
|
||
selector: '.glightbox'
|
||
});
|
||
|
||
/**
|
||
* Init isotope layout and filters
|
||
*/
|
||
document.querySelectorAll('.isotope-layout').forEach(function(isotopeItem) {
|
||
let layout = isotopeItem.getAttribute('data-layout') ?? 'masonry';
|
||
let filter = isotopeItem.getAttribute('data-default-filter') ?? '*';
|
||
let sort = isotopeItem.getAttribute('data-sort') ?? 'original-order';
|
||
|
||
let initIsotope;
|
||
imagesLoaded(isotopeItem.querySelector('.isotope-container'), function() {
|
||
initIsotope = new Isotope(isotopeItem.querySelector('.isotope-container'), {
|
||
itemSelector: '.isotope-item',
|
||
layoutMode: layout,
|
||
filter: filter,
|
||
sortBy: sort
|
||
});
|
||
});
|
||
|
||
isotopeItem.querySelectorAll('.isotope-filters li').forEach(function(filters) {
|
||
filters.addEventListener('click', function() {
|
||
isotopeItem.querySelector('.isotope-filters .filter-active').classList.remove('filter-active');
|
||
this.classList.add('filter-active');
|
||
initIsotope.arrange({
|
||
filter: this.getAttribute('data-filter')
|
||
});
|
||
if (typeof aosInit === 'function') {
|
||
aosInit();
|
||
}
|
||
}, false);
|
||
});
|
||
|
||
});
|
||
|
||
/**
|
||
* Frequently Asked Questions Toggle
|
||
*/
|
||
document.querySelectorAll('.faq-item h3, .faq-item .faq-toggle, .faq-item .faq-header').forEach((faqItem) => {
|
||
faqItem.addEventListener('click', () => {
|
||
faqItem.parentNode.classList.toggle('faq-active');
|
||
});
|
||
});
|
||
|
||
/**
|
||
* Init swiper sliders
|
||
*/
|
||
function initSwiper() {
|
||
document.querySelectorAll(".init-swiper").forEach(function(swiperElement) {
|
||
let config = JSON.parse(
|
||
swiperElement.querySelector(".swiper-config").innerHTML.trim()
|
||
);
|
||
|
||
if (swiperElement.classList.contains("swiper-tab")) {
|
||
initSwiperWithCustomPagination(swiperElement, config);
|
||
} else {
|
||
new Swiper(swiperElement, config);
|
||
}
|
||
});
|
||
}
|
||
|
||
window.addEventListener("load", initSwiper);
|
||
|
||
/**
|
||
* Correct scrolling position upon page load for URLs containing hash links.
|
||
*/
|
||
window.addEventListener('load', function(e) {
|
||
if (window.location.hash) {
|
||
if (document.querySelector(window.location.hash)) {
|
||
setTimeout(() => {
|
||
let section = document.querySelector(window.location.hash);
|
||
let scrollMarginTop = getComputedStyle(section).scrollMarginTop;
|
||
window.scrollTo({
|
||
top: section.offsetTop - parseInt(scrollMarginTop),
|
||
behavior: 'smooth'
|
||
});
|
||
}, 100);
|
||
}
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Navmenu Scrollspy
|
||
*/
|
||
let navmenulinks = document.querySelectorAll('.navmenu a');
|
||
|
||
function navmenuScrollspy() {
|
||
navmenulinks.forEach(navmenulink => {
|
||
if (!navmenulink.hash) return;
|
||
let section = document.querySelector(navmenulink.hash);
|
||
if (!section) return;
|
||
let position = window.scrollY + 200;
|
||
if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
|
||
document.querySelectorAll('.navmenu a.active').forEach(link => link.classList.remove('active'));
|
||
navmenulink.classList.add('active');
|
||
} else {
|
||
navmenulink.classList.remove('active');
|
||
}
|
||
})
|
||
}
|
||
window.addEventListener('load', navmenuScrollspy);
|
||
document.addEventListener('scroll', navmenuScrollspy);
|
||
|
||
})();
|
||
|
||
/**
|
||
* Template Name: NiceAdmin
|
||
* Updated: Jan 29 2024 with Bootstrap v5.3.2
|
||
* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/
|
||
* Author: BootstrapMade.com
|
||
* License: https://bootstrapmade.com/license/
|
||
*/
|
||
(function() {
|
||
"use strict";
|
||
|
||
/**
|
||
* Easy selector helper function
|
||
*/
|
||
const select = (el, all = false) => {
|
||
el = el.trim()
|
||
if (all) {
|
||
return [...document.querySelectorAll(el)]
|
||
} else {
|
||
return document.querySelector(el)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Easy event listener function
|
||
*/
|
||
const on = (type, el, listener, all = false) => {
|
||
if (all) {
|
||
select(el, all).forEach(e => e.addEventListener(type, listener))
|
||
} else {
|
||
select(el, all).addEventListener(type, listener)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Easy on scroll event listener
|
||
*/
|
||
const onscroll = (el, listener) => {
|
||
el.addEventListener('scroll', listener)
|
||
}
|
||
|
||
|
||
/**
|
||
* Search bar toggle
|
||
*/
|
||
if (select('.search-bar-toggle')) {
|
||
on('click', '.search-bar-toggle', function(e) {
|
||
select('.search-bar').classList.toggle('search-bar-show')
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Navbar links active state on scroll
|
||
*/
|
||
let navbarlinks = select('#navbar .scrollto', true)
|
||
const navbarlinksActive = () => {
|
||
let position = window.scrollY + 200
|
||
navbarlinks.forEach(navbarlink => {
|
||
if (!navbarlink.hash) return
|
||
let section = select(navbarlink.hash)
|
||
if (!section) return
|
||
if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
|
||
navbarlink.classList.add('active')
|
||
} else {
|
||
navbarlink.classList.remove('active')
|
||
}
|
||
})
|
||
}
|
||
window.addEventListener('load', navbarlinksActive)
|
||
onscroll(document, navbarlinksActive)
|
||
|
||
/**
|
||
* Toggle .header-scrolled class to #header when page is scrolled
|
||
*/
|
||
let selectHeader = select('#header')
|
||
if (selectHeader) {
|
||
const headerScrolled = () => {
|
||
if (window.scrollY > 100) {
|
||
selectHeader.classList.add('header-scrolled')
|
||
} else {
|
||
selectHeader.classList.remove('header-scrolled')
|
||
}
|
||
}
|
||
window.addEventListener('load', headerScrolled)
|
||
onscroll(document, headerScrolled)
|
||
}
|
||
|
||
/**
|
||
* Back to top button
|
||
*/
|
||
let backtotop = select('.back-to-top')
|
||
if (backtotop) {
|
||
const toggleBacktotop = () => {
|
||
if (window.scrollY > 100) {
|
||
backtotop.classList.add('active')
|
||
} else {
|
||
backtotop.classList.remove('active')
|
||
}
|
||
}
|
||
window.addEventListener('load', toggleBacktotop)
|
||
onscroll(document, toggleBacktotop)
|
||
}
|
||
|
||
/**
|
||
* Initiate tooltips
|
||
*/
|
||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||
var tooltipList = tooltipTriggerList.map(function(tooltipTriggerEl) {
|
||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||
})
|
||
|
||
/**
|
||
* Initiate quill editors
|
||
*/
|
||
if (select('.quill-editor-default')) {
|
||
new Quill('.quill-editor-default', {
|
||
theme: 'snow'
|
||
});
|
||
}
|
||
|
||
if (select('.quill-editor-bubble')) {
|
||
new Quill('.quill-editor-bubble', {
|
||
theme: 'bubble'
|
||
});
|
||
}
|
||
|
||
if (select('.quill-editor-full')) {
|
||
new Quill(".quill-editor-full", {
|
||
modules: {
|
||
toolbar: [
|
||
[{
|
||
font: []
|
||
}, {
|
||
size: []
|
||
}],
|
||
["bold", "italic", "underline", "strike"],
|
||
[{
|
||
color: []
|
||
},
|
||
{
|
||
background: []
|
||
}
|
||
],
|
||
[{
|
||
script: "super"
|
||
},
|
||
{
|
||
script: "sub"
|
||
}
|
||
],
|
||
[{
|
||
list: "ordered"
|
||
},
|
||
{
|
||
list: "bullet"
|
||
},
|
||
{
|
||
indent: "-1"
|
||
},
|
||
{
|
||
indent: "+1"
|
||
}
|
||
],
|
||
["direction", {
|
||
align: []
|
||
}],
|
||
["link", "image", "video"],
|
||
["clean"]
|
||
]
|
||
},
|
||
theme: "snow"
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Initiate TinyMCE Editor
|
||
*/
|
||
const useDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||
const isSmallScreen = window.matchMedia('(max-width: 1023.5px)').matches;
|
||
|
||
tinymce.init({
|
||
selector: 'textarea.tinymce-editor',
|
||
plugins: 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media template codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons',
|
||
editimage_cors_hosts: ['picsum.photos'],
|
||
menubar: 'file edit view insert format tools table help',
|
||
toolbar: 'undo redo | bold italic underline strikethrough | fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | insertfile image media template link anchor codesample | ltr rtl',
|
||
toolbar_sticky: true,
|
||
toolbar_sticky_offset: isSmallScreen ? 102 : 108,
|
||
autosave_ask_before_unload: true,
|
||
autosave_interval: '30s',
|
||
autosave_prefix: '{path}{query}-{id}-',
|
||
autosave_restore_when_empty: false,
|
||
autosave_retention: '2m',
|
||
image_advtab: true,
|
||
link_list: [{
|
||
title: 'My page 1',
|
||
value: 'https://www.tiny.cloud'
|
||
},
|
||
{
|
||
title: 'My page 2',
|
||
value: 'http://www.moxiecode.com'
|
||
}
|
||
],
|
||
image_list: [{
|
||
title: 'My page 1',
|
||
value: 'https://www.tiny.cloud'
|
||
},
|
||
{
|
||
title: 'My page 2',
|
||
value: 'http://www.moxiecode.com'
|
||
}
|
||
],
|
||
image_class_list: [{
|
||
title: 'None',
|
||
value: ''
|
||
},
|
||
{
|
||
title: 'Some class',
|
||
value: 'class-name'
|
||
}
|
||
],
|
||
importcss_append: true,
|
||
file_picker_callback: (callback, value, meta) => {
|
||
/* Provide file and text for the link dialog */
|
||
if (meta.filetype === 'file') {
|
||
callback('https://www.google.com/logos/google.jpg', {
|
||
text: 'My text'
|
||
});
|
||
}
|
||
|
||
/* Provide image and alt text for the image dialog */
|
||
if (meta.filetype === 'image') {
|
||
callback('https://www.google.com/logos/google.jpg', {
|
||
alt: 'My alt text'
|
||
});
|
||
}
|
||
|
||
/* Provide alternative source and posted for the media dialog */
|
||
if (meta.filetype === 'media') {
|
||
callback('movie.mp4', {
|
||
source2: 'alt.ogg',
|
||
poster: 'https://www.google.com/logos/google.jpg'
|
||
});
|
||
}
|
||
},
|
||
templates: [{
|
||
title: 'New Table',
|
||
description: 'creates a new table',
|
||
content: '<div class="mceTmpl"><table width="98%%" border="0" cellspacing="0" cellpadding="0"><tr><th scope="col"> </th><th scope="col"> </th></tr><tr><td> </td><td> </td></tr></table></div>'
|
||
},
|
||
{
|
||
title: 'Starting my story',
|
||
description: 'A cure for writers block',
|
||
content: 'Once upon a time...'
|
||
},
|
||
{
|
||
title: 'New list with dates',
|
||
description: 'New List with dates',
|
||
content: '<div class="mceTmpl"><span class="cdate">cdate</span><br><span class="mdate">mdate</span><h2>My List</h2><ul><li></li><li></li></ul></div>'
|
||
}
|
||
],
|
||
template_cdate_format: '[Date Created (CDATE): %m/%d/%Y : %H:%M:%S]',
|
||
template_mdate_format: '[Date Modified (MDATE): %m/%d/%Y : %H:%M:%S]',
|
||
height: 600,
|
||
image_caption: true,
|
||
quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
|
||
noneditable_class: 'mceNonEditable',
|
||
toolbar_mode: 'sliding',
|
||
contextmenu: 'link image table',
|
||
skin: useDarkMode ? 'oxide-dark' : 'oxide',
|
||
content_css: useDarkMode ? 'dark' : 'default',
|
||
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
|
||
});
|
||
|
||
/**
|
||
* Initiate Bootstrap validation check
|
||
*/
|
||
var needsValidation = document.querySelectorAll('.needs-validation')
|
||
|
||
Array.prototype.slice.call(needsValidation)
|
||
.forEach(function(form) {
|
||
form.addEventListener('submit', function(event) {
|
||
if (!form.checkValidity()) {
|
||
event.preventDefault()
|
||
event.stopPropagation()
|
||
}
|
||
|
||
form.classList.add('was-validated')
|
||
}, false)
|
||
})
|
||
|
||
/**
|
||
* Initiate Datatables
|
||
*/
|
||
const datatables = select('.datatable', true)
|
||
datatables.forEach(datatable => {
|
||
new simpleDatatables.DataTable(datatable, {
|
||
perPageSelect: [5, 10, 15, ["All", -1]],
|
||
columns: [{
|
||
select: 2,
|
||
sortSequence: ["desc", "asc"]
|
||
},
|
||
{
|
||
select: 3,
|
||
sortSequence: ["desc"]
|
||
},
|
||
{
|
||
select: 4,
|
||
cellClass: "green",
|
||
headerClass: "red"
|
||
}
|
||
]
|
||
});
|
||
})
|
||
|
||
/**
|
||
* Autoresize echart charts
|
||
*/
|
||
const mainContainer = select('#main');
|
||
if (mainContainer) {
|
||
setTimeout(() => {
|
||
new ResizeObserver(function() {
|
||
select('.echart', true).forEach(getEchart => {
|
||
echarts.getInstanceByDom(getEchart).resize();
|
||
})
|
||
}).observe(mainContainer);
|
||
}, 200);
|
||
}
|
||
|
||
})();
|
||
|
||
|