budget screen: dynamic month display, editable budget amounts (tap to set)
This commit is contained in:
parent
e3baad4dfa
commit
da74308bad
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../providers/db_provider.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import '../data/db.dart';
|
||||
|
||||
class BudgetScreen extends ConsumerWidget {
|
||||
const BudgetScreen({super.key});
|
||||
|
|
@ -10,6 +12,9 @@ class BudgetScreen extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final groups = ref.watch(categoryGroupsProvider).valueOrNull ?? [];
|
||||
final catsByGroup = ref.watch(categoriesByGroupProvider);
|
||||
final now = DateTime.now();
|
||||
final months = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];
|
||||
final monthLabel = '${months[now.month]} ${now.year}';
|
||||
|
||||
return ListView(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 90),
|
||||
|
|
@ -22,7 +27,7 @@ class BudgetScreen extends ConsumerWidget {
|
|||
children: [
|
||||
Text('Budget du mois', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Text('Juillet 2026', style: Theme.of(context).textTheme.bodySmall),
|
||||
Text(monthLabel, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -106,6 +111,51 @@ class _CategoryRow extends ConsumerWidget {
|
|||
final remaining = ref.watch(categoryRemainingProvider(categoryId));
|
||||
final pct = budgeted > 0 ? ((budgeted + spent) / budgeted).clamp(0.0, 1.0) : 0.0;
|
||||
|
||||
void _editBudget() async {
|
||||
final month = ref.read(currentMonthProvider);
|
||||
final db = ref.read(dbProvider);
|
||||
final ctrl = TextEditingController(text: budgeted > 0 ? budgeted.toStringAsFixed(2) : '');
|
||||
|
||||
final newAmount = await showDialog<double>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text('Budget : ${category.name}'),
|
||||
content: TextField(
|
||||
controller: ctrl,
|
||||
decoration: const InputDecoration(labelText: 'Montant', hintText: '0,00'),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
autofocus: true,
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('Annuler')),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
final v = double.tryParse(ctrl.text.trim().replaceAll(',', '.'));
|
||||
if (v != null) Navigator.pop(ctx, v);
|
||||
},
|
||||
child: const Text('Enregistrer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
ctrl.dispose();
|
||||
|
||||
if (newAmount == null) return;
|
||||
final existing = await (db.select(db.budgets)
|
||||
..where((b) => b.categoryId.equals(categoryId))
|
||||
..where((b) => b.month.equals(month)))
|
||||
.get();
|
||||
if (existing.isNotEmpty) {
|
||||
await db.update(db.budgets).replace(existing.first.copyWith(budgeted: newAmount));
|
||||
} else {
|
||||
await db.into(db.budgets).insert(BudgetsCompanion.insert(
|
||||
categoryId: categoryId,
|
||||
month: month,
|
||||
budgeted: Value(newAmount),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, bottom: 4),
|
||||
child: Card(
|
||||
|
|
@ -144,8 +194,14 @@ class _CategoryRow extends ConsumerWidget {
|
|||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Text('Budget: ${formatCurrency(budgeted)}',
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
GestureDetector(
|
||||
onTap: _editBudget,
|
||||
child: Text('Budget: ${formatCurrency(budgeted)}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text('Dépensé: ${formatCurrency(spent.abs())}',
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
|
|
|
|||
Loading…
Reference in a new issue