// State Management const state = { inventory: [], stats: { totalItems: 0, expiringSoon: 0, wasteAvoided: 0, moneySaved: 0 }, useMock: true }; // Mock Data (Fallback) const MOCK_INVENTORY = [ { id: 1, name: 'Milk (Mock)', quantity: '1L', expiryDate: '2023-10-30', category: 'Dairy', price: 2.50 }, { id: 2, name: 'Spinach (Mock)', quantity: '1 bag', expiryDate: '2023-10-26', category: 'Vegetables', price: 3.00 }, ]; const MOCK_RECIPES = [ { title: 'Mock Chicken Stir-fry', image: 'https://via.placeholder.com/300', ingredients: 'Chicken, Veggies' } ]; // Initialization // Initialization document.addEventListener('DOMContentLoaded', () => { // Check if backend is reachable or just load UI state.useMock = false; // We assume Python backend is primary now fetchInventory(); setupFormListeners(); // Hide N8N settings if present (optional UX polish) const settingsSection = document.getElementById('settings-section'); if (settingsSection) { // We can inject a message or hide legacy N8N inputs here if we wanted // For now, we just leave it as is } }); // Navigation function showSection(sectionId) { const sections = ['dashboard', 'inventory', 'recipes', 'settings']; sections.forEach(id => document.getElementById(`${id}-section`).classList.add('hidden')); document.getElementById(`${sectionId}-section`).classList.remove('hidden'); document.querySelectorAll('nav button').forEach(btn => btn.classList.remove('active')); if (sectionId === 'dashboard' || sectionId === 'inventory') { if (!state.useMock) fetchInventory(); else updateUI(); } } // Data Handling - Python API async function apiRequest(endpoint, method = 'GET', data = null) { // If endpoint doesn't start with /, add it const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; const url = `/api${path}`; const options = { method: method, headers: { 'Content-Type': 'application/json' } }; if (data) { options.body = JSON.stringify(data); } const response = await fetch(url, options); if (!response.ok) { throw new Error(`Server Error: ${response.status}`); } return await response.json(); } async function fetchInventory() { if (state.useMock) { updateUI(); return; } // Keep mock fallback for initial load if no URL was saved try { const data = await apiRequest('/get-items'); state.inventory = data.items || []; updateUI(); } catch (error) { console.error('API Error:', error); showToast('Backend not running? ' + error.message, 'error'); // Fallback to mock data if API fails if (state.inventory.length === 0) { state.inventory = MOCK_INVENTORY; state.useMock = true; // Switch to mock mode updateUI(); } } } async function addItem(itemData) { if (state.useMock) { state.inventory.push({ ...itemData, id: Date.now(), expiryDate: itemData.expiryDate || '2023-12-01' }); // Add ID for mock updateUI(); showToast('Item added (Mock)!', 'success'); return; } try { const result = await apiRequest('/add-item', 'POST', itemData); if (result.success) { showToast(`Item added! Expiry: ${result.expiryDate}`, 'success'); fetchInventory(); } else { showToast('Failed to add item: ' + (result.message || 'Unknown error'), 'error'); } } catch (error) { console.error('Add Item Error:', error); showToast('Failed to add item.', 'error'); } } async function getRecipeSuggestions() { const container = document.getElementById('recipes-container'); container.innerHTML = '
'; if (state.useMock) { await new Promise(r => setTimeout(r, 1000)); renderRecipes(MOCK_RECIPES); return; } try { const recipes = await apiRequest('/suggest-recipes', 'POST', {}); renderRecipes(recipes); } catch (e) { console.error(e); container.innerHTML = 'Error fetching recipes. Showing mock recipes.
'; setTimeout(() => renderRecipes(MOCK_RECIPES), 1000); // Fallback to mock recipes } } // Settings & Testing function saveSettings() { const url = document.getElementById('config-webhook-url').value; updateConfig(url); showToast('Settings Saved! Reloading...', 'success'); setTimeout(() => window.location.reload(), 1000); } async function testConnection() { const log = document.getElementById('connection-log'); log.style.display = 'block'; log.innerHTML = 'Testing connection...'; // Explicitly testing 'get_items' action which matches the Switch node logic const payload = { action: 'get_items' }; const url = document.getElementById('config-webhook-url').value; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (response.ok) { const data = await response.json(); log.innerHTML = `SUCCESS: Connected!No recipes found.
'; return; } recipes.forEach(recipe => { const div = document.createElement('div'); div.className = 'recipe-card'; div.innerHTML = `No urgent alerts. Good job!
'; return; } expiringItems.forEach(item => { const div = document.createElement('div'); div.style.marginBottom = '10px'; div.style.padding = '10px'; div.style.background = 'rgba(245, 158, 11, 0.1)'; div.style.borderRadius = '8px'; div.style.borderLeft = '3px solid var(--accent)'; div.innerHTML = `${item.name} is expiring soon!`; container.appendChild(div); }); } // Event Listeners function setupFormListeners() { document.getElementById('quick-add-form').addEventListener('submit', (e) => { e.preventDefault(); const name = document.getElementById('quick-name').value; const date = document.getElementById('quick-date').value; addItem({ name, quantity: '1 unit', expiryDate: date, category: 'Uncategorized' }); document.getElementById('quick-add-form').reset(); }); document.getElementById('add-item-form').addEventListener('submit', (e) => { e.preventDefault(); const formData = new FormData(e.target); addItem(Object.fromEntries(formData.entries())); e.target.reset(); showSection('inventory'); }); }