document.addEventListener('DOMContentLoaded', function () { // --- Constants --- const API_BASE = 'api/'; const addAssetForm = document.getElementById('addAssetForm'); const editAssetForm = document.getElementById('editAssetForm'); const assetsTableBody = document.getElementById('assets-table-body'); const loadingIndicator = document.getElementById('loading-indicator'); const notificationToastEl = document.getElementById('notificationToast'); const notificationToast = new bootstrap.Toast(notificationToastEl); const editAssetModalEl = document.getElementById('editAssetModal'); const editAssetModal = new bootstrap.Modal(editAssetModalEl); // --- Functions --- /** * Shows a toast notification. * @param {string} title - The title of the toast. * @param {string} body - The body message of the toast. * @param {string} type - 'success' or 'error'. */ function showToast(title, body, type = 'success') { const toastHeader = notificationToastEl.querySelector('.toast-header'); toastHeader.classList.remove('bg-success', 'text-white', 'bg-danger', 'text-white'); if (type === 'success') { toastHeader.classList.add('bg-success', 'text-white'); } else if (type === 'error') { toastHeader.classList.add('bg-danger', 'text-white'); } document.getElementById('toast-title').textContent = title; document.getElementById('toast-body').textContent = body; notificationToast.show(); } /** * Fetches data from the API. * @param {string} endpoint - The API endpoint to fetch from. * @param {object} options - Optional fetch options. * @returns {Promise} - The JSON response. */ async function fetchApi(endpoint, options = {}) { try { const response = await fetch(API_BASE + endpoint, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: 'Gagal memuat detail error.' })); throw new Error(errorData.message || `HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error(`Error fetching ${endpoint}:`, error); showToast('Error', `Terjadi kesalahan: ${error.message}`, 'error'); return null; } } /** * Populates select dropdowns for both add and edit modals. */ async function populateSelectOptions() { const data = await fetchApi('get_options.php'); if (!data) return; const selects = [ { el: document.getElementById('id_kategori'), data: data.kategori, placeholder: 'Pilih Kategori...', name: 'nama_kategori' }, { el: document.getElementById('id_kantor_lokasi'), data: data.kantor, placeholder: 'Pilih Lokasi...', name: 'nama_kantor' }, { el: document.getElementById('edit_id_kategori'), data: data.kategori, placeholder: 'Pilih Kategori...', name: 'nama_kategori' }, { el: document.getElementById('edit_id_kantor_lokasi'), data: data.kantor, placeholder: 'Pilih Lokasi...', name: 'nama_kantor' } ]; selects.forEach(s => { if (s.el) { s.el.innerHTML = ``; s.data.forEach(item => { s.el.innerHTML += ``; }); } }); } /** * Fetches assets and renders them in the table. */ async function loadAssets() { loadingIndicator.style.display = 'block'; assetsTableBody.innerHTML = ''; const assets = await fetchApi('get_assets.php'); loadingIndicator.style.display = 'none'; if (assets && assets.length > 0) { assets.forEach(asset => { const row = ` ${asset.kode_aset} ${asset.nama_aset} ${asset.nama_kategori} ${asset.nama_kantor} ${asset.status} `; assetsTableBody.innerHTML += row; }); } else if (assets) { assetsTableBody.innerHTML = 'Belum ada data aset.'; } else { assetsTableBody.innerHTML = 'Gagal memuat data aset.'; } feather.replace(); } /** * Handles the submission of the 'Add Asset' form. */ async function handleAddAssetSubmit(event) { event.preventDefault(); event.stopPropagation(); if (!addAssetForm.checkValidity()) { addAssetForm.classList.add('was-validated'); return; } const formData = new FormData(addAssetForm); const submitButton = addAssetForm.querySelector('button[type="submit"]'); submitButton.disabled = true; submitButton.innerHTML = ''' Menyimpan...'''; const result = await fetchApi('add_asset.php', { method: 'POST', body: formData }); if (result && result.success) { showToast('Sukses', 'Aset baru berhasil ditambahkan.'); addAssetForm.reset(); addAssetForm.classList.remove('was-validated'); bootstrap.Modal.getInstance(document.getElementById('addAssetModal')).hide(); loadAssets(); } else if (result) { showToast('Error', result.message, 'error'); } submitButton.disabled = false; submitButton.innerHTML = 'Simpan Aset'; } /** * Opens the edit modal and populates it with asset data. * @param {number} assetId - The ID of the asset to edit. */ async function openEditModal(assetId) { const asset = await fetchApi(`get_asset.php?id=${assetId}`); if (!asset) return; document.getElementById('edit_id').value = asset.id; document.getElementById('edit_nama_aset').value = asset.nama_aset; document.getElementById('edit_id_kategori').value = asset.id_kategori; document.getElementById('edit_id_kantor_lokasi').value = asset.id_kantor_lokasi; document.getElementById('edit_spesifikasi').value = asset.spesifikasi; document.getElementById('edit_tanggal_pembelian').value = asset.tanggal_pembelian; document.getElementById('edit_harga_pembelian').value = asset.harga_pembelian; document.getElementById('edit_vendor').value = asset.vendor; document.getElementById('edit_status').value = asset.status; editAssetModal.show(); } /** * Handles the submission of the 'Edit Asset' form. */ async function handleEditAssetSubmit(event) { event.preventDefault(); event.stopPropagation(); if (!editAssetForm.checkValidity()) { editAssetForm.classList.add('was-validated'); return; } const formData = new FormData(editAssetForm); const submitButton = editAssetForm.querySelector('button[type="submit"]'); submitButton.disabled = true; submitButton.innerHTML = ''' Menyimpan...'''; const result = await fetchApi('update_asset.php', { method: 'POST', body: formData }); if (result && result.success) { showToast('Sukses', 'Aset berhasil diperbarui.'); editAssetModal.hide(); loadAssets(); } else if (result) { showToast('Error', result.message, 'error'); } submitButton.disabled = false; submitButton.innerHTML = 'Simpan Perubahan'; } /** * Handles the deletion of an asset. * @param {number} assetId - The ID of the asset to delete. */ async function handleDeleteAsset(assetId) { if (!confirm('Anda yakin ingin menghapus aset ini? Tindakan ini tidak dapat dibatalkan.')) { return; } const result = await fetchApi('delete_asset.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: assetId }) }); if (result && result.success) { showToast('Sukses', 'Aset berhasil dihapus.'); loadAssets(); } else if (result) { showToast('Error', result.message, 'error'); } } // --- Initializations --- feather.replace(); populateSelectOptions(); loadAssets(); // --- Event Listeners --- addAssetForm.addEventListener('submit', handleAddAssetSubmit); editAssetForm.addEventListener('submit', handleEditAssetSubmit); assetsTableBody.addEventListener('click', function(event) { const editButton = event.target.closest('.btn-edit'); const deleteButton = event.target.closest('.btn-delete'); if (editButton) { const assetId = editButton.dataset.assetId; openEditModal(assetId); } if (deleteButton) { const assetId = deleteButton.dataset.assetId; handleDeleteAsset(assetId); } }); // Add feather icon class for easier styling document.querySelectorAll('i[data-feather]').forEach(el => { if(el.classList.contains('feather-sm')) return; const iconName = el.getAttribute('data-feather'); el.classList.add(`icon-${iconName}`); }); });