Spaces:
Running
Running
| // events.js - Manages all event listeners | |
| import { appState } from './state.js'; | |
| import { handleUpdateStock, handleRestock, handleSetStock } from './services.js'; | |
| import { refreshUI, showToast, renderReport, renderCustomPOModal } from './ui.js'; | |
| import { generatePurchaseOrder } from './purchaseOrderService.js'; | |
| import { supabase } from './supabaseClient.js'; | |
| export function attachAllListeners() { | |
| attachProductInputListeners(); | |
| attachCurrentStockEditListeners(); | |
| attachRestockListeners(); | |
| attachPurchaseOrderListener(); | |
| } | |
| export function attachOneTimeListeners() { | |
| attachAuthListeners(); | |
| attachModalListeners(); | |
| } | |
| function attachProductInputListeners() { | |
| document.querySelectorAll('.update-btn').forEach(btn => { | |
| if (btn.dataset.listenerAttached) return; | |
| btn.dataset.listenerAttached = 'true'; | |
| btn.addEventListener('click', (e) => { | |
| const card = e.target.closest('.dashboard-card'); | |
| const productName = e.target.dataset.productName; | |
| const quantity = parseInt(card.querySelector('input').value, 10); | |
| handleUpdateStock(productName, quantity); | |
| }); | |
| }); | |
| } | |
| function attachCurrentStockEditListeners() { | |
| document.querySelectorAll('.edit-current-stock').forEach(icon => { | |
| if (icon.dataset.listenerAttached) return; | |
| icon.dataset.listenerAttached = 'true'; | |
| icon.addEventListener('click', (e) => { | |
| const card = e.target.closest('.dashboard-card'); | |
| const valueDiv = card.querySelector('.current-stock-value'); | |
| const materialName = card.dataset.materialName; | |
| const input = document.createElement('input'); | |
| input.type = 'number'; | |
| input.value = valueDiv.textContent; | |
| input.className = 'input-field w-24 text-2xl font-bold'; | |
| valueDiv.replaceWith(input); | |
| input.focus(); | |
| input.select(); | |
| const saveChange = () => { | |
| const newValue = parseInt(input.value, 10); | |
| handleSetStock(materialName, newValue); | |
| }; | |
| input.addEventListener('blur', saveChange); | |
| input.addEventListener('keydown', (event) => { if (event.key === 'Enter') input.blur(); }); | |
| }); | |
| }); | |
| } | |
| function attachRestockListeners() { | |
| document.querySelectorAll('.restock-icon').forEach(btn => { | |
| if (btn.dataset.listenerAttached) return; | |
| btn.dataset.listenerAttached = 'true'; | |
| btn.addEventListener('click', (e) => { | |
| const parentEl = e.target.closest('[data-material-name]'); | |
| const materialName = parentEl.dataset.materialName; | |
| const formContainer = parentEl.querySelector('.restock-form'); | |
| if (formContainer.classList.contains('hidden')) { | |
| document.querySelectorAll('.restock-form').forEach(f => { f.classList.add('hidden'); f.innerHTML = ''; }); | |
| formContainer.classList.remove('hidden'); | |
| formContainer.innerHTML = `<div class="flex items-center gap-2"><input type="number" placeholder="Qty" class="input-field w-20"><button class="btn btn-primary text-xs confirm-restock-btn">Add</button></div>`; | |
| const input = formContainer.querySelector('input'); | |
| input.focus(); | |
| formContainer.querySelector('.confirm-restock-btn').addEventListener('click', () => { handleRestock(materialName, parseInt(input.value, 10)); }); | |
| input.addEventListener('keydown', (event) => { if (event.key === 'Enter') handleRestock(materialName, parseInt(input.value, 10)); if (event.key === 'Escape') formContainer.classList.add('hidden'); }); | |
| } else { | |
| formContainer.classList.add('hidden'); | |
| formContainer.innerHTML = ''; | |
| } | |
| }); | |
| }); | |
| } | |
| function attachPurchaseOrderListener() { | |
| // This listener is now attached dynamically in renderReorderList in ui.js | |
| // We can use event delegation on the header to make it more robust. | |
| const reorderHeader = document.getElementById('reorder-header'); | |
| if (reorderHeader && !reorderHeader.dataset.listenerAttached) { | |
| reorderHeader.dataset.listenerAttached = 'true'; | |
| reorderHeader.addEventListener('click', (e) => { | |
| if (e.target.id === 'open-po-modal-btn') { | |
| const materialsToOrder = appState.materials.filter(m => m.currentStock <= m.reorderPoint * 1.5); | |
| if (materialsToOrder.length > 0) { | |
| renderCustomPOModal(materialsToOrder); | |
| } else { | |
| showToast('No items need reordering.', 'info'); | |
| } | |
| } | |
| }); | |
| } | |
| } | |
| function attachAuthListeners() { | |
| const loginForm = document.getElementById('login-form'); | |
| const logoutBtn = document.getElementById('logout-btn'); | |
| if (loginForm) { | |
| loginForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const email = document.getElementById('email').value; | |
| const password = document.getElementById('password').value; | |
| const { error } = await supabase.auth.signInWithPassword({ email, password }); | |
| if (error) { | |
| showToast(`Login failed: ${error.message}`, 'error'); | |
| } | |
| // onAuthStateChange in main.js will handle success | |
| }); | |
| } | |
| if (logoutBtn) { | |
| logoutBtn.addEventListener('click', async () => { | |
| await supabase.auth.signOut(); | |
| // onAuthStateChange in main.js will handle UI changes | |
| }); | |
| } | |
| } | |
| function attachModalListeners() { | |
| const resetModal = document.getElementById('reset-modal'); | |
| const reportsModal = document.getElementById('reports-modal'); | |
| const customPOModal = document.getElementById('custom-po-modal'); | |
| document.getElementById('show-reset-modal-btn')?.addEventListener('click', () => resetModal.classList.remove('hidden')); | |
| resetModal.addEventListener('click', async (e) => { | |
| const target = e.target.closest('button'); | |
| if ((target && target.id === 'cancel-reset-btn') || e.target === resetModal) { | |
| resetModal.classList.add('hidden'); | |
| } | |
| if (target && target.id === 'confirm-reset-btn') { | |
| showToast('Resetting data... please wait.', 'info'); | |
| await supabase.from('production_log').delete().neq('id', '00000000-0000-0000-0000-000000000000'); | |
| await supabase.from('materials').delete().neq('id', '00000000-0000-0000-0000-000000000000'); | |
| window.location.reload(); | |
| } | |
| }); | |
| document.getElementById('show-reports-modal-btn')?.addEventListener('click', () => reportsModal.classList.remove('hidden')); | |
| reportsModal.addEventListener('click', (e) => { | |
| const target = e.target.closest('button'); | |
| if ((target && target.id === 'close-reports-modal-btn') || e.target === reportsModal) { | |
| reportsModal.classList.add('hidden'); | |
| } | |
| if (target && target.id === 'report-prod-summary') renderReport('production'); | |
| if (target && target.id === 'report-mat-usage') renderReport('material'); | |
| }); | |
| customPOModal.addEventListener('click', e => { | |
| const target = e.target; | |
| if (target.id === 'custom-po-modal' || target.closest('#cancel-po-btn') || target.id === 'cancel-po-btn-footer') { | |
| customPOModal.classList.add('hidden'); | |
| } | |
| if (target.id === 'confirm-po-generation-btn') { | |
| const supplierName = document.getElementById('supplier-name').value.trim(); | |
| const selectedItems = []; | |
| document.querySelectorAll('.po-item-select:checked').forEach(checkbox => { | |
| const row = checkbox.closest('.po-item-row'); | |
| const materialName = row.dataset.materialName; | |
| const material = appState.materials.find(m => m.name === materialName); | |
| const quantity = parseInt(row.querySelector('.po-item-qty').value, 10); | |
| if (material && !isNaN(quantity) && quantity > 0) { | |
| selectedItems.push({ material, quantity }); | |
| } | |
| }); | |
| if (selectedItems.length > 0) { | |
| generatePurchaseOrder(selectedItems, supplierName); | |
| showToast(`Generated PO for ${selectedItems.length} items.`, 'success'); | |
| customPOModal.classList.add('hidden'); | |
| } else { | |
| showToast('No items selected or quantities are invalid.', 'error'); | |
| } | |
| } | |
| }); | |
| } |