OHO-23(email testing)

This commit is contained in:
lokeshramchand-ctrl 2025-08-28 11:20:24 +05:30
parent 50fc322bc7
commit 9c79d8a59d
16 changed files with 113 additions and 61 deletions

View file

@ -212,13 +212,18 @@ app.get('/api/transactions/recent', async (req, res) => {
//VOICE
app.post('/api/transactions/voice', async (req, res) => {
try {
const { voiceInput } = req.body;
const { voiceInput, userId } = req.body; // ✅ take userId from body
if (!voiceInput) {
return res.status(400).json({ error: 'No voice input provided' });
}
if (!userId) {
return res.status(400).json({ error: 'Missing userId' }); // ✅ enforce userId like others
}
// Step 1: Use regex to extract amount
const amountMatch = voiceInput.match(/(?:\₹|\$)?(\d+(?:\.\d{1,2})?)/);
const amount = amountMatch ? parseFloat(amountMatch[1]) : null;
// Step 2: Extract description (rough logic: remove common verbs + amount)
const cleaned = voiceInput
.toLowerCase()
@ -226,19 +231,24 @@ app.post('/api/transactions/voice', async (req, res) => {
.replace(/₹?\d+/, '')
.trim();
const description = cleaned || 'misc';
// Step 3: Predict category using your Flask API
// Step 3: Predict category using Flask API
const predictRes = await axios.post('http://192.168.1.9:5000/api/predict', {
description,
});
const category = predictRes.data?.category || 'Other';
// Step 4: Save to DB
const newTransaction = new Transaction({
userId, // ✅ store userId
description,
amount,
category,
source: 'voice'
});
await newTransaction.save();
res.status(200).json({
message: '✅ Voice transaction saved',
data: newTransaction,

View file

@ -115,7 +115,7 @@
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/emai_screen.dart';
import 'package:monarch/main_pages/HomePage/Components/Voice/voice_dialog.dart';
//import 'package:monarch/email.dart';
import 'package:monarch/other_pages/enviroment.dart';
import 'dart:convert';
@ -181,12 +181,13 @@ class _LoginScreenState extends State<LoginScreen> {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => VoiceTransactionDialog(),
// builder:
// (_) => EmailsScreen(
// accessToken: accessToken,
// userId: data['user']['_id'],
// ),
builder: (_) => AutoSyncEmailsScreen(accessToken: accessToken,userId: data['user']['_id'],),
//builder: (_) => AutoSyncEmailsScreen(accessToken: accessToken,userId: data['user']['_id'],),
),
);
} else {

View file

@ -25,7 +25,7 @@ class MyApp extends StatelessWidget {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const FinTrackHomePage(),
home: const LoginScreen(),
// home: const SpeechInputPage(),
);

View file

@ -2,7 +2,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:monarch/main_pages/HomePage/transaction_button.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/transaction_button.dart';
import 'package:monarch/other_pages/colors.dart';
class GreetingHeader extends StatelessWidget {

View file

@ -4,8 +4,10 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/other_pages/reponsive.dart';
import 'package:monarch/other_pages/colors.dart';
import 'package:monarch/other_pages/enviroment.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ConfirmTransactionPage extends StatelessWidget {
final Map<String, dynamic> data;
@ -13,16 +15,27 @@ class ConfirmTransactionPage extends StatelessWidget {
const ConfirmTransactionPage({super.key, required this.data});
Future<void> _saveTransaction(BuildContext context) async {
const String saveUrl = '${Environment.baseUrl}/api/transaction/add';
const String saveUrl =
'${Environment.baseUrl}/api/transaction/add'; // ✅ make sure this matches backend
try {
final prefs = await SharedPreferences.getInstance();
final userId = prefs.getString('userId');
if (userId == null) {
throw Exception('User ID not found. Please log in again.');
}
final response = await http.post(
Uri.parse(saveUrl),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'description': data['description'],
'amount': data['amount'],
'category': data['category'],
'userId': userId,
'description': data['description'] ?? '',
'amount':
double.tryParse(data['amount'].toString()) ??
0.0, // ✅ force number
'category': data['category'] ?? 'Uncategorized',
'date': data['date'] ?? DateTime.now().toIso8601String(),
}),
);
@ -38,7 +51,8 @@ class ConfirmTransactionPage extends StatelessWidget {
);
Navigator.popUntil(context, (route) => route.isFirst);
} else {
throw Exception('Save failed with status: ${response.statusCode}');
// 👇 Log backend response
throw Exception('Save failed: ${response.statusCode} ${response.body}');
}
} catch (e) {
_showCustomSnackBar(
@ -53,35 +67,56 @@ class ConfirmTransactionPage extends StatelessWidget {
}
void _showCustomSnackBar({
required BuildContext context,
required String message,
required IconData icon,
required Color backgroundColor,
required Color iconColor,
required Duration duration,
Duration duration = const Duration(seconds: 3),
required BuildContext context,
}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(icon, color: iconColor),
const SizedBox(width: 12),
Expanded(
child: Text(
message,
style: GoogleFonts.inter(
color: Colors.white,
fontWeight: FontWeight.w500,
content: Container(
padding: EdgeInsets.symmetric(vertical: context.responsiveHeight(4)),
child: Row(
children: [
Container(
padding: EdgeInsets.all(context.responsiveWidth(8)),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(
context.responsiveWidth(8),
),
),
child: Icon(
icon,
color: iconColor,
size: context.responsiveText(20),
),
),
),
],
SizedBox(width: context.responsiveWidth(12)),
Expanded(
child: Text(
message,
style: GoogleFonts.inter(
fontWeight: FontWeight.w500,
color: Colors.white,
fontSize: context.responsiveText(14),
height: 1.4,
),
),
),
],
),
),
backgroundColor: backgroundColor,
duration: duration,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
margin: const EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(context.responsiveWidth(12)),
),
margin: EdgeInsets.all(context.responsiveWidth(16)),
duration: duration,
elevation: 8,
),
);
}

View file

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/speech.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:speech_to_text/speech_to_text.dart' as stt;
import 'package:monarch/other_pages/colors.dart';
import 'package:monarch/other_pages/enviroment.dart';
@ -103,10 +104,17 @@ class _VoiceTransactionDialogState extends State<VoiceTransactionDialog>
Future<void> _sendToBackend() async {
try {
final prefs = await SharedPreferences.getInstance();
final userId = prefs.getString('userId');
if (userId == null) {
throw Exception('User ID not found. Please log in again.');
}
final response = await http.post(
Uri.parse('${Environment.baseUrl}/api/transactions/voice'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'voiceInput': _spokenText}),
body: jsonEncode({'userId': userId, 'voiceInput': _spokenText}),
);
if (response.statusCode == 200) {
@ -120,28 +128,13 @@ class _VoiceTransactionDialogState extends State<VoiceTransactionDialog>
builder: (context) => ConfirmTransactionPage(data: transactionData),
),
);
_showCustomSnackBar(
message: 'Transaction processed successfully!',
icon: Icons.check_circle,
backgroundColor: const Color(0xFF4CAF50),
iconColor: Colors.white,
duration: const Duration(seconds: 2),
);
} else {
throw Exception('Failed to process transaction');
throw Exception('Save failed with status: ${response.statusCode}');
}
} catch (e) {
_showCustomSnackBar(
message: 'Error processing transaction: ${e.toString()}',
icon: Icons.error_outline,
backgroundColor: const Color(0xFFFF6B6B),
iconColor: Colors.white,
duration: const Duration(seconds: 4),
);
setState(() {
_processing = false;
});
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error: ${e.toString()}')));
}
}

View file

@ -1,15 +1,15 @@
// ignore_for_file: deprecated_member_use
import 'package:flutter/material.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/hero_card.dart';
import 'package:monarch/main_pages/HomePage/animated.dart';
import 'package:monarch/main_pages/HomePage/greeting.dart';
import 'package:monarch/main_pages/HomePage/navbar.dart';
import 'package:monarch/main_pages/HomePage/Components/Voice/t_dialog.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/greeting.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/navbar.dart';
import 'package:monarch/main_pages/HomePage/Components/Email/t_dialog.dart';
import 'package:monarch/main_pages/HomePage/Components/Voice/voice_dialog.dart';
import 'package:monarch/other_pages/colors.dart';
import 'package:monarch/support/fetch_service.dart';
import 'package:monarch/main_pages/HomePage/hero_card.dart';
import 'package:monarch/main_pages/HomePage/quick_actions.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/quick_actions.dart';
import 'package:monarch/support/transcations_recent.dart';
class FinTrackHomePage extends StatefulWidget {

View file

@ -7,12 +7,13 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/other_pages/colors.dart';
import 'package:monarch/support/add.dart';
import 'package:monarch/other_pages/enviroment.dart';
import 'package:monarch/main_pages/Statistics/Budget/update_budget.dart';
import 'package:monarch/main_pages/Statistics/Budget/budget_manager.dart';
import 'package:monarch/main_pages/HomePage/homepage.dart';
import 'package:monarch/main_pages/HomePage/navbar.dart';
import 'package:monarch/main_pages/HomePage/Components/Home/navbar.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Transaction {
@ -62,12 +63,6 @@ class StatisticsState extends State<Statistics> with TickerProviderStateMixin {
String selectedCategory = 'All';
Map<String, double> totalAmountPerCategory = {};
// Color Scheme
final Color backgroundColor = const Color(0xFFF8F9FA);
final Color primaryColor = const Color(0xFF2D3436);
final Color accentColor = const Color(0xFF00B894);
final Color cardColor = const Color(0xFFFFFFFF);
final Color textSecondary = const Color(0xFF636E72);
final Color surfaceColor = const Color(0xFFF1F2F6);
// Responsive methods
double responsiveWidth(double size) =>

View file

@ -1,5 +1,5 @@
class Environment {
static const String baseUrl = 'http://192.168.1.9:3000';
static const String baseUrl = 'http://10.231.7.150:3000';
static const String serverClientId =
"278433619849-cg6f7gk5cc45rgu1ojrlkf794lm4udgn.apps.googleusercontent.com";
}

View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
extension ResponsiveExtension on BuildContext {
double responsiveWidth(double size) {
return size * (MediaQuery.of(this).size.width / 375);
}
double responsiveHeight(double size) {
return size * (MediaQuery.of(this).size.height / 812);
}
double responsiveText(double size) {
final width = MediaQuery.of(this).size.width;
if (width < 350) return size * 0.9;
if (width > 600) return size * 1.2;
return size;
}
}