diff --git a/lib/common/url/api_path.dart b/lib/common/url/api_path.dart index 4a7cf4c..5033af9 100644 --- a/lib/common/url/api_path.dart +++ b/lib/common/url/api_path.dart @@ -4,5 +4,5 @@ class ApiPath { static String verify = '/api/v1/customer-auth/register/verify-otp'; static String setPassword = '/api/v1/customer-auth/register/set-password'; static String login = '/api/v1/customer-auth/login'; - static String resend = '/api/v1/customer-auth/register/resend-otp'; + static String resend = '/api/v1/customer-auth/resend-otp'; } diff --git a/lib/presentation/pages/auth/otp/otp_page.dart b/lib/presentation/pages/auth/otp/otp_page.dart index ca5496e..83d200d 100644 --- a/lib/presentation/pages/auth/otp/otp_page.dart +++ b/lib/presentation/pages/auth/otp/otp_page.dart @@ -2,17 +2,45 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../application/auth/resend_form/resend_form_bloc.dart'; +import '../../../../application/auth/verify_form/verify_form_bloc.dart'; import '../../../../common/theme/theme.dart'; +import '../../../../domain/auth/auth.dart'; +import '../../../../injection.dart'; +import '../../../components/toast/flushbar.dart'; import '../../../router/app_router.gr.dart'; @RoutePage() -class OtpPage extends StatefulWidget { +class OtpPage extends StatefulWidget implements AutoRouteWrapper { final String registrationToken; - const OtpPage({super.key, required this.registrationToken}); + final String phoneNumber; + + const OtpPage({ + super.key, + required this.registrationToken, + required this.phoneNumber, + }); @override State createState() => _OtpPageState(); + + @override + Widget wrappedRoute(BuildContext context) => MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => getIt() + ..add(VerifyFormEvent.registrationTokenChanged(registrationToken)), + ), + BlocProvider( + create: (context) => getIt() + ..add(ResendFormEvent.phoneNumberChanged(phoneNumber)) + ..add(ResendFormEvent.purposeChanged('registration')), + ), + ], + child: this, + ); } class _OtpPageState extends State { @@ -54,6 +82,7 @@ class _OtpPageState extends State { }); _startTimer(); // Add your resend logic here + context.read().add(ResendFormEvent.submitted()); } String _formatTime(int seconds) { @@ -83,7 +112,9 @@ class _OtpPageState extends State { void _verifyCode() { String code = _controllers.map((controller) => controller.text).join(); if (code.length == 6) { - context.router.push(CreatePasswordRoute()); + // context.router.push(CreatePasswordRoute()); + context.read().add(VerifyFormEvent.otpCodeChanged(code)); + context.read().add(VerifyFormEvent.submitted()); } } @@ -101,135 +132,178 @@ class _OtpPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text('Verifikasi')), - body: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 20), - - // Title - Text( - 'Masukan Kode Verifikasi', - style: AppStyle.xl.copyWith( - fontWeight: FontWeight.w600, - color: AppColor.textPrimary, + return MultiBlocListener( + listeners: [ + BlocListener( + listenWhen: (p, c) => + p.failureOrVerifyOption != c.failureOrVerifyOption, + listener: (context, state) { + state.failureOrVerifyOption.fold( + () {}, + (either) => either.fold( + (f) => AppFlushbar.showAuthFailureToast(context, f), + (data) { + AppFlushbar.showSuccess(context, data.message); + Future.delayed(Duration(milliseconds: 1000), () { + context.router.push(CreatePasswordRoute()); + }); + }, ), - ), + ); + }, + ), + BlocListener( + listenWhen: (p, c) => + p.failureOrResendOption != c.failureOrResendOption, + listener: (context, state) { + state.failureOrResendOption.fold( + () {}, + (either) => either.fold( + (f) => AppFlushbar.showAuthFailureToast(context, f), + (data) { + if (data.status.isSuccess) { + AppFlushbar.showSuccess(context, data.message); + } else { + AppFlushbar.showError(context, data.message); + } + }, + ), + ); + }, + ), + ], + child: Scaffold( + appBar: AppBar(title: Text('Verifikasi')), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 20), - const SizedBox(height: 12), + // Title + Text( + 'Masukan Kode Verifikasi', + style: AppStyle.xl.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), - // Description - RichText( - text: TextSpan( + const SizedBox(height: 12), + + // Description + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'Kami telah mengirimkan kode verifikasi melalui ', + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + height: 1.4, + ), + ), + TextSpan( + text: 'Whatsapp', + style: AppStyle.sm.copyWith( + color: AppColor.success, + fontWeight: FontWeight.w500, + height: 1.4, + ), + ), + TextSpan( + text: ' ke ', + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + height: 1.4, + ), + ), + TextSpan( + text: '+${widget.phoneNumber}', + style: AppStyle.sm.copyWith( + color: AppColor.textPrimary, + fontWeight: FontWeight.w500, + height: 1.4, + ), + ), + ], + ), + ), + + const SizedBox(height: 6), + + // Hidden text fields for input + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(6, (index) { + return Expanded( + child: Padding( + padding: EdgeInsets.only(right: index == 5 ? 0 : 8.0), + child: TextFormField( + controller: _controllers[index], + focusNode: _focusNodes[index], + keyboardType: TextInputType.number, + maxLength: 1, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + decoration: InputDecoration(counterText: ''), + textAlign: TextAlign.center, + style: AppStyle.lg.copyWith( + color: AppColor.primary, + fontWeight: FontWeight.w600, + ), + cursorColor: AppColor.primary, + onChanged: (value) { + setState(() {}); + _onCodeChanged(value, index); + }, + ), + ), + ); + }), + ), + + const SizedBox(height: 40), + + // Timer and Resend Section + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - TextSpan( - text: 'Kami telah mengirimkan kode verifikasi melalui ', - style: AppStyle.sm.copyWith( + Text( + 'Mohon tunggu untuk kirim ulang kode ', + style: AppStyle.xs.copyWith(color: AppColor.textSecondary), + ), + Text( + _formatTime(_secondsRemaining), + style: AppStyle.xs.copyWith( color: AppColor.textSecondary, - height: 1.4, - ), - ), - TextSpan( - text: 'Whatsapp', - style: AppStyle.sm.copyWith( - color: AppColor.success, fontWeight: FontWeight.w500, - height: 1.4, - ), - ), - TextSpan( - text: ' ke ', - style: AppStyle.sm.copyWith( - color: AppColor.textSecondary, - height: 1.4, - ), - ), - TextSpan( - text: '+6288976680234', - style: AppStyle.sm.copyWith( - color: AppColor.textPrimary, - fontWeight: FontWeight.w500, - height: 1.4, ), ), ], ), - ), - const SizedBox(height: 6), - - // Hidden text fields for input - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: List.generate(6, (index) { - return Expanded( - child: Padding( - padding: EdgeInsets.only(right: index == 5 ? 0 : 8.0), - child: TextFormField( - controller: _controllers[index], - focusNode: _focusNodes[index], - keyboardType: TextInputType.number, - maxLength: 1, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - decoration: InputDecoration(counterText: ''), - textAlign: TextAlign.center, - style: AppStyle.lg.copyWith( - color: AppColor.primary, - fontWeight: FontWeight.w600, + if (_canResend) ...[ + const SizedBox(height: 12), + Center( + child: TextButton( + onPressed: _resendCode, + child: Text( + 'Kirim Ulang Kode', + style: AppStyle.sm.copyWith( + color: AppColor.success, + fontWeight: FontWeight.w500, ), - cursorColor: AppColor.primary, - onChanged: (value) { - setState(() {}); - _onCodeChanged(value, index); - }, ), ), - ); - }), - ), - - const SizedBox(height: 40), - - // Timer and Resend Section - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - 'Mohon tunggu untuk kirim ulang kode ', - style: AppStyle.xs.copyWith(color: AppColor.textSecondary), - ), - Text( - _formatTime(_secondsRemaining), - style: AppStyle.xs.copyWith( - color: AppColor.textSecondary, - fontWeight: FontWeight.w500, - ), ), ], - ), - if (_canResend) ...[ - const SizedBox(height: 12), - Center( - child: TextButton( - onPressed: _resendCode, - child: Text( - 'Kirim Ulang Kode', - style: AppStyle.sm.copyWith( - color: AppColor.success, - fontWeight: FontWeight.w500, - ), - ), - ), - ), + const SizedBox(height: 24), ], - - const SizedBox(height: 24), - ], + ), ), ), ); diff --git a/lib/presentation/pages/auth/register/register_page.dart b/lib/presentation/pages/auth/register/register_page.dart index 11cd157..0096491 100644 --- a/lib/presentation/pages/auth/register/register_page.dart +++ b/lib/presentation/pages/auth/register/register_page.dart @@ -29,7 +29,10 @@ class RegisterPage extends StatelessWidget implements AutoRouteWrapper { AppFlushbar.showSuccess(context, data.message); Future.delayed(Duration(milliseconds: 1000), () { context.router.push( - OtpRoute(registrationToken: data.registrationToken), + OtpRoute( + registrationToken: data.registrationToken, + phoneNumber: phoneNumber, + ), ); }); }, diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index e2cee0a..f959757 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -420,10 +420,15 @@ class OtpRoute extends _i33.PageRouteInfo { OtpRoute({ _i34.Key? key, required String registrationToken, + required String phoneNumber, List<_i33.PageRouteInfo>? children, }) : super( OtpRoute.name, - args: OtpRouteArgs(key: key, registrationToken: registrationToken), + args: OtpRouteArgs( + key: key, + registrationToken: registrationToken, + phoneNumber: phoneNumber, + ), initialChildren: children, ); @@ -433,24 +438,33 @@ class OtpRoute extends _i33.PageRouteInfo { name, builder: (data) { final args = data.argsAs(); - return _i20.OtpPage( - key: args.key, - registrationToken: args.registrationToken, + return _i33.WrappedRoute( + child: _i20.OtpPage( + key: args.key, + registrationToken: args.registrationToken, + phoneNumber: args.phoneNumber, + ), ); }, ); } class OtpRouteArgs { - const OtpRouteArgs({this.key, required this.registrationToken}); + const OtpRouteArgs({ + this.key, + required this.registrationToken, + required this.phoneNumber, + }); final _i34.Key? key; final String registrationToken; + final String phoneNumber; + @override String toString() { - return 'OtpRouteArgs{key: $key, registrationToken: $registrationToken}'; + return 'OtpRouteArgs{key: $key, registrationToken: $registrationToken, phoneNumber: $phoneNumber}'; } }