390 lines
13 KiB
Dart
390 lines
13 KiB
Dart
|
|
// ignore_for_file: use_build_context_synchronously, deprecated_member_use
|
||
|
|
|
||
|
|
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/colors.dart';
|
||
|
|
import 'package:monarch/other_pages/enviroment.dart';
|
||
|
|
|
||
|
|
class ConfirmTransactionPage extends StatelessWidget {
|
||
|
|
final Map<String, dynamic> data;
|
||
|
|
|
||
|
|
const ConfirmTransactionPage({super.key, required this.data});
|
||
|
|
|
||
|
|
Future<void> _saveTransaction(BuildContext context) async {
|
||
|
|
const String saveUrl = '${Environment.baseUrl}/api/transaction/add';
|
||
|
|
|
||
|
|
try {
|
||
|
|
final response = await http.post(
|
||
|
|
Uri.parse(saveUrl),
|
||
|
|
headers: {'Content-Type': 'application/json'},
|
||
|
|
body: jsonEncode({
|
||
|
|
'description': data['description'],
|
||
|
|
'amount': data['amount'],
|
||
|
|
'category': data['category'],
|
||
|
|
'date': data['date'] ?? DateTime.now().toIso8601String(),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
|
||
|
|
if (response.statusCode == 200) {
|
||
|
|
_showCustomSnackBar(
|
||
|
|
context: context,
|
||
|
|
message: '✅ Transaction saved successfully!',
|
||
|
|
icon: Icons.check_circle,
|
||
|
|
backgroundColor: const Color(0xFF4CAF50),
|
||
|
|
iconColor: Colors.white,
|
||
|
|
duration: const Duration(seconds: 3),
|
||
|
|
);
|
||
|
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||
|
|
} else {
|
||
|
|
throw Exception('Save failed with status: ${response.statusCode}');
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
_showCustomSnackBar(
|
||
|
|
context: context,
|
||
|
|
message: 'Error saving transaction: ${e.toString()}',
|
||
|
|
icon: Icons.error_outline,
|
||
|
|
backgroundColor: const Color(0xFFFF6B6B),
|
||
|
|
iconColor: Colors.white,
|
||
|
|
duration: const Duration(seconds: 4),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void _showCustomSnackBar({
|
||
|
|
required BuildContext context,
|
||
|
|
required String message,
|
||
|
|
required IconData icon,
|
||
|
|
required Color backgroundColor,
|
||
|
|
required Color iconColor,
|
||
|
|
required Duration duration,
|
||
|
|
}) {
|
||
|
|
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,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
backgroundColor: backgroundColor,
|
||
|
|
duration: duration,
|
||
|
|
behavior: SnackBarBehavior.floating,
|
||
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||
|
|
margin: const EdgeInsets.all(16),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _buildDetailItem({
|
||
|
|
required IconData icon,
|
||
|
|
required String label,
|
||
|
|
required String value,
|
||
|
|
bool isAmount = false,
|
||
|
|
}) {
|
||
|
|
return Row(
|
||
|
|
children: [
|
||
|
|
Container(
|
||
|
|
width: 40,
|
||
|
|
height: 40,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: accentColor.withOpacity(0.1),
|
||
|
|
borderRadius: BorderRadius.circular(10),
|
||
|
|
),
|
||
|
|
child: Icon(icon, color: accentColor, size: 20),
|
||
|
|
),
|
||
|
|
const SizedBox(width: 16),
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
label,
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: primaryColor.withOpacity(0.6),
|
||
|
|
fontSize: 12,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
letterSpacing: 0.5,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 4),
|
||
|
|
Text(
|
||
|
|
value,
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: isAmount ? Colors.green : primaryColor,
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final amount = data['amount'];
|
||
|
|
final description = data['description'];
|
||
|
|
final category = data['category'];
|
||
|
|
final date =
|
||
|
|
data['date'] != null
|
||
|
|
? DateTime.parse(data['date']).toLocal()
|
||
|
|
: DateTime.now();
|
||
|
|
|
||
|
|
return Scaffold(
|
||
|
|
backgroundColor: backgroundColor,
|
||
|
|
appBar: AppBar(
|
||
|
|
backgroundColor: Colors.transparent,
|
||
|
|
elevation: 0,
|
||
|
|
leading: Container(
|
||
|
|
margin: const EdgeInsets.all(8),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: cardColor,
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
border: Border.all(color: primaryColor.withOpacity(0.1), width: 1),
|
||
|
|
),
|
||
|
|
child: IconButton(
|
||
|
|
icon: Icon(Icons.arrow_back, color: primaryColor),
|
||
|
|
onPressed: () => Navigator.pop(context),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
title: Text(
|
||
|
|
'Confirm Transaction',
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: primaryColor,
|
||
|
|
fontSize: 20,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
centerTitle: true,
|
||
|
|
),
|
||
|
|
body: SafeArea(
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(24),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
// Header Card
|
||
|
|
Container(
|
||
|
|
width: double.infinity,
|
||
|
|
padding: const EdgeInsets.all(20),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: cardColor,
|
||
|
|
borderRadius: BorderRadius.circular(20),
|
||
|
|
boxShadow: [
|
||
|
|
BoxShadow(
|
||
|
|
color: accentColor.withOpacity(0.05),
|
||
|
|
blurRadius: 20,
|
||
|
|
offset: const Offset(0, 8),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
border: Border.all(
|
||
|
|
color: accentColor.withOpacity(0.1),
|
||
|
|
width: 1.5,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
Container(
|
||
|
|
width: 48,
|
||
|
|
height: 48,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: Colors.green.withOpacity(0.1),
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
border: Border.all(
|
||
|
|
color: Colors.green.withOpacity(0.2),
|
||
|
|
width: 1.5,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: const Icon(
|
||
|
|
Icons.check_circle_outline,
|
||
|
|
color: Colors.green,
|
||
|
|
size: 24,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(width: 16),
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
'Review Transaction',
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: primaryColor,
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Text(
|
||
|
|
'Please confirm the details below',
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: primaryColor.withOpacity(0.6),
|
||
|
|
fontSize: 14,
|
||
|
|
fontWeight: FontWeight.w500,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
|
||
|
|
const SizedBox(height: 24),
|
||
|
|
|
||
|
|
// Transaction Details
|
||
|
|
Container(
|
||
|
|
width: double.infinity,
|
||
|
|
padding: const EdgeInsets.all(24),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: backgroundColor,
|
||
|
|
borderRadius: BorderRadius.circular(20),
|
||
|
|
border: Border.all(
|
||
|
|
color: primaryColor.withOpacity(0.1),
|
||
|
|
width: 1,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: Column(
|
||
|
|
children: [
|
||
|
|
_buildDetailItem(
|
||
|
|
icon: Icons.description_outlined,
|
||
|
|
label: 'Description',
|
||
|
|
value: description?.toString() ?? 'No description',
|
||
|
|
),
|
||
|
|
|
||
|
|
const SizedBox(height: 20),
|
||
|
|
|
||
|
|
_buildDetailItem(
|
||
|
|
icon: Icons.payments_outlined,
|
||
|
|
label: 'Amount',
|
||
|
|
value: '₹$amount',
|
||
|
|
isAmount: true,
|
||
|
|
),
|
||
|
|
|
||
|
|
const SizedBox(height: 20),
|
||
|
|
|
||
|
|
_buildDetailItem(
|
||
|
|
icon: Icons.category_outlined,
|
||
|
|
label: 'Category',
|
||
|
|
value: category?.toString() ?? 'Uncategorized',
|
||
|
|
),
|
||
|
|
|
||
|
|
const SizedBox(height: 20),
|
||
|
|
|
||
|
|
_buildDetailItem(
|
||
|
|
icon: Icons.calendar_today_outlined,
|
||
|
|
label: 'Date',
|
||
|
|
value: '${date.day}/${date.month}/${date.year}',
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
|
||
|
|
const Spacer(),
|
||
|
|
|
||
|
|
// Action Buttons
|
||
|
|
Column(
|
||
|
|
children: [
|
||
|
|
// Confirm Button
|
||
|
|
Container(
|
||
|
|
width: double.infinity,
|
||
|
|
height: 56,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
gradient: LinearGradient(
|
||
|
|
colors: [Colors.green, Colors.green.shade600],
|
||
|
|
begin: Alignment.topLeft,
|
||
|
|
end: Alignment.bottomRight,
|
||
|
|
),
|
||
|
|
borderRadius: BorderRadius.circular(16),
|
||
|
|
boxShadow: [
|
||
|
|
BoxShadow(
|
||
|
|
color: Colors.green.withOpacity(0.3),
|
||
|
|
blurRadius: 12,
|
||
|
|
offset: const Offset(0, 4),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
child: Material(
|
||
|
|
color: Colors.transparent,
|
||
|
|
borderRadius: BorderRadius.circular(16),
|
||
|
|
child: InkWell(
|
||
|
|
borderRadius: BorderRadius.circular(16),
|
||
|
|
onTap: () => _saveTransaction(context),
|
||
|
|
child: Center(
|
||
|
|
child: Row(
|
||
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
|
children: [
|
||
|
|
const Icon(
|
||
|
|
Icons.check,
|
||
|
|
color: Colors.white,
|
||
|
|
size: 20,
|
||
|
|
),
|
||
|
|
const SizedBox(width: 8),
|
||
|
|
Text(
|
||
|
|
'Confirm & Save Transaction',
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: Colors.white,
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: FontWeight.w700,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
|
||
|
|
// Cancel Button
|
||
|
|
Container(
|
||
|
|
width: double.infinity,
|
||
|
|
height: 48,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: backgroundColor,
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
border: Border.all(
|
||
|
|
color: primaryColor.withOpacity(0.2),
|
||
|
|
width: 1,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: TextButton(
|
||
|
|
onPressed: () => Navigator.pop(context),
|
||
|
|
child: Text(
|
||
|
|
'Edit or Cancel',
|
||
|
|
style: GoogleFonts.inter(
|
||
|
|
color: primaryColor.withOpacity(0.7),
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|