document.addEventListener('DOMContentLoaded', () => { const authSection = document.getElementById('auth-section'); const dashboardSection = document.getElementById('dashboard-section'); const userControls = document.getElementById('user-controls'); const userGreeting = document.getElementById('user-greeting'); const chartContainer = document.getElementById('chart-container'); const chartTitle = document.getElementById('chart-title'); const ctx = document.getElementById('priceChart').getContext('2d'); let chartInstance = null; let currentItems = []; // Check Auth Status const checkAuth = () => { const token = localStorage.getItem('token'); const scraperKey = localStorage.getItem('scraper_api_keys'); if (token && scraperKey) { authSection.classList.add('hidden'); dashboardSection.classList.remove('hidden'); userControls.classList.remove('hidden'); userGreeting.textContent = `User ID: ${token}`; fetchItems(); } else { authSection.classList.remove('hidden'); dashboardSection.classList.add('hidden'); userControls.classList.add('hidden'); } }; // Logout document.getElementById('logout-btn').addEventListener('click', () => { localStorage.removeItem('token'); localStorage.removeItem('scraper_api_keys'); checkAuth(); }); // Login Form document.getElementById('login-form').addEventListener('submit', async (e) => { e.preventDefault(); const username = document.getElementById('login-username').value; const password = document.getElementById('login-password').value; try { const res = await fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await res.json(); if (res.ok) { localStorage.setItem('token', data.user_id); localStorage.setItem('scraper_api_keys', data.scraper_api_keys); checkAuth(); } else { alert(data.error); } } catch (err) { console.error(err); } }); // Register Form document.getElementById('register-form').addEventListener('submit', async (e) => { e.preventDefault(); const username = document.getElementById('reg-username').value; const password = document.getElementById('reg-password').value; const email = document.getElementById('reg-email').value; const scraper_api_keys = document.getElementById('reg-scraper-key').value; try { const res = await fetch('/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password, email, scraper_api_keys, amazon_paapi_access_key: document.getElementById("reg-paapi-access").value, amazon_paapi_secret_key: document.getElementById("reg-paapi-secret").value, amazon_paapi_partner_tag: document.getElementById("reg-paapi-partner").value }) }); const data = await res.json(); if (res.ok) { localStorage.setItem('token', data.user_id); localStorage.setItem('scraper_api_keys', data.scraper_api_keys); checkAuth(); } else { alert(data.error); } } catch (err) { console.error(err); } }); // Add Item Form document.getElementById('add-item-form').addEventListener('submit', async (e) => { e.preventDefault(); const url = document.getElementById('item-url').value; const target_price = document.getElementById('item-target-price').value; const msgDiv = document.getElementById('add-msg'); msgDiv.textContent = 'Adding item... Please wait.'; try { const res = await fetch('/api/add', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ url, target_price, check_interval_hours: document.getElementById('item-check-interval').value, scraper_api_keys: localStorage.getItem('scraper_api_keys') }) }); const data = await res.json(); if (res.ok) { msgDiv.textContent = `Success! Added: ${data.title} at ₹${data.current_price}`; fetchItems(); document.getElementById('add-item-form').reset(); } else { msgDiv.textContent = `Error: ${data.error}`; } } catch (err) { msgDiv.textContent = 'Failed to add item.'; } }); // Fetch and Display Items const fetchItems = async () => { try { const res = await fetch('/api/items', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await res.json(); if (res.ok) { currentItems = data.items; renderItemsTable(); } } catch (err) { console.error('Failed to fetch items', err); } }; const renderItemsTable = () => { const tbody = document.getElementById('items-body'); tbody.innerHTML = ''; currentItems.forEach(item => { const tr = document.createElement('tr'); tr.innerHTML = ` ${item.title.substring(0, 50)}... ₹${item.current_price !== null ? item.current_price : 'N/A'} ₹${item.target_price} ${item.check_interval_hours || 24} hr `; tbody.appendChild(tr); }); }; // Chart logic window.viewGraph = (itemId) => { const item = currentItems.find(i => i.id === itemId); if (!item) return; chartContainer.classList.remove('hidden'); chartTitle.textContent = item.title; const labels = item.history.map(h => new Date(h.timestamp).toLocaleString()); const dataPoints = item.history.map(h => h.price); if (chartInstance) { chartInstance.destroy(); } chartInstance = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'Price (₹)', data: dataPoints, borderColor: '#f0c14b', backgroundColor: 'rgba(240, 193, 75, 0.2)', borderWidth: 2, fill: true, tension: 0.1 }] }, options: { responsive: true, scales: { y: { beginAtZero: false } } } }); // Scroll to chart chartContainer.scrollIntoView({ behavior: 'smooth' }); }; document.getElementById('close-chart').addEventListener('click', () => { chartContainer.classList.add('hidden'); if (chartInstance) { chartInstance.destroy(); } }); // Initial check // Delete Item window.deleteItem = async (itemId) => { if (!confirm('Are you sure you want to delete this item?')) return; try { const res = await fetch(`/api/items/${itemId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (res.ok) { fetchItems(); } else { const data = await res.json(); alert(data.error || 'Failed to delete item.'); } } catch (err) { console.error('Failed to delete item', err); } }; // Edit Profile Logic const profileSection = document.getElementById('profile-section'); document.getElementById('edit-profile-btn').addEventListener('click', async () => { profileSection.classList.toggle('hidden'); if (!profileSection.classList.contains('hidden')) { // Fetch profile try { const res = await fetch('/api/profile', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); const data = await res.json(); if (res.ok) { document.getElementById('profile-email').value = data.email || ''; document.getElementById('profile-scraper-keys').value = data.scraper_api_keys || ''; document.getElementById('profile-paapi-access').value = data.amazon_paapi_access_key || ''; document.getElementById('profile-paapi-secret').value = data.amazon_paapi_secret_key || ''; document.getElementById('profile-paapi-partner').value = data.amazon_paapi_partner_tag || ''; document.getElementById('profile-email-notif').checked = data.email_notifications; } } catch (err) { console.error(err); } } }); document.getElementById('profile-form').addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('profile-email').value; const scraper_api_keys = document.getElementById('profile-scraper-keys').value; const password = document.getElementById('profile-password').value; const email_notifications = document.getElementById('profile-email-notif').checked; const msgDiv = document.getElementById('profile-msg'); msgDiv.textContent = 'Updating...'; const payload = { email, scraper_api_keys, email_notifications, amazon_paapi_access_key: document.getElementById("profile-paapi-access").value, amazon_paapi_secret_key: document.getElementById("profile-paapi-secret").value, amazon_paapi_partner_tag: document.getElementById("profile-paapi-partner").value }; if (password) payload.password = password; try { const res = await fetch('/api/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify(payload) }); const data = await res.json(); if (res.ok) { msgDiv.textContent = 'Profile updated successfully!'; localStorage.setItem('scraper_api_keys', scraper_api_keys); // Update local storage setTimeout(() => { profileSection.classList.add('hidden'); msgDiv.textContent=''; }, 2000); } else { msgDiv.textContent = `Error: ${data.error}`; } } catch (err) { msgDiv.textContent = 'Failed to update profile.'; } }); checkAuth(); }); async function editTargetPrice(itemId, currentTargetPrice) { const newTargetPrice = prompt(`Enter new target price for item (current: ₹${currentTargetPrice}):`, currentTargetPrice); if (newTargetPrice === null || newTargetPrice.trim() === '') { return; // User cancelled or entered empty string } const price = parseFloat(newTargetPrice); if (isNaN(price) || price <= 0) { alert('Please enter a valid target price.'); return; } try { const token = localStorage.getItem('token'); const response = await fetch(`/api/items/${itemId}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ target_price: price }) }); const data = await response.json(); if (response.ok) { // Update the UI immediately without full reload const targetPriceSpan = document.getElementById(`target-price-${itemId}`); if (targetPriceSpan) { targetPriceSpan.textContent = `₹${price}`; } // Update the edit button onclick parameter to have the new price const editBtn = targetPriceSpan.nextElementSibling; if (editBtn) { editBtn.setAttribute('onclick', `editTargetPrice(${itemId}, ${price})`); } alert('Target price updated successfully!'); } else { alert(data.error || 'Failed to update target price'); } } catch (error) { console.error('Error updating target price:', error); alert('An error occurred. Please try again.'); } }