const API_URL = '/api'; let allDrugs = []; let currentDrug = null; let showLowStockOnly = false; let searchTerm = ''; let expandedDrugs = new Set(); // Initialize document.addEventListener('DOMContentLoaded', () => { const drugForm = document.getElementById('drugForm'); const variantForm = document.getElementById('variantForm'); const editVariantForm = document.getElementById('editVariantForm'); const dispenseForm = document.getElementById('dispenseForm'); const editForm = document.getElementById('editForm'); const addModal = document.getElementById('addModal'); const addVariantModal = document.getElementById('addVariantModal'); const editVariantModal = document.getElementById('editVariantModal'); const dispenseModal = document.getElementById('dispenseModal'); const editModal = document.getElementById('editModal'); const addDrugBtn = document.getElementById('addDrugBtn'); const cancelAddBtn = document.getElementById('cancelAddBtn'); const cancelVariantBtn = document.getElementById('cancelVariantBtn'); const cancelEditVariantBtn = document.getElementById('cancelEditVariantBtn'); const cancelDispenseBtn = document.getElementById('cancelDispenseBtn'); const cancelEditBtn = document.getElementById('cancelEditBtn'); const showAllBtn = document.getElementById('showAllBtn'); const showLowStockBtn = document.getElementById('showLowStockBtn'); // Modal close buttons const closeButtons = document.querySelectorAll('.close'); drugForm.addEventListener('submit', handleAddDrug); variantForm.addEventListener('submit', handleAddVariant); editVariantForm.addEventListener('submit', handleEditVariant); dispenseForm.addEventListener('submit', handleDispenseDrug); editForm.addEventListener('submit', handleEditDrug); addDrugBtn.addEventListener('click', () => openModal(addModal)); cancelAddBtn.addEventListener('click', () => closeModal(addModal)); cancelVariantBtn.addEventListener('click', () => closeModal(addVariantModal)); cancelEditVariantBtn.addEventListener('click', () => closeModal(editVariantModal)); cancelDispenseBtn.addEventListener('click', () => closeModal(dispenseModal)); cancelEditBtn.addEventListener('click', closeEditModal); const closeHistoryBtn = document.getElementById('closeHistoryBtn'); closeHistoryBtn.addEventListener('click', () => closeModal(document.getElementById('historyModal'))); closeButtons.forEach(btn => btn.addEventListener('click', (e) => { const modal = e.target.closest('.modal'); closeModal(modal); })); showAllBtn.addEventListener('click', () => { showLowStockOnly = false; updateFilterButtons(); renderDrugs(); }); showLowStockBtn.addEventListener('click', () => { showLowStockOnly = true; updateFilterButtons(); renderDrugs(); }); // Search functionality const drugSearch = document.getElementById('drugSearch'); if (drugSearch) { let searchTimeout; drugSearch.addEventListener('input', (e) => { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchTerm = e.target.value.toLowerCase().trim(); renderDrugs(); }, 150); }); } // Close modal when clicking outside window.addEventListener('click', (e) => { if (e.target.classList.contains('modal')) { closeModal(e.target); } }); loadDrugs(); }); // Load drugs from API async function loadDrugs() { try { const response = await fetch(`${API_URL}/drugs`); if (!response.ok) throw new Error('Failed to load drugs'); allDrugs = await response.json(); renderDrugs(); updateDispenseDrugSelect(); } catch (error) { console.error('Error loading drugs:', error); document.getElementById('drugsList').innerHTML = '

Error loading drugs. Make sure the backend is running on http://localhost:8000

'; } } // Modal utility functions function openModal(modal) { modal.classList.add('show'); document.body.style.overflow = 'hidden'; } function closeModal(modal) { modal.classList.remove('show'); document.body.style.overflow = 'auto'; } function closeEditModal() { closeModal(document.getElementById('editModal')); } function updateDispenseDrugSelect() { const select = document.getElementById('dispenseDrugSelect'); select.innerHTML = ''; allDrugs.forEach(drug => { drug.variants.forEach(variant => { const option = document.createElement('option'); option.value = variant.id; option.textContent = `${drug.name} ${variant.strength} (${variant.quantity} ${variant.unit})`; select.appendChild(option); }); }); } // Render drugs list function renderDrugs() { const drugsList = document.getElementById('drugsList'); let drugsToShow = allDrugs; // Apply search filter if (searchTerm) { drugsToShow = drugsToShow.filter(drug => drug.name.toLowerCase().includes(searchTerm) || (drug.description && drug.description.toLowerCase().includes(searchTerm)) || drug.variants.some(variant => variant.strength.toLowerCase().includes(searchTerm)) ); } // Apply stock filter if (showLowStockOnly) { drugsToShow = drugsToShow.filter(drug => drug.variants.some(variant => variant.quantity <= variant.low_stock_threshold) ); } if (drugsToShow.length === 0) { drugsList.innerHTML = '

No drugs found matching your criteria

'; return; } drugsList.innerHTML = drugsToShow.map(drug => { const totalVariants = drug.variants.length; const lowStockVariants = drug.variants.filter(v => v.quantity <= v.low_stock_threshold).length; const totalQuantity = drug.variants.reduce((sum, v) => sum + v.quantity, 0); const isLowStock = lowStockVariants > 0; const isExpanded = expandedDrugs.has(drug.id); const variantsHtml = isExpanded ? ` ${drug.variants.map(variant => { const variantIsLowStock = variant.quantity <= variant.low_stock_threshold; return `
${escapeHtml(drug.name)} ${escapeHtml(variant.strength)}
${variant.quantity} ${escapeHtml(variant.unit)}
${variantIsLowStock ? 'Low Stock' : 'OK'}
`; }).join('')}` : ''; return `
${escapeHtml(drug.name)}
${drug.description ? escapeHtml(drug.description) : 'No description'}
${totalVariants} variant${totalVariants !== 1 ? 's' : ''} (${totalQuantity} total units)
${isLowStock ? `${lowStockVariants} low` : 'All OK'}
${isExpanded ? 'â–¼' : 'â–¶'}
${variantsHtml}
`; }).join(''); } // Handle add drug form async function handleAddDrug(e) { e.preventDefault(); const drugData = { name: document.getElementById('drugName').value, description: document.getElementById('drugDescription').value }; try { const response = await fetch(`${API_URL}/drugs`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(drugData) }); if (!response.ok) throw new Error('Failed to add drug'); document.getElementById('drugForm').reset(); closeModal(document.getElementById('addModal')); await loadDrugs(); alert('Drug added successfully! Now add variants for this drug.'); } catch (error) { console.error('Error adding drug:', error); alert('Failed to add drug. Check the console for details.'); } } // Handle dispense drug form async function handleDispenseDrug(e) { e.preventDefault(); const variantId = parseInt(document.getElementById('dispenseDrugSelect').value); const quantity = parseFloat(document.getElementById('dispenseQuantity').value); const animalName = document.getElementById('dispenseAnimal').value; const userName = document.getElementById('dispenseUser').value; const notes = document.getElementById('dispenseNotes').value; if (!variantId || !quantity || !animalName || !userName) { alert('Please fill in all required fields'); return; } const dispensingData = { drug_variant_id: variantId, quantity: quantity, animal_name: animalName, user_name: userName, notes: notes || null }; try { const response = await fetch(`${API_URL}/dispense`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dispensingData) }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || 'Failed to dispense drug'); } document.getElementById('dispenseForm').reset(); closeModal(document.getElementById('dispenseModal')); await loadDrugs(); alert('Drug dispensed successfully!'); } catch (error) { console.error('Error dispensing drug:', error); alert('Failed to dispense drug: ' + error.message); } } // Open edit modal function openEditModal(drugId) { const drug = allDrugs.find(d => d.id === drugId); if (!drug) return; document.getElementById('editDrugId').value = drug.id; document.getElementById('editDrugName').value = drug.name; document.getElementById('editDrugDescription').value = drug.description || ''; document.getElementById('editModal').classList.add('show'); } // Close edit modal function closeEditModal() { document.getElementById('editModal').classList.remove('show'); document.getElementById('editForm').reset(); } // Show variants for a drug function toggleDrugExpansion(drugId) { if (expandedDrugs.has(drugId)) { expandedDrugs.delete(drugId); } else { expandedDrugs.add(drugId); } renderDrugs(); } // Open add variant modal function openAddVariantModal(drugId) { const drug = allDrugs.find(d => d.id === drugId); if (!drug) return; currentDrug = drug; document.getElementById('variantDrugId').value = drug.id; document.getElementById('addVariantModal').classList.add('show'); } // Handle add variant form async function handleAddVariant(e) { e.preventDefault(); const drugId = parseInt(document.getElementById('variantDrugId').value); const variantData = { strength: document.getElementById('variantStrength').value, quantity: parseFloat(document.getElementById('variantQuantity').value), unit: document.getElementById('variantUnit').value, low_stock_threshold: parseFloat(document.getElementById('variantThreshold').value) }; try { const response = await fetch(`${API_URL}/drugs/${drugId}/variants`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(variantData) }); if (!response.ok) throw new Error('Failed to add variant'); document.getElementById('variantForm').reset(); closeModal(document.getElementById('addVariantModal')); await loadDrugs(); renderDrugs(); alert('Variant added successfully!'); } catch (error) { console.error('Error adding variant:', error); alert('Failed to add variant. Check the console for details.'); } } // Open edit variant modal function openEditVariantModal(variantId) { const variant = currentDrug.variants.find(v => v.id === variantId); if (!variant) return; document.getElementById('editVariantId').value = variant.id; document.getElementById('editVariantStrength').value = variant.strength; document.getElementById('editVariantQuantity').value = variant.quantity; document.getElementById('editVariantUnit').value = variant.unit; document.getElementById('editVariantThreshold').value = variant.low_stock_threshold; document.getElementById('editVariantModal').classList.add('show'); } // Handle edit variant form async function handleEditVariant(e) { e.preventDefault(); const variantId = parseInt(document.getElementById('editVariantId').value); const variantData = { strength: document.getElementById('editVariantStrength').value, quantity: parseFloat(document.getElementById('editVariantQuantity').value), unit: document.getElementById('editVariantUnit').value, low_stock_threshold: parseFloat(document.getElementById('editVariantThreshold').value) }; try { const response = await fetch(`${API_URL}/variants/${variantId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(variantData) }); if (!response.ok) throw new Error('Failed to update variant'); closeModal(document.getElementById('editVariantModal')); await loadDrugs(); renderDrugs(); alert('Variant updated successfully!'); } catch (error) { console.error('Error updating variant:', error); alert('Failed to update variant. Check the console for details.'); } } // Dispense from variant function dispenseVariant(variantId) { // Update the dropdown display with all variants updateDispenseDrugSelect(); // Pre-select the variant in the dispense modal const drugSelect = document.getElementById('dispenseDrugSelect'); drugSelect.value = variantId; // Open dispense modal openModal(document.getElementById('dispenseModal')); } // Delete variant async function deleteVariant(variantId) { if (!confirm('Are you sure you want to delete this variant?')) return; try { const response = await fetch(`${API_URL}/variants/${variantId}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete variant'); await loadDrugs(); renderDrugs(); alert('Variant deleted successfully!'); } catch (error) { console.error('Error deleting variant:', error); alert('Failed to delete variant. Check the console for details.'); } } // Show dispensing history for a drug async function showDrugHistory(drugId) { const drug = allDrugs.find(d => d.id === drugId); if (!drug) return; const historyModal = document.getElementById('historyModal'); const historyContent = document.getElementById('historyContent'); document.getElementById('historyDrugName').textContent = drug.name; historyContent.innerHTML = '

Loading history...

'; openModal(historyModal); try { const response = await fetch(`${API_URL}/dispense/history`); if (!response.ok) throw new Error('Failed to fetch history'); const allHistory = await response.json(); // Filter history for this drug's variants const variantIds = drug.variants.map(v => v.id); const drugHistory = allHistory.filter(item => variantIds.includes(item.drug_variant_id)); if (drugHistory.length === 0) { historyContent.innerHTML = '

No dispensing history for this drug.

'; return; } // Sort by dispensed_at descending (most recent first) drugHistory.sort((a, b) => new Date(b.dispensed_at) - new Date(a.dispensed_at)); const historyHtml = drugHistory.map(item => { const variant = drug.variants.find(v => v.id === item.drug_variant_id); const date = new Date(item.dispensed_at).toLocaleDateString(); const time = new Date(item.dispensed_at).toLocaleTimeString(); return `
${drug.name} ${variant.strength}
${date} ${time}
Quantity: ${item.quantity} ${variant.unit}
Animal: ${escapeHtml(item.animal_name)}
User: ${escapeHtml(item.user_name)}
${item.notes ? `
Notes: ${escapeHtml(item.notes)}
` : ''}
`; }).join(''); historyContent.innerHTML = historyHtml; } catch (error) { console.error('Error fetching history:', error); historyContent.innerHTML = '

Failed to load history. Check the console for details.

'; } } // Handle edit drug form async function handleEditDrug(e) { e.preventDefault(); const drugId = parseInt(document.getElementById('editDrugId').value); const drugData = { name: document.getElementById('editDrugName').value, description: document.getElementById('editDrugDescription').value }; try { const response = await fetch(`${API_URL}/drugs/${drugId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(drugData) }); if (!response.ok) throw new Error('Failed to update drug'); closeEditModal(); await loadDrugs(); alert('Drug updated successfully!'); } catch (error) { console.error('Error updating drug:', error); alert('Failed to update drug. Check the console for details.'); } } // Delete drug async function deleteDrug(drugId) { if (!confirm('Are you sure you want to delete this drug?')) return; try { const response = await fetch(`${API_URL}/drugs/${drugId}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete drug'); await loadDrugs(); alert('Drug deleted successfully!'); } catch (error) { console.error('Error deleting drug:', error); alert('Failed to delete drug. Check the console for details.'); } } // Update filter button states function updateFilterButtons() { document.getElementById('showAllBtn').classList.toggle('active', !showLowStockOnly); document.getElementById('showLowStockBtn').classList.toggle('active', showLowStockOnly); } // Escape HTML to prevent XSS function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }