2025-11-17 12:17:44 +07:00

491 lines
14 KiB
JavaScript

// Dashboard User JavaScript - FIXED ENDPOINTS
let allItems = [];
let allLostItems = [];
let allClaims = [];
// Initialize dashboard
window.addEventListener("DOMContentLoaded", async () => {
const user = checkAuth();
if (!user || user.role !== "user") {
window.location.href = "/login";
return;
}
await loadStats();
await loadBrowseItems();
setupSearchAndFilters();
});
// Load statistics - CORRECT (sudah sesuai)
async function loadStats() {
try {
const stats = await apiCall("/api/user/stats");
document.getElementById("statLost").textContent = stats.lost_items || 0;
document.getElementById("statFound").textContent = stats.found_items || 0;
document.getElementById("statClaims").textContent = stats.claims || 0;
} catch (error) {
console.error("Error loading stats:", error);
}
}
// Load browse items - CORRECT (sudah sesuai)
async function loadBrowseItems() {
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 ditemukan");
return;
}
grid.innerHTML = items
.map(
(item) => `
<div class="item-card" onclick="viewItemDetail(${item.id})">
<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>
${
item.status === "unclaimed"
? `<button class="btn btn-primary btn-sm" onclick="event.stopPropagation(); openClaimModal(${item.id})">Klaim Barang</button>`
: ""
}
</div>
</div>
`
)
.join("");
}
// View item detail - CORRECT (sudah sesuai)
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>
${
item.status === "unclaimed"
? `<button class="btn btn-primary" onclick="openClaimModal(${item.id})" style="width: 100%; margin-top: 20px;">Klaim Barang Ini</button>`
: ""
}
`;
openModal("itemDetailModal");
} catch (error) {
console.error("Error loading item detail:", error);
showAlert("Gagal memuat detail barang", "danger");
}
}
// Open claim modal
function openClaimModal(itemId) {
closeModal("itemDetailModal");
const modalContent = document.getElementById("claimModalContent");
modalContent.innerHTML = `
<form id="claimForm" onsubmit="submitClaim(event, ${itemId})">
<div class="form-group">
<label>Deskripsi Barang (Jelaskan ciri-ciri barang Anda) *</label>
<textarea name="description" required placeholder="Jelaskan ciri khusus barang yang hanya Anda tahu..."></textarea>
</div>
<div class="form-group">
<label>Bukti Pendukung (Opsional)</label>
<input type="file" name="proof" accept="image/*">
<small style="color: #64748b;">Upload foto barang saat Anda masih memilikinya (opsional)</small>
</div>
<div class="form-group">
<label>No. Kontak *</label>
<input type="text" name="contact" required placeholder="08123456789">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%;">Submit Klaim</button>
</form>
`;
openModal("claimModal");
}
// Submit claim - CORRECT (sudah sesuai)
async function submitClaim(event, itemId) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
formData.append("item_id", itemId);
try {
const submitBtn = form.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="loading"></span> Mengirim...';
await apiUpload("/api/claims", formData);
showAlert(
"Klaim berhasil disubmit! Menunggu verifikasi dari manager.",
"success"
);
closeModal("claimModal");
await loadBrowseItems();
await loadStats();
} catch (error) {
console.error("Error submitting claim:", error);
showAlert(error.message || "Gagal submit klaim", "danger");
} finally {
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.textContent = "Submit Klaim";
}
}
}
// Load my lost items - CORRECT (sudah sesuai)
async function loadLost() {
setLoading("lostItemsGrid", true);
try {
const response = await apiCall("/api/user/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",
"😢",
"Anda belum melaporkan 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>
</div>
`
)
.join("");
}
// Load my found items - FIXED
async function loadFound() {
setLoading("foundItemsGrid", true);
try {
const response = await apiCall("/api/user/items");
const items = response.data || [];
renderFoundItems(items);
} catch (error) {
console.error("Error loading found items:", error);
showEmptyState(
"foundItemsGrid",
"🎉",
"Gagal memuat data barang yang ditemukan"
);
}
}
// Render found items
function renderFoundItems(items) {
const grid = document.getElementById("foundItemsGrid");
if (!items || items.length === 0) {
showEmptyState(
"foundItemsGrid",
"🎉",
"Anda belum melaporkan penemuan 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>
</div>
`
)
.join("");
}
// Load my claims - CORRECT (sudah sesuai)
async function loadClaims() {
setLoading("claimsGrid", true);
try {
const response = await apiCall("/api/user/claims");
allClaims = response.data || [];
renderClaims(allClaims);
} catch (error) {
console.error("Error loading claims:", error);
showEmptyState("claimsGrid", "🤝", "Gagal memuat data klaim");
}
}
// Render claims
function renderClaims(claims) {
const grid = document.getElementById("claimsGrid");
if (!claims || claims.length === 0) {
showEmptyState("claimsGrid", "🤝", "Anda belum pernah mengajukan klaim");
return;
}
grid.innerHTML = claims
.map(
(claim) => `
<div class="item-card">
<div class="item-body">
<h3 class="item-title">${claim.item_name}</h3>
<div class="item-meta">
<span>📅 ${formatDate(claim.created_at)}</span>
<span>${getStatusBadge(claim.status)}</span>
</div>
<p style="color: #64748b; font-size: 0.9rem; margin-top: 10px;">
${claim.description}
</p>
${
claim.status === "rejected" && claim.notes
? `
<p style="color: #ef4444; font-size: 0.9rem; margin-top: 10px;">
<strong>Alasan ditolak:</strong> ${claim.notes}
</p>
`
: ""
}
</div>
</div>
`
)
.join("");
}
// Report lost item
function openReportLostModal() {
openModal("reportLostModal");
}
// Submit lost item report - CORRECT (sudah sesuai)
document
.getElementById("reportLostForm")
?.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
try {
const submitBtn = e.target.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="loading"></span> Mengirim...';
await apiCall("/api/lost-items", {
method: "POST",
body: JSON.stringify(data),
});
showAlert("Laporan kehilangan berhasil disubmit!", "success");
closeModal("reportLostModal");
e.target.reset();
await loadLost();
await loadStats();
} catch (error) {
console.error("Error submitting lost item:", error);
showAlert(error.message || "Gagal submit laporan", "danger");
} finally {
const submitBtn = e.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.textContent = "Submit Laporan";
}
}
});
// Report found item
function openReportFoundModal() {
openModal("reportFoundModal");
}
// Submit found item report - CORRECT (sudah sesuai)
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("Laporan penemuan berhasil disubmit!", "success");
closeModal("reportFoundModal");
e.target.reset();
await loadFound();
await loadStats();
} catch (error) {
console.error("Error submitting found item:", error);
showAlert(error.message || "Gagal submit laporan", "danger");
} finally {
const submitBtn = e.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.textContent = "Submit Penemuan";
}
}
});
// Setup search and filters
function setupSearchAndFilters() {
const searchInput = document.getElementById("searchInput");
const categoryFilter = document.getElementById("categoryFilter");
const sortBy = document.getElementById("sortBy");
const performSearch = debounce(() => {
const searchTerm = searchInput?.value.toLowerCase() || "";
const category = categoryFilter?.value || "";
const sort = sortBy?.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;
return matchesSearch && matchesCategory;
});
// 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);
searchInput?.addEventListener("input", performSearch);
categoryFilter?.addEventListener("change", performSearch);
sortBy?.addEventListener("change", performSearch);
}
// Create claim modal if not exists
if (!document.getElementById("claimModal")) {
const claimModal = document.createElement("div");
claimModal.id = "claimModal";
claimModal.className = "modal";
claimModal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Klaim Barang</h3>
<button class="close-btn" onclick="closeModal('claimModal')">&times;</button>
</div>
<div class="modal-body" id="claimModalContent"></div>
</div>
`;
document.body.appendChild(claimModal);
}
// Create item detail modal if not exists
if (!document.getElementById("itemDetailModal")) {
const itemDetailModal = document.createElement("div");
itemDetailModal.id = "itemDetailModal";
itemDetailModal.className = "modal";
itemDetailModal.innerHTML = `
<div class="modal-content modal-large">
<div class="modal-header">
<h3 class="modal-title">Detail Barang</h3>
<button class="close-btn" onclick="closeModal('itemDetailModal')">&times;</button>
</div>
<div class="modal-body" id="itemDetailContent"></div>
</div>
`;
document.body.appendChild(itemDetailModal);
}