Files
Kifi/kifi-app/lib/features/dashboard/providers/insight_provider.dart
2026-08-16 20:13:21 +05:30

101 lines
3.6 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'
);
});