Done Category, Product Catalogue, Customer, Vendor, Purchase Invoice Module
This commit is contained in:
@@ -38,11 +38,21 @@ class StatisticsTab extends StatefulWidget {
|
||||
class _StatisticsTabState extends State<StatisticsTab> {
|
||||
String _selectedNature = 'EXPENSE';
|
||||
|
||||
Widget _buildTotalCard(String title, double amount, Color color, IconData icon) {
|
||||
Widget _buildTotalCard(
|
||||
String title,
|
||||
double amount,
|
||||
Color color,
|
||||
IconData icon,
|
||||
) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
return Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16), side: BorderSide(color: Theme.of(context).dividerColor.withOpacity(isDark ? 0.1 : 0.2))),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor.withOpacity(isDark ? 0.1 : 0.2),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
@@ -51,13 +61,35 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(backgroundColor: color.withOpacity(0.1), radius: 16, child: Icon(icon, color: color, size: 16)),
|
||||
CircleAvatar(
|
||||
backgroundColor: color.withOpacity(0.1),
|
||||
radius: 16,
|
||||
child: Icon(icon, color: color, size: 16),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(title, style: const TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text('Rs. ${amount.toStringAsFixed(0)}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: color)),
|
||||
Text(
|
||||
'Rs. ${amount.toStringAsFixed(0)}',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -67,13 +99,27 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Compute totals
|
||||
final totalIncome = widget.transactions.where((t) => t.type == 'INCOME' || (t.type == 'TRANSFER' && t.fromWalletId == null)).fold(0.0, (s, t) => s + t.amount);
|
||||
final totalExpense = widget.transactions.where((t) => t.type == 'EXPENSE' || (t.type == 'TRANSFER' && t.toWalletId == null)).fold(0.0, (s, t) => s + t.amount);
|
||||
final totalInvestment = widget.transactions.where((t) => t.type == 'INVESTMENT').fold(0.0, (s, t) => s + t.amount);
|
||||
|
||||
final totalIncome = widget.transactions
|
||||
.where(
|
||||
(t) =>
|
||||
t.type == 'INCOME' ||
|
||||
(t.type == 'TRANSFER' && t.fromWalletId == null),
|
||||
)
|
||||
.fold(0.0, (s, t) => s + t.amount);
|
||||
final totalExpense = widget.transactions
|
||||
.where(
|
||||
(t) =>
|
||||
t.type == 'EXPENSE' ||
|
||||
(t.type == 'TRANSFER' && t.toWalletId == null),
|
||||
)
|
||||
.fold(0.0, (s, t) => s + t.amount);
|
||||
final totalInvestment = widget.transactions
|
||||
.where((t) => t.type == 'INVESTMENT')
|
||||
.fold(0.0, (s, t) => s + t.amount);
|
||||
|
||||
double totalPayablesPeriod = 0.0;
|
||||
double totalReceivablesPeriod = 0.0;
|
||||
|
||||
|
||||
for (var t in widget.transactions) {
|
||||
if (t.toWalletId != null) {
|
||||
final w = widget.wallets.where((w) => w.id == t.toWalletId);
|
||||
@@ -124,13 +170,19 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
),
|
||||
if (widget.selectedFilter == 'Custom')
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Text(
|
||||
'${widget.startDate.day} ${widget.startDate.month} - ${widget.endDate.day} ${widget.endDate.month}',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.grey),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
@@ -139,9 +191,18 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Analytics', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
|
||||
const Text(
|
||||
'Analytics',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: barColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -150,13 +211,31 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
value: _selectedNature,
|
||||
underline: const SizedBox(),
|
||||
icon: Icon(Icons.arrow_drop_down, color: barColor),
|
||||
style: TextStyle(color: barColor, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
color: barColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
items: const [
|
||||
DropdownMenuItem(value: 'EXPENSE', child: Text('Expense')),
|
||||
DropdownMenuItem(value: 'INCOME', child: Text('Income')),
|
||||
DropdownMenuItem(value: 'PAYABLES', child: Text('Payables')),
|
||||
DropdownMenuItem(value: 'RECEIVABLES', child: Text('Receivables')),
|
||||
DropdownMenuItem(value: 'INVESTMENTS', child: Text('Investments')),
|
||||
DropdownMenuItem(
|
||||
value: 'EXPENSE',
|
||||
child: Text('Expense'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'INCOME',
|
||||
child: Text('Income'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'PAYABLES',
|
||||
child: Text('Payables'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'RECEIVABLES',
|
||||
child: Text('Receivables'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'INVESTMENTS',
|
||||
child: Text('Investments'),
|
||||
),
|
||||
],
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
@@ -168,19 +247,33 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
|
||||
if (widget.transactions.isEmpty)
|
||||
Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24), side: BorderSide(color: Theme.of(context).dividerColor.withOpacity(Theme.of(context).brightness == Brightness.dark ? 0.1 : 0.2))),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor.withOpacity(
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? 0.1
|
||||
: 0.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: Center(child: Text('No transactions in this period.', style: TextStyle(color: Colors.grey))),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'No transactions in this period.',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
else ...[
|
||||
DayWiseSpendingChart(
|
||||
transactions: widget.transactions,
|
||||
transactions: widget.transactions,
|
||||
wallets: widget.wallets,
|
||||
selectedNature: _selectedNature,
|
||||
startDate: widget.startDate,
|
||||
@@ -188,16 +281,19 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CategorySpendingChart(
|
||||
transactions: widget.transactions,
|
||||
transactions: widget.transactions,
|
||||
categories: widget.categories,
|
||||
selectedNature: _selectedNature,
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
const SizedBox(height: 32),
|
||||
const Text('Summary Totals', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
|
||||
const Text(
|
||||
'Summary Totals',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
|
||||
GridView.count(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 12,
|
||||
@@ -206,11 +302,36 @@ class _StatisticsTabState extends State<StatisticsTab> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
childAspectRatio: 1.5,
|
||||
children: [
|
||||
_buildTotalCard('Income', totalIncome, NatureColors.getColor('INCOME'), LucideIcons.arrowDown),
|
||||
_buildTotalCard('Expense', totalExpense, NatureColors.getColor('EXPENSE'), LucideIcons.arrowUp),
|
||||
_buildTotalCard('Investment', totalInvestment, NatureColors.getColor('INVESTMENTS'), LucideIcons.trendingUp),
|
||||
_buildTotalCard('Payables', totalPayablesPeriod, NatureColors.getColor('PAYABLES'), LucideIcons.alertCircle),
|
||||
_buildTotalCard('Receivables', totalReceivablesPeriod, NatureColors.getColor('RECEIVABLES'), LucideIcons.arrowDownLeft),
|
||||
_buildTotalCard(
|
||||
'Income',
|
||||
totalIncome,
|
||||
NatureColors.getColor('INCOME'),
|
||||
LucideIcons.arrowDown,
|
||||
),
|
||||
_buildTotalCard(
|
||||
'Expense',
|
||||
totalExpense,
|
||||
NatureColors.getColor('EXPENSE'),
|
||||
LucideIcons.arrowUp,
|
||||
),
|
||||
_buildTotalCard(
|
||||
'Investment',
|
||||
totalInvestment,
|
||||
NatureColors.getColor('INVESTMENTS'),
|
||||
LucideIcons.trendingUp,
|
||||
),
|
||||
_buildTotalCard(
|
||||
'Payables',
|
||||
totalPayablesPeriod,
|
||||
NatureColors.getColor('PAYABLES'),
|
||||
LucideIcons.alertCircle,
|
||||
),
|
||||
_buildTotalCard(
|
||||
'Receivables',
|
||||
totalReceivablesPeriod,
|
||||
NatureColors.getColor('RECEIVABLES'),
|
||||
LucideIcons.arrowDownLeft,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
Reference in New Issue
Block a user