import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:home/src/presentation/wallet_management_layout.dart'; import 'package:navigation/navigation.dart'; import 'package:sf_localizations/sf_localizations.dart'; import 'package:sf_shared/sf_shared.dart'; import '../../card_colors.dart'; import '../child_wallet/child_data_provider.dart'; import 'goals_view_model.dart'; class GoalsScreen extends ConsumerWidget { final String childId; final NavigationContract navigation; const GoalsScreen({ super.key, required this.childId, required this.navigation, }); @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); final viewState = ref.watch(goalsViewModelProvider(childId)); final cardStatus = ref.watch(childDataProvider(childId)).cardStatus; if (viewState.isLoading) { return const Scaffold(body: Center(child: AppLoadingIndicator())); } if (viewState.errorMessage.isNotEmpty) { return Scaffold( body: Center(child: Text('Error: ${viewState.errorMessage}')), ); } final childProfile = viewState.childProfile!; final childName = childProfile.firstName; final availableBalance = viewState.childWallet?.authorizedBalance ?? 0; return WalletManagementLayout( childName: childName, balance: availableBalance, cardColors: cardColorsFor(theme: theme, carrierGenre: viewState.device?.carrierGenre, cardStatus: cardStatus), navigation: navigation, children: [ SectionContainer( backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), spacing: 24, children: [ Row( spacing: 8, children: [ Text( context.translate(I18n.goalsTitle), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), Icon(Icons.workspace_premium), Spacer(), Text( context.translate(I18n.goalsOnlyFullPlan), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ], ), Text( context.translate(I18n.goalsTeachSavings), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ], ), SavingsBlock( fullPlan: viewState.fullPlan, savings: viewState.savingsGoals, ), TasksBlock( fullPlan: viewState.fullPlan, tasks: viewState.tasks, childId: childId, ), ], footer: Container(), ); } } class SavingsBlock extends ConsumerStatefulWidget { final bool fullPlan; final List savings; @override const SavingsBlock({ super.key, required this.fullPlan, required this.savings, }); @override ConsumerState createState() => SavingsBlockState(); } class SavingsBlockState extends ConsumerState { late List showEdit; late List showAdd; late bool showNew; @override void initState() { super.initState(); showEdit = widget.savings.map((_) => false).toList(); showAdd = widget.savings.map((_) => false).toList(); showNew = false; } @override Widget build(BuildContext context) { final theme = ref.watch(themePortProvider); var emptyBlock = ({fullPlan}) => Container( padding: EdgeInsets.all(24), decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundPrimary), borderRadius: BorderRadius.all(Radius.circular(24)), border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary)), ), child: Stack( children: [ Align( alignment: Alignment.topRight, child: SvgPicture.asset("assets/images/ui/ahorros.svg"), ), Column( spacing: 24, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsSavings), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), Align( alignment: Alignment.topLeft, child: SizedBox( width: 200, child: Text( context.translate(I18n.goalsTeachSavings), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ), ), if (fullPlan) Column( spacing: 24, children: [ Align( alignment: Alignment.topLeft, child: SecondaryButton( onPressed: () => setState(() { showNew = true; }), text: context.translate(I18n.goalsCreateSavingsGoal), radius: 8, height: 44, size: 14, width: 230, ), ), Align( alignment: Alignment.topLeft, child: TextButton( onPressed: () => {}, style: ButtonStyle( padding: WidgetStatePropertyAll(EdgeInsets.zero), ), child: Text( context.translate(I18n.goalsViewSavingsStatus), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), ), ], ), if (!fullPlan) TextButton( onPressed: () => {}, child: Row( spacing: 4, children: [ Icon(Icons.workspace_premium, size: 16), Text(context.translate(I18n.goalsSubscribeFullPlan)), ], ), ), ], ), ], ), ); var editBlock = ({create, index}) => Column( spacing: 24, children: [ CustomTextField( hint: create ? context.translate(I18n.goalsSavingsNameHint) : widget.savings[index].name, label: context.translate(I18n.goalsSavingsReasonLabel), lines: create ? 2 : 1, ), CustomTextField( hint: "30\u20AC", label: context.translate(I18n.goalsSavingsAmountLabel), keyboardType: TextInputType.number, ), CheckboxListTile( value: false, onChanged: (_) => {}, checkboxScaleFactor: 2, controlAffinity: ListTileControlAffinity.leading, activeColor: theme.getColorFor(ThemeCode.buttonPrimary), contentPadding: EdgeInsets.zero, title: Text( context.translate(I18n.goalsAutoSavingsFromAllowance), style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), CustomTextField( hint: "2\u20AC", label: context.translate(I18n.goalsSelectAmount), keyboardType: TextInputType.number, ), CheckboxListTile( value: false, onChanged: (_) => {}, checkboxScaleFactor: 2, controlAffinity: ListTileControlAffinity.leading, activeColor: theme.getColorFor(ThemeCode.buttonPrimary), contentPadding: EdgeInsets.zero, title: Text( context.translate(I18n.goalsAutoSendOnGoal), style: TextStyle(fontSize: 16, letterSpacing: 0), ), ), Column( spacing: 10, children: [ Text( context.translate(I18n.goalsDefaultMessagePrefix), style: TextStyle(fontSize: 16, letterSpacing: 0), ), Text( context.translate(I18n.goalsSavingsDefaultMessage), style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ], ), Column( spacing: 8, children: [ CustomTextField( hint: context.translate(I18n.allowanceMessageHint), label: context.translate(I18n.goalsWriteOtherMessage), lines: 4, length: 150, ), Row( spacing: 4, children: [ Icon(Icons.info_outline, size: 16), Text( context.translate(I18n.allowanceMaxChars, args: {'count': '150'}), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ], ), ], ), Column( spacing: 24, children: [ PrimaryButton( onPressed: () => {}, text: context.translate(I18n.goalsSaveChanges), color: theme.getColorFor(ThemeCode.buttonPrimary), ), TextButton( onPressed: () { if (create) { setState(() { showNew = false; }); } else { setState(() { showEdit[index] = false; }); } }, child: Text( context.translate(I18n.cancel), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), ], ), ], ); if (widget.fullPlan) { if (showNew) { return Container( padding: EdgeInsets.fromLTRB(24, 24, 24, 32), decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundSecondary), border: Border.all(color: theme.getColorFor(ThemeCode.textPrimary)), borderRadius: BorderRadius.all(Radius.circular(24)), ), child: Column( spacing: 24, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsSavings), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), Text( context.translate(I18n.goalsTeachSavings), style: TextStyle(fontSize: 14, letterSpacing: 0), ), Container( padding: EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(24)), color: theme.getColorFor(ThemeCode.backgroundPrimary), ), child: editBlock(create: true, index: null), ), ], ), ); } else { if (widget.savings.isNotEmpty) { return Container( padding: EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(24)), color: theme.getColorFor(ThemeCode.backgroundPrimary), ), child: Column( spacing: 24, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsSavings), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), ...List.generate( widget.savings.length, (int index) => Container( decoration: BoxDecoration( border: BoxBorder.all( color: theme.getColorFor(ThemeCode.textPrimary), ), color: theme.getColorFor(ThemeCode.backgroundPrimary), borderRadius: BorderRadius.all(Radius.circular(24)), ), padding: EdgeInsets.all(16), child: Column( spacing: 24, children: [ Row( spacing: 16, children: [ Expanded( child: Column( spacing: 8, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsSavingsFor), textAlign: TextAlign.left, style: TextStyle( fontSize: 16, letterSpacing: 0, ), ), ), Text( widget.savings[index].name, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ], ), ), Container( decoration: BoxDecoration( color: theme.getColorFor( ThemeCode.backgroundTertiary, ), borderRadius: BorderRadius.all( Radius.circular(16), ), ), padding: EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), child: Row( spacing: 8, children: [ Icon( Icons.account_balance, size: 24, color: theme.getColorFor( ThemeCode.buttonPrimary, ), ), MoneyText( text: "${widget.savings[index].goal}\u20AC", size: 30, secondarySize: 14, color: theme.getColorFor( ThemeCode.buttonPrimary, ), ), ], ), ), ], ), if (index == 0) Column( spacing: 8, children: [ ProgressBar( max: widget.savings[index].goal + 0.0, value: widget.savings[index].saved, height: 64, textSize: 40, textSecondarySize: 24, backgroundColor: theme.getColorFor( ThemeCode.backgroundTertiary, ), foregroundColor: theme.getColorFor( ThemeCode.buttonPrimary, ), textColor: theme.getColorFor( ThemeCode.textSecondary, ), ), Center(child: Text(context.translate(I18n.goalsSaved))), if (!showAdd[index]) TextButton( style: ButtonStyle( padding: WidgetStatePropertyAll( EdgeInsets.zero, ), ), onPressed: () => setState(() { showAdd[index] = true; }), child: Text( context.translate(I18n.goalsAddExtraMoney), textAlign: TextAlign.left, style: TextStyle( fontSize: 16, letterSpacing: 0, ), ), ), if (showAdd[index]) CustomTextField( hint: "5\u20AC", keyboardType: TextInputType.number, ), ], ), if (!showEdit[index]) Row( children: [ TextButton( style: ButtonStyle( padding: WidgetStatePropertyAll( EdgeInsets.zero, ), ), onPressed: () => setState(() { showEdit[index] = true; }), child: Row( spacing: 4, children: [ Icon(Icons.edit_outlined, size: 24), Text( context.translate(I18n.homeEdit), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ], ), ), Spacer(), TextButton( style: ButtonStyle( padding: WidgetStatePropertyAll( EdgeInsets.zero, ), ), onPressed: () => {}, child: Row( spacing: 4, children: [ Icon( Icons.delete_outline_outlined, size: 24, ), Text( context.translate(I18n.goalsDelete), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ], ), ), ], ), if (showEdit[index]) editBlock(create: false, index: index), ], ), ), ), TextButton( onPressed: () => setState(() { showNew = true; }), child: Row( spacing: 4, children: [ Spacer(), Icon(Icons.account_balance, size: 24), Text( context.translate(I18n.goalsCreateAnotherSavings), style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), Spacer(), ], ), ), ], ), ); } else { return emptyBlock(fullPlan: true); } } } else { return emptyBlock(fullPlan: false); } } } class TasksBlock extends ConsumerStatefulWidget { final bool fullPlan; final List tasks; final String childId; @override const TasksBlock({ super.key, required this.fullPlan, required this.tasks, required this.childId, }); @override ConsumerState createState() => TasksBlockState(); } class TasksBlockState extends ConsumerState { @override Widget build(BuildContext context) { final theme = ref.watch(themePortProvider); final viewModel = ref.read(goalsViewModelProvider(widget.childId).notifier); final emptyBlock = ({fullPlan}) => Container( padding: EdgeInsets.all(24), decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundPrimary), borderRadius: BorderRadius.all(Radius.circular(24)), border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary)), ), child: Stack( children: [ Align( alignment: Alignment.topRight, child: SvgPicture.asset("assets/images/ui/tareas.svg"), ), Column( spacing: 24, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsTasks), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), Align( alignment: Alignment.topLeft, child: SizedBox( width: 200, child: Text( context.translate(I18n.goalsTasksDescription), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ), ), if (fullPlan) Align( alignment: Alignment.topLeft, child: SecondaryButton( onPressed: () => {}, text: context.translate(I18n.goalsCreateTaskList), size: 14, width: 190, height: 44, ), ), if (!fullPlan) TextButton( onPressed: () => {}, child: Row( spacing: 4, children: [ Icon(Icons.workspace_premium, size: 16), Text(context.translate(I18n.goalsSubscribeFullPlan)), ], ), ), ], ), ], ), ); if (widget.fullPlan) { if (widget.tasks.isNotEmpty) { return Container( padding: EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(24)), border: BoxBorder.all( color: theme.getColorFor(ThemeCode.textPrimary), ), color: theme.getColorFor(ThemeCode.backgroundSecondary), ), child: Column( spacing: 24, children: [ Column( spacing: 8, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsTasks), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), Text( context.translate(I18n.goalsTasksDescription), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ], ), ...List.generate( widget.tasks.length, (int i) => Container( decoration: BoxDecoration( border: BoxBorder.fromLTRB( top: BorderSide( color: theme.getColorFor(ThemeCode.buttonPrimary), width: 8, ), ), borderRadius: BorderRadius.all(Radius.circular(24)), color: theme.getColorFor(ThemeCode.backgroundPrimary), ), padding: EdgeInsets.all(24), child: Column( spacing: 16, children: [ Row( children: [ Text( context.translate(I18n.goalsTaskList), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), Spacer(), Text( "10 oct - 17 oct", style: TextStyle(fontSize: 14, letterSpacing: 0), ), ], ), ...List.generate( widget.tasks[i].subtasks.length, (int j) => CheckboxListTile( contentPadding: EdgeInsets.zero, checkboxScaleFactor: 2, value: widget.tasks[i].subtasks[j].completed, title: Text(widget.tasks[i].subtasks[j].name), controlAffinity: ListTileControlAffinity.leading, activeColor: theme.getColorFor( ThemeCode.buttonPrimary, ), onChanged: (_) => viewModel.toggleSubtaskCompleted(i, j), ), ), TextButton( style: ButtonStyle( padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () => {}, child: Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsAddTask), style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), ), Container( decoration: BoxDecoration( color: theme.getColorFor( ThemeCode.backgroundTertiary, ), borderRadius: BorderRadius.all(Radius.circular(24)), ), padding: EdgeInsets.symmetric( horizontal: 20, vertical: 16, ), child: Row( spacing: 16, children: [ Expanded( child: Text( context.translate(I18n.goalsRewardForCompletion), style: TextStyle( fontSize: 16, letterSpacing: 0, ), ), ), Row( spacing: 8, children: [ Icon( Icons.emoji_events_outlined, size: 16, color: theme.getColorFor( ThemeCode.buttonPrimary, ), ), MoneyText( text: "${widget.tasks[i].rewardAmount}\u20AC", size: 40, secondarySize: 24, color: theme.getColorFor( ThemeCode.buttonPrimary, ), ), ], ), ], ), ), Column( spacing: 8, children: [ Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsTasksCompletedMessagePrefix), style: TextStyle(fontSize: 14, letterSpacing: 0), ), ), Align( alignment: Alignment.topLeft, child: Text( context.translate(I18n.goalsTasksCompletedMessage), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ), ], ), Align( alignment: Alignment.topLeft, child: TextButton( onPressed: () => {}, child: Row( spacing: 4, children: [ Icon(Icons.delete_outline_outlined, size: 24), Text( context.translate(I18n.goalsDeleteTaskList), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0, ), ), ], ), ), ), ], ), ), ), SecondaryButton( onPressed: () => {}, text: context.translate(I18n.goalsCreateNewTaskList), size: 14, ), ], ), ); } else { return emptyBlock(fullPlan: true); } } else { return emptyBlock(fullPlan: false); } } }