// 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.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}
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) => `
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.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.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);
}