351 lines
14 KiB
JavaScript
351 lines
14 KiB
JavaScript
// web/js/pages/user/UserTabsBrowse.js
|
||
|
||
// ✅ 1. Komponen BrowseTab (Barang Ditemukan)
|
||
const BrowseTab = ({ state, handlers }) => {
|
||
const {
|
||
filteredItems,
|
||
loading,
|
||
searchTerm,
|
||
setSearchTerm,
|
||
categoryFilter,
|
||
setCategoryFilter,
|
||
categories,
|
||
user,
|
||
} = state;
|
||
|
||
const { handleViewDetail, handleClaim } = handlers;
|
||
|
||
return (
|
||
<div className="bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl p-6 shadow-xl border border-slate-700">
|
||
<div className="p-6 border-b border-slate-700">
|
||
<h2 className="text-xl font-semibold mb-4 text-white">
|
||
📦 Barang Ditemukan
|
||
</h2>
|
||
|
||
<div className="flex gap-3 flex-wrap">
|
||
<input
|
||
type="text"
|
||
placeholder="Cari barang (nama/lokasi)..."
|
||
value={searchTerm}
|
||
onChange={(e) => setSearchTerm(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={categoryFilter}
|
||
onChange={(e) => setCategoryFilter(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="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{filteredItems.map((item) => (
|
||
<ItemCard
|
||
key={item.id}
|
||
item={item}
|
||
currentUserId={user?.id}
|
||
onViewDetail={handleViewDetail}
|
||
onClaim={handleClaim}
|
||
showActions={true}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{filteredItems.length === 0 && (
|
||
<div className="text-center py-12 text-slate-400">
|
||
<div className="text-6xl mb-4">📦</div>
|
||
<p>Tidak ada barang ditemukan sesuai pencarian.</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// ✅ 2. Komponen PublicLostItemsTab (Barang Hilang Publik)
|
||
const PublicLostItemsTab = ({ state, handlers }) => {
|
||
const { publicLostItems, loading, user } = state;
|
||
const [showContactModal, setShowContactModal] = React.useState(false);
|
||
const [selectedLostItem, setSelectedLostItem] = React.useState(null);
|
||
const [showDetailModal, setShowDetailModal] = React.useState(false);
|
||
const [selectedDetailItem, setSelectedDetailItem] = React.useState(null);
|
||
|
||
const handleShowContact = (item) => {
|
||
setSelectedLostItem(item);
|
||
setShowContactModal(true);
|
||
};
|
||
|
||
const handleShowDetail = (item) => {
|
||
setSelectedDetailItem(item);
|
||
setShowDetailModal(true);
|
||
};
|
||
|
||
return (
|
||
<div className="bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl p-6 shadow-xl border border-slate-700">
|
||
<div className="flex justify-between items-center mb-4">
|
||
<h2 className="text-xl font-semibold text-white">
|
||
Semua Laporan Barang Hilang
|
||
</h2>
|
||
<div className="text-slate-400 text-sm">
|
||
{publicLostItems.length} laporan
|
||
</div>
|
||
</div>
|
||
{loading ? (
|
||
<div className="text-center py-12 text-slate-400">
|
||
<div className="text-6xl mb-4">⏳</div>
|
||
<p>Memuat data...</p>
|
||
</div>
|
||
) : publicLostItems.length > 0 ? (
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{publicLostItems.map((item) => {
|
||
const isMine = user && user.id === item.user_id;
|
||
|
||
return (
|
||
<div
|
||
key={item.id}
|
||
className="bg-gradient-to-br from-slate-700 to-slate-800 border-2 border-slate-600 rounded-xl p-5 hover:border-blue-500 hover:shadow-xl hover:shadow-blue-500/20 transition-all"
|
||
>
|
||
{/* Header */}
|
||
<div className="flex justify-between items-start mb-3">
|
||
<h3 className="text-lg font-semibold text-white flex-1">
|
||
{item.name}
|
||
</h3>
|
||
|
||
{/* ✅ Tampilkan Badge jika barang sendiri */}
|
||
{isMine ? (
|
||
<span className="px-2 py-1 rounded text-[10px] font-bold bg-blue-500/20 text-blue-400 border border-blue-500/50">
|
||
BARANG SAYA
|
||
</span>
|
||
) : (
|
||
<span
|
||
className={`px-3 py-1 rounded-full text-xs font-semibold ${Helpers.getStatusBadgeClass(
|
||
item.status
|
||
)}`}
|
||
>
|
||
{item.status}
|
||
</span>
|
||
)}
|
||
</div>
|
||
|
||
<div className="space-y-2 mb-4">
|
||
<div className="flex items-center gap-2 text-sm text-slate-300">
|
||
<span className="text-blue-400">🏷️</span>
|
||
<span>{item.category || item.category_name}</span>
|
||
</div>
|
||
{item.location && (
|
||
<div className="flex items-center gap-2 text-sm text-slate-300">
|
||
<span className="text-blue-400">📍</span>
|
||
<span>{item.location}</span>
|
||
</div>
|
||
)}
|
||
<div className="flex items-center gap-2 text-sm text-slate-300">
|
||
<span className="text-blue-400">👤</span>
|
||
<span>{item.user_name}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-slate-900/50 p-3 rounded-lg border border-slate-700 mb-4">
|
||
<p className="text-sm text-slate-300 line-clamp-3">
|
||
{item.description}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => handleShowDetail(item)}
|
||
className="flex-1 px-3 py-2 bg-gradient-to-r from-blue-600 to-blue-700 text-white text-sm rounded-lg hover:from-blue-700 hover:to-blue-800 transition shadow-lg"
|
||
>
|
||
📋 Detail
|
||
</button>
|
||
{item.status === "active" && !isMine && (
|
||
<button
|
||
onClick={() => handlers.handleOpenFoundOption(item)}
|
||
className="flex-1 px-3 py-2 bg-gradient-to-r from-green-600 to-green-700 text-white text-sm rounded-lg hover:from-green-700 hover:to-green-800 transition shadow-lg"
|
||
>
|
||
✅ Saya Menemukan
|
||
</button>
|
||
)}
|
||
{isMine && (
|
||
<div className="flex-1 px-3 py-2 text-center text-xs text-slate-400 border border-slate-600 rounded-lg">
|
||
Kelola di tab "Barang Hilang Saya"
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
) : (
|
||
<div className="text-center py-12 text-slate-400">
|
||
<div className="text-6xl mb-4">😢</div>
|
||
<p>Belum ada laporan barang hilang</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* Modal Detail Barang Hilang */}
|
||
<Modal
|
||
isOpen={showDetailModal}
|
||
onClose={() => setShowDetailModal(false)}
|
||
title="📋 Detail Barang Hilang"
|
||
>
|
||
{selectedDetailItem && (
|
||
<div className="space-y-4">
|
||
<div className="bg-gradient-to-r from-slate-700 to-slate-800 p-4 rounded-xl border border-slate-600">
|
||
<h3 className="text-xl font-bold text-white mb-1">
|
||
{selectedDetailItem.name}
|
||
</h3>
|
||
<div className="flex flex-wrap gap-2 text-sm">
|
||
<span className="text-blue-400 font-medium">
|
||
🏷️{" "}
|
||
{selectedDetailItem.category ||
|
||
selectedDetailItem.category_name ||
|
||
"-"}
|
||
</span>
|
||
<span className="text-slate-500">•</span>
|
||
<span className="text-slate-300">
|
||
📅 {Helpers.formatDate(selectedDetailItem.date_lost)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div className="bg-slate-700/30 p-3 rounded-lg border border-slate-600">
|
||
<p className="text-xs text-slate-400 uppercase font-bold mb-1">
|
||
📍 Lokasi Hilang
|
||
</p>
|
||
<p className="text-white text-sm">
|
||
{selectedDetailItem.location || "-"}
|
||
</p>
|
||
</div>
|
||
<div className="bg-slate-700/30 p-3 rounded-lg border border-slate-600">
|
||
<p className="text-xs text-slate-400 uppercase font-bold mb-1">
|
||
🎨 Warna/Ciri
|
||
</p>
|
||
<p className="text-white text-sm">
|
||
{selectedDetailItem.color || "-"}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-slate-900/50 p-4 rounded-xl border border-slate-600">
|
||
<p className="text-xs text-slate-400 uppercase font-bold mb-2">
|
||
📝 Deskripsi Lengkap
|
||
</p>
|
||
<p className="text-slate-300 text-sm leading-relaxed italic">
|
||
"{selectedDetailItem.description}"
|
||
</p>
|
||
</div>
|
||
|
||
<div className="bg-blue-500/10 p-3 rounded-lg border border-blue-500/20 flex items-center gap-3">
|
||
<div className="bg-blue-600 w-10 h-10 rounded-full flex items-center justify-center text-lg shadow">
|
||
👤
|
||
</div>
|
||
<div>
|
||
<p className="text-xs text-blue-300 font-bold uppercase">
|
||
Dilaporkan Oleh
|
||
</p>
|
||
<p className="text-white font-medium">
|
||
{selectedDetailItem.user_name}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => setShowDetailModal(false)}
|
||
className="w-full py-3 bg-slate-700 hover:bg-slate-600 text-white rounded-xl font-semibold transition"
|
||
>
|
||
Tutup
|
||
</button>
|
||
</div>
|
||
)}
|
||
</Modal>
|
||
|
||
{/* Modal Kontak (Jika diperlukan) */}
|
||
<Modal
|
||
isOpen={showContactModal}
|
||
onClose={() => setShowContactModal(false)}
|
||
title="👋 Anda Menemukan Barang Ini?"
|
||
>
|
||
{selectedLostItem && (
|
||
<div className="text-center space-y-6">
|
||
<div className="bg-slate-700/50 p-6 rounded-2xl border border-slate-600">
|
||
<div className="w-20 h-20 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full mx-auto flex items-center justify-center text-3xl shadow-lg mb-4">
|
||
👤
|
||
</div>
|
||
<p className="text-slate-400 mb-1">Silakan hubungi pemilik:</p>
|
||
<h3 className="text-2xl font-bold text-white mb-2">
|
||
{selectedLostItem.user_name}
|
||
</h3>
|
||
{selectedLostItem.user_contact ? (
|
||
<a
|
||
href={`https://wa.me/${selectedLostItem.user_contact
|
||
.replace(/^0/, "62")
|
||
.replace(/\D/g, "")}`}
|
||
target="_blank"
|
||
className="inline-flex items-center gap-2 px-6 py-2 bg-green-600 hover:bg-green-700 text-white rounded-full font-semibold transition shadow-lg"
|
||
>
|
||
<span>📞</span> Hubungi via WhatsApp
|
||
</a>
|
||
) : (
|
||
<div className="text-yellow-400 text-sm bg-yellow-400/10 py-2 px-4 rounded-lg inline-block">
|
||
Kontak tidak ditampilkan secara publik
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="relative">
|
||
<div className="absolute inset-0 flex items-center">
|
||
<div className="w-full border-t border-slate-700"></div>
|
||
</div>
|
||
<div className="relative flex justify-center text-sm">
|
||
<span className="px-2 bg-slate-800 text-slate-400">Atau</span>
|
||
</div>
|
||
</div>
|
||
<div className="bg-blue-500/10 border border-blue-500/30 p-4 rounded-xl text-left flex gap-4">
|
||
<div className="text-3xl">📝</div>
|
||
<div>
|
||
<h4 className="font-bold text-blue-400 mb-1">
|
||
Laporkan Temuan
|
||
</h4>
|
||
<p className="text-sm text-slate-300 mb-2">
|
||
Agar lebih aman, Anda bisa melaporkan barang ini sebagai
|
||
"Ditemukan" di sistem kami.
|
||
</p>
|
||
<button
|
||
onClick={() => {
|
||
setShowContactModal(false);
|
||
}}
|
||
className="text-xs text-white bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition"
|
||
>
|
||
Buka Menu "Barang yang Saya Temukan"
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</Modal>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// Export ke window agar bisa diakses oleh UserApp
|
||
window.BrowseTab = BrowseTab;
|
||
window.PublicLostItemsTab = PublicLostItemsTab;
|