Basdat/web/js/pages/admin/tabs/LostItemsTabAdmin.js
2025-12-20 00:01:08 +07:00

239 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const LostItemsTabAdmin = ({ state, handlers }) => {
const {
lostItems,
lostItemSearchTerm,
setLostItemSearchTerm,
lostItemStatusFilter,
setLostItemStatusFilter,
lostItemCategoryFilter,
setLostItemCategoryFilter,
lostItemPage,
lostItemTotalPages,
lostItemTotalRecords,
categories,
loading,
showCreateLostItemModal,
setShowCreateLostItemModal,
} = state;
const {
handleViewLostItemDetail,
handleEditLostItemClick,
handleDeleteLostItem,
loadLostItems,
} = handlers;
React.useEffect(() => {
const timer = setTimeout(() => {
loadLostItems(lostItemPage);
}, 500);
return () => clearTimeout(timer);
}, [
lostItemPage,
lostItemSearchTerm,
lostItemStatusFilter,
lostItemCategoryFilter,
]);
React.useEffect(() => {
state.setLostItemPage(1);
}, [lostItemSearchTerm, lostItemStatusFilter, lostItemCategoryFilter]);
return (
<div className="bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl shadow-xl border border-slate-700">
<div className="p-6 border-b border-slate-700">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold text-white">
🔍 Kelola Laporan Barang Hilang
</h2>
<button
onClick={() => setShowCreateLostItemModal(true)}
className="px-4 py-2 bg-gradient-to-r from-green-600 to-green-700 text-white rounded-lg font-semibold hover:from-green-700 hover:to-green-800 transition shadow-lg"
>
Tambah Laporan
</button>
</div>
<div className="flex gap-3 flex-wrap">
<input
type="text"
placeholder="Cari laporan (nama/deskripsi)..."
value={lostItemSearchTerm}
onChange={(e) => setLostItemSearchTerm(e.target.value)}
className="flex-1 min-w-[200px] px-4 py-2 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
/>
<select
value={lostItemStatusFilter}
onChange={(e) => setLostItemStatusFilter(e.target.value)}
className="px-4 py-2 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
>
<option value="">Semua Status</option>
<option value="active">Active</option>
<option value="pending_verification">Pending Verification</option>
<option value="claimed">Claimed</option>
<option value="completed">Completed</option>
<option value="closed">Closed</option>
</select>
<select
value={lostItemCategoryFilter}
onChange={(e) => setLostItemCategoryFilter(e.target.value)}
className="px-4 py-2 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
>
<option value="">Semua Kategori</option>
{categories &&
categories.map((cat) => (
<option key={cat.id} value={cat.slug || cat.id}>
{cat.name}
</option>
))}
</select>
</div>
</div>
<div className="p-6">
{loading ? (
<div className="text-center py-12 text-slate-400">
<div className="text-6xl mb-4 animate-pulse"></div>
<p>Memuat data...</p>
</div>
) : (
<>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-slate-700">
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
ID
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Nama Barang
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Kategori
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Pelapor
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Lokasi
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Tanggal Hilang
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Status
</th>
<th className="text-left py-3 px-4 text-slate-300 font-semibold">
Aksi
</th>
</tr>
</thead>
<tbody>
{lostItems &&
lostItems.map((item) => (
<tr
key={item.id}
className="border-b border-slate-700/50 hover:bg-slate-700/30 transition"
>
<td className="py-3 px-4 text-slate-300">#{item.id}</td>
<td className="py-3 px-4">
<div className="font-semibold text-white">
{item.name}
</div>
{item.color && (
<div className="text-xs text-slate-400">
🎨 {item.color}
</div>
)}
</td>
<td className="py-3 px-4 text-slate-300">
{item.category}
</td>
<td className="py-3 px-4">
<div className="text-white">{item.user_name}</div>
<div className="text-xs text-slate-400">
ID: {item.user_id}
</div>
</td>
<td className="py-3 px-4 text-slate-300">
{item.location || "-"}
</td>
<td className="py-3 px-4 text-slate-300">
{Helpers.formatDateShort(item.date_lost)}
</td>
<td className="py-3 px-4">
<span
className={`inline-block px-3 py-1 rounded-full text-xs font-semibold ${Helpers.getStatusBadgeClass(
item.status
)}`}
>
{item.status}
</span>
{item.direct_claim_status && (
<div className="text-xs text-yellow-400 mt-1">
📋 Claim: {item.direct_claim_status}
</div>
)}
</td>
<td className="py-3 px-4">
<div className="flex gap-2">
<button
onClick={() => handleViewLostItemDetail(item)}
className="px-3 py-1 bg-blue-600 text-white text-xs rounded hover:bg-blue-700 transition"
title="Detail"
>
📋
</button>
<button
onClick={() => handleEditLostItemClick(item)}
className="px-3 py-1 bg-yellow-600 text-white text-xs rounded hover:bg-yellow-700 transition"
title="Edit"
>
</button>
<button
onClick={() => {
if (
confirm(
`⚠️ Yakin ingin menghapus laporan "${item.name}"?`
)
) {
handleDeleteLostItem(item.id);
}
}}
className="px-3 py-1 bg-red-600 text-white text-xs rounded hover:bg-red-700 transition"
title="Hapus"
>
🗑
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
{(!lostItems || lostItems.length === 0) && (
<div className="text-center py-12 text-slate-400">
<div className="text-6xl mb-4">🔍</div>
<p>Tidak ada laporan barang hilang</p>
</div>
)}
{lostItems && lostItems.length > 0 && (
<Pagination
currentPage={lostItemPage}
totalPages={lostItemTotalPages}
totalRecords={lostItemTotalRecords}
onPageChange={(page) => state.setLostItemPage(page)}
itemsPerPage={10}
/>
)}
</>
)}
</div>
</div>
);
};