document.addEventListener('DOMContentLoaded', function() { // Initialize inventory management initInventory(); // Event listeners document.getElementById('productSearch').addEventListener('input', filterProducts); document.getElementById('categoryFilter').addEventListener('change', filterProducts); document.getElementById('stockFilter').addEventListener('change', filterProducts); document.getElementById('addProductBtn').addEventListener('click', showAddProductModal); document.getElementById('manageCategories').addEventListener('click', showCategoryModal); document.getElementById('saveProduct').addEventListener('click', saveProduct); document.getElementById('cancelProduct').addEventListener('click', hideProductModal); document.getElementById('saveCategory').addEventListener('click', saveCategory); document.getElementById('cancelCategory').addEventListener('click', resetCategoryForm); document.getElementById('saveAdjustment').addEventListener('click', saveStockAdjustment); document.getElementById('cancelAdjustment').addEventListener('click', hideStockModal); document.getElementById('exportInventory').addEventListener('click', exportInventory); // Close modals when clicking on X document.querySelectorAll('.close-modal').forEach(button => { button.addEventListener('click', function() { this.closest('.modal').classList.remove('show'); }); }); }); // Global variables let products = []; let categories = []; // Initialize inventory management async function initInventory() { try { // Fetch categories const categoryResponse = await apiRequest('inventory/categories'); if (categoryResponse.status === 'success') { categories = categoryResponse.data; renderCategoryDropdowns(); } // Fetch products const productResponse = await apiRequest('inventory/products'); if (productResponse.status === 'success') { products = productResponse.data; renderProducts(products); } } catch (error) { console.error('Failed to initialize inventory:', error); showNotification('Error loading inventory data', 'error'); } } // Render category dropdowns function renderCategoryDropdowns() { const categoryFilter = document.getElementById('categoryFilter'); const categorySelect = document.getElementById('categoryId'); // Clear existing options (except the first one) while (categoryFilter.options.length > 1) { categoryFilter.remove(1); } while (categorySelect.options.length > 1) { categorySelect.remove(1); } // Add categories to dropdowns categories.forEach(category => { if (category.status === 'active') { const filterOption = document.createElement('option'); filterOption.value = category.id; filterOption.textContent = category.name; categoryFilter.appendChild(filterOption); const selectOption = document.createElement('option'); selectOption.value = category.id; selectOption.textContent = category.name; categorySelect.appendChild(selectOption); } }); } // Render products table function renderProducts(productsToRender) { const tableBody = document.querySelector('#inventoryTable tbody'); tableBody.innerHTML = ''; if (productsToRender.length === 0) { const row = document.createElement('tr'); row.innerHTML = 'No products found'; tableBody.appendChild(row); return; } productsToRender.forEach(product => { const row = document.createElement('tr'); // Determine stock status let stockClass = ''; if (product.quantity <= 0) { stockClass = 'text-danger'; } else if (product.quantity <= product.low_stock_threshold) { stockClass = 'text-warning'; } row.innerHTML = ` ${product.sku} ${product.name} ${product.category_name || 'Uncategorized'} ${formatCurrency(product.price)} ${formatCurrency(product.cost)} ${product.quantity} ${product.status} `; tableBody.appendChild(row); }); // Add event listeners for action buttons document.querySelectorAll('.edit-product').forEach(button => { button.addEventListener('click', function() { const productId = this.getAttribute('data-id'); if (productId) { editProduct(productId); } else { showNotification('Product ID is missing', 'error'); } }); }); document.querySelectorAll('.adjust-stock').forEach(button => { button.addEventListener('click', function() { const productId = this.getAttribute('data-id'); const productName = this.getAttribute('data-name'); const currentStock = this.getAttribute('data-stock'); showStockModal(productId, productName, currentStock); }); }); document.querySelectorAll('.delete-product').forEach(button => { button.addEventListener('click', function() { const productId = this.getAttribute('data-id'); deleteProduct(productId); }); }); } // Filter products function filterProducts() { const searchTerm = document.getElementById('productSearch').value.toLowerCase(); const categoryId = document.getElementById('categoryFilter').value; const stockFilter = document.getElementById('stockFilter').value; let filtered = [...products]; // Apply search filter if (searchTerm) { filtered = filtered.filter(product => { return product.name.toLowerCase().includes(searchTerm) || product.sku.toLowerCase().includes(searchTerm) || (product.barcode && product.barcode.toLowerCase().includes(searchTerm)); }); } // Apply category filter if (categoryId) { filtered = filtered.filter(product => product.category_id === categoryId); } // Apply stock filter if (stockFilter !== 'all') { if (stockFilter === 'low') { filtered = filtered.filter(product => product.quantity <= product.low_stock_threshold && product.quantity > 0 ); } else if (stockFilter === 'out') { filtered = filtered.filter(product => product.quantity <= 0); } } renderProducts(filtered); } // Show add product modal function showAddProductModal() { // Reset form document.getElementById('productForm').reset(); document.getElementById('productId').value = ''; document.getElementById('productModalTitle').textContent = 'Add Product'; document.getElementById('status').value = 'active'; // Show modal document.getElementById('productModal').classList.add('show'); } // Show edit product modal async function editProduct(productId) { try { if (!productId) { showNotification('Product ID is required', 'error'); return; } console.log("Editing product with ID:", productId); // Debug line const response = await apiRequest(`inventory/product?id=${productId}`); if (response.status === 'success') { const product = response.data; console.log("Product data received:", product); // Debug line // Fill form fields document.getElementById('productId').value = product.id; document.getElementById('sku').value = product.sku; document.getElementById('barcode').value = product.barcode || ''; document.getElementById('name').value = product.name; document.getElementById('description').value = product.description || ''; document.getElementById('categoryId').value = product.category_id || ''; document.getElementById('price').value = product.price; document.getElementById('cost').value = product.cost; document.getElementById('quantity').value = product.quantity; document.getElementById('lowStockThreshold').value = product.low_stock_threshold; document.getElementById('status').value = product.status; // Update modal title document.getElementById('productModalTitle').textContent = 'Edit Product'; // Show modal document.getElementById('productModal').classList.add('show'); } else { showNotification(response.message || 'Failed to load product data', 'error'); } } catch (error) { console.error('Error fetching product:', error); showNotification('Error loading product data', 'error'); } } // Hide product modal function hideProductModal() { document.getElementById('productModal').classList.remove('show'); } // Save product (create or update) async function saveProduct() { try { const productId = document.getElementById('productId').value; // Gather form data const productData = { sku: document.getElementById('sku').value, barcode: document.getElementById('barcode').value, name: document.getElementById('name').value, description: document.getElementById('description').value, category_id: document.getElementById('categoryId').value, price: parseFloat(document.getElementById('price').value), cost: parseFloat(document.getElementById('cost').value), quantity: parseInt(document.getElementById('quantity').value), low_stock_threshold: parseInt(document.getElementById('lowStockThreshold').value), status: document.getElementById('status').value }; let response; if (productId) { // Update existing product response = await apiRequest(`inventory/product?id=${productId}`, 'PUT', productData); } else { // Create new product response = await apiRequest('inventory/products', 'POST', productData); } if (response.status === 'success') { showNotification(productId ? 'Product updated successfully' : 'Product created successfully', 'success'); hideProductModal(); // Refresh product list const productResponse = await apiRequest('inventory/products'); if (productResponse.status === 'success') { products = productResponse.data; renderProducts(products); } } else { showNotification(response.message, 'error'); } } catch (error) { console.error('Error saving product:', error); showNotification('Error saving product data', 'error'); } } // Delete product async function deleteProduct(productId) { if (confirm('Are you sure you want to delete this product?')) { try { const response = await apiRequest(`inventory/product?id=${productId}`, 'DELETE'); if (response.status === 'success') { showNotification('Product deleted successfully', 'success'); // Refresh product list const productResponse = await apiRequest('inventory/products'); if (productResponse.status === 'success') { products = productResponse.data; renderProducts(products); } } else { showNotification(response.message, 'error'); } } catch (error) { console.error('Error deleting product:', error); showNotification('Error deleting product', 'error'); } } } // Show stock adjustment modal function showStockModal(productId, productName, currentStock) { document.getElementById('stockProductId').value = productId; document.getElementById('stockProductName').textContent = productName; document.getElementById('currentStock').textContent = currentStock; document.getElementById('adjustmentType').value = 'add'; document.getElementById('adjustmentQuantity').value = ''; document.getElementById('adjustmentNotes').value = ''; document.getElementById('stockModal').classList.add('show'); } // Hide stock adjustment modal function hideStockModal() { document.getElementById('stockModal').classList.remove('show'); } // Save stock adjustment async function saveStockAdjustment() { try { const productId = document.getElementById('stockProductId').value; const adjustmentType = document.getElementById('adjustmentType').value; const quantityInput = parseInt(document.getElementById('adjustmentQuantity').value); const notes = document.getElementById('adjustmentNotes').value; if (isNaN(quantityInput) || quantityInput < 0) { showNotification('Please enter a valid quantity', 'error'); return; } let quantity, type; const currentStock = parseInt(document.getElementById('currentStock').textContent); switch (adjustmentType) { case 'add': quantity = currentStock + quantityInput; type = 'adjustment'; break; case 'subtract': if (currentStock < quantityInput) { showNotification('Cannot subtract more than current stock', 'error'); return; } quantity = currentStock - quantityInput; type = 'adjustment'; break; case 'set': quantity = quantityInput; type = 'adjustment'; break; } const adjustmentData = { product_id: productId, type: type, quantity: quantity, notes: `${adjustmentType} stock: ${quantityInput} - ${notes}` }; const response = await apiRequest('inventory/transactions', 'POST', adjustmentData); if (response.status === 'success') { showNotification('Stock adjustment saved successfully', 'success'); hideStockModal(); // Refresh product list const productResponse = await apiRequest('inventory/products'); if (productResponse.status === 'success') { products = productResponse.data; renderProducts(products); } } else { showNotification(response.message, 'error'); } } catch (error) { console.error('Error adjusting stock:', error); showNotification('Error saving stock adjustment', 'error'); } } // Show category management modal function showCategoryModal() { document.getElementById('categoryForm').reset(); document.getElementById('categoryId').value = ''; document.getElementById('categoryModalTitle').textContent = 'Manage Categories'; renderCategoryList(); document.getElementById('categoryModal').classList.add('show'); } // Render category list in modal function renderCategoryList() { const categoryList = document.getElementById('categoryList'); categoryList.innerHTML = ''; categories.forEach(category => { const categoryItem = document.createElement('div'); categoryItem.className = 'category-item'; categoryItem.innerHTML = `
${category.name}
`; categoryList.appendChild(categoryItem); }); // Add event listeners for category actions document.querySelectorAll('.edit-category').forEach(button => { button.addEventListener('click', function() { const categoryId = this.dataset.id; editCategory(categoryId); }); }); document.querySelectorAll('.delete-category').forEach(button => { button.addEventListener('click', function() { const categoryId = this.dataset.id; deleteCategory(categoryId); }); }); } // Edit category function editCategory(categoryId) { const category = categories.find(c => c.id === categoryId); if (category) { document.getElementById('categoryId').value = category.id; document.getElementById('categoryName').value = category.name; document.getElementById('categoryDescription').value = category.description || ''; } } // Reset category form function resetCategoryForm() { document.getElementById('categoryForm').reset(); document.getElementById('categoryId').value = ''; } // Save category (create or update) async function saveCategory() { try { const categoryId = document.getElementById('categoryId').value; const categoryName = document.getElementById('categoryName').value; const categoryDescription = document.getElementById('categoryDescription').value; if (!categoryName) { showNotification('Category name is required', 'error'); return; } const categoryData = { name: categoryName, description: categoryDescription }; let response; if (categoryId) { // Update existing category response = await apiRequest(`inventory/category?id=${categoryId}`, 'PUT', categoryData); } else { // Create new category response = await apiRequest('inventory/categories', 'POST', categoryData); } if (response.status === 'success') { showNotification(categoryId ? 'Category updated successfully' : 'Category created successfully', 'success'); resetCategoryForm(); // Refresh category list const categoryResponse = await apiRequest('inventory/categories'); if (categoryResponse.status === 'success') { categories = categoryResponse.data; renderCategoryList(); renderCategoryDropdowns(); } } else { showNotification(response.message, 'error'); } } catch (error) { console.error('Error saving category:', error); showNotification('Error saving category', 'error'); } } // Delete category async function deleteCategory(categoryId) { if (confirm('Are you sure you want to delete this category?')) { try { const response = await apiRequest(`inventory/category?id=${categoryId}`, 'DELETE'); if (response.status === 'success') { showNotification('Category deleted successfully', 'success'); // Refresh category list const categoryResponse = await apiRequest('inventory/categories'); if (categoryResponse.status === 'success') { categories = categoryResponse.data; renderCategoryList(); renderCategoryDropdowns(); } } else { showNotification(response.message, 'error'); } } catch (error) { console.error('Error deleting category:', error); showNotification('Error deleting category', 'error'); } } } // Export inventory to CSV function exportInventory() { // Get filtered products const searchTerm = document.getElementById('productSearch').value.toLowerCase(); const categoryId = document.getElementById('categoryFilter').value; const stockFilter = document.getElementById('stockFilter').value; let filtered = [...products]; // Apply filters if (searchTerm) { filtered = filtered.filter(product => { return product.name.toLowerCase().includes(searchTerm) || product.sku.toLowerCase().includes(searchTerm) || (product.barcode && product.barcode.toLowerCase().includes(searchTerm)); }); } if (categoryId) { filtered = filtered.filter(product => product.category_id === categoryId); } if (stockFilter !== 'all') { if (stockFilter === 'low') { filtered = filtered.filter(product => product.quantity <= product.low_stock_threshold && product.quantity > 0 ); } else if (stockFilter === 'out') { filtered = filtered.filter(product => product.quantity <= 0); } } // Create CSV content let csvContent = 'SKU,Name,Category,Price,Cost,Stock,Status\n'; filtered.forEach(product => { const row = [ product.sku, `"${product.name.replace(/"/g, '""')}"`, `"${(product.category_name || 'Uncategorized').replace(/"/g, '""')}"`, product.price, product.cost, product.quantity, product.status ]; csvContent += row.join(',') + '\n'; }); // Create download link const blob = new Blob([csvContent], {type: 'text/csv;charset=utf-8;'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', `inventory_export_${new Date().toISOString().split('T')[0]}.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // Show notification function showNotification(message, type = 'info') { // You can implement a notification system here // For simplicity, we'll use alert for now alert(message); }