// Transactions page JavaScript let currentPage = 1; let filters = { category_id: '', start_date: '', end_date: '', search: '' }; // Load transactions async function loadTransactions() { try { const params = new URLSearchParams({ page: currentPage, ...filters }); const data = await apiCall(`/api/expenses/?${params}`); displayTransactions(data.expenses); displayPagination(data.pages, data.current_page, data.total || data.expenses.length); } catch (error) { console.error('Failed to load transactions:', error); } } // Display transactions function displayTransactions(transactions) { const container = document.getElementById('transactions-list'); if (transactions.length === 0) { container.innerHTML = ` receipt_long

No transactions found

`; return; } container.innerHTML = transactions.map(tx => { const txDate = new Date(tx.date); const dateStr = txDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); const timeStr = txDate.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true }); // Get category color const categoryColors = { 'Food': { bg: 'bg-green-500/10', text: 'text-green-400', border: 'border-green-500/20', dot: 'bg-green-400' }, 'Transport': { bg: 'bg-orange-500/10', text: 'text-orange-400', border: 'border-orange-500/20', dot: 'bg-orange-400' }, 'Entertainment': { bg: 'bg-purple-500/10', text: 'text-purple-400', border: 'border-purple-500/20', dot: 'bg-purple-400' }, 'Shopping': { bg: 'bg-blue-500/10', text: 'text-blue-400', border: 'border-blue-500/20', dot: 'bg-blue-400' }, 'Healthcare': { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/20', dot: 'bg-red-400' }, 'Bills': { bg: 'bg-yellow-500/10', text: 'text-yellow-400', border: 'border-yellow-500/20', dot: 'bg-yellow-400' }, 'Education': { bg: 'bg-pink-500/10', text: 'text-pink-400', border: 'border-pink-500/20', dot: 'bg-pink-400' }, 'Other': { bg: 'bg-gray-500/10', text: 'text-gray-400', border: 'border-gray-500/20', dot: 'bg-gray-400' } }; const catColor = categoryColors[tx.category_name] || categoryColors['Other']; // Status icon (completed/pending) const isCompleted = true; // For now, all are completed const statusIcon = isCompleted ? 'check' : 'schedule'; const statusClass = isCompleted ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'; const statusTitle = isCompleted ? 'Completed' : 'Pending'; return `
payments
${tx.description} ${tx.tags.length > 0 ? tx.tags.join(', ') : 'Expense'}
${tx.category_name} ${dateStr} ${timeStr}
credit_card •••• ${tx.currency}
${formatCurrency(tx.amount, tx.currency)} ${statusIcon}
${tx.receipt_path ? ` ` : ''}
`; }).join(''); } // Display pagination function displayPagination(totalPages, current, totalItems = 0) { const container = document.getElementById('pagination'); // Update pagination info const perPage = 10; const start = (current - 1) * perPage + 1; const end = Math.min(current * perPage, totalItems); document.getElementById('page-start').textContent = totalItems > 0 ? start : 0; document.getElementById('page-end').textContent = end; document.getElementById('total-count').textContent = totalItems; if (totalPages <= 1) { container.innerHTML = ''; return; } let html = ''; // Previous button const prevDisabled = current <= 1; html += ` `; // Next button const nextDisabled = current >= totalPages; html += ` `; container.innerHTML = html; } // Change page function changePage(page) { currentPage = page; loadTransactions(); } // Delete transaction async function deleteTransaction(id) { if (!confirm('Are you sure you want to delete this transaction?')) { return; } try { await apiCall(`/api/expenses/${id}`, { method: 'DELETE' }); showToast('Transaction deleted', 'success'); loadTransactions(); } catch (error) { console.error('Failed to delete transaction:', error); } } // Load categories for filter async function loadCategoriesFilter() { try { const data = await apiCall('/api/expenses/categories'); const select = document.getElementById('filter-category'); select.innerHTML = '' + data.categories.map(cat => ``).join(''); } catch (error) { console.error('Failed to load categories:', error); } } // Toggle advanced filters function toggleAdvancedFilters() { const advFilters = document.getElementById('advanced-filters'); advFilters.classList.toggle('hidden'); } // Filter event listeners document.getElementById('filter-category').addEventListener('change', (e) => { filters.category_id = e.target.value; currentPage = 1; loadTransactions(); }); document.getElementById('filter-start-date').addEventListener('change', (e) => { filters.start_date = e.target.value; currentPage = 1; loadTransactions(); }); document.getElementById('filter-end-date').addEventListener('change', (e) => { filters.end_date = e.target.value; currentPage = 1; loadTransactions(); }); document.getElementById('filter-search').addEventListener('input', (e) => { filters.search = e.target.value; currentPage = 1; loadTransactions(); }); // More filters button document.getElementById('more-filters-btn').addEventListener('click', toggleAdvancedFilters); // Date filter button (same as more filters for now) document.getElementById('date-filter-btn').addEventListener('click', toggleAdvancedFilters); // Export CSV document.getElementById('export-csv-btn').addEventListener('click', () => { window.location.href = '/api/expenses/export/csv'; }); // Import CSV document.getElementById('import-csv-btn').addEventListener('click', () => { document.getElementById('csv-file-input').click(); }); document.getElementById('csv-file-input').addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); try { const result = await apiCall('/api/expenses/import/csv', { method: 'POST', body: formData }); showToast(`Imported ${result.imported} transactions`, 'success'); if (result.errors.length > 0) { console.warn('Import errors:', result.errors); } loadTransactions(); } catch (error) { console.error('Failed to import CSV:', error); } e.target.value = ''; // Reset file input }); // Initialize document.addEventListener('DOMContentLoaded', () => { loadTransactions(); loadCategoriesFilter(); });