fix no data and handle default page load no data

This commit is contained in:
arifal
2025-08-26 13:59:36 +07:00
parent c6257b79bf
commit 63310f2748
4 changed files with 487 additions and 133 deletions

View File

@@ -8,12 +8,42 @@ class PublicSearch {
this.baseUrl = baseInput ? baseInput.value.split("?")[0] : "";
this.keywordInput = document.getElementById("search_input");
this.searchButton = document.getElementById("search_button");
this.searchHeader = document.getElementById("search-header");
this.tableWrapper = document.getElementById("table-wrapper");
this.emptyState = document.getElementById("empty-state");
this.datatableUrl = this.buildUrl(this.keywordInput.value);
// Tidak inisialisasi datatable sampai ada pencarian
this.datatableUrl = null;
}
init() {
this.bindSearchButton();
// Check if there's a keyword in URL
const urlParams = new URLSearchParams(window.location.search);
const keyword = urlParams.get("keyword");
if (keyword && keyword.trim() !== "") {
this.keywordInput.value = keyword.trim();
this.handleSearchFromUrl(keyword.trim());
}
}
handleSearchFromUrl(keyword) {
// Validasi input kosong atau hanya spasi
if (!keyword || keyword.trim().length === 0) {
this.showEmptyState("Mulai Pencarian");
return;
}
// Validasi minimal 3 karakter
if (keyword.trim().length < 3) {
this.showEmptyState("Minimal 3 karakter untuk pencarian");
return;
}
this.datatableUrl = this.buildUrl(keyword.trim());
this.showSearchResults();
this.initDatatable();
}
@@ -21,8 +51,21 @@ class PublicSearch {
const handleSearch = () => {
const newKeyword = this.keywordInput.value.trim();
// Validasi input kosong atau hanya spasi
if (!newKeyword || newKeyword.length === 0) {
this.showEmptyState("Mulai Pencarian");
return;
}
// Validasi minimal 3 karakter (setelah trim)
if (newKeyword.length < 3) {
this.showEmptyState("Minimal 3 karakter untuk pencarian");
return;
}
// 1. Update datatable URL and reload
this.datatableUrl = this.buildUrl(newKeyword);
this.showSearchResults();
this.initDatatable();
// 2. Update URL query string (tanpa reload page)
@@ -46,13 +89,64 @@ class PublicSearch {
handleSearch();
}
});
// Handle input change untuk real-time validation
this.keywordInput.addEventListener("input", (event) => {
const value = event.target.value.trim();
// Remove existing classes
this.keywordInput.classList.remove("valid", "warning", "invalid");
// Jika input kosong atau hanya spasi, show empty state
if (!value || value.length === 0) {
this.showEmptyState("Mulai Pencarian");
return;
}
// Jika kurang dari 3 karakter, show warning
if (value.length < 3) {
this.showEmptyState("Minimal 3 karakter untuk pencarian");
this.keywordInput.classList.add("warning");
return;
}
// Jika valid, add valid class
this.keywordInput.classList.add("valid");
});
// Handle input focus untuk clear warning state
this.keywordInput.addEventListener("focus", () => {
const value = this.keywordInput.value.trim();
if (value.length >= 3) {
// Jika sudah valid, hide empty state
this.emptyState.style.display = "none";
}
});
// Handle input blur untuk final validation
this.keywordInput.addEventListener("blur", () => {
const value = this.keywordInput.value.trim();
if (!value || value.length === 0) {
this.showEmptyState("Mulai Pencarian");
} else if (value.length < 3) {
this.showEmptyState("Minimal 3 karakter untuk pencarian");
}
});
// Handle Escape key untuk clear search
this.keywordInput.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
this.clearSearch();
}
});
}
buildUrl(keyword) {
const url = new URL(this.baseUrl, window.location.origin);
if (keyword && keyword.trim() !== "") {
url.searchParams.set("search", keyword);
// Validasi keyword tidak kosong dan tidak hanya spasi
if (keyword && keyword.trim() !== "" && keyword.trim().length >= 3) {
url.searchParams.set("search", keyword.trim());
} else {
url.searchParams.delete("search"); // pastikan tidak ada search param
}
@@ -60,6 +154,73 @@ class PublicSearch {
return url.toString();
}
showSearchResults() {
this.searchHeader.style.display = "block";
this.tableWrapper.style.display = "block";
this.emptyState.style.display = "none";
}
showEmptyState(message = "Tidak ada data yang ditemukan") {
this.searchHeader.style.display = "none";
this.tableWrapper.style.display = "none";
this.emptyState.style.display = "block";
// Update empty state message and icon
const emptyStateTitle = this.emptyState.querySelector("h4");
const emptyStateDesc = this.emptyState.querySelector("p");
const emptyIcon = this.emptyState.querySelector(".empty-icon i");
if (emptyStateTitle) {
emptyStateTitle.textContent = message;
}
if (emptyStateDesc) {
if (message === "Mulai Pencarian") {
emptyStateDesc.textContent =
"Masukkan kata kunci minimal 3 karakter untuk mencari data PBG";
} else if (message === "Minimal 3 karakter untuk pencarian") {
emptyStateDesc.textContent =
"Masukkan kata kunci minimal 3 karakter untuk mencari data PBG";
} else {
emptyStateDesc.textContent =
"Coba gunakan kata kunci yang berbeda atau lebih spesifik";
}
}
// Update icon based on message
if (emptyIcon) {
if (message === "Mulai Pencarian") {
emptyIcon.className = "fas fa-search fa-3x text-muted";
} else if (message === "Minimal 3 karakter untuk pencarian") {
emptyIcon.className =
"fas fa-exclamation-triangle fa-3x text-warning";
} else {
emptyIcon.className = "fas fa-search fa-3x text-muted";
}
}
// Clear existing table if any
if (this.table) {
this.table.destroy();
this.table = null;
}
}
clearSearch() {
this.keywordInput.value = "";
this.showEmptyState("Mulai Pencarian");
// Reset CSS classes
this.keywordInput.classList.remove("valid", "warning", "invalid");
// Clear URL parameter
const newUrl = window.location.pathname;
window.history.pushState({ path: newUrl }, "", newUrl);
// Reset datatable URL
this.datatableUrl = null;
}
initDatatable() {
const tableContainer = document.getElementById(
"datatable-public-search"
@@ -67,17 +228,17 @@ class PublicSearch {
const config = {
columns: [
"ID",
{ name: "Nama Pemohon" },
{ name: "Nama Pemilik" },
{ name: "Kondisi" },
"Nomor Registrasi",
"Status",
"Jenis Fungsi",
{ name: "Nama Bangunan" },
"Jenis Konsultasi",
{ name: "Tanggal Jatuh Tempo" },
{ name: "Retribusi" },
{ name: "ID", width: "80px" },
{ name: "Nama Pemohon", width: "150px" },
{ name: "Nama Pemilik", width: "150px" },
{ name: "Kondisi", width: "120px" },
{ name: "Nomor Registrasi", width: "180px" },
{ name: "Status", width: "120px" },
{ name: "Jenis Fungsi", width: "150px" },
{ name: "Nama Bangunan", width: "200px" },
{ name: "Jenis Konsultasi", width: "150px" },
{ name: "Tanggal Jatuh Tempo", width: "140px" },
{ name: "Retribusi", width: "120px" },
],
search: false,
pagination: {
@@ -92,21 +253,40 @@ class PublicSearch {
sort: true,
server: {
url: this.datatableUrl,
then: (data) =>
data.data.map((item) => [
item.id,
item.name,
item.owner_name,
item.condition,
item.registration_number,
item.status_name,
item.function_type,
item.name_building,
item.consultation_type,
item.due_date,
addThousandSeparators(item.nilai_retribusi_bangunan),
]),
total: (data) => data.total,
then: (data) => {
// Check if data is empty
if (!data.data || data.data.length === 0) {
this.showEmptyState(
data.message || "Tidak ada data yang ditemukan"
);
return [];
}
return data.data.map((item) => [
item.id || "-",
item.name || "-",
item.owner_name || "-",
item.condition || "-",
item.registration_number || "-",
item.status_name || "-",
item.function_type || "-",
item.name_building || "-",
item.consultation_type || "-",
item.due_date || "-",
item.nilai_retribusi_bangunan
? addThousandSeparators(
item.nilai_retribusi_bangunan
)
: "-",
]);
},
total: (data) => data.total || 0,
error: (error) => {
console.error("Datatable error:", error);
this.showEmptyState(
"Terjadi kesalahan saat mengambil data"
);
},
},
};

View File

@@ -1,149 +1,281 @@
.qs-wrapper {
padding: 20px;
width: 100%;
max-width: 100%;
margin: 0 auto;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
color: #2c3e50;
min-height: 100vh;
}
.qs-toolbar {
border-bottom: 1px solid #e0e0e0;
margin-bottom: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.qs-search-form {
width: 100%;
max-width: 1000px; // biar kotak ga terlalu panjang di layar besar
margin: 0 auto; // center
position: relative;
.gsp-input {
width: 100%;
height: 48px;
flex: 1;
padding: 12px 16px;
border: 2px solid #e9ecef;
border-radius: 6px;
font-size: 16px;
padding: 0 48px 0 48px; // kasih space kiri buat icon
border-radius: 999px; // pill shape
border: 1px solid #dfe1e5;
background-color: #fff;
box-shadow: none;
transition: box-shadow 0.2s ease-in-out, border-color 0.2s;
transition: all 0.3s ease;
&:focus {
border-color: transparent;
box-shadow: 0 1px 6px rgba(32, 33, 36, 0.28);
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
&:invalid {
border-color: #dc3545;
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
}
&::placeholder {
color: #6c757d;
}
// Style untuk input yang valid
&.valid {
border-color: #28a745;
box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.1);
}
// Style untuk input yang warning
&.warning {
border-color: #ffc107;
box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.1);
}
}
// ikon search di kiri input
&::before {
content: "🔍";
position: absolute;
left: 18px;
top: 50%;
transform: translateY(-50%);
font-size: 18px;
color: #5f6368;
pointer-events: none;
}
.gsp-btn {
margin-left: 12px;
height: 44px;
padding: 0 24px;
font-size: 14px;
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 999px;
background-color: #007c61;
color: #ffffff;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
transition: all 0.3s ease;
&:hover {
background-color: #36a852;
background: #0056b3;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
&:active {
transform: translateY(0);
}
}
}
.qs-header {
margin-bottom: 30px;
text-align: center;
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-left: 4px solid #007bff;
h2 {
font-size: 24px;
color: #2c3e50;
margin-bottom: 8px;
font-weight: 600;
color: #1a237e;
em {
font-style: normal;
color: #0d47a1;
}
}
p {
color: #6c757d;
margin: 0;
font-size: 16px;
color: #555;
}
}
.qs-table-wrapper {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
overflow-x: auto; // allow horizontal scroll on small screens
padding: 30px 20px; // 🔑 kasih jarak kanan, kiri, atas, bawah
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* Grid.js overrides */
.qs-table-wrapper .gridjs {
font-size: 14px;
color: #333;
.qs-empty-state {
background: white;
border-radius: 8px;
padding: 60px 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.empty-icon {
color: #dee2e6;
i {
opacity: 0.7;
&.text-warning {
color: #ffc107 !important;
}
}
}
h4 {
font-weight: 500;
margin-bottom: 12px;
}
p {
font-size: 16px;
line-height: 1.5;
max-width: 400px;
margin: 0 auto;
}
}
.qs-table-wrapper .gridjs-table {
width: 100%;
border-collapse: collapse;
// GridJS customization
.gridjs-wrapper {
border: none !important;
box-shadow: none !important;
width: 100% !important;
max-width: 100% !important;
}
.qs-table-wrapper .gridjs-th,
.qs-table-wrapper .gridjs-td {
padding: 12px 16px;
border: 1px solid #e0e0e0;
text-align: left;
vertical-align: middle;
.gridjs-table {
border: none !important;
width: 100% !important;
table-layout: auto !important;
}
.qs-table-wrapper .gridjs-th {
background-color: #f5f5f5;
font-weight: 600;
color: #1b1b1b;
// Ensure table cells don't wrap unnecessarily
.gridjs-td {
border-bottom: 1px solid #e9ecef !important;
padding: 16px 12px !important;
vertical-align: middle !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
max-width: 200px !important;
}
.qs-table-wrapper .gridjs-tr:hover {
background-color: #f9f9f9;
// Allow specific columns to wrap if needed
.gridjs-td:nth-child(4), // Kondisi
.gridjs-td:nth-child(7), // Jenis Fungsi
.gridjs-td:nth-child(8), // Nama Bangunan
.gridjs-td:nth-child(9), // Jenis Konsultasi
.gridjs-td:nth-child(10) {
// Tanggal Jatuh Tempo
white-space: normal !important;
word-wrap: break-word !important;
max-width: 150px !important;
}
.qs-table-wrapper .gridjs-pagination {
margin-top: 16px;
justify-content: center;
.gridjs-th {
background: #f8f9fa !important;
border-bottom: 2px solid #dee2e6 !important;
font-weight: 600 !important;
color: #495057 !important;
padding: 16px 12px !important;
}
.gridjs-td {
border-bottom: 1px solid #e9ecef !important;
padding: 16px 12px !important;
vertical-align: middle !important;
}
.gridjs-pagination {
border-top: 1px solid #e9ecef !important;
padding: 20px !important;
.gridjs-pages {
button {
border: 1px solid #dee2e6 !important;
border-radius: 4px !important;
padding: 8px 12px !important;
margin: 0 2px !important;
&:hover {
background: #e9ecef !important;
}
&.gridjs-currentPage {
background: #007bff !important;
color: white !important;
border-color: #007bff !important;
}
}
}
}
// Responsive design
@media (max-width: 768px) {
.qs-header h2 {
font-size: 20px;
}
.qs-wrapper {
padding: 20px 10px;
}
.qs-table-wrapper {
padding: 15px;
}
.qs-table-wrapper .gridjs-th,
.qs-table-wrapper .gridjs-td {
padding: 10px 12px;
font-size: 13px;
.qs-toolbar {
padding: 15px;
}
.qs-search-form {
flex-direction: column;
gap: 15px;
.gsp-input {
width: 100%;
}
.gsp-btn {
width: 100%;
}
}
.qs-header {
padding: 15px;
h2 {
font-size: 20px;
}
}
.qs-empty-state {
padding: 40px 15px;
.empty-icon i {
font-size: 2.5rem !important;
}
h4 {
font-size: 18px;
}
p {
font-size: 14px;
}
}
}
// Table responsive improvements
@media (max-width: 1200px) {
.gridjs-wrapper {
overflow-x: auto !important;
}
.gridjs-table {
min-width: 1000px !important;
}
}
// Ensure full width on larger screens
@media (min-width: 1201px) {
.qs-wrapper {
padding: 20px 40px;
}
.gridjs-wrapper {
max-width: none !important;
}
}

View File

@@ -23,14 +23,22 @@
</div>
</div>
<div class="qs-header mb-3">
<div class="qs-header mb-3" id="search-header" style="display: none;">
<h2>Hasil Pencarian</h2>
<p>Berikut adalah data hasil pencarian berdasarkan kata kunci yang Anda masukkan.</p>
</div>
<div class="qs-table-wrapper">
<div class="qs-table-wrapper" id="table-wrapper" style="display: none;">
<div class="p-3" id="datatable-public-search"></div>
</div>
<div class="qs-empty-state text-center py-5" id="empty-state">
<div class="empty-icon mb-3">
<i class="fas fa-search fa-3x text-muted"></i>
</div>
<h4 class="text-muted mb-2">Mulai Pencarian</h4>
<p class="text-muted">Masukkan kata kunci minimal 3 karakter untuk mencari data PBG</p>
</div>
</div>
@endsection