From 0b2f1ff86922712f0a8d70bba669e11555332ab4 Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Fri, 19 Dec 2025 13:47:31 +0100 Subject: [PATCH 1/4] Added two factor login, otp code widget and intl --- .../auth_remote_datasource_impl.dart | 82 ++- .../repositories/auth_repository_impl.dart | 2 +- .../domain/repositories/auth_repository.dart | 2 +- .../features/login/domain/login_use_case.dart | 2 +- .../login/domain/login_use_case_impl.dart | 2 +- .../login/domain/two_factor_use_case.dart | 3 + .../domain/two_factor_use_case_impl.dart | 13 + .../login/presentation/login_screen.dart | 475 +++++++++++++----- .../providers/two_factor_provider.dart | 9 + .../presentation/state/login_view_model.dart | 155 +++++- .../presentation/state/login_view_state.dart | 7 + .../state/login_view_state.freezed.dart | 65 ++- .../presentation/widgets/otp_code_fields.dart | 211 ++++++++ .../widgets/two_factor_bottom_sheet.dart | 118 +++++ packages/sf_localizations/assets/l10n/de.json | 15 +- packages/sf_localizations/assets/l10n/en.json | 15 +- packages/sf_localizations/assets/l10n/es.json | 15 +- packages/sf_localizations/assets/l10n/fr.json | 15 +- packages/sf_localizations/assets/l10n/it.json | 15 +- packages/sf_localizations/assets/l10n/pt.json | 15 +- .../lib/src/generated/i18n.dart | 14 + 21 files changed, 1041 insertions(+), 209 deletions(-) create mode 100644 modules/auth/lib/src/features/login/domain/two_factor_use_case.dart create mode 100644 modules/auth/lib/src/features/login/domain/two_factor_use_case_impl.dart create mode 100644 modules/auth/lib/src/features/login/presentation/providers/two_factor_provider.dart create mode 100644 modules/auth/lib/src/features/login/presentation/widgets/otp_code_fields.dart create mode 100644 modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart diff --git a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart index 0510575a..4c9a04a4 100644 --- a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart +++ b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:sf_infrastructure/sf_infrastructure.dart'; @@ -38,22 +40,6 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource { } } - Exception _mapDioError(DioException error, {required String defaultMessage}) { - final responseData = error.response?.data; - String message = defaultMessage; - - if (responseData is Map) { - final serverMessage = responseData['message']; - if (serverMessage is String && serverMessage.isNotEmpty) { - message = serverMessage; - } - } else if (error.message != null && error.message!.isNotEmpty) { - message = error.message!; - } - - return Exception(message); - } - @override Future login({ required String email, @@ -67,19 +53,71 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource { final token = response.data!['token']; return token; } on DioException catch (error) { - throw _mapDioError(error, defaultMessage: 'Error in login'); + throw _mapDioError( + error, + defaultMessage: error.message ?? 'Error in login', + ); } } @override - Future twoFALogin({required String token, required String code}) async { + Future twoFALogin({ + required String token, + required String code, + }) async { try { - await _repository.post>( - '/auth/login', - body: {'token': token, 'password': code}, + final response = await _repository.post( + '/auth/totp/login', + body: { + 'token': token, + 'code': code, + 'rememberMe': true, + }, ); + + final data = response.data; + if (data == null || data.isEmpty) { + throw Exception('Empty response from /auth/totp/login'); + } + + return data; } on DioException catch (error) { - throw _mapDioError(error, defaultMessage: 'Error in login'); + throw _mapDioError(error, defaultMessage: 'Error in twoFALogin'); } } } + +Exception _mapDioError(DioException error, {required String defaultMessage}) { + final apiMsg = _extractApiMessage(error.response?.data); + final msg = apiMsg ?? error.message ?? defaultMessage; + return Exception(msg); +} + +String? _extractApiMessage(Object? data) { + if (data == null) return null; + + if (data is Map) { + final errorObj = data['error']; + if (errorObj is Map && errorObj['message'] is String) { + return (errorObj['message'] as String).trim(); + } + if (data['message'] is String) { + return (data['message'] as String).trim(); + } + return null; + } + + if (data is String) { + final raw = data.trim(); + if (raw.isEmpty) return null; + + try { + final decoded = jsonDecode(raw); + return _extractApiMessage(decoded); + } catch (_) { + return raw; + } + } + + return null; +} diff --git a/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart index 1bfc3593..a0bb81c6 100644 --- a/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart +++ b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart @@ -22,7 +22,7 @@ class AuthRepositoryImpl implements AuthRepository { } @override - Future twoFALogin({required String token, required String code}) { + Future twoFactor({required String token, required String code}) { return _remote.twoFALogin(token: token, code: code); } } diff --git a/modules/auth/lib/src/core/domain/repositories/auth_repository.dart b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart index 43c8fdb4..c011369d 100644 --- a/modules/auth/lib/src/core/domain/repositories/auth_repository.dart +++ b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart @@ -5,5 +5,5 @@ abstract class AuthRepository { Future login({required String email, required String password}); - Future twoFALogin({required String token, required String code}); + Future twoFactor({required String token, required String code}); } diff --git a/modules/auth/lib/src/features/login/domain/login_use_case.dart b/modules/auth/lib/src/features/login/domain/login_use_case.dart index f6ab5b70..6b822de9 100644 --- a/modules/auth/lib/src/features/login/domain/login_use_case.dart +++ b/modules/auth/lib/src/features/login/domain/login_use_case.dart @@ -1,3 +1,3 @@ abstract class LoginUseCase { - Future login({required String email, required String password}); + Future login({required String email, required String password}); } diff --git a/modules/auth/lib/src/features/login/domain/login_use_case_impl.dart b/modules/auth/lib/src/features/login/domain/login_use_case_impl.dart index 6688eee8..926e15ad 100644 --- a/modules/auth/lib/src/features/login/domain/login_use_case_impl.dart +++ b/modules/auth/lib/src/features/login/domain/login_use_case_impl.dart @@ -7,7 +7,7 @@ class LoginUseCaseImpl implements LoginUseCase { final AuthRepository _repository; @override - Future login({required String email, required String password}) { + Future login({required String email, required String password}) { return _repository.login(email: email, password: password); } } diff --git a/modules/auth/lib/src/features/login/domain/two_factor_use_case.dart b/modules/auth/lib/src/features/login/domain/two_factor_use_case.dart new file mode 100644 index 00000000..e7c29742 --- /dev/null +++ b/modules/auth/lib/src/features/login/domain/two_factor_use_case.dart @@ -0,0 +1,3 @@ +abstract class TwoFactorUseCase { + Future twoFactor({required String token, required String code}); +} diff --git a/modules/auth/lib/src/features/login/domain/two_factor_use_case_impl.dart b/modules/auth/lib/src/features/login/domain/two_factor_use_case_impl.dart new file mode 100644 index 00000000..ce7ee7e1 --- /dev/null +++ b/modules/auth/lib/src/features/login/domain/two_factor_use_case_impl.dart @@ -0,0 +1,13 @@ +import 'package:auth/src/core/domain/repositories/auth_repository.dart'; +import 'package:auth/src/features/login/domain/two_factor_use_case.dart'; + +class TwoFactorUseCaseImpl implements TwoFactorUseCase { + TwoFactorUseCaseImpl(this._repository); + + final AuthRepository _repository; + + @override + Future twoFactor({required String token, required String code}) { + return _repository.twoFactor(token: token, code: code); + } +} diff --git a/modules/auth/lib/src/features/login/presentation/login_screen.dart b/modules/auth/lib/src/features/login/presentation/login_screen.dart index 331c59e9..ae18c21b 100644 --- a/modules/auth/lib/src/features/login/presentation/login_screen.dart +++ b/modules/auth/lib/src/features/login/presentation/login_screen.dart @@ -1,5 +1,6 @@ import 'package:auth/src/features/login/presentation/loading_google_screen.dart'; import 'package:auth/src/features/login/presentation/state/login_view_model.dart'; +import 'package:auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -11,155 +12,351 @@ class LoginScreen extends ConsumerWidget { const LoginScreen({super.key, required this.navigationContract}); + Future _onLogIn(BuildContext context, WidgetRef ref) async { + FocusManager.instance.primaryFocus?.unfocus(); + + final vm = ref.read(loginViewModelProvider.notifier); + + final String? token = await vm.login(); + if (!context.mounted) return; + + if (token == null || token.isEmpty) return; + + vm.prepareTwoFactor(); + + final bool? verified = await showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + isDismissible: false, + enableDrag: false, + backgroundColor: Colors.transparent, + builder: (_) => TwoFactorBottomSheet(token: token), + ); + + if (!context.mounted) return; + + if (verified == true) { + navigationContract.goTo(AppRoutes.dashboardHome); + } + } + @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); - final vm = ref.read(loginViewModelProvider.notifier); - final state = ref.watch(loginViewModelProvider); - - Future onSignIn() async { - FocusScope.of(context).unfocus(); - final login = await vm.login(); - if (login) navigationContract.goTo(AppRoutes.dashboardHome); - } + final bool isLoading = ref.watch( + loginViewModelProvider.select((s) => s.isLoading), + ); return Scaffold( backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.check, - color: theme.getColorFor(ThemeCode.buttonPrimary), - size: 54, - ), - Text( - context.translate(I18n.welcome), - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 48), - - CustomTextField( - hint: context.translate(I18n.username), - label: context.translate(I18n.username), - controller: vm.emailController, - keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.next, - ), - - const SizedBox(height: 24), - - CustomTextField( - showPassword: state.passwordVisible, - label: context.translate(I18n.password), - hint: "********", - controller: vm.passwordController, - - textInputAction: TextInputAction.done, - onSubmitted: (_) => onSignIn(), - ), - - const SizedBox(height: 16), - - Align( - alignment: Alignment.topLeft, - child: CustomTextButton( - text: context.translate(I18n.forgotPassword), - onPressed: state.isLoading - ? () {} - : () => - navigationContract.pushTo(AppRoutes.recoverPassword), - size: 16, - ), - ), - - const SizedBox(height: 30), - - PrimaryButton( - onPressed: state.isLoading ? () {} : onSignIn, - text: context.translate(I18n.signIn), - color: theme.getColorFor(ThemeCode.buttonPrimary), - leading: state.isLoading - ? const SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.white, - ), - ) - : null, - ), - - const SizedBox(height: 30), - - Stack( + body: SafeArea( + child: AbsorbPointer( + absorbing: isLoading, + child: SingleChildScrollView( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 40), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Divider(endIndent: 74, indent: 74), - Align( - alignment: Alignment.center, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 14), - color: theme.getColorFor(ThemeCode.backgroundPrimary), - child: Text(context.translate(I18n.orContinueWith)), - ), + _Header(theme: theme), + SizedBox(height: 48), + const _EmailSection(), + SizedBox(height: 24), + _PasswordSection(onSubmitted: () => _onLogIn(context, ref)), + SizedBox(height: 16), + _ForgotPassword(navigationContract: navigationContract), + SizedBox(height: 30), + _SignInSection( + theme: theme, + onSignIn: () => _onLogIn(context, ref), ), + SizedBox(height: 30), + _OrContinueWith(theme: theme), + SizedBox(height: 24), + _SocialButtons(theme: theme), + SizedBox(height: 30), + _Footer(navigationContract: navigationContract), ], ), - - const SizedBox(height: 24), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SecondaryButton( - onPressed: state.isLoading - ? () {} - : () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const LoadingGoogleScreen(), - ), - ), - radius: 16, - padding: 44, - text: context.translate(I18n.google), - label: 'Google', - ), - const SizedBox(width: 16), - SecondaryButton( - onPressed: state.isLoading ? () {} : () {}, - radius: 16, - padding: 44, - icon: Icons.apple, - label: 'Apple', - ), - ], - ), - - const SizedBox(height: 30), - - Text( - context.translate(I18n.dontHaveAccount), - style: const TextStyle(fontSize: 18, letterSpacing: 0), - ), - TextButton( - onPressed: state.isLoading - ? null - : () => navigationContract.goTo(AppRoutes.signup), - child: Text( - context.translate(I18n.createOneNow), - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - letterSpacing: 0, - ), - ), - ), - ], + ), + ), + ), + ); + } +} + +class _Header extends StatelessWidget { + const _Header({required this.theme}); + final ThemePort theme; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Icon( + Icons.check, + color: theme.getColorFor(ThemeCode.buttonPrimary), + size: 54, + ), + Text( + context.translate(I18n.welcome), + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold), + ), + ], + ); + } +} + +class _EmailSection extends ConsumerWidget { + const _EmailSection(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final vm = ref.read(loginViewModelProvider.notifier); + final String emailErrorKey = ref.watch( + loginViewModelProvider.select((s) => s.emailError), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + CustomTextField( + hint: context.translate(I18n.username), + label: context.translate(I18n.username), + controller: vm.emailController, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + ), + _FieldErrorText.fromKey(errorKey: emailErrorKey), + ], + ); + } +} + +class _PasswordSection extends ConsumerWidget { + const _PasswordSection({required this.onSubmitted}); + final Future Function() onSubmitted; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final vm = ref.read(loginViewModelProvider.notifier); + + final bool passwordVisible = ref.watch( + loginViewModelProvider.select((s) => s.passwordVisible), + ); + final String passwordErrorKey = ref.watch( + loginViewModelProvider.select((s) => s.passwordError), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + CustomTextField( + showPassword: passwordVisible, + label: context.translate(I18n.password), + hint: '********', + controller: vm.passwordController, + textInputAction: TextInputAction.done, + onSubmitted: (_) => onSubmitted(), + ), + _FieldErrorText.fromKey(errorKey: passwordErrorKey), + ], + ); + } +} + +class _ForgotPassword extends ConsumerWidget { + const _ForgotPassword({required this.navigationContract}); + final NavigationContract navigationContract; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isLoading = ref.watch( + loginViewModelProvider.select((s) => s.isLoading), + ); + + return Align( + alignment: Alignment.topLeft, + child: CustomTextButton( + text: context.translate(I18n.forgotPassword), + onPressed: isLoading + ? () {} + : () => navigationContract.pushTo(AppRoutes.recoverPassword), + size: 16, + ), + ); + } +} + +class _SignInSection extends ConsumerWidget { + const _SignInSection({required this.onSignIn, required this.theme}); + + final VoidCallback onSignIn; + final ThemePort theme; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isLoading = ref.watch( + loginViewModelProvider.select((s) => s.isLoading), + ); + final String errorMessage = ref.watch( + loginViewModelProvider.select((s) => s.errorMessage), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + PrimaryButton( + onPressed: isLoading ? () {} : onSignIn, + text: context.translate(I18n.signIn), + color: theme.getColorFor(ThemeCode.buttonPrimary), + leading: isLoading + ? const SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : null, + ), + if (errorMessage.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 12), + child: Text( + errorMessage, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + fontSize: 13, + ), + ), + ), + ], + ); + } +} + +class _OrContinueWith extends StatelessWidget { + const _OrContinueWith({required this.theme}); + final ThemePort theme; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + const Divider(endIndent: 74, indent: 74), + Align( + alignment: Alignment.center, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14), + color: theme.getColorFor(ThemeCode.backgroundPrimary), + child: Text(context.translate(I18n.orContinueWith)), + ), + ), + ], + ); + } +} + +class _SocialButtons extends ConsumerWidget { + const _SocialButtons({required this.theme}); + final ThemePort theme; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isLoading = ref.watch( + loginViewModelProvider.select((s) => s.isLoading), + ); + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SecondaryButton( + onPressed: isLoading + ? () {} + : () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const LoadingGoogleScreen(), + ), + ), + radius: 16, + padding: 44, + text: context.translate(I18n.google), + label: 'Google', + ), + const SizedBox(width: 16), + SecondaryButton( + onPressed: isLoading ? () {} : () {}, + radius: 16, + padding: 44, + icon: Icons.apple, + label: 'Apple', + ), + ], + ); + } +} + +class _Footer extends ConsumerWidget { + const _Footer({required this.navigationContract}); + final NavigationContract navigationContract; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isLoading = ref.watch( + loginViewModelProvider.select((s) => s.isLoading), + ); + + return Column( + children: [ + Text( + context.translate(I18n.dontHaveAccount), + style: const TextStyle(fontSize: 18, letterSpacing: 0), + ), + TextButton( + onPressed: isLoading + ? null + : () => navigationContract.goTo(AppRoutes.signup), + child: Text( + context.translate(I18n.createOneNow), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + letterSpacing: 0, + ), + ), + ), + ], + ); + } +} + +class _FieldErrorText extends StatelessWidget { + const _FieldErrorText._({required this.text}); + final String text; + + factory _FieldErrorText.fromKey({required String errorKey}) { + return _FieldErrorText._(text: errorKey); + } + + @override + Widget build(BuildContext context) { + if (text.isEmpty) return const SizedBox.shrink(); + + return Padding( + padding: const EdgeInsets.only(top: 8), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + context.translate(text), + style: TextStyle( + color: Theme.of(context).colorScheme.error, + fontSize: 12, + ), ), ), ); diff --git a/modules/auth/lib/src/features/login/presentation/providers/two_factor_provider.dart b/modules/auth/lib/src/features/login/presentation/providers/two_factor_provider.dart new file mode 100644 index 00000000..5eac987d --- /dev/null +++ b/modules/auth/lib/src/features/login/presentation/providers/two_factor_provider.dart @@ -0,0 +1,9 @@ +import 'package:auth/src/core/providers/auth_repository_provider.dart'; +import 'package:auth/src/features/login/domain/two_factor_use_case.dart'; +import 'package:auth/src/features/login/domain/two_factor_use_case_impl.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final twoFactorUseCaseProvider = Provider.autoDispose((ref) { + final authRepository = ref.read(authRepositoryProvider); + return TwoFactorUseCaseImpl(authRepository); +}); diff --git a/modules/auth/lib/src/features/login/presentation/state/login_view_model.dart b/modules/auth/lib/src/features/login/presentation/state/login_view_model.dart index 28a3f5fa..4ce9568d 100644 --- a/modules/auth/lib/src/features/login/presentation/state/login_view_model.dart +++ b/modules/auth/lib/src/features/login/presentation/state/login_view_model.dart @@ -1,8 +1,11 @@ import 'package:auth/src/features/login/domain/login_use_case.dart'; +import 'package:auth/src/features/login/domain/two_factor_use_case.dart'; import 'package:auth/src/features/login/presentation/providers/login_provider.dart'; +import 'package:auth/src/features/login/presentation/providers/two_factor_provider.dart'; import 'package:auth/src/features/login/presentation/state/login_view_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:sf_localizations/sf_localizations.dart'; final loginViewModelProvider = NotifierProvider.autoDispose( @@ -11,18 +14,30 @@ final loginViewModelProvider = class LoginViewModel extends Notifier { late final LoginUseCase _loginUseCase; + late final TwoFactorUseCase _twoFactorUseCase; + late final TextEditingController emailController; late final TextEditingController passwordController; + late final TextEditingController otpController; + + static final RegExp _emailRegex = RegExp( + r'^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$', + caseSensitive: false, + ); + @override LoginViewState build() { _loginUseCase = ref.read(loginUseCaseProvider); + _twoFactorUseCase = ref.read(twoFactorUseCaseProvider); emailController = TextEditingController(); passwordController = TextEditingController(); + otpController = TextEditingController(); emailController.addListener(_onEmailChanged); passwordController.addListener(_onPasswordChanged); + otpController.addListener(_onOtpChanged); ref.onDispose(disposeControllers); @@ -30,17 +45,24 @@ class LoginViewModel extends Notifier { } void _onEmailChanged() { - if (emailController.text != state.email) { - state = state.copyWith(email: emailController.text, errorMessage: ''); + final text = emailController.text; + if (text == state.email) return; + + state = state.copyWith(email: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith(emailError: _emailErrorFor(text)); } } void _onPasswordChanged() { - if (passwordController.text != state.password) { - state = state.copyWith( - password: passwordController.text, - errorMessage: '', - ); + final text = passwordController.text; + if (text == state.password) return; + + state = state.copyWith(password: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith(passwordError: _passwordErrorFor(text)); } } @@ -48,24 +70,122 @@ class LoginViewModel extends Notifier { state = state.copyWith(passwordVisible: !state.passwordVisible); } - Future login() async { + bool _isValidEmail(String email) => _emailRegex.hasMatch(email); + + String _emailErrorFor(String value) { + final email = value.trim(); + if (email.isEmpty) return I18n.errorEmailRequired; + if (!_isValidEmail(email)) return I18n.errorEmailInvalid; + return ''; + } + + String _passwordErrorFor(String value) { + final password = value.trim(); + if (password.isEmpty) return I18n.errorPasswordRequired; + if (password.length < 6) return I18n.errorPasswordMinLength; + return ''; + } + + bool _validateForm() { + final emailError = _emailErrorFor(state.email); + final passwordError = _passwordErrorFor(state.password); + + state = state.copyWith( + showErrors: true, + emailError: emailError, + passwordError: passwordError, + errorMessage: '', + ); + + return emailError.isEmpty && passwordError.isEmpty; + } + + Future login() async { + if (!_validateForm()) return null; + final email = state.email.trim(); final password = state.password.trim(); - if (email.isEmpty) { - state = state.copyWith(errorMessage: 'errorMessageIsEmpty'); - return false; - } - state = state.copyWith(isLoading: true, errorMessage: ''); + try { - await _loginUseCase.login(email: email, password: password); - if (!ref.mounted) return false; + final String token = await _loginUseCase.login( + email: email, + password: password, + ); + + if (!ref.mounted) return null; + state = state.copyWith(isLoading: false); + return token; + } catch (e) { + if (!ref.mounted) return null; + + state = state.copyWith(isLoading: false, errorMessage: e.toString()); + return null; + } + } + + void prepareTwoFactor() { + otpController.text = ''; + state = state.copyWith(otpCode: '', otpError: '', isOtpLoading: false); + } + + void _onOtpChanged() { + final raw = otpController.text; + if (raw == state.otpCode) return; + + state = state.copyWith(otpCode: raw, otpError: ''); + + if (state.showErrors) { + state = state.copyWith(otpError: _otpErrorFor(raw)); + } + } + + void setOtpCode(String code) { + state = state.copyWith(otpCode: code, otpError: ''); + } + + String _otpErrorFor(String value) { + final code = value.trim(); + if (code.isEmpty) return I18n.errorTwoFactorCodeRequired; + if (code.length != 6) return I18n.errorTwoFactorCodeInvalidLength; + return ''; + } + + bool _validateOtp() { + final otpError = _otpErrorFor(state.otpCode); + + state = state.copyWith( + showErrors: true, + otpError: otpError, + errorMessage: '', + ); + + return otpError.isEmpty; + } + + Future twoFactor({required String token}) async { + if (!_validateOtp()) return false; + + final code = state.otpCode.trim(); + + state = state.copyWith(isOtpLoading: true, otpError: '', errorMessage: ''); + + try { + await _twoFactorUseCase.twoFactor(token: token, code: code); + + if (!ref.mounted) return false; + + state = state.copyWith(isOtpLoading: false); return true; } catch (e) { if (!ref.mounted) return false; - state = state.copyWith(isLoading: false, errorMessage: e.toString()); + + state = state.copyWith( + isOtpLoading: false, + otpError: I18n.errorTwoFactorCodeInvalid, + ); return false; } } @@ -73,7 +193,10 @@ class LoginViewModel extends Notifier { void disposeControllers() { emailController.removeListener(_onEmailChanged); passwordController.removeListener(_onPasswordChanged); + otpController.removeListener(_onOtpChanged); + emailController.dispose(); passwordController.dispose(); + otpController.dispose(); } } diff --git a/modules/auth/lib/src/features/login/presentation/state/login_view_state.dart b/modules/auth/lib/src/features/login/presentation/state/login_view_state.dart index 5dfb3fbd..f3783870 100644 --- a/modules/auth/lib/src/features/login/presentation/state/login_view_state.dart +++ b/modules/auth/lib/src/features/login/presentation/state/login_view_state.dart @@ -8,7 +8,14 @@ abstract class LoginViewState with _$LoginViewState { @Default('') String email, @Default('') String password, @Default(false) bool passwordVisible, + @Default('') String emailError, + @Default('') String passwordError, @Default('') String errorMessage, + @Default(false) bool showErrors, @Default(false) bool isLoading, + @Default('') String token, + @Default('') String otpCode, + @Default('') String otpError, + @Default(false) bool isOtpLoading, }) = _LoginViewState; } diff --git a/modules/auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart b/modules/auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart index 5d528dd7..fdadb062 100644 --- a/modules/auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart +++ b/modules/auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$LoginViewState { - String get email; String get password; bool get passwordVisible; String get errorMessage; bool get isLoading; + String get email; String get password; bool get passwordVisible; String get emailError; String get passwordError; String get errorMessage; bool get showErrors; bool get isLoading; String get token; String get otpCode; String get otpError; bool get isOtpLoading; /// Create a copy of LoginViewState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $LoginViewStateCopyWith get copyWith => _$LoginViewStateCopyWith @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.token, token) || other.token == token)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)); } @override -int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,errorMessage,isLoading); +int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,emailError,passwordError,errorMessage,showErrors,isLoading,token,otpCode,otpError,isOtpLoading); @override String toString() { - return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, errorMessage: $errorMessage, isLoading: $isLoading)'; + return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorMessage: $errorMessage, showErrors: $showErrors, isLoading: $isLoading, token: $token, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading)'; } @@ -45,7 +45,7 @@ abstract mixin class $LoginViewStateCopyWith<$Res> { factory $LoginViewStateCopyWith(LoginViewState value, $Res Function(LoginViewState) _then) = _$LoginViewStateCopyWithImpl; @useResult $Res call({ - String email, String password, bool passwordVisible, String errorMessage, bool isLoading + String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading }); @@ -62,13 +62,20 @@ class _$LoginViewStateCopyWithImpl<$Res> /// Create a copy of LoginViewState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? errorMessage = null,Object? isLoading = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorMessage = null,Object? showErrors = null,Object? isLoading = null,Object? token = null,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,}) { return _then(_self.copyWith( email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable -as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable +as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable +as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable +as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable +as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable as bool, )); } @@ -154,10 +161,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _LoginViewState() when $default != null: -return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _: +return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _: return orElse(); } @@ -175,10 +182,10 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMess /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading) $default,) {final _that = this; switch (_that) { case _LoginViewState(): -return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _: +return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _: throw StateError('Unexpected subclass'); } @@ -195,10 +202,10 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMess /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading)? $default,) {final _that = this; switch (_that) { case _LoginViewState() when $default != null: -return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _: +return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _: return null; } @@ -210,14 +217,21 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMess class _LoginViewState implements LoginViewState { - const _LoginViewState({this.email = '', this.password = '', this.passwordVisible = false, this.errorMessage = '', this.isLoading = false}); + const _LoginViewState({this.email = '', this.password = '', this.passwordVisible = false, this.emailError = '', this.passwordError = '', this.errorMessage = '', this.showErrors = false, this.isLoading = false, this.token = '', this.otpCode = '', this.otpError = '', this.isOtpLoading = false}); @override@JsonKey() final String email; @override@JsonKey() final String password; @override@JsonKey() final bool passwordVisible; +@override@JsonKey() final String emailError; +@override@JsonKey() final String passwordError; @override@JsonKey() final String errorMessage; +@override@JsonKey() final bool showErrors; @override@JsonKey() final bool isLoading; +@override@JsonKey() final String token; +@override@JsonKey() final String otpCode; +@override@JsonKey() final String otpError; +@override@JsonKey() final bool isOtpLoading; /// Create a copy of LoginViewState /// with the given fields replaced by the non-null parameter values. @@ -229,16 +243,16 @@ _$LoginViewStateCopyWith<_LoginViewState> get copyWith => __$LoginViewStateCopyW @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.token, token) || other.token == token)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)); } @override -int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,errorMessage,isLoading); +int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,emailError,passwordError,errorMessage,showErrors,isLoading,token,otpCode,otpError,isOtpLoading); @override String toString() { - return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, errorMessage: $errorMessage, isLoading: $isLoading)'; + return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorMessage: $errorMessage, showErrors: $showErrors, isLoading: $isLoading, token: $token, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading)'; } @@ -249,7 +263,7 @@ abstract mixin class _$LoginViewStateCopyWith<$Res> implements $LoginViewStateCo factory _$LoginViewStateCopyWith(_LoginViewState value, $Res Function(_LoginViewState) _then) = __$LoginViewStateCopyWithImpl; @override @useResult $Res call({ - String email, String password, bool passwordVisible, String errorMessage, bool isLoading + String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading }); @@ -266,13 +280,20 @@ class __$LoginViewStateCopyWithImpl<$Res> /// Create a copy of LoginViewState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? errorMessage = null,Object? isLoading = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorMessage = null,Object? showErrors = null,Object? isLoading = null,Object? token = null,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,}) { return _then(_LoginViewState( email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable -as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable +as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable +as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable +as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable +as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable as bool, )); } diff --git a/modules/auth/lib/src/features/login/presentation/widgets/otp_code_fields.dart b/modules/auth/lib/src/features/login/presentation/widgets/otp_code_fields.dart new file mode 100644 index 00000000..4b0fc151 --- /dev/null +++ b/modules/auth/lib/src/features/login/presentation/widgets/otp_code_fields.dart @@ -0,0 +1,211 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class OtpCodeFields extends StatefulWidget { + const OtpCodeFields({ + super.key, + this.length = 6, + this.autofocus = true, + this.enabled = true, + this.errorText, + this.onChanged, + this.onCompleted, + this.boxSize = 48, + this.gap = 10, + }); + + final int length; + final bool autofocus; + final bool enabled; + final String? errorText; + final ValueChanged? onChanged; + final ValueChanged? onCompleted; + final double boxSize; + final double gap; + + @override + State createState() => _OtpCodeFieldsState(); +} + +class _OtpCodeFieldsState extends State { + late final List _controllers; + late final List _focusNodes; + + String get _code => _controllers.map((c) => c.text.trim()).join(); + + @override + void initState() { + super.initState(); + _controllers = List.generate(widget.length, (_) => TextEditingController()); + _focusNodes = List.generate(widget.length, (_) => FocusNode()); + + if (widget.autofocus) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + _focusNodes.first.requestFocus(); + }); + } + } + + @override + void dispose() { + for (final c in _controllers) { + c.dispose(); + } + for (final f in _focusNodes) { + f.dispose(); + } + super.dispose(); + } + + void _emit() { + final code = _code; + widget.onChanged?.call(code); + if (code.length == widget.length && + !_controllers.any((c) => c.text.isEmpty)) { + widget.onCompleted?.call(code); + } + } + + void _setFromPaste(String value) { + final digits = value.replaceAll(RegExp(r'\D'), ''); + if (digits.isEmpty) return; + + final clipped = digits.length > widget.length + ? digits.substring(0, widget.length) + : digits; + + for (var i = 0; i < widget.length; i++) { + _controllers[i].text = i < clipped.length ? clipped[i] : ''; + } + + final nextIndex = clipped.length >= widget.length + ? widget.length - 1 + : clipped.length; + + _focusNodes[nextIndex].requestFocus(); + _emit(); + setState(() {}); + } + + void _onChanged(int index, String value) { + if (!mounted) return; + + if (value.length > 1) { + _setFromPaste(value); + return; + } + + if (value.isNotEmpty && index < widget.length - 1) { + _focusNodes[index + 1].requestFocus(); + } + + _emit(); + setState(() {}); + } + + KeyEventResult _onKey(int index, KeyEvent event) { + if (event is! KeyDownEvent) return KeyEventResult.ignored; + + if (event.logicalKey == LogicalKeyboardKey.backspace) { + final current = _controllers[index].text; + + if (current.isEmpty && index > 0) { + _controllers[index - 1].text = ''; + _focusNodes[index - 1].requestFocus(); + _emit(); + setState(() {}); + return KeyEventResult.handled; + } + } + + return KeyEventResult.ignored; + } + + @override + Widget build(BuildContext context) { + final borderColor = widget.errorText == null || widget.errorText!.isEmpty + ? Theme.of(context).dividerColor + : Theme.of(context).colorScheme.error; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + spacing: widget.gap, + children: List.generate(widget.length, (i) { + return SizedBox( + width: widget.boxSize, + height: widget.boxSize, + child: Focus( + onKeyEvent: (_, event) => _onKey(i, event), + child: TextField( + enabled: widget.enabled, + controller: _controllers[i], + focusNode: _focusNodes[i], + keyboardType: TextInputType.number, + textInputAction: i == widget.length - 1 + ? TextInputAction.done + : TextInputAction.next, + textAlign: TextAlign.center, + maxLength: 1, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + ), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(1), + ], + decoration: InputDecoration( + counterText: '', + contentPadding: EdgeInsets.zero, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide(color: borderColor), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 1.6, + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.error, + width: 1.2, + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide( + color: Theme.of(context).colorScheme.error, + width: 1.6, + ), + ), + ), + onChanged: (v) => _onChanged(i, v), + ), + ), + ); + }), + ), + if (widget.errorText != null && widget.errorText!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 10), + child: Text( + widget.errorText!, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + fontSize: 12, + ), + ), + ), + ], + ); + } +} diff --git a/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart b/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart new file mode 100644 index 00000000..27d5d21d --- /dev/null +++ b/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart @@ -0,0 +1,118 @@ +import 'package:auth/src/features/login/presentation/state/login_view_model.dart'; +import 'package:auth/src/features/login/presentation/widgets/otp_code_fields.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:sf_localizations/sf_localizations.dart'; + +class TwoFactorBottomSheet extends ConsumerWidget { + const TwoFactorBottomSheet({super.key, required this.token}); + + final String token; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(loginViewModelProvider.notifier); + + final String otpErrorKey = ref.watch( + loginViewModelProvider.select((s) => s.otpError), + ); + final bool isOtpLoading = ref.watch( + loginViewModelProvider.select((s) => s.isOtpLoading), + ); + final String otpCode = ref.watch( + loginViewModelProvider.select((s) => s.otpCode), + ); + + final String otpErrorText = otpErrorKey.isEmpty + ? '' + : context.translate(otpErrorKey); + Future onVerify() async { + FocusManager.instance.primaryFocus?.unfocus(); + + final ok = await vm.twoFactor(token: token); + if (!context.mounted) return; + + if (ok) Navigator.of(context).pop(true); + } + + return Container( + decoration: BoxDecoration( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), + ), + padding: EdgeInsets.fromLTRB( + 24, + 12, + 24, + 24 + MediaQuery.of(context).viewInsets.bottom, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 4, + width: 48, + decoration: BoxDecoration( + color: Colors.grey.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(999), + ), + ), + const SizedBox(height: 16), + + Text( + context.translate(I18n.twoFactorTitle), + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + Text( + context.translate(I18n.twoFactorSubtitle), + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 14), + ), + const SizedBox(height: 20), + + OtpCodeFields( + length: 6, + enabled: !isOtpLoading, + errorText: otpErrorText.isEmpty ? null : otpErrorText, + onChanged: (code) { + vm.setOtpCode(code); + }, + onCompleted: (_) => onVerify(), + ), + const SizedBox(height: 20), + + PrimaryButton( + onPressed: (isOtpLoading || otpCode.trim().length != 6) + ? () {} + : onVerify, + text: context.translate(I18n.twoFactorVerify), + color: theme.getColorFor(ThemeCode.buttonPrimary), + leading: isOtpLoading + ? const SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : null, + ), + + const SizedBox(height: 12), + + TextButton( + onPressed: isOtpLoading + ? null + : () => Navigator.of(context).pop(false), + child: Text(context.translate(I18n.close)), + ), + ], + ), + ); + } +} diff --git a/packages/sf_localizations/assets/l10n/de.json b/packages/sf_localizations/assets/l10n/de.json index 95f198e1..e406016c 100644 --- a/packages/sf_localizations/assets/l10n/de.json +++ b/packages/sf_localizations/assets/l10n/de.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "Du hast noch kein Konto?", - "createOneNow": "Jetzt eines erstellen" + "createOneNow": "Jetzt eines erstellen", + "errorEmailRequired": "E-Mail ist erforderlich.", + "errorEmailInvalid": "Bitte gib eine gültige E-Mail-Adresse ein.", + "errorPasswordRequired": "Passwort ist erforderlich.", + "errorPasswordMinLength": "Das Passwort muss mindestens 6 Zeichen lang sein.", + "twoFactorTitle": "Zwei-Faktor-Authentifizierung", + "twoFactorSubtitle": "Gib den 6-stelligen Code ein, um fortzufahren.", + "twoFactorCodeLabel": "Bestätigungscode", + "twoFactorCodeHint": "6-stelliger Code", + "twoFactorVerify": "Bestätigen", + "close": "Schließen", + "errorTwoFactorCodeRequired": "Der Bestätigungscode ist erforderlich.", + "errorTwoFactorCodeInvalidLength": "Der Code muss 6-stellig sein.", + "errorTwoFactorCodeInvalid": "Ungültiger Code. Bitte versuche es erneut." } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json index c106c693..5fce03fd 100755 --- a/packages/sf_localizations/assets/l10n/en.json +++ b/packages/sf_localizations/assets/l10n/en.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "Don't have an account?", - "createOneNow": "Create one now" + "createOneNow": "Create one now", + "errorEmailRequired": "Email is required.", + "errorEmailInvalid": "Please enter a valid email address.", + "errorPasswordRequired": "Password is required.", + "errorPasswordMinLength": "Password must be at least 6 characters.", + "twoFactorTitle": "Two-factor authentication", + "twoFactorSubtitle": "Enter the 6-digit code to continue.", + "twoFactorCodeLabel": "Verification code", + "twoFactorCodeHint": "6-digit code", + "twoFactorVerify": "Verify", + "close": "Close", + "errorTwoFactorCodeRequired": "The verification code is required.", + "errorTwoFactorCodeInvalidLength": "The code must be 6 digits.", + "errorTwoFactorCodeInvalid": "Invalid code. Please try again." } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json index 2f05efaf..21067dfd 100644 --- a/packages/sf_localizations/assets/l10n/es.json +++ b/packages/sf_localizations/assets/l10n/es.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "¿No tienes cuenta?", - "createOneNow": "Crear una ahora" + "createOneNow": "Crear una ahora", + "errorEmailRequired": "El email es obligatorio.", + "errorEmailInvalid": "Introduce un email válido.", + "errorPasswordRequired": "La contraseña es obligatoria.", + "errorPasswordMinLength": "La contraseña debe tener al menos 6 caracteres.", + "twoFactorTitle": "Autenticación en dos pasos", + "twoFactorSubtitle": "Introduce el código de 6 dígitos para continuar.", + "twoFactorCodeLabel": "Código de verificación", + "twoFactorCodeHint": "Código de 6 dígitos", + "twoFactorVerify": "Verificar", + "close": "Cerrar", + "errorTwoFactorCodeRequired": "El código de verificación es obligatorio.", + "errorTwoFactorCodeInvalidLength": "El código debe tener 6 dígitos.", + "errorTwoFactorCodeInvalid": "Código incorrecto. Inténtalo de nuevo." } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/fr.json b/packages/sf_localizations/assets/l10n/fr.json index 806b14d9..3b2c8f68 100644 --- a/packages/sf_localizations/assets/l10n/fr.json +++ b/packages/sf_localizations/assets/l10n/fr.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "Tu n'as pas de compte ?", - "createOneNow": "Crée-en un maintenant" + "createOneNow": "Crée-en un maintenant", + "errorEmailRequired": "L'e-mail est obligatoire.", + "errorEmailInvalid": "Veuillez saisir une adresse e-mail valide.", + "errorPasswordRequired": "Le mot de passe est obligatoire.", + "errorPasswordMinLength": "Le mot de passe doit contenir au moins 6 caractères.", + "twoFactorTitle": "Authentification à deux facteurs", + "twoFactorSubtitle": "Saisissez le code à 6 chiffres pour continuer.", + "twoFactorCodeLabel": "Code de vérification", + "twoFactorCodeHint": "Code à 6 chiffres", + "twoFactorVerify": "Vérifier", + "close": "Fermer", + "errorTwoFactorCodeRequired": "Le code de vérification est obligatoire.", + "errorTwoFactorCodeInvalidLength": "Le code doit contenir 6 chiffres.", + "errorTwoFactorCodeInvalid": "Code incorrect. Veuillez réessayer." } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/it.json b/packages/sf_localizations/assets/l10n/it.json index a5a3055e..516bc426 100644 --- a/packages/sf_localizations/assets/l10n/it.json +++ b/packages/sf_localizations/assets/l10n/it.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "Non hai un account?", - "createOneNow": "Creane uno adesso" + "createOneNow": "Creane uno adesso", + "errorEmailRequired": "L'email è obbligatoria.", + "errorEmailInvalid": "Inserisci un'email valida.", + "errorPasswordRequired": "La password è obbligatoria.", + "errorPasswordMinLength": "La password deve contenere almeno 6 caratteri.", + "twoFactorTitle": "Autenticazione a due fattori", + "twoFactorSubtitle": "Inserisci il codice a 6 cifre per continuare.", + "twoFactorCodeLabel": "Codice di verifica", + "twoFactorCodeHint": "Codice a 6 cifre", + "twoFactorVerify": "Verifica", + "close": "Chiudi", + "errorTwoFactorCodeRequired": "Il codice di verifica è obbligatorio.", + "errorTwoFactorCodeInvalidLength": "Il codice deve essere di 6 cifre.", + "errorTwoFactorCodeInvalid": "Codice non valido. Riprova." } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/pt.json b/packages/sf_localizations/assets/l10n/pt.json index 0bad019b..a3ff7bea 100644 --- a/packages/sf_localizations/assets/l10n/pt.json +++ b/packages/sf_localizations/assets/l10n/pt.json @@ -30,5 +30,18 @@ "google": "Google", "apple": "Apple", "dontHaveAccount": "Não tem conta?", - "createOneNow": "Criar uma agora" + "createOneNow": "Criar uma agora", + "errorEmailRequired": "O e-mail é obrigatório.", + "errorEmailInvalid": "Introduz um e-mail válido.", + "errorPasswordRequired": "A palavra-passe é obrigatória.", + "errorPasswordMinLength": "A palavra-passe deve ter pelo menos 6 caracteres.", + "twoFactorTitle": "Autenticação de dois fatores", + "twoFactorSubtitle": "Introduz o código de 6 dígitos para continuar.", + "twoFactorCodeLabel": "Código de verificação", + "twoFactorCodeHint": "Código de 6 dígitos", + "twoFactorVerify": "Verificar", + "close": "Fechar", + "errorTwoFactorCodeRequired": "O código de verificação é obrigatório.", + "errorTwoFactorCodeInvalidLength": "O código deve ter 6 dígitos.", + "errorTwoFactorCodeInvalid": "Código inválido. Tenta novamente." } \ No newline at end of file diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart index 0170a2df..16430635 100755 --- a/packages/sf_localizations/lib/src/generated/i18n.dart +++ b/packages/sf_localizations/lib/src/generated/i18n.dart @@ -35,4 +35,18 @@ class I18n { static const String apple = "apple"; static const String dontHaveAccount = "dontHaveAccount"; static const String createOneNow = "createOneNow"; + static const String errorEmailRequired = 'errorEmailRequired'; + static const String errorEmailInvalid = 'errorEmailInvalid'; + static const String errorPasswordRequired = 'errorPasswordRequired'; + static const String errorPasswordMinLength = 'errorPasswordMinLength'; + static const String twoFactorTitle = 'twoFactorTitle'; + static const String twoFactorSubtitle = 'twoFactorSubtitle'; + static const String twoFactorCodeLabel = 'twoFactorCodeLabel'; + static const String twoFactorCodeHint = 'twoFactorCodeHint'; + static const String twoFactorVerify = 'twoFactorVerify'; + static const String close = 'close'; + static const String errorTwoFactorCodeRequired = 'errorTwoFactorCodeRequired'; + static const String errorTwoFactorCodeInvalidLength = + 'errorTwoFactorCodeInvalidLength'; + static const String errorTwoFactorCodeInvalid = 'errorTwoFactorCodeInvalid'; } From 4c46b2f4981d6f7c13309622e6b4e623c9a35c71 Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Fri, 26 Dec 2025 00:29:15 +0100 Subject: [PATCH 2/4] sign up feature first version done --- apps/mobile_app/pubspec.lock | 16 + modules/auth/lib/auth.dart | 2 +- .../datasource/auth_remote_datasource.dart | 11 + .../auth_remote_datasource_impl.dart | 82 ++ .../src/core/data/models/address_model.dart | 31 + .../data/models/address_model.freezed.dart | 292 +++++++ .../src/core/data/models/address_model.g.dart | 27 + .../data/models/sign_up_request_model.dart | 53 ++ .../models/sign_up_request_model.freezed.dart | 328 ++++++++ .../data/models/sign_up_request_model.g.dart | 47 ++ .../data/models/sign_up_response_model.dart | 24 + .../sign_up_response_model.freezed.dart | 561 +++++++++++++ .../data/models/sign_up_response_model.g.dart | 27 + .../models/two_fa_secret_response_model.dart | 37 + .../two_fa_secret_response_model.freezed.dart | 564 +++++++++++++ .../two_fa_secret_response_model.g.dart | 31 + .../repositories/auth_repository_impl.dart | 20 + .../domain/repositories/auth_repository.dart | 11 + .../device_sign_up/device_signup_screen.dart | 7 +- .../login/presentation/login_screen.dart | 50 +- .../widgets/two_factor_bottom_sheet.dart | 96 ++- .../sign_up/account_created_screen.dart | 90 --- .../domain/entities/address_entity.dart | 15 + .../entities/address_entity.freezed.dart | 286 +++++++ .../entities/sign_up_request_entity.dart | 25 + .../sign_up_request_entity.freezed.dart | 327 ++++++++ .../domain/entities/two_fa_secret_entity.dart | 19 + .../two_fa_secret_entity.freezed.dart | 552 +++++++++++++ .../generate_two_fa_sign_up_use_case.dart | 5 + ...generate_two_fa_sign_up_use_case_impl.dart | 13 + .../sign_up/domain/sign_up_use_case.dart | 5 + .../sign_up/domain/sign_up_use_case_impl.dart | 14 + .../verify_two_fa_code_sign_up_use_case.dart | 6 + ...ify_two_fa_code_sign_up_use_case_impl.dart | 15 + .../sign_up/models/sign_up_step_config.dart | 19 + .../presentation/account_created_screen.dart | 92 +++ .../mappers/address_view_state_mapper.dart | 23 + .../mappers/sign_up_view_state_mapper.dart | 46 ++ .../generate_two_fa_sign_up_provider.dart | 10 + .../providers/sign_up_provider.dart | 9 + .../verify_two_fa_code_sign_up_provider.dart | 10 + .../presentation/sign_up_address_screen.dart | 197 +++++ .../presentation/sign_up_password_screen.dart | 35 + .../presentation/sign_up_personal_screen.dart | 135 ++++ .../sign_up/presentation/sign_up_screen.dart | 134 ++++ .../sign_up/presentation/sign_up_steps.dart | 128 +++ .../state/address_view_state.dart | 15 + .../state/address_view_state.freezed.dart | 286 +++++++ .../state/sign_up_view_model.dart | 740 ++++++++++++++++++ .../state/sign_up_view_state.dart | 45 ++ .../state/sign_up_view_state.freezed.dart | 400 ++++++++++ ...gnup_builder.dart => sign_up_builder.dart} | 2 +- .../sign_up/signup_address_screen.dart | 114 --- .../sign_up/signup_personal_screen.dart | 40 - .../src/features/sign_up/signup_screen.dart | 134 ---- .../features/sign_up/signup_user_screen.dart | 33 - .../lib/src/widgets/form_error_banner.dart | 36 + .../src/widgets/layouts/form_step_layout.dart | 128 +-- .../src/widgets/layouts/sign_up_layout.dart | 114 +++ modules/auth/lib/src/widgets/steps.dart | 1 + modules/auth/pubspec.yaml | 3 + .../lib/src/dropdowns/dropdown.dart | 29 +- 62 files changed, 6125 insertions(+), 522 deletions(-) create mode 100644 modules/auth/lib/src/core/data/models/address_model.dart create mode 100644 modules/auth/lib/src/core/data/models/address_model.freezed.dart create mode 100644 modules/auth/lib/src/core/data/models/address_model.g.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_request_model.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_response_model.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_response_model.freezed.dart create mode 100644 modules/auth/lib/src/core/data/models/sign_up_response_model.g.dart create mode 100644 modules/auth/lib/src/core/data/models/two_fa_secret_response_model.dart create mode 100644 modules/auth/lib/src/core/data/models/two_fa_secret_response_model.freezed.dart create mode 100644 modules/auth/lib/src/core/data/models/two_fa_secret_response_model.g.dart delete mode 100644 modules/auth/lib/src/features/sign_up/account_created_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/address_entity.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/address_entity.freezed.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.freezed.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.freezed.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/sign_up_use_case.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/sign_up_use_case_impl.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart create mode 100644 modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart create mode 100644 modules/auth/lib/src/features/sign_up/models/sign_up_step_config.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/mappers/sign_up_view_state_mapper.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/providers/sign_up_provider.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart rename modules/auth/lib/src/features/sign_up/{signup_builder.dart => sign_up_builder.dart} (86%) delete mode 100644 modules/auth/lib/src/features/sign_up/signup_address_screen.dart delete mode 100644 modules/auth/lib/src/features/sign_up/signup_personal_screen.dart delete mode 100644 modules/auth/lib/src/features/sign_up/signup_screen.dart delete mode 100644 modules/auth/lib/src/features/sign_up/signup_user_screen.dart create mode 100644 modules/auth/lib/src/widgets/form_error_banner.dart create mode 100644 modules/auth/lib/src/widgets/layouts/sign_up_layout.dart create mode 100644 modules/auth/lib/src/widgets/steps.dart diff --git a/apps/mobile_app/pubspec.lock b/apps/mobile_app/pubspec.lock index d43830a6..8292e0f1 100644 --- a/apps/mobile_app/pubspec.lock +++ b/apps/mobile_app/pubspec.lock @@ -472,6 +472,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: transitive + description: + name: json_serializable + sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 + url: "https://pub.dev" + source: hosted + version: "6.11.2" leak_tracker: dependency: transitive description: @@ -830,6 +838,14 @@ packages: relative: true source: path version: "0.0.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + url: "https://pub.dev" + source: hosted + version: "4.5.2" vector_graphics: dependency: transitive description: diff --git a/modules/auth/lib/auth.dart b/modules/auth/lib/auth.dart index 2991c63d..58bd1651 100644 --- a/modules/auth/lib/auth.dart +++ b/modules/auth/lib/auth.dart @@ -5,4 +5,4 @@ export 'src/features/link_phone/presentation/verify_code/verify_link_phone_code_ export 'src/features/login/login_builder.dart'; export 'src/features/recover_password/recover_password_builder.dart'; export 'src/features/device_sign_up/device_signup_builder.dart'; -export 'src/features/sign_up/signup_builder.dart'; +export 'src/features/sign_up/sign_up_builder.dart'; diff --git a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart index 1907cb09..b44aa613 100644 --- a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart +++ b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart @@ -1,3 +1,6 @@ +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; + abstract class AuthRemoteDatasource { Future requestPhoneCode({required String phone}); @@ -6,4 +9,12 @@ abstract class AuthRemoteDatasource { Future login({required String email, required String password}); Future twoFALogin({required String token, required String code}); + + Future signUp({required SignUpRequestEntity request}); + + Future generateTwoFASignUp({required String token}); + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }); } diff --git a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart index 4c9a04a4..45b63197 100644 --- a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart +++ b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart @@ -1,6 +1,12 @@ import 'dart:convert'; +import 'package:auth/src/core/data/models/sign_up_request_model.dart'; +import 'package:auth/src/core/data/models/sign_up_response_model.dart'; +import 'package:auth/src/core/data/models/two_fa_secret_response_model.dart'; +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; import 'package:sf_infrastructure/sf_infrastructure.dart'; import 'auth_remote_datasource.dart'; @@ -85,6 +91,82 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource { throw _mapDioError(error, defaultMessage: 'Error in twoFALogin'); } } + + @override + Future signUp({required SignUpRequestEntity request}) async { + try { + final body = request.toModel().toJson(); + debugPrint(body.toString()); + debugPrint(const JsonEncoder.withIndent(' ').convert(body)); + + final response = await _repository.post>( + '/payment-profiles/signup', + body: body, + ); + + final data = response.data; + if (data == null || data.isEmpty) { + throw Exception('Empty response from /payment-profiles/signup'); + } + + final parsed = SignUpResponseModel.fromJson(data); + + if (!parsed.isCreated) { + throw Exception('Sign up failed: isCreated=false'); + } + + final token = parsed.item.token.trim(); + if (token.isEmpty) { + throw Exception('Sign up response has empty token'); + } + + return token; + } on DioException catch (error) { + throw _mapDioError(error, defaultMessage: 'Error in signUp'); + } + } + + @override + Future generateTwoFASignUp({required String token}) async { + try { + final response = await _repository.post>( + '/auth/totp/secret', + body: {'token': token}, + ); + + final data = response.data; + if (data == null || data.isEmpty) { + throw Exception('Empty response from /auth/totp/secret'); + } + + final model = TwoFASecretResponseModel.fromJson(data); + return model.toEntity(); + } on DioException catch (error) { + throw _mapDioError(error, defaultMessage: 'Error in twoFASignUp'); + } + } + + @override + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }) async { + try { + final response = await _repository.post( + '/auth/totp/code', + body: {'token': token, 'code': code}, + ); + + final data = response.data; + if (data == null || data.isEmpty) { + throw Exception('Empty response from /auth/totp/code'); + } + + return data; + } on DioException catch (error) { + throw _mapDioError(error, defaultMessage: 'Error in twoFaCodeSignUp'); + } + } } Exception _mapDioError(DioException error, {required String defaultMessage}) { diff --git a/modules/auth/lib/src/core/data/models/address_model.dart b/modules/auth/lib/src/core/data/models/address_model.dart new file mode 100644 index 00000000..6caefd49 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/address_model.dart @@ -0,0 +1,31 @@ +import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'address_model.freezed.dart'; +part 'address_model.g.dart'; + +@freezed +abstract class AddressModel with _$AddressModel { + const factory AddressModel({ + required String street, + required String city, + required String province, + required String state, + required String country, + required int postCode, + }) = _AddressModel; + + factory AddressModel.fromJson(Map json) => + _$AddressModelFromJson(json); +} + +extension AddressModelMapper on AddressEntity { + AddressModel toModel() => AddressModel( + street: street, + city: city, + province: province, + state: state, + country: country, + postCode: postCode, + ); +} diff --git a/modules/auth/lib/src/core/data/models/address_model.freezed.dart b/modules/auth/lib/src/core/data/models/address_model.freezed.dart new file mode 100644 index 00000000..78f7dada --- /dev/null +++ b/modules/auth/lib/src/core/data/models/address_model.freezed.dart @@ -0,0 +1,292 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'address_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$AddressModel { + + String get street; String get city; String get province; String get state; String get country; int get postCode; +/// Create a copy of AddressModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AddressModelCopyWith get copyWith => _$AddressModelCopyWithImpl(this as AddressModel, _$identity); + + /// Serializes this AddressModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class $AddressModelCopyWith<$Res> { + factory $AddressModelCopyWith(AddressModel value, $Res Function(AddressModel) _then) = _$AddressModelCopyWithImpl; +@useResult +$Res call({ + String street, String city, String province, String state, String country, int postCode +}); + + + + +} +/// @nodoc +class _$AddressModelCopyWithImpl<$Res> + implements $AddressModelCopyWith<$Res> { + _$AddressModelCopyWithImpl(this._self, this._then); + + final AddressModel _self; + final $Res Function(AddressModel) _then; + +/// Create a copy of AddressModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) { + return _then(_self.copyWith( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int, + )); +} + +} + + +/// Adds pattern-matching-related methods to [AddressModel]. +extension AddressModelPatterns on AddressModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AddressModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AddressModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AddressModel value) $default,){ +final _that = this; +switch (_that) { +case _AddressModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AddressModel value)? $default,){ +final _that = this; +switch (_that) { +case _AddressModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AddressModel() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String street, String city, String province, String state, String country, int postCode) $default,) {final _that = this; +switch (_that) { +case _AddressModel(): +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this; +switch (_that) { +case _AddressModel() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _AddressModel implements AddressModel { + const _AddressModel({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode}); + factory _AddressModel.fromJson(Map json) => _$AddressModelFromJson(json); + +@override final String street; +@override final String city; +@override final String province; +@override final String state; +@override final String country; +@override final int postCode; + +/// Create a copy of AddressModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AddressModelCopyWith<_AddressModel> get copyWith => __$AddressModelCopyWithImpl<_AddressModel>(this, _$identity); + +@override +Map toJson() { + return _$AddressModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class _$AddressModelCopyWith<$Res> implements $AddressModelCopyWith<$Res> { + factory _$AddressModelCopyWith(_AddressModel value, $Res Function(_AddressModel) _then) = __$AddressModelCopyWithImpl; +@override @useResult +$Res call({ + String street, String city, String province, String state, String country, int postCode +}); + + + + +} +/// @nodoc +class __$AddressModelCopyWithImpl<$Res> + implements _$AddressModelCopyWith<$Res> { + __$AddressModelCopyWithImpl(this._self, this._then); + + final _AddressModel _self; + final $Res Function(_AddressModel) _then; + +/// Create a copy of AddressModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) { + return _then(_AddressModel( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/core/data/models/address_model.g.dart b/modules/auth/lib/src/core/data/models/address_model.g.dart new file mode 100644 index 00000000..477011e7 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/address_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'address_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_AddressModel _$AddressModelFromJson(Map json) => + _AddressModel( + street: json['street'] as String, + city: json['city'] as String, + province: json['province'] as String, + state: json['state'] as String, + country: json['country'] as String, + postCode: (json['postCode'] as num).toInt(), + ); + +Map _$AddressModelToJson(_AddressModel instance) => + { + 'street': instance.street, + 'city': instance.city, + 'province': instance.province, + 'state': instance.state, + 'country': instance.country, + 'postCode': instance.postCode, + }; diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.dart new file mode 100644 index 00000000..e89ff0d2 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.dart @@ -0,0 +1,53 @@ +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'address_model.dart'; + +part 'sign_up_request_model.freezed.dart'; +part 'sign_up_request_model.g.dart'; + +@freezed +abstract class SignUpRequestModel with _$SignUpRequestModel { + const factory SignUpRequestModel({ + required String firstName, + required String lastName, + required String email, + required String phone, + required String language, + required String password, + required List taxResidences, + required List addresses, + required int bornAt, + required String userId, + required String placeOfBirth, + required String birthCountry, + + @JsonKey(name: 'dni') required String documentNumber, + + required String relationship, + }) = _SignUpRequestModel; + + factory SignUpRequestModel.fromJson(Map json) => + _$SignUpRequestModelFromJson(json); +} + +extension SignUpRequestModelMapper on SignUpRequestEntity { + SignUpRequestModel toModel() => SignUpRequestModel( + firstName: firstName, + lastName: lastName, + email: email, + phone: phone, + language: language, + password: password, + taxResidences: taxResidences + .map((e) => e.toModel()) + .toList(growable: false), + addresses: addresses.map((e) => e.toModel()).toList(growable: false), + bornAt: bornAt, + userId: userId, + placeOfBirth: placeOfBirth, + birthCountry: birthCountry, + documentNumber: documentNumber, + + relationship: relationship, + ); +} diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart new file mode 100644 index 00000000..74e87167 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart @@ -0,0 +1,328 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sign_up_request_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$SignUpRequestModel { + + String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;@JsonKey(name: 'dni') String get documentNumber; String get relationship; +/// Create a copy of SignUpRequestModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SignUpRequestModelCopyWith get copyWith => _$SignUpRequestModelCopyWithImpl(this as SignUpRequestModel, _$identity); + + /// Serializes this SignUpRequestModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,relationship); + +@override +String toString() { + return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, relationship: $relationship)'; +} + + +} + +/// @nodoc +abstract mixin class $SignUpRequestModelCopyWith<$Res> { + factory $SignUpRequestModelCopyWith(SignUpRequestModel value, $Res Function(SignUpRequestModel) _then) = _$SignUpRequestModelCopyWithImpl; +@useResult +$Res call({ + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String relationship +}); + + + + +} +/// @nodoc +class _$SignUpRequestModelCopyWithImpl<$Res> + implements $SignUpRequestModelCopyWith<$Res> { + _$SignUpRequestModelCopyWithImpl(this._self, this._then); + + final SignUpRequestModel _self; + final $Res Function(SignUpRequestModel) _then; + +/// Create a copy of SignUpRequestModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? relationship = null,}) { + return _then(_self.copyWith( +firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable +as List,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable +as List,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SignUpRequestModel]. +extension SignUpRequestModelPatterns on SignUpRequestModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SignUpRequestModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SignUpRequestModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SignUpRequestModel value) $default,){ +final _that = this; +switch (_that) { +case _SignUpRequestModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SignUpRequestModel value)? $default,){ +final _that = this; +switch (_that) { +case _SignUpRequestModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SignUpRequestModel() when $default != null: +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship) $default,) {final _that = this; +switch (_that) { +case _SignUpRequestModel(): +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship)? $default,) {final _that = this; +switch (_that) { +case _SignUpRequestModel() when $default != null: +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SignUpRequestModel implements SignUpRequestModel { + const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'dni') required this.documentNumber, required this.relationship}): _taxResidences = taxResidences,_addresses = addresses; + factory _SignUpRequestModel.fromJson(Map json) => _$SignUpRequestModelFromJson(json); + +@override final String firstName; +@override final String lastName; +@override final String email; +@override final String phone; +@override final String language; +@override final String password; + final List _taxResidences; +@override List get taxResidences { + if (_taxResidences is EqualUnmodifiableListView) return _taxResidences; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_taxResidences); +} + + final List _addresses; +@override List get addresses { + if (_addresses is EqualUnmodifiableListView) return _addresses; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_addresses); +} + +@override final int bornAt; +@override final String userId; +@override final String placeOfBirth; +@override final String birthCountry; +@override@JsonKey(name: 'dni') final String documentNumber; +@override final String relationship; + +/// Create a copy of SignUpRequestModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SignUpRequestModelCopyWith<_SignUpRequestModel> get copyWith => __$SignUpRequestModelCopyWithImpl<_SignUpRequestModel>(this, _$identity); + +@override +Map toJson() { + return _$SignUpRequestModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,relationship); + +@override +String toString() { + return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, relationship: $relationship)'; +} + + +} + +/// @nodoc +abstract mixin class _$SignUpRequestModelCopyWith<$Res> implements $SignUpRequestModelCopyWith<$Res> { + factory _$SignUpRequestModelCopyWith(_SignUpRequestModel value, $Res Function(_SignUpRequestModel) _then) = __$SignUpRequestModelCopyWithImpl; +@override @useResult +$Res call({ + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String relationship +}); + + + + +} +/// @nodoc +class __$SignUpRequestModelCopyWithImpl<$Res> + implements _$SignUpRequestModelCopyWith<$Res> { + __$SignUpRequestModelCopyWithImpl(this._self, this._then); + + final _SignUpRequestModel _self; + final $Res Function(_SignUpRequestModel) _then; + +/// Create a copy of SignUpRequestModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? relationship = null,}) { + return _then(_SignUpRequestModel( +firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable +as List,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable +as List,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart new file mode 100644 index 00000000..e89fba61 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart @@ -0,0 +1,47 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sign_up_request_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SignUpRequestModel _$SignUpRequestModelFromJson(Map json) => + _SignUpRequestModel( + firstName: json['firstName'] as String, + lastName: json['lastName'] as String, + email: json['email'] as String, + phone: json['phone'] as String, + language: json['language'] as String, + password: json['password'] as String, + taxResidences: (json['taxResidences'] as List) + .map((e) => AddressModel.fromJson(e as Map)) + .toList(), + addresses: (json['addresses'] as List) + .map((e) => AddressModel.fromJson(e as Map)) + .toList(), + bornAt: (json['bornAt'] as num).toInt(), + userId: json['userId'] as String, + placeOfBirth: json['placeOfBirth'] as String, + birthCountry: json['birthCountry'] as String, + documentNumber: json['dni'] as String, + relationship: json['relationship'] as String, + ); + +Map _$SignUpRequestModelToJson(_SignUpRequestModel instance) => + { + 'firstName': instance.firstName, + 'lastName': instance.lastName, + 'email': instance.email, + 'phone': instance.phone, + 'language': instance.language, + 'password': instance.password, + 'taxResidences': instance.taxResidences, + 'addresses': instance.addresses, + 'bornAt': instance.bornAt, + 'userId': instance.userId, + 'placeOfBirth': instance.placeOfBirth, + 'birthCountry': instance.birthCountry, + 'dni': instance.documentNumber, + 'relationship': instance.relationship, + }; diff --git a/modules/auth/lib/src/core/data/models/sign_up_response_model.dart b/modules/auth/lib/src/core/data/models/sign_up_response_model.dart new file mode 100644 index 00000000..52340c70 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_response_model.dart @@ -0,0 +1,24 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'sign_up_response_model.freezed.dart'; +part 'sign_up_response_model.g.dart'; + +@freezed +abstract class SignUpResponseModel with _$SignUpResponseModel { + const factory SignUpResponseModel({ + required bool isCreated, + required SignUpResponseItemModel item, + }) = _SignUpResponseModel; + + factory SignUpResponseModel.fromJson(Map json) => + _$SignUpResponseModelFromJson(json); +} + +@freezed +abstract class SignUpResponseItemModel with _$SignUpResponseItemModel { + const factory SignUpResponseItemModel({required String token}) = + _SignUpResponseItemModel; + + factory SignUpResponseItemModel.fromJson(Map json) => + _$SignUpResponseItemModelFromJson(json); +} diff --git a/modules/auth/lib/src/core/data/models/sign_up_response_model.freezed.dart b/modules/auth/lib/src/core/data/models/sign_up_response_model.freezed.dart new file mode 100644 index 00000000..02a9cb64 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_response_model.freezed.dart @@ -0,0 +1,561 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sign_up_response_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$SignUpResponseModel { + + bool get isCreated; SignUpResponseItemModel get item; +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SignUpResponseModelCopyWith get copyWith => _$SignUpResponseModelCopyWithImpl(this as SignUpResponseModel, _$identity); + + /// Serializes this SignUpResponseModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'SignUpResponseModel(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class $SignUpResponseModelCopyWith<$Res> { + factory $SignUpResponseModelCopyWith(SignUpResponseModel value, $Res Function(SignUpResponseModel) _then) = _$SignUpResponseModelCopyWithImpl; +@useResult +$Res call({ + bool isCreated, SignUpResponseItemModel item +}); + + +$SignUpResponseItemModelCopyWith<$Res> get item; + +} +/// @nodoc +class _$SignUpResponseModelCopyWithImpl<$Res> + implements $SignUpResponseModelCopyWith<$Res> { + _$SignUpResponseModelCopyWithImpl(this._self, this._then); + + final SignUpResponseModel _self; + final $Res Function(SignUpResponseModel) _then; + +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_self.copyWith( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as SignUpResponseItemModel, + )); +} +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SignUpResponseItemModelCopyWith<$Res> get item { + + return $SignUpResponseItemModelCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [SignUpResponseModel]. +extension SignUpResponseModelPatterns on SignUpResponseModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SignUpResponseModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SignUpResponseModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SignUpResponseModel value) $default,){ +final _that = this; +switch (_that) { +case _SignUpResponseModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SignUpResponseModel value)? $default,){ +final _that = this; +switch (_that) { +case _SignUpResponseModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isCreated, SignUpResponseItemModel item)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SignUpResponseModel() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool isCreated, SignUpResponseItemModel item) $default,) {final _that = this; +switch (_that) { +case _SignUpResponseModel(): +return $default(_that.isCreated,_that.item);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isCreated, SignUpResponseItemModel item)? $default,) {final _that = this; +switch (_that) { +case _SignUpResponseModel() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SignUpResponseModel implements SignUpResponseModel { + const _SignUpResponseModel({required this.isCreated, required this.item}); + factory _SignUpResponseModel.fromJson(Map json) => _$SignUpResponseModelFromJson(json); + +@override final bool isCreated; +@override final SignUpResponseItemModel item; + +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SignUpResponseModelCopyWith<_SignUpResponseModel> get copyWith => __$SignUpResponseModelCopyWithImpl<_SignUpResponseModel>(this, _$identity); + +@override +Map toJson() { + return _$SignUpResponseModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'SignUpResponseModel(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class _$SignUpResponseModelCopyWith<$Res> implements $SignUpResponseModelCopyWith<$Res> { + factory _$SignUpResponseModelCopyWith(_SignUpResponseModel value, $Res Function(_SignUpResponseModel) _then) = __$SignUpResponseModelCopyWithImpl; +@override @useResult +$Res call({ + bool isCreated, SignUpResponseItemModel item +}); + + +@override $SignUpResponseItemModelCopyWith<$Res> get item; + +} +/// @nodoc +class __$SignUpResponseModelCopyWithImpl<$Res> + implements _$SignUpResponseModelCopyWith<$Res> { + __$SignUpResponseModelCopyWithImpl(this._self, this._then); + + final _SignUpResponseModel _self; + final $Res Function(_SignUpResponseModel) _then; + +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_SignUpResponseModel( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as SignUpResponseItemModel, + )); +} + +/// Create a copy of SignUpResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SignUpResponseItemModelCopyWith<$Res> get item { + + return $SignUpResponseItemModelCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + + +/// @nodoc +mixin _$SignUpResponseItemModel { + + String get token; +/// Create a copy of SignUpResponseItemModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SignUpResponseItemModelCopyWith get copyWith => _$SignUpResponseItemModelCopyWithImpl(this as SignUpResponseItemModel, _$identity); + + /// Serializes this SignUpResponseItemModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpResponseItemModel&&(identical(other.token, token) || other.token == token)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,token); + +@override +String toString() { + return 'SignUpResponseItemModel(token: $token)'; +} + + +} + +/// @nodoc +abstract mixin class $SignUpResponseItemModelCopyWith<$Res> { + factory $SignUpResponseItemModelCopyWith(SignUpResponseItemModel value, $Res Function(SignUpResponseItemModel) _then) = _$SignUpResponseItemModelCopyWithImpl; +@useResult +$Res call({ + String token +}); + + + + +} +/// @nodoc +class _$SignUpResponseItemModelCopyWithImpl<$Res> + implements $SignUpResponseItemModelCopyWith<$Res> { + _$SignUpResponseItemModelCopyWithImpl(this._self, this._then); + + final SignUpResponseItemModel _self; + final $Res Function(SignUpResponseItemModel) _then; + +/// Create a copy of SignUpResponseItemModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? token = null,}) { + return _then(_self.copyWith( +token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SignUpResponseItemModel]. +extension SignUpResponseItemModelPatterns on SignUpResponseItemModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SignUpResponseItemModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SignUpResponseItemModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SignUpResponseItemModel value) $default,){ +final _that = this; +switch (_that) { +case _SignUpResponseItemModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SignUpResponseItemModel value)? $default,){ +final _that = this; +switch (_that) { +case _SignUpResponseItemModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String token)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SignUpResponseItemModel() when $default != null: +return $default(_that.token);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String token) $default,) {final _that = this; +switch (_that) { +case _SignUpResponseItemModel(): +return $default(_that.token);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String token)? $default,) {final _that = this; +switch (_that) { +case _SignUpResponseItemModel() when $default != null: +return $default(_that.token);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SignUpResponseItemModel implements SignUpResponseItemModel { + const _SignUpResponseItemModel({required this.token}); + factory _SignUpResponseItemModel.fromJson(Map json) => _$SignUpResponseItemModelFromJson(json); + +@override final String token; + +/// Create a copy of SignUpResponseItemModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SignUpResponseItemModelCopyWith<_SignUpResponseItemModel> get copyWith => __$SignUpResponseItemModelCopyWithImpl<_SignUpResponseItemModel>(this, _$identity); + +@override +Map toJson() { + return _$SignUpResponseItemModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpResponseItemModel&&(identical(other.token, token) || other.token == token)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,token); + +@override +String toString() { + return 'SignUpResponseItemModel(token: $token)'; +} + + +} + +/// @nodoc +abstract mixin class _$SignUpResponseItemModelCopyWith<$Res> implements $SignUpResponseItemModelCopyWith<$Res> { + factory _$SignUpResponseItemModelCopyWith(_SignUpResponseItemModel value, $Res Function(_SignUpResponseItemModel) _then) = __$SignUpResponseItemModelCopyWithImpl; +@override @useResult +$Res call({ + String token +}); + + + + +} +/// @nodoc +class __$SignUpResponseItemModelCopyWithImpl<$Res> + implements _$SignUpResponseItemModelCopyWith<$Res> { + __$SignUpResponseItemModelCopyWithImpl(this._self, this._then); + + final _SignUpResponseItemModel _self; + final $Res Function(_SignUpResponseItemModel) _then; + +/// Create a copy of SignUpResponseItemModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? token = null,}) { + return _then(_SignUpResponseItemModel( +token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/core/data/models/sign_up_response_model.g.dart b/modules/auth/lib/src/core/data/models/sign_up_response_model.g.dart new file mode 100644 index 00000000..03168148 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/sign_up_response_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sign_up_response_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SignUpResponseModel _$SignUpResponseModelFromJson(Map json) => + _SignUpResponseModel( + isCreated: json['isCreated'] as bool, + item: SignUpResponseItemModel.fromJson( + json['item'] as Map, + ), + ); + +Map _$SignUpResponseModelToJson( + _SignUpResponseModel instance, +) => {'isCreated': instance.isCreated, 'item': instance.item}; + +_SignUpResponseItemModel _$SignUpResponseItemModelFromJson( + Map json, +) => _SignUpResponseItemModel(token: json['token'] as String); + +Map _$SignUpResponseItemModelToJson( + _SignUpResponseItemModel instance, +) => {'token': instance.token}; diff --git a/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.dart b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.dart new file mode 100644 index 00000000..f3ea65bf --- /dev/null +++ b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.dart @@ -0,0 +1,37 @@ +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'two_fa_secret_response_model.freezed.dart'; +part 'two_fa_secret_response_model.g.dart'; + +@freezed +abstract class TwoFASecretResponseModel with _$TwoFASecretResponseModel { + const factory TwoFASecretResponseModel({ + required bool isCreated, + required TwoFASecretItemResponseModel item, + }) = _TwoFASecretResponseModel; + + factory TwoFASecretResponseModel.fromJson(Map json) => + _$TwoFASecretResponseModelFromJson(json); +} + +@freezed +abstract class TwoFASecretItemResponseModel + with _$TwoFASecretItemResponseModel { + const factory TwoFASecretItemResponseModel({ + required String secret, + required String qr, + }) = _TwoFASecretItemResponseModel; + + factory TwoFASecretItemResponseModel.fromJson(Map json) => + _$TwoFASecretItemResponseModelFromJson(json); +} + +extension TwoFASecretResponseModelMapper on TwoFASecretResponseModel { + TwoFASecretEntity toEntity() { + return TwoFASecretEntity( + isCreated: isCreated, + item: TwoFASecretItemEntity(secret: item.secret, qr: item.qr), + ); + } +} diff --git a/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.freezed.dart b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.freezed.dart new file mode 100644 index 00000000..605e8bed --- /dev/null +++ b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.freezed.dart @@ -0,0 +1,564 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'two_fa_secret_response_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$TwoFASecretResponseModel { + + bool get isCreated; TwoFASecretItemResponseModel get item; +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$TwoFASecretResponseModelCopyWith get copyWith => _$TwoFASecretResponseModelCopyWithImpl(this as TwoFASecretResponseModel, _$identity); + + /// Serializes this TwoFASecretResponseModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'TwoFASecretResponseModel(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class $TwoFASecretResponseModelCopyWith<$Res> { + factory $TwoFASecretResponseModelCopyWith(TwoFASecretResponseModel value, $Res Function(TwoFASecretResponseModel) _then) = _$TwoFASecretResponseModelCopyWithImpl; +@useResult +$Res call({ + bool isCreated, TwoFASecretItemResponseModel item +}); + + +$TwoFASecretItemResponseModelCopyWith<$Res> get item; + +} +/// @nodoc +class _$TwoFASecretResponseModelCopyWithImpl<$Res> + implements $TwoFASecretResponseModelCopyWith<$Res> { + _$TwoFASecretResponseModelCopyWithImpl(this._self, this._then); + + final TwoFASecretResponseModel _self; + final $Res Function(TwoFASecretResponseModel) _then; + +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_self.copyWith( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as TwoFASecretItemResponseModel, + )); +} +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretItemResponseModelCopyWith<$Res> get item { + + return $TwoFASecretItemResponseModelCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [TwoFASecretResponseModel]. +extension TwoFASecretResponseModelPatterns on TwoFASecretResponseModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _TwoFASecretResponseModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _TwoFASecretResponseModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _TwoFASecretResponseModel value) $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretResponseModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _TwoFASecretResponseModel value)? $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretResponseModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isCreated, TwoFASecretItemResponseModel item)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _TwoFASecretResponseModel() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool isCreated, TwoFASecretItemResponseModel item) $default,) {final _that = this; +switch (_that) { +case _TwoFASecretResponseModel(): +return $default(_that.isCreated,_that.item);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isCreated, TwoFASecretItemResponseModel item)? $default,) {final _that = this; +switch (_that) { +case _TwoFASecretResponseModel() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _TwoFASecretResponseModel implements TwoFASecretResponseModel { + const _TwoFASecretResponseModel({required this.isCreated, required this.item}); + factory _TwoFASecretResponseModel.fromJson(Map json) => _$TwoFASecretResponseModelFromJson(json); + +@override final bool isCreated; +@override final TwoFASecretItemResponseModel item; + +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$TwoFASecretResponseModelCopyWith<_TwoFASecretResponseModel> get copyWith => __$TwoFASecretResponseModelCopyWithImpl<_TwoFASecretResponseModel>(this, _$identity); + +@override +Map toJson() { + return _$TwoFASecretResponseModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'TwoFASecretResponseModel(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class _$TwoFASecretResponseModelCopyWith<$Res> implements $TwoFASecretResponseModelCopyWith<$Res> { + factory _$TwoFASecretResponseModelCopyWith(_TwoFASecretResponseModel value, $Res Function(_TwoFASecretResponseModel) _then) = __$TwoFASecretResponseModelCopyWithImpl; +@override @useResult +$Res call({ + bool isCreated, TwoFASecretItemResponseModel item +}); + + +@override $TwoFASecretItemResponseModelCopyWith<$Res> get item; + +} +/// @nodoc +class __$TwoFASecretResponseModelCopyWithImpl<$Res> + implements _$TwoFASecretResponseModelCopyWith<$Res> { + __$TwoFASecretResponseModelCopyWithImpl(this._self, this._then); + + final _TwoFASecretResponseModel _self; + final $Res Function(_TwoFASecretResponseModel) _then; + +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_TwoFASecretResponseModel( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as TwoFASecretItemResponseModel, + )); +} + +/// Create a copy of TwoFASecretResponseModel +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretItemResponseModelCopyWith<$Res> get item { + + return $TwoFASecretItemResponseModelCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + + +/// @nodoc +mixin _$TwoFASecretItemResponseModel { + + String get secret; String get qr; +/// Create a copy of TwoFASecretItemResponseModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$TwoFASecretItemResponseModelCopyWith get copyWith => _$TwoFASecretItemResponseModelCopyWithImpl(this as TwoFASecretItemResponseModel, _$identity); + + /// Serializes this TwoFASecretItemResponseModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretItemResponseModel&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,secret,qr); + +@override +String toString() { + return 'TwoFASecretItemResponseModel(secret: $secret, qr: $qr)'; +} + + +} + +/// @nodoc +abstract mixin class $TwoFASecretItemResponseModelCopyWith<$Res> { + factory $TwoFASecretItemResponseModelCopyWith(TwoFASecretItemResponseModel value, $Res Function(TwoFASecretItemResponseModel) _then) = _$TwoFASecretItemResponseModelCopyWithImpl; +@useResult +$Res call({ + String secret, String qr +}); + + + + +} +/// @nodoc +class _$TwoFASecretItemResponseModelCopyWithImpl<$Res> + implements $TwoFASecretItemResponseModelCopyWith<$Res> { + _$TwoFASecretItemResponseModelCopyWithImpl(this._self, this._then); + + final TwoFASecretItemResponseModel _self; + final $Res Function(TwoFASecretItemResponseModel) _then; + +/// Create a copy of TwoFASecretItemResponseModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? secret = null,Object? qr = null,}) { + return _then(_self.copyWith( +secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable +as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [TwoFASecretItemResponseModel]. +extension TwoFASecretItemResponseModelPatterns on TwoFASecretItemResponseModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _TwoFASecretItemResponseModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _TwoFASecretItemResponseModel value) $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _TwoFASecretItemResponseModel value)? $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String secret, String qr)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel() when $default != null: +return $default(_that.secret,_that.qr);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String secret, String qr) $default,) {final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel(): +return $default(_that.secret,_that.qr);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String secret, String qr)? $default,) {final _that = this; +switch (_that) { +case _TwoFASecretItemResponseModel() when $default != null: +return $default(_that.secret,_that.qr);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _TwoFASecretItemResponseModel implements TwoFASecretItemResponseModel { + const _TwoFASecretItemResponseModel({required this.secret, required this.qr}); + factory _TwoFASecretItemResponseModel.fromJson(Map json) => _$TwoFASecretItemResponseModelFromJson(json); + +@override final String secret; +@override final String qr; + +/// Create a copy of TwoFASecretItemResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$TwoFASecretItemResponseModelCopyWith<_TwoFASecretItemResponseModel> get copyWith => __$TwoFASecretItemResponseModelCopyWithImpl<_TwoFASecretItemResponseModel>(this, _$identity); + +@override +Map toJson() { + return _$TwoFASecretItemResponseModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretItemResponseModel&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,secret,qr); + +@override +String toString() { + return 'TwoFASecretItemResponseModel(secret: $secret, qr: $qr)'; +} + + +} + +/// @nodoc +abstract mixin class _$TwoFASecretItemResponseModelCopyWith<$Res> implements $TwoFASecretItemResponseModelCopyWith<$Res> { + factory _$TwoFASecretItemResponseModelCopyWith(_TwoFASecretItemResponseModel value, $Res Function(_TwoFASecretItemResponseModel) _then) = __$TwoFASecretItemResponseModelCopyWithImpl; +@override @useResult +$Res call({ + String secret, String qr +}); + + + + +} +/// @nodoc +class __$TwoFASecretItemResponseModelCopyWithImpl<$Res> + implements _$TwoFASecretItemResponseModelCopyWith<$Res> { + __$TwoFASecretItemResponseModelCopyWithImpl(this._self, this._then); + + final _TwoFASecretItemResponseModel _self; + final $Res Function(_TwoFASecretItemResponseModel) _then; + +/// Create a copy of TwoFASecretItemResponseModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? secret = null,Object? qr = null,}) { + return _then(_TwoFASecretItemResponseModel( +secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable +as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.g.dart b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.g.dart new file mode 100644 index 00000000..da9b5562 --- /dev/null +++ b/modules/auth/lib/src/core/data/models/two_fa_secret_response_model.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'two_fa_secret_response_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_TwoFASecretResponseModel _$TwoFASecretResponseModelFromJson( + Map json, +) => _TwoFASecretResponseModel( + isCreated: json['isCreated'] as bool, + item: TwoFASecretItemResponseModel.fromJson( + json['item'] as Map, + ), +); + +Map _$TwoFASecretResponseModelToJson( + _TwoFASecretResponseModel instance, +) => {'isCreated': instance.isCreated, 'item': instance.item}; + +_TwoFASecretItemResponseModel _$TwoFASecretItemResponseModelFromJson( + Map json, +) => _TwoFASecretItemResponseModel( + secret: json['secret'] as String, + qr: json['qr'] as String, +); + +Map _$TwoFASecretItemResponseModelToJson( + _TwoFASecretItemResponseModel instance, +) => {'secret': instance.secret, 'qr': instance.qr}; diff --git a/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart index a0bb81c6..d3d8188b 100644 --- a/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart +++ b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart @@ -1,5 +1,7 @@ import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart'; import 'package:auth/src/core/domain/repositories/auth_repository.dart'; +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; class AuthRepositoryImpl implements AuthRepository { const AuthRepositoryImpl(this._remote); @@ -25,4 +27,22 @@ class AuthRepositoryImpl implements AuthRepository { Future twoFactor({required String token, required String code}) { return _remote.twoFALogin(token: token, code: code); } + + @override + Future signUp({required SignUpRequestEntity request}) { + return _remote.signUp(request: request); + } + + @override + Future generateTwoFASignUp({required String token}) { + return _remote.generateTwoFASignUp(token: token); + } + + @override + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }) { + return _remote.verifyTwoFACodeSignUp(token: token, code: code); + } } diff --git a/modules/auth/lib/src/core/domain/repositories/auth_repository.dart b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart index c011369d..9308cfad 100644 --- a/modules/auth/lib/src/core/domain/repositories/auth_repository.dart +++ b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart @@ -1,3 +1,6 @@ +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; + abstract class AuthRepository { Future requestPhoneCode({required String phone}); @@ -6,4 +9,12 @@ abstract class AuthRepository { Future login({required String email, required String password}); Future twoFactor({required String token, required String code}); + + Future signUp({required SignUpRequestEntity request}); + + Future generateTwoFASignUp({required String token}); + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }); } diff --git a/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart index 2db8e906..6a954195 100644 --- a/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart +++ b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart @@ -2,7 +2,7 @@ import 'package:auth/auth.dart'; import 'package:auth/src/features/device_sign_up/add_kid_screen.dart'; import 'package:auth/src/features/device_sign_up/link_watch/link_watch_screen.dart'; import 'package:auth/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart'; -import 'package:auth/src/features/sign_up/account_created_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/account_created_screen.dart'; import 'package:auth/src/widgets/layouts/form_step_layout.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; @@ -103,10 +103,7 @@ class DeviceSignupScreenState extends ConsumerState { nextStep: () => {}, previousStep: () => {}, ), - AccountCreatedScreen( - navigationContract: navigationContract, - kidAccount: true, - ), + AccountCreatedScreen(navigationContract: navigationContract), ]; } } diff --git a/modules/auth/lib/src/features/login/presentation/login_screen.dart b/modules/auth/lib/src/features/login/presentation/login_screen.dart index ae18c21b..cf11c562 100644 --- a/modules/auth/lib/src/features/login/presentation/login_screen.dart +++ b/modules/auth/lib/src/features/login/presentation/login_screen.dart @@ -31,7 +31,51 @@ class LoginScreen extends ConsumerWidget { isDismissible: false, enableDrag: false, backgroundColor: Colors.transparent, - builder: (_) => TwoFactorBottomSheet(token: token), + builder: (context) { + return Consumer( + builder: (context, ref, _) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(loginViewModelProvider.notifier); + + final otpErrorKey = ref.watch( + loginViewModelProvider.select((s) => s.otpError), + ); + final isOtpLoading = ref.watch( + loginViewModelProvider.select((s) => s.isOtpLoading), + ); + final otpCode = ref.watch( + loginViewModelProvider.select((s) => s.otpCode), + ); + + final otpErrorText = otpErrorKey.isEmpty + ? '' + : context.translate(otpErrorKey); + + Future onVerify() async { + FocusManager.instance.primaryFocus?.unfocus(); + + final ok = await vm.twoFactor(token: token); + if (!context.mounted) return; + + if (ok) Navigator.of(context).pop(true); + } + + return TwoFactorBottomSheetView( + theme: theme, + title: context.translate(I18n.twoFactorTitle), + subtitle: context.translate(I18n.twoFactorSubtitle), + verifyText: context.translate(I18n.twoFactorVerify), + closeText: context.translate(I18n.close), + isOtpLoading: isOtpLoading, + otpCode: otpCode, + otpErrorText: otpErrorText, + onChanged: vm.setOtpCode, + onVerify: onVerify, + onClose: () => Navigator.of(context).pop(false), + ); + }, + ); + }, ); if (!context.mounted) return; @@ -54,7 +98,7 @@ class LoginScreen extends ConsumerWidget { child: AbsorbPointer( absorbing: isLoading, child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: 24, vertical: 40), + padding: EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -320,7 +364,7 @@ class _Footer extends ConsumerWidget { TextButton( onPressed: isLoading ? null - : () => navigationContract.goTo(AppRoutes.signup), + : () => navigationContract.pushTo(AppRoutes.signup), child: Text( context.translate(I18n.createOneNow), style: const TextStyle( diff --git a/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart b/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart index 27d5d21d..decb7a31 100644 --- a/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart +++ b/modules/auth/lib/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart @@ -1,53 +1,52 @@ -import 'package:auth/src/features/login/presentation/state/login_view_model.dart'; +import 'dart:async'; + import 'package:auth/src/features/login/presentation/widgets/otp_code_fields.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:sf_localizations/sf_localizations.dart'; -class TwoFactorBottomSheet extends ConsumerWidget { - const TwoFactorBottomSheet({super.key, required this.token}); +class TwoFactorBottomSheetView extends StatelessWidget { + const TwoFactorBottomSheetView({ + super.key, + required this.theme, + required this.title, + required this.subtitle, + required this.verifyText, + required this.closeText, + required this.isOtpLoading, + required this.otpCode, + required this.otpErrorText, + required this.onChanged, + required this.onVerify, + required this.onClose, + }); - final String token; + final ThemePort theme; + + final String title; + final String subtitle; + final String verifyText; + final String closeText; + + final bool isOtpLoading; + final String otpCode; + final String otpErrorText; + + final ValueChanged onChanged; + final Future Function() onVerify; + final VoidCallback onClose; + + bool get _isValidOtp => otpCode.trim().length == 6; @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = ref.watch(themePortProvider); - final vm = ref.read(loginViewModelProvider.notifier); - - final String otpErrorKey = ref.watch( - loginViewModelProvider.select((s) => s.otpError), - ); - final bool isOtpLoading = ref.watch( - loginViewModelProvider.select((s) => s.isOtpLoading), - ); - final String otpCode = ref.watch( - loginViewModelProvider.select((s) => s.otpCode), - ); - - final String otpErrorText = otpErrorKey.isEmpty - ? '' - : context.translate(otpErrorKey); - Future onVerify() async { - FocusManager.instance.primaryFocus?.unfocus(); - - final ok = await vm.twoFactor(token: token); - if (!context.mounted) return; - - if (ok) Navigator.of(context).pop(true); - } + Widget build(BuildContext context) { + final bottomInset = MediaQuery.of(context).viewInsets.bottom; return Container( decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundPrimary), borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), ), - padding: EdgeInsets.fromLTRB( - 24, - 12, - 24, - 24 + MediaQuery.of(context).viewInsets.bottom, - ), + padding: EdgeInsets.fromLTRB(24, 12, 24, 24 + bottomInset), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -62,13 +61,13 @@ class TwoFactorBottomSheet extends ConsumerWidget { const SizedBox(height: 16), Text( - context.translate(I18n.twoFactorTitle), + title, textAlign: TextAlign.center, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700), ), const SizedBox(height: 8), Text( - context.translate(I18n.twoFactorSubtitle), + subtitle, textAlign: TextAlign.center, style: const TextStyle(fontSize: 14), ), @@ -78,18 +77,19 @@ class TwoFactorBottomSheet extends ConsumerWidget { length: 6, enabled: !isOtpLoading, errorText: otpErrorText.isEmpty ? null : otpErrorText, - onChanged: (code) { - vm.setOtpCode(code); + onChanged: onChanged, + onCompleted: (_) { + if (isOtpLoading || !_isValidOtp) return; + unawaited(onVerify()); }, - onCompleted: (_) => onVerify(), ), const SizedBox(height: 20), PrimaryButton( - onPressed: (isOtpLoading || otpCode.trim().length != 6) + onPressed: (isOtpLoading || !_isValidOtp) ? () {} - : onVerify, - text: context.translate(I18n.twoFactorVerify), + : () => unawaited(onVerify()), + text: verifyText, color: theme.getColorFor(ThemeCode.buttonPrimary), leading: isOtpLoading ? const SizedBox( @@ -106,10 +106,8 @@ class TwoFactorBottomSheet extends ConsumerWidget { const SizedBox(height: 12), TextButton( - onPressed: isOtpLoading - ? null - : () => Navigator.of(context).pop(false), - child: Text(context.translate(I18n.close)), + onPressed: isOtpLoading ? null : onClose, + child: Text(closeText), ), ], ), diff --git a/modules/auth/lib/src/features/sign_up/account_created_screen.dart b/modules/auth/lib/src/features/sign_up/account_created_screen.dart deleted file mode 100644 index f2828367..00000000 --- a/modules/auth/lib/src/features/sign_up/account_created_screen.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:navigation/navigation.dart'; - -class AccountCreatedScreen extends ConsumerWidget { - final bool kidAccount; - final NavigationContract navigationContract; - - const AccountCreatedScreen({ - super.key, - required this.kidAccount, - required this.navigationContract, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = ref.watch(themePortProvider); - - final email = "usuario@example.com"; - final fullName = "Carlos Pérez Cruz"; - final model = "SaveWatch Plus 2"; - final id = "1106652524"; - - return Scaffold( - backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: Container( - margin: EdgeInsets.all(30), - child: Center( - child: Column( - spacing: 20, - children: [ - Spacer(flex: 10), - Icon( - Icons.check, - color: theme.getColorFor(ThemeCode.buttonPrimary), - size: 50, - ), - Text( - "Cuenta creada", - style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - Text.rich( - textAlign: TextAlign.center, - TextSpan( - text: "Has creado la cuenta para:\n", - children: [ - TextSpan( - text: fullName, - style: TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - if (!kidAccount) Text.rich( - textAlign: TextAlign.center, - TextSpan( - text: "Hemos enviado un email de verificación a:\n", - children: [ - TextSpan( - text: email, - style: TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - if (kidAccount) Text("Reloj: $model\nID del reloj: $id"), - Text( - "Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj", - textAlign: TextAlign.center, - ), - PrimaryButton( - onPressed: () => { - if (kidAccount){ - navigationContract.goTo(AppRoutes.dashboardHome) - } else { - navigationContract.pushTo(AppRoutes.deviceSignup) - } - }, - text: "Continuar", - color: theme.getColorFor(ThemeCode.buttonPrimary) - ), - Spacer(flex: 8), - ], - ), - ), - ), - ); - } -} diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.dart b/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.dart new file mode 100644 index 00000000..15a11bc3 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.dart @@ -0,0 +1,15 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'address_entity.freezed.dart'; + +@freezed +abstract class AddressEntity with _$AddressEntity { + const factory AddressEntity({ + required String street, + required String city, + required String province, + required String state, + required String country, + required int postCode, + }) = _AddressEntity; +} diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.freezed.dart b/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.freezed.dart new file mode 100644 index 00000000..fbff9cb2 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/address_entity.freezed.dart @@ -0,0 +1,286 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'address_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$AddressEntity { + + String get street; String get city; String get province; String get state; String get country; int get postCode; +/// Create a copy of AddressEntity +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AddressEntityCopyWith get copyWith => _$AddressEntityCopyWithImpl(this as AddressEntity, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + + +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class $AddressEntityCopyWith<$Res> { + factory $AddressEntityCopyWith(AddressEntity value, $Res Function(AddressEntity) _then) = _$AddressEntityCopyWithImpl; +@useResult +$Res call({ + String street, String city, String province, String state, String country, int postCode +}); + + + + +} +/// @nodoc +class _$AddressEntityCopyWithImpl<$Res> + implements $AddressEntityCopyWith<$Res> { + _$AddressEntityCopyWithImpl(this._self, this._then); + + final AddressEntity _self; + final $Res Function(AddressEntity) _then; + +/// Create a copy of AddressEntity +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) { + return _then(_self.copyWith( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int, + )); +} + +} + + +/// Adds pattern-matching-related methods to [AddressEntity]. +extension AddressEntityPatterns on AddressEntity { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AddressEntity value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AddressEntity() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AddressEntity value) $default,){ +final _that = this; +switch (_that) { +case _AddressEntity(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AddressEntity value)? $default,){ +final _that = this; +switch (_that) { +case _AddressEntity() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AddressEntity() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String street, String city, String province, String state, String country, int postCode) $default,) {final _that = this; +switch (_that) { +case _AddressEntity(): +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this; +switch (_that) { +case _AddressEntity() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _AddressEntity implements AddressEntity { + const _AddressEntity({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode}); + + +@override final String street; +@override final String city; +@override final String province; +@override final String state; +@override final String country; +@override final int postCode; + +/// Create a copy of AddressEntity +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AddressEntityCopyWith<_AddressEntity> get copyWith => __$AddressEntityCopyWithImpl<_AddressEntity>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + + +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class _$AddressEntityCopyWith<$Res> implements $AddressEntityCopyWith<$Res> { + factory _$AddressEntityCopyWith(_AddressEntity value, $Res Function(_AddressEntity) _then) = __$AddressEntityCopyWithImpl; +@override @useResult +$Res call({ + String street, String city, String province, String state, String country, int postCode +}); + + + + +} +/// @nodoc +class __$AddressEntityCopyWithImpl<$Res> + implements _$AddressEntityCopyWith<$Res> { + __$AddressEntityCopyWithImpl(this._self, this._then); + + final _AddressEntity _self; + final $Res Function(_AddressEntity) _then; + +/// Create a copy of AddressEntity +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) { + return _then(_AddressEntity( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.dart b/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.dart new file mode 100644 index 00000000..abadd091 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.dart @@ -0,0 +1,25 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'address_entity.dart'; + +part 'sign_up_request_entity.freezed.dart'; + +@freezed +abstract class SignUpRequestEntity with _$SignUpRequestEntity { + const factory SignUpRequestEntity({ + required String documentType, + required String documentNumber, + required String relationship, + required String firstName, + required String lastName, + required String email, + required String phone, + required String language, + required String password, + required List taxResidences, + required List addresses, + required int bornAt, + required String userId, // UUID + required String placeOfBirth, + required String birthCountry, + }) = _SignUpRequestEntity; +} diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.freezed.dart b/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.freezed.dart new file mode 100644 index 00000000..7c24b4fc --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/sign_up_request_entity.freezed.dart @@ -0,0 +1,327 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sign_up_request_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$SignUpRequestEntity { + + String get documentType; String get documentNumber; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId;// UUID + String get placeOfBirth; String get birthCountry; +/// Create a copy of SignUpRequestEntity +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SignUpRequestEntityCopyWith get copyWith => _$SignUpRequestEntityCopyWithImpl(this as SignUpRequestEntity, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestEntity&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)); +} + + +@override +int get hashCode => Object.hash(runtimeType,documentType,documentNumber,relationship,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry); + +@override +String toString() { + return 'SignUpRequestEntity(documentType: $documentType, documentNumber: $documentNumber, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry)'; +} + + +} + +/// @nodoc +abstract mixin class $SignUpRequestEntityCopyWith<$Res> { + factory $SignUpRequestEntityCopyWith(SignUpRequestEntity value, $Res Function(SignUpRequestEntity) _then) = _$SignUpRequestEntityCopyWithImpl; +@useResult +$Res call({ + String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry +}); + + + + +} +/// @nodoc +class _$SignUpRequestEntityCopyWithImpl<$Res> + implements $SignUpRequestEntityCopyWith<$Res> { + _$SignUpRequestEntityCopyWithImpl(this._self, this._then); + + final SignUpRequestEntity _self; + final $Res Function(SignUpRequestEntity) _then; + +/// Create a copy of SignUpRequestEntity +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? documentType = null,Object? documentNumber = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,}) { + return _then(_self.copyWith( +documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable +as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable +as List,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable +as List,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SignUpRequestEntity]. +extension SignUpRequestEntityPatterns on SignUpRequestEntity { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SignUpRequestEntity value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SignUpRequestEntity() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SignUpRequestEntity value) $default,){ +final _that = this; +switch (_that) { +case _SignUpRequestEntity(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SignUpRequestEntity value)? $default,){ +final _that = this; +switch (_that) { +case _SignUpRequestEntity() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SignUpRequestEntity() when $default != null: +return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry) $default,) {final _that = this; +switch (_that) { +case _SignUpRequestEntity(): +return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry)? $default,) {final _that = this; +switch (_that) { +case _SignUpRequestEntity() when $default != null: +return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _SignUpRequestEntity implements SignUpRequestEntity { + const _SignUpRequestEntity({required this.documentType, required this.documentNumber, required this.relationship, required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry}): _taxResidences = taxResidences,_addresses = addresses; + + +@override final String documentType; +@override final String documentNumber; +@override final String relationship; +@override final String firstName; +@override final String lastName; +@override final String email; +@override final String phone; +@override final String language; +@override final String password; + final List _taxResidences; +@override List get taxResidences { + if (_taxResidences is EqualUnmodifiableListView) return _taxResidences; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_taxResidences); +} + + final List _addresses; +@override List get addresses { + if (_addresses is EqualUnmodifiableListView) return _addresses; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_addresses); +} + +@override final int bornAt; +@override final String userId; +// UUID +@override final String placeOfBirth; +@override final String birthCountry; + +/// Create a copy of SignUpRequestEntity +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SignUpRequestEntityCopyWith<_SignUpRequestEntity> get copyWith => __$SignUpRequestEntityCopyWithImpl<_SignUpRequestEntity>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestEntity&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)); +} + + +@override +int get hashCode => Object.hash(runtimeType,documentType,documentNumber,relationship,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry); + +@override +String toString() { + return 'SignUpRequestEntity(documentType: $documentType, documentNumber: $documentNumber, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry)'; +} + + +} + +/// @nodoc +abstract mixin class _$SignUpRequestEntityCopyWith<$Res> implements $SignUpRequestEntityCopyWith<$Res> { + factory _$SignUpRequestEntityCopyWith(_SignUpRequestEntity value, $Res Function(_SignUpRequestEntity) _then) = __$SignUpRequestEntityCopyWithImpl; +@override @useResult +$Res call({ + String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry +}); + + + + +} +/// @nodoc +class __$SignUpRequestEntityCopyWithImpl<$Res> + implements _$SignUpRequestEntityCopyWith<$Res> { + __$SignUpRequestEntityCopyWithImpl(this._self, this._then); + + final _SignUpRequestEntity _self; + final $Res Function(_SignUpRequestEntity) _then; + +/// Create a copy of SignUpRequestEntity +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? documentType = null,Object? documentNumber = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,}) { + return _then(_SignUpRequestEntity( +documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable +as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable +as List,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable +as List,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.dart b/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.dart new file mode 100644 index 00000000..ab6db14d --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'two_fa_secret_entity.freezed.dart'; + +@freezed +abstract class TwoFASecretEntity with _$TwoFASecretEntity { + const factory TwoFASecretEntity({ + required bool isCreated, + required TwoFASecretItemEntity item, + }) = _TwoFASecretEntity; +} + +@freezed +abstract class TwoFASecretItemEntity with _$TwoFASecretItemEntity { + const factory TwoFASecretItemEntity({ + required String secret, + required String qr, + }) = _TwoFASecretItemEntity; +} diff --git a/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.freezed.dart b/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.freezed.dart new file mode 100644 index 00000000..635635ce --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/entities/two_fa_secret_entity.freezed.dart @@ -0,0 +1,552 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'two_fa_secret_entity.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$TwoFASecretEntity { + + bool get isCreated; TwoFASecretItemEntity get item; +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$TwoFASecretEntityCopyWith get copyWith => _$TwoFASecretEntityCopyWithImpl(this as TwoFASecretEntity, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + + +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'TwoFASecretEntity(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class $TwoFASecretEntityCopyWith<$Res> { + factory $TwoFASecretEntityCopyWith(TwoFASecretEntity value, $Res Function(TwoFASecretEntity) _then) = _$TwoFASecretEntityCopyWithImpl; +@useResult +$Res call({ + bool isCreated, TwoFASecretItemEntity item +}); + + +$TwoFASecretItemEntityCopyWith<$Res> get item; + +} +/// @nodoc +class _$TwoFASecretEntityCopyWithImpl<$Res> + implements $TwoFASecretEntityCopyWith<$Res> { + _$TwoFASecretEntityCopyWithImpl(this._self, this._then); + + final TwoFASecretEntity _self; + final $Res Function(TwoFASecretEntity) _then; + +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_self.copyWith( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as TwoFASecretItemEntity, + )); +} +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretItemEntityCopyWith<$Res> get item { + + return $TwoFASecretItemEntityCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [TwoFASecretEntity]. +extension TwoFASecretEntityPatterns on TwoFASecretEntity { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _TwoFASecretEntity value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _TwoFASecretEntity() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _TwoFASecretEntity value) $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretEntity(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _TwoFASecretEntity value)? $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretEntity() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool isCreated, TwoFASecretItemEntity item)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _TwoFASecretEntity() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool isCreated, TwoFASecretItemEntity item) $default,) {final _that = this; +switch (_that) { +case _TwoFASecretEntity(): +return $default(_that.isCreated,_that.item);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isCreated, TwoFASecretItemEntity item)? $default,) {final _that = this; +switch (_that) { +case _TwoFASecretEntity() when $default != null: +return $default(_that.isCreated,_that.item);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _TwoFASecretEntity implements TwoFASecretEntity { + const _TwoFASecretEntity({required this.isCreated, required this.item}); + + +@override final bool isCreated; +@override final TwoFASecretItemEntity item; + +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$TwoFASecretEntityCopyWith<_TwoFASecretEntity> get copyWith => __$TwoFASecretEntityCopyWithImpl<_TwoFASecretEntity>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item)); +} + + +@override +int get hashCode => Object.hash(runtimeType,isCreated,item); + +@override +String toString() { + return 'TwoFASecretEntity(isCreated: $isCreated, item: $item)'; +} + + +} + +/// @nodoc +abstract mixin class _$TwoFASecretEntityCopyWith<$Res> implements $TwoFASecretEntityCopyWith<$Res> { + factory _$TwoFASecretEntityCopyWith(_TwoFASecretEntity value, $Res Function(_TwoFASecretEntity) _then) = __$TwoFASecretEntityCopyWithImpl; +@override @useResult +$Res call({ + bool isCreated, TwoFASecretItemEntity item +}); + + +@override $TwoFASecretItemEntityCopyWith<$Res> get item; + +} +/// @nodoc +class __$TwoFASecretEntityCopyWithImpl<$Res> + implements _$TwoFASecretEntityCopyWith<$Res> { + __$TwoFASecretEntityCopyWithImpl(this._self, this._then); + + final _TwoFASecretEntity _self; + final $Res Function(_TwoFASecretEntity) _then; + +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) { + return _then(_TwoFASecretEntity( +isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable +as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable +as TwoFASecretItemEntity, + )); +} + +/// Create a copy of TwoFASecretEntity +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretItemEntityCopyWith<$Res> get item { + + return $TwoFASecretItemEntityCopyWith<$Res>(_self.item, (value) { + return _then(_self.copyWith(item: value)); + }); +} +} + +/// @nodoc +mixin _$TwoFASecretItemEntity { + + String get secret; String get qr; +/// Create a copy of TwoFASecretItemEntity +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$TwoFASecretItemEntityCopyWith get copyWith => _$TwoFASecretItemEntityCopyWithImpl(this as TwoFASecretItemEntity, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretItemEntity&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr)); +} + + +@override +int get hashCode => Object.hash(runtimeType,secret,qr); + +@override +String toString() { + return 'TwoFASecretItemEntity(secret: $secret, qr: $qr)'; +} + + +} + +/// @nodoc +abstract mixin class $TwoFASecretItemEntityCopyWith<$Res> { + factory $TwoFASecretItemEntityCopyWith(TwoFASecretItemEntity value, $Res Function(TwoFASecretItemEntity) _then) = _$TwoFASecretItemEntityCopyWithImpl; +@useResult +$Res call({ + String secret, String qr +}); + + + + +} +/// @nodoc +class _$TwoFASecretItemEntityCopyWithImpl<$Res> + implements $TwoFASecretItemEntityCopyWith<$Res> { + _$TwoFASecretItemEntityCopyWithImpl(this._self, this._then); + + final TwoFASecretItemEntity _self; + final $Res Function(TwoFASecretItemEntity) _then; + +/// Create a copy of TwoFASecretItemEntity +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? secret = null,Object? qr = null,}) { + return _then(_self.copyWith( +secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable +as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [TwoFASecretItemEntity]. +extension TwoFASecretItemEntityPatterns on TwoFASecretItemEntity { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _TwoFASecretItemEntity value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _TwoFASecretItemEntity() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _TwoFASecretItemEntity value) $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretItemEntity(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _TwoFASecretItemEntity value)? $default,){ +final _that = this; +switch (_that) { +case _TwoFASecretItemEntity() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String secret, String qr)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _TwoFASecretItemEntity() when $default != null: +return $default(_that.secret,_that.qr);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String secret, String qr) $default,) {final _that = this; +switch (_that) { +case _TwoFASecretItemEntity(): +return $default(_that.secret,_that.qr);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String secret, String qr)? $default,) {final _that = this; +switch (_that) { +case _TwoFASecretItemEntity() when $default != null: +return $default(_that.secret,_that.qr);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _TwoFASecretItemEntity implements TwoFASecretItemEntity { + const _TwoFASecretItemEntity({required this.secret, required this.qr}); + + +@override final String secret; +@override final String qr; + +/// Create a copy of TwoFASecretItemEntity +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$TwoFASecretItemEntityCopyWith<_TwoFASecretItemEntity> get copyWith => __$TwoFASecretItemEntityCopyWithImpl<_TwoFASecretItemEntity>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretItemEntity&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr)); +} + + +@override +int get hashCode => Object.hash(runtimeType,secret,qr); + +@override +String toString() { + return 'TwoFASecretItemEntity(secret: $secret, qr: $qr)'; +} + + +} + +/// @nodoc +abstract mixin class _$TwoFASecretItemEntityCopyWith<$Res> implements $TwoFASecretItemEntityCopyWith<$Res> { + factory _$TwoFASecretItemEntityCopyWith(_TwoFASecretItemEntity value, $Res Function(_TwoFASecretItemEntity) _then) = __$TwoFASecretItemEntityCopyWithImpl; +@override @useResult +$Res call({ + String secret, String qr +}); + + + + +} +/// @nodoc +class __$TwoFASecretItemEntityCopyWithImpl<$Res> + implements _$TwoFASecretItemEntityCopyWith<$Res> { + __$TwoFASecretItemEntityCopyWithImpl(this._self, this._then); + + final _TwoFASecretItemEntity _self; + final $Res Function(_TwoFASecretItemEntity) _then; + +/// Create a copy of TwoFASecretItemEntity +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? secret = null,Object? qr = null,}) { + return _then(_TwoFASecretItemEntity( +secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable +as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart b/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart new file mode 100644 index 00000000..dc001be0 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart @@ -0,0 +1,5 @@ +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; + +abstract class GenerateTwoFASignUpUseCase { + Future generateTwoFASignUp({required String token}); +} diff --git a/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart b/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart new file mode 100644 index 00000000..c0a10acf --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart @@ -0,0 +1,13 @@ +import 'package:auth/src/core/domain/repositories/auth_repository.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; +import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart'; + +class GenerateTwoFASignUpUseCaseImpl implements GenerateTwoFASignUpUseCase { + GenerateTwoFASignUpUseCaseImpl(this._repository); + + final AuthRepository _repository; + @override + Future generateTwoFASignUp({required String token}) { + return _repository.generateTwoFASignUp(token: token); + } +} diff --git a/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case.dart b/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case.dart new file mode 100644 index 00000000..75bae34a --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case.dart @@ -0,0 +1,5 @@ +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; + +abstract class SignUpUseCase { + Future signUp({required SignUpRequestEntity request}); +} diff --git a/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case_impl.dart b/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case_impl.dart new file mode 100644 index 00000000..4aea3dcf --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/sign_up_use_case_impl.dart @@ -0,0 +1,14 @@ +import 'package:auth/src/core/domain/repositories/auth_repository.dart'; +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart'; + +class SignUpUseCaseImpl implements SignUpUseCase { + SignUpUseCaseImpl(this._repository); + + final AuthRepository _repository; + + @override + Future signUp({required SignUpRequestEntity request}) { + return _repository.signUp(request: request); + } +} diff --git a/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart b/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart new file mode 100644 index 00000000..9d54314c --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart @@ -0,0 +1,6 @@ +abstract class VerifyTwoFACodeSignUpUseCase { + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }); +} diff --git a/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart b/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart new file mode 100644 index 00000000..3de69e57 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart @@ -0,0 +1,15 @@ +import 'package:auth/src/core/domain/repositories/auth_repository.dart'; +import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart'; + +class VerifyTwoFaCodeSignUpUseCaseImpl implements VerifyTwoFACodeSignUpUseCase { + VerifyTwoFaCodeSignUpUseCaseImpl(this._repository); + + final AuthRepository _repository; + @override + Future verifyTwoFACodeSignUp({ + required String token, + required String code, + }) { + return _repository.verifyTwoFACodeSignUp(token: token, code: code); + } +} diff --git a/modules/auth/lib/src/features/sign_up/models/sign_up_step_config.dart b/modules/auth/lib/src/features/sign_up/models/sign_up_step_config.dart new file mode 100644 index 00000000..1a58e0d9 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/models/sign_up_step_config.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +typedef SignUpBodyBuilder = + Widget Function(BuildContext context, WidgetRef ref); + +class SignUpStepConfig { + final String supertitle; + final String title; + final String? subtitle; + final SignUpBodyBuilder bodyBuilder; + + const SignUpStepConfig({ + required this.supertitle, + required this.title, + this.subtitle, + required this.bodyBuilder, + }); +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart new file mode 100644 index 00000000..c8a96e9f --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart @@ -0,0 +1,92 @@ +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; + +class AccountCreatedScreen extends ConsumerWidget { + final NavigationContract navigationContract; + + const AccountCreatedScreen({super.key, required this.navigationContract}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + final state = ref.watch(signUpViewModelProvider); + + final String email = state.email; + final String fullName = '${state.firstName} ${state.lastName}'.trim(); + + return PopScope( + canPop: false, + child: Scaffold( + backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), + body: Container( + margin: const EdgeInsets.all(30), + child: Center( + child: Column( + children: [ + const Spacer(flex: 10), + Icon( + Icons.check, + color: theme.getColorFor(ThemeCode.buttonPrimary), + size: 50, + ), + const SizedBox(height: 20), + const Text( + "Cuenta creada", + style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 20), + + Text.rich( + textAlign: TextAlign.center, + TextSpan( + text: "Has creado la cuenta para:\n", + children: [ + TextSpan( + text: fullName, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + ), + + const SizedBox(height: 16), + Text.rich( + textAlign: TextAlign.center, + TextSpan( + text: "Hemos enviado un email de verificación a:\n", + children: [ + TextSpan( + text: email, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + ), + + const SizedBox(height: 20), + const Text( + "Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj", + textAlign: TextAlign.center, + ), + + const SizedBox(height: 20), + PrimaryButton( + onPressed: () { + navigationContract.goTo(AppRoutes.login); + }, + text: "Continuar", + color: theme.getColorFor(ThemeCode.buttonPrimary), + ), + const Spacer(flex: 8), + ], + ), + ), + ), + ), + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart b/modules/auth/lib/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart new file mode 100644 index 00000000..73a73fa6 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart @@ -0,0 +1,23 @@ +import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart'; +import 'package:auth/src/features/sign_up/presentation/state/address_view_state.dart'; + +extension AddressViewStateMapper on AddressViewState { + bool get isValid => + street.trim().isNotEmpty && + city.trim().isNotEmpty && + province.trim().isNotEmpty && + state.trim().isNotEmpty && + country.trim().isNotEmpty && + (postCode != null); + + AddressEntity toEntity() { + return AddressEntity( + street: street.trim(), + city: city.trim(), + province: province.trim(), + state: state.trim(), + country: country.trim(), + postCode: postCode ?? 0, + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/mappers/sign_up_view_state_mapper.dart b/modules/auth/lib/src/features/sign_up/presentation/mappers/sign_up_view_state_mapper.dart new file mode 100644 index 00000000..e6fce1e0 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/mappers/sign_up_view_state_mapper.dart @@ -0,0 +1,46 @@ +import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart'; +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_state.dart'; + +extension SignUpViewStateMapper on SignUpViewState { + bool get canSubmit => + documentNumber.trim().isNotEmpty && + documentType.trim().isNotEmpty && + relationship.trim().isNotEmpty && + firstName.trim().isNotEmpty && + lastName.trim().isNotEmpty && + email.trim().isNotEmpty && + phone.trim().isNotEmpty && + password.isNotEmpty && + bornAt != null && + userId.trim().isNotEmpty && + placeOfBirth.trim().isNotEmpty && + birthCountry.trim().isNotEmpty && + address.isValid; + + SignUpRequestEntity toRequestEntity() { + final birth = bornAt; + if (birth == null) { + throw Exception('bornAt is required'); + } + + return SignUpRequestEntity( + documentNumber: documentNumber.trim(), + documentType: documentType.trim(), + relationship: relationship.trim(), + firstName: firstName.trim(), + lastName: lastName.trim(), + email: email.trim(), + phone: phone.trim(), + language: language.trim(), + password: password, + bornAt: birth.millisecondsSinceEpoch, + userId: userId.trim(), + placeOfBirth: placeOfBirth.trim(), + birthCountry: birthCountry.trim(), + addresses: [address.toEntity()], + taxResidences: [address.toEntity()], + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart b/modules/auth/lib/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart new file mode 100644 index 00000000..615e82c5 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart @@ -0,0 +1,10 @@ +import 'package:auth/src/core/providers/auth_repository_provider.dart'; +import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final generateTwoFASignUpUseCaseProvider = + Provider.autoDispose((ref) { + final authRepository = ref.read(authRepositoryProvider); + return GenerateTwoFASignUpUseCaseImpl(authRepository); + }); diff --git a/modules/auth/lib/src/features/sign_up/presentation/providers/sign_up_provider.dart b/modules/auth/lib/src/features/sign_up/presentation/providers/sign_up_provider.dart new file mode 100644 index 00000000..875681db --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/providers/sign_up_provider.dart @@ -0,0 +1,9 @@ +import 'package:auth/src/core/providers/auth_repository_provider.dart'; +import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/domain/sign_up_use_case_impl.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final signUpUseCaseProvider = Provider.autoDispose((ref) { + final authRepository = ref.read(authRepositoryProvider); + return SignUpUseCaseImpl(authRepository); +}); diff --git a/modules/auth/lib/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart b/modules/auth/lib/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart new file mode 100644 index 00000000..23647ddf --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart @@ -0,0 +1,10 @@ +import 'package:auth/src/core/providers/auth_repository_provider.dart'; +import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final verifyTwoFACodeSignUpUseCaseProvider = + Provider.autoDispose((ref) { + final authRepository = ref.read(authRepositoryProvider); + return VerifyTwoFaCodeSignUpUseCaseImpl(authRepository); + }); diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart new file mode 100644 index 00000000..aecc2632 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart @@ -0,0 +1,197 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class SignupAddressScreen extends StatelessWidget { + const SignupAddressScreen({ + super.key, + + required this.bornAtController, + + required this.relationshipSelected, + required this.onRelationshipChanged, + required this.relationshipOptions, + required this.relationshipHint, + required this.relationshipLabel, + + required this.placeOfBirthController, + required this.birthCountryController, + + required this.streetController, + required this.cityController, + required this.provinceController, + required this.stateController, + + required this.addressCountrySelected, + required this.onAddressCountryChanged, + required this.addressCountryOptions, + required this.addressCountryHint, + required this.addressCountryLabel, + + required this.postCodeController, + + required this.birthDateLabel, + required this.birthDateHint, + + required this.placeOfBirthLabel, + required this.placeOfBirthHint, + + required this.birthCountryLabel, + required this.birthCountryHint, + + required this.streetLabel, + required this.streetHint, + + required this.cityLabel, + required this.cityHint, + + required this.provinceLabel, + required this.provinceHint, + + required this.stateLabel, + required this.stateHint, + + required this.postCodeLabel, + required this.postCodeHint, + }); + + final TextEditingController bornAtController; + + final String? relationshipSelected; + final ValueChanged onRelationshipChanged; + final List relationshipOptions; + final String relationshipHint; + final String relationshipLabel; + + final TextEditingController placeOfBirthController; + final TextEditingController birthCountryController; + + final TextEditingController streetController; + final TextEditingController cityController; + final TextEditingController provinceController; + final TextEditingController stateController; + + final String? addressCountrySelected; + final ValueChanged onAddressCountryChanged; + final List addressCountryOptions; + final String addressCountryHint; + final String addressCountryLabel; + + final TextEditingController postCodeController; + + final String birthDateLabel; + final String birthDateHint; + + final String placeOfBirthLabel; + final String placeOfBirthHint; + + final String birthCountryLabel; + final String birthCountryHint; + + final String streetLabel; + final String streetHint; + + final String cityLabel; + final String cityHint; + + final String provinceLabel; + final String provinceHint; + + final String stateLabel; + final String stateHint; + + final String postCodeLabel; + final String postCodeHint; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + CustomTextField( + label: birthDateLabel, + hint: birthDateHint, + keyboardType: TextInputType.number, + controller: bornAtController, + ), + const SizedBox(height: 8), + + Align( + alignment: Alignment.bottomLeft, + child: Text(relationshipLabel, style: const TextStyle(fontSize: 14)), + ), + CustomDropdown( + items: relationshipOptions.map(Text.new).toList(growable: false), + values: relationshipOptions, + value: relationshipSelected, + hint: relationshipHint, + onChanged: (v) => onRelationshipChanged(v as String?), + ), + const SizedBox(height: 8), + + CustomTextField( + label: placeOfBirthLabel, + hint: placeOfBirthHint, + controller: placeOfBirthController, + ), + const SizedBox(height: 8), + + CustomTextField( + label: birthCountryLabel, + hint: birthCountryHint, + controller: birthCountryController, + ), + const SizedBox(height: 8), + + CustomTextField( + label: streetLabel, + hint: streetHint, + controller: streetController, + ), + const SizedBox(height: 8), + + CustomTextField( + label: cityLabel, + hint: cityHint, + controller: cityController, + ), + const SizedBox(height: 8), + + CustomTextField( + label: provinceLabel, + hint: provinceHint, + controller: provinceController, + ), + const SizedBox(height: 8), + + CustomTextField( + label: stateLabel, + hint: stateHint, + controller: stateController, + ), + const SizedBox(height: 8), + + Align( + alignment: Alignment.bottomLeft, + child: Text( + addressCountryLabel, + style: const TextStyle(fontSize: 14, letterSpacing: 0), + ), + ), + CustomDropdown( + items: addressCountryOptions.map(Text.new).toList(growable: false), + values: addressCountryOptions, + value: addressCountrySelected, + hint: addressCountryHint, + onChanged: (v) => onAddressCountryChanged(v as String?), + ), + const SizedBox(height: 8), + + CustomTextField( + label: postCodeLabel, + hint: postCodeHint, + keyboardType: TextInputType.number, + controller: postCodeController, + ), + ], + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart new file mode 100644 index 00000000..d8cb0dfd --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart @@ -0,0 +1,35 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class SignUpPasswordScreen extends StatelessWidget { + final bool isPasswordVisible; + final TextEditingController passwordTextFieldController; + final TextEditingController repeatPasswordTextFieldController; + const SignUpPasswordScreen({ + super.key, + required this.isPasswordVisible, + required this.passwordTextFieldController, + required this.repeatPasswordTextFieldController, + }); + + @override + Widget build(BuildContext context) { + return Column( + spacing: 24, + children: [ + CustomTextField( + showPassword: isPasswordVisible, + label: "Contraseña", + hint: "********", + controller: passwordTextFieldController, + ), + CustomTextField( + showPassword: isPasswordVisible, + label: "Repetir contraseña", + hint: "*******", + controller: repeatPasswordTextFieldController, + ), + ], + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart new file mode 100644 index 00000000..f5adb8cb --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart @@ -0,0 +1,135 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class SignupPersonalScreen extends StatelessWidget { + final TextEditingController firstNameTextFieldController; + final TextEditingController lastNameTextFieldController; + final TextEditingController documentNumberTextFieldController; + final TextEditingController phoneTextFieldController; + final TextEditingController emailTextFieldController; + + final String? documentTypeSelected; + final ValueChanged onDocumentTypeChanged; + + final String firstNameLabel; + final String firstNameHint; + + final String lastNameLabel; + final String lastNameHint; + + final List documentTypeOptions; + final String documentTypeHint; + + final String documentNumberLabel; + final String documentNumberHint; + + final String phoneLabel; + final String phoneHint; + + final String emailLabel; + final String emailHint; + + final bool acceptTerms; + final ValueChanged? onAcceptTermsPressed; + final String termsText; + final ThemePort theme; + + const SignupPersonalScreen({ + super.key, + required this.firstNameTextFieldController, + required this.lastNameTextFieldController, + required this.documentNumberTextFieldController, + required this.phoneTextFieldController, + required this.emailTextFieldController, + required this.documentTypeSelected, + required this.onDocumentTypeChanged, + required this.firstNameLabel, + required this.firstNameHint, + required this.lastNameLabel, + required this.lastNameHint, + required this.documentTypeOptions, + required this.documentTypeHint, + required this.documentNumberLabel, + required this.documentNumberHint, + required this.phoneLabel, + required this.phoneHint, + required this.emailLabel, + required this.emailHint, + required this.acceptTerms, + required this.onAcceptTermsPressed, + required this.termsText, + required this.theme, + }); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + CustomTextField( + label: firstNameLabel, + hint: firstNameHint, + controller: firstNameTextFieldController, + ), + const SizedBox(height: 8), + CustomTextField( + label: lastNameLabel, + hint: lastNameHint, + controller: lastNameTextFieldController, + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: CustomDropdown( + items: documentTypeOptions + .map(Text.new) + .toList(growable: false), + values: documentTypeOptions, + value: documentTypeSelected, + hint: documentTypeHint, + onChanged: (v) => onDocumentTypeChanged(v as String?), + ), + ), + const SizedBox(width: 8), + Expanded( + child: CustomTextField( + label: documentNumberLabel, + hint: documentNumberHint, + controller: documentNumberTextFieldController, + ), + ), + ], + ), + const SizedBox(height: 8), + CustomTextField( + label: phoneLabel, + hint: phoneHint, + keyboardType: TextInputType.number, + controller: phoneTextFieldController, + ), + const SizedBox(height: 8), + CustomTextField( + label: emailLabel, + hint: emailHint, + keyboardType: TextInputType.emailAddress, + controller: emailTextFieldController, + ), + + CheckboxListTile( + value: acceptTerms, + onChanged: onAcceptTermsPressed, + title: Text( + termsText, + style: TextStyle(fontSize: 16, letterSpacing: 0), + ), + checkboxScaleFactor: 1.5, + contentPadding: EdgeInsets.zero, + activeColor: theme.getColorFor(ThemeCode.buttonPrimary), + controlAffinity: ListTileControlAffinity.leading, + ), + ], + ), + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart new file mode 100644 index 00000000..2b230d6c --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart @@ -0,0 +1,134 @@ +import 'package:auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; +import 'package:auth/src/features/sign_up/presentation/account_created_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/sign_up_steps.dart'; +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:auth/src/widgets/layouts/sign_up_layout.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; +import 'package:sf_localizations/sf_localizations.dart'; + +class SignupScreen extends ConsumerWidget { + final NavigationContract navigationContract; + + const SignupScreen({super.key, required this.navigationContract}); + + Future _onNextPressed(BuildContext context, WidgetRef ref) async { + FocusManager.instance.primaryFocus?.unfocus(); + + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.read(signUpViewModelProvider); + + final steps = signUpSteps(); + final isLastStep = state.currentIndex >= steps.length - 1; + + if (!isLastStep) { + vm.next(); + return; + } + + final isSignUp = await vm.signUp(); + if (!context.mounted) return; + + if (!isSignUp) return; + + final verified = await _openTwoFactorSignUpBottomSheet(context, ref); + if (!context.mounted) return; + + if (verified == true) { + vm.showAccountCreated(); + } + } + + Future _openTwoFactorSignUpBottomSheet( + BuildContext context, + WidgetRef ref, + ) async { + final token = ref.read(signUpViewModelProvider).token; + if (token.trim().isEmpty) return false; + + return showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + isDismissible: false, + enableDrag: false, + backgroundColor: Colors.transparent, + builder: (context) { + return Consumer( + builder: (context, ref, _) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(signUpViewModelProvider.notifier); + + final otpErrorKey = ref.watch( + signUpViewModelProvider.select((s) => s.otpError), + ); + final isOtpLoading = ref.watch( + signUpViewModelProvider.select((s) => s.isOtpLoading), + ); + final otpCode = ref.watch( + signUpViewModelProvider.select((s) => s.otpCode), + ); + + final otpErrorText = otpErrorKey.isEmpty + ? '' + : context.translate(otpErrorKey); + + Future onVerify() async { + FocusManager.instance.primaryFocus?.unfocus(); + + final ok = await vm.verifyTwoFACodeSignUp(token: token); + if (!context.mounted) return; + + if (ok) Navigator.of(context).pop(true); + } + + return TwoFactorBottomSheetView( + theme: theme, + title: context.translate(I18n.twoFactorTitle), + subtitle: context.translate(I18n.twoFactorSubtitle), + verifyText: context.translate(I18n.twoFactorVerify), + closeText: context.translate(I18n.close), + isOtpLoading: isOtpLoading, + otpCode: otpCode, + otpErrorText: otpErrorText, + onChanged: vm.setOtpCode, + onVerify: onVerify, + onClose: () => Navigator.of(context).pop(false), + ); + }, + ); + }, + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.watch(signUpViewModelProvider); + + final steps = signUpSteps(); + final index = state.currentIndex.clamp(0, steps.length - 1); + final step = steps[index]; + + if (state.showAccountCreated) { + return AccountCreatedScreen(navigationContract: navigationContract); + } + return SignUpLayout( + theme: theme, + supertitle: step.supertitle, + title: step.title, + subtitle: step.subtitle ?? '', + currentStep: index + 1, + numSteps: steps.length, + body: step.bodyBuilder(context, ref), + errorMessage: state.errorMessage, + onBackPressed: state.currentIndex == 0 + ? navigationContract.goBack + : vm.back, + onNextPressed: () => _onNextPressed(context, ref), + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart new file mode 100644 index 00000000..9fd432c5 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart @@ -0,0 +1,128 @@ +import 'package:auth/src/features/sign_up/models/sign_up_step_config.dart'; +import 'package:auth/src/features/sign_up/presentation/sign_up_address_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/sign_up_password_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/sign_up_personal_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:design_system/design_system.dart'; + +List signUpSteps() => [ + SignUpStepConfig( + supertitle: 'Usuario y contacto', + title: 'Crea tu usuario', + subtitle: 'Con tu email y tu número podremos mantenerte siempre informado', + bodyBuilder: (context, ref) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.watch(signUpViewModelProvider); + + return SignupPersonalScreen( + firstNameTextFieldController: vm.firstNameController, + lastNameTextFieldController: vm.lastNameController, + documentNumberTextFieldController: vm.documentNumberController, + phoneTextFieldController: vm.phoneController, + emailTextFieldController: vm.emailController, + documentTypeSelected: state.documentType.trim().isEmpty + ? null + : state.documentType, + onDocumentTypeChanged: vm.setDocumentType, + acceptTerms: state.acceptTerms, + onAcceptTermsPressed: (v) => vm.setAcceptTerms(v ?? false), + termsText: 'Acepto los términos y condiciones', + theme: theme, + firstNameLabel: 'Nombre', + firstNameHint: 'Nombre', + lastNameLabel: 'Apellido', + lastNameHint: 'Apellido', + documentTypeOptions: const ['DNI', 'NIE', 'Pasaporte'], + documentTypeHint: 'Documento', + documentNumberLabel: '', + documentNumberHint: 'DNI/NIE/Pasaporte', + phoneLabel: 'Teléfono móvil', + phoneHint: 'Teléfono móvil', + emailLabel: 'Correo electrónico', + emailHint: 'Correo electrónico', + ); + }, + ), + SignUpStepConfig( + supertitle: 'Datos personales', + title: 'Identifícate', + subtitle: + 'Nos aseguraremos de que la cuenta esté a nombre del adulto responsable', + bodyBuilder: (context, ref) { + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.watch(signUpViewModelProvider); + + return SignupAddressScreen( + bornAtController: vm.bornAtController, + + relationshipSelected: state.relationship.trim().isEmpty + ? null + : state.relationship, + onRelationshipChanged: vm.setRelationship, + relationshipOptions: const ['father', 'mother', 'tutor'], + relationshipHint: 'Selecciona una opción', + relationshipLabel: '¿Qué familiar eres?', + + placeOfBirthController: vm.placeOfBirthController, + birthCountryController: vm.birthCountryController, + + streetController: vm.addressStreetController, + cityController: vm.addressCityController, + provinceController: vm.addressProvinceController, + stateController: vm.addressStateController, + + addressCountrySelected: state.address.country.trim().isEmpty + ? null + : state.address.country, + onAddressCountryChanged: vm.setAddressCountry, + addressCountryOptions: const ['Spain', 'France', 'Portugal'], + addressCountryHint: 'País', + addressCountryLabel: 'País (dirección)', + + postCodeController: vm.addressPostCodeController, + + birthDateLabel: 'Fecha de nacimiento', + birthDateHint: 'DD/MM/AAAA', + + placeOfBirthLabel: 'Lugar de nacimiento', + placeOfBirthHint: 'Ciudad de nacimiento', + + birthCountryLabel: 'País de nacimiento', + birthCountryHint: 'País de nacimiento', + + streetLabel: 'Calle / Dirección', + streetHint: 'Calle Gran Vía 30 6º', + + cityLabel: 'Ciudad', + cityHint: 'Ciudad', + + provinceLabel: 'Provincia', + provinceHint: 'Provincia', + + stateLabel: 'Comunidad / Estado', + stateHint: 'Comunidad / Estado', + + postCodeLabel: 'Código postal', + postCodeHint: '28013', + ); + }, + ), + + SignUpStepConfig( + supertitle: 'Domicilio', + title: 'Tu dirección', + subtitle: + 'Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial', + bodyBuilder: (context, ref) { + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.watch(signUpViewModelProvider); + + return SignUpPasswordScreen( + isPasswordVisible: state.isShowPassword, + passwordTextFieldController: vm.passwordController, + repeatPasswordTextFieldController: vm.repeatPasswordController, + ); + }, + ), +]; diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart new file mode 100644 index 00000000..489e02ee --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart @@ -0,0 +1,15 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'address_view_state.freezed.dart'; + +@freezed +abstract class AddressViewState with _$AddressViewState { + const factory AddressViewState({ + @Default('') String street, + @Default('') String city, + @Default('') String province, + @Default('') String state, + @Default('') String country, + int? postCode, + }) = _AddressViewState; +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart new file mode 100644 index 00000000..27889783 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart @@ -0,0 +1,286 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'address_view_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$AddressViewState { + + String get street; String get city; String get province; String get state; String get country; int? get postCode; +/// Create a copy of AddressViewState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$AddressViewStateCopyWith get copyWith => _$AddressViewStateCopyWithImpl(this as AddressViewState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressViewState&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + + +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class $AddressViewStateCopyWith<$Res> { + factory $AddressViewStateCopyWith(AddressViewState value, $Res Function(AddressViewState) _then) = _$AddressViewStateCopyWithImpl; +@useResult +$Res call({ + String street, String city, String province, String state, String country, int? postCode +}); + + + + +} +/// @nodoc +class _$AddressViewStateCopyWithImpl<$Res> + implements $AddressViewStateCopyWith<$Res> { + _$AddressViewStateCopyWithImpl(this._self, this._then); + + final AddressViewState _self; + final $Res Function(AddressViewState) _then; + +/// Create a copy of AddressViewState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = freezed,}) { + return _then(_self.copyWith( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [AddressViewState]. +extension AddressViewStatePatterns on AddressViewState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _AddressViewState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _AddressViewState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _AddressViewState value) $default,){ +final _that = this; +switch (_that) { +case _AddressViewState(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _AddressViewState value)? $default,){ +final _that = this; +switch (_that) { +case _AddressViewState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String street, String city, String province, String state, String country, int? postCode)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _AddressViewState() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String street, String city, String province, String state, String country, int? postCode) $default,) {final _that = this; +switch (_that) { +case _AddressViewState(): +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String street, String city, String province, String state, String country, int? postCode)? $default,) {final _that = this; +switch (_that) { +case _AddressViewState() when $default != null: +return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _AddressViewState implements AddressViewState { + const _AddressViewState({this.street = '', this.city = '', this.province = '', this.state = '', this.country = '', this.postCode}); + + +@override@JsonKey() final String street; +@override@JsonKey() final String city; +@override@JsonKey() final String province; +@override@JsonKey() final String state; +@override@JsonKey() final String country; +@override final int? postCode; + +/// Create a copy of AddressViewState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AddressViewStateCopyWith<_AddressViewState> get copyWith => __$AddressViewStateCopyWithImpl<_AddressViewState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressViewState&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode)); +} + + +@override +int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); + +@override +String toString() { + return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; +} + + +} + +/// @nodoc +abstract mixin class _$AddressViewStateCopyWith<$Res> implements $AddressViewStateCopyWith<$Res> { + factory _$AddressViewStateCopyWith(_AddressViewState value, $Res Function(_AddressViewState) _then) = __$AddressViewStateCopyWithImpl; +@override @useResult +$Res call({ + String street, String city, String province, String state, String country, int? postCode +}); + + + + +} +/// @nodoc +class __$AddressViewStateCopyWithImpl<$Res> + implements _$AddressViewStateCopyWith<$Res> { + __$AddressViewStateCopyWithImpl(this._self, this._then); + + final _AddressViewState _self; + final $Res Function(_AddressViewState) _then; + +/// Create a copy of AddressViewState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = freezed,}) { + return _then(_AddressViewState( +street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable +as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable +as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable +as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + +// dart format on diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart new file mode 100644 index 00000000..c2dee9af --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart @@ -0,0 +1,740 @@ +import 'dart:async'; + +import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart'; +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; +import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart'; +import 'package:auth/src/features/sign_up/presentation/providers/generate_two_fa_sign_up_provider.dart'; +import 'package:auth/src/features/sign_up/presentation/providers/sign_up_provider.dart'; +import 'package:auth/src/features/sign_up/presentation/providers/verify_two_fa_code_sign_up_provider.dart'; +import 'package:auth/src/features/sign_up/presentation/state/address_view_state.dart'; +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_state.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:sf_localizations/sf_localizations.dart'; +import 'package:uuid/uuid.dart'; + +final signUpViewModelProvider = + NotifierProvider.autoDispose( + SignUpViewModel.new, + ); + +class SignUpViewModel extends Notifier { + late final SignUpUseCase _signUpUseCase; + late final GenerateTwoFASignUpUseCase _generateTwoFASignUpUseCase; + late final VerifyTwoFACodeSignUpUseCase _verifyTwoFACodeSignUpUseCase; + + late final TextEditingController firstNameController; + late final TextEditingController lastNameController; + late final TextEditingController documentNumberController; + late final TextEditingController documentTypeController; + late final TextEditingController phoneController; + late final TextEditingController emailController; + + late final TextEditingController relationshipController; + late final TextEditingController bornAtController; + late final TextEditingController placeOfBirthController; + late final TextEditingController birthCountryController; + + late final TextEditingController addressStreetController; + late final TextEditingController addressCityController; + late final TextEditingController addressProvinceController; + late final TextEditingController addressStateController; + late final TextEditingController addressCountryController; + late final TextEditingController addressPostCodeController; + + late final TextEditingController passwordController; + late final TextEditingController repeatPasswordController; + + static const int _lastIndex = 2; + + static final RegExp _emailRegex = RegExp( + r'^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$', + caseSensitive: false, + ); + + static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$'); + + @override + SignUpViewState build() { + _signUpUseCase = ref.read(signUpUseCaseProvider); + _generateTwoFASignUpUseCase = ref.read(generateTwoFASignUpUseCaseProvider); + _verifyTwoFACodeSignUpUseCase = ref.read( + verifyTwoFACodeSignUpUseCaseProvider, + ); + final initial = SignUpViewState(userId: const Uuid().v4()); + _initControllers(initial); + _addListeners(); + + ref.onDispose(disposeControllers); + + return initial; + } + + void _initControllers(SignUpViewState s) { + firstNameController = TextEditingController(text: s.firstName); + lastNameController = TextEditingController(text: s.lastName); + documentNumberController = TextEditingController(text: s.documentNumber); + documentTypeController = TextEditingController(text: s.documentType); + phoneController = TextEditingController(text: s.phone); + emailController = TextEditingController(text: s.email); + + relationshipController = TextEditingController(text: s.relationship); + + bornAtController = TextEditingController( + text: s.bornAt == null ? '' : _formatDate(s.bornAt!), + ); + + placeOfBirthController = TextEditingController(text: s.placeOfBirth); + birthCountryController = TextEditingController(text: s.birthCountry); + + addressStreetController = TextEditingController(text: s.address.street); + addressCityController = TextEditingController(text: s.address.city); + addressProvinceController = TextEditingController(text: s.address.province); + addressStateController = TextEditingController(text: s.address.state); + addressCountryController = TextEditingController(text: s.address.country); + addressPostCodeController = TextEditingController( + text: s.address.postCode?.toString() ?? '', + ); + + passwordController = TextEditingController(text: s.password); + repeatPasswordController = TextEditingController(text: s.repeatPassword); + } + + void _addListeners() { + firstNameController.addListener(_onFirstNameChanged); + lastNameController.addListener(_onLastNameChanged); + documentNumberController.addListener(_onDocumentNumberChanged); + documentTypeController.addListener(_onDocumentTypeChanged); + phoneController.addListener(_onPhoneChanged); + emailController.addListener(_onEmailChanged); + + relationshipController.addListener(_onRelationshipChanged); + bornAtController.addListener(_onBornAtTextChanged); + placeOfBirthController.addListener(_onPlaceOfBirthChanged); + birthCountryController.addListener(_onBirthCountryChanged); + + addressStreetController.addListener(_onAddressStreetChanged); + addressCityController.addListener(_onAddressCityChanged); + addressProvinceController.addListener(_onAddressProvinceChanged); + addressStateController.addListener(_onAddressStateChanged); + addressCountryController.addListener(_onAddressCountryChanged); + addressPostCodeController.addListener(_onAddressPostCodeChanged); + + passwordController.addListener(_onPasswordChanged); + repeatPasswordController.addListener(_onRepeatPasswordChanged); + } + + void next() { + if (state.isLoading) return; + + final ok = switch (state.currentIndex) { + 0 => _validateStep0(), + 1 => _validateStep1(), + 2 => _validateStep2(), + _ => true, + }; + + if (!ok) return; + + if (state.currentIndex >= _lastIndex) { + unawaited(signUp()); + return; + } + + state = state.copyWith( + currentIndex: (state.currentIndex + 1).clamp(0, _lastIndex), + ); + } + + void back() { + if (state.isLoading) return; + if (state.currentIndex <= 0) return; + + state = state.copyWith( + currentIndex: (state.currentIndex - 1).clamp(0, _lastIndex), + ); + } + + void setDocumentType(String? value) { + final v = value ?? ''; + if (documentTypeController.text == v) return; + documentTypeController.text = v; + } + + void setRelationship(String? value) { + final v = value ?? ''; + if (relationshipController.text == v) return; + relationshipController.text = v; + } + + void setAddressCountry(String? value) { + final v = value ?? ''; + if (addressCountryController.text == v) return; + addressCountryController.text = v; + } + + void setAcceptTerms(bool value) { + if (value == state.acceptTerms) return; + state = state.copyWith(acceptTerms: value, errorMessage: ''); + } + + void toggleShowPassword() { + state = state.copyWith(isShowPassword: !state.isShowPassword); + } + + void _onFirstNameChanged() { + final text = firstNameController.text; + if (text == state.firstName) return; + state = state.copyWith(firstName: text, errorMessage: ''); + } + + void _onLastNameChanged() { + final text = lastNameController.text; + if (text == state.lastName) return; + state = state.copyWith(lastName: text, errorMessage: ''); + } + + void _onDocumentNumberChanged() { + final text = documentNumberController.text; + if (text == state.documentNumber) return; + state = state.copyWith(documentNumber: text, errorMessage: ''); + } + + void _onDocumentTypeChanged() { + final text = documentTypeController.text; + if (text == state.documentType) return; + state = state.copyWith(documentType: text, errorMessage: ''); + } + + void _onRelationshipChanged() { + final text = relationshipController.text; + if (text == state.relationship) return; + state = state.copyWith(relationship: text, errorMessage: ''); + } + + void _onPhoneChanged() { + final text = phoneController.text; + if (text == state.phone) return; + + state = state.copyWith(phone: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith(phoneError: _phoneErrorFor(text)); + } + } + + void _onEmailChanged() { + final text = emailController.text; + if (text == state.email) return; + + state = state.copyWith(email: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith(emailError: _emailErrorFor(text)); + } + } + + void _onPasswordChanged() { + final text = passwordController.text; + if (text == state.password) return; + + state = state.copyWith(password: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith( + passwordError: _passwordErrorFor( + password: state.password, + repeatPassword: state.repeatPassword, + ), + ); + } + } + + void _onRepeatPasswordChanged() { + final text = repeatPasswordController.text; + if (text == state.repeatPassword) return; + + state = state.copyWith(repeatPassword: text, errorMessage: ''); + + if (state.showErrors) { + state = state.copyWith( + passwordError: _passwordErrorFor( + password: state.password, + repeatPassword: state.repeatPassword, + ), + ); + } + } + + void _onBornAtTextChanged() { + final text = bornAtController.text; + final parsed = _tryParseDate(text); + + if (text.trim().isEmpty) { + if (state.bornAt != null) { + state = state.copyWith(bornAt: null, errorMessage: ''); + } + return; + } + + if (parsed != null && parsed != state.bornAt) { + state = state.copyWith(bornAt: parsed, errorMessage: ''); + } + } + + void _onPlaceOfBirthChanged() { + final text = placeOfBirthController.text; + if (text == state.placeOfBirth) return; + state = state.copyWith(placeOfBirth: text, errorMessage: ''); + } + + void _onBirthCountryChanged() { + final text = birthCountryController.text; + if (text == state.birthCountry) return; + state = state.copyWith(birthCountry: text, errorMessage: ''); + } + + void _onAddressStreetChanged() { + final text = addressStreetController.text; + if (text == state.address.street) return; + state = state.copyWith( + address: state.address.copyWith(street: text), + errorMessage: '', + ); + } + + void _onAddressCityChanged() { + final text = addressCityController.text; + if (text == state.address.city) return; + state = state.copyWith( + address: state.address.copyWith(city: text), + errorMessage: '', + ); + } + + void _onAddressProvinceChanged() { + final text = addressProvinceController.text; + if (text == state.address.province) return; + state = state.copyWith( + address: state.address.copyWith(province: text), + errorMessage: '', + ); + } + + void _onAddressStateChanged() { + final text = addressStateController.text; + if (text == state.address.state) return; + state = state.copyWith( + address: state.address.copyWith(state: text), + errorMessage: '', + ); + } + + void _onAddressCountryChanged() { + final text = addressCountryController.text; + if (text == state.address.country) return; + state = state.copyWith( + address: state.address.copyWith(country: text), + errorMessage: '', + ); + } + + void _onAddressPostCodeChanged() { + final text = addressPostCodeController.text.trim(); + final parsed = int.tryParse(text); + + if (text.isEmpty) { + if (state.address.postCode != null) { + state = state.copyWith( + address: state.address.copyWith(postCode: null), + errorMessage: '', + ); + } + return; + } + + if (parsed != null && parsed != state.address.postCode) { + state = state.copyWith( + address: state.address.copyWith(postCode: parsed), + errorMessage: '', + ); + } + } + + void setOtpCode(String code) { + state = state.copyWith(otpCode: code, otpError: ''); + } + + bool _validateStep0() { + final emailError = _emailErrorFor(state.email); + final phoneError = _phoneErrorFor(state.phone); + state = state.copyWith( + showErrors: true, + emailError: emailError, + phoneError: phoneError, + errorMessage: '', + ); + if (state.firstName.trim().isEmpty) { + state = state.copyWith(errorMessage: 'El nombre es obligatorio'); + return false; + } + if (state.lastName.trim().isEmpty) { + state = state.copyWith(errorMessage: 'El apellido es obligatorio'); + return false; + } + if (state.documentType.trim().isEmpty) { + state = state.copyWith( + errorMessage: 'El tipo de documento es obligatorio', + ); + return false; + } + if (state.documentNumber.trim().isEmpty) { + state = state.copyWith( + errorMessage: 'El número de documento es obligatorio', + ); + return false; + } + if (phoneError.isNotEmpty) { + state = state.copyWith(errorMessage: phoneError); + return false; + } + if (emailError.isNotEmpty) { + state = state.copyWith(errorMessage: emailError); + return false; + } + if (!state.acceptTerms) { + state = state.copyWith( + errorMessage: 'Debes aceptar los términos y condiciones', + ); + return false; + } + return true; + } + + bool _validateStep1() { + if (!state.showErrors) { + state = state.copyWith(showErrors: true, errorMessage: ''); + } else { + state = state.copyWith(errorMessage: ''); + } + + if (state.relationship.trim().isEmpty) { + state = state.copyWith(errorMessage: 'La relación es obligatoria'); + return false; + } + + if (state.bornAt == null) { + state = state.copyWith( + errorMessage: 'Selecciona una fecha de nacimiento válida (DD/MM/AAAA)', + ); + return false; + } + + if (state.placeOfBirth.trim().isEmpty) { + state = state.copyWith(errorMessage: 'Falta el lugar de nacimiento'); + return false; + } + + if (state.birthCountry.trim().isEmpty) { + state = state.copyWith(errorMessage: 'Falta el país de nacimiento'); + return false; + } + + if (!_isAddressValid(state.address)) { + state = state.copyWith(errorMessage: 'Completa la dirección'); + return false; + } + + return true; + } + + bool _validateStep2() { + final passwordError = _passwordErrorFor( + password: state.password, + repeatPassword: state.repeatPassword, + ); + + state = state.copyWith( + showErrors: true, + passwordError: passwordError, + errorMessage: '', + ); + + if (passwordError.isNotEmpty) return false; + + return true; + } + + bool _validateForm() { + return _validateStep0() && _validateStep1() && _validateStep2(); + } + + bool _isValidEmail(String email) => _emailRegex.hasMatch(email); + + String _emailErrorFor(String value) { + final email = value.trim(); + if (email.isEmpty) return I18n.errorEmailRequired; + if (!_isValidEmail(email)) return I18n.errorEmailInvalid; + return ''; + } + + String _passwordErrorFor({ + required String password, + required String repeatPassword, + }) { + final p = password.trim(); + final rp = repeatPassword.trim(); + + if (p.isEmpty) return I18n.errorPasswordRequired; + if (p.length < 6) return I18n.errorPasswordMinLength; + + if (rp.isEmpty) return 'Repite la contraseña'; + if (p != rp) return 'Las contraseñas no coinciden'; + + return ''; + } + + String _phoneErrorFor(String value) { + final phone = value.trim(); + if (phone.isEmpty) return 'El teléfono es obligatorio'; + if (!_phoneRegex.hasMatch(phone)) { + return 'El teléfono no tiene un formato válido'; + } + return ''; + } + + bool _isAddressValid(AddressViewState a) { + return a.street.trim().isNotEmpty && + a.city.trim().isNotEmpty && + a.province.trim().isNotEmpty && + a.state.trim().isNotEmpty && + a.country.trim().isNotEmpty && + a.postCode != null; + } + + AddressEntity _toAddressEntity(AddressViewState a) { + return AddressEntity( + street: a.street.trim(), + city: a.city.trim(), + province: a.province.trim(), + state: a.state.trim(), + country: a.country.trim(), + postCode: a.postCode ?? 0, + ); + } + + SignUpRequestEntity _toRequest() { + final bornAt = state.bornAt; + if (bornAt == null) throw Exception('bornAt is required'); + + return SignUpRequestEntity( + documentType: state.documentType.trim(), + documentNumber: state.documentNumber.trim(), + relationship: state.relationship.trim(), + firstName: state.firstName.trim(), + lastName: state.lastName.trim(), + email: state.email.trim(), + phone: state.phone.trim(), + language: state.language.trim().isEmpty ? 'es' : state.language.trim(), + password: state.password, + bornAt: bornAt.millisecondsSinceEpoch, + userId: state.userId.trim(), + placeOfBirth: state.placeOfBirth.trim(), + birthCountry: state.birthCountry.trim(), + addresses: [_toAddressEntity(state.address)], + taxResidences: [_toAddressEntity(state.address)], + ); + } + + Future signUp() async { + if (state.isLoading) return false; + if (!_validateForm()) return false; + + _startLoadingForSignUp(); + + try { + final request = _toRequest(); + + final token = await _signUp(request); + if (!ref.mounted) return false; + + final secretEntity = await _generateTwoFA(token); + if (!ref.mounted) return false; + + final validationError = _twoFAResponseError(secretEntity); + if (validationError != null) { + _finishWithError(token: token, message: validationError); + return false; + } + + _finishWithSuccess(token: token, twoFASecret: secretEntity); + return true; + } catch (e) { + if (!ref.mounted) return false; + _finishWithError(message: e.toString()); + return false; + } + } + + void _startLoadingForSignUp() { + state = state.copyWith( + isLoading: true, + errorMessage: '', + twoFASecret: null, + ); + } + + Future _signUp(SignUpRequestEntity request) async { + return _signUpUseCase.signUp(request: request); + } + + Future _generateTwoFA(String token) async { + return _generateTwoFASignUpUseCase.generateTwoFASignUp(token: token); + } + + Future verifyTwoFACodeSignUp({required String token}) async { + if (state.isOtpLoading) return false; + + final code = state.otpCode.trim(); + if (code.length != 6) { + state = state.copyWith(otpError: 'error otp'); + return false; + } + + state = state.copyWith(isOtpLoading: true, otpError: ''); + + try { + await _verifyTwoFACodeSignUpUseCase.verifyTwoFACodeSignUp( + token: token, + code: code, + ); + + if (!ref.mounted) return false; + state = state.copyWith(isOtpLoading: false); + return true; + } catch (e) { + if (!ref.mounted) return false; + state = state.copyWith(isOtpLoading: false, otpError: e.toString()); + return false; + } + } + + String? _twoFAResponseError(TwoFASecretEntity entity) { + if (!entity.isCreated) { + return 'No se pudo generar el 2FA (isCreated=false)'; + } + + final secret = entity.item.secret.trim(); + final qr = entity.item.qr.trim(); + + if (secret.isEmpty || qr.isEmpty) { + return 'Respuesta inválida del 2FA (secret/qr vacío)'; + } + + return null; + } + + void _finishWithSuccess({ + required String token, + required TwoFASecretEntity twoFASecret, + }) { + state = state.copyWith( + isLoading: false, + token: token, + twoFASecret: twoFASecret, + errorMessage: '', + ); + } + + void _finishWithError({String? token, required String message}) { + state = state.copyWith( + isLoading: false, + token: token ?? state.token, + errorMessage: message, + ); + } + + void showAccountCreated() { + state = state.copyWith(showAccountCreated: true, errorMessage: ''); + } + + DateTime? _tryParseDate(String value) { + final v = value.trim(); + if (v.isEmpty) return null; + + final parts = v.split('/'); + if (parts.length != 3) return null; + + final d = int.tryParse(parts[0]); + final m = int.tryParse(parts[1]); + final y = int.tryParse(parts[2]); + + if (d == null || m == null || y == null) return null; + + final date = DateTime(y, m, d); + if (date.year != y || date.month != m || date.day != d) return null; + + return date; + } + + String _formatDate(DateTime d) { + final dd = d.day.toString().padLeft(2, '0'); + final mm = d.month.toString().padLeft(2, '0'); + final yyyy = d.year.toString(); + return '$dd/$mm/$yyyy'; + } + + void setBornAt(DateTime date) { + bornAtController.text = _formatDate(date); + state = state.copyWith(bornAt: date, errorMessage: ''); + } + + void disposeControllers() { + firstNameController.removeListener(_onFirstNameChanged); + lastNameController.removeListener(_onLastNameChanged); + documentNumberController.removeListener(_onDocumentNumberChanged); + documentTypeController.removeListener(_onDocumentTypeChanged); + phoneController.removeListener(_onPhoneChanged); + emailController.removeListener(_onEmailChanged); + + relationshipController.removeListener(_onRelationshipChanged); + bornAtController.removeListener(_onBornAtTextChanged); + placeOfBirthController.removeListener(_onPlaceOfBirthChanged); + birthCountryController.removeListener(_onBirthCountryChanged); + + addressStreetController.removeListener(_onAddressStreetChanged); + addressCityController.removeListener(_onAddressCityChanged); + addressProvinceController.removeListener(_onAddressProvinceChanged); + addressStateController.removeListener(_onAddressStateChanged); + addressCountryController.removeListener(_onAddressCountryChanged); + addressPostCodeController.removeListener(_onAddressPostCodeChanged); + + passwordController.removeListener(_onPasswordChanged); + repeatPasswordController.removeListener(_onRepeatPasswordChanged); + + firstNameController.dispose(); + lastNameController.dispose(); + documentNumberController.dispose(); + documentTypeController.dispose(); + phoneController.dispose(); + emailController.dispose(); + + relationshipController.dispose(); + bornAtController.dispose(); + placeOfBirthController.dispose(); + birthCountryController.dispose(); + + addressStreetController.dispose(); + addressCityController.dispose(); + addressProvinceController.dispose(); + addressStateController.dispose(); + addressCountryController.dispose(); + addressPostCodeController.dispose(); + + passwordController.dispose(); + repeatPasswordController.dispose(); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart new file mode 100644 index 00000000..c5bcf911 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart @@ -0,0 +1,45 @@ +import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'address_view_state.dart'; + +part 'sign_up_view_state.freezed.dart'; + +@freezed +abstract class SignUpViewState with _$SignUpViewState { + const factory SignUpViewState({ + @Default(0) int currentIndex, + + @Default('') String documentNumber, + @Default('') String documentType, + @Default(false) bool acceptTerms, + @Default('') String relationship, + + @Default('') String firstName, + @Default('') String lastName, + @Default('') String email, + @Default('') String phone, + @Default('') String language, + DateTime? bornAt, + @Default('') String password, + @Default('') String repeatPassword, + @Default(false) bool isShowPassword, + @Default('') String userId, + @Default('') String placeOfBirth, + @Default('') String birthCountry, + + @Default(AddressViewState()) AddressViewState address, + + @Default('') String emailError, + @Default('') String passwordError, + @Default('') String phoneError, + @Default('') String errorMessage, + @Default(false) bool isLoading, + @Default(false) bool showErrors, + @Default('') String token, + TwoFASecretEntity? twoFASecret, + @Default('') String otpCode, + @Default('') String otpError, + @Default(false) bool isOtpLoading, + @Default(false) bool showAccountCreated, + }) = _SignUpViewState; +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart new file mode 100644 index 00000000..510070e5 --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart @@ -0,0 +1,400 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sign_up_view_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$SignUpViewState { + + int get currentIndex; String get documentNumber; String get documentType; bool get acceptTerms; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get language; DateTime? get bornAt; String get password; String get repeatPassword; bool get isShowPassword; String get userId; String get placeOfBirth; String get birthCountry; AddressViewState get address; String get emailError; String get passwordError; String get phoneError; String get errorMessage; bool get isLoading; bool get showErrors; String get token; TwoFASecretEntity? get twoFASecret; String get otpCode; String get otpError; bool get isOtpLoading; bool get showAccountCreated; +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SignUpViewStateCopyWith get copyWith => _$SignUpViewStateCopyWithImpl(this as SignUpViewState, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)); +} + + +@override +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated]); + +@override +String toString() { + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated)'; +} + + +} + +/// @nodoc +abstract mixin class $SignUpViewStateCopyWith<$Res> { + factory $SignUpViewStateCopyWith(SignUpViewState value, $Res Function(SignUpViewState) _then) = _$SignUpViewStateCopyWithImpl; +@useResult +$Res call({ + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated +}); + + +$AddressViewStateCopyWith<$Res> get address;$TwoFASecretEntityCopyWith<$Res>? get twoFASecret; + +} +/// @nodoc +class _$SignUpViewStateCopyWithImpl<$Res> + implements $SignUpViewStateCopyWith<$Res> { + _$SignUpViewStateCopyWithImpl(this._self, this._then); + + final SignUpViewState _self; + final $Res Function(SignUpViewState) _then; + +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,}) { + return _then(_self.copyWith( +currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable +as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable +as String,acceptTerms: null == acceptTerms ? _self.acceptTerms : acceptTerms // ignore: cast_nullable_to_non_nullable +as bool,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as DateTime?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,repeatPassword: null == repeatPassword ? _self.repeatPassword : repeatPassword // ignore: cast_nullable_to_non_nullable +as String,isShowPassword: null == isShowPassword ? _self.isShowPassword : isShowPassword // ignore: cast_nullable_to_non_nullable +as bool,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable +as AddressViewState,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable +as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable +as String,phoneError: null == phoneError ? _self.phoneError : phoneError // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable +as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String,twoFASecret: freezed == twoFASecret ? _self.twoFASecret : twoFASecret // ignore: cast_nullable_to_non_nullable +as TwoFASecretEntity?,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable +as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable +as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable +as bool,showAccountCreated: null == showAccountCreated ? _self.showAccountCreated : showAccountCreated // ignore: cast_nullable_to_non_nullable +as bool, + )); +} +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AddressViewStateCopyWith<$Res> get address { + + return $AddressViewStateCopyWith<$Res>(_self.address, (value) { + return _then(_self.copyWith(address: value)); + }); +}/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretEntityCopyWith<$Res>? get twoFASecret { + if (_self.twoFASecret == null) { + return null; + } + + return $TwoFASecretEntityCopyWith<$Res>(_self.twoFASecret!, (value) { + return _then(_self.copyWith(twoFASecret: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [SignUpViewState]. +extension SignUpViewStatePatterns on SignUpViewState { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SignUpViewState value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SignUpViewState() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SignUpViewState value) $default,){ +final _that = this; +switch (_that) { +case _SignUpViewState(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SignUpViewState value)? $default,){ +final _that = this; +switch (_that) { +case _SignUpViewState() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SignUpViewState() when $default != null: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated) $default,) {final _that = this; +switch (_that) { +case _SignUpViewState(): +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated)? $default,) {final _that = this; +switch (_that) { +case _SignUpViewState() when $default != null: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _SignUpViewState implements SignUpViewState { + const _SignUpViewState({this.currentIndex = 0, this.documentNumber = '', this.documentType = '', this.acceptTerms = false, this.relationship = '', this.firstName = '', this.lastName = '', this.email = '', this.phone = '', this.language = '', this.bornAt, this.password = '', this.repeatPassword = '', this.isShowPassword = false, this.userId = '', this.placeOfBirth = '', this.birthCountry = '', this.address = const AddressViewState(), this.emailError = '', this.passwordError = '', this.phoneError = '', this.errorMessage = '', this.isLoading = false, this.showErrors = false, this.token = '', this.twoFASecret, this.otpCode = '', this.otpError = '', this.isOtpLoading = false, this.showAccountCreated = false}); + + +@override@JsonKey() final int currentIndex; +@override@JsonKey() final String documentNumber; +@override@JsonKey() final String documentType; +@override@JsonKey() final bool acceptTerms; +@override@JsonKey() final String relationship; +@override@JsonKey() final String firstName; +@override@JsonKey() final String lastName; +@override@JsonKey() final String email; +@override@JsonKey() final String phone; +@override@JsonKey() final String language; +@override final DateTime? bornAt; +@override@JsonKey() final String password; +@override@JsonKey() final String repeatPassword; +@override@JsonKey() final bool isShowPassword; +@override@JsonKey() final String userId; +@override@JsonKey() final String placeOfBirth; +@override@JsonKey() final String birthCountry; +@override@JsonKey() final AddressViewState address; +@override@JsonKey() final String emailError; +@override@JsonKey() final String passwordError; +@override@JsonKey() final String phoneError; +@override@JsonKey() final String errorMessage; +@override@JsonKey() final bool isLoading; +@override@JsonKey() final bool showErrors; +@override@JsonKey() final String token; +@override final TwoFASecretEntity? twoFASecret; +@override@JsonKey() final String otpCode; +@override@JsonKey() final String otpError; +@override@JsonKey() final bool isOtpLoading; +@override@JsonKey() final bool showAccountCreated; + +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SignUpViewStateCopyWith<_SignUpViewState> get copyWith => __$SignUpViewStateCopyWithImpl<_SignUpViewState>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)); +} + + +@override +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated]); + +@override +String toString() { + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated)'; +} + + +} + +/// @nodoc +abstract mixin class _$SignUpViewStateCopyWith<$Res> implements $SignUpViewStateCopyWith<$Res> { + factory _$SignUpViewStateCopyWith(_SignUpViewState value, $Res Function(_SignUpViewState) _then) = __$SignUpViewStateCopyWithImpl; +@override @useResult +$Res call({ + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated +}); + + +@override $AddressViewStateCopyWith<$Res> get address;@override $TwoFASecretEntityCopyWith<$Res>? get twoFASecret; + +} +/// @nodoc +class __$SignUpViewStateCopyWithImpl<$Res> + implements _$SignUpViewStateCopyWith<$Res> { + __$SignUpViewStateCopyWithImpl(this._self, this._then); + + final _SignUpViewState _self; + final $Res Function(_SignUpViewState) _then; + +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,}) { + return _then(_SignUpViewState( +currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable +as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable +as String,acceptTerms: null == acceptTerms ? _self.acceptTerms : acceptTerms // ignore: cast_nullable_to_non_nullable +as bool,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable +as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable +as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable +as DateTime?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable +as String,repeatPassword: null == repeatPassword ? _self.repeatPassword : repeatPassword // ignore: cast_nullable_to_non_nullable +as String,isShowPassword: null == isShowPassword ? _self.isShowPassword : isShowPassword // ignore: cast_nullable_to_non_nullable +as bool,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable +as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable +as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable +as AddressViewState,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable +as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable +as String,phoneError: null == phoneError ? _self.phoneError : phoneError // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as bool,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable +as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable +as String,twoFASecret: freezed == twoFASecret ? _self.twoFASecret : twoFASecret // ignore: cast_nullable_to_non_nullable +as TwoFASecretEntity?,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable +as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable +as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable +as bool,showAccountCreated: null == showAccountCreated ? _self.showAccountCreated : showAccountCreated // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + +/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$AddressViewStateCopyWith<$Res> get address { + + return $AddressViewStateCopyWith<$Res>(_self.address, (value) { + return _then(_self.copyWith(address: value)); + }); +}/// Create a copy of SignUpViewState +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$TwoFASecretEntityCopyWith<$Res>? get twoFASecret { + if (_self.twoFASecret == null) { + return null; + } + + return $TwoFASecretEntityCopyWith<$Res>(_self.twoFASecret!, (value) { + return _then(_self.copyWith(twoFASecret: value)); + }); +} +} + +// dart format on diff --git a/modules/auth/lib/src/features/sign_up/signup_builder.dart b/modules/auth/lib/src/features/sign_up/sign_up_builder.dart similarity index 86% rename from modules/auth/lib/src/features/sign_up/signup_builder.dart rename to modules/auth/lib/src/features/sign_up/sign_up_builder.dart index 70834e73..f3c57dd1 100644 --- a/modules/auth/lib/src/features/sign_up/signup_builder.dart +++ b/modules/auth/lib/src/features/sign_up/sign_up_builder.dart @@ -1,4 +1,4 @@ -import 'package:auth/src/features/sign_up/signup_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/sign_up_screen.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:get_it/get_it.dart'; diff --git a/modules/auth/lib/src/features/sign_up/signup_address_screen.dart b/modules/auth/lib/src/features/sign_up/signup_address_screen.dart deleted file mode 100644 index d1db0593..00000000 --- a/modules/auth/lib/src/features/sign_up/signup_address_screen.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class SignupAddressScreen extends ConsumerStatefulWidget { - const SignupAddressScreen({super.key}); - - @override - ConsumerState createState() => - SignupAddressScreenState(); -} - -class SignupAddressScreenState extends ConsumerState { - late String country; - late int relation; - - @override - void initState() { - relation = 0; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - spacing: 24, - children: [ - Column( - spacing: 8, - children: [ - Align( - alignment: Alignment.bottomLeft, - child: Text( - "Fecha de nacimiento", - style: TextStyle(fontSize: 14, letterSpacing: 0), - ), - ), - Row( - spacing: 8, - children: [ - Expanded( - child: CustomTextField( - //label: "Fecha de nacimiento", - hint: "DD", - length: 2, - keyboardType: TextInputType.number, - ), - ), - Expanded( - child: CustomTextField( - hint: "MM", - length: 2, - keyboardType: TextInputType.number, - ), - ), - Expanded( - child: CustomTextField( - hint: "AAAA", - length: 4, - keyboardType: TextInputType.number, - ), - ), - ], - ), - ], - ), - Column( - spacing: 8, - children: [ - Align( - alignment: Alignment.bottomLeft, - child: Text( - "¿Qué familiar eres?", - style: TextStyle(fontSize: 14, letterSpacing: 0), - ), - ), - CustomDropdown( - items: [Text("Padre"), Text("Madre"), Text("Tutor")], - hint: "¿Qué familiar eres?", - onChanged: (value) => setState(() { - relation = value; - }), - ), - ], - ), - CustomTextField( - label: "Dirección completa", - hint: "Calle Gran Vía 30 6º, 28013", - ), - CustomTextField(label: "Ciudad", hint: "Ciudad"), - Column( - spacing: 8, - children: [ - Align( - alignment: Alignment.bottomLeft, - child: Text( - "País", - style: TextStyle(fontSize: 14, letterSpacing: 0), - ), - ), - CustomDropdown( - items: [Text("España"), Text("Francia"), Text("Portugal")], - hint: "País", - onChanged: (value) => setState(() { - country = value; - }), - ), - ], - ), - CustomTextField(label: "Nacionalidad", hint: "España"), - ], - ); - } -} diff --git a/modules/auth/lib/src/features/sign_up/signup_personal_screen.dart b/modules/auth/lib/src/features/sign_up/signup_personal_screen.dart deleted file mode 100644 index e39e504e..00000000 --- a/modules/auth/lib/src/features/sign_up/signup_personal_screen.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; - -class SignupPersonalScreen extends StatelessWidget { - const SignupPersonalScreen({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - spacing: 24, - children: [ - CustomTextField(label: "Nombre", hint: "Nombre"), - CustomTextField(label: "Apellidos", hint: "Apellidos"), - CustomTextField(label: "DNI", hint: "DNI"), - Row( - spacing: 8, - children: [ - /*CustomDropdown( - value: 0, - items: [Icon(Icons.outlined_flag), Icon(Icons.outlined_flag), Icon(Icons.outlined_flag)], - onChanged: (value)=> {}, - width: 80, - ),*/ - Expanded( - child: CustomTextField( - label: "Teléfono móvil", - hint: "123456789", - keyboardType: TextInputType.number, - ), - ), - ], - ), - CustomTextField( - label: "Correo electrónico", - hint: "Correo electrónico", - ), - ], - ); - } -} diff --git a/modules/auth/lib/src/features/sign_up/signup_screen.dart b/modules/auth/lib/src/features/sign_up/signup_screen.dart deleted file mode 100644 index e1fdc9f9..00000000 --- a/modules/auth/lib/src/features/sign_up/signup_screen.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:auth/src/widgets/layouts/form_step_layout.dart'; -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:auth/src/features/sign_up/signup_address_screen.dart'; -import 'package:auth/src/features/sign_up/signup_personal_screen.dart'; -import 'package:auth/src/features/sign_up/signup_user_screen.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:get_it/get_it.dart'; -import 'package:navigation/navigation.dart'; - -import 'account_created_screen.dart'; - -class SignupScreen extends ConsumerStatefulWidget { - NavigationContract navigationContract; - - SignupScreen({super.key, required this.navigationContract}); - - @override - ConsumerState createState() => _SignupScreenState(); -} - -class _SignupScreenState extends ConsumerState { - int currentStep = 0; - bool acceptTerms = false; - - @override - Widget build(BuildContext context) { - return getSteps()[currentStep]; - } - - List getSteps() { - final theme = ref.watch(themePortProvider); - - return [ - FormStepLayout( - title: "Crea tu usuario", - subtitle: - "Con tu email y tu número podremos mantenerte siempre informado", - supertitle: "Usuario y contacto", - currentStep: 1, - numSteps: 3, - body: [SignupPersonalScreen()], - footer: [ - Row( - spacing: 16, - children: [ - Expanded( - child: SecondaryButton( - onPressed: () => {}, - text: "Atrás", - size: 16, - ), - ), - Expanded( - child: PrimaryButton( - onPressed: () => { - setState(() { - currentStep++; - }), - }, - text: "Siguiente", - size: 16, - color: theme.getColorFor(ThemeCode.buttonSecondary), - ), - ), - ], - ), - CheckboxListTile( - value: acceptTerms, - onChanged: (_) => setState(() { - acceptTerms = !acceptTerms; - }), - title: Text( - "Acepto los términos y condiciones", - style: TextStyle(fontSize: 16, letterSpacing: 0), - ), - checkboxScaleFactor: 1.5, - contentPadding: EdgeInsets.zero, - activeColor: theme.getColorFor(ThemeCode.buttonPrimary), - controlAffinity: ListTileControlAffinity.leading, - ), - ], - nextStep: () => { - setState(() { - currentStep++; - }), - }, - previousStep: () => {}, - ), - FormStepLayout( - title: "Tu dirección", - subtitle: - "Tu dirección nos ayudará a verificar y mantener la seguridad de tu cuenta", - supertitle: "Domicilio", - currentStep: 2, - numSteps: 3, - body: [SignupAddressScreen()], - nextStep: () => { - setState(() { - currentStep++; - }), - }, - previousStep: () => { - setState(() { - currentStep--; - }), - }, - ), - FormStepLayout( - title: "Identifícate", - subtitle: - "Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial", - supertitle: "Usuario y contacto", - currentStep: 3, - numSteps: 3, - body: [SignupUserScreen()], - nextStep: () => { - setState(() { - currentStep++; - }), - }, - previousStep: () => { - setState(() { - currentStep--; - }), - }, - ), - AccountCreatedScreen( - navigationContract: widget.navigationContract, - kidAccount: false, - ), - ]; - } -} diff --git a/modules/auth/lib/src/features/sign_up/signup_user_screen.dart b/modules/auth/lib/src/features/sign_up/signup_user_screen.dart deleted file mode 100644 index b152964a..00000000 --- a/modules/auth/lib/src/features/sign_up/signup_user_screen.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; - -class SignupUserScreen extends StatefulWidget{ - const SignupUserScreen({super.key}); - - @override - State createState() => SignupUserScreenState(); -} - -class SignupUserScreenState extends State{ - bool passwordVisible=false; - - @override - Widget build(BuildContext context) { - return Column( - spacing: 24, - children: [ - CustomTextField( - showPassword: passwordVisible, - label: "Contraseña", - hint: "********" - ), - CustomTextField( - showPassword: passwordVisible, - label: "Repetir contraseña", - hint: "*******" - ), - ], - ); - } - -} \ No newline at end of file diff --git a/modules/auth/lib/src/widgets/form_error_banner.dart b/modules/auth/lib/src/widgets/form_error_banner.dart new file mode 100644 index 00000000..bfe26152 --- /dev/null +++ b/modules/auth/lib/src/widgets/form_error_banner.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class FormErrorBanner extends StatelessWidget { + final String message; + + const FormErrorBanner({super.key, required this.message}); + + @override + Widget build(BuildContext context) { + if (message.trim().isEmpty) return const SizedBox.shrink(); + + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.red.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.red.withValues(alpha: 0.35)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.error_outline, size: 18, color: Colors.red), + const SizedBox(width: 8), + Expanded( + child: Text( + message, + style: const TextStyle(fontSize: 14, height: 1.2), + ), + ), + ], + ), + ); + } +} diff --git a/modules/auth/lib/src/widgets/layouts/form_step_layout.dart b/modules/auth/lib/src/widgets/layouts/form_step_layout.dart index d87d2dda..6326b45c 100644 --- a/modules/auth/lib/src/widgets/layouts/form_step_layout.dart +++ b/modules/auth/lib/src/widgets/layouts/form_step_layout.dart @@ -2,7 +2,7 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -class FormStepLayout extends ConsumerWidget{ +class FormStepLayout extends ConsumerWidget { final int currentStep; final int numSteps; final String? supertitle; @@ -23,7 +23,7 @@ class FormStepLayout extends ConsumerWidget{ required this.body, this.footer, required this.nextStep, - required this.previousStep + required this.previousStep, }); @override @@ -32,63 +32,101 @@ class FormStepLayout extends ConsumerWidget{ return Scaffold( backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: SafeArea(child: SingleChildScrollView(child: Container( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - padding: EdgeInsets.only(top: 40, left: 24, right: 24), - child: Column( - spacing: 32, - children: [ - Column( - spacing: 24, + body: SafeArea( + child: SingleChildScrollView( + child: Container( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + padding: EdgeInsets.only(left: 24, right: 24), + child: Column( children: [ - StepIndicator( - total: numSteps, - current: currentStep, - color: theme.getColorFor(ThemeCode.buttonPrimary) + Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: StepIndicator( + total: numSteps, + current: currentStep, + color: theme.getColorFor(ThemeCode.buttonPrimary), + ), + ), + SizedBox(height: 16), + if (supertitle != null) + Text( + supertitle!, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + SizedBox(height: 10), + Text( + title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + if (subtitle != null) + Text( + subtitle!, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18), + ), + ], ), - if (supertitle!=null) Text(supertitle!, textAlign: TextAlign.center, style: TextStyle(fontSize: 18)), - Text(title, textAlign: TextAlign.center, style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), - if (subtitle!=null) Text(subtitle!, textAlign: TextAlign.center, style: TextStyle(fontSize: 18)), - ] - ), - Column( - spacing: 40, - children: [ ...body, - if (footer==null) navigationButtons(currentStep, numSteps, nextStep, previousStep, theme), - ...(footer?? []) - ] - ) - ] - ) - ))) + SizedBox(height: 16), + if (footer == null) + navigationButtons( + currentStep, + numSteps, + nextStep, + previousStep, + theme, + ), + ...(footer ?? []), + ], + ), + ), + ), + ), ); } - Widget navigationButtons(int currentStep, int numSteps, VoidCallback nextStep, VoidCallback previousStep, ThemePort theme) { - if (currentStep == numSteps){ + Widget navigationButtons( + int currentStep, + int numSteps, + VoidCallback nextStep, + VoidCallback previousStep, + ThemePort theme, + ) { + if (currentStep == numSteps) { return PrimaryButton( onPressed: nextStep, text: "Continuar", - color: theme.getColorFor(ThemeCode.buttonPrimary) + color: theme.getColorFor(ThemeCode.buttonPrimary), ); } else { return Row( spacing: 16, children: [ - Expanded(child: SecondaryButton( - onPressed: previousStep, - text: "Atrás", - size: 16, - )), - Expanded(child: PrimaryButton( - onPressed: nextStep, - text: "Siguiente", - size: 16, - color: theme.getColorFor(ThemeCode.buttonSecondary) - )) - ] + Expanded( + child: SecondaryButton( + onPressed: previousStep, + text: "Atrás", + size: 16, + ), + ), + Expanded( + child: PrimaryButton( + onPressed: nextStep, + text: "Siguiente", + size: 16, + color: theme.getColorFor(ThemeCode.buttonSecondary), + ), + ), + ], ); } } -} \ No newline at end of file +} diff --git a/modules/auth/lib/src/widgets/layouts/sign_up_layout.dart b/modules/auth/lib/src/widgets/layouts/sign_up_layout.dart new file mode 100644 index 00000000..5fe55a2b --- /dev/null +++ b/modules/auth/lib/src/widgets/layouts/sign_up_layout.dart @@ -0,0 +1,114 @@ +import 'package:auth/src/widgets/form_error_banner.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class SignUpLayout extends StatelessWidget { + final ThemePort theme; + final String title; + final String subtitle; + final String supertitle; + final int currentStep; + final int numSteps; + + final Widget body; + + final VoidCallback onBackPressed; + final VoidCallback onNextPressed; + + final String errorMessage; + + const SignUpLayout({ + super.key, + required this.theme, + required this.title, + required this.subtitle, + required this.supertitle, + required this.currentStep, + required this.numSteps, + required this.body, + required this.onBackPressed, + required this.onNextPressed, + this.errorMessage = '', + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), + body: SafeArea( + child: Container( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + padding: const EdgeInsets.only(left: 24, right: 24), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: StepIndicator( + total: numSteps, + current: currentStep, + color: theme.getColorFor(ThemeCode.buttonPrimary), + ), + ), + const SizedBox(height: 8), + Text( + supertitle, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18), + ), + const SizedBox(height: 10), + Text( + title, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Text( + subtitle, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18), + ), + const SizedBox(height: 16), + + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.only(bottom: 16), + child: Column( + children: [ + FormErrorBanner(message: errorMessage), + body, + ], + ), + ), + ), + + Row( + spacing: 16, + children: [ + Expanded( + child: SecondaryButton( + onPressed: onBackPressed, + text: "Atrás", + size: 16, + ), + ), + Expanded( + child: PrimaryButton( + onPressed: onNextPressed, + text: "Siguiente", + size: 16, + color: theme.getColorFor(ThemeCode.buttonSecondary), + ), + ), + ], + ), + const SizedBox(height: 12), + ], + ), + ), + ), + ); + } +} diff --git a/modules/auth/lib/src/widgets/steps.dart b/modules/auth/lib/src/widgets/steps.dart new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/modules/auth/lib/src/widgets/steps.dart @@ -0,0 +1 @@ + diff --git a/modules/auth/pubspec.yaml b/modules/auth/pubspec.yaml index d8ff6537..d816d25c 100644 --- a/modules/auth/pubspec.yaml +++ b/modules/auth/pubspec.yaml @@ -33,6 +33,9 @@ dependencies: freezed: ^3.2.3 dio: ^5.9.0 country_code_picker: ^3.4.1 + json_annotation: ^4.9.0 + json_serializable: ^6.11.2 + uuid: ^4.5.2 dev_dependencies: flutter_test: diff --git a/packages/design_system/lib/src/dropdowns/dropdown.dart b/packages/design_system/lib/src/dropdowns/dropdown.dart index 1c27311b..52b83928 100644 --- a/packages/design_system/lib/src/dropdowns/dropdown.dart +++ b/packages/design_system/lib/src/dropdowns/dropdown.dart @@ -3,7 +3,9 @@ import 'package:flutter/material.dart'; class CustomDropdown extends StatelessWidget { final List items; final List? values; + final ValueChanged onChanged; + final dynamic value; final String? hint; final String? label; @@ -28,10 +30,15 @@ class CustomDropdown extends StatelessWidget { @override Widget build(BuildContext context) { + final borderColor = color ?? const Color(0xFF4B4B4B); + + OutlineInputBorder border(Color c) => OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(radius)), + borderSide: BorderSide(color: c), + ); return Column( - spacing: 8, children: [ - if (label != null) + if (label != null) ...[ Align( alignment: Alignment.bottomLeft, child: Text( @@ -39,6 +46,8 @@ class CustomDropdown extends StatelessWidget { style: const TextStyle(fontSize: 14, letterSpacing: 0), ), ), + const SizedBox(height: 8), + ], SizedBox( width: width, height: height, @@ -46,15 +55,21 @@ class CustomDropdown extends StatelessWidget { child: DropdownButtonFormField( dropdownColor: Colors.white, decoration: InputDecoration( - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(radius)), - borderSide: BorderSide( - color: color ?? const Color(0xFF4B4B4B), - ), + enabledBorder: border(borderColor), + focusedBorder: border(borderColor), + disabledBorder: border(borderColor), + errorBorder: border(borderColor), + focusedErrorBorder: border(borderColor), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 16, ), ), + initialValue: value, + onChanged: onChanged, + hint: hint != null ? Text(hint!) : null, items: List>.generate(items.length, ( int index, From 0e3fd8e6d589942ad99550f5dc16153ae8e4bd3d Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Fri, 26 Dec 2025 14:47:08 +0100 Subject: [PATCH 3/4] sign up feature second version done --- .../mobile_app/lib/navigation/app_router.dart | 2 +- .../data/models/sign_up_request_model.dart | 10 +- .../models/sign_up_request_model.freezed.dart | 39 +-- .../data/models/sign_up_request_model.g.dart | 2 + .../device_sign_up/device_signup_screen.dart | 2 +- .../presentation/account_created_screen.dart | 92 ------ .../screens/account_created_screen.dart | 92 ++++++ .../screens/secret_code_screen.dart | 281 ++++++++++++++++++ .../{ => screens}/sign_up_address_screen.dart | 79 +++-- .../sign_up_password_screen.dart | 0 .../sign_up_personal_screen.dart | 29 +- .../sign_up/presentation/sign_up_screen.dart | 86 +----- .../sign_up/presentation/sign_up_steps.dart | 166 +++++++---- .../state/address_view_state.dart | 2 +- .../state/address_view_state.freezed.dart | 2 +- .../state/sign_up_view_model.dart | 65 ++-- .../state/sign_up_view_state.dart | 4 +- .../state/sign_up_view_state.freezed.dart | 42 +-- .../src/dropdowns/country_prefix_picker.dart | 7 +- .../lib/src/inputs/textfields.dart | 3 + packages/sf_localizations/assets/l10n/de.json | 65 +++- packages/sf_localizations/assets/l10n/en.json | 65 +++- packages/sf_localizations/assets/l10n/es.json | 65 +++- packages/sf_localizations/assets/l10n/fr.json | 65 +++- packages/sf_localizations/assets/l10n/it.json | 65 +++- packages/sf_localizations/assets/l10n/pt.json | 65 +++- .../lib/src/generated/i18n.dart | 83 ++++++ 27 files changed, 1153 insertions(+), 325 deletions(-) delete mode 100644 modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/screens/account_created_screen.dart create mode 100644 modules/auth/lib/src/features/sign_up/presentation/screens/secret_code_screen.dart rename modules/auth/lib/src/features/sign_up/presentation/{ => screens}/sign_up_address_screen.dart (69%) rename modules/auth/lib/src/features/sign_up/presentation/{ => screens}/sign_up_password_screen.dart (100%) rename modules/auth/lib/src/features/sign_up/presentation/{ => screens}/sign_up_personal_screen.dart (81%) diff --git a/apps/mobile_app/lib/navigation/app_router.dart b/apps/mobile_app/lib/navigation/app_router.dart index e7ee4782..260e7568 100644 --- a/apps/mobile_app/lib/navigation/app_router.dart +++ b/apps/mobile_app/lib/navigation/app_router.dart @@ -15,7 +15,7 @@ late final GoRouter appRouter; void configureAppRouter() { appRouter = GoRouter( navigatorKey: rootNavigatorKey, - initialLocation: AppRoutes.login, + initialLocation: AppRoutes.onboarding, debugLogDiagnostics: true, routes: [ GoRoute( diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.dart index e89ff0d2..898dde0f 100644 --- a/modules/auth/lib/src/core/data/models/sign_up_request_model.dart +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.dart @@ -20,14 +20,18 @@ abstract class SignUpRequestModel with _$SignUpRequestModel { required String userId, required String placeOfBirth, required String birthCountry, - + // ignore: invalid_annotation_target @JsonKey(name: 'dni') required String documentNumber, - + required String documentType, required String relationship, }) = _SignUpRequestModel; factory SignUpRequestModel.fromJson(Map json) => _$SignUpRequestModelFromJson(json); + + @override + @JsonKey(name: 'dni') + String get documentNumber; } extension SignUpRequestModelMapper on SignUpRequestEntity { @@ -47,7 +51,7 @@ extension SignUpRequestModelMapper on SignUpRequestEntity { placeOfBirth: placeOfBirth, birthCountry: birthCountry, documentNumber: documentNumber, - + documentType: documentType, relationship: relationship, ); } diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart index 74e87167..0bbf6d41 100644 --- a/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SignUpRequestModel { - String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;@JsonKey(name: 'dni') String get documentNumber; String get relationship; + String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;@JsonKey(name: 'dni') String get documentNumber; String get documentType; String get relationship; /// Create a copy of SignUpRequestModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $SignUpRequestModelCopyWith get copyWith => _$SignUpRequestM @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.relationship, relationship) || other.relationship == relationship)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,relationship); +int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,documentType,relationship); @override String toString() { - return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, relationship: $relationship)'; + return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, documentType: $documentType, relationship: $relationship)'; } @@ -48,7 +48,7 @@ abstract mixin class $SignUpRequestModelCopyWith<$Res> { factory $SignUpRequestModelCopyWith(SignUpRequestModel value, $Res Function(SignUpRequestModel) _then) = _$SignUpRequestModelCopyWithImpl; @useResult $Res call({ - String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String relationship + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String documentType, String relationship }); @@ -65,7 +65,7 @@ class _$SignUpRequestModelCopyWithImpl<$Res> /// Create a copy of SignUpRequestModel /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? relationship = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? documentType = null,Object? relationship = null,}) { return _then(_self.copyWith( firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable @@ -80,6 +80,7 @@ as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_t as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable as String, )); @@ -166,10 +167,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SignUpRequestModel() when $default != null: -return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: return orElse(); } @@ -187,10 +188,10 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship) $default,) {final _that = this; switch (_that) { case _SignUpRequestModel(): -return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: throw StateError('Unexpected subclass'); } @@ -207,10 +208,10 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String relationship)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship)? $default,) {final _that = this; switch (_that) { case _SignUpRequestModel() when $default != null: -return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.relationship);case _: +return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: return null; } @@ -222,7 +223,7 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan @JsonSerializable() class _SignUpRequestModel implements SignUpRequestModel { - const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'dni') required this.documentNumber, required this.relationship}): _taxResidences = taxResidences,_addresses = addresses; + const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'dni') required this.documentNumber, required this.documentType, required this.relationship}): _taxResidences = taxResidences,_addresses = addresses; factory _SignUpRequestModel.fromJson(Map json) => _$SignUpRequestModelFromJson(json); @override final String firstName; @@ -250,6 +251,7 @@ class _SignUpRequestModel implements SignUpRequestModel { @override final String placeOfBirth; @override final String birthCountry; @override@JsonKey(name: 'dni') final String documentNumber; +@override final String documentType; @override final String relationship; /// Create a copy of SignUpRequestModel @@ -265,16 +267,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.relationship, relationship) || other.relationship == relationship)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,relationship); +int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,documentType,relationship); @override String toString() { - return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, relationship: $relationship)'; + return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, documentType: $documentType, relationship: $relationship)'; } @@ -285,7 +287,7 @@ abstract mixin class _$SignUpRequestModelCopyWith<$Res> implements $SignUpReques factory _$SignUpRequestModelCopyWith(_SignUpRequestModel value, $Res Function(_SignUpRequestModel) _then) = __$SignUpRequestModelCopyWithImpl; @override @useResult $Res call({ - String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String relationship + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String documentType, String relationship }); @@ -302,7 +304,7 @@ class __$SignUpRequestModelCopyWithImpl<$Res> /// Create a copy of SignUpRequestModel /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? relationship = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? documentType = null,Object? relationship = null,}) { return _then(_SignUpRequestModel( firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable @@ -317,6 +319,7 @@ as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_t as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable +as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable as String, )); diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart index e89fba61..fb8353b1 100644 --- a/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart @@ -25,6 +25,7 @@ _SignUpRequestModel _$SignUpRequestModelFromJson(Map json) => placeOfBirth: json['placeOfBirth'] as String, birthCountry: json['birthCountry'] as String, documentNumber: json['dni'] as String, + documentType: json['documentType'] as String, relationship: json['relationship'] as String, ); @@ -43,5 +44,6 @@ Map _$SignUpRequestModelToJson(_SignUpRequestModel instance) => 'placeOfBirth': instance.placeOfBirth, 'birthCountry': instance.birthCountry, 'dni': instance.documentNumber, + 'documentType': instance.documentType, 'relationship': instance.relationship, }; diff --git a/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart index 6a954195..bc12c18e 100644 --- a/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart +++ b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart @@ -2,7 +2,7 @@ import 'package:auth/auth.dart'; import 'package:auth/src/features/device_sign_up/add_kid_screen.dart'; import 'package:auth/src/features/device_sign_up/link_watch/link_watch_screen.dart'; import 'package:auth/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart'; -import 'package:auth/src/features/sign_up/presentation/account_created_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/account_created_screen.dart'; import 'package:auth/src/widgets/layouts/form_step_layout.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; diff --git a/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart deleted file mode 100644 index c8a96e9f..00000000 --- a/modules/auth/lib/src/features/sign_up/presentation/account_created_screen.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:navigation/navigation.dart'; - -class AccountCreatedScreen extends ConsumerWidget { - final NavigationContract navigationContract; - - const AccountCreatedScreen({super.key, required this.navigationContract}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = ref.watch(themePortProvider); - - final state = ref.watch(signUpViewModelProvider); - - final String email = state.email; - final String fullName = '${state.firstName} ${state.lastName}'.trim(); - - return PopScope( - canPop: false, - child: Scaffold( - backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: Container( - margin: const EdgeInsets.all(30), - child: Center( - child: Column( - children: [ - const Spacer(flex: 10), - Icon( - Icons.check, - color: theme.getColorFor(ThemeCode.buttonPrimary), - size: 50, - ), - const SizedBox(height: 20), - const Text( - "Cuenta creada", - style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 20), - - Text.rich( - textAlign: TextAlign.center, - TextSpan( - text: "Has creado la cuenta para:\n", - children: [ - TextSpan( - text: fullName, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - - const SizedBox(height: 16), - Text.rich( - textAlign: TextAlign.center, - TextSpan( - text: "Hemos enviado un email de verificación a:\n", - children: [ - TextSpan( - text: email, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - - const SizedBox(height: 20), - const Text( - "Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj", - textAlign: TextAlign.center, - ), - - const SizedBox(height: 20), - PrimaryButton( - onPressed: () { - navigationContract.goTo(AppRoutes.login); - }, - text: "Continuar", - color: theme.getColorFor(ThemeCode.buttonPrimary), - ), - const Spacer(flex: 8), - ], - ), - ), - ), - ), - ); - } -} diff --git a/modules/auth/lib/src/features/sign_up/presentation/screens/account_created_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/screens/account_created_screen.dart new file mode 100644 index 00000000..041546ab --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/screens/account_created_screen.dart @@ -0,0 +1,92 @@ +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; +import 'package:sf_localizations/sf_localizations.dart'; + +class AccountCreatedScreen extends ConsumerWidget { + final NavigationContract navigationContract; + + const AccountCreatedScreen({super.key, required this.navigationContract}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + final state = ref.watch(signUpViewModelProvider); + + final String email = state.email; + final String fullName = '${state.firstName} ${state.lastName}'.trim(); + + return Scaffold( + backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), + body: Container( + margin: const EdgeInsets.all(30), + child: Center( + child: Column( + children: [ + const Spacer(flex: 10), + Icon( + Icons.check, + color: theme.getColorFor(ThemeCode.buttonPrimary), + size: 50, + ), + const SizedBox(height: 20), + Text( + context.translate(I18n.accountCreatedTitle), + style: const TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 20), + + Text.rich( + textAlign: TextAlign.center, + TextSpan( + text: '${context.translate(I18n.accountCreatedForLabel)}\n', + children: [ + TextSpan( + text: fullName, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + ), + + const SizedBox(height: 16), + Text.rich( + textAlign: TextAlign.center, + TextSpan( + text: + '${context.translate(I18n.accountCreatedEmailVerificationSentLabel)}\n', + children: [ + TextSpan( + text: email, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + ), + + const SizedBox(height: 20), + Text( + context.translate(I18n.accountCreatedChildSetupHint), + textAlign: TextAlign.center, + ), + + const SizedBox(height: 20), + PrimaryButton( + onPressed: () => navigationContract.goTo(AppRoutes.login), + text: context.translate(I18n.accountCreatedContinue), + color: theme.getColorFor(ThemeCode.buttonPrimary), + ), + const Spacer(flex: 8), + ], + ), + ), + ), + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/screens/secret_code_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/screens/secret_code_screen.dart new file mode 100644 index 00000000..bef23b5d --- /dev/null +++ b/modules/auth/lib/src/features/sign_up/presentation/screens/secret_code_screen.dart @@ -0,0 +1,281 @@ +import 'dart:convert'; + +import 'package:auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; +import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; +import 'package:sf_localizations/sf_localizations.dart'; + +class SecretCodeScreen extends ConsumerWidget { + final NavigationContract navigationContract; + + const SecretCodeScreen({super.key, required this.navigationContract}); + + Uint8List? _qrBytes(String? dataUri) { + final value = (dataUri ?? '').trim(); + if (value.isEmpty) return null; + + final idx = value.indexOf('base64,'); + final b64 = idx == -1 ? value : value.substring(idx + 7); + + try { + return base64Decode(b64); + } catch (_) { + return null; + } + } + + Future _openTwoFactorSignUpBottomSheet( + BuildContext context, + WidgetRef ref, + ) async { + final token = ref.read(signUpViewModelProvider).token; + if (token.trim().isEmpty) return false; + + return showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + isDismissible: false, + enableDrag: false, + backgroundColor: Colors.transparent, + builder: (context) { + return Consumer( + builder: (context, ref, _) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(signUpViewModelProvider.notifier); + + final otpErrorKey = ref.watch( + signUpViewModelProvider.select((s) => s.otpError), + ); + final isOtpLoading = ref.watch( + signUpViewModelProvider.select((s) => s.isOtpLoading), + ); + final otpCode = ref.watch( + signUpViewModelProvider.select((s) => s.otpCode), + ); + + final otpErrorText = otpErrorKey.isEmpty + ? '' + : context.translate(otpErrorKey); + + Future onVerify() async { + FocusManager.instance.primaryFocus?.unfocus(); + + final ok = await vm.verifyTwoFACodeSignUp(token: token); + if (!context.mounted) return; + + if (ok) Navigator.of(context).pop(true); + } + + return TwoFactorBottomSheetView( + theme: theme, + title: context.translate(I18n.twoFactorTitle), + subtitle: context.translate(I18n.twoFactorSubtitle), + verifyText: context.translate(I18n.twoFactorVerify), + closeText: context.translate(I18n.close), + isOtpLoading: isOtpLoading, + otpCode: otpCode, + otpErrorText: otpErrorText, + onChanged: vm.setOtpCode, + onVerify: onVerify, + onClose: () => Navigator.of(context).pop(false), + ); + }, + ); + }, + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + final vm = ref.read(signUpViewModelProvider.notifier); + final state = ref.watch(signUpViewModelProvider); + + final secret = state.twoFASecret?.item.secret.trim() ?? ''; + final qrDataUri = state.twoFASecret?.item.qr; + final qrBytes = _qrBytes(qrDataUri); + + return Scaffold( + backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), + body: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(24, 8, 24, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.translate(I18n.secretCodeTitle), + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: theme.getColorFor(ThemeCode.textPrimary), + ), + ), + const SizedBox(height: 20), + + _StepBlock( + theme: theme, + number: '1.', + title: context.translate(I18n.secretCodeStep1Title), + body: context.translate(I18n.secretCodeStep1Body), + ), + const SizedBox(height: 14), + + _StepBlock( + theme: theme, + number: '2.', + title: context.translate(I18n.secretCodeStep2Title), + body: context.translate(I18n.secretCodeStep2Body), + ), + const SizedBox(height: 12), + + Center( + child: Container( + width: 150, + height: 150, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(width: 1, color: Colors.black), + color: theme.getColorFor(ThemeCode.backgroundSecondary), + ), + child: qrBytes == null + ? const Center(child: Icon(Icons.qr_code_2, size: 60)) + : ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.memory(qrBytes, fit: BoxFit.cover), + ), + ), + ), + const SizedBox(height: 12), + + Center( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 10, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: theme.getColorFor(ThemeCode.backgroundSecondary), + border: Border.all(width: 1, color: Colors.black), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + secret, + style: TextStyle( + fontWeight: FontWeight.w700, + letterSpacing: 1.1, + color: theme.getColorFor(ThemeCode.textPrimary), + ), + ), + const SizedBox(width: 10), + IconButton( + visualDensity: VisualDensity.compact, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + icon: const Icon(Icons.copy, size: 18), + onPressed: secret.isEmpty + ? null + : () async { + await Clipboard.setData( + ClipboardData(text: secret), + ); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + context.translate( + I18n.secretCodeKeyCopied, + ), + ), + ), + ); + }, + ), + ], + ), + ), + ), + + const SizedBox(height: 18), + _StepBlock( + theme: theme, + number: '3.', + title: context.translate(I18n.secretCodeStep3Title), + body: context.translate(I18n.secretCodeStep3Body), + ), + + const SizedBox(height: 26), + PrimaryButton( + onPressed: () async { + final verified = await _openTwoFactorSignUpBottomSheet( + context, + ref, + ); + if (!context.mounted) return; + + if (verified == true) { + vm.showAccountCreated(); + } + }, + text: context.translate(I18n.secretCodeConfigure), + color: theme.getColorFor(ThemeCode.buttonPrimary), + ), + ], + ), + ), + ), + ); + } +} + +class _StepBlock extends StatelessWidget { + final ThemePort theme; + final String number; + final String title; + final String body; + + const _StepBlock({ + required this.theme, + required this.number, + required this.title, + required this.body, + }); + + @override + Widget build(BuildContext context) { + return RichText( + text: TextSpan( + style: TextStyle( + height: 1.35, + fontSize: 14, + color: theme.getColorFor(ThemeCode.textPrimary), + ), + children: [ + TextSpan( + text: '$number ', + style: const TextStyle(fontWeight: FontWeight.w700), + ), + TextSpan( + text: '$title\n', + style: const TextStyle(fontWeight: FontWeight.w700), + ), + TextSpan( + text: body, + style: TextStyle( + fontWeight: FontWeight.w400, + color: theme.getColorFor(ThemeCode.textSecondary), + ), + ), + ], + ), + ); + } +} diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_address_screen.dart similarity index 69% rename from modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart rename to modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_address_screen.dart index aecc2632..d3f5df5c 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/sign_up_address_screen.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_address_screen.dart @@ -1,12 +1,14 @@ +import 'package:country_code_picker/country_code_picker.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; +import 'package:sf_localizations/sf_localizations.dart'; class SignupAddressScreen extends StatelessWidget { const SignupAddressScreen({ super.key, required this.bornAtController, - + required this.onPickBornAt, required this.relationshipSelected, required this.onRelationshipChanged, required this.relationshipOptions, @@ -23,8 +25,7 @@ class SignupAddressScreen extends StatelessWidget { required this.addressCountrySelected, required this.onAddressCountryChanged, - required this.addressCountryOptions, - required this.addressCountryHint, + required this.addressCountryController, required this.addressCountryLabel, required this.postCodeController, @@ -37,6 +38,7 @@ class SignupAddressScreen extends StatelessWidget { required this.birthCountryLabel, required this.birthCountryHint, + required this.onBirthCountryChanged, required this.streetLabel, required this.streetHint, @@ -55,6 +57,7 @@ class SignupAddressScreen extends StatelessWidget { }); final TextEditingController bornAtController; + final VoidCallback onPickBornAt; final String? relationshipSelected; final ValueChanged onRelationshipChanged; @@ -71,10 +74,9 @@ class SignupAddressScreen extends StatelessWidget { final TextEditingController stateController; final String? addressCountrySelected; - final ValueChanged onAddressCountryChanged; - final List addressCountryOptions; - final String addressCountryHint; + final ValueChanged onAddressCountryChanged; final String addressCountryLabel; + final TextEditingController addressCountryController; final TextEditingController postCodeController; @@ -83,6 +85,7 @@ class SignupAddressScreen extends StatelessWidget { final String placeOfBirthLabel; final String placeOfBirthHint; + final ValueChanged onBirthCountryChanged; final String birthCountryLabel; final String birthCountryHint; @@ -106,12 +109,19 @@ class SignupAddressScreen extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - CustomTextField( - label: birthDateLabel, - hint: birthDateHint, - keyboardType: TextInputType.number, - controller: bornAtController, + GestureDetector( + onTap: onPickBornAt, + child: AbsorbPointer( + child: CustomTextField( + label: birthDateLabel, + hint: birthDateHint, + controller: bornAtController, + readOnly: true, + keyboardType: TextInputType.none, + ), + ), ), + const SizedBox(height: 8), Align( @@ -133,11 +143,26 @@ class SignupAddressScreen extends StatelessWidget { controller: placeOfBirthController, ), const SizedBox(height: 8), - - CustomTextField( - label: birthCountryLabel, - hint: birthCountryHint, - controller: birthCountryController, + Align( + alignment: Alignment.bottomLeft, + child: Text(birthCountryLabel, style: const TextStyle(fontSize: 14)), + ), + const SizedBox(height: 8), + Row( + spacing: 10, + children: [ + CountryPrefixPicker( + headerText: context.translate(I18n.selectYourCountry), + onChanged: onBirthCountryChanged, + ), + Expanded( + child: CustomTextField( + readOnly: true, + controller: birthCountryController, + hint: context.translate(I18n.birthCountryLabel), + ), + ), + ], ), const SizedBox(height: 8), @@ -176,12 +201,22 @@ class SignupAddressScreen extends StatelessWidget { style: const TextStyle(fontSize: 14, letterSpacing: 0), ), ), - CustomDropdown( - items: addressCountryOptions.map(Text.new).toList(growable: false), - values: addressCountryOptions, - value: addressCountrySelected, - hint: addressCountryHint, - onChanged: (v) => onAddressCountryChanged(v as String?), + const SizedBox(height: 8), + Row( + spacing: 10, + children: [ + CountryPrefixPicker( + headerText: context.translate(I18n.selectYourCountry), + onChanged: onAddressCountryChanged, + ), + Expanded( + child: CustomTextField( + readOnly: true, + controller: addressCountryController, + hint: context.translate(I18n.addressCountryHint), + ), + ), + ], ), const SizedBox(height: 8), diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_password_screen.dart similarity index 100% rename from modules/auth/lib/src/features/sign_up/presentation/sign_up_password_screen.dart rename to modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_password_screen.dart diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_personal_screen.dart similarity index 81% rename from modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart rename to modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_personal_screen.dart index f5adb8cb..1ab23f79 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/sign_up_personal_screen.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/screens/sign_up_personal_screen.dart @@ -1,5 +1,7 @@ +import 'package:country_code_picker/country_code_picker.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; +import 'package:sf_localizations/sf_localizations.dart'; class SignupPersonalScreen extends StatelessWidget { final TextEditingController firstNameTextFieldController; @@ -25,6 +27,7 @@ class SignupPersonalScreen extends StatelessWidget { final String phoneLabel; final String phoneHint; + final ValueChanged onDialCodeChanged; final String emailLabel; final String emailHint; @@ -53,6 +56,7 @@ class SignupPersonalScreen extends StatelessWidget { required this.documentNumberHint, required this.phoneLabel, required this.phoneHint, + required this.onDialCodeChanged, required this.emailLabel, required this.emailHint, required this.acceptTerms, @@ -102,11 +106,26 @@ class SignupPersonalScreen extends StatelessWidget { ], ), const SizedBox(height: 8), - CustomTextField( - label: phoneLabel, - hint: phoneHint, - keyboardType: TextInputType.number, - controller: phoneTextFieldController, + Align( + alignment: Alignment.bottomLeft, + child: Text(phoneLabel, style: const TextStyle(fontSize: 14)), + ), + const SizedBox(height: 8), + Row( + spacing: 10, + children: [ + CountryPrefixPicker( + headerText: context.translate(I18n.selectYourCountry), + onChanged: onDialCodeChanged, + ), + Expanded( + child: CustomTextField( + controller: phoneTextFieldController, + hint: context.translate(I18n.phoneNumber), + keyboardType: TextInputType.number, + ), + ), + ], ), const SizedBox(height: 8), CustomTextField( diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart index 2b230d6c..f8256afc 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_screen.dart @@ -1,13 +1,12 @@ -import 'package:auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; -import 'package:auth/src/features/sign_up/presentation/account_created_screen.dart'; import 'package:auth/src/features/sign_up/presentation/sign_up_steps.dart'; import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/account_created_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/secret_code_screen.dart'; import 'package:auth/src/widgets/layouts/sign_up_layout.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:navigation/navigation.dart'; -import 'package:sf_localizations/sf_localizations.dart'; class SignupScreen extends ConsumerWidget { final NavigationContract navigationContract; @@ -20,7 +19,7 @@ class SignupScreen extends ConsumerWidget { final vm = ref.read(signUpViewModelProvider.notifier); final state = ref.read(signUpViewModelProvider); - final steps = signUpSteps(); + final steps = signUpSteps(context); final isLastStep = state.currentIndex >= steps.length - 1; if (!isLastStep) { @@ -28,79 +27,10 @@ class SignupScreen extends ConsumerWidget { return; } - final isSignUp = await vm.signUp(); + final ok = await vm.signUp(); if (!context.mounted) return; - if (!isSignUp) return; - - final verified = await _openTwoFactorSignUpBottomSheet(context, ref); - if (!context.mounted) return; - - if (verified == true) { - vm.showAccountCreated(); - } - } - - Future _openTwoFactorSignUpBottomSheet( - BuildContext context, - WidgetRef ref, - ) async { - final token = ref.read(signUpViewModelProvider).token; - if (token.trim().isEmpty) return false; - - return showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - isDismissible: false, - enableDrag: false, - backgroundColor: Colors.transparent, - builder: (context) { - return Consumer( - builder: (context, ref, _) { - final theme = ref.watch(themePortProvider); - final vm = ref.read(signUpViewModelProvider.notifier); - - final otpErrorKey = ref.watch( - signUpViewModelProvider.select((s) => s.otpError), - ); - final isOtpLoading = ref.watch( - signUpViewModelProvider.select((s) => s.isOtpLoading), - ); - final otpCode = ref.watch( - signUpViewModelProvider.select((s) => s.otpCode), - ); - - final otpErrorText = otpErrorKey.isEmpty - ? '' - : context.translate(otpErrorKey); - - Future onVerify() async { - FocusManager.instance.primaryFocus?.unfocus(); - - final ok = await vm.verifyTwoFACodeSignUp(token: token); - if (!context.mounted) return; - - if (ok) Navigator.of(context).pop(true); - } - - return TwoFactorBottomSheetView( - theme: theme, - title: context.translate(I18n.twoFactorTitle), - subtitle: context.translate(I18n.twoFactorSubtitle), - verifyText: context.translate(I18n.twoFactorVerify), - closeText: context.translate(I18n.close), - isOtpLoading: isOtpLoading, - otpCode: otpCode, - otpErrorText: otpErrorText, - onChanged: vm.setOtpCode, - onVerify: onVerify, - onClose: () => Navigator.of(context).pop(false), - ); - }, - ); - }, - ); + if (!ok) return; } @override @@ -109,10 +39,14 @@ class SignupScreen extends ConsumerWidget { final vm = ref.read(signUpViewModelProvider.notifier); final state = ref.watch(signUpViewModelProvider); - final steps = signUpSteps(); + final steps = signUpSteps(context); final index = state.currentIndex.clamp(0, steps.length - 1); final step = steps[index]; + if (state.showSecretCode) { + return SecretCodeScreen(navigationContract: navigationContract); + } + if (state.showAccountCreated) { return AccountCreatedScreen(navigationContract: navigationContract); } diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart index 9fd432c5..74d60d9d 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart @@ -1,68 +1,114 @@ import 'package:auth/src/features/sign_up/models/sign_up_step_config.dart'; -import 'package:auth/src/features/sign_up/presentation/sign_up_address_screen.dart'; -import 'package:auth/src/features/sign_up/presentation/sign_up_password_screen.dart'; -import 'package:auth/src/features/sign_up/presentation/sign_up_personal_screen.dart'; import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/sign_up_address_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/sign_up_password_screen.dart'; +import 'package:auth/src/features/sign_up/presentation/screens/sign_up_personal_screen.dart'; +import 'package:country_code_picker/country_code_picker.dart'; import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:sf_localizations/sf_localizations.dart'; -List signUpSteps() => [ +const Map documentType = { + 'dni': I18n.documentTypeDni, + 'nie': I18n.documentTypeNie, + 'passport': I18n.documentTypePassport, +}; +const Map relationship = { + 'father': I18n.relationshipFather, + 'mother': I18n.relationshipMother, + 'tutor': I18n.relationshipTutor, +}; + +List signUpSteps(BuildContext context) => [ SignUpStepConfig( - supertitle: 'Usuario y contacto', - title: 'Crea tu usuario', - subtitle: 'Con tu email y tu número podremos mantenerte siempre informado', + supertitle: context.translate(I18n.stepUserContactSupertitle), + title: context.translate(I18n.stepUserContactTitle), + subtitle: context.translate(I18n.stepUserContactSubtitle), bodyBuilder: (context, ref) { final theme = ref.watch(themePortProvider); final vm = ref.read(signUpViewModelProvider.notifier); final state = ref.watch(signUpViewModelProvider); + final documentLabel = { + for (final e in documentType.entries) e.key: context.translate(e.value), + }; + final documentKeys = { + for (final e in documentLabel.entries) e.value: e.key, + }; + + final String? documentTypeSelected = state.documentType.trim().isEmpty + ? null + : documentLabel[state.documentType]; return SignupPersonalScreen( firstNameTextFieldController: vm.firstNameController, lastNameTextFieldController: vm.lastNameController, documentNumberTextFieldController: vm.documentNumberController, phoneTextFieldController: vm.phoneController, emailTextFieldController: vm.emailController, - documentTypeSelected: state.documentType.trim().isEmpty - ? null - : state.documentType, - onDocumentTypeChanged: vm.setDocumentType, + + documentTypeSelected: documentTypeSelected, + + onDocumentTypeChanged: (label) { + final documentKey = documentKeys[label ?? ''] ?? ''; + vm.setDocumentType(documentKey); + }, + acceptTerms: state.acceptTerms, onAcceptTermsPressed: (v) => vm.setAcceptTerms(v ?? false), - termsText: 'Acepto los términos y condiciones', + termsText: context.translate(I18n.termsText), theme: theme, - firstNameLabel: 'Nombre', - firstNameHint: 'Nombre', - lastNameLabel: 'Apellido', - lastNameHint: 'Apellido', - documentTypeOptions: const ['DNI', 'NIE', 'Pasaporte'], - documentTypeHint: 'Documento', + + firstNameLabel: context.translate(I18n.firstNameLabel), + firstNameHint: context.translate(I18n.firstNameHint), + lastNameLabel: context.translate(I18n.lastNameLabel), + lastNameHint: context.translate(I18n.lastNameHint), + + documentTypeOptions: documentLabel.values.toList(), + documentTypeHint: context.translate(I18n.documentTypeHint), + documentNumberLabel: '', - documentNumberHint: 'DNI/NIE/Pasaporte', - phoneLabel: 'Teléfono móvil', - phoneHint: 'Teléfono móvil', - emailLabel: 'Correo electrónico', - emailHint: 'Correo electrónico', + documentNumberHint: context.translate(I18n.documentNumberHint), + phoneLabel: context.translate(I18n.phoneLabel), + phoneHint: context.translate(I18n.phoneHint), + emailLabel: context.translate(I18n.emailLabel), + emailHint: context.translate(I18n.emailHint), + + onDialCodeChanged: (CountryCode value) { + vm.updateDialCode(value.dialCode ?? state.dialCode); + }, ); }, ), SignUpStepConfig( - supertitle: 'Datos personales', - title: 'Identifícate', - subtitle: - 'Nos aseguraremos de que la cuenta esté a nombre del adulto responsable', + supertitle: context.translate(I18n.stepPersonalDataSupertitle), + title: context.translate(I18n.stepPersonalDataTitle), + subtitle: context.translate(I18n.stepPersonalDataSubtitle), bodyBuilder: (context, ref) { final vm = ref.read(signUpViewModelProvider.notifier); final state = ref.watch(signUpViewModelProvider); + final relationshipLabel = { + for (final e in relationship.entries) e.key: context.translate(e.value), + }; + final relationshipKeys = { + for (final e in relationshipLabel.entries) e.value: e.key, + }; + final String? relationshipSelected = state.relationship.trim().isEmpty + ? null + : relationshipLabel[state.relationship]; + return SignupAddressScreen( bornAtController: vm.bornAtController, + onPickBornAt: () => vm.pickBornAt(context), - relationshipSelected: state.relationship.trim().isEmpty - ? null - : state.relationship, - onRelationshipChanged: vm.setRelationship, - relationshipOptions: const ['father', 'mother', 'tutor'], - relationshipHint: 'Selecciona una opción', - relationshipLabel: '¿Qué familiar eres?', + relationshipSelected: relationshipSelected, + onRelationshipChanged: (label) { + final key = relationshipKeys[label ?? ''] ?? ''; + vm.setRelationship(key); + }, + relationshipOptions: relationshipLabel.values.toList(), + relationshipHint: context.translate(I18n.relationshipHint), + relationshipLabel: context.translate(I18n.relationshipLabel), placeOfBirthController: vm.placeOfBirthController, birthCountryController: vm.birthCountryController, @@ -75,45 +121,47 @@ List signUpSteps() => [ addressCountrySelected: state.address.country.trim().isEmpty ? null : state.address.country, - onAddressCountryChanged: vm.setAddressCountry, - addressCountryOptions: const ['Spain', 'France', 'Portugal'], - addressCountryHint: 'País', - addressCountryLabel: 'País (dirección)', + onAddressCountryChanged: (CountryCode value) { + vm.setAddressCountry(name: value.name ?? ''); + }, + addressCountryLabel: context.translate(I18n.addressCountryLabel), + addressCountryController: vm.addressCountryController, postCodeController: vm.addressPostCodeController, - birthDateLabel: 'Fecha de nacimiento', - birthDateHint: 'DD/MM/AAAA', + birthDateLabel: context.translate(I18n.birthDateLabel), + birthDateHint: context.translate(I18n.birthDateHint), - placeOfBirthLabel: 'Lugar de nacimiento', - placeOfBirthHint: 'Ciudad de nacimiento', + placeOfBirthLabel: context.translate(I18n.placeOfBirthLabel), + placeOfBirthHint: context.translate(I18n.placeOfBirthHint), - birthCountryLabel: 'País de nacimiento', - birthCountryHint: 'País de nacimiento', + birthCountryLabel: context.translate(I18n.birthCountryLabel), + birthCountryHint: context.translate(I18n.birthCountryHint), + onBirthCountryChanged: (CountryCode value) { + vm.setBirthCountryFromPicker(name: value.name ?? ''); + }, - streetLabel: 'Calle / Dirección', - streetHint: 'Calle Gran Vía 30 6º', + streetLabel: context.translate(I18n.streetLabel), + streetHint: context.translate(I18n.streetHint), - cityLabel: 'Ciudad', - cityHint: 'Ciudad', + cityLabel: context.translate(I18n.cityLabel), + cityHint: context.translate(I18n.cityHint), - provinceLabel: 'Provincia', - provinceHint: 'Provincia', + provinceLabel: context.translate(I18n.provinceLabel), + provinceHint: context.translate(I18n.provinceHint), - stateLabel: 'Comunidad / Estado', - stateHint: 'Comunidad / Estado', + stateLabel: context.translate(I18n.stateLabel), + stateHint: context.translate(I18n.stateHint), - postCodeLabel: 'Código postal', - postCodeHint: '28013', + postCodeLabel: context.translate(I18n.postCodeLabel), + postCodeHint: context.translate(I18n.postCodeHint), ); }, ), - SignUpStepConfig( - supertitle: 'Domicilio', - title: 'Tu dirección', - subtitle: - 'Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial', + supertitle: context.translate(I18n.stepAddressSupertitle), + title: context.translate(I18n.stepAddressTitle), + subtitle: context.translate(I18n.passwordRulesSubtitle), bodyBuilder: (context, ref) { final vm = ref.read(signUpViewModelProvider.notifier); final state = ref.watch(signUpViewModelProvider); diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart index 489e02ee..cd197339 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.dart @@ -9,7 +9,7 @@ abstract class AddressViewState with _$AddressViewState { @Default('') String city, @Default('') String province, @Default('') String state, - @Default('') String country, + @Default('España') String country, int? postCode, }) = _AddressViewState; } diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart index 27889783..a600ce08 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/address_view_state.freezed.dart @@ -211,7 +211,7 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country class _AddressViewState implements AddressViewState { - const _AddressViewState({this.street = '', this.city = '', this.province = '', this.state = '', this.country = '', this.postCode}); + const _AddressViewState({this.street = '', this.city = '', this.province = '', this.state = '', this.country = 'España', this.postCode}); @override@JsonKey() final String street; diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart index c2dee9af..4722e0f3 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart @@ -158,6 +158,36 @@ class SignUpViewModel extends Notifier { ); } + void updateDialCode(String dialCode) { + state = state.copyWith(dialCode: dialCode); + } + + void setBirthCountryFromPicker({required String name}) { + birthCountryController.text = name; + state = state.copyWith(birthCountry: name); + } + + Future pickBornAt(BuildContext context) async { + FocusManager.instance.primaryFocus?.unfocus(); + + final now = DateTime.now(); + final initial = state.bornAt ?? DateTime(now.year - 18, now.month, now.day); + + final safeInitial = initial.isAfter(now) ? now : initial; + + final picked = await showDatePicker( + context: context, + initialDate: safeInitial, + firstDate: DateTime(1900, 1, 1), + lastDate: now, + ); + + if (!ref.mounted) return; + if (picked == null) return; + + setBornAt(picked); + } + void setDocumentType(String? value) { final v = value ?? ''; if (documentTypeController.text == v) return; @@ -170,10 +200,9 @@ class SignUpViewModel extends Notifier { relationshipController.text = v; } - void setAddressCountry(String? value) { - final v = value ?? ''; - if (addressCountryController.text == v) return; - addressCountryController.text = v; + void setAddressCountry({required String name}) { + addressCountryController.text = name; + state = state.copyWith(address: state.address.copyWith(country: name)); } void setAcceptTerms(bool value) { @@ -219,7 +248,7 @@ class SignUpViewModel extends Notifier { final text = phoneController.text; if (text == state.phone) return; - state = state.copyWith(phone: text, errorMessage: ''); + state = state.copyWith(phone: text); if (state.showErrors) { state = state.copyWith(phoneError: _phoneErrorFor(text)); @@ -420,18 +449,16 @@ class SignUpViewModel extends Notifier { } else { state = state.copyWith(errorMessage: ''); } - - if (state.relationship.trim().isEmpty) { - state = state.copyWith(errorMessage: 'La relación es obligatoria'); - return false; - } - if (state.bornAt == null) { state = state.copyWith( errorMessage: 'Selecciona una fecha de nacimiento válida (DD/MM/AAAA)', ); return false; } + if (state.relationship.trim().isEmpty) { + state = state.copyWith(errorMessage: 'La relación es obligatoria'); + return false; + } if (state.placeOfBirth.trim().isEmpty) { state = state.copyWith(errorMessage: 'Falta el lugar de nacimiento'); @@ -537,7 +564,7 @@ class SignUpViewModel extends Notifier { firstName: state.firstName.trim(), lastName: state.lastName.trim(), email: state.email.trim(), - phone: state.phone.trim(), + phone: state.dialCode.trim() + state.phone.trim(), language: state.language.trim().isEmpty ? 'es' : state.language.trim(), password: state.password, bornAt: bornAt.millisecondsSinceEpoch, @@ -569,7 +596,7 @@ class SignUpViewModel extends Notifier { _finishWithError(token: token, message: validationError); return false; } - + state = state.copyWith(showSecretCode: true); _finishWithSuccess(token: token, twoFASecret: secretEntity); return true; } catch (e) { @@ -584,6 +611,8 @@ class SignUpViewModel extends Notifier { isLoading: true, errorMessage: '', twoFASecret: null, + showSecretCode: false, + showAccountCreated: false, ); } @@ -658,7 +687,7 @@ class SignUpViewModel extends Notifier { } void showAccountCreated() { - state = state.copyWith(showAccountCreated: true, errorMessage: ''); + state = state.copyWith(showAccountCreated: true, showSecretCode: false); } DateTime? _tryParseDate(String value) { @@ -680,10 +709,10 @@ class SignUpViewModel extends Notifier { return date; } - String _formatDate(DateTime d) { - final dd = d.day.toString().padLeft(2, '0'); - final mm = d.month.toString().padLeft(2, '0'); - final yyyy = d.year.toString(); + String _formatDate(DateTime date) { + final dd = date.day.toString().padLeft(2, '0'); + final mm = date.month.toString().padLeft(2, '0'); + final yyyy = date.year.toString(); return '$dd/$mm/$yyyy'; } diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart index c5bcf911..94653d45 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart @@ -18,6 +18,7 @@ abstract class SignUpViewState with _$SignUpViewState { @Default('') String lastName, @Default('') String email, @Default('') String phone, + @Default('+34') String dialCode, @Default('') String language, DateTime? bornAt, @Default('') String password, @@ -25,7 +26,7 @@ abstract class SignUpViewState with _$SignUpViewState { @Default(false) bool isShowPassword, @Default('') String userId, @Default('') String placeOfBirth, - @Default('') String birthCountry, + @Default('España') String birthCountry, @Default(AddressViewState()) AddressViewState address, @@ -41,5 +42,6 @@ abstract class SignUpViewState with _$SignUpViewState { @Default('') String otpError, @Default(false) bool isOtpLoading, @Default(false) bool showAccountCreated, + @Default(false) bool showSecretCode, }) = _SignUpViewState; } diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart index 510070e5..04e3356a 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SignUpViewState { - int get currentIndex; String get documentNumber; String get documentType; bool get acceptTerms; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get language; DateTime? get bornAt; String get password; String get repeatPassword; bool get isShowPassword; String get userId; String get placeOfBirth; String get birthCountry; AddressViewState get address; String get emailError; String get passwordError; String get phoneError; String get errorMessage; bool get isLoading; bool get showErrors; String get token; TwoFASecretEntity? get twoFASecret; String get otpCode; String get otpError; bool get isOtpLoading; bool get showAccountCreated; + int get currentIndex; String get documentNumber; String get documentType; bool get acceptTerms; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get dialCode; String get language; DateTime? get bornAt; String get password; String get repeatPassword; bool get isShowPassword; String get userId; String get placeOfBirth; String get birthCountry; AddressViewState get address; String get emailError; String get passwordError; String get phoneError; String get errorMessage; bool get isLoading; bool get showErrors; String get token; TwoFASecretEntity? get twoFASecret; String get otpCode; String get otpError; bool get isOtpLoading; bool get showAccountCreated; bool get showSecretCode; /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $SignUpViewStateCopyWith get copyWith => _$SignUpViewStateCopyW @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); } @override -int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated]); +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); @override String toString() { - return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated)'; + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; } @@ -45,7 +45,7 @@ abstract mixin class $SignUpViewStateCopyWith<$Res> { factory $SignUpViewStateCopyWith(SignUpViewState value, $Res Function(SignUpViewState) _then) = _$SignUpViewStateCopyWithImpl; @useResult $Res call({ - int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode }); @@ -62,7 +62,7 @@ class _$SignUpViewStateCopyWithImpl<$Res> /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { return _then(_self.copyWith( currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable @@ -73,6 +73,7 @@ as String,firstName: null == firstName ? _self.firstName : firstName // ignore: as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable as DateTime?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable @@ -94,6 +95,7 @@ as TwoFASecretEntity?,otpCode: null == otpCode ? _self.otpCode : otpCode // igno as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable as bool,showAccountCreated: null == showAccountCreated ? _self.showAccountCreated : showAccountCreated // ignore: cast_nullable_to_non_nullable +as bool,showSecretCode: null == showSecretCode ? _self.showSecretCode : showSecretCode // ignore: cast_nullable_to_non_nullable as bool, )); } @@ -200,10 +202,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SignUpViewState() when $default != null: -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: return orElse(); } @@ -221,10 +223,10 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode) $default,) {final _that = this; switch (_that) { case _SignUpViewState(): -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: throw StateError('Unexpected subclass'); } @@ -241,10 +243,10 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,) {final _that = this; switch (_that) { case _SignUpViewState() when $default != null: -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: return null; } @@ -256,7 +258,7 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that class _SignUpViewState implements SignUpViewState { - const _SignUpViewState({this.currentIndex = 0, this.documentNumber = '', this.documentType = '', this.acceptTerms = false, this.relationship = '', this.firstName = '', this.lastName = '', this.email = '', this.phone = '', this.language = '', this.bornAt, this.password = '', this.repeatPassword = '', this.isShowPassword = false, this.userId = '', this.placeOfBirth = '', this.birthCountry = '', this.address = const AddressViewState(), this.emailError = '', this.passwordError = '', this.phoneError = '', this.errorMessage = '', this.isLoading = false, this.showErrors = false, this.token = '', this.twoFASecret, this.otpCode = '', this.otpError = '', this.isOtpLoading = false, this.showAccountCreated = false}); + const _SignUpViewState({this.currentIndex = 0, this.documentNumber = '', this.documentType = '', this.acceptTerms = false, this.relationship = '', this.firstName = '', this.lastName = '', this.email = '', this.phone = '', this.dialCode = '+34', this.language = '', this.bornAt, this.password = '', this.repeatPassword = '', this.isShowPassword = false, this.userId = '', this.placeOfBirth = '', this.birthCountry = 'España', this.address = const AddressViewState(), this.emailError = '', this.passwordError = '', this.phoneError = '', this.errorMessage = '', this.isLoading = false, this.showErrors = false, this.token = '', this.twoFASecret, this.otpCode = '', this.otpError = '', this.isOtpLoading = false, this.showAccountCreated = false, this.showSecretCode = false}); @override@JsonKey() final int currentIndex; @@ -268,6 +270,7 @@ class _SignUpViewState implements SignUpViewState { @override@JsonKey() final String lastName; @override@JsonKey() final String email; @override@JsonKey() final String phone; +@override@JsonKey() final String dialCode; @override@JsonKey() final String language; @override final DateTime? bornAt; @override@JsonKey() final String password; @@ -289,6 +292,7 @@ class _SignUpViewState implements SignUpViewState { @override@JsonKey() final String otpError; @override@JsonKey() final bool isOtpLoading; @override@JsonKey() final bool showAccountCreated; +@override@JsonKey() final bool showSecretCode; /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. @@ -300,16 +304,16 @@ _$SignUpViewStateCopyWith<_SignUpViewState> get copyWith => __$SignUpViewStateCo @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); } @override -int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated]); +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); @override String toString() { - return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated)'; + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; } @@ -320,7 +324,7 @@ abstract mixin class _$SignUpViewStateCopyWith<$Res> implements $SignUpViewState factory _$SignUpViewStateCopyWith(_SignUpViewState value, $Res Function(_SignUpViewState) _then) = __$SignUpViewStateCopyWithImpl; @override @useResult $Res call({ - int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode }); @@ -337,7 +341,7 @@ class __$SignUpViewStateCopyWithImpl<$Res> /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { return _then(_SignUpViewState( currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable @@ -348,6 +352,7 @@ as String,firstName: null == firstName ? _self.firstName : firstName // ignore: as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable +as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable as DateTime?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable @@ -369,6 +374,7 @@ as TwoFASecretEntity?,otpCode: null == otpCode ? _self.otpCode : otpCode // igno as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable as bool,showAccountCreated: null == showAccountCreated ? _self.showAccountCreated : showAccountCreated // ignore: cast_nullable_to_non_nullable +as bool,showSecretCode: null == showSecretCode ? _self.showSecretCode : showSecretCode // ignore: cast_nullable_to_non_nullable as bool, )); } diff --git a/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart b/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart index 7d5d3695..2e45bff5 100644 --- a/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart +++ b/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart @@ -29,12 +29,13 @@ class CountryPrefixPicker extends StatelessWidget { width: width, height: height, child: CountryCodePicker( + showCountryOnly: true, + showOnlyCountryWhenClosed: true, + showDropDownButton: true, headerText: headerText, onChanged: onChanged, initialSelection: initialCountryCode, - showFlag: false, - showDropDownButton: false, - hideMainText: true, + showFlag: true, padding: EdgeInsets.zero, builder: (CountryCode? country) { if (country == null) { diff --git a/packages/design_system/lib/src/inputs/textfields.dart b/packages/design_system/lib/src/inputs/textfields.dart index 86c35c15..e394236d 100644 --- a/packages/design_system/lib/src/inputs/textfields.dart +++ b/packages/design_system/lib/src/inputs/textfields.dart @@ -9,6 +9,7 @@ class CustomTextField extends StatefulWidget { final String label; final int? lines; final ValueChanged? onChanged; + final bool readOnly; final int? length; final TextEditingController? controller; @@ -23,6 +24,7 @@ class CustomTextField extends StatefulWidget { this.lines, this.length, this.onChanged, + this.readOnly = false, this.controller, }); @@ -52,6 +54,7 @@ class CustomTextFieldState extends State { ), ), TextFormField( + readOnly: widget.readOnly, onFieldSubmitted: widget.onSubmitted, textInputAction: widget.textInputAction, controller: widget.controller, diff --git a/packages/sf_localizations/assets/l10n/de.json b/packages/sf_localizations/assets/l10n/de.json index e406016c..914c8fd8 100644 --- a/packages/sf_localizations/assets/l10n/de.json +++ b/packages/sf_localizations/assets/l10n/de.json @@ -43,5 +43,68 @@ "close": "Schließen", "errorTwoFactorCodeRequired": "Der Bestätigungscode ist erforderlich.", "errorTwoFactorCodeInvalidLength": "Der Code muss 6-stellig sein.", - "errorTwoFactorCodeInvalid": "Ungültiger Code. Bitte versuche es erneut." + "errorTwoFactorCodeInvalid": "Ungültiger Code. Bitte versuche es erneut.", + "stepUserContactSupertitle": "Benutzer und Kontakt", + "stepUserContactTitle": "Erstelle dein Konto", + "stepUserContactSubtitle": "Mit deiner E-Mail und deiner Telefonnummer können wir dich jederzeit informieren", + "termsText": "Ich akzeptiere die Allgemeinen Geschäftsbedingungen", + "firstNameLabel": "Vorname", + "firstNameHint": "Vorname", + "lastNameLabel": "Nachname", + "lastNameHint": "Nachname", + "documentTypeHint": "Dokument", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Reisepass", + "documentNumberHint": "DNI/NIE/Reisepass", + "phoneLabel": "Mobiltelefon", + "phoneHint": "Mobiltelefon", + "emailLabel": "E-Mail-Adresse", + "emailHint": "E-Mail-Adresse", + "stepPersonalDataSupertitle": "Persönliche Daten", + "stepPersonalDataTitle": "Identifiziere dich", + "stepPersonalDataSubtitle": "Wir stellen sicher, dass das Konto auf den verantwortlichen Erwachsenen läuft", + "relationshipLabel": "Welche Beziehung hast du?", + "relationshipHint": "Wähle eine Option", + "relationshipFather": "Vater", + "relationshipMother": "Mutter", + "relationshipTutor": "Erziehungsberechtigter", + "addressCountryLabel": "Land (Adresse)", + "addressCountryHint": "Land", + "countrySpain": "Spanien", + "countryFrance": "Frankreich", + "countryPortugal": "Portugal", + "birthDateLabel": "Geburtsdatum", + "birthDateHint": "TT/MM/JJJJ", + "placeOfBirthLabel": "Geburtsort", + "placeOfBirthHint": "Geburtsstadt", + "birthCountryLabel": "Geburtsland", + "birthCountryHint": "Geburtsland", + "streetLabel": "Straße / Adresse", + "streetHint": "Gran Vía 30, 6. Stock", + "cityLabel": "Stadt", + "cityHint": "Stadt", + "provinceLabel": "Provinz", + "provinceHint": "Provinz", + "stateLabel": "Region / Bundesland", + "stateHint": "Region / Bundesland", + "postCodeLabel": "Postleitzahl", + "postCodeHint": "28013", + "stepAddressSupertitle": "Adresse", + "stepAddressTitle": "Deine Adresse", + "passwordRulesSubtitle": "Mindestens 8 Zeichen, mit einem Großbuchstaben, einer Zahl und einem Sonderzeichen", + "accountCreatedTitle": "Konto erstellt", + "accountCreatedForLabel": "Du hast das Konto erstellt für:", + "accountCreatedEmailVerificationSentLabel": "Wir haben eine Bestätigungs-E-Mail gesendet an:", + "accountCreatedChildSetupHint": "Erstelle das Konto deines Kindes und trage sein \nerstes Taschengeld ein, um es mit seiner Uhr zu nutzen", + "accountCreatedContinue": "Weiter", + "secretCodeTitle": "Einrichtungsanweisungen", + "secretCodeStep1Title": "Lade eine Authenticator-App herunter", + "secretCodeStep1Body": "Stelle sicher, dass Google Authenticator auf deinem Gerät installiert ist.", + "secretCodeStep2Title": "QR-Code scannen oder Schlüssel kopieren", + "secretCodeStep2Body": "Scanne den QR-Code unten mit der Authenticator-App, um das Gerät zu verifizieren.\n\nOder kopiere den Schlüssel und gib ihn manuell in der Authenticator-App ein.", + "secretCodeKeyCopied": "Schlüssel kopiert", + "secretCodeStep3Title": "Generierten Code kopieren", + "secretCodeStep3Body": "Nachdem du den QR-Code gescannt oder den Schlüssel in der Authenticator-App eingegeben hast, kopiere den generierten 6-stelligen Code und gib ihn im nächsten Bildschirm ein.", + "secretCodeConfigure": "Einrichten" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json index 5fce03fd..68d3b66d 100755 --- a/packages/sf_localizations/assets/l10n/en.json +++ b/packages/sf_localizations/assets/l10n/en.json @@ -43,5 +43,68 @@ "close": "Close", "errorTwoFactorCodeRequired": "The verification code is required.", "errorTwoFactorCodeInvalidLength": "The code must be 6 digits.", - "errorTwoFactorCodeInvalid": "Invalid code. Please try again." + "errorTwoFactorCodeInvalid": "Invalid code. Please try again.", + "stepUserContactSupertitle": "User & contact", + "stepUserContactTitle": "Create your account", + "stepUserContactSubtitle": "With your email and phone number we can keep you informed at all times", + "termsText": "I accept the terms and conditions", + "firstNameLabel": "First name", + "firstNameHint": "First name", + "lastNameLabel": "Last name", + "lastNameHint": "Last name", + "documentTypeHint": "Document", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Passport", + "documentNumberHint": "DNI/NIE/Passport", + "phoneLabel": "Mobile phone", + "phoneHint": "Mobile phone", + "emailLabel": "Email address", + "emailHint": "Email address", + "stepPersonalDataSupertitle": "Personal details", + "stepPersonalDataTitle": "Identify yourself", + "stepPersonalDataSubtitle": "We'll make sure the account is in the name of the responsible adult", + "relationshipLabel": "What is your relationship?", + "relationshipHint": "Select an option", + "relationshipFather": "Father", + "relationshipMother": "Mother", + "relationshipTutor": "Legal guardian", + "addressCountryLabel": "Country (address)", + "addressCountryHint": "Country", + "countrySpain": "Spain", + "countryFrance": "France", + "countryPortugal": "Portugal", + "birthDateLabel": "Date of birth", + "birthDateHint": "DD/MM/YYYY", + "placeOfBirthLabel": "Place of birth", + "placeOfBirthHint": "City of birth", + "birthCountryLabel": "Country of birth", + "birthCountryHint": "Country of birth", + "streetLabel": "Street / Address", + "streetHint": "Gran Vía St 30, 6th floor", + "cityLabel": "City", + "cityHint": "City", + "provinceLabel": "Province", + "provinceHint": "Province", + "stateLabel": "Region / State", + "stateHint": "Region / State", + "postCodeLabel": "Postal code", + "postCodeHint": "28013", + "stepAddressSupertitle": "Address", + "stepAddressTitle": "Your address", + "passwordRulesSubtitle": "Minimum 8 characters, including an uppercase letter, a number, and a special character", + "accountCreatedTitle": "Account created", + "accountCreatedForLabel": "You created the account for:", + "accountCreatedEmailVerificationSentLabel": "We’ve sent a verification email to:", + "accountCreatedChildSetupHint": "Create your child’s account and enter their \nfirst allowance to use it with their watch", + "accountCreatedContinue": "Continue", + "secretCodeTitle": "Setup instructions", + "secretCodeStep1Title": "Download an authenticator app", + "secretCodeStep1Body": "Make sure you have Google Authenticator on your device.", + "secretCodeStep2Title": "Scan the QR code or copy the key", + "secretCodeStep2Body": "Scan the QR code below with the authenticator app to verify the device.\n\nOr copy the key and enter it manually in the authenticator app.", + "secretCodeKeyCopied": "Key copied", + "secretCodeStep3Title": "Copy the generated code", + "secretCodeStep3Body": "After scanning the QR code or entering the key in the authenticator app, copy the generated 6-digit code and enter it on the next screen.", + "secretCodeConfigure": "Set up" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json index 21067dfd..ff87df9a 100644 --- a/packages/sf_localizations/assets/l10n/es.json +++ b/packages/sf_localizations/assets/l10n/es.json @@ -43,5 +43,68 @@ "close": "Cerrar", "errorTwoFactorCodeRequired": "El código de verificación es obligatorio.", "errorTwoFactorCodeInvalidLength": "El código debe tener 6 dígitos.", - "errorTwoFactorCodeInvalid": "Código incorrecto. Inténtalo de nuevo." + "errorTwoFactorCodeInvalid": "Código incorrecto. Inténtalo de nuevo.", + "stepUserContactSupertitle": "Usuario y contacto", + "stepUserContactTitle": "Crea tu usuario", + "stepUserContactSubtitle": "Con tu email y tu número podremos mantenerte siempre informado", + "termsText": "Acepto los términos y condiciones", + "firstNameLabel": "Nombre", + "firstNameHint": "Nombre", + "lastNameLabel": "Apellido", + "lastNameHint": "Apellido", + "documentTypeHint": "Documento", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Pasaporte", + "documentNumberHint": "DNI/NIE/Pasaporte", + "phoneLabel": "Teléfono móvil", + "phoneHint": "Teléfono móvil", + "emailLabel": "Correo electrónico", + "emailHint": "Correo electrónico", + "stepPersonalDataSupertitle": "Datos personales", + "stepPersonalDataTitle": "Identifícate", + "stepPersonalDataSubtitle": "Nos aseguraremos de que la cuenta esté a nombre del adulto responsable", + "relationshipLabel": "¿Qué familiar eres?", + "relationshipHint": "Selecciona una opción", + "relationshipFather": "Padre", + "relationshipMother": "Madre", + "relationshipTutor": "Tutor/a legal", + "addressCountryLabel": "País (dirección)", + "addressCountryHint": "País", + "countrySpain": "España", + "countryFrance": "Francia", + "countryPortugal": "Portugal", + "birthDateLabel": "Fecha de nacimiento", + "birthDateHint": "DD/MM/AAAA", + "placeOfBirthLabel": "Lugar de nacimiento", + "placeOfBirthHint": "Ciudad de nacimiento", + "birthCountryLabel": "País de nacimiento", + "birthCountryHint": "País de nacimiento", + "streetLabel": "Calle / Dirección", + "streetHint": "Calle Gran Vía 30 6º", + "cityLabel": "Ciudad", + "cityHint": "Ciudad", + "provinceLabel": "Provincia", + "provinceHint": "Provincia", + "stateLabel": "Comunidad / Estado", + "stateHint": "Comunidad / Estado", + "postCodeLabel": "Código postal", + "postCodeHint": "28013", + "stepAddressSupertitle": "Domicilio", + "stepAddressTitle": "Tu dirección", + "passwordRulesSubtitle": "Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial", + "accountCreatedTitle": "Cuenta creada", + "accountCreatedForLabel": "Has creado la cuenta para:", + "accountCreatedEmailVerificationSentLabel": "Hemos enviado un email de verificación a:", + "accountCreatedChildSetupHint": "Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj", + "accountCreatedContinue": "Continuar", + "secretCodeTitle": "Intrucciones para la configuración", + "secretCodeStep1Title": "Descargue una aplicación de autenticación", + "secretCodeStep1Body": "Asegúrate de tener Google Authenticator en tu dispositivo.", + "secretCodeStep2Title": "Escanea el código QR o copia la llave", + "secretCodeStep2Body": "Escanea el código QR debajo con la aplicación de autenticación para verificar el dispositivo.\n\nO copie la llave e ingresela manualmente en la aplicación de autenticación.", + "secretCodeKeyCopied": "Llave copiada", + "secretCodeStep3Title": "Copia el código generado", + "secretCodeStep3Body": "Después de escanear el código QR o introducir la llave en la aplicación de autenticación, copia el código generado de 6 dígitos e introdúcelo en la siguiente pantalla.", + "secretCodeConfigure": "Configurar" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/fr.json b/packages/sf_localizations/assets/l10n/fr.json index 3b2c8f68..e70cae3c 100644 --- a/packages/sf_localizations/assets/l10n/fr.json +++ b/packages/sf_localizations/assets/l10n/fr.json @@ -43,5 +43,68 @@ "close": "Fermer", "errorTwoFactorCodeRequired": "Le code de vérification est obligatoire.", "errorTwoFactorCodeInvalidLength": "Le code doit contenir 6 chiffres.", - "errorTwoFactorCodeInvalid": "Code incorrect. Veuillez réessayer." + "errorTwoFactorCodeInvalid": "Code incorrect. Veuillez réessayer.", + "stepUserContactSupertitle": "Utilisateur et contact", + "stepUserContactTitle": "Crée ton compte", + "stepUserContactSubtitle": "Avec ton e-mail et ton numéro, nous pourrons te tenir informé à tout moment", + "termsText": "J'accepte les conditions générales", + "firstNameLabel": "Prénom", + "firstNameHint": "Prénom", + "lastNameLabel": "Nom", + "lastNameHint": "Nom", + "documentTypeHint": "Document", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Passeport", + "documentNumberHint": "DNI/NIE/Passeport", + "phoneLabel": "Téléphone mobile", + "phoneHint": "Téléphone mobile", + "emailLabel": "Adresse e-mail", + "emailHint": "Adresse e-mail", + "stepPersonalDataSupertitle": "Données personnelles", + "stepPersonalDataTitle": "Identifie-toi", + "stepPersonalDataSubtitle": "Nous nous assurerons que le compte est au nom de l'adulte responsable", + "relationshipLabel": "Quel lien de parenté as-tu ?", + "relationshipHint": "Sélectionne une option", + "relationshipFather": "Père", + "relationshipMother": "Mère", + "relationshipTutor": "Tuteur légal", + "addressCountryLabel": "Pays (adresse)", + "addressCountryHint": "Pays", + "countrySpain": "Espagne", + "countryFrance": "France", + "countryPortugal": "Portugal", + "birthDateLabel": "Date de naissance", + "birthDateHint": "JJ/MM/AAAA", + "placeOfBirthLabel": "Lieu de naissance", + "placeOfBirthHint": "Ville de naissance", + "birthCountryLabel": "Pays de naissance", + "birthCountryHint": "Pays de naissance", + "streetLabel": "Rue / Adresse", + "streetHint": "Rue Gran Vía 30, 6e étage", + "cityLabel": "Ville", + "cityHint": "Ville", + "provinceLabel": "Province", + "provinceHint": "Province", + "stateLabel": "Région / État", + "stateHint": "Région / État", + "postCodeLabel": "Code postal", + "postCodeHint": "28013", + "stepAddressSupertitle": "Adresse", + "stepAddressTitle": "Ton adresse", + "passwordRulesSubtitle": "Mot de passe d'au moins 8 caractères, avec une majuscule, un chiffre et un caractère spécial", + "accountCreatedTitle": "Compte créé", + "accountCreatedForLabel": "Vous avez créé le compte pour :", + "accountCreatedEmailVerificationSentLabel": "Nous avons envoyé un e-mail de vérification à :", + "accountCreatedChildSetupHint": "Créez le compte de votre enfant et saisissez sa \npremière allocation pour l’utiliser avec sa montre", + "accountCreatedContinue": "Continuer", + "secretCodeTitle": "Instructions de configuration", + "secretCodeStep1Title": "Téléchargez une application d’authentification", + "secretCodeStep1Body": "Assurez-vous d’avoir Google Authenticator sur votre appareil.", + "secretCodeStep2Title": "Scannez le code QR ou copiez la clé", + "secretCodeStep2Body": "Scannez le code QR ci-dessous avec l’application d’authentification pour vérifier l’appareil.\n\nOu copiez la clé et saisissez-la manuellement dans l’application d’authentification.", + "secretCodeKeyCopied": "Clé copiée", + "secretCodeStep3Title": "Copiez le code généré", + "secretCodeStep3Body": "Après avoir scanné le code QR ou saisi la clé dans l’application d’authentification, copiez le code à 6 chiffres généré et saisissez-le sur l’écran suivant.", + "secretCodeConfigure": "Configurer" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/it.json b/packages/sf_localizations/assets/l10n/it.json index 516bc426..29637575 100644 --- a/packages/sf_localizations/assets/l10n/it.json +++ b/packages/sf_localizations/assets/l10n/it.json @@ -43,5 +43,68 @@ "close": "Chiudi", "errorTwoFactorCodeRequired": "Il codice di verifica è obbligatorio.", "errorTwoFactorCodeInvalidLength": "Il codice deve essere di 6 cifre.", - "errorTwoFactorCodeInvalid": "Codice non valido. Riprova." + "errorTwoFactorCodeInvalid": "Codice non valido. Riprova.", + "stepUserContactSupertitle": "Utente e contatti", + "stepUserContactTitle": "Crea il tuo account", + "stepUserContactSubtitle": "Con la tua email e il tuo numero potremo tenerti sempre informato", + "termsText": "Accetto i termini e condizioni", + "firstNameLabel": "Nome", + "firstNameHint": "Nome", + "lastNameLabel": "Cognome", + "lastNameHint": "Cognome", + "documentTypeHint": "Documento", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Passaporto", + "documentNumberHint": "DNI/NIE/Passaporto", + "phoneLabel": "Cellulare", + "phoneHint": "Cellulare", + "emailLabel": "Indirizzo email", + "emailHint": "Indirizzo email", + "stepPersonalDataSupertitle": "Dati personali", + "stepPersonalDataTitle": "Identificati", + "stepPersonalDataSubtitle": "Ci assicureremo che l'account sia intestato all'adulto responsabile", + "relationshipLabel": "Che rapporto di parentela hai?", + "relationshipHint": "Seleziona un'opzione", + "relationshipFather": "Padre", + "relationshipMother": "Madre", + "relationshipTutor": "Tutore legale", + "addressCountryLabel": "Paese (indirizzo)", + "addressCountryHint": "Paese", + "countrySpain": "Spagna", + "countryFrance": "Francia", + "countryPortugal": "Portogallo", + "birthDateLabel": "Data di nascita", + "birthDateHint": "GG/MM/AAAA", + "placeOfBirthLabel": "Luogo di nascita", + "placeOfBirthHint": "Città di nascita", + "birthCountryLabel": "Paese di nascita", + "birthCountryHint": "Paese di nascita", + "streetLabel": "Via / Indirizzo", + "streetHint": "Via Gran Vía 30, 6º piano", + "cityLabel": "Città", + "cityHint": "Città", + "provinceLabel": "Provincia", + "provinceHint": "Provincia", + "stateLabel": "Regione / Stato", + "stateHint": "Regione / Stato", + "postCodeLabel": "CAP", + "postCodeHint": "28013", + "stepAddressSupertitle": "Indirizzo", + "stepAddressTitle": "Il tuo indirizzo", + "passwordRulesSubtitle": "Password minima di 8 caratteri, con una maiuscola, un numero e un carattere speciale", + "accountCreatedTitle": "Account creato", + "accountCreatedForLabel": "Hai creato l’account per:", + "accountCreatedEmailVerificationSentLabel": "Abbiamo inviato un’email di verifica a:", + "accountCreatedChildSetupHint": "Crea l’account del tuo bambino e inserisci la sua \nprima paghetta per usarlo con il suo orologio", + "accountCreatedContinue": "Continua", + "secretCodeTitle": "Istruzioni di configurazione", + "secretCodeStep1Title": "Scarica un’app di autenticazione", + "secretCodeStep1Body": "Assicurati di avere Google Authenticator sul tuo dispositivo.", + "secretCodeStep2Title": "Scansiona il codice QR o copia la chiave", + "secretCodeStep2Body": "Scansiona il codice QR qui sotto con l’app di autenticazione per verificare il dispositivo.\n\nOppure copia la chiave e inseriscila manualmente nell’app di autenticazione.", + "secretCodeKeyCopied": "Chiave copiata", + "secretCodeStep3Title": "Copia il codice generato", + "secretCodeStep3Body": "Dopo aver scansionato il codice QR o inserito la chiave nell’app di autenticazione, copia il codice a 6 cifre generato e inseriscilo nella schermata successiva.", + "secretCodeConfigure": "Configura" } \ No newline at end of file diff --git a/packages/sf_localizations/assets/l10n/pt.json b/packages/sf_localizations/assets/l10n/pt.json index a3ff7bea..a52a0d36 100644 --- a/packages/sf_localizations/assets/l10n/pt.json +++ b/packages/sf_localizations/assets/l10n/pt.json @@ -43,5 +43,68 @@ "close": "Fechar", "errorTwoFactorCodeRequired": "O código de verificação é obrigatório.", "errorTwoFactorCodeInvalidLength": "O código deve ter 6 dígitos.", - "errorTwoFactorCodeInvalid": "Código inválido. Tenta novamente." + "errorTwoFactorCodeInvalid": "Código inválido. Tenta novamente.", + "stepUserContactSupertitle": "Utilizador e contacto", + "stepUserContactTitle": "Cria a tua conta", + "stepUserContactSubtitle": "Com o teu email e o teu número poderemos manter-te sempre informado", + "termsText": "Aceito os termos e condições", + "firstNameLabel": "Nome próprio", + "firstNameHint": "Nome próprio", + "lastNameLabel": "Apelido", + "lastNameHint": "Apelido", + "documentTypeHint": "Documento", + "documentTypeDni": "DNI", + "documentTypeNie": "NIE", + "documentTypePassport": "Passaporte", + "documentNumberHint": "DNI/NIE/Passaporte", + "phoneLabel": "Telemóvel", + "phoneHint": "Telemóvel", + "emailLabel": "Email", + "emailHint": "Email", + "stepPersonalDataSupertitle": "Dados pessoais", + "stepPersonalDataTitle": "Identifica-te", + "stepPersonalDataSubtitle": "Vamos garantir que a conta está em nome do adulto responsável", + "relationshipLabel": "Qual é o teu grau de parentesco?", + "relationshipHint": "Seleciona uma opção", + "relationshipFather": "Pai", + "relationshipMother": "Mãe", + "relationshipTutor": "Tutor legal", + "addressCountryLabel": "País (morada)", + "addressCountryHint": "País", + "countrySpain": "Espanha", + "countryFrance": "França", + "countryPortugal": "Portugal", + "birthDateLabel": "Data de nascimento", + "birthDateHint": "DD/MM/AAAA", + "placeOfBirthLabel": "Local de nascimento", + "placeOfBirthHint": "Cidade de nascimento", + "birthCountryLabel": "País de nascimento", + "birthCountryHint": "País de nascimento", + "streetLabel": "Rua / Morada", + "streetHint": "Rua Gran Vía 30, 6.º andar", + "cityLabel": "Cidade", + "cityHint": "Cidade", + "provinceLabel": "Província", + "provinceHint": "Província", + "stateLabel": "Região / Estado", + "stateHint": "Região / Estado", + "postCodeLabel": "Código postal", + "postCodeHint": "28013", + "stepAddressSupertitle": "Morada", + "stepAddressTitle": "A tua morada", + "passwordRulesSubtitle": "Palavra-passe mínima de 8 caracteres, com uma maiúscula, um número e um carácter especial", + "accountCreatedTitle": "Conta criada", + "accountCreatedForLabel": "Criaste a conta para:", + "accountCreatedEmailVerificationSentLabel": "Enviámos um e-mail de verificação para:", + "accountCreatedChildSetupHint": "Cria a conta do teu filho e introduz a sua \nprimeira mesada para a usar com o relógio", + "accountCreatedContinue": "Continuar", + "secretCodeTitle": "Instruções de configuração", + "secretCodeStep1Title": "Descarrega uma aplicação de autenticação", + "secretCodeStep1Body": "Certifica-te de que tens o Google Authenticator no teu dispositivo.", + "secretCodeStep2Title": "Lê o código QR ou copia a chave", + "secretCodeStep2Body": "Lê o código QR abaixo com a aplicação de autenticação para verificar o dispositivo.\n\nOu copia a chave e introduz-a manualmente na aplicação de autenticação.", + "secretCodeKeyCopied": "Chave copiada", + "secretCodeStep3Title": "Copia o código gerado", + "secretCodeStep3Body": "Depois de leres o código QR ou introduzires a chave na aplicação de autenticação, copia o código de 6 dígitos gerado e introduz-lo no ecrã seguinte.", + "secretCodeConfigure": "Configurar" } \ No newline at end of file diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart index 16430635..cd64d37a 100755 --- a/packages/sf_localizations/lib/src/generated/i18n.dart +++ b/packages/sf_localizations/lib/src/generated/i18n.dart @@ -49,4 +49,87 @@ class I18n { static const String errorTwoFactorCodeInvalidLength = 'errorTwoFactorCodeInvalidLength'; static const String errorTwoFactorCodeInvalid = 'errorTwoFactorCodeInvalid'; + + static const String stepUserContactSupertitle = 'stepUserContactSupertitle'; + static const String stepUserContactTitle = 'stepUserContactTitle'; + static const String stepUserContactSubtitle = 'stepUserContactSubtitle'; + + static const String termsText = 'termsText'; + + static const String firstNameLabel = 'firstNameLabel'; + static const String firstNameHint = 'firstNameHint'; + static const String lastNameLabel = 'lastNameLabel'; + static const String lastNameHint = 'lastNameHint'; + + static const String documentTypeHint = 'documentTypeHint'; + static const String documentTypeDni = 'documentTypeDni'; + static const String documentTypeNie = 'documentTypeNie'; + static const String documentTypePassport = 'documentTypePassport'; + static const String documentNumberHint = 'documentNumberHint'; + + static const String phoneLabel = 'phoneLabel'; + static const String phoneHint = 'phoneHint'; + static const String emailLabel = 'emailLabel'; + static const String emailHint = 'emailHint'; + + static const String stepPersonalDataSupertitle = 'stepPersonalDataSupertitle'; + static const String stepPersonalDataTitle = 'stepPersonalDataTitle'; + static const String stepPersonalDataSubtitle = 'stepPersonalDataSubtitle'; + + static const String relationshipLabel = 'relationshipLabel'; + static const String relationshipHint = 'relationshipHint'; + static const String relationshipFather = 'relationshipFather'; + static const String relationshipMother = 'relationshipMother'; + static const String relationshipTutor = 'relationshipTutor'; + + static const String addressCountryLabel = 'addressCountryLabel'; + static const String addressCountryHint = 'addressCountryHint'; + static const String countrySpain = 'countrySpain'; + static const String countryFrance = 'countryFrance'; + static const String countryPortugal = 'countryPortugal'; + + static const String birthDateLabel = 'birthDateLabel'; + static const String birthDateHint = 'birthDateHint'; + + static const String placeOfBirthLabel = 'placeOfBirthLabel'; + static const String placeOfBirthHint = 'placeOfBirthHint'; + + static const String birthCountryLabel = 'birthCountryLabel'; + static const String birthCountryHint = 'birthCountryHint'; + + static const String streetLabel = 'streetLabel'; + static const String streetHint = 'streetHint'; + + static const String cityLabel = 'cityLabel'; + static const String cityHint = 'cityHint'; + + static const String provinceLabel = 'provinceLabel'; + static const String provinceHint = 'provinceHint'; + + static const String stateLabel = 'stateLabel'; + static const String stateHint = 'stateHint'; + + static const String postCodeLabel = 'postCodeLabel'; + static const String postCodeHint = 'postCodeHint'; + + static const String stepAddressSupertitle = 'stepAddressSupertitle'; + static const String stepAddressTitle = 'stepAddressTitle'; + static const String passwordRulesSubtitle = 'passwordRulesSubtitle'; + + static const String accountCreatedTitle = 'accountCreatedTitle'; + static const String accountCreatedForLabel = 'accountCreatedForLabel'; + static const String accountCreatedEmailVerificationSentLabel = + 'accountCreatedEmailVerificationSentLabel'; + static const String accountCreatedChildSetupHint = + 'accountCreatedChildSetupHint'; + static const String accountCreatedContinue = 'accountCreatedContinue'; + static const String secretCodeTitle = 'secretCodeTitle'; + static const String secretCodeStep1Title = 'secretCodeStep1Title'; + static const String secretCodeStep1Body = 'secretCodeStep1Body'; + static const String secretCodeStep2Title = 'secretCodeStep2Title'; + static const String secretCodeStep2Body = 'secretCodeStep2Body'; + static const String secretCodeKeyCopied = 'secretCodeKeyCopied'; + static const String secretCodeStep3Title = 'secretCodeStep3Title'; + static const String secretCodeStep3Body = 'secretCodeStep3Body'; + static const String secretCodeConfigure = 'secretCodeConfigure'; } From 87548845442d24ba67694da85cf0f3e7c0e82903 Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Mon, 5 Jan 2026 10:03:18 +0100 Subject: [PATCH 4/4] sign up parameters fixed change document, documentType and country code --- .../android/app/src/main/AndroidManifest.xml | 4 +- apps/mobile_app/lib/main.dart | 3 +- apps/mobile_app/pubspec.lock | 2 +- apps/mobile_app/pubspec.yaml | 1 + .../data/models/sign_up_request_model.dart | 10 +++-- .../models/sign_up_request_model.freezed.dart | 22 ++++++----- .../data/models/sign_up_request_model.g.dart | 8 ++-- .../sign_up/presentation/sign_up_steps.dart | 17 ++++---- .../state/sign_up_view_model.dart | 7 ++-- .../state/sign_up_view_state.dart | 1 + .../state/sign_up_view_state.freezed.dart | 39 ++++++++++--------- packages/sf_localizations/assets/l10n/de.json | 3 -- packages/sf_localizations/assets/l10n/en.json | 3 -- packages/sf_localizations/assets/l10n/es.json | 5 +-- packages/sf_localizations/assets/l10n/fr.json | 3 -- packages/sf_localizations/assets/l10n/it.json | 3 -- packages/sf_localizations/assets/l10n/pt.json | 3 -- .../lib/src/generated/i18n.dart | 3 -- 18 files changed, 68 insertions(+), 69 deletions(-) diff --git a/apps/mobile_app/android/app/src/main/AndroidManifest.xml b/apps/mobile_app/android/app/src/main/AndroidManifest.xml index 38d21dbf..2bb4d4d7 100644 --- a/apps/mobile_app/android/app/src/main/AndroidManifest.xml +++ b/apps/mobile_app/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ - + + + json) => _$SignUpRequestModelFromJson(json); @override - @JsonKey(name: 'dni') + @JsonKey(name: 'document') String get documentNumber; + @override + @JsonKey(name: 'relationType') + String get relationship; } extension SignUpRequestModelMapper on SignUpRequestEntity { diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart index 0bbf6d41..d9f06424 100644 --- a/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.freezed.dart @@ -15,7 +15,9 @@ T _$identity(T value) => value; /// @nodoc mixin _$SignUpRequestModel { - String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;@JsonKey(name: 'dni') String get documentNumber; String get documentType; String get relationship; + String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List get taxResidences; List get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;// ignore: invalid_annotation_target +@JsonKey(name: 'document') String get documentNumber; String get documentType;// ignore: invalid_annotation_target +@JsonKey(name: 'relationType') String get relationship; /// Create a copy of SignUpRequestModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -48,7 +50,7 @@ abstract mixin class $SignUpRequestModelCopyWith<$Res> { factory $SignUpRequestModelCopyWith(SignUpRequestModel value, $Res Function(SignUpRequestModel) _then) = _$SignUpRequestModelCopyWithImpl; @useResult $Res call({ - String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String documentType, String relationship + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'document') String documentNumber, String documentType,@JsonKey(name: 'relationType') String relationship }); @@ -167,7 +169,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SignUpRequestModel() when $default != null: return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: @@ -188,7 +190,7 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship) $default,) {final _that = this; switch (_that) { case _SignUpRequestModel(): return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: @@ -208,7 +210,7 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'dni') String documentNumber, String documentType, String relationship)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship)? $default,) {final _that = this; switch (_that) { case _SignUpRequestModel() when $default != null: return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _: @@ -223,7 +225,7 @@ return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.lan @JsonSerializable() class _SignUpRequestModel implements SignUpRequestModel { - const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'dni') required this.documentNumber, required this.documentType, required this.relationship}): _taxResidences = taxResidences,_addresses = addresses; + const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List taxResidences, required final List addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'document') required this.documentNumber, required this.documentType, @JsonKey(name: 'relationType') required this.relationship}): _taxResidences = taxResidences,_addresses = addresses; factory _SignUpRequestModel.fromJson(Map json) => _$SignUpRequestModelFromJson(json); @override final String firstName; @@ -250,9 +252,11 @@ class _SignUpRequestModel implements SignUpRequestModel { @override final String userId; @override final String placeOfBirth; @override final String birthCountry; -@override@JsonKey(name: 'dni') final String documentNumber; +// ignore: invalid_annotation_target +@override@JsonKey(name: 'document') final String documentNumber; @override final String documentType; -@override final String relationship; +// ignore: invalid_annotation_target +@override@JsonKey(name: 'relationType') final String relationship; /// Create a copy of SignUpRequestModel /// with the given fields replaced by the non-null parameter values. @@ -287,7 +291,7 @@ abstract mixin class _$SignUpRequestModelCopyWith<$Res> implements $SignUpReques factory _$SignUpRequestModelCopyWith(_SignUpRequestModel value, $Res Function(_SignUpRequestModel) _then) = __$SignUpRequestModelCopyWithImpl; @override @useResult $Res call({ - String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'dni') String documentNumber, String documentType, String relationship + String firstName, String lastName, String email, String phone, String language, String password, List taxResidences, List addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'document') String documentNumber, String documentType,@JsonKey(name: 'relationType') String relationship }); diff --git a/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart index fb8353b1..38569d37 100644 --- a/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart +++ b/modules/auth/lib/src/core/data/models/sign_up_request_model.g.dart @@ -24,9 +24,9 @@ _SignUpRequestModel _$SignUpRequestModelFromJson(Map json) => userId: json['userId'] as String, placeOfBirth: json['placeOfBirth'] as String, birthCountry: json['birthCountry'] as String, - documentNumber: json['dni'] as String, + documentNumber: json['document'] as String, documentType: json['documentType'] as String, - relationship: json['relationship'] as String, + relationship: json['relationType'] as String, ); Map _$SignUpRequestModelToJson(_SignUpRequestModel instance) => @@ -43,7 +43,7 @@ Map _$SignUpRequestModelToJson(_SignUpRequestModel instance) => 'userId': instance.userId, 'placeOfBirth': instance.placeOfBirth, 'birthCountry': instance.birthCountry, - 'dni': instance.documentNumber, + 'document': instance.documentNumber, 'documentType': instance.documentType, - 'relationship': instance.relationship, + 'relationType': instance.relationship, }; diff --git a/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart index 74d60d9d..b2155d47 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/sign_up_steps.dart @@ -9,14 +9,14 @@ import 'package:flutter/material.dart'; import 'package:sf_localizations/sf_localizations.dart'; const Map documentType = { - 'dni': I18n.documentTypeDni, - 'nie': I18n.documentTypeNie, - 'passport': I18n.documentTypePassport, + 'DNI': I18n.documentTypeDni, + 'NIE': I18n.documentTypeNie, + 'PASSPORT': I18n.documentTypePassport, }; const Map relationship = { - 'father': I18n.relationshipFather, - 'mother': I18n.relationshipMother, - 'tutor': I18n.relationshipTutor, + 'FATHER': I18n.relationshipFather, + 'MOTHER': I18n.relationshipMother, + 'OTHER': I18n.relationshipTutor, }; List signUpSteps(BuildContext context) => [ @@ -138,7 +138,10 @@ List signUpSteps(BuildContext context) => [ birthCountryLabel: context.translate(I18n.birthCountryLabel), birthCountryHint: context.translate(I18n.birthCountryHint), onBirthCountryChanged: (CountryCode value) { - vm.setBirthCountryFromPicker(name: value.name ?? ''); + vm.setBirthCountryFromPicker( + name: value.name ?? '', + code: value.code ?? '', + ); }, streetLabel: context.translate(I18n.streetLabel), diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart index 4722e0f3..23e9f96f 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_model.dart @@ -162,9 +162,9 @@ class SignUpViewModel extends Notifier { state = state.copyWith(dialCode: dialCode); } - void setBirthCountryFromPicker({required String name}) { + void setBirthCountryFromPicker({required String name, required String code}) { birthCountryController.text = name; - state = state.copyWith(birthCountry: name); + state = state.copyWith(birthCountry: name, birthCountryCode: code); } Future pickBornAt(BuildContext context) async { @@ -570,7 +570,8 @@ class SignUpViewModel extends Notifier { bornAt: bornAt.millisecondsSinceEpoch, userId: state.userId.trim(), placeOfBirth: state.placeOfBirth.trim(), - birthCountry: state.birthCountry.trim(), + birthCountry: state.birthCountryCode.trim(), + // birthCountry: 'spain', addresses: [_toAddressEntity(state.address)], taxResidences: [_toAddressEntity(state.address)], ); diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart index 94653d45..27ac5778 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.dart @@ -27,6 +27,7 @@ abstract class SignUpViewState with _$SignUpViewState { @Default('') String userId, @Default('') String placeOfBirth, @Default('España') String birthCountry, + @Default('ES') String birthCountryCode, @Default(AddressViewState()) AddressViewState address, diff --git a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart index 04e3356a..cf19d07c 100644 --- a/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart +++ b/modules/auth/lib/src/features/sign_up/presentation/state/sign_up_view_state.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SignUpViewState { - int get currentIndex; String get documentNumber; String get documentType; bool get acceptTerms; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get dialCode; String get language; DateTime? get bornAt; String get password; String get repeatPassword; bool get isShowPassword; String get userId; String get placeOfBirth; String get birthCountry; AddressViewState get address; String get emailError; String get passwordError; String get phoneError; String get errorMessage; bool get isLoading; bool get showErrors; String get token; TwoFASecretEntity? get twoFASecret; String get otpCode; String get otpError; bool get isOtpLoading; bool get showAccountCreated; bool get showSecretCode; + int get currentIndex; String get documentNumber; String get documentType; bool get acceptTerms; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get dialCode; String get language; DateTime? get bornAt; String get password; String get repeatPassword; bool get isShowPassword; String get userId; String get placeOfBirth; String get birthCountry; String get birthCountryCode; AddressViewState get address; String get emailError; String get passwordError; String get phoneError; String get errorMessage; bool get isLoading; bool get showErrors; String get token; TwoFASecretEntity? get twoFASecret; String get otpCode; String get otpError; bool get isOtpLoading; bool get showAccountCreated; bool get showSecretCode; /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $SignUpViewStateCopyWith get copyWith => _$SignUpViewStateCopyW @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.birthCountryCode, birthCountryCode) || other.birthCountryCode == birthCountryCode)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); } @override -int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,birthCountryCode,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); @override String toString() { - return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, birthCountryCode: $birthCountryCode, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; } @@ -45,7 +45,7 @@ abstract mixin class $SignUpViewStateCopyWith<$Res> { factory $SignUpViewStateCopyWith(SignUpViewState value, $Res Function(SignUpViewState) _then) = _$SignUpViewStateCopyWithImpl; @useResult $Res call({ - int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, String birthCountryCode, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode }); @@ -62,7 +62,7 @@ class _$SignUpViewStateCopyWithImpl<$Res> /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? birthCountryCode = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { return _then(_self.copyWith( currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable @@ -82,6 +82,7 @@ as String,isShowPassword: null == isShowPassword ? _self.isShowPassword : isShow as bool,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,birthCountryCode: null == birthCountryCode ? _self.birthCountryCode : birthCountryCode // ignore: cast_nullable_to_non_nullable as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as AddressViewState,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable @@ -202,10 +203,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, String birthCountryCode, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SignUpViewState() when $default != null: -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.birthCountryCode,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: return orElse(); } @@ -223,10 +224,10 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, String birthCountryCode, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode) $default,) {final _that = this; switch (_that) { case _SignUpViewState(): -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.birthCountryCode,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: throw StateError('Unexpected subclass'); } @@ -243,10 +244,10 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, String birthCountryCode, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode)? $default,) {final _that = this; switch (_that) { case _SignUpViewState() when $default != null: -return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: +return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that.acceptTerms,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.dialCode,_that.language,_that.bornAt,_that.password,_that.repeatPassword,_that.isShowPassword,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.birthCountryCode,_that.address,_that.emailError,_that.passwordError,_that.phoneError,_that.errorMessage,_that.isLoading,_that.showErrors,_that.token,_that.twoFASecret,_that.otpCode,_that.otpError,_that.isOtpLoading,_that.showAccountCreated,_that.showSecretCode);case _: return null; } @@ -258,7 +259,7 @@ return $default(_that.currentIndex,_that.documentNumber,_that.documentType,_that class _SignUpViewState implements SignUpViewState { - const _SignUpViewState({this.currentIndex = 0, this.documentNumber = '', this.documentType = '', this.acceptTerms = false, this.relationship = '', this.firstName = '', this.lastName = '', this.email = '', this.phone = '', this.dialCode = '+34', this.language = '', this.bornAt, this.password = '', this.repeatPassword = '', this.isShowPassword = false, this.userId = '', this.placeOfBirth = '', this.birthCountry = 'España', this.address = const AddressViewState(), this.emailError = '', this.passwordError = '', this.phoneError = '', this.errorMessage = '', this.isLoading = false, this.showErrors = false, this.token = '', this.twoFASecret, this.otpCode = '', this.otpError = '', this.isOtpLoading = false, this.showAccountCreated = false, this.showSecretCode = false}); + const _SignUpViewState({this.currentIndex = 0, this.documentNumber = '', this.documentType = '', this.acceptTerms = false, this.relationship = '', this.firstName = '', this.lastName = '', this.email = '', this.phone = '', this.dialCode = '+34', this.language = '', this.bornAt, this.password = '', this.repeatPassword = '', this.isShowPassword = false, this.userId = '', this.placeOfBirth = '', this.birthCountry = 'España', this.birthCountryCode = 'ES', this.address = const AddressViewState(), this.emailError = '', this.passwordError = '', this.phoneError = '', this.errorMessage = '', this.isLoading = false, this.showErrors = false, this.token = '', this.twoFASecret, this.otpCode = '', this.otpError = '', this.isOtpLoading = false, this.showAccountCreated = false, this.showSecretCode = false}); @override@JsonKey() final int currentIndex; @@ -279,6 +280,7 @@ class _SignUpViewState implements SignUpViewState { @override@JsonKey() final String userId; @override@JsonKey() final String placeOfBirth; @override@JsonKey() final String birthCountry; +@override@JsonKey() final String birthCountryCode; @override@JsonKey() final AddressViewState address; @override@JsonKey() final String emailError; @override@JsonKey() final String passwordError; @@ -304,16 +306,16 @@ _$SignUpViewStateCopyWith<_SignUpViewState> get copyWith => __$SignUpViewStateCo @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpViewState&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.acceptTerms, acceptTerms) || other.acceptTerms == acceptTerms)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.language, language) || other.language == language)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.password, password) || other.password == password)&&(identical(other.repeatPassword, repeatPassword) || other.repeatPassword == repeatPassword)&&(identical(other.isShowPassword, isShowPassword) || other.isShowPassword == isShowPassword)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.birthCountryCode, birthCountryCode) || other.birthCountryCode == birthCountryCode)&&(identical(other.address, address) || other.address == address)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.phoneError, phoneError) || other.phoneError == phoneError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFASecret, twoFASecret) || other.twoFASecret == twoFASecret)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading)&&(identical(other.showAccountCreated, showAccountCreated) || other.showAccountCreated == showAccountCreated)&&(identical(other.showSecretCode, showSecretCode) || other.showSecretCode == showSecretCode)); } @override -int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); +int get hashCode => Object.hashAll([runtimeType,currentIndex,documentNumber,documentType,acceptTerms,relationship,firstName,lastName,email,phone,dialCode,language,bornAt,password,repeatPassword,isShowPassword,userId,placeOfBirth,birthCountry,birthCountryCode,address,emailError,passwordError,phoneError,errorMessage,isLoading,showErrors,token,twoFASecret,otpCode,otpError,isOtpLoading,showAccountCreated,showSecretCode]); @override String toString() { - return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; + return 'SignUpViewState(currentIndex: $currentIndex, documentNumber: $documentNumber, documentType: $documentType, acceptTerms: $acceptTerms, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, dialCode: $dialCode, language: $language, bornAt: $bornAt, password: $password, repeatPassword: $repeatPassword, isShowPassword: $isShowPassword, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, birthCountryCode: $birthCountryCode, address: $address, emailError: $emailError, passwordError: $passwordError, phoneError: $phoneError, errorMessage: $errorMessage, isLoading: $isLoading, showErrors: $showErrors, token: $token, twoFASecret: $twoFASecret, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading, showAccountCreated: $showAccountCreated, showSecretCode: $showSecretCode)'; } @@ -324,7 +326,7 @@ abstract mixin class _$SignUpViewStateCopyWith<$Res> implements $SignUpViewState factory _$SignUpViewStateCopyWith(_SignUpViewState value, $Res Function(_SignUpViewState) _then) = __$SignUpViewStateCopyWithImpl; @override @useResult $Res call({ - int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode + int currentIndex, String documentNumber, String documentType, bool acceptTerms, String relationship, String firstName, String lastName, String email, String phone, String dialCode, String language, DateTime? bornAt, String password, String repeatPassword, bool isShowPassword, String userId, String placeOfBirth, String birthCountry, String birthCountryCode, AddressViewState address, String emailError, String passwordError, String phoneError, String errorMessage, bool isLoading, bool showErrors, String token, TwoFASecretEntity? twoFASecret, String otpCode, String otpError, bool isOtpLoading, bool showAccountCreated, bool showSecretCode }); @@ -341,7 +343,7 @@ class __$SignUpViewStateCopyWithImpl<$Res> /// Create a copy of SignUpViewState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? currentIndex = null,Object? documentNumber = null,Object? documentType = null,Object? acceptTerms = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? dialCode = null,Object? language = null,Object? bornAt = freezed,Object? password = null,Object? repeatPassword = null,Object? isShowPassword = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? birthCountryCode = null,Object? address = null,Object? emailError = null,Object? passwordError = null,Object? phoneError = null,Object? errorMessage = null,Object? isLoading = null,Object? showErrors = null,Object? token = null,Object? twoFASecret = freezed,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,Object? showAccountCreated = null,Object? showSecretCode = null,}) { return _then(_SignUpViewState( currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable as int,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable @@ -361,6 +363,7 @@ as String,isShowPassword: null == isShowPassword ? _self.isShowPassword : isShow as bool,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable +as String,birthCountryCode: null == birthCountryCode ? _self.birthCountryCode : birthCountryCode // ignore: cast_nullable_to_non_nullable as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as AddressViewState,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable diff --git a/packages/sf_localizations/assets/l10n/de.json b/packages/sf_localizations/assets/l10n/de.json index 914c8fd8..ae096950 100644 --- a/packages/sf_localizations/assets/l10n/de.json +++ b/packages/sf_localizations/assets/l10n/de.json @@ -71,9 +71,6 @@ "relationshipTutor": "Erziehungsberechtigter", "addressCountryLabel": "Land (Adresse)", "addressCountryHint": "Land", - "countrySpain": "Spanien", - "countryFrance": "Frankreich", - "countryPortugal": "Portugal", "birthDateLabel": "Geburtsdatum", "birthDateHint": "TT/MM/JJJJ", "placeOfBirthLabel": "Geburtsort", diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json index 68d3b66d..c90eb3f3 100755 --- a/packages/sf_localizations/assets/l10n/en.json +++ b/packages/sf_localizations/assets/l10n/en.json @@ -71,9 +71,6 @@ "relationshipTutor": "Legal guardian", "addressCountryLabel": "Country (address)", "addressCountryHint": "Country", - "countrySpain": "Spain", - "countryFrance": "France", - "countryPortugal": "Portugal", "birthDateLabel": "Date of birth", "birthDateHint": "DD/MM/YYYY", "placeOfBirthLabel": "Place of birth", diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json index ff87df9a..12fa5485 100644 --- a/packages/sf_localizations/assets/l10n/es.json +++ b/packages/sf_localizations/assets/l10n/es.json @@ -18,7 +18,7 @@ "connect": "Conéctate", "verificationCodeSentTo": "Hemos enviado el código al ", "enterCodeHere": "Introduce el código aquí", - "enter": "enter", + "enter": "entrar", "didNotReceiveIt": "¿No lo has recibido?", "tryAgain": "Volver a intentarlo", "welcome": "¡Te damos la bienvenida!", @@ -71,9 +71,6 @@ "relationshipTutor": "Tutor/a legal", "addressCountryLabel": "País (dirección)", "addressCountryHint": "País", - "countrySpain": "España", - "countryFrance": "Francia", - "countryPortugal": "Portugal", "birthDateLabel": "Fecha de nacimiento", "birthDateHint": "DD/MM/AAAA", "placeOfBirthLabel": "Lugar de nacimiento", diff --git a/packages/sf_localizations/assets/l10n/fr.json b/packages/sf_localizations/assets/l10n/fr.json index e70cae3c..a5e7eb4a 100644 --- a/packages/sf_localizations/assets/l10n/fr.json +++ b/packages/sf_localizations/assets/l10n/fr.json @@ -71,9 +71,6 @@ "relationshipTutor": "Tuteur légal", "addressCountryLabel": "Pays (adresse)", "addressCountryHint": "Pays", - "countrySpain": "Espagne", - "countryFrance": "France", - "countryPortugal": "Portugal", "birthDateLabel": "Date de naissance", "birthDateHint": "JJ/MM/AAAA", "placeOfBirthLabel": "Lieu de naissance", diff --git a/packages/sf_localizations/assets/l10n/it.json b/packages/sf_localizations/assets/l10n/it.json index 29637575..aa9feb2f 100644 --- a/packages/sf_localizations/assets/l10n/it.json +++ b/packages/sf_localizations/assets/l10n/it.json @@ -71,9 +71,6 @@ "relationshipTutor": "Tutore legale", "addressCountryLabel": "Paese (indirizzo)", "addressCountryHint": "Paese", - "countrySpain": "Spagna", - "countryFrance": "Francia", - "countryPortugal": "Portogallo", "birthDateLabel": "Data di nascita", "birthDateHint": "GG/MM/AAAA", "placeOfBirthLabel": "Luogo di nascita", diff --git a/packages/sf_localizations/assets/l10n/pt.json b/packages/sf_localizations/assets/l10n/pt.json index a52a0d36..f34625da 100644 --- a/packages/sf_localizations/assets/l10n/pt.json +++ b/packages/sf_localizations/assets/l10n/pt.json @@ -71,9 +71,6 @@ "relationshipTutor": "Tutor legal", "addressCountryLabel": "País (morada)", "addressCountryHint": "País", - "countrySpain": "Espanha", - "countryFrance": "França", - "countryPortugal": "Portugal", "birthDateLabel": "Data de nascimento", "birthDateHint": "DD/MM/AAAA", "placeOfBirthLabel": "Local de nascimento", diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart index cd64d37a..643f6ed2 100755 --- a/packages/sf_localizations/lib/src/generated/i18n.dart +++ b/packages/sf_localizations/lib/src/generated/i18n.dart @@ -84,9 +84,6 @@ class I18n { static const String addressCountryLabel = 'addressCountryLabel'; static const String addressCountryHint = 'addressCountryHint'; - static const String countrySpain = 'countrySpain'; - static const String countryFrance = 'countryFrance'; - static const String countryPortugal = 'countryPortugal'; static const String birthDateLabel = 'birthDateLabel'; static const String birthDateHint = 'birthDateHint';