Done:Cleaning Homepage
This commit is contained in:
parent
7641cff987
commit
c0548e67bc
13 changed files with 531 additions and 389 deletions
|
|
@ -92,6 +92,28 @@ app.get('/api/transactions', async (req, res) => {
|
|||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
// Get 5 most recent transactions
|
||||
app.get('/api/transactions/recent', async (req, res) => {
|
||||
try {
|
||||
const transactions = await Transaction.find({})
|
||||
.sort({ date: -1 })
|
||||
.limit(5); // 👈 limit to 5
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: transactions.map(tx => ({
|
||||
id: tx._id,
|
||||
description: tx.description,
|
||||
amount: tx.amount,
|
||||
category: tx.category,
|
||||
date: tx.date,
|
||||
})),
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Start server
|
||||
app.listen(3000, () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:monarch/static.dart';
|
||||
import 'package:monarch/statistics.dart';
|
||||
|
||||
class AddExpenseScreen extends StatefulWidget {
|
||||
const AddExpenseScreen({super.key});
|
||||
|
|
|
|||
8
Frontend/monarch/lib/colors.dart
Normal file
8
Frontend/monarch/lib/colors.dart
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import 'dart:ui';
|
||||
|
||||
final Color backgroundColor = const Color(0xFFF8F9FA); // Light cream/white
|
||||
final Color primaryColor = const Color(0xFF2D3436); // Deep charcoal
|
||||
final Color accentColor = const Color(0xFF00B894); // Emerald green
|
||||
final Color cardColor = const Color(0xFFFFFFFF); // Pure white
|
||||
final Color textSecondary = const Color(0xFF636E72); // Muted gray
|
||||
final Color surfaceColor = const Color(0xFFF1F2F6); // Light surface
|
||||
18
Frontend/monarch/lib/fetch_service.dart
Normal file
18
Frontend/monarch/lib/fetch_service.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
Future<List<Map<String, dynamic>>> fetchRecentTransactions() async {
|
||||
final response = await http.get(
|
||||
Uri.parse('http://192.168.1.9:3000/api/transactions/recent'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = jsonDecode(response.body)['data'];
|
||||
return List<Map<String, dynamic>>.from(data);
|
||||
} else {
|
||||
print('Failed to load recent transactions');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
129
Frontend/monarch/lib/hero_card.dart
Normal file
129
Frontend/monarch/lib/hero_card.dart
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:monarch/colors.dart';
|
||||
final double totalBalance = 5847.32;
|
||||
final double monthlyIncome = 8500.00;
|
||||
final double monthlyExpenses = 2652.68;
|
||||
|
||||
|
||||
class BalanceCardPage extends StatelessWidget {
|
||||
const BalanceCardPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [accentColor, accentColor.withOpacity(0.8)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: accentColor.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Total Balance',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.visibility_outlined,
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'\$${totalBalance.toStringAsFixed(2)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Income',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'\$${monthlyIncome.toStringAsFixed(0)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
height: 40,
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Expenses',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'\$${monthlyExpenses.toStringAsFixed(0)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
80
Frontend/monarch/lib/home_animations.dart
Normal file
80
Frontend/monarch/lib/home_animations.dart
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AnimationControllerPage extends StatefulWidget {
|
||||
final Function(Animation<Offset>) onAnimationCreated;
|
||||
final Function(Animation<double>) onFadeAnimationCreated;
|
||||
|
||||
const AnimationControllerPage({super.key, required this.onAnimationCreated, required this.onFadeAnimationCreated, required Center child});
|
||||
|
||||
@override
|
||||
_AnimationControllerPageState createState() => _AnimationControllerPageState();
|
||||
}
|
||||
|
||||
class _AnimationControllerPageState extends State<AnimationControllerPage> with TickerProviderStateMixin {
|
||||
late AnimationController _fadeController;
|
||||
late AnimationController _slideController;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fadeController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_slideController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_fadeAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).animate(CurvedAnimation(parent: _fadeController, curve: Curves.easeOut));
|
||||
|
||||
_slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 0.3),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic),
|
||||
);
|
||||
|
||||
// Start animations
|
||||
_fadeController.forward();
|
||||
_slideController.forward();
|
||||
|
||||
widget.onAnimationCreated(_slideAnimation);
|
||||
widget.onFadeAnimationCreated(_fadeAnimation);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_fadeController.dispose();
|
||||
_slideController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 200,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
// ignore_for_file: deprecated_member_use, unused_local_variable, use_super_parameters, avoid_print
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:monarch/add.dart';
|
||||
import 'package:monarch/colors.dart';
|
||||
import 'package:monarch/fetch_service.dart';
|
||||
import 'package:monarch/hero_card.dart';
|
||||
import 'package:monarch/navbar.dart';
|
||||
import 'package:monarch/quick_actions.dart';
|
||||
import 'package:monarch/transcations_recent.dart';
|
||||
//import 'package:monarch/static.dart';
|
||||
|
||||
class FinTrackHomePage extends StatefulWidget {
|
||||
|
|
@ -20,15 +21,9 @@ class FinTrackHomePage extends StatefulWidget {
|
|||
class _FinTrackHomePageState extends State<FinTrackHomePage>
|
||||
with TickerProviderStateMixin {
|
||||
// Modern color palette
|
||||
final Color backgroundColor = const Color(0xFFF8F9FA); // Light cream/white
|
||||
final Color primaryColor = const Color(0xFF2D3436); // Deep charcoal
|
||||
final Color accentColor = const Color(0xFF00B894); // Emerald green
|
||||
final Color cardColor = const Color(0xFFFFFFFF); // Pure white
|
||||
final Color textSecondary = const Color(0xFF636E72); // Muted gray
|
||||
final Color surfaceColor = const Color(0xFFF1F2F6); // Light surface
|
||||
|
||||
// List to hold recent transactions
|
||||
List<Map<String, dynamic>> recentTransactions = [];
|
||||
|
||||
|
||||
late AnimationController _fadeController;
|
||||
late AnimationController _slideController;
|
||||
|
|
@ -36,33 +31,14 @@ class _FinTrackHomePageState extends State<FinTrackHomePage>
|
|||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
// Sample data for dashboard
|
||||
final double totalBalance = 5847.32;
|
||||
final double monthlyIncome = 8500.00;
|
||||
final double monthlyExpenses = 2652.68;
|
||||
|
||||
|
||||
|
||||
Future<void> fetchRecentTransactions() async {
|
||||
final response = await http.get(
|
||||
Uri.parse('http://192.168.1.9:3000/api/transactions/recent'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = jsonDecode(response.body)['data'];
|
||||
|
||||
setState(() {
|
||||
recentTransactions = List<Map<String, dynamic>>.from(data);
|
||||
});
|
||||
} else {
|
||||
print('Failed to load recent transactions');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchRecentTransactions();
|
||||
fetchRecentTransactions().then((transactions) {
|
||||
// Do something with the transactions
|
||||
});
|
||||
|
||||
_fadeController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
vsync: this,
|
||||
|
|
@ -97,327 +73,6 @@ fetchRecentTransactions();
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildBalanceCard() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [accentColor, accentColor.withOpacity(0.8)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: accentColor.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Total Balance',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.visibility_outlined,
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'\$${totalBalance.toStringAsFixed(2)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Income',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'\$${monthlyIncome.toStringAsFixed(0)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
height: 40,
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Expenses',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white.withOpacity(0.8),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'\$${monthlyExpenses.toStringAsFixed(0)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuickActions() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AddExpenseScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(Icons.add, color: accentColor, size: 24),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Add Expense',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
HapticFeedback.lightImpact();
|
||||
// Navigate to Statistics
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.bar_chart,
|
||||
color: Colors.blue,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Statistics',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecentTransactions() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Recent Transactions',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'View All',
|
||||
style: GoogleFonts.inter(
|
||||
color: accentColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...recentTransactions.map(
|
||||
(transaction) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.shopping_bag, // You can customize based on category
|
||||
color: accentColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
transaction['description'] ?? '',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'${transaction['category']} • ${DateTime.parse(transaction['date']).toLocal().toString().split(' ')[0]}',
|
||||
style: GoogleFonts.inter(
|
||||
color: textSecondary,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'₹${transaction['amount'].toStringAsFixed(2)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: transaction['amount'] >= 0 ? accentColor : Colors.red,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _selectedIndex = 0;
|
||||
|
||||
@override
|
||||
|
|
@ -465,24 +120,17 @@ Widget _buildRecentTransactions() {
|
|||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Balance Card
|
||||
_buildBalanceCard(),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
const BalanceCardPage(),
|
||||
const SizedBox(height: 24),
|
||||
QuickActionsPage(),
|
||||
const SizedBox(height: 24),
|
||||
RecentTransactionsWidget(
|
||||
recentTransactions: fetchRecentTransactions(),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Quick Actions
|
||||
_buildQuickActions(),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Recent Transactions
|
||||
_buildRecentTransactions(),
|
||||
|
||||
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import 'dart:convert';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:monarch/budget.dart';
|
||||
import 'package:monarch/update_budget.dart';
|
||||
import 'package:monarch/homepage.dart';
|
||||
//import 'package:monarch/homepage.dart';
|
||||
import 'package:monarch/static.dart';
|
||||
import 'package:monarch/statistics.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized(); // This fixes the issue
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:monarch/add.dart';
|
||||
import 'package:monarch/homepage.dart';
|
||||
import 'package:monarch/static.dart';
|
||||
import 'package:monarch/statistics.dart';
|
||||
|
||||
class CustomNavBar extends StatelessWidget {
|
||||
final int currentIndex;
|
||||
|
|
@ -146,4 +146,4 @@ class NavItem {
|
|||
required this.index,
|
||||
required this.page,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
117
Frontend/monarch/lib/quick_actions.dart
Normal file
117
Frontend/monarch/lib/quick_actions.dart
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:monarch/add.dart';
|
||||
import 'package:monarch/colors.dart';
|
||||
|
||||
class QuickActionsPage extends StatelessWidget {
|
||||
const QuickActionsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AddExpenseScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(Icons.add, color: accentColor, size: 24),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Add Expense',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
HapticFeedback.lightImpact();
|
||||
// Navigate to Statistics
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.bar_chart,
|
||||
color: Colors.blue,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Statistics',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import 'package:fl_chart/fl_chart.dart';
|
|||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:monarch/add.dart';
|
||||
import 'package:monarch/budget.dart';
|
||||
import 'package:monarch/update_budget.dart';
|
||||
import 'package:monarch/budget_manager.dart';
|
||||
import 'package:monarch/homepage.dart';
|
||||
import 'package:monarch/navbar.dart';
|
||||
|
|
@ -203,7 +203,7 @@ class StatisticsState extends State<Statistics> with TickerProviderStateMixin {
|
|||
onSelected: (String value) async {
|
||||
final updatedBudget = await Navigator.push<double>(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const BudgetScreen()),
|
||||
MaterialPageRoute(builder: (context) => const UpdateBudget()),
|
||||
);
|
||||
if (updatedBudget != null) {
|
||||
setState(() => budget = updatedBudget);
|
||||
126
Frontend/monarch/lib/transcations_recent.dart
Normal file
126
Frontend/monarch/lib/transcations_recent.dart
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:monarch/colors.dart';
|
||||
|
||||
class RecentTransactionsWidget extends StatelessWidget {
|
||||
final Future<List<Map<String, dynamic>>> recentTransactions;
|
||||
|
||||
const RecentTransactionsWidget({super.key, required this.recentTransactions});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: cardColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: primaryColor.withOpacity(0.08),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Recent Transactions',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'View All',
|
||||
style: GoogleFonts.inter(
|
||||
color: accentColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FutureBuilder<List<Map<String, dynamic>>>(
|
||||
future: recentTransactions,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}');
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Text('No recent transactions.');
|
||||
} else {
|
||||
return Column(
|
||||
children: snapshot.data!.map(
|
||||
(transaction) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: accentColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.shopping_bag, // You can customize based on category
|
||||
color: accentColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
transaction['description'] ?? '',
|
||||
style: GoogleFonts.inter(
|
||||
color: primaryColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'${transaction['category']} • ${DateTime.parse(transaction['date']).toLocal().toString().split(' ')[0]}',
|
||||
style: GoogleFonts.inter(
|
||||
color: textSecondary,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'₹${transaction['amount'].toStringAsFixed(2)}',
|
||||
style: GoogleFonts.inter(
|
||||
color: transaction['amount'] >= 0 ? accentColor : Colors.red,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,28 +3,22 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:monarch/static.dart';
|
||||
import 'package:monarch/colors.dart';
|
||||
import 'package:monarch/statistics.dart';
|
||||
|
||||
class BudgetScreen extends StatefulWidget {
|
||||
const BudgetScreen({super.key});
|
||||
class UpdateBudget extends StatefulWidget {
|
||||
const UpdateBudget({super.key});
|
||||
|
||||
@override
|
||||
State<BudgetScreen> createState() => _BudgetScreenState();
|
||||
State<UpdateBudget> createState() => _UpdateBudgetState();
|
||||
}
|
||||
|
||||
class _BudgetScreenState extends State<BudgetScreen>
|
||||
class _UpdateBudgetState extends State<UpdateBudget>
|
||||
with TickerProviderStateMixin {
|
||||
// Constants and Controllers
|
||||
static const _animationDuration = Duration(milliseconds: 600);
|
||||
static const _fadeDuration = Duration(milliseconds: 800);
|
||||
|
||||
// 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);
|
||||
|
||||
// Controllers and Focus Nodes
|
||||
final TextEditingController amountController = TextEditingController();
|
||||
Loading…
Reference in a new issue