Files
Kifi/kifi-app/lib/features/dashboard/presentation/widgets/upcoming_dues_widget.dart

186 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:intl/intl.dart';
import '../../../transactions/providers/providers.dart';
import '../../../transactions/data/models.dart';
import '../../../../core/theme/nature_colors.dart';
class UpcomingDuesWidget extends ConsumerWidget {
const UpcomingDuesWidget({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final walletsState = ref.watch(walletProvider);
if (!walletsState.hasValue || walletsState.value == null) {
return const SizedBox.shrink();
}
final wallets = walletsState.value!;
// Filter for payables that have a due date/amount
final payables = wallets.where((w) => w.nature == 'PAYABLES').toList();
if (payables.isEmpty) {
return const SizedBox.shrink();
}
final now = DateTime.now();
// Calculate dues
final List<Map<String, dynamic>> dues = [];
for (var w in payables) {
double dueAmount = 0;
DateTime? dueDate;
String dueLabel = 'Upcoming Due';
if (w.subNature == 'CREDIT_CARD' || w.subNature == 'OD_LIMIT') {
// Balance is negative if we owe money
if (w.balance < 0) {
dueAmount = w.balance.abs();
}
} else if (w.subNature == 'LOAN_EMI' || w.subNature == 'POLICY_PREMIUM') {
dueAmount = w.fixedAmount ?? 0;
}
if (dueAmount > 0 && w.cycleDate != null) {
int year = now.year;
int month = now.month;
// Find next due date based on cycle
if (w.paymentCycle == 'MONTHLY' || w.paymentCycle == null) {
if (now.day > w.cycleDate!) {
// Due date has passed for this month, next is next month
month++;
if (month > 12) {
month = 1;
year++;
}
}
} else if (w.paymentCycle == 'YEARLY') {
// Assume cycleDate is day of current month, this is simplistic
if (now.day > w.cycleDate!) {
year++;
}
}
// Handle end of month issues (e.g. Feb 30th)
int maxDays = DateTime(year, month + 1, 0).day;
int day = w.cycleDate! > maxDays ? maxDays : w.cycleDate!;
dueDate = DateTime(year, month, day);
int daysLeft = dueDate.difference(DateTime(now.year, now.month, now.day)).inDays;
if (daysLeft == 0) {
dueLabel = 'Due Today';
} else if (daysLeft == 1) {
dueLabel = 'Due Tomorrow';
} else {
dueLabel = 'Due in $daysLeft days';
}
dues.add({
'wallet': w,
'amount': dueAmount,
'dueDate': dueDate,
'label': dueLabel,
'daysLeft': daysLeft,
});
}
}
if (dues.isEmpty) {
return const SizedBox.shrink();
}
// Sort by nearest due date
dues.sort((a, b) => (a['daysLeft'] as int).compareTo(b['daysLeft'] as int));
// Only show top 3 dues
final displayDues = dues.take(3).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(LucideIcons.calendarClock, color: Color(0xFF6C63FF), size: 20),
const SizedBox(width: 8),
Text('Upcoming Dues', style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold)),
],
),
const SizedBox(height: 12),
...displayDues.map((due) {
final w = due['wallet'] as Wallet;
final amount = due['amount'] as double;
final dueDate = due['dueDate'] as DateTime;
final label = due['label'] as String;
final daysLeft = due['daysLeft'] as int;
final isUrgent = daysLeft <= 3;
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isUrgent ? Colors.red.shade50 : Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: isUrgent ? Colors.red.shade200 : Colors.grey.shade200),
boxShadow: [
if (!isUrgent) BoxShadow(color: Colors.black.withOpacity(0.02), blurRadius: 8, offset: const Offset(0, 2))
],
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: (isUrgent ? Colors.red : NatureColors.getColor('PAYABLES')).withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
w.subNature == 'CREDIT_CARD' ? LucideIcons.creditCard :
(w.subNature == 'LOAN_EMI' ? LucideIcons.home : LucideIcons.fileText),
color: isUrgent ? Colors.red : NatureColors.getColor('PAYABLES'),
size: 20,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(w.name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15)),
const SizedBox(height: 4),
Text(
'$label${DateFormat('MMM dd').format(dueDate)}',
style: TextStyle(
color: isUrgent ? Colors.red.shade700 : Colors.grey.shade600,
fontSize: 12,
fontWeight: isUrgent ? FontWeight.bold : FontWeight.normal
),
),
],
),
),
Text(
'Rs. ${amount.toStringAsFixed(0)}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: isUrgent ? Colors.red.shade700 : Colors.black87
),
),
],
),
);
}),
const SizedBox(height: 12),
],
);
}
}