2026-02-17 20:54:14 +01:00
|
|
|
import 'package:design_system/design_system.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:home/src/presentation/wallet_management_layout.dart';
|
|
|
|
|
import 'package:navigation/navigation.dart';
|
|
|
|
|
import 'package:sf_localizations/sf_localizations.dart';
|
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
|
|
2026-02-26 14:59:51 +01:00
|
|
|
import '../../card_colors.dart';
|
|
|
|
|
import '../child_wallet/child_data_provider.dart';
|
2026-02-17 20:54:14 +01:00
|
|
|
import 'allowance_view_model.dart';
|
|
|
|
|
|
|
|
|
|
class AllowanceScreen extends ConsumerWidget {
|
|
|
|
|
final String childId;
|
|
|
|
|
final NavigationContract navigation;
|
|
|
|
|
|
|
|
|
|
const AllowanceScreen({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.childId,
|
|
|
|
|
required this.navigation,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
List<String> _localizedWeekDays(BuildContext context) {
|
|
|
|
|
final locale = Localizations.localeOf(context).toString();
|
|
|
|
|
return List.generate(7, (i) {
|
|
|
|
|
final date = DateTime(2024, 1, 1).add(Duration(days: i));
|
|
|
|
|
final name = DateFormat.EEEE(locale).format(date);
|
|
|
|
|
return name[0].toUpperCase() + name.substring(1);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
|
|
final theme = ref.watch(themePortProvider);
|
|
|
|
|
final viewState = ref.watch(allowanceViewModelProvider(childId));
|
|
|
|
|
final viewModel = ref.read(allowanceViewModelProvider(childId).notifier);
|
2026-02-26 14:59:51 +01:00
|
|
|
final cardStatus = ref.watch(childDataProvider(childId)).cardStatus;
|
2026-02-17 20:54:14 +01:00
|
|
|
final weekDays = _localizedWeekDays(context);
|
|
|
|
|
|
|
|
|
|
if (viewState.isLoading) {
|
2026-02-27 12:34:49 +01:00
|
|
|
return const Scaffold(body: Center(child: AppLoadingIndicator()));
|
2026-02-17 20:54:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2026-02-26 14:59:51 +01:00
|
|
|
cardColors: cardColorsFor(theme: theme, carrierGenre: viewState.device?.carrierGenre, cardStatus: cardStatus),
|
2026-02-17 20:54:14 +01:00
|
|
|
navigation: navigation,
|
2026-02-26 14:59:51 +01:00
|
|
|
footer: FooterContainer(
|
|
|
|
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
|
|
|
primaryColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
primaryText: context.translate(I18n.allowanceActivateAutoAllowance),
|
|
|
|
|
onPrimaryPressed: () => {},
|
|
|
|
|
cancelText: context.translate(I18n.cancel),
|
|
|
|
|
onCancelPressed: () {},
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
|
|
|
|
children: [
|
2026-02-26 14:59:51 +01:00
|
|
|
SectionContainer(
|
|
|
|
|
padding: 10,
|
|
|
|
|
borderRadius: 20,
|
|
|
|
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
|
|
|
children: [
|
|
|
|
|
Align(
|
|
|
|
|
alignment: Alignment.topLeft,
|
|
|
|
|
child: Text(
|
|
|
|
|
context.translate(I18n.allowanceAutoAllowance),
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
2026-02-26 14:59:51 +01:00
|
|
|
),
|
|
|
|
|
CustomTextField(
|
|
|
|
|
controller: viewModel.amountController,
|
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
|
label: context.translate(I18n.depositAmountLabel),
|
|
|
|
|
hint: context.translate(I18n.depositAmountHint),
|
|
|
|
|
),
|
|
|
|
|
Text(
|
|
|
|
|
context.translate(
|
|
|
|
|
I18n.allowanceBalanceAfter,
|
|
|
|
|
args: {'amount': '30'},
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
2026-02-26 14:59:51 +01:00
|
|
|
),
|
|
|
|
|
],
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
|
|
|
|
|
2026-02-26 14:59:51 +01:00
|
|
|
SectionContainer(
|
|
|
|
|
borderRadius: 20,
|
|
|
|
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
|
|
|
title: context.translate(I18n.allowanceFrequency),
|
|
|
|
|
subtitle: context.translate(I18n.allowanceFrequencyDescription),
|
|
|
|
|
children: [
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(context.translate(I18n.allowanceWeekly)),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.frequency == 'weekly',
|
|
|
|
|
onChanged: (_) => viewModel.selectFrequency('weekly'),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(context.translate(I18n.allowanceBiweekly)),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.frequency == 'biweekly',
|
|
|
|
|
onChanged: (_) => viewModel.selectFrequency('biweekly'),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(context.translate(I18n.allowanceMonthly)),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.frequency == 'monthly',
|
|
|
|
|
onChanged: (_) => viewModel.selectFrequency('monthly'),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
CustomDropdown(
|
|
|
|
|
items: weekDays.map((d) => Text(d)).toList(),
|
|
|
|
|
values: weekDays,
|
|
|
|
|
onChanged: (value) => viewModel.selectDayOfWeek(value ?? ''),
|
|
|
|
|
hint: context.translate(I18n.allowanceDayOfWeek),
|
|
|
|
|
),
|
|
|
|
|
CustomDropdown(
|
|
|
|
|
hint: context.translate(I18n.allowanceTimeOfDay),
|
|
|
|
|
items: List<Widget>.generate(24, (int index) {
|
|
|
|
|
return Text("$index:00");
|
|
|
|
|
}),
|
|
|
|
|
onChanged: (value) => viewModel.selectTimeOfDay(value ?? ''),
|
|
|
|
|
),
|
|
|
|
|
CustomTextField(
|
|
|
|
|
controller: viewModel.messageController,
|
|
|
|
|
lines: 3,
|
|
|
|
|
length: 150,
|
|
|
|
|
label: context.translate(
|
|
|
|
|
I18n.allowanceMessageLabel,
|
|
|
|
|
args: {'name': childName},
|
|
|
|
|
),
|
|
|
|
|
hint: context.translate(I18n.allowanceMessageHint),
|
|
|
|
|
),
|
|
|
|
|
Align(
|
|
|
|
|
alignment: Alignment.topLeft,
|
|
|
|
|
child: Text(
|
|
|
|
|
context.translate(
|
|
|
|
|
I18n.allowanceMaxChars,
|
|
|
|
|
args: {'count': '150'},
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
|
|
|
|
),
|
2026-02-26 14:59:51 +01:00
|
|
|
),
|
|
|
|
|
],
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
|
|
|
|
|
2026-02-26 14:59:51 +01:00
|
|
|
SectionContainer(
|
|
|
|
|
borderRadius: 20,
|
|
|
|
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
|
|
|
title: context.translate(I18n.allowanceConditions),
|
|
|
|
|
subtitle: context.translate(I18n.allowanceConditionsDescription),
|
|
|
|
|
children: [
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(
|
|
|
|
|
context.translate(I18n.allowanceConditionWeeklyLimits),
|
|
|
|
|
),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.conditionWeeklyLimits,
|
|
|
|
|
onChanged: (_) => viewModel.toggleConditionWeeklyLimits(),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(
|
|
|
|
|
context.translate(I18n.allowanceConditionNoIncidents),
|
|
|
|
|
),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.conditionNoIncidents,
|
|
|
|
|
onChanged: (_) => viewModel.toggleConditionNoIncidents(),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
CheckboxListTile(
|
|
|
|
|
contentPadding: EdgeInsets.zero,
|
|
|
|
|
title: Text(
|
|
|
|
|
context.translate(I18n.allowanceConditionPauseHolidays),
|
|
|
|
|
),
|
|
|
|
|
controlAffinity: ListTileControlAffinity.leading,
|
|
|
|
|
value: viewState.conditionPauseHolidays,
|
|
|
|
|
onChanged: (_) => viewModel.toggleConditionPauseHolidays(),
|
|
|
|
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
|
|
|
),
|
|
|
|
|
],
|
2026-02-17 20:54:14 +01:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|