// web/js/manager.js // 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) => `
${item.name}

${item.name}

📍 ${item.location} 📅 ${formatDate(item.date_found)} ${getStatusBadge(item.status)}
${ item.status !== "case_closed" && item.status !== "expired" ? `` : "" } ${ item.status === "verified" ? `` : "" }
` ) .join(""); } // View item detail - FIXED async function viewItemDetail(itemId) { try { const item = await apiCall(`/api/items/${itemId}`); const modalContent = document.getElementById("itemDetailContent"); modalContent.innerHTML = ` ${item.name}

${item.name}

Kategori: ${item.category}
Lokasi Ditemukan: ${item.location}
Tanggal Ditemukan: ${formatDate( item.date_found )}
Status: ${getStatusBadge(item.status)}
Pelapor: ${item.reporter_name}
Kontak: ${item.reporter_contact}
Deskripsi Keunikan (Rahasia):
${item.description}
`; 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 = ' 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 = `
🤝

Gagal memuat data klaim

`; } } // Render claims function renderClaims(claims) { const list = document.getElementById("claimsList"); if (!claims || claims.length === 0) { list.innerHTML = `
🤝

Belum ada klaim yang masuk

`; return; } list.innerHTML = claims .map( (claim) => `

${claim.item_name}

${getStatusBadge(claim.status)}
Pengklaim: ${claim.user_name}
Kontak: ${claim.contact}
Tanggal Klaim: ${formatDate( claim.created_at )}
${ claim.match_percentage ? `
Match: ${claim.match_percentage}%
` : "" }
Deskripsi dari Pengklaim:
${claim.description}
${ claim.status === "pending" ? `
` : "" } ${ claim.status === "rejected" && claim.notes ? `
Alasan: ${claim.notes}
` : "" }
` ) .join(""); } // Verify claim - FIXED async function verifyClaim(claimId) { try { const claim = await apiCall(`/api/claims/${claimId}`); const modalContent = document.getElementById("verifyClaimContent"); modalContent.innerHTML = `

Deskripsi Asli Barang

${claim.item_description}

Deskripsi dari Pengklaim

${claim.description}
${ claim.proof_url ? `

Bukti Pendukung

` : "" } ${ claim.match_percentage ? `
Similarity Match: ${claim.match_percentage}%
` : "" }
Info Pengklaim:
Nama: ${claim.user_name}
Kontak: ${claim.contact}
`; 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) => `

${item.name}

🏷️ ${item.category} 🎨 ${item.color} 📅 ${formatDate(item.date_lost)} ${item.location ? `📍 ${item.location}` : ""}

${ item.description }

Pelapor: ${item.user_name}
` ) .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 = `
🔍

Tidak ada barang yang cocok

Belum ada barang ditemukan yang mirip dengan laporan ini

`; return; } modalContent.innerHTML = `

Ditemukan ${ matches.length } barang yang mungkin cocok:

${matches .map( (match) => `
${match.name}
${match.similarity}% Match

${match.name}

📍 ${match.location} 📅 ${formatDate(match.date_found)} ${getStatusBadge(match.status)}
` ) .join("")}
`; } catch (error) { console.error("Error finding similar items:", error); document.getElementById("matchItemsContent").innerHTML = `

Gagal mencari barang yang mirip

`; } } // 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) => `
${item.name}

${item.name}

📍 ${item.location} 📅 ${formatDate(item.date_found)} ${getStatusBadge(item.status)}
` ) .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 = ' 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 = ` `; document.body.appendChild(editItemModal); }