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

430 lines
13 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.

const useUserHandlers = (state) => {
const {
setToast,
setLoading,
setStats,
setItems,
setMyFoundItems,
setMyLostItems,
setPublicLostItems,
setMyClaims,
setSelectedItem,
setShowDetailModal,
setShowClaimModal,
setShowReportFoundModal,
setPhotoPreview,
setSuggestedLostItems,
setShowSelectLostItemModal,
setClaimInitialData,
myLostItems,
} = state;
const showToast = (message, type = "info") => {
setToast({ message, type });
};
const loadData = async () => {
try {
state.setLoading(true);
const [
itemsResponse,
myFoundResponse,
myLostResponse,
publicLostResponse,
myClaimsResponse,
categoriesResponse,
] = await Promise.all([
ApiUtils.get(`${CONFIG.API_ENDPOINTS.ITEMS}?page=1&limit=100`),
ApiUtils.get(CONFIG.API_ENDPOINTS.USER.ITEMS).catch(() => ({
data: [],
})),
ApiUtils.get(CONFIG.API_ENDPOINTS.USER.LOST_ITEMS).catch(() => ({
data: [],
})),
ApiUtils.get(CONFIG.API_ENDPOINTS.LOST_ITEMS).catch(() => ({
data: [],
})),
ApiUtils.get(CONFIG.API_ENDPOINTS.USER.CLAIMS).catch(() => ({
data: [],
})),
ApiUtils.get(
CONFIG.API_ENDPOINTS.CATEGORIES || "/api/categories"
).catch(() => ({ data: [] })),
]);
state.setStats({
lost_items: myLostResponse.data?.length || 0,
found_items: myFoundResponse.data?.length || 0,
claims: myClaimsResponse.data?.length || 0,
});
state.setItems(itemsResponse.data || []);
state.setMyFoundItems(myFoundResponse.data || []);
state.setMyLostItems(myLostResponse.data || []);
state.setPublicLostItems(publicLostResponse.data || []);
state.setMyClaims(myClaimsResponse.data || []);
if (state.setCategories) {
state.setCategories(categoriesResponse.data || []);
}
} catch (error) {
console.error("Error loading data:", error);
showToast("Gagal memuat data. Cek koneksi database.", "error");
} finally {
state.setLoading(false);
}
};
const handleLogout = () => {
if (Helpers.showConfirm("Apakah Anda yakin ingin logout?")) {
AuthUtils.clearAuth();
window.location.href = "/login";
}
};
const handleViewDetail = async (item) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}/${item.id}`
);
setSelectedItem(response.data || response);
setShowDetailModal(true);
} catch (error) {
showToast("Gagal memuat detail", "error");
}
};
const handleUserRespondClaim = async (claimId, action) => {
const confirmMsg =
action === "approve"
? '✅ Yakin ini barang Anda?\n\nJika approve:\n- Status akan menjadi "Verified"\n- Anda bisa koordinasi pengambilan dengan penemu\n- Setelah terima barang, konfirmasi untuk case closed'
: "❌ Yakin menolak?\n\nJika reject:\n- Klaim akan ditolak\n- Barang kembali dalam status pencarian\n- Orang lain bisa mengajukan klaim";
if (!confirm(confirmMsg)) return;
try {
state.setLoading(true);
// FIXED: URL endpoint sesuai controller
await ApiUtils.post(`/api/claims/${claimId}/respond`, {
action: action,
});
showToast(
action === "approve"
? "✅ Berhasil! Silakan koordinasi pengambilan dengan penemu."
: "❌ Klaim ditolak. Barang kembali dalam pencarian.",
"success"
);
// Reload data agar tampilan tombol berubah
await loadData();
} catch (error) {
console.error("Error responding:", error);
showToast(
"Gagal memproses: " + (error.response?.data?.message || error.message),
"error"
);
} finally {
state.setLoading(false);
}
};
const handleOpenDirectClaimModal = (lostItem) => {
console.log("Membuka direct claim modal untuk:", lostItem.name);
state.setSelectedLostItemForFound(lostItem);
state.setShowDirectClaimModal(true);
};
const handleClaim = (item) => {
setSelectedItem(item);
const matches = myLostItems.filter(
(lostItem) =>
lostItem.status === "active" &&
lostItem.category_id === item.category_id
);
if (matches.length > 0) {
setSuggestedLostItems(matches);
setShowSelectLostItemModal(true);
} else {
setClaimInitialData(null);
setShowClaimModal(true);
}
};
const handleSelectLostItemForClaim = (lostItem) => {
const initialData = {
description: `[Berdasarkan Laporan Hilang #${lostItem.id} - ${lostItem.name}]\n${lostItem.description}`,
contact: state.user?.phone || "",
};
setClaimInitialData(initialData);
setShowSelectLostItemModal(false);
setShowClaimModal(true);
};
const handleManualClaimEntry = () => {
setClaimInitialData(null);
setShowSelectLostItemModal(false);
setShowClaimModal(true);
};
const submitClaim = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
if (!formData.get("description") || !formData.get("contact")) {
showToast("Deskripsi dan Kontak harus diisi", "error");
return;
}
try {
setLoading(true);
showToast("Memproses klaim...", "info");
let proofUrl = "";
const proofFile = formData.get("proof");
if (proofFile && proofFile.size > 0) {
showToast("Mengupload bukti...", "info");
const uploadFormData = new FormData();
uploadFormData.append("proof", proofFile);
const uploadResponse = await ApiUtils.uploadFile(
"/api/upload/claim-proof",
uploadFormData
);
proofUrl = uploadResponse.data.url;
}
const payload = {
item_id: parseInt(state.selectedItem.id),
description: formData.get("description"),
contact: formData.get("contact"),
proof_url: proofUrl,
};
await ApiUtils.post(CONFIG.API_ENDPOINTS.CLAIMS, payload);
showToast("Klaim berhasil disubmit!", "success");
state.setShowClaimModal(false);
loadData();
} catch (error) {
showToast(error.message || "Gagal submit klaim", "error");
} finally {
setLoading(false);
}
};
const submitReportLost = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
const dateLost = formData.get("date_lost");
const dateLostISO = new Date(dateLost + "T00:00:00Z").toISOString();
const data = {
name: formData.get("name"),
category_id: parseInt(formData.get("category_id")),
color: formData.get("color") || "",
location: formData.get("location") || "",
description: formData.get("description"),
date_lost: dateLostISO,
};
await ApiUtils.post(CONFIG.API_ENDPOINTS.LOST_ITEMS, data);
showToast("Laporan kehilangan berhasil!", "success");
state.setShowReportLostModal(false);
loadData();
} catch (error) {
showToast("Gagal submit laporan: " + error.message, "error");
} finally {
setLoading(false);
}
};
const submitReportFound = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
let photoUrl = "";
const photoFile = formData.get("photo");
if (photoFile && photoFile.size > 0) {
showToast("Mengupload foto...", "info");
const uploadFormData = new FormData();
uploadFormData.append("image", photoFile);
const uploadData = await ApiUtils.uploadFile(
CONFIG.API_ENDPOINTS.UPLOAD,
uploadFormData
);
photoUrl = uploadData.data.url;
}
const dateFound = formData.get("date_found");
const dateFoundISO = new Date(dateFound + "T00:00:00Z").toISOString();
const payload = {
name: formData.get("name"),
category_id: parseInt(formData.get("category_id")),
photo_url: photoUrl,
location: formData.get("location"),
description: formData.get("description"),
secret_details: formData.get("secret_details"),
date_found: dateFoundISO,
reporter_name: formData.get("reporter_name"),
reporter_contact: formData.get("reporter_contact"),
};
if (
state.selectedLostItemForFound &&
state.foundReportMethod === "direct"
) {
payload.lost_item_id = state.selectedLostItemForFound.id;
payload.verification_flow = "direct";
payload.is_direct_to_owner = true;
}
await ApiUtils.post(CONFIG.API_ENDPOINTS.ITEMS, payload);
if (
state.foundReportMethod === "direct" &&
state.selectedLostItemForFound
) {
showToast("Laporan terkirim ke PEMILIK untuk disetujui!", "success");
} else {
showToast("Laporan terkirim ke MANAGER untuk verifikasi.", "success");
}
setShowReportFoundModal(false);
setPhotoPreview(null);
state.setSelectedLostItemForFound(null);
loadData();
} catch (error) {
showToast("Gagal mengirim laporan: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleOpenFoundOption = (lostItem) => {
console.log("Membuka opsi penemuan untuk:", lostItem.name);
state.setSelectedLostItemForFound(lostItem);
state.setShowFoundOptionModal(true);
};
const handleSelectFoundOption = (method) => {
console.log("Metode dipilih:", method);
state.setFoundReportMethod(method);
state.setShowFoundOptionModal(false);
if (method === "direct") {
handleDirectClaimFlow();
} else {
state.setShowReportFoundModal(true);
}
};
const handleDirectClaimFlow = () => {
if (!state.selectedLostItemForFound) {
showToast("Tidak ada lost item yang dipilih", "error");
return;
}
state.setShowDirectClaimModal(true);
};
const handlePhotoChange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setPhotoPreview(reader.result);
};
reader.readAsDataURL(file);
}
};
const handleUserCompleteCase = async (claimId) => {
const confirmMsg =
'🎉 Konfirmasi Penerimaan Barang\n\nApakah Anda sudah menerima barang fisik ini?\n\n✅ Jika Ya:\n- Status akan menjadi "Completed"\n- Kasus ditutup secara permanen\n- Tidak bisa dibatalkan\n\n⚠ Pastikan barang sudah di tangan Anda!';
if (!confirm(confirmMsg)) return;
try {
state.setLoading(true);
// FIXED: Gunakan /api/claims/:id/complete (bukan /api/user/claims/:id/complete)
await ApiUtils.post(`/api/claims/${claimId}/complete`, {});
showToast(
"🎉 Selamat! Barang Anda telah kembali. Kasus selesai.",
"success"
);
loadData();
} catch (error) {
console.error("Error completing case:", error);
showToast("Gagal menyelesaikan: " + error.message, "error");
} finally {
state.setLoading(false);
}
};
const handleDirectClaimSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
if (!state.selectedLostItemForFound) {
showToast("Tidak ada lost item yang dipilih", "error");
return;
}
try {
state.setLoading(true);
const payload = {
description: formData.get("description"),
contact: formData.get("contact"),
proof_url: formData.get("proof_url") || "",
};
await ApiUtils.post(
`/api/lost-items/${state.selectedLostItemForFound.id}/direct-claim`,
payload
);
showToast("✅ Klaim terkirim ke pemilik untuk persetujuan!", "success");
state.setShowDirectClaimModal(false);
state.setSelectedLostItemForFound(null);
loadData();
} catch (error) {
console.error("Direct claim error:", error);
showToast("Gagal mengirim klaim: " + error.message, "error");
} finally {
state.setLoading(false);
}
};
return {
showToast,
loadData,
handleLogout,
handleViewDetail,
handleClaim,
submitClaim,
submitReportLost,
submitReportFound,
handlePhotoChange,
handleSelectLostItemForClaim,
handleManualClaimEntry,
handleOpenFoundOption,
handleSelectFoundOption,
handleUserRespondClaim,
handleUserCompleteCase,
handleDirectClaimSubmit,
handleDirectClaimFlow,
handleOpenDirectClaimModal,
};
};
window.useUserHandlers = useUserHandlers;