import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/db_provider.dart'; import '../theme/app_theme.dart'; class BudgetScreen extends ConsumerWidget { const BudgetScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final groups = ref.watch(categoryGroupsProvider).valueOrNull ?? []; final catsByGroup = ref.watch(categoriesByGroupProvider); return ListView( padding: const EdgeInsets.fromLTRB(16, 8, 16, 90), children: [ Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Budget du mois', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 4), Text('Juillet 2026', style: Theme.of(context).textTheme.bodySmall), ], ), ), ), const SizedBox(height: 16), ...groups.map((group) { final cats = catsByGroup[group.id] ?? []; if (cats.isEmpty) return const SizedBox.shrink(); return _CategoryGroupTile(group: group, categoryIds: cats.map((c) => c.id).toList()); }), ], ); } } class _CategoryGroupTile extends ConsumerWidget { final CategoryGroup group; final List categoryIds; const _CategoryGroupTile({required this.group, required this.categoryIds}); @override Widget build(BuildContext context, WidgetRef ref) { final expanded = ref.watch(_expandedGroupProvider(group.id)); final cats = ref.watch(categoriesByGroupProvider)[group.id] ?? []; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ InkWell( onTap: () => ref.read(_expandedGroupProvider(group.id).notifier).toggle(), borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4), child: Row( children: [ Icon( expanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, size: 18, color: Theme.of(context).colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), Text(group.name, style: Theme.of(context) .textTheme .titleSmall ?.copyWith(fontWeight: FontWeight.w600)), const Spacer(), Text( formatCurrency( cats.fold(0.0, (s, c) => s + ref.watch(categoryRemainingProvider(c.id))), ), style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), if (expanded) ...cats.map((c) => _CategoryRow(categoryId: c.id)), const Divider(height: 1), ], ); } } class _CategoryRow extends ConsumerWidget { final int categoryId; const _CategoryRow({required this.categoryId}); @override Widget build(BuildContext context, WidgetRef ref) { final cats = ref.watch(categoriesByGroupProvider); final category = cats.values .expand((list) => list) .firstWhere((c) => c.id == categoryId, orElse: () => throw StateError('missing')); final budgeted = ref.watch(monthlyBudgetProvider(categoryId)) ?? 0.0; final spent = ref.watch(categorySpentProvider(categoryId)); final remaining = ref.watch(categoryRemainingProvider(categoryId)); final pct = budgeted > 0 ? ((budgeted + spent) / budgeted).clamp(0.0, 1.0) : 0.0; return Padding( padding: const EdgeInsets.only(left: 8, bottom: 4), child: Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text(category.name, style: Theme.of(context).textTheme.bodyMedium), ), Text( formatCurrency(remaining), style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, color: remaining >= 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error, ), ), ], ), const SizedBox(height: 4), ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: pct, backgroundColor: Theme.of(context).colorScheme.outlineVariant, valueColor: AlwaysStoppedAnimation( remaining >= 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error, ), minHeight: 4, ), ), const SizedBox(height: 2), Row( children: [ Text('Budget: ${formatCurrency(budgeted)}', style: Theme.of(context).textTheme.bodySmall), const SizedBox(width: 12), Text('Dépensé: ${formatCurrency(spent.abs())}', style: Theme.of(context).textTheme.bodySmall), ], ), ], ), ), ), ); } } class _ExpandedGroupNotifier extends StateNotifier { _ExpandedGroupNotifier() : super(true); void toggle() => state = !state; } final _expandedGroupProvider = StateNotifierProvider.family<_ExpandedGroupNotifier, bool, int>( (ref, id) => _ExpandedGroupNotifier());