import Big from "big.js"; import GlobalConfig, { addThousandSeparators } from "../../global-config.js"; import InitDatePicker from "../../utils/InitDatePicker.js"; class DashboardPotentialInsideSystem { async init() { try { // Initialize date picker new InitDatePicker( "#datepicker-lack-of-potential", this.handleChangedDate.bind(this) ).init(); // Initialize default values this.bigTotalLackPotential = 0; // Fetch data with error handling this.totalPotensi = (await this.getDataTotalPotensi("latest")) || { total: 0, }; this.totalTargetPAD = (await this.getDataSettings("TARGET_PAD")) || 0; this.allCountData = (await this.getValueDashboard()) || {}; // Set counts with safe fallbacks this.pdamCount = this.allCountData.total_pdam ?? 0; this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0; this.pajakReklameCount = this.allCountData.data_pajak_reklame ?? 0; this.surveyLapanganCount = this.allCountData.total_reklame ?? 0; this.reklameCount = this.pajakReklameCount + this.surveyLapanganCount; this.pajakRestoranCount = this.allCountData.data_pajak_restoran ?? 0; this.pajakHiburanCount = this.allCountData.data_pajak_hiburan ?? 0; this.pajakHotelCount = this.allCountData.data_pajak_hotel ?? 0; this.pajakParkirCount = this.allCountData.data_pajak_parkir ?? 0; this.tataRuangUsahaCount = this.allCountData.total_tata_ruang_usaha ?? 0; this.tataRuangNonUsahaCount = this.allCountData.total_tata_ruang_non_usaha ?? 0; // Handle tourism data safely let dataReportTourism = this.allCountData.data_report || []; this.totalVilla = dataReportTourism .filter( (item) => item.kbli_title && item.kbli_title.toLowerCase() === "vila" ) .reduce((sum, item) => sum + (item.total_records || 0), 0); this.totalRestoran = dataReportTourism .filter( (item) => item.kbli_title && item.kbli_title.toLowerCase() === "restoran" ) .reduce((sum, item) => sum + (item.total_records || 0), 0); this.totalPariwisata = dataReportTourism.reduce( (sum, item) => sum + (item.total_records || 0), 0 ); // Calculate big numbers this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0); this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0); this.bigTotalLackPotential = this.bigTargetPAD.minus( this.bigTotalPotensi ); // Initialize charts and data this.initChartKekuranganPotensi(); this.initDataValueDashboard(); } catch (error) { console.error("Error initializing dashboard:", error); // Set safe fallback values this.reklameCount = 0; this.pdamCount = 0; this.tataRuangCount = 0; this.tataRuangUsahaCount = 0; this.tataRuangNonUsahaCount = 0; this.totalVilla = 0; this.totalRestoran = 0; this.totalPariwisata = 0; this.bigTotalLackPotential = new Big(0); // Still try to initialize the dashboard with safe values this.initDataValueDashboard(); } } async handleChangedDate(filterDate) { const totalPotensi = await this.getDataTotalPotensi(filterDate); this.bigTotalPotensi = new Big(totalPotensi.total ?? 0); this.bigTotalLackPotential = this.bigTargetPAD.minus( this.bigTotalPotensi ); this.initChartKekuranganPotensi(); } async getDataTotalPotensi(filterDate) { try { const response = await fetch( `${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterDate}&type=simbg`, { credentials: "include", headers: { Authorization: `Bearer ${ document.querySelector("meta[name='api-token']") .content }`, "Content-Type": "application/json", }, } ); if (!response.ok) { console.error("Network response was not ok", response); } const data = await response.json(); return { total: data.total_potensi.sum, }; } catch (error) { console.error("Error fetching chart data:", error); return null; } } async getDataSettings(string_key) { try { const response = await fetch( `${GlobalConfig.apiHost}/api/data-settings?search=${string_key}`, { credentials: "include", headers: { Authorization: `Bearer ${ document.querySelector("meta[name='api-token']") .content }`, "Content-Type": "application/json", }, } ); if (!response.ok) { console.error("Network response was not ok", response); } const data = await response.json(); return data.data[0].value; } catch (error) { console.error("Error fetching chart data:", error); return 0; } } async getValueDashboard() { try { const response = await fetch( `${GlobalConfig.apiHost}/api/dashboard-potential-count`, { credentials: "include", headers: { Authorization: `Bearer ${ document.querySelector("meta[name='api-token']") .content }`, "Content-Type": "application/json", }, } ); if (!response.ok) { console.error("Network response was not ok", response); } const data = await response.json(); return data; } catch (error) { console.error("Error fetching chart data:", error); return 0; } } initChartKekuranganPotensi() { // Helper function to safely update elements with class selector const safeUpdateElements = (selector, callback) => { try { const elements = document.querySelectorAll(selector); if (elements.length > 0) { elements.forEach(callback); } else { console.warn( `No elements found with selector '${selector}'` ); } } catch (error) { console.error( `Error updating elements with selector '${selector}':`, error ); } }; safeUpdateElements( ".document-count.chart-lack-of-potential", (element) => { element.innerText = ``; } ); safeUpdateElements( ".document-total.chart-lack-of-potential", (element) => { element.innerText = `Rp.${addThousandSeparators( this.bigTotalLackPotential.toString() )}`; } ); safeUpdateElements( ".small-percentage.chart-lack-of-potential", (element) => { element.innerText = ``; } ); } initDataValueDashboard() { // Helper function to safely set element text const safeSetText = (elementId, value) => { const element = document.getElementById(elementId); if (element) { element.innerText = value; } else { console.warn(`Element with id '${elementId}' not found`); } }; safeSetText("reklame-count", this.reklameCount); safeSetText("survey-lapangan-count", this.surveyLapanganCount); safeSetText("pajak-reklame-count", this.pajakReklameCount); safeSetText("restoran-count", this.pajakRestoranCount); safeSetText("hiburan-count", this.pajakHiburanCount); safeSetText("hotel-count", this.pajakHotelCount); safeSetText("parkir-count", this.pajakParkirCount); safeSetText("pdam-count", this.pdamCount); safeSetText("tata-ruang-count", this.tataRuangCount); safeSetText("tata-ruang-usaha-count", this.tataRuangUsahaCount); safeSetText("tata-ruang-non-usaha-count", this.tataRuangNonUsahaCount); safeSetText("pariwisata-count", this.totalPariwisata); } } document.addEventListener("DOMContentLoaded", async function (e) { await new DashboardPotentialInsideSystem().init(); }); function handleCircleClick(element) { const url = element.getAttribute("data-url") || "#"; if (url !== "#") { window.location.href = url; } } function resizeDashboard() { let targetElement = document.getElementById("lack-of-potential-wrapper"); let dashboardElement = document.getElementById( "lack-of-potential-fixed-container" ); // Check if required elements exist if (!targetElement || !dashboardElement) { console.warn("Required elements for dashboard resize not found"); return; } let targetWidth = targetElement.offsetWidth; let dashboardWidth = 1400; let scaleFactor = (targetWidth / dashboardWidth).toFixed(2); // Prevent scaling beyond 1 (100%) to avoid overflow scaleFactor = Math.min(scaleFactor, 1); dashboardElement.style.transformOrigin = "left top"; dashboardElement.style.transition = "transform 0.2s ease-in-out"; dashboardElement.style.transform = `scale(${scaleFactor})`; // Fix SVG scaling issue - reset SVG transform to prevent oversized icons const svgElements = dashboardElement.querySelectorAll("svg"); svgElements.forEach((svg) => { svg.style.transform = `scale(${1 / scaleFactor})`; svg.style.transformOrigin = "center"; svg.style.width = "17px"; svg.style.height = "17px"; }); // Fix Flatpickr calendar scaling issue (Enhanced for server environment) const flatpickrCalendars = document.querySelectorAll(".flatpickr-calendar"); flatpickrCalendars.forEach((calendar) => { // Force reset all transformation properties calendar.style.transform = "none"; calendar.style.scale = "1"; calendar.style.position = "fixed"; calendar.style.zIndex = "10000"; calendar.style.fontSize = "14px"; calendar.style.lineHeight = "normal"; // Apply to all child elements const allElements = calendar.querySelectorAll("*"); allElements.forEach((element) => { element.style.transform = "none"; element.style.scale = "1"; }); // Fix SVG inside Flatpickr with more specific targeting const flatpickrSvgs = calendar.querySelectorAll( "svg, .flatpickr-prev-month svg, .flatpickr-next-month svg" ); flatpickrSvgs.forEach((svg) => { svg.style.transform = "none"; svg.style.scale = "1"; svg.style.width = "17px"; svg.style.height = "17px"; svg.style.maxWidth = "17px"; svg.style.maxHeight = "17px"; svg.style.minWidth = "17px"; svg.style.minHeight = "17px"; }); }); // Ensure horizontal scrolling is allowed if necessary if (document.body) { document.body.style.overflowX = "auto"; } } // Debounced function for better server performance function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Additional function to force fix Flatpickr on server function forceFlatpickrFix() { const calendars = document.querySelectorAll(".flatpickr-calendar"); calendars.forEach((calendar) => { calendar.style.setProperty("transform", "none", "important"); calendar.style.setProperty("scale", "1", "important"); calendar.style.setProperty("position", "fixed", "important"); calendar.style.setProperty("z-index", "10000", "important"); const svgs = calendar.querySelectorAll("svg"); svgs.forEach((svg) => { svg.style.setProperty("width", "17px", "important"); svg.style.setProperty("height", "17px", "important"); svg.style.setProperty("transform", "none", "important"); svg.style.setProperty("scale", "1", "important"); }); }); } window.addEventListener("load", resizeDashboard); window.addEventListener("resize", debounce(resizeDashboard, 100)); // Force fix on various events for server environment window.addEventListener("load", forceFlatpickrFix); document.addEventListener("click", debounce(forceFlatpickrFix, 50)); window.addEventListener("scroll", debounce(forceFlatpickrFix, 100)); // Fix Flatpickr when it's opened dynamically document.addEventListener("DOMContentLoaded", function () { // Add mutation observer to handle dynamically created Flatpickr calendars const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.type === "childList") { mutation.addedNodes.forEach(function (node) { if ( node.nodeType === 1 && node.classList && node.classList.contains("flatpickr-calendar") ) { // Fix newly created Flatpickr calendar (Enhanced for server) node.style.transform = "none"; node.style.scale = "1"; node.style.position = "fixed"; node.style.zIndex = "10000"; node.style.fontSize = "14px"; node.style.lineHeight = "normal"; // Apply to all child elements immediately const allElements = node.querySelectorAll("*"); allElements.forEach((element) => { element.style.transform = "none"; element.style.scale = "1"; }); // Fix SVG inside the new calendar with enhanced targeting const svgs = node.querySelectorAll( "svg, .flatpickr-prev-month svg, .flatpickr-next-month svg" ); svgs.forEach((svg) => { svg.style.transform = "none"; svg.style.scale = "1"; svg.style.width = "17px"; svg.style.height = "17px"; svg.style.maxWidth = "17px"; svg.style.maxHeight = "17px"; svg.style.minWidth = "17px"; svg.style.minHeight = "17px"; }); } }); } }); }); // Observe document body for new Flatpickr calendars observer.observe(document.body, { childList: true, subtree: true, }); });