// Income Management JavaScript let incomeData = []; let incomeSources = []; let currentIncomeId = null; // Helper function for notifications function showNotification(message, type = 'success') { if (typeof showToast === 'function') { showToast(message, type); } else { console.log(`${type.toUpperCase()}: ${message}`); } } // Load user currency from profile async function loadUserCurrency() { try { const profile = await apiCall('/api/settings/profile'); window.userCurrency = profile.profile.currency || 'GBP'; } catch (error) { console.error('Failed to load user currency:', error); // Fallback to GBP if API fails window.userCurrency = 'GBP'; } } // Load income data async function loadIncome() { try { console.log('Loading income data...'); const response = await apiCall('/api/income/'); console.log('Income API response:', response); console.log('Response has income?', response.income); console.log('Income array:', response.income); if (response.income) { incomeData = response.income; console.log('Income data loaded:', incomeData.length, 'entries'); console.log('Full income data:', JSON.stringify(incomeData, null, 2)); renderIncomeTable(); } else { console.warn('No income data in response'); incomeData = []; renderIncomeTable(); } } catch (error) { console.error('Error loading income:', error); showNotification(window.getTranslation('common.error', 'An error occurred'), 'error'); } } // Load income sources async function loadIncomeSources() { try { const response = await apiCall('/api/income/sources'); if (response.sources) { incomeSources = response.sources; renderIncomeSourceOptions(); } } catch (error) { console.error('Error loading income sources:', error); } } // Render income source options in select function renderIncomeSourceOptions() { const selects = document.querySelectorAll('.income-source-select'); selects.forEach(select => { select.innerHTML = ''; incomeSources.forEach(source => { select.innerHTML += ``; }); }); } // Render income table function renderIncomeTable() { console.log('Rendering income table with', incomeData.length, 'entries'); const tbody = document.getElementById('income-table-body'); if (!tbody) { console.error('Income table body not found!'); return; } if (incomeData.length === 0) { tbody.innerHTML = ` payments

${window.getTranslation('income.noIncome', 'No income entries yet')}

${window.getTranslation('income.addFirst', 'Add your first income entry')}

`; return; } tbody.innerHTML = incomeData.map(income => { const date = new Date(income.date); const formattedDate = formatDate(income.date); const source = incomeSources.find(s => s.value === income.source); const sourceLabel = source ? source.label : income.source; const sourceIcon = source ? source.icon : 'category'; // Check if this is recurring income const isRecurring = income.is_recurring; const nextDueDate = income.next_due_date ? formatDate(income.next_due_date) : null; const isActive = income.is_active; const autoCreate = income.auto_create; // Build recurring info badge let recurringBadge = ''; if (isRecurring && autoCreate) { const statusColor = isActive ? 'green' : 'gray'; const statusIcon = isActive ? 'check_circle' : 'pause_circle'; recurringBadge = `
${statusIcon} ${income.frequency} ${nextDueDate ? `• Next: ${nextDueDate}` : ''}
`; } // Build action buttons let actionButtons = ` `; if (isRecurring && autoCreate) { actionButtons += ` `; } actionButtons += ` `; return `
${sourceIcon}

${income.description}

${sourceLabel}

${recurringBadge}
${formattedDate} ${sourceLabel} +${formatCurrency(income.amount, income.currency)}
${actionButtons}
`; }).join(''); console.log('Income table rendered successfully'); } // Open income modal function openIncomeModal() { const modal = document.getElementById('income-modal'); const form = document.getElementById('income-form'); const title = document.getElementById('income-modal-title'); currentIncomeId = null; form.reset(); title.textContent = window.getTranslation('income.add', 'Add Income'); modal.classList.remove('hidden'); // Set today's date as default const dateInput = document.getElementById('income-date'); if (dateInput) { dateInput.valueAsDate = new Date(); } } // Close income modal function closeIncomeModal() { const modal = document.getElementById('income-modal'); modal.classList.add('hidden'); currentIncomeId = null; } // Edit income function editIncome(id) { const income = incomeData.find(i => i.id === id); if (!income) return; currentIncomeId = id; const modal = document.getElementById('income-modal'); const form = document.getElementById('income-form'); const title = document.getElementById('income-modal-title'); title.textContent = window.getTranslation('income.edit', 'Edit Income'); document.getElementById('income-amount').value = income.amount; document.getElementById('income-source').value = income.source; document.getElementById('income-description').value = income.description; document.getElementById('income-date').value = income.date.split('T')[0]; document.getElementById('income-tags').value = income.tags.join(', '); document.getElementById('income-frequency').value = income.frequency || 'once'; // Show/hide custom frequency based on frequency value const customContainer = document.getElementById('custom-frequency-container'); if (income.frequency === 'custom') { customContainer.classList.remove('hidden'); document.getElementById('income-custom-days').value = income.custom_days || ''; } else { customContainer.classList.add('hidden'); } // Set auto_create checkbox const autoCreateCheckbox = document.getElementById('income-auto-create'); if (autoCreateCheckbox) { autoCreateCheckbox.checked = income.auto_create || false; } modal.classList.remove('hidden'); } // Save income async function saveIncome(event) { event.preventDefault(); console.log('Saving income...'); const amount = document.getElementById('income-amount').value; const source = document.getElementById('income-source').value; const description = document.getElementById('income-description').value; const date = document.getElementById('income-date').value; const tagsInput = document.getElementById('income-tags').value; const frequency = document.getElementById('income-frequency').value; const customDays = document.getElementById('income-custom-days').value; const autoCreate = document.getElementById('income-auto-create')?.checked || false; if (!amount || !source || !description) { showNotification(window.getTranslation('common.missingFields', 'Missing required fields'), 'error'); return; } // Validate custom frequency if (frequency === 'custom' && (!customDays || customDays < 1)) { showNotification(window.getTranslation('income.customDaysRequired', 'Please enter a valid number of days for custom frequency'), 'error'); return; } const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(t => t) : []; const data = { amount: parseFloat(amount), source: source, description: description, date: date, tags: tags, currency: window.userCurrency, frequency: frequency, custom_days: frequency === 'custom' ? parseInt(customDays) : null, auto_create: autoCreate }; console.log('Income data to save:', data); try { let response; if (currentIncomeId) { console.log('Updating income:', currentIncomeId); response = await apiCall(`/api/income/${currentIncomeId}`, { method: 'PUT', body: JSON.stringify(data) }); showNotification(window.getTranslation('income.updated', 'Income updated successfully'), 'success'); } else { console.log('Creating new income'); response = await apiCall('/api/income/', { method: 'POST', body: JSON.stringify(data) }); console.log('Income created response:', response); showNotification(window.getTranslation('income.created', 'Income added successfully'), 'success'); } closeIncomeModal(); console.log('Reloading income list...'); await loadIncome(); // Reload dashboard if on dashboard page if (typeof loadDashboardData === 'function') { loadDashboardData(); } } catch (error) { console.error('Error saving income:', error); showNotification(window.getTranslation('common.error', 'An error occurred'), 'error'); } } // Delete income async function deleteIncome(id) { if (!confirm(window.getTranslation('income.deleteConfirm', 'Are you sure you want to delete this income entry?'))) { return; } try { await apiCall(`/api/income/${id}`, { method: 'DELETE' }); showNotification(window.getTranslation('income.deleted', 'Income deleted successfully'), 'success'); loadIncome(); // Reload dashboard if on dashboard page if (typeof loadDashboardData === 'function') { loadDashboardData(); } } catch (error) { console.error('Error deleting income:', error); showNotification(window.getTranslation('common.error', 'An error occurred'), 'error'); } } // Toggle recurring income active status async function toggleRecurringIncome(id) { try { const response = await apiCall(`/api/income/${id}/toggle`, { method: 'PUT' }); if (response.success) { showNotification(response.message, 'success'); loadIncome(); } } catch (error) { console.error('Error toggling recurring income:', error); showNotification(window.getTranslation('common.error', 'An error occurred'), 'error'); } } // Create income now from recurring income async function createIncomeNow(id) { if (!confirm(window.getTranslation('income.createNowConfirm', 'Create an income entry now from this recurring income?'))) { return; } try { const response = await apiCall(`/api/income/${id}/create-now`, { method: 'POST' }); if (response.success) { showNotification(response.message, 'success'); loadIncome(); // Reload dashboard if on dashboard page if (typeof loadDashboardData === 'function') { loadDashboardData(); } } } catch (error) { console.error('Error creating income:', error); showNotification(window.getTranslation('common.error', 'An error occurred'), 'error'); } } // Initialize income page document.addEventListener('DOMContentLoaded', () => { if (document.getElementById('income-table-body')) { loadUserCurrency(); // Load currency first loadIncome(); loadIncomeSources(); // Setup form submit const form = document.getElementById('income-form'); if (form) { form.addEventListener('submit', saveIncome); } // Setup frequency change handler const frequencySelect = document.getElementById('income-frequency'); if (frequencySelect) { frequencySelect.addEventListener('change', (e) => { const customContainer = document.getElementById('custom-frequency-container'); if (customContainer) { if (e.target.value === 'custom') { customContainer.classList.remove('hidden'); } else { customContainer.classList.add('hidden'); } } }); } } }); // Make functions global window.openIncomeModal = openIncomeModal; window.closeIncomeModal = closeIncomeModal; window.editIncome = editIncome; window.deleteIncome = deleteIncome; window.saveIncome = saveIncome; window.toggleRecurringIncome = toggleRecurringIncome; window.createIncomeNow = createIncomeNow;