diff --git a/lib/ui/company_gateway/company_gateway_list.dart b/lib/ui/company_gateway/company_gateway_list.dart index 02974f7e3..32fb26889 100644 --- a/lib/ui/company_gateway/company_gateway_list.dart +++ b/lib/ui/company_gateway/company_gateway_list.dart @@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/ui/app/form_card.dart'; +import 'package:invoiceninja_flutter/ui/app/forms/bool_dropdown_button.dart'; import 'package:invoiceninja_flutter/ui/app/help_text.dart'; import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart'; import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_list_item.dart'; @@ -22,12 +24,27 @@ class CompanyGatewayList extends StatelessWidget { final state = store.state; final listUIState = state.uiState.companyGatewayUIState.listUIState; final isInMultiselect = listUIState.isInMultiselect(); + final localization = AppLocalization.of(context); + final settings = viewModel.settings; return Column( children: [ - SizedBox( - height: 32, - ), + FormCard(children: [ + BoolDropdownButton( + label: localization.allowOverPayment, + value: settings.clientPortalAllowOverPayment, + helpLabel: localization.allowOverPaymentHelp, + onChanged: (value) => viewModel.onSettingsChanged(settings + .rebuild((b) => b..clientPortalAllowOverPayment = value)), + ), + BoolDropdownButton( + label: localization.allowUnderPayment, + value: settings.clientPortalAllowUnderPayment, + helpLabel: localization.allowUnderPaymentHelp, + onChanged: (value) => viewModel.onSettingsChanged(settings + .rebuild((b) => b..clientPortalAllowUnderPayment = value)), + ), + ]), Flexible( fit: FlexFit.tight, child: !viewModel.state.isLoaded && @@ -58,7 +75,7 @@ class CompanyGatewayList extends StatelessWidget { return CompanyGatewayListItem( key: ValueKey( '__company_gateway_$companyGatewayId'), - user: viewModel.userCompany.user, + user: state.userCompany.user, filter: viewModel.filter, companyGateway: companyGateway, onRemovePressed: diff --git a/lib/ui/company_gateway/company_gateway_list_item.dart b/lib/ui/company_gateway/company_gateway_list_item.dart index a2b303827..85e59713e 100644 --- a/lib/ui/company_gateway/company_gateway_list_item.dart +++ b/lib/ui/company_gateway/company_gateway_list_item.dart @@ -36,6 +36,7 @@ class CompanyGatewayListItem extends StatelessWidget { final store = StoreProvider.of(context); final state = store.state; + final localization = AppLocalization.of(context); final filterMatch = filter != null && filter.isNotEmpty ? companyGateway.matchesFilterValue(filter) : null; @@ -53,53 +54,68 @@ class CompanyGatewayListItem extends StatelessWidget { trailing: onRemovePressed == null ? null : FlatButton( - child: Text(AppLocalization.of(context).remove), - onPressed: onRemovePressed, - ), + child: Text(AppLocalization + .of(context) + .remove), + onPressed: onRemovePressed, + ), leading: showCheckbox ? IgnorePointer( - ignoring: listUIState.isInMultiselect(), - child: Checkbox( - value: isChecked, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - onChanged: (value) => onCheckboxChanged(value), - activeColor: Theme.of(context).accentColor, - ), - ) + ignoring: listUIState.isInMultiselect(), + child: Checkbox( + value: isChecked, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onChanged: (value) => onCheckboxChanged(value), + activeColor: Theme + .of(context) + .accentColor, + ), + ) : Icon(Icons.drag_handle), title: Container( - width: MediaQuery.of(context).size.width, + width: MediaQuery + .of(context) + .size + .width, child: Row( - children: [ - Expanded( - child: Text( - companyGateway.label + - (companyGateway.isTestMode - ? ' [${AppLocalization.of(context).testMode}]' - : ''), - style: Theme.of(context).textTheme.headline6, - ), - ), - Text(formatNumber(companyGateway.listDisplayAmount, context), - style: Theme.of(context).textTheme.headline6), - ], - ), - ), - subtitle: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - subtitle != null && subtitle.isNotEmpty - ? Text( - subtitle, - maxLines: 3, - overflow: TextOverflow.ellipsis, - ) - : Container(), - EntityStateLabel(companyGateway), - ], + children: [ + Expanded( + child: Text( + companyGateway.label, + style: Theme + .of(context) + .textTheme + .headline6, ), ), + Text(formatNumber(companyGateway.listDisplayAmount, context), + style: Theme + .of(context) + .textTheme + .headline6), + ], + ), + ), + subtitle: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (companyGateway.isTestMode) + Text(localization.testMode), + subtitle != null && subtitle.isNotEmpty + ? Text( + subtitle, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ) + : Container(), + EntityStateLabel(companyGateway), + ] + , + ) + , + ) + , ); } } diff --git a/lib/ui/company_gateway/company_gateway_list_vm.dart b/lib/ui/company_gateway/company_gateway_list_vm.dart index a93006106..17b149b03 100644 --- a/lib/ui/company_gateway/company_gateway_list_vm.dart +++ b/lib/ui/company_gateway/company_gateway_list_vm.dart @@ -36,16 +36,16 @@ class CompanyGatewayListBuilder extends StatelessWidget { class CompanyGatewayListVM { CompanyGatewayListVM({ @required this.state, - @required this.userCompany, @required this.companyGatewayList, @required this.companyGatewayMap, @required this.filter, - @required this.isLoading, @required this.onCompanyGatewayTap, @required this.listState, @required this.onRefreshed, @required this.onSortChanged, @required this.onRemovePressed, + @required this.onSettingsChanged, + @required this.settings, }); static CompanyGatewayListVM fromStore(Store store) { @@ -77,11 +77,10 @@ class CompanyGatewayListVM { return CompanyGatewayListVM( state: state, - userCompany: state.userCompany, listState: state.companyGatewayListState, companyGatewayList: gatewayIds, + settings: state.uiState.settingsUIState.settings, companyGatewayMap: state.companyGatewayState.map, - isLoading: state.isLoading, filter: state.companyGatewayUIState.listUIState.filter, onCompanyGatewayTap: (context, companyGateway) { if (store.state.companyGatewayListState.isInMultiselect()) { @@ -108,18 +107,20 @@ class CompanyGatewayListVM { .rebuild((b) => b..companyGatewayIds = gatewayIds.join(',')); store.dispatch(UpdateSettings(settings: settings)); }, + onSettingsChanged: (settings) => + store.dispatch(UpdateSettings(settings: settings)), ); } final AppState state; - final UserCompanyEntity userCompany; final List companyGatewayList; final BuiltMap companyGatewayMap; final ListUIState listState; final String filter; - final bool isLoading; final Function(BuildContext, CompanyGatewayEntity) onCompanyGatewayTap; final Function(BuildContext) onRefreshed; final Function(int, int) onSortChanged; final Function(String) onRemovePressed; + final SettingsEntity settings; + final Function(SettingsEntity) onSettingsChanged; } diff --git a/lib/ui/company_gateway/company_gateway_screen.dart b/lib/ui/company_gateway/company_gateway_screen.dart index 45094bf76..881353305 100644 --- a/lib/ui/company_gateway/company_gateway_screen.dart +++ b/lib/ui/company_gateway/company_gateway_screen.dart @@ -64,25 +64,23 @@ class CompanyGatewayScreen extends StatelessWidget { onCancelPressed: (context) => store.dispatch(ClearCompanyGatewayMultiselect()), ) - else if (state.companyGatewayState.list.isNotEmpty) ...[ - if (state.uiState.settingsUIState.isFiltered) ...[ - FlatButton( - child: Text(localization.reset, - style: TextStyle(color: store.state.headerTextColor)), - onPressed: () { - final settings = store.state.uiState.settingsUIState.settings - .rebuild((b) => b..companyGatewayIds = ''); - store.dispatch(UpdateSettings(settings: settings)); - }, - ), - SizedBox(width: 10), - ], - SaveCancelButtons( - isSaving: state.isSaving, - onSavePressed: viewModel.onSavePressed, - onCancelPressed: (_) => store.dispatch(ResetSettings()), - ) + else if (state.uiState.settingsUIState.isFiltered) ...[ + FlatButton( + child: Text(localization.reset, + style: TextStyle(color: store.state.headerTextColor)), + onPressed: () { + final settings = store.state.uiState.settingsUIState.settings + .rebuild((b) => b..companyGatewayIds = ''); + store.dispatch(UpdateSettings(settings: settings)); + }, + ), + SizedBox(width: 10), ], + SaveCancelButtons( + isSaving: state.isSaving, + onSavePressed: viewModel.onSavePressed, + onCancelPressed: (_) => store.dispatch(ResetSettings()), + ) ], body: CompanyGatewayListBuilder(), bottomNavigationBar: AppBottomBar( diff --git a/lib/ui/dashboard/dashboard_chart.dart b/lib/ui/dashboard/dashboard_chart.dart index 286afe759..1339c9049 100644 --- a/lib/ui/dashboard/dashboard_chart.dart +++ b/lib/ui/dashboard/dashboard_chart.dart @@ -106,7 +106,7 @@ class _DashboardChartState extends State { ); return Container( - child: FormCard( + child: _FormCard( children: [ Padding( padding: EdgeInsets.all(16), @@ -233,8 +233,8 @@ class _DashboardChartState extends State { } } -class FormCard extends StatelessWidget { - const FormCard({ +class _FormCard extends StatelessWidget { + const _FormCard({ Key key, @required this.children, }) : super(key: key); diff --git a/lib/utils/i18n.dart b/lib/utils/i18n.dart index 7ae949fd4..159f2547d 100644 --- a/lib/utils/i18n.dart +++ b/lib/utils/i18n.dart @@ -15,6 +15,11 @@ mixin LocalizationsProvider on LocaleCodeAware { static final Map> _localizedValues = { 'en': { // STARTER: lang key - do not remove comment + 'allow_over_payment': 'Allow Over Payment', + 'allow_over_payment_help': 'Support paying extra to accept tips', + 'allow_under_payment': 'Allow Under Payment', + 'allow_under_payment_help': + 'Support paying at minimum the partial/deposit amount', 'test_mode': 'Test Mode', 'opened': 'opened', 'payment_reconciliation_failure': 'Reconciliation Failure', @@ -4467,6 +4472,18 @@ mixin LocalizationsProvider on LocaleCodeAware { String get testMode => _localizedValues[localeCode]['test_mode'] ?? ''; + String get allowOverPayment => + _localizedValues[localeCode]['allow_over_payment'] ?? ''; + + String get allowOverPaymentHelp => + _localizedValues[localeCode]['allow_over_payment_help'] ?? ''; + + String get allowUnderPayment => + _localizedValues[localeCode]['allow_under_payment'] ?? ''; + + String get allowUnderPaymentHelp => + _localizedValues[localeCode]['allow_under_payment_help'] ?? ''; + String lookup(String key) { final lookupKey = toSnakeCase(key);