524 lines
18 KiB
JavaScript
524 lines
18 KiB
JavaScript
// assets/js/pages/user/UserModals.js
|
||
|
||
// ✅ [BARU] Modal untuk memilih Laporan Hilang yang cocok
|
||
const SelectLostItemModal = ({
|
||
isOpen,
|
||
onClose,
|
||
lostItems,
|
||
onSelect,
|
||
onManual,
|
||
}) => (
|
||
<Modal isOpen={isOpen} onClose={onClose} title="💡 Ditemukan Laporan Cocok">
|
||
<div className="space-y-4">
|
||
<div className="bg-blue-500/10 border border-blue-500/30 p-4 rounded-xl">
|
||
<p className="text-sm text-blue-200">
|
||
Kami menemukan laporan <strong>Barang Hilang Anda</strong> dengan
|
||
kategori yang sama. Pilih salah satu untuk mengisi form klaim secara
|
||
otomatis.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="space-y-3 max-h-80 overflow-y-auto pr-1">
|
||
{lostItems.map((item) => (
|
||
<div
|
||
key={item.id}
|
||
onClick={() => onSelect(item)}
|
||
className="bg-slate-800 border border-slate-600 hover:border-blue-500 hover:bg-slate-700/50 p-4 rounded-xl cursor-pointer transition group"
|
||
>
|
||
<div className="flex justify-between items-start">
|
||
<div>
|
||
<h4 className="font-bold text-white group-hover:text-blue-400 transition">
|
||
{item.name}
|
||
</h4>
|
||
<p className="text-xs text-slate-400 mt-1">
|
||
📅 {Helpers.formatDate(item.date_lost)} • 📍{" "}
|
||
{item.location || "?"}
|
||
</p>
|
||
</div>
|
||
<span className="text-2xl opacity-50 group-hover:opacity-100 transition">
|
||
👉
|
||
</span>
|
||
</div>
|
||
<p className="text-xs text-slate-300 mt-2 line-clamp-2 italic">
|
||
"{item.description}"
|
||
</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-slate-700">
|
||
<button
|
||
onClick={onManual}
|
||
className="w-full py-3 bg-slate-700 hover:bg-slate-600 text-white rounded-xl font-semibold transition"
|
||
>
|
||
📝 Tidak, Saya Ingin Input Manual
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
);
|
||
|
||
const DetailModalUser = ({ isOpen, onClose, item, user, onClaim }) => (
|
||
<Modal isOpen={isOpen} onClose={onClose} title="Detail Barang">
|
||
{item && (
|
||
<div>
|
||
<img
|
||
src={item.photo_url || "https://via.placeholder.com/600x400"}
|
||
alt={item.name}
|
||
className="w-full h-64 object-cover rounded-xl mb-4 border-2 border-slate-600"
|
||
/>
|
||
<h3 className="text-2xl font-bold mb-4 text-white">{item.name}</h3>
|
||
<div className="space-y-3 text-slate-200">
|
||
<div className="bg-slate-700/50 p-3 rounded-lg">
|
||
<strong className="text-blue-400">Kategori:</strong>
|
||
<span className="ml-2">
|
||
{item.category || Helpers.getCategoryName(item.category_id)}
|
||
</span>
|
||
</div>
|
||
<div className="bg-slate-700/50 p-3 rounded-lg">
|
||
<strong className="text-blue-400">Lokasi:</strong>
|
||
<span className="ml-2">{item.location}</span>
|
||
</div>
|
||
<div className="bg-slate-700/50 p-3 rounded-lg">
|
||
<strong className="text-blue-400">Tanggal:</strong>
|
||
<span className="ml-2">{Helpers.formatDate(item.date_found)}</span>
|
||
</div>
|
||
<div className="bg-slate-700/50 p-3 rounded-lg">
|
||
<strong className="text-blue-400">Status:</strong>
|
||
<span
|
||
className={`ml-2 px-3 py-1 rounded-full text-xs font-semibold ${Helpers.getStatusBadgeClass(
|
||
item.status
|
||
)}`}
|
||
>
|
||
{item.status}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{item.status === "unclaimed" && item.reporter_id !== user?.id && (
|
||
<button
|
||
onClick={() => {
|
||
onClose();
|
||
onClaim(item);
|
||
}}
|
||
className="w-full mt-6 px-4 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-xl hover:from-blue-700 hover:to-blue-800 transition font-semibold shadow-lg"
|
||
>
|
||
Klaim Barang Ini
|
||
</button>
|
||
)}
|
||
|
||
{item.reporter_id === user?.id && (
|
||
<div className="mt-6 p-4 bg-green-500/10 border-2 border-green-500/30 rounded-xl text-center">
|
||
<div className="text-green-400 font-semibold text-lg mb-2">
|
||
✓ Ini adalah barang yang Anda laporkan
|
||
</div>
|
||
<p className="text-slate-400 text-sm">
|
||
Anda tidak dapat mengklaim barang yang Anda sendiri temukan
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</Modal>
|
||
);
|
||
|
||
const ClaimModalUser = ({
|
||
isOpen,
|
||
onClose,
|
||
onSubmit,
|
||
loading,
|
||
initialData,
|
||
categories, // Pastikan prop ini diterima
|
||
}) => {
|
||
const [formData, setFormData] = React.useState({
|
||
description: "",
|
||
contact: "",
|
||
});
|
||
|
||
React.useEffect(() => {
|
||
if (isOpen) {
|
||
if (initialData) {
|
||
setFormData({
|
||
description: initialData.description || "",
|
||
contact: initialData.contact || "",
|
||
});
|
||
} else {
|
||
setFormData({ description: "", contact: "" });
|
||
}
|
||
}
|
||
}, [isOpen, initialData]);
|
||
|
||
const handleChange = (e) => {
|
||
const { name, value } = e.target;
|
||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||
};
|
||
|
||
if (!isOpen) return null;
|
||
|
||
return (
|
||
<Modal isOpen={isOpen} onClose={onClose} title="Klaim Barang">
|
||
<form onSubmit={onSubmit} className="space-y-4">
|
||
{initialData && (
|
||
<div className="bg-green-500/10 border border-green-500/30 p-3 rounded-lg mb-2">
|
||
<p className="text-xs text-green-300">
|
||
✅ Data terisi otomatis dari laporan hilang Anda. Silakan lengkapi
|
||
bukti.
|
||
</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* Deskripsi */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Deskripsi Barang *
|
||
</label>
|
||
<textarea
|
||
name="description"
|
||
required
|
||
rows="4"
|
||
value={formData.description}
|
||
onChange={handleChange}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Jelaskan ciri khusus barang yang hanya Anda tahu..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Kontak */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Kontak yang bisa dihubungi *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="contact"
|
||
required
|
||
value={formData.contact}
|
||
onChange={handleChange}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="No HP / WhatsApp / Line"
|
||
/>
|
||
</div>
|
||
|
||
{/* Bukti */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Bukti Pendukung (Opsional)
|
||
</label>
|
||
<input
|
||
type="file"
|
||
name="proof"
|
||
accept="image/*,.pdf"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-slate-300 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-600 file:text-white hover:file:bg-blue-700 transition"
|
||
/>
|
||
<p className="text-xs text-slate-400 mt-1">
|
||
Foto struk, foto lama barang, atau bukti kepemilikan lainnya.
|
||
</p>
|
||
</div>
|
||
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
className="w-full px-4 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-xl hover:from-blue-700 hover:to-blue-800 transition font-semibold shadow-lg disabled:from-slate-600 disabled:to-slate-700"
|
||
>
|
||
{loading ? "Mengirim..." : "Ajukan Klaim"}
|
||
</button>
|
||
</form>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
const ReportLostModalUser = ({
|
||
isOpen,
|
||
onClose,
|
||
onSubmit,
|
||
loading,
|
||
categories,
|
||
}) => {
|
||
if (!isOpen) return null;
|
||
|
||
return (
|
||
<Modal isOpen={isOpen} onClose={onClose} title="📢 Lapor Kehilangan Barang">
|
||
<form onSubmit={onSubmit} className="space-y-4">
|
||
{/* Nama Barang */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Nama Barang Hilang *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="name"
|
||
required
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Contoh: Dompet Hitam Merk X"
|
||
/>
|
||
</div>
|
||
|
||
{/* Kategori Dropdown */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Kategori *
|
||
</label>
|
||
<select
|
||
name="category_id"
|
||
required
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
|
||
>
|
||
<option value="">-- Pilih Kategori --</option>
|
||
{categories && categories.length > 0 ? (
|
||
categories.map((cat) => (
|
||
<option key={cat.id} value={cat.id}>
|
||
{cat.name}
|
||
</option>
|
||
))
|
||
) : (
|
||
<option value="" disabled>
|
||
Gagal memuat kategori (Cek Database)
|
||
</option>
|
||
)}
|
||
</select>
|
||
</div>
|
||
|
||
{/* Warna (Opsional) */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Warna Dominan
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="color"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Contoh: Hitam"
|
||
/>
|
||
</div>
|
||
|
||
{/* Lokasi */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Perkiraan Lokasi Hilang
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="location"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Contoh: Kantin, Lab Komputer"
|
||
/>
|
||
</div>
|
||
|
||
{/* Deskripsi */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Ciri-ciri Detail *
|
||
</label>
|
||
<textarea
|
||
name="description"
|
||
required
|
||
rows="3"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Jelaskan ciri-ciri unik, isi dompet, stiker laptop, dll..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Tanggal Hilang */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Tanggal Hilang *
|
||
</label>
|
||
<input
|
||
type="date"
|
||
name="date_lost"
|
||
required
|
||
max={new Date().toISOString().split("T")[0]}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
className="w-full px-4 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-xl hover:from-blue-700 hover:to-blue-800 transition font-semibold shadow-lg disabled:from-slate-600 disabled:to-slate-700"
|
||
>
|
||
{loading ? "Mengirim..." : "Submit Laporan"}
|
||
</button>
|
||
</form>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
const ReportFoundModalUser = ({
|
||
isOpen,
|
||
onClose,
|
||
onSubmit,
|
||
loading,
|
||
photoPreview,
|
||
onPhotoChange,
|
||
user,
|
||
categories,
|
||
}) => (
|
||
<Modal isOpen={isOpen} onClose={onClose} title="Lapor Barang Temuan">
|
||
<form onSubmit={onSubmit} className="space-y-4">
|
||
{/* Input Nama Barang */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Nama Barang *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="name"
|
||
required
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Contoh: Botol Minum Biru"
|
||
/>
|
||
</div>
|
||
|
||
{/* Input Kategori - BAGIAN YANG DIPERBAIKI */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Kategori *
|
||
</label>
|
||
<select
|
||
name="category_id"
|
||
required
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
|
||
>
|
||
<option value="">-- Pilih Kategori --</option>
|
||
{categories &&
|
||
categories.map((cat) => (
|
||
<option key={cat.id} value={cat.id}>
|
||
{cat.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* Input Foto Barang */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Foto Barang
|
||
</label>
|
||
<input
|
||
type="file"
|
||
name="photo"
|
||
accept="image/*"
|
||
onChange={onPhotoChange}
|
||
className="w-full text-slate-300 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-600 file:text-white hover:file:bg-blue-700 transition"
|
||
/>
|
||
{photoPreview && (
|
||
<div className="mt-3">
|
||
<p className="text-xs text-slate-400 mb-1">Preview:</p>
|
||
<img
|
||
src={photoPreview}
|
||
alt="Preview"
|
||
className="w-full h-48 object-cover rounded-xl border-2 border-blue-500"
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Input Lokasi */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Lokasi Ditemukan *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="location"
|
||
required
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Contoh: Perpustakaan Lantai 2"
|
||
/>
|
||
</div>
|
||
|
||
{/* Input Deskripsi */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Deskripsi Umum *
|
||
</label>
|
||
<textarea
|
||
name="description"
|
||
required
|
||
rows="3"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Deskripsi umum barang yang akan ditampilkan..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Input Ciri Rahasia */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Ciri Khusus Rahasia * 🔒
|
||
</label>
|
||
<textarea
|
||
name="secret_details"
|
||
required
|
||
rows="3"
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="Jelaskan ciri khusus yang HANYA pemilik asli yang tahu (contoh: ada goresan di bagian belakang, ada tulisan nama di dalam, dll)"
|
||
/>
|
||
<p className="text-xs text-yellow-400 mt-1">
|
||
⚠️ Info ini RAHASIA dan hanya digunakan untuk verifikasi klaim
|
||
</p>
|
||
</div>
|
||
|
||
{/* Input Tanggal */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Tanggal Ditemukan *
|
||
</label>
|
||
<input
|
||
type="date"
|
||
name="date_found"
|
||
required
|
||
max={new Date().toISOString().split("T")[0]}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
|
||
/>
|
||
</div>
|
||
|
||
{/* Input Pelapor (Read Only) */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Nama Pelapor *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="reporter_name"
|
||
required
|
||
defaultValue={user?.name}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
readOnly
|
||
/>
|
||
</div>
|
||
|
||
{/* Input Kontak */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Kontak Pelapor *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="reporter_contact"
|
||
required
|
||
defaultValue={user?.phone}
|
||
className="w-full px-4 py-3 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
|
||
placeholder="08123456789"
|
||
/>
|
||
</div>
|
||
|
||
{/* Tombol Submit */}
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
className="w-full px-4 py-3 bg-gradient-to-r from-green-600 to-green-700 text-white rounded-xl hover:from-green-700 hover:to-green-800 transition font-semibold shadow-lg disabled:from-slate-600 disabled:to-slate-700"
|
||
>
|
||
{loading ? "Mengirim..." : "Submit Laporan"}
|
||
</button>
|
||
</form>
|
||
</Modal>
|
||
);
|
||
|
||
// Export ke window
|
||
window.DetailModalUser = DetailModalUser;
|
||
window.ClaimModalUser = ClaimModalUser;
|
||
window.SelectLostItemModal = SelectLostItemModal;
|
||
window.ReportLostModalUser = ReportLostModalUser; // Pastikan ini ada
|
||
window.ReportFoundModalUser = ReportFoundModalUser;
|