diff --git a/Backend/index.js b/Backend/index.js index 23424ee..57cdb85 100644 --- a/Backend/index.js +++ b/Backend/index.js @@ -242,12 +242,49 @@ app.post('/api/transactions/voice', async (req, res) => { } }); // Bank rules (can expand later) +// ---------- BANK RULES ---------- const bankRules = [ { name: 'HDFC Bank', email: 'alerts@hdfcbank.net' }, - { name: 'ICICI Bank', email: 'alerts@icicibank.com' }, + { name: 'ICICI Bank', email: 'alerts@icicibank.com' }, ]; -// ✅ Gmail fetch function +// ---------- PARSER ---------- +function parseBankMessage(snippet) { + const result = { + amount: null, + vendor: null, + type: null, + date: null, + raw: snippet, + }; + + // Amount (handles Rs/INR) + const amtMatch = snippet.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i); + if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, "")); + + + // Debit / Credit + if (/debited/i.test(snippet)) result.type = "debit"; + else if (/credited/i.test(snippet)) result.type = "credit"; + + // Vendor (look for "to" or "at") + const vendorMatch = snippet.match(/to\s+([A-Za-z0-9@.\s&-]+)/i); + if (vendorMatch) { + result.vendor = vendorMatch[1].trim(); + } else { + const atMatch = snippet.match(/at\s+([A-Za-z0-9\s&.-]+)/i); + if (atMatch) result.vendor = atMatch[1].trim(); + } + + // Date (dd-mm-yy or dd/mm/yyyy) + const dateMatch = snippet.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/); + if (dateMatch) result.date = dateMatch[1]; + + + return result; +} + +// ---------- FETCH EMAILS ---------- async function fetchBankEmails(accessToken, bankEmails) { const oauth2Client = new google.auth.OAuth2(); oauth2Client.setCredentials({ access_token: accessToken }); @@ -281,7 +318,7 @@ async function fetchBankEmails(accessToken, bankEmails) { return allEmails; } -// ✅ Gmail sync route +// ---------- ROUTE ---------- app.post('/api/sync-gmail', async (req, res) => { try { const { accessToken } = req.body; @@ -293,7 +330,13 @@ app.post('/api/sync-gmail', async (req, res) => { const bankEmails = bankRules.map(b => b.email); const emails = await fetchBankEmails(accessToken, bankEmails); - res.json({ success: true, emails }); + // 🔥 parse emails into structured transactions + const parsedEmails = emails.map(email => ({ + from: email.from, + ...parseBankMessage(email.snippet), + })); + + res.json({ success: true, emails: parsedEmails }); } catch (error) { console.error('❌ Gmail fetch error:', error); res.status(500).json({ error: 'Failed to fetch Gmail messages' }); diff --git a/Frontend/lib/email.dart b/Frontend/lib/email.dart index 1364360..22d65be 100644 --- a/Frontend/lib/email.dart +++ b/Frontend/lib/email.dart @@ -56,11 +56,12 @@ class _EmailsScreenState extends State { }); } } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Bank Emails"), + title: const Text("Bank Transactions"), actions: [ IconButton(icon: const Icon(Icons.refresh), onPressed: fetchEmails), ], @@ -76,10 +77,49 @@ class _EmailsScreenState extends State { itemCount: _emails.length, itemBuilder: (context, index) { final email = _emails[index]; - return ListTile( - title: Text(email['subject'] ?? "No subject"), - subtitle: Text(email['snippet'] ?? ""), - leading: const Icon(Icons.email), + return Card( + margin: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 12, + ), + child: ListTile( + leading: Icon( + email['type'] == 'debit' + ? Icons.arrow_upward + : Icons.arrow_downward, + color: + email['type'] == 'debit' + ? Colors.red + : Colors.green, + ), + title: Text( + "₹${email['amount']?.toStringAsFixed(2) ?? '---'}", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(email['vendor'] ?? "Unknown Vendor"), + Text( + "Date: ${email['date'] ?? 'N/A'}", + style: const TextStyle(fontSize: 12), + ), + ], + ), + trailing: Text( + email['type']?.toUpperCase() ?? "", + style: TextStyle( + color: + email['type'] == 'debit' + ? Colors.red + : Colors.green, + fontWeight: FontWeight.bold, + ), + ), + ), ); }, ),