class CustomTable { constructor(tableId, apiUrl) { this.tableId = tableId; this.apiUrl = apiUrl; this.currentPage = 1; this.pageSize = 10; this.draw = 1; this.searchTerm = ""; this.totalRecords = 0; this.filteredRecords = 0; this.data = []; this.isLoading = false; this.paginationClickTimeout = null; this.init(); } init() { this.injectStyles(); this.createTableStructure(); this.createPaginationControls(); this.createSearchInput(); this.bindEvents(); this.loadData(); } injectStyles() { const styles = ` `; const existingStyles = document.getElementById("custom-table-styles"); if (existingStyles) { existingStyles.remove(); } document.head.insertAdjacentHTML("beforeend", styles); } createTableStructure() { const table = document.getElementById(this.tableId); table.classList.add("modern-table"); let tbody = table.querySelector("tbody"); if (tbody) { tbody.id = `${this.tableId}-tbody`; } this.showLoading(); } createSearchInput() {} createPaginationControls() {} bindEvents() { const searchInput = document.getElementById("searchInput"); const pageSizeSelect = document.getElementById("perPage"); if (searchInput) { searchInput.addEventListener("keypress", (e) => { if (e.key === "Enter") { this.searchTerm = searchInput.value; this.currentPage = 1; this.draw++; this.loadData(); } }); searchInput.addEventListener("input", (e) => { clearTimeout(this.searchTimeout); this.searchTimeout = setTimeout(() => { this.searchTerm = searchInput.value; this.currentPage = 1; this.draw++; this.loadData(); }, 500); }); } if (pageSizeSelect) { pageSizeSelect.addEventListener("change", (e) => { this.pageSize = parseInt(e.target.value); this.currentPage = 1; this.draw++; this.loadData(); }); } } async loadData() { if (this.isLoading) { console.log("Already loading, skipping..."); return; } console.log("Starting loadData...", { currentPage: this.currentPage, pageSize: this.pageSize, searchTerm: this.searchTerm, draw: this.draw, }); this.isLoading = true; this.showLoading(); try { const start = (this.currentPage - 1) * this.pageSize; const formData = new URLSearchParams(); formData.append("draw", this.draw.toString()); formData.append("start", start.toString()); formData.append("length", this.pageSize.toString()); if (this.searchTerm) { formData.append("search[value]", this.searchTerm); } const response = await fetch(this.apiUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: formData, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); this.data = result.data || []; this.totalRecords = result.recordsTotal || 0; this.filteredRecords = result.recordsFiltered || 0; console.log("Data loaded successfully:", { dataLength: this.data.length, totalRecords: this.totalRecords, filteredRecords: this.filteredRecords, }); this.renderTable(); this.renderPagination(); this.hideLoading(); } catch (error) { console.error("Error loading data:", error); this.showError("Gagal memuat data. Silakan coba lagi."); } finally { this.isLoading = false; } } showLoading() { const loadingIndicator = document.getElementById("loadingIndicator"); const emptyState = document.getElementById("emptyState"); const tableContainer = document.querySelector(".overflow-x-auto"); const pagination = document.getElementById("pagination"); if (loadingIndicator) loadingIndicator.classList.remove("hidden"); if (emptyState) emptyState.classList.add("hidden"); if (tableContainer) tableContainer.style.display = "none"; if (pagination) { const buttons = pagination.querySelectorAll("button"); buttons.forEach((btn) => { btn.style.opacity = "0.5"; btn.style.pointerEvents = "none"; }); } } showError(message) { const loadingIndicator = document.getElementById("loadingIndicator"); const emptyState = document.getElementById("emptyState"); const tableContainer = document.querySelector(".overflow-x-auto"); if (loadingIndicator) { loadingIndicator.classList.add("hidden"); } if (emptyState) { emptyState.classList.remove("hidden"); emptyState.innerHTML = `

${message}

`; } if (tableContainer) tableContainer.style.display = "none"; } hideLoading() { const loadingIndicator = document.getElementById("loadingIndicator"); const emptyState = document.getElementById("emptyState"); const tableContainer = document.querySelector(".overflow-x-auto"); const pagination = document.getElementById("pagination"); if (loadingIndicator) loadingIndicator.classList.add("hidden"); if (emptyState) emptyState.classList.add("hidden"); if (tableContainer) tableContainer.style.display = "block"; if (pagination) { const buttons = pagination.querySelectorAll("button"); buttons.forEach((btn) => { if (!btn.hasAttribute("disabled")) { btn.style.opacity = "1"; btn.style.pointerEvents = "auto"; } }); } } renderTable() { const tbody = document.querySelector(`#${this.tableId} tbody`) || document.getElementById(`${this.tableId}-tbody`); const emptyState = document.getElementById("emptyState"); const tableContainer = document.querySelector(".overflow-x-auto"); if (this.data.length === 0) { if (tableContainer) tableContainer.style.display = "none"; if (emptyState) emptyState.classList.remove("hidden"); this.updateTableInfo(); this.renderPagination(); return; } if (tableContainer) tableContainer.style.display = "block"; if (emptyState) emptyState.classList.add("hidden"); const start = (this.currentPage - 1) * this.pageSize; let html = ""; this.data.forEach((row, index) => { const no = start + index + 1; const dateInfo = this.formatDate(row.tglBerlakuIzin); html += ` ${no}
${this.escapeHtml( row.nama || "-" )}
${this.escapeHtml( row.alamat || "-" )}
${ dateInfo.formatted }
${dateInfo.statusText}
`; }); tbody.innerHTML = html; this.updateTableInfo(); } renderPagination() { const container = document.getElementById("pagination"); const totalPages = Math.ceil(this.filteredRecords / this.pageSize); if (!container) return; let paginationHtml = ""; paginationHtml += ` `; const startPage = Math.max(1, this.currentPage - 2); const endPage = Math.min(totalPages, this.currentPage + 2); if (startPage > 1) { paginationHtml += ``; if (startPage > 2) { paginationHtml += `...`; } } for (let i = startPage; i <= endPage; i++) { paginationHtml += ` `; } if (endPage < totalPages) { if (endPage < totalPages - 1) { paginationHtml += `...`; } paginationHtml += ``; } paginationHtml += ` `; container.innerHTML = paginationHtml; if (container._clickHandler) { container.removeEventListener("click", container._clickHandler); } if (container._touchHandler) { container.removeEventListener("touchstart", container._touchHandler); } if (container._touchEndHandler) { container.removeEventListener("touchend", container._touchEndHandler); } container._clickHandler = (e) => { e.preventDefault(); e.stopPropagation(); console.log("Pagination clicked:", e.target); let clickedButton = e.target; if (e.target.tagName === "SVG" || e.target.tagName === "PATH") { clickedButton = e.target.closest("button"); } if ( clickedButton && clickedButton.tagName === "BUTTON" && clickedButton.dataset.page ) { console.log("Button clicked:", { page: clickedButton.dataset.page, disabled: clickedButton.disabled, hasAttribute: clickedButton.hasAttribute("disabled"), }); const page = parseInt(clickedButton.dataset.page); if ( page && page !== this.currentPage && page >= 1 && page <= totalPages && !clickedButton.disabled && !clickedButton.hasAttribute("disabled") && !this.isLoading ) { console.log("Navigating to page:", page); if (this.paginationClickTimeout) { clearTimeout(this.paginationClickTimeout); } this.paginationClickTimeout = setTimeout(() => { this.currentPage = page; this.draw++; this.loadData(); }, 100); } else { console.log("Click ignored:", { samePage: page === this.currentPage, outOfRange: page < 1 || page > totalPages, disabled: clickedButton.disabled || clickedButton.hasAttribute("disabled"), loading: this.isLoading, }); } } }; container._touchHandler = (e) => { e.preventDefault(); e.stopPropagation(); }; container.addEventListener("click", container._clickHandler); container.addEventListener("touchstart", container._touchHandler, { passive: false, }); container._touchEndHandler = (e) => { e.preventDefault(); e.stopPropagation(); }; container.addEventListener("touchend", container._touchEndHandler, { passive: false, }); } updateTableInfo() { const infoContainer = document.getElementById("tableInfo"); if (infoContainer) { const start = (this.currentPage - 1) * this.pageSize + 1; const end = Math.min( this.currentPage * this.pageSize, this.filteredRecords ); infoContainer.innerHTML = `Menampilkan ${start} hingga ${end} dari ${this.filteredRecords} data`; } } formatDate(dateString) { if (!dateString || dateString === "0001-01-01") { return { formatted: "-", statusClass: "bg-gray-100 text-gray-800", statusText: "Tidak diketahui", }; } try { const date = new Date(dateString); const formattedDate = date.toLocaleDateString("id-ID", { year: "numeric", month: "2-digit", day: "2-digit", }); const today = new Date(); const daysUntilExpiry = Math.ceil((date - today) / (1000 * 60 * 60 * 24)); let statusClass = "bg-green-100 text-green-800"; let statusText = `${daysUntilExpiry} hari`; if (daysUntilExpiry < 0) { statusClass = "bg-red-100 text-red-800"; statusText = "Expired"; } else if (daysUntilExpiry < 30) { statusClass = "bg-red-100 text-red-800"; } else if (daysUntilExpiry < 90) { statusClass = "bg-yellow-100 text-yellow-800"; } return { formatted: formattedDate, statusClass: statusClass, statusText: statusText, }; } catch (error) { return { formatted: dateString, statusClass: "bg-gray-100 text-gray-800", statusText: "Invalid", }; } } escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } } document.addEventListener("DOMContentLoaded", function () { const tableElement = document.getElementById("pengolahanTable"); if (tableElement && !tableElement.customTableInitialized) { tableElement.customTableInitialized = true; console.log("Initializing CustomTable..."); new CustomTable( "pengolahanTable", "https://pesapakawan.dinaslhdki.id/api/web/jasa/olah" ); } });