import 'package:flutter/cupertino.dart'; import 'package:please_pay_me/core/format/formatters.dart'; import 'package:please_pay_me/data/models/budget.dart'; import 'package:please_pay_me/features/budgets/budgets_controller.dart'; import 'package:please_pay_me/theme/theme.dart'; import 'package:please_pay_me/ui/ui.dart'; /// Create / edit form for an envelope. class BudgetFormSheet extends StatefulWidget { const BudgetFormSheet({super.key, required this.onSubmit, this.initial}); final BudgetStatus? initial; final Future Function({ required String name, required double totalAmount, required DateTime startDate, required DateTime endDate, required bool resetExpenses, }) onSubmit; @override State createState() => _BudgetFormSheetState(); } class _BudgetFormSheetState extends State { late final _nameController = TextEditingController( text: widget.initial?.budget.name ?? '', ); late final _amountController = TextEditingController( text: widget.initial == null ? '' : widget.initial!.budget.totalAmount.toStringAsFixed(0), ); late DateTime _startDate = widget.initial?.budget.startDate ?? DateTime.now(); late DateTime _endDate = widget.initial?.budget.endDate ?? DateTime.now().add(const Duration(days: 14)); bool _resetExpenses = false; bool _saving = false; String? _error; bool get _isEditing => widget.initial != null; @override void dispose() { _nameController.dispose(); _amountController.dispose(); super.dispose(); } Future _submit() async { final name = _nameController.text.trim(); final amount = double.tryParse( _amountController.text.trim().replaceAll(',', '.').replaceAll(' ', ''), ); if (name.isEmpty) { setState(() => _error = 'Введите название бюджета'); return; } if (amount == null || amount <= 0) { setState(() => _error = 'Введите сумму больше нуля'); return; } if (!_endDate.isAfter(_startDate)) { setState(() => _error = 'Дата окончания должна быть позже начала'); return; } setState(() { _saving = true; _error = null; }); final error = await widget.onSubmit( name: name, totalAmount: amount, startDate: _startDate, endDate: _endDate, resetExpenses: _resetExpenses, ); if (!mounted) return; if (error != null) { setState(() { _saving = false; _error = error; }); return; } Navigator.of(context).pop(true); } @override Widget build(BuildContext context) { return CupertinoPageScaffold( backgroundColor: AppColors.of(context, AppColors.groupedBackground), navigationBar: AppNavBar( title: _isEditing ? 'Бюджет' : 'Новый бюджет', leading: CupertinoButton( padding: EdgeInsets.zero, minimumSize: Size.zero, onPressed: _saving ? null : () => Navigator.of(context).pop(false), child: const AppText.body('Отмена', color: AppColors.accent), ), ), child: SafeArea( child: ListView( padding: const EdgeInsets.only(top: AppSpacing.s4, bottom: AppSpacing.s6), children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.gutter), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ AppTextField( label: 'Название', placeholder: 'До аванса', controller: _nameController, enabled: !_saving, ), const SizedBox(height: AppSpacing.s4), AppTextField( label: 'Сумма на период', placeholder: '0', controller: _amountController, enabled: !_saving, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), ], ), ), const SizedBox(height: AppSpacing.s5), AppListSection( header: 'Период', footer: 'Дневной лимит = остаток ÷ количество оставшихся дней.', children: [ AppListTile( title: 'Начало', value: formatShortDate(_startDate), onTap: _saving ? null : () => _pickDate(isStart: true), ), AppListTile( title: 'Окончание', value: formatShortDate(_endDate), onTap: _saving ? null : () => _pickDate(isStart: false), ), ], ), if (_isEditing) ...[ const SizedBox(height: AppSpacing.s5), AppListSection( footer: 'Сбросить траты — обнулить потраченное по этому бюджету.', children: [ AppSwitchRow( title: 'Сбросить траты', value: _resetExpenses, onChanged: _saving ? null : (v) => setState(() => _resetExpenses = v), ), ], ), ], if (_error != null) Padding( padding: const EdgeInsets.fromLTRB( AppSpacing.gutter, AppSpacing.s3, AppSpacing.gutter, 0, ), child: AppText.footnote(_error!, color: AppColors.systemRed), ), const SizedBox(height: AppSpacing.s5), Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.gutter), child: AppButton( label: _isEditing ? 'Сохранить' : 'Создать бюджет', loading: _saving, onPressed: _submit, ), ), ], ), ), ); } Future _pickDate({required bool isStart}) async { final picked = await showAppDatePicker( context: context, initialDate: isStart ? _startDate : _endDate, minimumDate: isStart ? null : _startDate, ); if (picked == null || !mounted) return; setState(() { if (isStart) { _startDate = picked; if (!_endDate.isAfter(_startDate)) { _endDate = _startDate.add(const Duration(days: 14)); } } else { _endDate = picked; } }); } } /// Opens the sheet wired to [BudgetsController]. Future showBudgetFormSheet({ required BuildContext context, required BudgetsController controller, BudgetStatus? initial, }) async { final saved = await showAppFormSheet( context: context, builder: (_) => BudgetFormSheet( initial: initial, onSubmit: ({ required name, required totalAmount, required startDate, required endDate, required resetExpenses, }) { if (initial == null) { return controller.create( name: name, totalAmount: totalAmount, endDate: endDate, startDate: startDate, ); } return controller.update( budgetId: initial.budget.id, name: name, totalAmount: totalAmount, endDate: endDate, startDate: startDate, resetExpenses: resetExpenses, ); }, ), ); if (saved == true && context.mounted) { await showAppToast( context, message: initial == null ? 'Бюджет создан' : 'Бюджет обновлён', ); } }