Skip to content

Commit

Permalink
Review Request Page (#65)
Browse files Browse the repository at this point in the history
* Add translations

* Add optional total to fee table

* Add review request page and test

* pass data from withdraw or deposit page to payment details page

* update translations

* change to shorthand syntax and add test helper widgets

* upadte page heading to match payment details
  • Loading branch information
kirahsapong authored Feb 15, 2024
1 parent e245d9e commit 810c103
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 32 deletions.
19 changes: 14 additions & 5 deletions lib/features/deposit/deposit_page.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:didpay/features/home/transaction.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:didpay/features/currency/currency_converter.dart';
Expand Down Expand Up @@ -25,6 +26,8 @@ class DepositPage extends HookWidget {
final isValidKeyPress = useState<bool>(true);
final selectedCurrencyItem =
useState<Map<String, Object>>(supportedCurrencyList[1]);
final outputAmount = double.parse('0${depositAmount.value}') /
double.parse(selectedCurrencyItem.value['exchangeRate'].toString());

return Scaffold(
appBar: AppBar(),
Expand All @@ -45,10 +48,7 @@ class DepositPage extends HookWidget {
inputSelectedCurrency:
selectedCurrencyItem.value['label'].toString(),
inputLabel: Loc.of(context).youDeposit,
outputAmount: (double.parse('0${depositAmount.value}') /
double.parse(selectedCurrencyItem
.value['exchangeRate']
.toString())),
outputAmount: outputAmount,
isValidKeyPress: isValidKeyPress.value,
onDropdownTap: () {
CurrencyModal.show(
Expand Down Expand Up @@ -83,7 +83,16 @@ class DepositPage extends HookWidget {
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PaymentDetailsPage(),
builder: (context) => PaymentDetailsPage(
inputAmount: depositAmount.value,
inputCurrency:
selectedCurrencyItem.value['label'].toString(),
exchangeRate: selectedCurrencyItem.value['exchangeRate']
.toString(),
outputAmount: outputAmount.toString(),
outputCurrency: Loc.of(context).usd,
transactionType: Type.deposit,
),
),
);
},
Expand Down
45 changes: 43 additions & 2 deletions lib/features/payments/payment_details_page.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:collection/collection.dart';
import 'package:didpay/features/payments/review_request_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:didpay/features/payments/payment_method.dart';
Expand All @@ -8,7 +10,21 @@ import 'package:didpay/shared/json_schema_form.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class PaymentDetailsPage extends HookConsumerWidget {
const PaymentDetailsPage({super.key});
final String inputCurrency;
final String inputAmount;
final String outputCurrency;
final String outputAmount;
final String exchangeRate;
final String transactionType;

const PaymentDetailsPage(
{required this.inputCurrency,
required this.inputAmount,
required this.outputCurrency,
required this.outputAmount,
required this.exchangeRate,
required this.transactionType,
super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down Expand Up @@ -174,12 +190,37 @@ class PaymentDetailsPage extends HookConsumerWidget {
child: JsonSchemaForm(
schema: selectedPaymentMethod.value!.requiredPaymentDetails,
onSubmit: (formData) {
// TODO: save payment details here
if (isValidOnSubmit(formData, selectedPaymentMethod)) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ReviewRequestPage(
formData: formData,
bankName: selectedPaymentMethod.value!.kind
.split('_')
.lastOrNull ??
'',
inputAmount: inputAmount,
outputAmount: outputAmount,
inputCurrency: inputCurrency,
outputCurrency: outputCurrency,
exchangeRate: exchangeRate,
serviceFee: selectedPaymentMethod.value!.fee ?? '0.00',
transactionType: transactionType,
),
),
);
}
},
),
);
}

bool isValidOnSubmit(Map<String, String> formData,
ValueNotifier<PaymentMethod?> selectedPaymentMethod) {
return formData['accountNumber'] != null &&
selectedPaymentMethod.value!.kind.split('_').lastOrNull != null;
}

Widget _buildDisabledButton(BuildContext context) {
return Expanded(
child: Column(
Expand Down
173 changes: 173 additions & 0 deletions lib/features/payments/review_request_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import 'package:didpay/features/home/transaction.dart';
import 'package:didpay/l10n/app_localizations.dart';
import 'package:didpay/shared/fee_details.dart';
import 'package:didpay/shared/success_page.dart';
import 'package:didpay/shared/theme/grid.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:intl/intl.dart';

class ReviewRequestPage extends HookWidget {
final String inputAmount;
final String outputAmount;
final String inputCurrency;
final String outputCurrency;
final String exchangeRate;
final String serviceFee;
final String bankName;
final Map<String, String> formData;
final String transactionType;

const ReviewRequestPage({
required this.inputAmount,
required this.outputAmount,
required this.inputCurrency,
required this.outputCurrency,
required this.exchangeRate,
required this.serviceFee,
required this.bankName,
required this.formData,
required this.transactionType,
super.key,
});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: Grid.side),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(context),
const SizedBox(height: Grid.md),
_buildAmounts(context),
const SizedBox(height: Grid.md),
_buildFeeDetails(context),
const SizedBox(height: Grid.md),
_buildBankDetails(context),
])),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FilledButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SuccessPage(
text: Loc.of(context).yourRequestWasSubmitted,
),
),
);
},
child: Text(Loc.of(context).submit),
),
],
),
],
))));
}

Widget _buildHeader(BuildContext context) => Column(children: [
Align(
alignment: Alignment.topLeft,
child: Text(
Loc.of(context).reviewYourRequest,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: Grid.xs),
Align(
alignment: Alignment.topLeft,
child: Text(
Loc.of(context).makeSureInfoIsCorrect,
style: Theme.of(context).textTheme.bodyMedium,
),
),
]);

Widget _buildAmounts(BuildContext context) =>
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
NumberFormat.simpleCurrency().format(double.parse(inputAmount)),
style: Theme.of(context).textTheme.displaySmall,
),
const SizedBox(width: Grid.xs),
Text(
inputCurrency,
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
const SizedBox(height: Grid.xxs),
Text(
transactionType == Type.deposit
? Loc.of(context).youPay
: Loc.of(context).withdrawAmount,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: Grid.xs),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
NumberFormat.simpleCurrency().format(double.parse(outputAmount)),
style: Theme.of(context).textTheme.displaySmall,
),
const SizedBox(width: Grid.xs),
Text(
outputCurrency,
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
const SizedBox(height: Grid.xxs),
Text(
transactionType == Type.deposit
? Loc.of(context).depositAmount
: Loc.of(context).youGet,
style: Theme.of(context).textTheme.bodyLarge,
)
]);

Widget _buildFeeDetails(BuildContext context) => FeeDetails(
originCurrency: Loc.of(context).usd,
destinationCurrency:
inputCurrency != Loc.of(context).usd ? inputCurrency : outputCurrency,
exchangeRate: exchangeRate,
serviceFee: double.parse(serviceFee).toStringAsFixed(2),
total: inputCurrency != Loc.of(context).usd
? (double.parse(inputAmount) + double.parse(serviceFee))
.toStringAsFixed(2)
: (double.parse(outputAmount) + double.parse(serviceFee))
.toStringAsFixed(2));

Widget _buildBankDetails(BuildContext context) => Column(children: [
Text(bankName),
const SizedBox(height: Grid.xxs),
Text(obscureAccountNumber(formData['accountNumber']!)),
]);

String obscureAccountNumber(String input) {
if (input.length <= 4) {
return input;
}
return '${'•' * (input.length - 4)} ${input.substring(input.length - 4)}';
}
}
20 changes: 14 additions & 6 deletions lib/features/withdraw/withdraw_page.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:didpay/features/home/transaction.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:didpay/features/currency/currency_converter.dart';
Expand Down Expand Up @@ -25,6 +26,8 @@ class WithdrawPage extends HookWidget {
final isValidKeyPress = useState<bool>(true);
final selectedCurrencyItem =
useState<Map<String, Object>>(supportedCurrencyList[1]);
final outputAmount = double.parse('0${withdrawAmount.value}') *
double.parse(selectedCurrencyItem.value['exchangeRate'].toString());

return Scaffold(
appBar: AppBar(),
Expand All @@ -45,11 +48,7 @@ class WithdrawPage extends HookWidget {
inputLabel: Loc.of(context).youWithdraw,
outputSelectedCurrency:
selectedCurrencyItem.value['label'].toString(),
outputAmount:
(double.parse('0${withdrawAmount.value}') *
double.parse(selectedCurrencyItem
.value['exchangeRate']
.toString())),
outputAmount: outputAmount,
isValidKeyPress: isValidKeyPress.value,
onDropdownTap: () {
CurrencyModal.show(
Expand Down Expand Up @@ -84,7 +83,16 @@ class WithdrawPage extends HookWidget {
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const PaymentDetailsPage(),
builder: (context) => PaymentDetailsPage(
inputAmount: withdrawAmount.value,
inputCurrency: Loc.of(context).usd,
exchangeRate: selectedCurrencyItem.value['exchangeRate']
.toString(),
outputAmount: outputAmount.toString(),
outputCurrency:
selectedCurrencyItem.value['label'].toString(),
transactionType: Type.withdrawal,
),
),
);
},
Expand Down
8 changes: 7 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,11 @@
"copiedDid": "Copied DID!",
"simulatedQrCodeScan": "Simulated QR code scan!",
"sendingPayment": "Sending payment...",
"verifyingYourIdentity": "Verifying your identity..."
"verifyingYourIdentity": "Verifying your identity...",
"reviewYourRequest": "Review your request",
"depositAmount": "Deposit amount",
"withdrawAmount": "Withdraw amount",
"total": "Total",
"submit": "Submit",
"yourRequestWasSubmitted": "Your request was submitted!"
}
36 changes: 36 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,42 @@ abstract class Loc {
/// In en, this message translates to:
/// **'Verifying your identity...'**
String get verifyingYourIdentity;

/// No description provided for @reviewYourRequest.
///
/// In en, this message translates to:
/// **'Review your request'**
String get reviewYourRequest;

/// No description provided for @depositAmount.
///
/// In en, this message translates to:
/// **'Deposit amount'**
String get depositAmount;

/// No description provided for @withdrawAmount.
///
/// In en, this message translates to:
/// **'Withdraw amount'**
String get withdrawAmount;

/// No description provided for @total.
///
/// In en, this message translates to:
/// **'Total'**
String get total;

/// No description provided for @submit.
///
/// In en, this message translates to:
/// **'Submit'**
String get submit;

/// No description provided for @yourRequestWasSubmitted.
///
/// In en, this message translates to:
/// **'Your request was submitted!'**
String get yourRequestWasSubmitted;
}

class _LocDelegate extends LocalizationsDelegate<Loc> {
Expand Down
Loading

0 comments on commit 810c103

Please sign in to comment.