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

1075 lines
32 KiB
JavaScript

// assets/js/pages/admin/useAdminHandlers.js - WITH ARCHIVES HANDLERS
const useAdminHandlers = (state) => {
const {
setToast,
setLoading,
setStats,
setUsers,
setTotalPages,
setTotalRecords,
setItems,
setSelectedItemDetail,
setShowItemDetailModal,
setSelectedUser,
setShowEditModal,
setShowCreateItemModal,
setShowEditItemModal,
setSelectedItemToEdit,
setPhotoPreview,
currentPage,
// Claims States
setClaims,
setSelectedClaim,
setShowClaimDetailModal,
setShowCreateClaimModal,
setShowEditClaimModal,
setSelectedClaimToEdit,
setCategories,
setSelectedCategory,
// ✅ Archives States
setArchives,
setSelectedArchive,
setShowArchiveDetailModal,
setAuditLogs,
setLostItems,
setSelectedLostItemDetail,
setShowLostItemDetailModal,
setShowCreateLostItemModal,
setShowEditLostItemModal,
setSelectedLostItemToEdit,
} = state;
const showToast = (message, type = "info") => {
setToast({ message, type });
};
const loadData = async () => {
try {
const [statsData, usersData] = await Promise.all([
ApiUtils.get(CONFIG.API_ENDPOINTS.ADMIN.DASHBOARD),
ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ADMIN.USERS}?page=${currentPage}&limit=${CONFIG.PAGINATION.LIMIT}`
),
]);
setStats(statsData.data || {});
setUsers(usersData.data || []);
if (usersData.pagination) {
setTotalPages(usersData.pagination.total_pages);
setTotalRecords(usersData.pagination.total_records);
}
} catch (error) {
showToast("Gagal memuat data", "error");
}
};
// NEW: Load Lost Items
const loadLostItems = async (page = 1) => {
try {
setLoading(true);
const {
lostItemSearchTerm,
lostItemStatusFilter,
lostItemCategoryFilter,
} = state;
const queryParams = new URLSearchParams({
page: page,
limit: 10,
search: lostItemSearchTerm || "",
status: lostItemStatusFilter || "",
category: lostItemCategoryFilter || "",
});
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.LOST_ITEMS}?${queryParams.toString()}`
);
setLostItems(response.data || []);
if (response.pagination) {
state.setLostItemPage(response.pagination.current_page);
state.setLostItemTotalPages(response.pagination.total_pages);
state.setLostItemTotalRecords(response.pagination.total_records);
}
state.setFilteredLostItems(response.data || []);
} catch (error) {
showToast("Gagal memuat data laporan barang hilang", "error");
console.error(error);
} finally {
setLoading(false);
}
};
// NEW: View Lost Item Detail
const handleViewLostItemDetail = async (lostItem) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.LOST_ITEMS}/${lostItem.id}`
);
setSelectedLostItemDetail(response.data || response);
setShowLostItemDetailModal(true);
} catch (error) {
showToast("Gagal memuat detail laporan", "error");
}
};
// NEW: Create Lost Item
const handleCreateLostItem = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses pembuatan laporan...", "info");
const dateLost = formData.get("date_lost");
const dateLostISO = new Date(dateLost + "T00:00:00Z").toISOString();
const lostItemData = {
user_id: parseInt(formData.get("user_id")),
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, lostItemData);
showToast("Laporan berhasil ditambahkan!", "success");
setShowCreateLostItemModal(false);
loadLostItems();
loadData();
} catch (error) {
showToast("Gagal menambahkan laporan: " + error.message, "error");
} finally {
setLoading(false);
}
};
// NEW: Edit Lost Item Click
const handleEditLostItemClick = async (lostItem) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.LOST_ITEMS}/${lostItem.id}`
);
setSelectedLostItemToEdit(response.data || response);
setShowEditLostItemModal(true);
} catch (error) {
showToast("Gagal memuat data laporan", "error");
}
};
// NEW: Update Lost Item
const handleUpdateLostItem = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses update laporan...", "info");
const dateLost = formData.get("date_lost");
const dateLostISO = new Date(dateLost + "T00:00:00Z").toISOString();
// ✅ PERBAIKAN: Pastikan category_id dikirim sebagai integer
const categoryId = parseInt(formData.get("category_id"));
// ✅ VALIDASI: Cek apakah category_id valid
if (!categoryId || isNaN(categoryId)) {
showToast("Error: Category harus dipilih!", "error");
setLoading(false);
return;
}
const lostItemData = {
name: formData.get("name"),
category_id: categoryId, // ✅ Gunakan integer, bukan string
color: formData.get("color") || "",
location: formData.get("location") || "",
description: formData.get("description"),
date_lost: dateLostISO,
status: formData.get("status"),
reason: formData.get("reason") || "Admin update",
};
// ✅ DEBUG LOG
console.log("📤 Data yang dikirim ke API:", lostItemData);
console.log("🎯 Lost Item ID:", state.selectedLostItemToEdit.id);
const response = await ApiUtils.put(
`${CONFIG.API_ENDPOINTS.LOST_ITEMS}/${state.selectedLostItemToEdit.id}`,
lostItemData
);
// ✅ DEBUG LOG response
console.log("✅ Response dari API:", response);
showToast("Laporan berhasil diupdate!", "success");
setShowEditLostItemModal(false);
setSelectedLostItemToEdit(null);
// Reload data
await loadLostItems();
await loadData();
} catch (error) {
console.error("❌ Update error:", error);
showToast("Gagal update laporan: " + error.message, "error");
} finally {
setLoading(false);
}
};
// NEW: Delete Lost Item
const handleDeleteLostItem = async (lostItemId) => {
try {
setLoading(true);
await ApiUtils.delete(`${CONFIG.API_ENDPOINTS.LOST_ITEMS}/${lostItemId}`);
showToast("Laporan berhasil dihapus!", "success");
loadLostItems();
loadData();
} catch (error) {
showToast("Gagal menghapus laporan: " + error.message, "error");
} finally {
setLoading(false);
}
};
// ✅ UPDATE: loadItems dengan Pagination & Filter Server-side
const loadItems = async (page = 1) => {
try {
setLoading(true);
// Ambil filter dari state saat ini
const { itemSearchTerm, itemStatusFilter, itemCategoryFilter } = state;
// Susun Query Params
const queryParams = new URLSearchParams({
page: page,
limit: 10, // ✅ Batasi 10 barang per halaman
search: itemSearchTerm || "",
status: itemStatusFilter || "",
category: itemCategoryFilter || "", // Backend butuh slug kategori
});
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}?${queryParams.toString()}`
);
// Set Data Items
setItems(response.data || []);
// Set Data Pagination Barang
if (response.pagination) {
state.setItemPage(response.pagination.current_page);
state.setItemTotalPages(response.pagination.total_pages);
state.setItemTotalRecords(response.pagination.total_records);
}
// Update filteredItems langsung dengan data yang diterima (karena filter sudah di server)
state.setFilteredItems(response.data || []);
} catch (error) {
showToast("Gagal memuat data barang", "error");
console.error(error);
} finally {
setLoading(false);
}
};
const loadClaims = async () => {
try {
setLoading(true);
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.CLAIMS}?page=1&limit=1000`
);
setClaims(response.data || []);
} catch (error) {
showToast("Gagal memuat data klaim", "error");
} finally {
setLoading(false);
}
};
const loadCategories = async () => {
try {
setLoading(true);
const response = await fetch(
`${CONFIG.API_URL}${CONFIG.API_ENDPOINTS.CATEGORIES}`,
{
method: "GET",
headers: {
Authorization: `Bearer ${AuthUtils.getToken()}`,
},
}
);
if (!response.ok) {
throw new Error("Gagal memuat kategori");
}
const result = await response.json();
// PERUBAHAN 1: Jangan pakai || CONFIG.CATEGORIES
// Jika data kosong, biarkan array kosong []
setCategories(result.data || []);
} catch (error) {
console.error("Load categories error:", error);
showToast("Gagal memuat kategori", "error");
// PERUBAHAN 2: Hapus fallback di sini
// Jangan paksa isi dengan data palsu/statis jika error
setCategories([]);
} finally {
setLoading(false);
}
};
// ✅ NEW: Create Category
const handleCreateCategory = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses pembuatan kategori...", "info");
const categoryData = {
name: formData.get("name"),
value: formData.get("value").toLowerCase().replace(/\s+/g, "_"),
description: formData.get("description") || "",
};
// ✅ PATH: POST /api/categories
const response = await fetch(
`${CONFIG.API_URL}${CONFIG.API_ENDPOINTS.CATEGORIES}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${AuthUtils.getToken()}`,
},
body: JSON.stringify(categoryData),
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Gagal menambahkan kategori");
}
await response.json();
showToast("Kategori berhasil ditambahkan!", "success");
state.setShowCreateCategoryModal(false);
loadCategories();
loadData();
} catch (error) {
console.error("Create category error:", error);
showToast("Gagal menambahkan kategori: " + error.message, "error");
} finally {
setLoading(false);
}
};
// ✅ FIXED: Update Category Handler
const handleUpdateCategory = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses update kategori...", "info");
const categoryData = {
name: formData.get("name"),
value: formData.get("value").toLowerCase().replace(/\s+/g, "_"),
description: formData.get("description") || "",
};
// ✅ FIX: Tambahkan selectedCategory.id di URL
// PATH: PUT /api/categories/:id
const response = await fetch(
`${CONFIG.API_URL}${CONFIG.API_ENDPOINTS.CATEGORIES}/${state.selectedCategory.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${AuthUtils.getToken()}`,
},
body: JSON.stringify(categoryData),
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Gagal update kategori");
}
await response.json();
showToast("Kategori berhasil diupdate!", "success");
state.setShowEditCategoryModal(false);
setSelectedCategory(null);
loadCategories();
loadData();
} catch (error) {
console.error("Update category error:", error);
showToast("Gagal update kategori: " + error.message, "error");
} finally {
setLoading(false);
}
};
const loadRolesAndPermissions = async () => {
try {
setLoading(true);
const [rolesData, permsData] = await Promise.all([
ApiUtils.get(CONFIG.API_ENDPOINTS.ROLES), // ✅ Benar (Sesuai config.js)
ApiUtils.get(CONFIG.API_ENDPOINTS.PERMISSIONS), // ✅ Benar (Sesuai config.js)
]);
state.setRoles(rolesData.data || []);
state.setPermissions(permsData.data || []);
} catch (error) {
showToast("Gagal memuat roles/permissions", "error");
} finally {
setLoading(false);
}
};
const handleCreateRole = async (formData) => {
try {
setLoading(true);
await ApiUtils.post(CONFIG.API_ENDPOINTS.ROLES, formData);
showToast("Role berhasil dibuat", "success");
state.setShowCreateRoleModal(false);
loadRolesAndPermissions();
} catch (error) {
showToast("Gagal membuat role: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleUpdateRole = async (formData) => {
try {
setLoading(true);
// GANTI DARI: CONFIG.API_ENDPOINTS.ADMIN.ROLES
// MENJADI: CONFIG.API_ENDPOINTS.ROLES
await ApiUtils.put(
`${CONFIG.API_ENDPOINTS.ROLES}/${state.selectedRole.id}`,
formData
);
showToast("Role berhasil diupdate", "success");
state.setShowEditRoleModal(false);
state.setSelectedRole(null);
loadRolesAndPermissions();
} catch (error) {
showToast("Gagal update role: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleDeleteRole = async (id) => {
if (!confirm("Yakin hapus role ini?")) return;
try {
// GANTI DARI: CONFIG.API_ENDPOINTS.ADMIN.ROLES
// MENJADI: CONFIG.API_ENDPOINTS.ROLES
await ApiUtils.delete(`${CONFIG.API_ENDPOINTS.ROLES}/${id}`);
showToast("Role dihapus", "success");
loadRolesAndPermissions();
} catch (error) {
showToast("Gagal hapus role: " + error.message, "error");
}
};
const handleEditRole = (role) => {
state.setSelectedRole(role);
state.setShowEditRoleModal(true);
};
// ✅ NEW: Delete Category
const handleDeleteCategory = async (categoryId) => {
if (!confirm("⚠️ Yakin ingin menghapus kategori ini?")) return;
try {
setLoading(true);
showToast("Menghapus kategori...", "info");
// ✅ FIX: Tambahkan categoryId di URL
// PATH: DELETE /api/categories/:id
const response = await fetch(
`${CONFIG.API_URL}${CONFIG.API_ENDPOINTS.CATEGORIES}/${categoryId}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${AuthUtils.getToken()}`,
},
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Gagal menghapus kategori");
}
await response.json();
showToast("Kategori berhasil dihapus!", "success");
loadCategories();
loadData();
} catch (error) {
console.error("Delete category error:", error);
showToast("Gagal menghapus kategori: " + error.message, "error");
} finally {
setLoading(false);
}
};
// ✅ NEW: Load Archives
const loadArchives = async () => {
try {
setLoading(true);
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}?status=case_closed&page=1&limit=1000`
);
setArchives(response.data || []);
} catch (error) {
showToast("Gagal memuat data arsip", "error");
} finally {
setLoading(false);
}
};
// Load Audit Logs
const loadAuditLogs = async (page = 1) => {
try {
setLoading(true);
// Ambil filter dari state
const { auditLogActionFilter, auditLogUserFilter } = state;
// Susun Query Params
const queryParams = new URLSearchParams({
page: page,
limit: 10, // ✅ Set limit 10 per halaman
action: auditLogActionFilter || "",
// Kirim filter lain jika backend mendukung (misal user_id atau search generic)
// search: state.auditLogSearchTerm || ""
});
// Jika ada filter user (asumsi backend menerima user_id atau username)
if (auditLogUserFilter) {
queryParams.append("user_id", auditLogUserFilter); // Sesuaikan dengan parameter backend
}
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ADMIN.AUDIT_LOGS}?${queryParams.toString()}`
);
// Set Data
setAuditLogs(response.data || []);
// ✅ Update State Pagination (Generic State di useAdminState)
if (response.pagination) {
setTotalPages(response.pagination.total_pages);
setTotalRecords(response.pagination.total_records);
state.setCurrentPage(response.pagination.current_page);
}
// Karena pagination sekarang di server, filteredAuditLogs sama dengan data yang diterima
state.setFilteredAuditLogs(response.data || []);
} catch (error) {
showToast("Gagal memuat audit log", "error");
console.error(error);
} finally {
setLoading(false);
}
};
// ✅ NEW: View Archive Detail
const handleViewArchiveDetail = async (archive) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}/${archive.id}`
);
setSelectedArchive(response.data || response);
setShowArchiveDetailModal(true);
} catch (error) {
showToast("Gagal memuat detail arsip", "error");
}
};
const handleCreateClaim = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses pembuatan klaim...", "info");
const claimData = {
item_id: parseInt(formData.get("item_id")),
user_id: parseInt(formData.get("user_id")),
description: formData.get("description"),
contact: formData.get("contact"),
admin_notes: formData.get("admin_notes") || "",
};
await ApiUtils.post(CONFIG.API_ENDPOINTS.CLAIMS, claimData);
showToast("Klaim berhasil ditambahkan!", "success");
setShowCreateClaimModal(false);
loadClaims();
loadData();
} catch (error) {
showToast("Gagal menambahkan klaim: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleEditClaimClick = async (claim) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.CLAIMS}/${claim.id}`
);
setSelectedClaimToEdit(response.data || response);
setShowEditClaimModal(true);
} catch (error) {
showToast("Gagal memuat data klaim", "error");
}
};
const handleUpdateClaim = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses update klaim...", "info");
const claimData = {
description: formData.get("description"),
contact: formData.get("contact"),
reason: formData.get("reason") || "Admin update",
};
await ApiUtils.patch(
`${CONFIG.API_ENDPOINTS.CLAIMS}/${state.selectedClaimToEdit.id}`,
claimData
);
showToast("Klaim berhasil diupdate!", "success");
setShowEditClaimModal(false);
setSelectedClaimToEdit(null);
loadClaims();
loadData();
} catch (error) {
showToast("Gagal update klaim: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleViewClaimDetail = async (claim) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.CLAIMS}/${claim.id}`
);
setSelectedClaim(response.data || response);
setShowClaimDetailModal(true);
} catch (error) {
showToast("Gagal memuat detail klaim", "error");
}
};
const handleDeleteClaim = async (claimId) => {
if (!confirm("⚠️ Yakin ingin menghapus klaim ini?")) return;
try {
setLoading(true);
await ApiUtils.delete(`${CONFIG.API_ENDPOINTS.CLAIMS}/${claimId}`);
showToast("Klaim berhasil dihapus!", "success");
loadClaims();
loadData();
} catch (error) {
showToast("Gagal menghapus klaim: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleViewItemDetail = async (item) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}/${item.id}`
);
setSelectedItemDetail(response.data || response);
setShowItemDetailModal(true);
} catch (error) {
showToast("Gagal memuat detail barang", "error");
}
};
const handleCreateItem = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses pembuatan barang...", "info");
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 itemData = {
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"),
};
await ApiUtils.post(CONFIG.API_ENDPOINTS.ITEMS, itemData);
showToast("Barang berhasil ditambahkan!", "success");
setShowCreateItemModal(false);
setPhotoPreview(null);
loadItems();
loadData();
} catch (error) {
showToast("Gagal menambahkan barang: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleEditItemClick = async (item) => {
try {
const response = await ApiUtils.get(
`${CONFIG.API_ENDPOINTS.ITEMS}/${item.id}`
);
setSelectedItemToEdit(response.data || response);
setShowEditItemModal(true);
} catch (error) {
showToast("Gagal memuat data barang", "error");
}
};
const handleUpdateItem = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
try {
setLoading(true);
showToast("Memproses update barang...", "info");
// 🔧 FIX 1: Upload foto baru jika ada
let photoUrl = state.selectedItemToEdit.photo_url; // Gunakan foto lama sebagai default
const photoFile = formData.get("photo");
if (photoFile && photoFile.size > 0) {
showToast("Mengupload foto baru...", "info");
const uploadFormData = new FormData();
uploadFormData.append("image", photoFile);
try {
const uploadData = await ApiUtils.uploadFile(
CONFIG.API_ENDPOINTS.UPLOAD,
uploadFormData
);
photoUrl = uploadData.data.url;
} catch (uploadError) {
console.error("Upload error:", uploadError);
showToast(
"Gagal upload foto, melanjutkan dengan foto lama",
"warning"
);
}
}
const dateFound = formData.get("date_found");
const dateFoundISO = new Date(dateFound + "T00:00:00Z").toISOString();
const categoryId = parseInt(formData.get("category_id"));
if (isNaN(categoryId)) {
showToast("Error: Category ID tidak valid!", "error");
return;
}
// 🔧 FIX 2: Selalu kirim photo_url
const itemData = {
name: formData.get("name"),
category_id: categoryId,
photo_url: photoUrl, // ✅ TAMBAHKAN INI
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"),
status: formData.get("status"),
reason: formData.get("reason") || "Admin update",
};
console.log("📤 Data yang akan dikirim:", itemData);
console.log("📤 Item yang sedang diedit:", state.selectedItemToEdit);
await ApiUtils.put(
`${CONFIG.API_ENDPOINTS.ITEMS}/${state.selectedItemToEdit.id}`,
itemData
);
showToast("Barang berhasil diupdate!", "success");
setShowEditItemModal(false);
setSelectedItemToEdit(null);
setPhotoPreview(null); // Reset preview
loadItems();
loadData();
} catch (error) {
console.error("❌ Update error:", error);
showToast("Gagal update barang: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handleDeleteItem = async (itemId) => {
if (!confirm("⚠️ Yakin ingin menghapus barang ini?")) return;
try {
setLoading(true);
await ApiUtils.delete(`${CONFIG.API_ENDPOINTS.ITEMS}/${itemId}`);
showToast("Barang berhasil dihapus!", "success");
loadItems();
loadData();
} catch (error) {
showToast("Gagal menghapus barang: " + error.message, "error");
} finally {
setLoading(false);
}
};
const handlePhotoChange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setPhotoPreview(reader.result);
};
reader.readAsDataURL(file);
}
};
const handleExport = async (format) => {
try {
setLoading(true);
const period =
document.querySelector('select[name="period"]')?.value || "month";
const reportType =
document.querySelector('select[name="report_type"]')?.value || "all";
const endDate = new Date();
const startDate = new Date();
switch (period) {
case "month":
startDate.setMonth(startDate.getMonth() - 1);
break;
case "semester":
startDate.setMonth(startDate.getMonth() - 6);
break;
case "year":
startDate.setFullYear(startDate.getFullYear() - 1);
break;
}
const requestBody = {
type: reportType,
format: format,
start_date: startDate.toISOString(),
end_date: endDate.toISOString(),
};
showToast(`Generating ${format.toUpperCase()} report...`, "info");
const response = await fetch(
`${CONFIG.API_URL}${CONFIG.API_ENDPOINTS.ADMIN.EXPORT}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${AuthUtils.getToken()}`,
},
body: JSON.stringify(requestBody),
}
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Export failed: ${errorText}`);
}
const contentDisposition = response.headers.get("Content-Disposition");
let filename = `report_${Date.now()}.${
format === "pdf" ? "pdf" : "xlsx"
}`;
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename="?(.+)"?/);
if (filenameMatch) {
filename = filenameMatch[1];
}
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
showToast(`${format.toUpperCase()} exported successfully!`, "success");
} catch (error) {
showToast(`Failed to export: ${error.message}`, "error");
} finally {
setLoading(false);
}
};
const handleLogout = () => {
if (Helpers.showConfirm("Apakah Anda yakin ingin logout?")) {
AuthUtils.clearAuth();
window.location.href = "/login";
}
};
const handleEditUser = (userData) => {
setSelectedUser(userData);
setShowEditModal(true);
};
const handleUpdateUser = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const role = formData.get("role");
const roleMapping = {
user: 3,
manager: 2,
admin: 1,
};
const role_id = roleMapping[role];
try {
setLoading(true);
await ApiUtils.patch(
`${CONFIG.API_ENDPOINTS.ADMIN.USERS}/${state.selectedUser.id}/role`,
{ role_id }
);
showToast("User berhasil diupdate!", "success");
setShowEditModal(false);
loadData();
} catch (error) {
showToast(`Gagal update user: ${error.message}`, "error");
} finally {
setLoading(false);
}
};
const handleBlockUser = async (userId) => {
if (!Helpers.showConfirm("Block user ini?")) return;
try {
await ApiUtils.post(
`${CONFIG.API_ENDPOINTS.ADMIN.USERS}/${userId}/block`,
{}
);
showToast("User berhasil diblock!", "success");
loadData();
} catch (error) {
showToast("Gagal block user", "error");
}
};
const handleUnblockUser = async (userId) => {
if (!Helpers.showConfirm("Unblock user ini?")) return;
try {
await ApiUtils.post(
`${CONFIG.API_ENDPOINTS.ADMIN.USERS}/${userId}/unblock`,
{}
);
showToast("User berhasil di-unblock!", "success");
loadData();
} catch (error) {
showToast("Gagal unblock user", "error");
}
};
return {
showToast,
loadData,
loadItems,
handleViewItemDetail,
handleExport,
handleLogout,
handleEditUser,
handleUpdateUser,
handleBlockUser,
handleUnblockUser,
handleCreateItem,
handleEditItemClick,
handleUpdateItem,
handleDeleteItem,
handlePhotoChange,
// Claims Handlers
loadClaims,
handleViewClaimDetail,
handleDeleteClaim,
handleCreateClaim,
handleEditClaimClick,
handleUpdateClaim,
loadCategories,
handleCreateCategory,
handleUpdateCategory,
handleDeleteCategory,
// ✅ NEW: Archives Handlers
loadArchives,
handleViewArchiveDetail,
loadAuditLogs,
loadRolesAndPermissions,
handleCreateRole,
handleUpdateRole,
handleDeleteRole,
handleEditRole,
loadLostItems,
handleViewLostItemDetail,
handleCreateLostItem,
handleEditLostItemClick,
handleUpdateLostItem,
handleDeleteLostItem,
};
};