Files
Kifi/kifi-app/lib/features/dashboard/providers/insight_provider.dart

127 lines
3.8 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../transactions/providers/providers.dart';
import '../../transactions/data/models.dart';
class Insight {
final String title;
final String message;
final String type; // 'WARNING', 'SUCCESS', 'INFO'
Insight(this.title, this.message, this.type);
}
final insightProvider = Provider<Insight?>((ref) {
final transState = ref.watch(transactionProvider);
final budgetsState = ref.watch(budgetProvider);
final categoriesState = ref.watch(categoryProvider);
if (!transState.hasValue ||
!budgetsState.hasValue ||
!categoriesState.hasValue) {
return null;
}
final transactions = transState.value!;
final budgets = budgetsState.value!;
final categories = categoriesState.value!;
final now = DateTime.now();
final currentMonthTxs = transactions
.where((t) => t.date.month == now.month && t.date.year == now.year)
.toList();
final totalIncome = currentMonthTxs
.where((t) => t.type == 'INCOME')
.fold(0.0, (s, t) => s + t.amount);
final totalExpense = currentMonthTxs
.where((t) => t.type == 'EXPENSE')
.fold(0.0, (s, t) => s + t.amount);
// Rule 1: Expenses > Income
if (totalExpense > totalIncome && totalIncome > 0) {
return Insight(
'Spending Alert',
'You have spent more than you earned this month! (Rs. ${totalExpense.toStringAsFixed(0)} vs Rs. ${totalIncome.toStringAsFixed(0)})',
'WARNING',
);
}
// Rule 2: Over Budget Categories
for (var budget in budgets) {
final spent = currentMonthTxs
.where((t) => t.type == 'EXPENSE' && t.categoryId == budget.categoryId)
.fold(0.0, (s, t) => s + t.amount);
if (spent > budget.monthlyLimit) {
final categoryName = categories
.firstWhere(
(c) => c.id == budget.categoryId,
orElse: () => Category(id: 0, name: 'Unknown'),
)
.name;
return Insight(
'Budget Exceeded',
'You have exceeded your $categoryName budget by Rs. ${(spent - budget.monthlyLimit).toStringAsFixed(0)}.',
'WARNING',
);
} else if (spent > budget.monthlyLimit * 0.9) {
final categoryName = categories
.firstWhere(
(c) => c.id == budget.categoryId,
orElse: () => Category(id: 0, name: 'Unknown'),
)
.name;
return Insight(
'Nearing Budget Limit',
'Careful! You have used ${(spent / budget.monthlyLimit * 100).toStringAsFixed(0)}% of your $categoryName budget.',
'WARNING',
);
}
}
// Rule 3: Highest spending category warning if > 40% of total
if (totalExpense > 0) {
final Map<int, double> spentByCategory = {};
for (var t in currentMonthTxs.where((t) => t.type == 'EXPENSE')) {
if (t.categoryId != null) {
spentByCategory[t.categoryId!] =
(spentByCategory[t.categoryId!] ?? 0) + t.amount;
}
}
if (spentByCategory.isNotEmpty) {
final maxEntry = spentByCategory.entries.reduce(
(a, b) => a.value > b.value ? a : b,
);
if (maxEntry.value > totalExpense * 0.4) {
final categoryName = categories
.firstWhere(
(c) => c.id == maxEntry.key,
orElse: () => Category(id: 0, name: 'Unknown'),
)
.name;
return Insight(
'High Concentration',
'${(maxEntry.value / totalExpense * 100).toStringAsFixed(0)}% of your expenses this month went to $categoryName.',
'INFO',
);
}
}
}
// Default Positive Insight
if (totalIncome > 0 && totalExpense < totalIncome * 0.5) {
return Insight(
'Great Job!',
'You have saved over 50% of your income this month. Keep it up!',
'SUCCESS',
);
}
return Insight(
'On Track',
'Your finances are looking stable this month.',
'SUCCESS',
);
});