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/budget_form_sheet.dart'; import 'package:please_pay_me/features/budgets/budgets_controller.dart'; import 'package:please_pay_me/features/journal/journal_controller.dart'; import 'package:please_pay_me/theme/theme.dart'; import 'package:please_pay_me/ui/ui.dart'; import 'package:provider/provider.dart'; /// All envelopes: pick the active one, edit, archive or delete. class BudgetsScreen extends StatelessWidget { const BudgetsScreen({super.key}); @override Widget build(BuildContext context) { final controller = context.watch(); return CupertinoPageScaffold( backgroundColor: AppColors.of(context, AppColors.groupedBackground), child: CustomScrollView( physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), slivers: [ AppLargeNavBar( title: 'Бюджеты', trailing: CupertinoButton( padding: EdgeInsets.zero, minimumSize: Size.zero, onPressed: () => showBudgetFormSheet(context: context, controller: controller), child: const AppIcon(CupertinoIcons.add_circled, color: AppColors.accent), ), ), CupertinoSliverRefreshControl(onRefresh: () => controller.load(silent: true)), SliverToBoxAdapter( child: controller.state.map( loading: () => AppListSection( children: List.generate(3, (_) => const AppSkeletonRow()), ), error: (message) => AppErrorView(message: message, onRetry: controller.load), data: (items) => items.isEmpty ? AppEmptyState( icon: CupertinoIcons.money_rubl_circle, title: 'Бюджетов нет', message: 'Создайте первый конверт до следующей зарплаты.', actionLabel: 'Создать бюджет', onAction: () => showBudgetFormSheet(context: context, controller: controller), ) : _BudgetsList(items: items, controller: controller), ), ), const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s7)), ], ), ); } } class _BudgetsList extends StatelessWidget { const _BudgetsList({required this.items, required this.controller}); final List items; final BudgetsController controller; @override Widget build(BuildContext context) { final active = items.where((status) => !status.isExpired).toList(); final archived = items.where((status) => status.isExpired).toList(); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (active.isNotEmpty) AppListSection( header: 'Активные', footer: 'Нажмите, чтобы сделать бюджет текущим.', separatorIndent: 60, children: [ for (final status in active) _BudgetRow(status: status, controller: controller), ], ), if (archived.isNotEmpty) ...[ const SizedBox(height: AppSpacing.s5), AppListSection( header: 'Завершённые', separatorIndent: 60, children: [ for (final status in archived) _BudgetRow(status: status, controller: controller), ], ), ], ], ); } } class _BudgetRow extends StatelessWidget { const _BudgetRow({required this.status, required this.controller}); final BudgetStatus status; final BudgetsController controller; @override Widget build(BuildContext context) { final currency = status.budget.currency; final subtitle = status.isExpired ? 'Завершён ${formatShortDate(status.budget.endDate)}' : '${formatDaysLeft(status.daysLeft)} · лимит ${formatMoney(status.dailyLimit, currency: currency)}'; return AppListTile( leading: AppIconBadge( icon: status.selected ? CupertinoIcons.checkmark_alt : CupertinoIcons.tray_full, color: status.selected ? AppColors.accent : status.isExpired ? AppColors.systemGray : AppColors.systemOrange, ), title: status.budget.name, subtitle: subtitle, value: formatMoney(status.remaining, currency: currency), onTap: () => _openActions(context), ); } Future _openActions(BuildContext context) async { final journal = context.read(); final index = await showAppActionSheet( context: context, title: status.budget.name, message: 'Остаток ${formatMoney(status.remaining, currency: status.budget.currency)}', actions: [ if (!status.selected) const AppActionSheetAction(label: 'Сделать текущим', isDefault: true), const AppActionSheetAction(label: 'Редактировать'), AppActionSheetAction(label: status.budget.isActive ? 'В архив' : 'Вернуть из архива'), const AppActionSheetAction(label: 'Удалить', destructive: true), ], ); if (index == null || !context.mounted) return; final actions = [ if (!status.selected) 'select', 'edit', 'archive', 'delete', ]; switch (actions[index]) { case 'select': final error = await controller.select(status.budget.id); journal.bindBudget(controller.selected?.budget.id); await journal.load(silent: true); if (context.mounted) { await showAppToast(context, message: error ?? 'Бюджет выбран'); } case 'edit': await showBudgetFormSheet( context: context, controller: controller, initial: status, ); case 'archive': final error = await controller.setActive( status.budget.id, isActive: !status.budget.isActive, ); if (context.mounted) { await showAppToast( context, message: error ?? (status.budget.isActive ? 'Бюджет в архиве' : 'Бюджет активен'), ); } case 'delete': await _confirmDelete(context, journal); } } Future _confirmDelete(BuildContext context, JournalController journal) async { final confirmed = await showAppAlert( context: context, title: 'Удалить «${status.budget.name}»?', message: 'Вместе с бюджетом удалятся все его операции.', confirmLabel: 'Удалить', cancelLabel: 'Отмена', destructive: true, ); if (confirmed != true) return; final error = await controller.delete(status.budget.id); journal.bindBudget(controller.selected?.budget.id); await journal.load(silent: true); if (context.mounted) { await showAppToast(context, message: error ?? 'Бюджет удалён'); } } }