306 lines
11 KiB
JavaScript
306 lines
11 KiB
JavaScript
// web/js/pages/manager/ManagerManualClaimModal.js
|
||
// Modal untuk manager input klaim manual ketika user datang langsung (offline)
|
||
|
||
const ManagerManualClaimModal = ({
|
||
isOpen,
|
||
onClose,
|
||
onSubmit,
|
||
loading,
|
||
item,
|
||
}) => {
|
||
const [users, setUsers] = React.useState([]);
|
||
const [loadingUsers, setLoadingUsers] = React.useState(false);
|
||
const [selectedUser, setSelectedUser] = React.useState(null);
|
||
|
||
// State untuk input manual
|
||
const [claimerName, setClaimerName] = React.useState("");
|
||
const [claimerContact, setClaimerContact] = React.useState("");
|
||
const [description, setDescription] = React.useState("");
|
||
const [managerNotes, setManagerNotes] = React.useState("");
|
||
|
||
// Load users when modal opens
|
||
React.useEffect(() => {
|
||
if (isOpen) {
|
||
loadUsers();
|
||
resetForm();
|
||
}
|
||
}, [isOpen]);
|
||
|
||
const resetForm = () => {
|
||
setSelectedUser(null);
|
||
setClaimerName("");
|
||
setClaimerContact("");
|
||
setDescription("");
|
||
setManagerNotes("");
|
||
};
|
||
|
||
const loadUsers = async () => {
|
||
try {
|
||
setLoadingUsers(true);
|
||
const token = localStorage.getItem("token");
|
||
const response = await fetch(
|
||
`http://localhost:8080/api/manager/users?page=1&limit=1000`,
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
setUsers(data.data || []);
|
||
}
|
||
} catch (error) {
|
||
console.error("Failed to load users:", error);
|
||
} finally {
|
||
setLoadingUsers(false);
|
||
}
|
||
};
|
||
|
||
const handleUserSelect = (e) => {
|
||
const userId = e.target.value;
|
||
if (!userId) {
|
||
setSelectedUser(null);
|
||
setClaimerName("");
|
||
setClaimerContact("");
|
||
return;
|
||
}
|
||
|
||
const user = users.find((u) => u.id === parseInt(userId));
|
||
if (user) {
|
||
setSelectedUser(user);
|
||
setClaimerName(user.name);
|
||
setClaimerContact(user.phone || "");
|
||
}
|
||
};
|
||
|
||
const handleSubmit = () => {
|
||
// Validasi
|
||
if (!claimerName || !claimerContact || !description) {
|
||
alert("Nama pengklaim, kontak, dan deskripsi wajib diisi!");
|
||
return;
|
||
}
|
||
|
||
// Prepare data
|
||
const formData = {
|
||
item_id: item?.id,
|
||
user_id: selectedUser?.id || null,
|
||
claimer_name: claimerName,
|
||
contact: claimerContact,
|
||
description: description,
|
||
manager_notes: managerNotes,
|
||
};
|
||
|
||
onSubmit(formData);
|
||
};
|
||
|
||
if (!isOpen) return null;
|
||
|
||
return (
|
||
<div
|
||
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-70 p-4"
|
||
onClick={onClose}
|
||
>
|
||
<div
|
||
className="bg-gradient-to-br from-slate-800 to-slate-900 rounded-2xl w-full max-h-[90vh] overflow-y-auto border border-slate-700 shadow-2xl max-w-2xl"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
{/* Header */}
|
||
<div className="flex justify-between items-center p-6 border-b border-slate-700 sticky top-0 bg-slate-800/95 backdrop-blur">
|
||
<h3 className="text-xl font-semibold text-white">
|
||
🤝 Input Klaim Manual (Offline)
|
||
</h3>
|
||
<button
|
||
onClick={onClose}
|
||
className="text-2xl text-slate-400 hover:text-white transition"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
{/* Body */}
|
||
<div className="p-6 space-y-4">
|
||
{/* Info Box */}
|
||
<div className="bg-blue-500/10 border-2 border-blue-500/30 p-4 rounded-xl">
|
||
<div className="flex items-start gap-3">
|
||
<span className="text-2xl">ℹ️</span>
|
||
<div className="flex-1">
|
||
<strong className="text-blue-400 block mb-1">
|
||
Klaim Offline
|
||
</strong>
|
||
<p className="text-sm text-slate-300">
|
||
Fitur ini untuk mencatat klaim yang disampaikan langsung oleh
|
||
user kepada manager. Jika pengklaim memiliki akun, pilih di
|
||
bawah. Jika tidak, isi data manual.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Item Info */}
|
||
{item && (
|
||
<div className="bg-green-500/10 border-2 border-green-500/30 p-4 rounded-xl">
|
||
<strong className="text-green-400 block mb-2">
|
||
📦 Barang yang Diklaim:
|
||
</strong>
|
||
<div className="grid grid-cols-2 gap-3 text-sm text-slate-300">
|
||
<div>
|
||
<strong className="text-slate-200">Nama:</strong> {item.name}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">Lokasi:</strong>{" "}
|
||
{item.location}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">Tanggal:</strong>{" "}
|
||
{new Date(item.date_found).toLocaleDateString("id-ID")}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">Status:</strong>{" "}
|
||
{item.status}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* User Selection */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Pilih User (Pengklaim){" "}
|
||
<span className="text-slate-500 font-normal text-sm">
|
||
(Opsional)
|
||
</span>
|
||
</label>
|
||
<select
|
||
onChange={handleUserSelect}
|
||
disabled={loadingUsers}
|
||
value={selectedUser?.id || ""}
|
||
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="">-- Tamu / Tidak Terdaftar --</option>
|
||
{users.map((user) => (
|
||
<option key={user.id} value={user.id}>
|
||
{user.name} - {user.email} ({user.nrp})
|
||
</option>
|
||
))}
|
||
</select>
|
||
<p className="text-xs text-slate-400 mt-1">
|
||
Pilih jika pengklaim sudah punya akun. Jika belum, biarkan kosong
|
||
dan isi manual.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Selected User Info */}
|
||
{selectedUser && (
|
||
<div className="bg-green-500/10 border border-green-500/30 p-4 rounded-xl">
|
||
<strong className="text-green-400 block mb-2">
|
||
✅ User Terpilih:
|
||
</strong>
|
||
<div className="grid grid-cols-2 gap-3 text-sm text-slate-300">
|
||
<div>
|
||
<strong className="text-slate-200">Nama:</strong>{" "}
|
||
{selectedUser.name}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">Email:</strong>{" "}
|
||
{selectedUser.email}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">NRP:</strong>{" "}
|
||
{selectedUser.nrp}
|
||
</div>
|
||
<div>
|
||
<strong className="text-slate-200">Telepon:</strong>{" "}
|
||
{selectedUser.phone || "-"}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="border-t border-slate-700 pt-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4">
|
||
Detail Klaim
|
||
</h3>
|
||
</div>
|
||
|
||
{/* Claimer Name */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Nama Pengklaim *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={claimerName}
|
||
onChange={(e) => setClaimerName(e.target.value)}
|
||
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="Nama lengkap pengklaim"
|
||
/>
|
||
<p className="text-xs text-slate-400 mt-1">
|
||
Otomatis terisi jika memilih user, atau isi manual
|
||
</p>
|
||
</div>
|
||
|
||
{/* Contact */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Kontak Pengklaim *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={claimerContact}
|
||
onChange={(e) => setClaimerContact(e.target.value)}
|
||
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>
|
||
|
||
{/* Description */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Deskripsi Klaim (Ciri-ciri yang Disebutkan Pengklaim) *
|
||
</label>
|
||
<textarea
|
||
value={description}
|
||
onChange={(e) => setDescription(e.target.value)}
|
||
rows={4}
|
||
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="Tuliskan ciri-ciri barang yang disebutkan oleh pengklaim..."
|
||
/>
|
||
<p className="text-xs text-yellow-400 mt-1">
|
||
⚠️ Pastikan mencatat detail yang disebutkan pengklaim untuk
|
||
verifikasi
|
||
</p>
|
||
</div>
|
||
|
||
{/* Manager Notes */}
|
||
<div>
|
||
<label className="block font-semibold mb-2 text-slate-300">
|
||
Catatan Manager (Opsional)
|
||
</label>
|
||
<textarea
|
||
value={managerNotes}
|
||
onChange={(e) => setManagerNotes(e.target.value)}
|
||
rows={2}
|
||
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="Catatan internal untuk referensi..."
|
||
/>
|
||
</div>
|
||
|
||
{/* Submit Button */}
|
||
<button
|
||
onClick={handleSubmit}
|
||
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 disabled:cursor-not-allowed"
|
||
>
|
||
{loading ? "⏳ Memproses..." : "✅ Submit Klaim"}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// Export ke window untuk digunakan di file lain
|
||
window.ManagerManualClaimModal = ManagerManualClaimModal;
|