239 lines
9.6 KiB
JavaScript
239 lines
9.6 KiB
JavaScript
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>
|
||
);
|
||
};
|