Basdat/web/js/pages/manager/ManagerManualClaimModal.js
2025-12-20 00:01:08 +07:00

306 lines
11 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.

// 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;