768 lines
24 KiB
JavaScript
768 lines
24 KiB
JavaScript
// Dashboard Manager JavaScript - FIXED ENDPOINTS
|
|
|
|
let allItems = [];
|
|
let allClaims = [];
|
|
let allLostItems = [];
|
|
let allArchive = [];
|
|
|
|
// Initialize dashboard
|
|
window.addEventListener("DOMContentLoaded", async () => {
|
|
const user = checkAuth();
|
|
if (!user || user.role !== "manager") {
|
|
window.location.href = "/login";
|
|
return;
|
|
}
|
|
|
|
await loadStats();
|
|
await loadItems();
|
|
setupSearchAndFilters();
|
|
});
|
|
|
|
// Load statistics - FIXED
|
|
async function loadStats() {
|
|
try {
|
|
const stats = await apiCall("/api/manager/dashboard");
|
|
document.getElementById("statTotalItems").textContent =
|
|
stats.total_items || 0;
|
|
document.getElementById("statPendingClaims").textContent =
|
|
stats.pending_claims || 0;
|
|
document.getElementById("statVerified").textContent = stats.verified || 0;
|
|
document.getElementById("statExpired").textContent = stats.expired || 0;
|
|
} catch (error) {
|
|
console.error("Error loading stats:", error);
|
|
}
|
|
}
|
|
|
|
// Load items - FIXED
|
|
async function loadItems() {
|
|
setLoading("itemsGrid", true);
|
|
|
|
try {
|
|
const response = await apiCall("/api/items");
|
|
allItems = response.data || [];
|
|
renderItems(allItems);
|
|
} catch (error) {
|
|
console.error("Error loading items:", error);
|
|
showEmptyState("itemsGrid", "📦", "Gagal memuat data barang");
|
|
}
|
|
}
|
|
|
|
// Render items
|
|
function renderItems(items) {
|
|
const grid = document.getElementById("itemsGrid");
|
|
|
|
if (!items || items.length === 0) {
|
|
showEmptyState("itemsGrid", "📦", "Belum ada barang");
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = items
|
|
.map(
|
|
(item) => `
|
|
<div class="item-card">
|
|
<img src="${
|
|
item.photo_url || "https://via.placeholder.com/280x200?text=No+Image"
|
|
}"
|
|
alt="${item.name}"
|
|
class="item-image"
|
|
onerror="this.src='https://via.placeholder.com/280x200?text=No+Image'">
|
|
<div class="item-body">
|
|
<h3 class="item-title">${item.name}</h3>
|
|
<div class="item-meta">
|
|
<span>📍 ${item.location}</span>
|
|
<span>📅 ${formatDate(item.date_found)}</span>
|
|
<span>${getStatusBadge(item.status)}</span>
|
|
</div>
|
|
<div class="item-actions">
|
|
<button class="btn btn-primary btn-sm" onclick="viewItemDetail(${
|
|
item.id
|
|
})">Detail</button>
|
|
${
|
|
item.status !== "case_closed" && item.status !== "expired"
|
|
? `<button class="btn btn-warning btn-sm" onclick="editItem(${item.id})">Edit</button>`
|
|
: ""
|
|
}
|
|
${
|
|
item.status === "verified"
|
|
? `<button class="btn btn-success btn-sm" onclick="closeCase(${item.id})">Close Case</button>`
|
|
: ""
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
// View item detail - FIXED
|
|
async function viewItemDetail(itemId) {
|
|
try {
|
|
const item = await apiCall(`/api/items/${itemId}`);
|
|
|
|
const modalContent = document.getElementById("itemDetailContent");
|
|
modalContent.innerHTML = `
|
|
<img src="${
|
|
item.photo_url || "https://via.placeholder.com/600x400?text=No+Image"
|
|
}"
|
|
alt="${item.name}"
|
|
style="width: 100%; max-height: 400px; object-fit: cover; border-radius: 10px; margin-bottom: 20px;"
|
|
onerror="this.src='https://via.placeholder.com/600x400?text=No+Image'">
|
|
<h3>${item.name}</h3>
|
|
<div style="display: grid; gap: 15px; margin-top: 20px;">
|
|
<div><strong>Kategori:</strong> ${item.category}</div>
|
|
<div><strong>Lokasi Ditemukan:</strong> ${item.location}</div>
|
|
<div><strong>Tanggal Ditemukan:</strong> ${formatDate(
|
|
item.date_found
|
|
)}</div>
|
|
<div><strong>Status:</strong> ${getStatusBadge(item.status)}</div>
|
|
<div><strong>Pelapor:</strong> ${item.reporter_name}</div>
|
|
<div><strong>Kontak:</strong> ${item.reporter_contact}</div>
|
|
<div style="background: #f8fafc; padding: 15px; border-radius: 10px;">
|
|
<strong>Deskripsi Keunikan (Rahasia):</strong><br>
|
|
${item.description}
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
openModal("itemDetailModal");
|
|
} catch (error) {
|
|
console.error("Error loading item detail:", error);
|
|
showAlert("Gagal memuat detail barang", "danger");
|
|
}
|
|
}
|
|
|
|
// Edit item - FIXED
|
|
async function editItem(itemId) {
|
|
try {
|
|
const item = await apiCall(`/api/items/${itemId}`);
|
|
|
|
const form = document.getElementById("editItemForm");
|
|
form.elements.item_id.value = item.id;
|
|
form.elements.name.value = item.name;
|
|
form.elements.category.value = item.category;
|
|
form.elements.location.value = item.location;
|
|
form.elements.description.value = item.description;
|
|
form.elements.reporter_name.value = item.reporter_name;
|
|
form.elements.reporter_contact.value = item.reporter_contact;
|
|
form.elements.date_found.value = item.date_found.split("T")[0];
|
|
|
|
openModal("editItemModal");
|
|
} catch (error) {
|
|
console.error("Error loading item:", error);
|
|
showAlert("Gagal memuat data barang", "danger");
|
|
}
|
|
}
|
|
|
|
// Submit edit item - FIXED
|
|
document
|
|
.getElementById("editItemForm")
|
|
?.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(e.target);
|
|
const itemId = formData.get("item_id");
|
|
formData.delete("item_id");
|
|
|
|
try {
|
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="loading"></span> Menyimpan...';
|
|
|
|
await apiUpload(`/api/items/${itemId}`, formData, "PUT");
|
|
|
|
showAlert("Barang berhasil diupdate!", "success");
|
|
closeModal("editItemModal");
|
|
await loadItems();
|
|
await loadStats();
|
|
} catch (error) {
|
|
console.error("Error updating item:", error);
|
|
showAlert(error.message || "Gagal update barang", "danger");
|
|
} finally {
|
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = "Update";
|
|
}
|
|
}
|
|
});
|
|
|
|
// Close case - FIXED
|
|
async function closeCase(itemId) {
|
|
if (!confirmAction("Tutup kasus ini? Barang akan dipindahkan ke arsip."))
|
|
return;
|
|
|
|
try {
|
|
await apiCall(`/api/items/${itemId}/status`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ status: "case_closed" }),
|
|
});
|
|
|
|
showAlert("Kasus berhasil ditutup!", "success");
|
|
await loadItems();
|
|
await loadStats();
|
|
} catch (error) {
|
|
console.error("Error closing case:", error);
|
|
showAlert(error.message || "Gagal menutup kasus", "danger");
|
|
}
|
|
}
|
|
|
|
// Load claims - FIXED
|
|
async function loadClaims() {
|
|
setLoading("claimsList", true);
|
|
|
|
try {
|
|
const response = await apiCall("/api/claims");
|
|
allClaims = response.data || [];
|
|
renderClaims(allClaims);
|
|
} catch (error) {
|
|
console.error("Error loading claims:", error);
|
|
document.getElementById("claimsList").innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">🤝</div>
|
|
<p>Gagal memuat data klaim</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Render claims
|
|
function renderClaims(claims) {
|
|
const list = document.getElementById("claimsList");
|
|
|
|
if (!claims || claims.length === 0) {
|
|
list.innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">🤝</div>
|
|
<p>Belum ada klaim yang masuk</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
list.innerHTML = claims
|
|
.map(
|
|
(claim) => `
|
|
<div class="claim-card">
|
|
<div class="claim-header">
|
|
<h3>${claim.item_name}</h3>
|
|
${getStatusBadge(claim.status)}
|
|
</div>
|
|
<div class="claim-info">
|
|
<div><strong>Pengklaim:</strong> ${claim.user_name}</div>
|
|
<div><strong>Kontak:</strong> ${claim.contact}</div>
|
|
<div><strong>Tanggal Klaim:</strong> ${formatDate(
|
|
claim.created_at
|
|
)}</div>
|
|
${
|
|
claim.match_percentage
|
|
? `
|
|
<div><strong>Match:</strong>
|
|
<span style="color: ${
|
|
claim.match_percentage >= 70 ? "#10b981" : "#f59e0b"
|
|
}; font-weight: 600;">
|
|
${claim.match_percentage}%
|
|
</span>
|
|
</div>
|
|
`
|
|
: ""
|
|
}
|
|
</div>
|
|
<div style="background: #f8fafc; padding: 15px; border-radius: 10px; margin-bottom: 15px;">
|
|
<strong>Deskripsi dari Pengklaim:</strong><br>
|
|
${claim.description}
|
|
</div>
|
|
${
|
|
claim.status === "pending"
|
|
? `
|
|
<div class="claim-actions">
|
|
<button class="btn btn-primary btn-sm" onclick="verifyClaim(${claim.id})">Verifikasi</button>
|
|
<button class="btn btn-success btn-sm" onclick="approveClaim(${claim.id})">Approve</button>
|
|
<button class="btn btn-danger btn-sm" onclick="rejectClaim(${claim.id})">Reject</button>
|
|
</div>
|
|
`
|
|
: ""
|
|
}
|
|
${
|
|
claim.status === "rejected" && claim.notes
|
|
? `
|
|
<div style="color: #ef4444; margin-top: 10px;">
|
|
<strong>Alasan:</strong> ${claim.notes}
|
|
</div>
|
|
`
|
|
: ""
|
|
}
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
// Verify claim - FIXED
|
|
async function verifyClaim(claimId) {
|
|
try {
|
|
const claim = await apiCall(`/api/claims/${claimId}`);
|
|
|
|
const modalContent = document.getElementById("verifyClaimContent");
|
|
modalContent.innerHTML = `
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px;">
|
|
<div>
|
|
<h4>Deskripsi Asli Barang</h4>
|
|
<div style="background: #f8fafc; padding: 15px; border-radius: 10px; margin-top: 10px;">
|
|
${claim.item_description}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4>Deskripsi dari Pengklaim</h4>
|
|
<div style="background: #fef3c7; padding: 15px; border-radius: 10px; margin-top: 10px;">
|
|
${claim.description}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
${
|
|
claim.proof_url
|
|
? `
|
|
<div style="margin-top: 20px;">
|
|
<h4>Bukti Pendukung</h4>
|
|
<img src="${claim.proof_url}" style="width: 100%; max-height: 300px; object-fit: contain; border-radius: 10px; margin-top: 10px;">
|
|
</div>
|
|
`
|
|
: ""
|
|
}
|
|
|
|
${
|
|
claim.match_percentage
|
|
? `
|
|
<div style="margin-top: 20px; padding: 15px; background: ${
|
|
claim.match_percentage >= 70 ? "#d1fae5" : "#fef3c7"
|
|
}; border-radius: 10px; text-align: center;">
|
|
<strong>Similarity Match:</strong>
|
|
<span style="font-size: 2rem; font-weight: 700; color: ${
|
|
claim.match_percentage >= 70 ? "#10b981" : "#f59e0b"
|
|
};">
|
|
${claim.match_percentage}%
|
|
</span>
|
|
</div>
|
|
`
|
|
: ""
|
|
}
|
|
|
|
<div style="margin-top: 20px;">
|
|
<strong>Info Pengklaim:</strong>
|
|
<div style="margin-top: 10px;">
|
|
<div>Nama: ${claim.user_name}</div>
|
|
<div>Kontak: ${claim.contact}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display: flex; gap: 10px; margin-top: 30px;">
|
|
<button class="btn btn-success" onclick="approveClaim(${claimId})" style="flex: 1;">✓ Approve Klaim</button>
|
|
<button class="btn btn-danger" onclick="rejectClaim(${claimId})" style="flex: 1;">✗ Reject Klaim</button>
|
|
</div>
|
|
`;
|
|
|
|
openModal("verifyClaimModal");
|
|
} catch (error) {
|
|
console.error("Error loading claim:", error);
|
|
showAlert("Gagal memuat data klaim", "danger");
|
|
}
|
|
}
|
|
|
|
// Approve claim - FIXED
|
|
async function approveClaim(claimId) {
|
|
const notes = prompt("Catatan (opsional):");
|
|
|
|
try {
|
|
await apiCall(`/api/claims/${claimId}/verify`, {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
approved: true,
|
|
notes: notes || "",
|
|
}),
|
|
});
|
|
|
|
showAlert("Klaim berhasil diapprove!", "success");
|
|
closeModal("verifyClaimModal");
|
|
await loadClaims();
|
|
await loadItems();
|
|
await loadStats();
|
|
} catch (error) {
|
|
console.error("Error approving claim:", error);
|
|
showAlert(error.message || "Gagal approve klaim", "danger");
|
|
}
|
|
}
|
|
|
|
// Reject claim - FIXED
|
|
async function rejectClaim(claimId) {
|
|
const notes = prompt("Alasan penolakan (wajib):");
|
|
if (!notes) {
|
|
showAlert("Alasan penolakan harus diisi!", "warning");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await apiCall(`/api/claims/${claimId}/verify`, {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
approved: false,
|
|
notes,
|
|
}),
|
|
});
|
|
|
|
showAlert("Klaim berhasil ditolak!", "success");
|
|
closeModal("verifyClaimModal");
|
|
await loadClaims();
|
|
await loadStats();
|
|
} catch (error) {
|
|
console.error("Error rejecting claim:", error);
|
|
showAlert(error.message || "Gagal reject klaim", "danger");
|
|
}
|
|
}
|
|
|
|
// Load lost items - FIXED
|
|
async function loadLost() {
|
|
setLoading("lostItemsGrid", true);
|
|
|
|
try {
|
|
const response = await apiCall("/api/lost-items");
|
|
allLostItems = response.data || [];
|
|
renderLostItems(allLostItems);
|
|
} catch (error) {
|
|
console.error("Error loading lost items:", error);
|
|
showEmptyState("lostItemsGrid", "😢", "Gagal memuat data barang hilang");
|
|
}
|
|
}
|
|
|
|
// Render lost items
|
|
function renderLostItems(items) {
|
|
const grid = document.getElementById("lostItemsGrid");
|
|
|
|
if (!items || items.length === 0) {
|
|
showEmptyState("lostItemsGrid", "😢", "Belum ada laporan barang hilang");
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = items
|
|
.map(
|
|
(item) => `
|
|
<div class="item-card">
|
|
<div class="item-body">
|
|
<h3 class="item-title">${item.name}</h3>
|
|
<div class="item-meta">
|
|
<span>🏷️ ${item.category}</span>
|
|
<span>🎨 ${item.color}</span>
|
|
<span>📅 ${formatDate(item.date_lost)}</span>
|
|
${item.location ? `<span>📍 ${item.location}</span>` : ""}
|
|
</div>
|
|
<p style="color: #64748b; font-size: 0.9rem; margin-top: 10px;">${
|
|
item.description
|
|
}</p>
|
|
<div style="margin-top: 10px;">
|
|
<small><strong>Pelapor:</strong> ${item.user_name}</small>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="findSimilarItems(${
|
|
item.id
|
|
})" style="margin-top: 10px; width: 100%;">
|
|
🔍 Cari Barang yang Mirip
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
// Find similar items - FIXED
|
|
async function findSimilarItems(lostItemId) {
|
|
try {
|
|
setLoading("matchItemsContent", true);
|
|
openModal("matchItemsModal");
|
|
|
|
const response = await apiCall(`/api/lost-items/${lostItemId}/matches`);
|
|
const matches = response.data || [];
|
|
|
|
const modalContent = document.getElementById("matchItemsContent");
|
|
|
|
if (matches.length === 0) {
|
|
modalContent.innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">🔍</div>
|
|
<h3>Tidak ada barang yang cocok</h3>
|
|
<p>Belum ada barang ditemukan yang mirip dengan laporan ini</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
modalContent.innerHTML = `
|
|
<p style="margin-bottom: 20px; color: #64748b;">Ditemukan ${
|
|
matches.length
|
|
} barang yang mungkin cocok:</p>
|
|
<div class="items-grid">
|
|
${matches
|
|
.map(
|
|
(match) => `
|
|
<div class="item-card" style="border: 2px solid ${
|
|
match.similarity >= 70 ? "#10b981" : "#f59e0b"
|
|
};">
|
|
<img src="${
|
|
match.photo_url ||
|
|
"https://via.placeholder.com/280x200?text=No+Image"
|
|
}"
|
|
alt="${match.name}"
|
|
class="item-image"
|
|
onerror="this.src='https://via.placeholder.com/280x200?text=No+Image'">
|
|
<div class="item-body">
|
|
<div style="text-align: center; margin-bottom: 10px;">
|
|
<span style="font-size: 1.5rem; font-weight: 700; color: ${
|
|
match.similarity >= 70 ? "#10b981" : "#f59e0b"
|
|
};">
|
|
${match.similarity}% Match
|
|
</span>
|
|
</div>
|
|
<h3 class="item-title">${match.name}</h3>
|
|
<div class="item-meta">
|
|
<span>📍 ${match.location}</span>
|
|
<span>📅 ${formatDate(match.date_found)}</span>
|
|
<span>${getStatusBadge(match.status)}</span>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="viewItemDetail(${
|
|
match.id
|
|
})" style="width: 100%; margin-top: 10px;">
|
|
Lihat Detail
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`
|
|
)
|
|
.join("")}
|
|
</div>
|
|
`;
|
|
} catch (error) {
|
|
console.error("Error finding similar items:", error);
|
|
document.getElementById("matchItemsContent").innerHTML = `
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">❌</div>
|
|
<p>Gagal mencari barang yang mirip</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Load archive - FIXED
|
|
async function loadArchive() {
|
|
setLoading("archiveGrid", true);
|
|
|
|
try {
|
|
const response = await apiCall("/api/archives");
|
|
allArchive = response.data || [];
|
|
renderArchive(allArchive);
|
|
} catch (error) {
|
|
console.error("Error loading archive:", error);
|
|
showEmptyState("archiveGrid", "📂", "Gagal memuat data arsip");
|
|
}
|
|
}
|
|
|
|
// Render archive
|
|
function renderArchive(items) {
|
|
const grid = document.getElementById("archiveGrid");
|
|
|
|
if (!items || items.length === 0) {
|
|
showEmptyState("archiveGrid", "📂", "Belum ada barang di arsip");
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = items
|
|
.map(
|
|
(item) => `
|
|
<div class="item-card">
|
|
<img src="${
|
|
item.photo_url || "https://via.placeholder.com/280x200?text=No+Image"
|
|
}"
|
|
alt="${item.name}"
|
|
class="item-image"
|
|
onerror="this.src='https://via.placeholder.com/280x200?text=No+Image'">
|
|
<div class="item-body">
|
|
<h3 class="item-title">${item.name}</h3>
|
|
<div class="item-meta">
|
|
<span>📍 ${item.location}</span>
|
|
<span>📅 ${formatDate(item.date_found)}</span>
|
|
<span>${getStatusBadge(item.status)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
// Report found item - FIXED
|
|
function openReportFoundModal() {
|
|
openModal("reportFoundModal");
|
|
}
|
|
|
|
// Submit found item report - FIXED
|
|
document
|
|
.getElementById("reportFoundForm")
|
|
?.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(e.target);
|
|
|
|
try {
|
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="loading"></span> Mengirim...';
|
|
|
|
await apiUpload("/api/items", formData);
|
|
|
|
showAlert("Barang berhasil ditambahkan!", "success");
|
|
closeModal("reportFoundModal");
|
|
e.target.reset();
|
|
await loadItems();
|
|
await loadStats();
|
|
} catch (error) {
|
|
console.error("Error submitting item:", error);
|
|
showAlert(error.message || "Gagal menambahkan barang", "danger");
|
|
} finally {
|
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = "Submit";
|
|
}
|
|
}
|
|
});
|
|
|
|
// Setup search and filters
|
|
function setupSearchAndFilters() {
|
|
// Items tab
|
|
const searchItems = document.getElementById("searchItems");
|
|
const categoryFilterItems = document.getElementById("categoryFilterItems");
|
|
const statusFilterItems = document.getElementById("statusFilterItems");
|
|
const sortItems = document.getElementById("sortItems");
|
|
|
|
const performItemsSearch = debounce(() => {
|
|
const searchTerm = searchItems?.value.toLowerCase() || "";
|
|
const category = categoryFilterItems?.value || "";
|
|
const status = statusFilterItems?.value || "";
|
|
const sort = sortItems?.value || "date_desc";
|
|
|
|
let filtered = allItems.filter((item) => {
|
|
const matchesSearch =
|
|
item.name.toLowerCase().includes(searchTerm) ||
|
|
item.location.toLowerCase().includes(searchTerm);
|
|
const matchesCategory = !category || item.category === category;
|
|
const matchesStatus = !status || item.status === status;
|
|
return matchesSearch && matchesCategory && matchesStatus;
|
|
});
|
|
|
|
// Sort
|
|
filtered.sort((a, b) => {
|
|
switch (sort) {
|
|
case "date_desc":
|
|
return new Date(b.date_found) - new Date(a.date_found);
|
|
case "date_asc":
|
|
return new Date(a.date_found) - new Date(b.date_found);
|
|
case "name_asc":
|
|
return a.name.localeCompare(b.name);
|
|
case "name_desc":
|
|
return b.name.localeCompare(a.name);
|
|
default:
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
renderItems(filtered);
|
|
}, 300);
|
|
|
|
searchItems?.addEventListener("input", performItemsSearch);
|
|
categoryFilterItems?.addEventListener("change", performItemsSearch);
|
|
statusFilterItems?.addEventListener("change", performItemsSearch);
|
|
sortItems?.addEventListener("change", performItemsSearch);
|
|
|
|
// Claims tab
|
|
const searchClaims = document.getElementById("searchClaims");
|
|
const statusFilterClaims = document.getElementById("statusFilterClaims");
|
|
|
|
const performClaimsSearch = debounce(() => {
|
|
const searchTerm = searchClaims?.value.toLowerCase() || "";
|
|
const status = statusFilterClaims?.value || "";
|
|
|
|
let filtered = allClaims.filter((claim) => {
|
|
const matchesSearch =
|
|
claim.item_name.toLowerCase().includes(searchTerm) ||
|
|
claim.user_name.toLowerCase().includes(searchTerm);
|
|
const matchesStatus = !status || claim.status === status;
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
renderClaims(filtered);
|
|
}, 300);
|
|
|
|
searchClaims?.addEventListener("input", performClaimsSearch);
|
|
statusFilterClaims?.addEventListener("change", performClaimsSearch);
|
|
}
|
|
|
|
// Create edit item modal if not exists
|
|
if (!document.getElementById("editItemModal")) {
|
|
const editItemModal = document.createElement("div");
|
|
editItemModal.id = "editItemModal";
|
|
editItemModal.className = "modal";
|
|
editItemModal.innerHTML = `
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3 class="modal-title">Edit Barang</h3>
|
|
<button class="close-btn" onclick="closeModal('editItemModal')">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="editItemForm">
|
|
<input type="hidden" name="item_id">
|
|
<div class="form-group">
|
|
<label>Foto Barang (Opsional - Kosongkan jika tidak ingin mengubah)</label>
|
|
<input type="file" name="photo" accept="image/*">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Nama Barang *</label>
|
|
<input type="text" name="name" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Kategori *</label>
|
|
<select name="category" required>
|
|
<option value="">Pilih Kategori</option>
|
|
<option value="pakaian">Pakaian</option>
|
|
<option value="alat_makan">Alat Makan</option>
|
|
<option value="aksesoris">Aksesoris</option>
|
|
<option value="elektronik">Elektronik</option>
|
|
<option value="alat_tulis">Alat Tulis</option>
|
|
<option value="lainnya">Lainnya</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Lokasi Ditemukan *</label>
|
|
<input type="text" name="location" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Deskripsi Keunikan *</label>
|
|
<textarea name="description" required></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Nama Pelapor *</label>
|
|
<input type="text" name="reporter_name" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Kontak Pelapor *</label>
|
|
<input type="text" name="reporter_contact" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Tanggal Ditemukan *</label>
|
|
<input type="date" name="date_found" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary" style="width: 100%;">Update</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(editItemModal);
|
|
}
|