diff --git a/assets/icons/icon-remove.png b/assets/icons/icon-remove.png new file mode 100644 index 0000000..1b5b9ab Binary files /dev/null and b/assets/icons/icon-remove.png differ diff --git a/lib/application/assets/path_assets.dart b/lib/application/assets/path_assets.dart index f6e47fa..fc3a0ee 100644 --- a/lib/application/assets/path_assets.dart +++ b/lib/application/assets/path_assets.dart @@ -54,6 +54,7 @@ class PathAssets { static const String iconNavigationTransaction = 'assets/icons/icon-navigation-transaction.png'; static const String iconNavigationPortfolio = 'assets/icons/icon-navigation-portfolio.png'; static const String iconNavigationProfile = 'assets/icons/icon-navigation-profile.png'; + static const String iconRemove = 'assets/icons/icon-remove.png'; /// IMAGE static const String imgSplashLogo = 'assets/images/splash-logo.png'; @@ -91,4 +92,20 @@ class PathAssets { static const String imgPaymentSuccess = 'assets/images/img-payment-success.png'; static const String frameSignature = 'assets/images/frame-signature.png'; static const String imgFinish = 'assets/images/img-finish.png'; + + static const Map goalInvestIcon = { + 'Education': iconToga, + 'Marriage': iconCake, + 'Old age days': iconHouse, + 'Home': iconHouse, + 'Other Plan': iconCreatePlan, + 'Create Plan': iconCreatePlan, + 'Entertainment': iconTicket, + 'Gadget': iconGadget, + 'Business': iconMarket, + 'Fashion': iconBag, + 'Shop': iconBag, + 'Vehicle': iconCar, + 'Holiday': iconPlane, + }; } diff --git a/lib/application/component/numeric_pad/numeric_pad.dart b/lib/application/component/numeric_pad/numeric_pad.dart index 959d666..cbf71bf 100644 --- a/lib/application/component/numeric_pad/numeric_pad.dart +++ b/lib/application/component/numeric_pad/numeric_pad.dart @@ -1,3 +1,5 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/core/utils/size_config.dart'; import 'package:flutter/material.dart'; @@ -101,8 +103,9 @@ class NumericPad extends StatelessWidget { number, textAlign: TextAlign.center, style: TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold + fontSize: 28, + fontWeight: FontWeight.bold, + color: ColorPalette.slate800 ), ), ), @@ -117,8 +120,9 @@ class NumericPad extends StatelessWidget { onNumberSelected(''); }, child: Icon( - Icons.highlight_remove, + Icons.backspace_outlined, size: 28, + color: ColorPalette.slate800, ), ) ); diff --git a/lib/application/theme/color_palette.dart b/lib/application/theme/color_palette.dart index 992d6d5..2f4eab5 100644 --- a/lib/application/theme/color_palette.dart +++ b/lib/application/theme/color_palette.dart @@ -92,6 +92,7 @@ class ColorPalette { static const Color green100 = Color(0xFFDCFCE7); static const Color green400 = Color(0xFF4ADE80); static const Color green500 = Color(0xFF16A34A); + static const Color red600 = Color(0xffDC2626); static const Map investTypeColor = { 'Money Market': purple500, diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_detail_view.dart b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_detail_view.dart new file mode 100644 index 0000000..f53f129 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_detail_view.dart @@ -0,0 +1,258 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/button/back_button_view.dart'; +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class PortfolioDetailView extends StatelessWidget { + const PortfolioDetailView({super.key}); + + @override + Widget build(BuildContext context) { + List listProduct = [ + PortfolioProduct( + name: 'Gemilang Dana Kas Maxima', + type: '', + yield: 8.17, + priceUnit: 2600.79, + funds: 6300000), + PortfolioProduct( + name: 'Gemilang Dana Likuid', + type: '', + yield: 6.42, + priceUnit: 1600.79, + funds: 2340000), + PortfolioProduct( + name: 'Gemilang Income Fund', + type: '', + yield: 8.17, + priceUnit: 2600.79, + funds: 6300000) + ]; + + + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => RedeemProductViewModel()) + ], + child: Scaffold( + backgroundColor: Colors.white, + body: SizedBox( + width: SizeConfig.width, + height: SizeConfig.height, + child: Stack( + children: [ + const ImageView(image: PathAssets.imgDashboardAccount), + Column( + children: [ + const SizedBox( + height: 50, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BackButtonView(), + const TextTitle(title: 'Education', color: Colors.white), + SizedBox(width: SizeConfig.width * 0.1,) + ], + ), + ), + const SizedBox(height: 24,), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Portfolio Value', + style: TextStyle( + color: ColorPalette.white, + fontWeight: FontWeight.w400 + ), + ), + TextTitle(title: 'Rp 2.000.000', fontSize: 14, color: Colors.white) + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('Advantages', + style: TextStyle( + color: ColorPalette.white, + fontWeight: FontWeight.w400 + ), + ), + TextTitle(title: 'Rp 2.000.000', fontSize: 14, color: Colors.white) + ], + ) + ], + ), + ), + const SizedBox(height: 24,), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: ListView( + padding: const EdgeInsets.all(24), + children: [ + cardPortfolio(context) + ], + ), + ), + ) + ) + ], + ) + ], + ), + ), + ), + ); + } + + Widget cardPortfolio(context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: ColorPalette.slate200), + boxShadow: [ + BoxShadow( + color: const Color(0xff1E293B).withOpacity(0.04), + blurRadius: 8, + spreadRadius: 2 + ) + ] + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImageView( + image: PathAssets.imgProduct, + width: SizeConfig.width * .13, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, + border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) + ), + child: Text( + 'Money Market' ?? '', + style: TextStyle( + color: ColorPalette.investTypeColor['Money Market'], + fontWeight: FontWeight.w600 + ), + ), + ) + ], + ), + ) + ], + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Divider(height: 1, color: ColorPalette.slate200,), + ), + Wrap( + runSpacing: 8, + children: [ + rowDescription('Present Value', 'Rp2.660.706', fontWeight: FontWeight.w700), + rowDescription('Investment Capital', 'Rp2.660.706'), + rowDescription('Advantages', 'Rp2.660.706'), + rowDescription('Purchase Price', 'Rp1.500,57'), + rowDescription('Number of Units', '14.002'), + ], + ), + const SizedBox(height: 16,), + Row( + children: [ + Expanded( + child: ButtonView( + name: 'Redeem', + marginVertical: 0, + width: SizeConfig.width, + isOutlined: true, + heightWrapContent: true, + contentPadding: const EdgeInsets.all(12), + backgroundColor: Colors.white, + borderColor: ColorPalette.red600, + textColor: ColorPalette.red600, + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return RedeemProduct(); + }, + ); + }, + ), + ), + const SizedBox(width: 16), + Expanded( + child: ButtonView( + name: 'Buy', + marginVertical: 0, + width: SizeConfig.width, + heightWrapContent: true, + contentPadding: const EdgeInsets.all(12), + onPressed: () { + + }, + ), + ) + ], + ) + ], + ), + ); + } + + Widget rowDescription(String title, String value, {Color? color, FontWeight? fontWeight}) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title, + style: const TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600 + ), + ), + Text(value, + style: TextStyle( + color: color ?? ColorPalette.slate800, + fontWeight: fontWeight ?? FontWeight.w600, + ), + ) + ], + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart index ab4915c..2e5c504 100644 --- a/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart +++ b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart @@ -1,7 +1,10 @@ import 'package:cims_apps/application/assets/path_assets.dart'; import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/route/route.dart'; import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/portfolio_detail_view.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; @@ -147,9 +150,31 @@ class _PortofolioViewState extends State { ), menuPortofolio(), const SizedBox( - height: 24, + height: 12, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextTitle(title: 'My Portfolio', fontSize: 16), + Row( + children: [ + Icon(Icons.add, size: 18, color: ColorPalette.primary), + SizedBox(width: 4), + Text('Create', + style: TextStyle( + color: ColorPalette.primary, + fontWeight: FontWeight.w600 + ), + ) + ], + ) + ], + ), ), ...listColumnPortofolio(), + cardPortfolio() ], ), ), @@ -183,6 +208,7 @@ class _PortofolioViewState extends State { seePortofolioValue ? Icons.visibility_off_outlined : Icons.visibility_outlined, + size: 18, color: const Color(0xff93C5FD))) ], ), @@ -358,4 +384,85 @@ class _PortofolioViewState extends State { ); }).toList(); } + + Widget cardPortfolio() { + return GestureDetector( + onTap: () { + routePush(context, page: PortfolioDetailView()); + }, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 24), + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: ColorPalette.slate200) + ), + child: Column( + children: [ + Row( + children: [ + Container( + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + color: ColorPalette.blue200.withOpacity(0.5), + borderRadius: BorderRadius.circular(8) + ), + child: ImageView( + image: PathAssets.goalInvestIcon['Education'], + width: SizeConfig.width * 0.07 + ) + ), + SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextTitle(title: 'Education', fontSize: 16,), + Text('2 Subscriptions', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600 + ), + ) + ], + ), + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Divider(height: 1, color: ColorPalette.slate200), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Portfolio Value', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w400 + ), + ), + TextTitle(title: 'Rp 2.000.000', fontSize: 14,) + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('Advantages', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w400 + ), + ), + TextTitle(title: 'Rp 2.000.000', fontSize: 14) + ], + ) + ], + ) + ], + ), + ), + ); + } } diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_amount.dart b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_amount.dart new file mode 100644 index 0000000..e5d5a0a --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_amount.dart @@ -0,0 +1,235 @@ +import 'dart:math'; + +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class ChangeAmount extends StatefulWidget { + final int totalAmount; + const ChangeAmount({super.key, required this.totalAmount}); + + @override + State createState() => _ChangeAmountState(); +} + +class _ChangeAmountState extends State { + TextEditingController amountController = TextEditingController(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + amountController.text = NumberFormatter.numberCurrency(widget.totalAmount, 'Rp ', 'id_ID', decimalDigits: 0); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + amountController.dispose(); + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => RedeemProductViewModel(), + child: Consumer( + builder: (context, provider, child) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return const RedeemProduct(); + }, + ); + }, + child: const Icon(Icons.arrow_back_rounded) + ), + const Text('Type amount', + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + color: ColorPalette.slate800 + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: const Icon(Icons.close_rounded) + ) + ], + ), + ), + const Divider(height: 1, color: ColorPalette.slate200,), + Padding( + padding: EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + cardProduct(), + SizedBox(height: 24), + TextField( + controller: amountController, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.w600, + color: ColorPalette.slate800 + ), + keyboardType: TextInputType.number, + onChanged: (value) { + value = value.replaceAll('Rp ', '').replaceAll('.', ''); + double parseValue = double.parse(value); + if(value.isNotEmpty){ + amountController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0); + }else{ + amountController.text = NumberFormatter.numberCurrency(0, 'Rp ', 'id_ID', decimalDigits: 0); + } + }, + decoration: const InputDecoration( + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: ColorPalette.primary, + width: 2 + ), + ) + ), + ), + SizedBox(height: 12), + Text( + 'Min Redeem: ${(NumberFormatter.numberCurrency(10000, 'Rp ', 'id_ID', decimalDigits: 0))}', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600, + ), + ), + Text( + 'Max Redeem: ${(NumberFormatter.numberCurrency((provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toInt(), 'Rp ', 'id_ID', decimalDigits: 0))}', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 24), + NumericPad(onNumberSelected: (p0) { + String checkIsZeroInput = amountController.text.replaceAll('Rp ', '').replaceAll('.', ''); + String getNumeric = p0; + + if(p0.isNotEmpty){ + if(checkIsZeroInput != '0'){ + getNumeric = checkIsZeroInput + getNumeric; + } + }else{ + getNumeric = checkIsZeroInput.substring(0, checkIsZeroInput.length - 1); + } + if(getNumeric.isEmpty){ + getNumeric = '0'; + } + if(double.parse(getNumeric) >= provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!){ + getNumeric = (provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toString(); + } + String formatNumeric = NumberFormatter.numberCurrency( + double.parse(getNumeric).toInt(), 'Rp ', 'id_ID', decimalDigits: 0); + amountController.text = formatNumeric; + }), + const SizedBox(height: 24), + ButtonView( + name: 'Confirm', + textSize: 20, + marginVertical: 0, + onPressed: () { + String formatValueInput = amountController.text.replaceAll('Rp ', '').replaceAll('.', ''); + provider.setAmount(double.parse(formatValueInput)); + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return const RedeemProduct(); + }, + ); + }, + ) + ], + ), + ) + ], + ); + } + ), + ); + } + + Widget cardProduct() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: ColorPalette.slate200), + boxShadow: [ + BoxShadow( + color: const Color(0xff1E293B).withOpacity(0.04), + blurRadius: 8, + spreadRadius: 2 + ) + ] + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImageView( + image: PathAssets.imgProduct, + width: SizeConfig.width * .13, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, + border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) + ), + child: Text( + 'Money Market' ?? '', + style: TextStyle( + color: ColorPalette.investTypeColor['Money Market'], + fontWeight: FontWeight.w600 + ), + ), + ) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_destination_account.dart b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_destination_account.dart new file mode 100644 index 0000000..71ca1c5 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_destination_account.dart @@ -0,0 +1,191 @@ +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class ChangeDestinationAccount extends StatelessWidget { + const ChangeDestinationAccount({super.key}); + + @override + Widget build(BuildContext context) { + List listAccount = [ + Account('Muhamad Rosyidin', 'BRI', '902139012324'), + Account('Achmad Muhaimin', 'BCA', '21391283928') + ]; + + return ChangeNotifierProvider( + create: (context) => RedeemProductViewModel(), + child: Consumer( + builder: (context, provider, child) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return const RedeemProduct(); + }, + ); + }, + child: const Icon(Icons.arrow_back_rounded) + ), + const Text('Change Destination Account', + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + color: ColorPalette.slate800 + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: const Icon(Icons.close_rounded) + ) + ], + ), + ), + const Divider(height: 1, color: ColorPalette.slate200,), + Padding( + padding: const EdgeInsets.all(24), + child: Column( + children: [ + ...listAccount.asMap().entries.map((e) { + return GestureDetector( + onTap: () => provider.setSelectedAcc(e.value), + child: Padding( + padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0), + child: cardAccount(e.value, provider.selectedAccount?.number ?? provider.getCurrentAccount.number), + ), + ); + }), + const SizedBox(height: 24), + GestureDetector( + child: const Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.add, size: 24, color: ColorPalette.primary), + SizedBox(width: 12,), + Text('New Bank', + style: TextStyle( + color: ColorPalette.primary, + fontSize: 20, + fontWeight: FontWeight.w600 + ), + ) + ], + ), + ), + const SizedBox(height: 32), + ButtonView( + name: 'Save', + textSize: 20, + marginVertical: 0, + onPressed: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + provider.setCurrentAcc(provider.selectedAccount!); + return const RedeemProduct(); + }, + ); + }, + ) + ], + ), + ), + ], + ); + } + ), + ); + } + + Widget cardAccount(Account account, String numberSelected) { + List listNumber = []; + + List.generate(account.number.length, (index) { + if(index > 3 && index < account.number.length - 4){ + listNumber.add('*'); + }else{ + listNumber.add(account.number[index]); + } + }); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: ColorPalette.slate200) + ), + child: Row( + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(account.nameOwner, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.slate800 + ), + ), + Text( + '${account.nameBank} - ${listNumber.join("")}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.slate400 + ), + ) + ], + ), + ], + ), + ), + AnimatedContainer( + margin: const EdgeInsets.only(top: 4), + duration: const Duration(milliseconds: 200), + height: 16, + width: 16, + padding: const EdgeInsets.all(1), + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: numberSelected == account.number + ? ColorPalette.primary + : ColorPalette.slate200)), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + child: Container( + decoration: BoxDecoration( + color: + numberSelected == account.number ? ColorPalette.primary : ColorPalette.white, + shape: BoxShape.circle), + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart new file mode 100644 index 0000000..57e2a43 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart @@ -0,0 +1,361 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_form/text_form_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_amount.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_destination_account.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/total_redeem.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class RedeemProduct extends StatefulWidget { + const RedeemProduct({super.key}); + + @override + State createState() => _RedeemProductState(); +} + +class _RedeemProductState extends State { + TextEditingController amountController = TextEditingController(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + amountController.dispose(); + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => RedeemProductViewModel(), + child: Consumer( + builder: (context, provider, child) { + double amount = provider.getAmount ?? provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!; + amountController.text = NumberFormatter.numberCurrency(amount.toInt(), 'Rp ', 'id_ID', decimalDigits: 0); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Products to be Redeemed', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + fontSize: 16 + ), + ), + GestureDetector( + onTap: () => Navigator.pop(context), + child: const Icon(Icons.close_rounded, color: ColorPalette.slate800,) + ) + ], + ), + ), + const Divider(height: 1, color: ColorPalette.slate200,), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + cardProduct(), + const SizedBox(height: 16), + segmentAmount( + context, + provider.getAmount ?? provider.getCurrentProduct.priceUnit! * (provider.getCurrentProduct.totalUnit! / 2.0), + provider.getUnit ?? provider.getCurrentProduct.totalUnit! / 2.0, + (value) { + provider.setUnit(value); + }, + ), + const SizedBox(height: 16), + segmentDestinationAcc(provider.getCurrentAccount), + const SizedBox(height: 36), + ButtonView( + name: 'Redeem', + textSize: 20, + marginVertical: 0, + onPressed: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return TotalRedeem(); + }, + ); + }, + ), + const SizedBox(height: 16) + ], + ), + ) + ], + ); + } + ), + ); + } + + Widget cardProduct() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: ColorPalette.slate200), + boxShadow: [ + BoxShadow( + color: const Color(0xff1E293B).withOpacity(0.04), + blurRadius: 8, + spreadRadius: 2 + ) + ] + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImageView( + image: PathAssets.imgProduct, + width: SizeConfig.width * .13, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, + border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) + ), + child: Text( + 'Money Market' ?? '', + style: TextStyle( + color: ColorPalette.investTypeColor['Money Market'], + fontWeight: FontWeight.w600 + ), + ), + ) + ], + ), + ) + ], + ), + ); + } + + Widget segmentAmount(context, double currentAmount, double currentUnit, void Function(double value) setUnit) { + double sliderValue = currentUnit / Provider.of(context, listen: false).getCurrentProduct.totalUnit!; + return Column( + children: [ + const Align( + alignment: Alignment.centerLeft, + child: Text('Amount', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + fontSize: 16 + ), + ), + ), + TextField( + controller: amountController, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.w700, + color: ColorPalette.slate800 + ), + keyboardType: TextInputType.number, + readOnly: true, + decoration: InputDecoration( + enabledBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: ColorPalette.slate200, + width: 1 + ), + ), + suffixIcon: GestureDetector( + onTap: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return ChangeAmount( + totalAmount: currentAmount.toInt(), + ); + }, + ); + }, + child: const Icon(Icons.edit, color: ColorPalette.primary), + ) + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(currentUnit.toStringAsFixed(2).replaceAll('.', ','), + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 18, + color: ColorPalette.slate800 + ), + ), + const Text(' unit', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate400 + ), + ) + ], + ), + ), + Row( + children: [ + Expanded( + child: SliderTheme( + data: SliderTheme.of(context).copyWith( + trackHeight: 4.0, + thumbColor: ColorPalette.primary, + thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10.0), + overlayColor:Colors.deepPurple, + inactiveTickMarkColor: ColorPalette.primary, + inactiveTrackColor: ColorPalette.slate200, + overlayShape: SliderComponentShape.noOverlay + ), + child: Slider( + value: sliderValue, + onChanged: setUnit, + label: sliderValue.round().toString(), + ), + ), + ), + SizedBox(width: 12), + Text('${(sliderValue * 100).toStringAsFixed(2)} %', + style: const TextStyle( + color: ColorPalette.slate800, + fontWeight: FontWeight.w600 + ), + ) + ], + ) + ], + ); + } + + Widget segmentDestinationAcc(Account account) { + List listNumber = []; + + List.generate(account.number.length, (index) { + if(index > 3 && index < account.number.length - 4){ + listNumber.add('*'); + }else{ + listNumber.add(account.number[index]); + } + }); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Destination Account', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + fontSize: 16 + ), + ), + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: ColorPalette.slate200) + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(account.nameOwner, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.slate800 + ), + ), + Text('${account.nameBank} - ${listNumber.join("")}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.slate400 + ), + ) + ], + ), + ], + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => const ChangeDestinationAccount(), + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + color: ColorPalette.blue200.withOpacity(0.5) + ), + child: const Row( + children: [ + Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20), + SizedBox(width: 4), + Text('Change', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.primary + ), + ) + ], + ), + ), + ) + ], + ), + ) + ], + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/total_redeem.dart b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/total_redeem.dart new file mode 100644 index 0000000..bfbae8e --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/total_redeem.dart @@ -0,0 +1,151 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class TotalRedeem extends StatelessWidget { + const TotalRedeem({super.key}); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => RedeemProductViewModel(), + child: Consumer( + builder: (context, provider, child) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Investment Funds that You Cash Out', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + fontSize: 16 + ), + ), + GestureDetector( + onTap: () => Navigator.pop(context), + child: Icon(Icons.close_rounded, color: ColorPalette.slate800,) + ) + ], + ), + ), + Divider(height: 1, color: ColorPalette.slate200,), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: cardProduct(provider.getCurrentProduct, provider.getUnit!), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Sales Commission', + style: TextStyle( + color: ColorPalette.slate400, + fontSize: 16 + ), + ), + Text('Free', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.primary, + fontSize: 16 + ), + ) + ], + ), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Cash Out Method', + style: TextStyle( + color: ColorPalette.slate400, + fontSize: 16 + ), + ), + Text('Regular', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.primary, + fontSize: 16 + ), + ) + ], + ), + ), + SizedBox(height: 16,), + + ], + ); + } + ), + ); + } + + Widget cardProduct(PortfolioProduct product, double currentUnit) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: ColorPalette.slate200), + boxShadow: [ + BoxShadow( + color: const Color(0xff1E293B).withOpacity(0.04), + blurRadius: 8, + spreadRadius: 2 + ) + ] + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImageView( + image: PathAssets.imgProduct, + width: SizeConfig.width * .13, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextTitle(title: product.name ?? '', fontSize: 16,), + const SizedBox(height: 4), + Row( + children: [ + Text(currentUnit.toStringAsFixed(2).replaceAll('.', ','), + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 18, + color: ColorPalette.slate800 + ), + ), + const Text(' unit', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate400 + ), + ) + ], + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart new file mode 100644 index 0000000..77147d6 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; + +class Account { + String nameOwner, nameBank, number; + + Account(this.nameOwner, this.nameBank, this.number); +} + +class PortfolioProduct { + String? name, type; + double? yield; + double? priceUnit, funds, totalPercent, totalUnit; + + PortfolioProduct({this.name, this.type, this.yield, this.priceUnit, this.funds, this.totalPercent = 1, this.totalUnit}); +} + +class RedeemProductViewModel extends ChangeNotifier { + static Account currentAccount = Account('Muhamad Rosyidin', 'BRI', '902139012324'); + static PortfolioProduct currentProduct = + PortfolioProduct( + name: 'Gemilang Dana Kas Maxima', + type: '', + yield: 8.17, + priceUnit: 2600.79, + funds: 6300000, + totalUnit: 25 + ); + + static double? amount; + static double? unit; + Account? selectedAccount; + + Account get getCurrentAccount => currentAccount; + PortfolioProduct get getCurrentProduct => currentProduct; + double? get getAmount => amount; + double? get getUnit => unit; + + void setCurrentAcc(Account account) { + currentAccount = account; + notifyListeners(); + } + + void setSelectedAcc(Account account){ + selectedAccount = account; + notifyListeners(); + } + + void setAmount(double amount) { + amount = amount; + unit = (amount / currentProduct.priceUnit!); + notifyListeners(); + } + + void setUnit(double value){ + unit = currentProduct.totalUnit! * value; + amount = unit! * currentProduct.priceUnit!; + notifyListeners(); + } + + void setProduct(PortfolioProduct product) { + currentProduct = product; + notifyListeners(); + } +} \ No newline at end of file