diff --git a/analysis_options.yaml b/analysis_options.yaml index c4d2cef..1d331a6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -13,6 +13,7 @@ analyzer: sort_unnamed_constructors_first: ignore invalid_annotation_target: ignore use_build_context_synchronously: ignore + deprecated_member_use: ignore exclude: - test/generated/** - "**/**.g.dart" diff --git a/lib/application/auth/bloc/login_form_bloc.dart b/lib/application/auth/bloc/login_form_bloc.dart new file mode 100644 index 0000000..e5bad21 --- /dev/null +++ b/lib/application/auth/bloc/login_form_bloc.dart @@ -0,0 +1,62 @@ +import 'dart:developer'; + +import 'package:bloc/bloc.dart'; +import 'package:dartz/dartz.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../domain/auth/auth.dart'; + +part 'login_form_event.dart'; +part 'login_form_state.dart'; +part 'login_form_bloc.freezed.dart'; + +@injectable +class LoginFormBloc extends Bloc { + final IAuthRepository _authRepository; + LoginFormBloc(this._authRepository) : super(LoginFormState.initial()) { + on(_onLoginFormEvent); + } + + Future _onLoginFormEvent( + LoginFormEvent event, + Emitter emit, + ) { + return event.map( + emailChanged: (e) async { + emit(state.copyWith(email: e.email, failureOrLoginOption: none())); + }, + passwordChanged: (e) async { + emit( + state.copyWith(password: e.password, failureOrLoginOption: none()), + ); + }, + submitted: (e) async { + Either? failureOrLogin; + emit(state.copyWith(isSubmitting: true, failureOrLoginOption: none())); + + final emailValid = state.email.isNotEmpty; + final passwordValid = state.password.isNotEmpty; + + log( + 'emailValid: $emailValid, passwordValid: $passwordValid, email: ${state.email}, password: ${state.password}', + ); + + if (emailValid && passwordValid) { + failureOrLogin = await _authRepository.login( + email: state.email, + password: state.password, + ); + + emit( + state.copyWith( + isSubmitting: false, + failureOrLoginOption: optionOf(failureOrLogin), + ), + ); + } + emit(state.copyWith(showErrorMessages: true, isSubmitting: false)); + }, + ); + } +} diff --git a/lib/application/auth/bloc/login_form_bloc.freezed.dart b/lib/application/auth/bloc/login_form_bloc.freezed.dart new file mode 100644 index 0000000..ec41a8f --- /dev/null +++ b/lib/application/auth/bloc/login_form_bloc.freezed.dart @@ -0,0 +1,734 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// 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 'login_form_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$LoginFormEvent { + @optionalTypeArgs + TResult when({ + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + required TResult Function() submitted, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(String email)? emailChanged, + TResult? Function(String password)? passwordChanged, + TResult? Function()? submitted, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + TResult Function()? submitted, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_EmailChanged value) emailChanged, + required TResult Function(_PasswordChanged value) passwordChanged, + required TResult Function(_Submitted value) submitted, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_EmailChanged value)? emailChanged, + TResult? Function(_PasswordChanged value)? passwordChanged, + TResult? Function(_Submitted value)? submitted, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_EmailChanged value)? emailChanged, + TResult Function(_PasswordChanged value)? passwordChanged, + TResult Function(_Submitted value)? submitted, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LoginFormEventCopyWith<$Res> { + factory $LoginFormEventCopyWith( + LoginFormEvent value, + $Res Function(LoginFormEvent) then, + ) = _$LoginFormEventCopyWithImpl<$Res, LoginFormEvent>; +} + +/// @nodoc +class _$LoginFormEventCopyWithImpl<$Res, $Val extends LoginFormEvent> + implements $LoginFormEventCopyWith<$Res> { + _$LoginFormEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$EmailChangedImplCopyWith<$Res> { + factory _$$EmailChangedImplCopyWith( + _$EmailChangedImpl value, + $Res Function(_$EmailChangedImpl) then, + ) = __$$EmailChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({String email}); +} + +/// @nodoc +class __$$EmailChangedImplCopyWithImpl<$Res> + extends _$LoginFormEventCopyWithImpl<$Res, _$EmailChangedImpl> + implements _$$EmailChangedImplCopyWith<$Res> { + __$$EmailChangedImplCopyWithImpl( + _$EmailChangedImpl _value, + $Res Function(_$EmailChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? email = null}) { + return _then( + _$EmailChangedImpl( + null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$EmailChangedImpl implements _EmailChanged { + const _$EmailChangedImpl(this.email); + + @override + final String email; + + @override + String toString() { + return 'LoginFormEvent.emailChanged(email: $email)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$EmailChangedImpl && + (identical(other.email, email) || other.email == email)); + } + + @override + int get hashCode => Object.hash(runtimeType, email); + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$EmailChangedImplCopyWith<_$EmailChangedImpl> get copyWith => + __$$EmailChangedImplCopyWithImpl<_$EmailChangedImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + required TResult Function() submitted, + }) { + return emailChanged(email); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(String email)? emailChanged, + TResult? Function(String password)? passwordChanged, + TResult? Function()? submitted, + }) { + return emailChanged?.call(email); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + TResult Function()? submitted, + required TResult orElse(), + }) { + if (emailChanged != null) { + return emailChanged(email); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_EmailChanged value) emailChanged, + required TResult Function(_PasswordChanged value) passwordChanged, + required TResult Function(_Submitted value) submitted, + }) { + return emailChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_EmailChanged value)? emailChanged, + TResult? Function(_PasswordChanged value)? passwordChanged, + TResult? Function(_Submitted value)? submitted, + }) { + return emailChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_EmailChanged value)? emailChanged, + TResult Function(_PasswordChanged value)? passwordChanged, + TResult Function(_Submitted value)? submitted, + required TResult orElse(), + }) { + if (emailChanged != null) { + return emailChanged(this); + } + return orElse(); + } +} + +abstract class _EmailChanged implements LoginFormEvent { + const factory _EmailChanged(final String email) = _$EmailChangedImpl; + + String get email; + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$EmailChangedImplCopyWith<_$EmailChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$PasswordChangedImplCopyWith<$Res> { + factory _$$PasswordChangedImplCopyWith( + _$PasswordChangedImpl value, + $Res Function(_$PasswordChangedImpl) then, + ) = __$$PasswordChangedImplCopyWithImpl<$Res>; + @useResult + $Res call({String password}); +} + +/// @nodoc +class __$$PasswordChangedImplCopyWithImpl<$Res> + extends _$LoginFormEventCopyWithImpl<$Res, _$PasswordChangedImpl> + implements _$$PasswordChangedImplCopyWith<$Res> { + __$$PasswordChangedImplCopyWithImpl( + _$PasswordChangedImpl _value, + $Res Function(_$PasswordChangedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? password = null}) { + return _then( + _$PasswordChangedImpl( + null == password + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$PasswordChangedImpl implements _PasswordChanged { + const _$PasswordChangedImpl(this.password); + + @override + final String password; + + @override + String toString() { + return 'LoginFormEvent.passwordChanged(password: $password)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$PasswordChangedImpl && + (identical(other.password, password) || + other.password == password)); + } + + @override + int get hashCode => Object.hash(runtimeType, password); + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$PasswordChangedImplCopyWith<_$PasswordChangedImpl> get copyWith => + __$$PasswordChangedImplCopyWithImpl<_$PasswordChangedImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + required TResult Function() submitted, + }) { + return passwordChanged(password); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(String email)? emailChanged, + TResult? Function(String password)? passwordChanged, + TResult? Function()? submitted, + }) { + return passwordChanged?.call(password); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + TResult Function()? submitted, + required TResult orElse(), + }) { + if (passwordChanged != null) { + return passwordChanged(password); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_EmailChanged value) emailChanged, + required TResult Function(_PasswordChanged value) passwordChanged, + required TResult Function(_Submitted value) submitted, + }) { + return passwordChanged(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_EmailChanged value)? emailChanged, + TResult? Function(_PasswordChanged value)? passwordChanged, + TResult? Function(_Submitted value)? submitted, + }) { + return passwordChanged?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_EmailChanged value)? emailChanged, + TResult Function(_PasswordChanged value)? passwordChanged, + TResult Function(_Submitted value)? submitted, + required TResult orElse(), + }) { + if (passwordChanged != null) { + return passwordChanged(this); + } + return orElse(); + } +} + +abstract class _PasswordChanged implements LoginFormEvent { + const factory _PasswordChanged(final String password) = _$PasswordChangedImpl; + + String get password; + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$PasswordChangedImplCopyWith<_$PasswordChangedImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$SubmittedImplCopyWith<$Res> { + factory _$$SubmittedImplCopyWith( + _$SubmittedImpl value, + $Res Function(_$SubmittedImpl) then, + ) = __$$SubmittedImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$SubmittedImplCopyWithImpl<$Res> + extends _$LoginFormEventCopyWithImpl<$Res, _$SubmittedImpl> + implements _$$SubmittedImplCopyWith<$Res> { + __$$SubmittedImplCopyWithImpl( + _$SubmittedImpl _value, + $Res Function(_$SubmittedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of LoginFormEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$SubmittedImpl implements _Submitted { + const _$SubmittedImpl(); + + @override + String toString() { + return 'LoginFormEvent.submitted()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$SubmittedImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + required TResult Function() submitted, + }) { + return submitted(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(String email)? emailChanged, + TResult? Function(String password)? passwordChanged, + TResult? Function()? submitted, + }) { + return submitted?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + TResult Function()? submitted, + required TResult orElse(), + }) { + if (submitted != null) { + return submitted(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_EmailChanged value) emailChanged, + required TResult Function(_PasswordChanged value) passwordChanged, + required TResult Function(_Submitted value) submitted, + }) { + return submitted(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_EmailChanged value)? emailChanged, + TResult? Function(_PasswordChanged value)? passwordChanged, + TResult? Function(_Submitted value)? submitted, + }) { + return submitted?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_EmailChanged value)? emailChanged, + TResult Function(_PasswordChanged value)? passwordChanged, + TResult Function(_Submitted value)? submitted, + required TResult orElse(), + }) { + if (submitted != null) { + return submitted(this); + } + return orElse(); + } +} + +abstract class _Submitted implements LoginFormEvent { + const factory _Submitted() = _$SubmittedImpl; +} + +/// @nodoc +mixin _$LoginFormState { + String get email => throw _privateConstructorUsedError; + String get password => throw _privateConstructorUsedError; + Option> get failureOrLoginOption => + throw _privateConstructorUsedError; + bool get isSubmitting => throw _privateConstructorUsedError; + bool get showErrorMessages => throw _privateConstructorUsedError; + + /// Create a copy of LoginFormState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $LoginFormStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LoginFormStateCopyWith<$Res> { + factory $LoginFormStateCopyWith( + LoginFormState value, + $Res Function(LoginFormState) then, + ) = _$LoginFormStateCopyWithImpl<$Res, LoginFormState>; + @useResult + $Res call({ + String email, + String password, + Option> failureOrLoginOption, + bool isSubmitting, + bool showErrorMessages, + }); +} + +/// @nodoc +class _$LoginFormStateCopyWithImpl<$Res, $Val extends LoginFormState> + implements $LoginFormStateCopyWith<$Res> { + _$LoginFormStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of LoginFormState + /// 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? failureOrLoginOption = null, + Object? isSubmitting = null, + Object? showErrorMessages = null, + }) { + return _then( + _value.copyWith( + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + password: null == password + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String, + failureOrLoginOption: null == failureOrLoginOption + ? _value.failureOrLoginOption + : failureOrLoginOption // ignore: cast_nullable_to_non_nullable + as Option>, + isSubmitting: null == isSubmitting + ? _value.isSubmitting + : isSubmitting // ignore: cast_nullable_to_non_nullable + as bool, + showErrorMessages: null == showErrorMessages + ? _value.showErrorMessages + : showErrorMessages // ignore: cast_nullable_to_non_nullable + as bool, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$LoginFormStateImplCopyWith<$Res> + implements $LoginFormStateCopyWith<$Res> { + factory _$$LoginFormStateImplCopyWith( + _$LoginFormStateImpl value, + $Res Function(_$LoginFormStateImpl) then, + ) = __$$LoginFormStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + String email, + String password, + Option> failureOrLoginOption, + bool isSubmitting, + bool showErrorMessages, + }); +} + +/// @nodoc +class __$$LoginFormStateImplCopyWithImpl<$Res> + extends _$LoginFormStateCopyWithImpl<$Res, _$LoginFormStateImpl> + implements _$$LoginFormStateImplCopyWith<$Res> { + __$$LoginFormStateImplCopyWithImpl( + _$LoginFormStateImpl _value, + $Res Function(_$LoginFormStateImpl) _then, + ) : super(_value, _then); + + /// Create a copy of LoginFormState + /// 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? failureOrLoginOption = null, + Object? isSubmitting = null, + Object? showErrorMessages = null, + }) { + return _then( + _$LoginFormStateImpl( + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + password: null == password + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String, + failureOrLoginOption: null == failureOrLoginOption + ? _value.failureOrLoginOption + : failureOrLoginOption // ignore: cast_nullable_to_non_nullable + as Option>, + isSubmitting: null == isSubmitting + ? _value.isSubmitting + : isSubmitting // ignore: cast_nullable_to_non_nullable + as bool, + showErrorMessages: null == showErrorMessages + ? _value.showErrorMessages + : showErrorMessages // ignore: cast_nullable_to_non_nullable + as bool, + ), + ); + } +} + +/// @nodoc + +class _$LoginFormStateImpl implements _LoginFormState { + const _$LoginFormStateImpl({ + required this.email, + required this.password, + required this.failureOrLoginOption, + this.isSubmitting = false, + this.showErrorMessages = false, + }); + + @override + final String email; + @override + final String password; + @override + final Option> failureOrLoginOption; + @override + @JsonKey() + final bool isSubmitting; + @override + @JsonKey() + final bool showErrorMessages; + + @override + String toString() { + return 'LoginFormState(email: $email, password: $password, failureOrLoginOption: $failureOrLoginOption, isSubmitting: $isSubmitting, showErrorMessages: $showErrorMessages)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoginFormStateImpl && + (identical(other.email, email) || other.email == email) && + (identical(other.password, password) || + other.password == password) && + (identical(other.failureOrLoginOption, failureOrLoginOption) || + other.failureOrLoginOption == failureOrLoginOption) && + (identical(other.isSubmitting, isSubmitting) || + other.isSubmitting == isSubmitting) && + (identical(other.showErrorMessages, showErrorMessages) || + other.showErrorMessages == showErrorMessages)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + email, + password, + failureOrLoginOption, + isSubmitting, + showErrorMessages, + ); + + /// Create a copy of LoginFormState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$LoginFormStateImplCopyWith<_$LoginFormStateImpl> get copyWith => + __$$LoginFormStateImplCopyWithImpl<_$LoginFormStateImpl>( + this, + _$identity, + ); +} + +abstract class _LoginFormState implements LoginFormState { + const factory _LoginFormState({ + required final String email, + required final String password, + required final Option> failureOrLoginOption, + final bool isSubmitting, + final bool showErrorMessages, + }) = _$LoginFormStateImpl; + + @override + String get email; + @override + String get password; + @override + Option> get failureOrLoginOption; + @override + bool get isSubmitting; + @override + bool get showErrorMessages; + + /// Create a copy of LoginFormState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$LoginFormStateImplCopyWith<_$LoginFormStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/auth/bloc/login_form_event.dart b/lib/application/auth/bloc/login_form_event.dart new file mode 100644 index 0000000..f15ee8a --- /dev/null +++ b/lib/application/auth/bloc/login_form_event.dart @@ -0,0 +1,9 @@ +part of 'login_form_bloc.dart'; + +@freezed +class LoginFormEvent with _$LoginFormEvent { + const factory LoginFormEvent.emailChanged(String email) = _EmailChanged; + const factory LoginFormEvent.passwordChanged(String password) = + _PasswordChanged; + const factory LoginFormEvent.submitted() = _Submitted; +} diff --git a/lib/application/auth/bloc/login_form_state.dart b/lib/application/auth/bloc/login_form_state.dart new file mode 100644 index 0000000..528e932 --- /dev/null +++ b/lib/application/auth/bloc/login_form_state.dart @@ -0,0 +1,15 @@ +part of 'login_form_bloc.dart'; + +@freezed +class LoginFormState with _$LoginFormState { + const factory LoginFormState({ + required String email, + required String password, + required Option> failureOrLoginOption, + @Default(false) bool isSubmitting, + @Default(false) bool showErrorMessages, + }) = _LoginFormState; + + factory LoginFormState.initial() => + LoginFormState(email: '', password: '', failureOrLoginOption: none()); +} diff --git a/lib/common/api/api_client.dart b/lib/common/api/api_client.dart index 74f74f2..0584967 100644 --- a/lib/common/api/api_client.dart +++ b/lib/common/api/api_client.dart @@ -28,6 +28,8 @@ class ApiClient { ApiClient(this._dio, this._env) { _dio.options.baseUrl = _env.baseUrl; _dio.options.connectTimeout = const Duration(seconds: 20); + _dio.options.validateStatus = (status) => + status != null && status >= 200 && status < 500; _dio.interceptors.add(BadNetworkErrorInterceptor()); _dio.interceptors.add(BadRequestErrorInterceptor()); _dio.interceptors.add(InternalServerErrorInterceptor()); diff --git a/lib/common/constant/app_constant.dart b/lib/common/constant/app_constant.dart index b0dbc06..8705613 100644 --- a/lib/common/constant/app_constant.dart +++ b/lib/common/constant/app_constant.dart @@ -1,3 +1,3 @@ class AppConstant { - static const String appName = ""; + static const String appName = "Apskel POS"; } diff --git a/lib/common/constant/local_storage_key.dart b/lib/common/constant/local_storage_key.dart new file mode 100644 index 0000000..aebb90f --- /dev/null +++ b/lib/common/constant/local_storage_key.dart @@ -0,0 +1,4 @@ +class LocalStorageKey { + static const String token = 'token'; + static const String user = 'user'; +} diff --git a/lib/common/theme/app_color.dart b/lib/common/theme/app_color.dart index e90e46a..ab8f9b2 100644 --- a/lib/common/theme/app_color.dart +++ b/lib/common/theme/app_color.dart @@ -1,46 +1,66 @@ part of 'theme.dart'; class AppColor { - /// primary = #3949AB - static const Color primary = Color(0xff36175e); + // Primary Colors + static const Color primary = Color(0xFF36175e); + static const Color primaryLight = Color(0xFF5a2d85); + static const Color primaryDark = Color(0xFF1e0d35); - /// grey = #B7B7B7 - static const Color grey = Color(0xffB7B7B7); + // Secondary Colors + static const Color secondary = Color(0xFF4CAF50); + static const Color secondaryLight = Color(0xFF81C784); + static const Color secondaryDark = Color(0xFF388E3C); - /// light = #F8F5FF - static const Color light = Color(0xffF8F5FF); + // Background Colors + static const Color background = Color(0xFFF8F9FA); + static const Color backgroundLight = Color(0xFFFFFFFF); + static const Color backgroundDark = Color(0xFF1A1A1A); + static const Color surface = Color(0xFFFFFFFF); + static const Color surfaceDark = Color(0xFF2D2D2D); - /// blueLight = #C7D0EB - static const Color blueLight = Color(0xffC7D0EB); + // Text Colors + static const Color textPrimary = Color(0xFF212121); + static const Color textSecondary = Color(0xFF757575); + static const Color textLight = Color(0xFFBDBDBD); + static const Color textWhite = Color(0xFFFFFFFF); - /// black = #000000 - static const Color black = Color(0xff000000); + // Status Colors + static const Color success = Color(0xFF4CAF50); + static const Color error = Color(0xFFE53E3E); + static const Color warning = Color(0xFFFF9800); + static const Color info = Color(0xFF2196F3); - /// white = #FFFFFF - static const Color white = Color(0xffFFFFFF); - static const Color whiteText = Color(0xfff1eaf9); + // Border Colors + static const Color border = Color(0xFFE0E0E0); + static const Color borderLight = Color(0xFFF0F0F0); + static const Color borderDark = Color(0xFFBDBDBD); - /// green = #50C474 - static const Color green = Color(0xff50C474); + // Basic Color + static const Color white = Color(0xFFFFFFFF); + static const Color black = Color(0xFF000000); - /// red = #F4261A - static const Color red = Color(0xffF4261A); + // Gradient Colors + static const List primaryGradient = [ + Color(0xFF36175e), + Color(0xFF5a2d85), + ]; - /// card = #E5E5E5 - static const Color card = Color(0xffE5E5E5); + static const List successGradient = [ + Color(0xFF4CAF50), + Color(0xFF81C784), + ]; - /// disabled = #C8D1E1 - static const Color disabled = Color(0xffC8D1E1); + static const List backgroundGradient = [ + Color(0xFFF5F5F5), + Color(0xFFE8E8E8), + ]; - /// subtitle = #7890CD - static const Color subtitle = Color(0xff7890CD); - - /// stroke = #EFF0F6 - static const Color stroke = Color(0xffEFF0F6); - - static const Color background = Color.fromARGB(255, 241, 241, 241); - - static const Color primaryLight = Color(0xFF5A3E8A); - static const Color greyLight = Color(0xFFE0E0E0); - static const Color greyDark = Color(0xFF707070); + // Opacity Variations + static Color primaryWithOpacity(double opacity) => + primary.withOpacity(opacity); + static Color successWithOpacity(double opacity) => + success.withOpacity(opacity); + static Color errorWithOpacity(double opacity) => error.withOpacity(opacity); + static Color warningWithOpacity(double opacity) => + warning.withOpacity(opacity); } diff --git a/lib/common/theme/app_style.dart b/lib/common/theme/app_style.dart index 5c422cc..45c1016 100644 --- a/lib/common/theme/app_style.dart +++ b/lib/common/theme/app_style.dart @@ -1,5 +1,27 @@ part of 'theme.dart'; class AppStyle { - // TODO: define style + static TextStyle xs = TextStyle(color: AppColor.black, fontSize: 11); + + static TextStyle sm = TextStyle(color: AppColor.black, fontSize: 12); + + static TextStyle md = TextStyle(color: AppColor.black, fontSize: 14); + + static TextStyle lg = TextStyle(color: AppColor.black, fontSize: 16); + + static TextStyle xl = TextStyle(color: AppColor.black, fontSize: 18); + + static TextStyle xxl = TextStyle(color: AppColor.black, fontSize: 20); + + static TextStyle h6 = TextStyle(color: AppColor.black, fontSize: 22); + + static TextStyle h5 = TextStyle(color: AppColor.black, fontSize: 24); + + static TextStyle h4 = TextStyle(color: AppColor.black, fontSize: 26); + + static TextStyle h3 = TextStyle(color: AppColor.black, fontSize: 28); + + static TextStyle h2 = TextStyle(color: AppColor.black, fontSize: 30); + + static TextStyle h1 = TextStyle(color: AppColor.black, fontSize: 32); } diff --git a/lib/common/theme/theme.dart b/lib/common/theme/theme.dart index 24976d7..b621835 100644 --- a/lib/common/theme/theme.dart +++ b/lib/common/theme/theme.dart @@ -25,10 +25,10 @@ class ThemeApp { colorScheme: ColorScheme.fromSeed(seedColor: AppColor.primary), inputDecorationTheme: InputDecorationTheme( contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), - hintStyle: const TextStyle(color: AppColor.grey), + hintStyle: const TextStyle(color: AppColor.textSecondary), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), - borderSide: BorderSide(color: AppColor.primary), + borderSide: BorderSide(color: AppColor.borderDark), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), @@ -36,7 +36,11 @@ class ThemeApp { ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), - borderSide: BorderSide(color: AppColor.primary), + borderSide: BorderSide(color: AppColor.borderDark), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: BorderSide(color: AppColor.error), ), ), ); diff --git a/lib/common/url/api_path.dart b/lib/common/url/api_path.dart new file mode 100644 index 0000000..78543f2 --- /dev/null +++ b/lib/common/url/api_path.dart @@ -0,0 +1,3 @@ +class ApiPath { + static const String login = '/api/v1/auth/login'; +} diff --git a/lib/common/validator/validator.dart b/lib/common/validator/validator.dart new file mode 100644 index 0000000..63589f0 --- /dev/null +++ b/lib/common/validator/validator.dart @@ -0,0 +1,28 @@ +class AppValidator { + static String? validateEmail(String? value) { + if (value == null || value.isEmpty) { + return 'Email wajib diisi'; + } + final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegex.hasMatch(value)) { + return 'Format email tidak valid'; + } + return null; + } + + static String? validatePassword(String? value) { + if (value == null || value.isEmpty) { + return 'Password wajib diisi'; + } + if (value.length < 8) { + return 'Password minimal 8 karakter'; + } + // if (!RegExp(r'[A-Z]').hasMatch(value)) { + // return 'Password harus mengandung huruf besar'; + // } + // if (!RegExp(r'[0-9]').hasMatch(value)) { + // return 'Password harus mengandung angka'; + // } + return null; + } +} diff --git a/lib/domain/auth/auth.dart b/lib/domain/auth/auth.dart new file mode 100644 index 0000000..fbe867c --- /dev/null +++ b/lib/domain/auth/auth.dart @@ -0,0 +1,11 @@ +import 'package:dartz/dartz.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../common/api/api_failure.dart'; + +part 'auth.freezed.dart'; + +part 'entities/login_entity.dart'; +part 'entities/user_entity.dart'; +part 'failures/auth_failure.dart'; +part 'repositories/i_auth_repository.dart'; diff --git a/lib/domain/auth/auth.freezed.dart b/lib/domain/auth/auth.freezed.dart new file mode 100644 index 0000000..d3a0fd3 --- /dev/null +++ b/lib/domain/auth/auth.freezed.dart @@ -0,0 +1,1095 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// 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 'auth.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$Login { + String get token => throw _privateConstructorUsedError; + String get refreshToken => throw _privateConstructorUsedError; + String get expiresAt => throw _privateConstructorUsedError; + String get refreshExpiresAt => throw _privateConstructorUsedError; + User get user => throw _privateConstructorUsedError; + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $LoginCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LoginCopyWith<$Res> { + factory $LoginCopyWith(Login value, $Res Function(Login) then) = + _$LoginCopyWithImpl<$Res, Login>; + @useResult + $Res call({ + String token, + String refreshToken, + String expiresAt, + String refreshExpiresAt, + User user, + }); + + $UserCopyWith<$Res> get user; +} + +/// @nodoc +class _$LoginCopyWithImpl<$Res, $Val extends Login> + implements $LoginCopyWith<$Res> { + _$LoginCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? token = null, + Object? refreshToken = null, + Object? expiresAt = null, + Object? refreshExpiresAt = null, + Object? user = null, + }) { + return _then( + _value.copyWith( + token: null == token + ? _value.token + : token // ignore: cast_nullable_to_non_nullable + as String, + refreshToken: null == refreshToken + ? _value.refreshToken + : refreshToken // ignore: cast_nullable_to_non_nullable + as String, + expiresAt: null == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as String, + refreshExpiresAt: null == refreshExpiresAt + ? _value.refreshExpiresAt + : refreshExpiresAt // ignore: cast_nullable_to_non_nullable + as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as User, + ) + as $Val, + ); + } + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserCopyWith<$Res> get user { + return $UserCopyWith<$Res>(_value.user, (value) { + return _then(_value.copyWith(user: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$LoginImplCopyWith<$Res> implements $LoginCopyWith<$Res> { + factory _$$LoginImplCopyWith( + _$LoginImpl value, + $Res Function(_$LoginImpl) then, + ) = __$$LoginImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + String token, + String refreshToken, + String expiresAt, + String refreshExpiresAt, + User user, + }); + + @override + $UserCopyWith<$Res> get user; +} + +/// @nodoc +class __$$LoginImplCopyWithImpl<$Res> + extends _$LoginCopyWithImpl<$Res, _$LoginImpl> + implements _$$LoginImplCopyWith<$Res> { + __$$LoginImplCopyWithImpl( + _$LoginImpl _value, + $Res Function(_$LoginImpl) _then, + ) : super(_value, _then); + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? token = null, + Object? refreshToken = null, + Object? expiresAt = null, + Object? refreshExpiresAt = null, + Object? user = null, + }) { + return _then( + _$LoginImpl( + token: null == token + ? _value.token + : token // ignore: cast_nullable_to_non_nullable + as String, + refreshToken: null == refreshToken + ? _value.refreshToken + : refreshToken // ignore: cast_nullable_to_non_nullable + as String, + expiresAt: null == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as String, + refreshExpiresAt: null == refreshExpiresAt + ? _value.refreshExpiresAt + : refreshExpiresAt // ignore: cast_nullable_to_non_nullable + as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as User, + ), + ); + } +} + +/// @nodoc + +class _$LoginImpl implements _Login { + const _$LoginImpl({ + required this.token, + required this.refreshToken, + required this.expiresAt, + required this.refreshExpiresAt, + required this.user, + }); + + @override + final String token; + @override + final String refreshToken; + @override + final String expiresAt; + @override + final String refreshExpiresAt; + @override + final User user; + + @override + String toString() { + return 'Login(token: $token, refreshToken: $refreshToken, expiresAt: $expiresAt, refreshExpiresAt: $refreshExpiresAt, user: $user)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoginImpl && + (identical(other.token, token) || other.token == token) && + (identical(other.refreshToken, refreshToken) || + other.refreshToken == refreshToken) && + (identical(other.expiresAt, expiresAt) || + other.expiresAt == expiresAt) && + (identical(other.refreshExpiresAt, refreshExpiresAt) || + other.refreshExpiresAt == refreshExpiresAt) && + (identical(other.user, user) || other.user == user)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + token, + refreshToken, + expiresAt, + refreshExpiresAt, + user, + ); + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$LoginImplCopyWith<_$LoginImpl> get copyWith => + __$$LoginImplCopyWithImpl<_$LoginImpl>(this, _$identity); +} + +abstract class _Login implements Login { + const factory _Login({ + required final String token, + required final String refreshToken, + required final String expiresAt, + required final String refreshExpiresAt, + required final User user, + }) = _$LoginImpl; + + @override + String get token; + @override + String get refreshToken; + @override + String get expiresAt; + @override + String get refreshExpiresAt; + @override + User get user; + + /// Create a copy of Login + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$LoginImplCopyWith<_$LoginImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +mixin _$User { + String get id => throw _privateConstructorUsedError; + String get organizationId => throw _privateConstructorUsedError; + String get outletId => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get email => throw _privateConstructorUsedError; + String get role => throw _privateConstructorUsedError; + Map get permissions => throw _privateConstructorUsedError; + bool get isActive => throw _privateConstructorUsedError; + String get createdAt => throw _privateConstructorUsedError; + String get updatedAt => throw _privateConstructorUsedError; + + /// Create a copy of User + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $UserCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserCopyWith<$Res> { + factory $UserCopyWith(User value, $Res Function(User) then) = + _$UserCopyWithImpl<$Res, User>; + @useResult + $Res call({ + String id, + String organizationId, + String outletId, + String name, + String email, + String role, + Map permissions, + bool isActive, + String createdAt, + String updatedAt, + }); +} + +/// @nodoc +class _$UserCopyWithImpl<$Res, $Val extends User> + implements $UserCopyWith<$Res> { + _$UserCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of User + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? organizationId = null, + Object? outletId = null, + Object? name = null, + Object? email = null, + Object? role = null, + Object? permissions = null, + Object? isActive = null, + Object? createdAt = null, + Object? updatedAt = null, + }) { + return _then( + _value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + organizationId: null == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String, + outletId: null == outletId + ? _value.outletId + : outletId // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String, + permissions: null == permissions + ? _value.permissions + : permissions // ignore: cast_nullable_to_non_nullable + as Map, + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as String, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> { + factory _$$UserImplCopyWith( + _$UserImpl value, + $Res Function(_$UserImpl) then, + ) = __$$UserImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + String id, + String organizationId, + String outletId, + String name, + String email, + String role, + Map permissions, + bool isActive, + String createdAt, + String updatedAt, + }); +} + +/// @nodoc +class __$$UserImplCopyWithImpl<$Res> + extends _$UserCopyWithImpl<$Res, _$UserImpl> + implements _$$UserImplCopyWith<$Res> { + __$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then) + : super(_value, _then); + + /// Create a copy of User + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? organizationId = null, + Object? outletId = null, + Object? name = null, + Object? email = null, + Object? role = null, + Object? permissions = null, + Object? isActive = null, + Object? createdAt = null, + Object? updatedAt = null, + }) { + return _then( + _$UserImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + organizationId: null == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String, + outletId: null == outletId + ? _value.outletId + : outletId // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + email: null == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String, + permissions: null == permissions + ? _value._permissions + : permissions // ignore: cast_nullable_to_non_nullable + as Map, + isActive: null == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as String, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$UserImpl implements _User { + const _$UserImpl({ + required this.id, + required this.organizationId, + required this.outletId, + required this.name, + required this.email, + required this.role, + required final Map permissions, + required this.isActive, + required this.createdAt, + required this.updatedAt, + }) : _permissions = permissions; + + @override + final String id; + @override + final String organizationId; + @override + final String outletId; + @override + final String name; + @override + final String email; + @override + final String role; + final Map _permissions; + @override + Map get permissions { + if (_permissions is EqualUnmodifiableMapView) return _permissions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_permissions); + } + + @override + final bool isActive; + @override + final String createdAt; + @override + final String updatedAt; + + @override + String toString() { + return 'User(id: $id, organizationId: $organizationId, outletId: $outletId, name: $name, email: $email, role: $role, permissions: $permissions, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.organizationId, organizationId) || + other.organizationId == organizationId) && + (identical(other.outletId, outletId) || + other.outletId == outletId) && + (identical(other.name, name) || other.name == name) && + (identical(other.email, email) || other.email == email) && + (identical(other.role, role) || other.role == role) && + const DeepCollectionEquality().equals( + other._permissions, + _permissions, + ) && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + organizationId, + outletId, + name, + email, + role, + const DeepCollectionEquality().hash(_permissions), + isActive, + createdAt, + updatedAt, + ); + + /// Create a copy of User + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$UserImplCopyWith<_$UserImpl> get copyWith => + __$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity); +} + +abstract class _User implements User { + const factory _User({ + required final String id, + required final String organizationId, + required final String outletId, + required final String name, + required final String email, + required final String role, + required final Map permissions, + required final bool isActive, + required final String createdAt, + required final String updatedAt, + }) = _$UserImpl; + + @override + String get id; + @override + String get organizationId; + @override + String get outletId; + @override + String get name; + @override + String get email; + @override + String get role; + @override + Map get permissions; + @override + bool get isActive; + @override + String get createdAt; + @override + String get updatedAt; + + /// Create a copy of User + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$UserImplCopyWith<_$UserImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +mixin _$AuthFailure { + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AuthFailureCopyWith<$Res> { + factory $AuthFailureCopyWith( + AuthFailure value, + $Res Function(AuthFailure) then, + ) = _$AuthFailureCopyWithImpl<$Res, AuthFailure>; +} + +/// @nodoc +class _$AuthFailureCopyWithImpl<$Res, $Val extends AuthFailure> + implements $AuthFailureCopyWith<$Res> { + _$AuthFailureCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$ServerErrorImplCopyWith<$Res> { + factory _$$ServerErrorImplCopyWith( + _$ServerErrorImpl value, + $Res Function(_$ServerErrorImpl) then, + ) = __$$ServerErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({ApiFailure failure}); + + $ApiFailureCopyWith<$Res> get failure; +} + +/// @nodoc +class __$$ServerErrorImplCopyWithImpl<$Res> + extends _$AuthFailureCopyWithImpl<$Res, _$ServerErrorImpl> + implements _$$ServerErrorImplCopyWith<$Res> { + __$$ServerErrorImplCopyWithImpl( + _$ServerErrorImpl _value, + $Res Function(_$ServerErrorImpl) _then, + ) : super(_value, _then); + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? failure = null}) { + return _then( + _$ServerErrorImpl( + null == failure + ? _value.failure + : failure // ignore: cast_nullable_to_non_nullable + as ApiFailure, + ), + ); + } + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $ApiFailureCopyWith<$Res> get failure { + return $ApiFailureCopyWith<$Res>(_value.failure, (value) { + return _then(_value.copyWith(failure: value)); + }); + } +} + +/// @nodoc + +class _$ServerErrorImpl implements _ServerError { + const _$ServerErrorImpl(this.failure); + + @override + final ApiFailure failure; + + @override + String toString() { + return 'AuthFailure.serverError(failure: $failure)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ServerErrorImpl && + (identical(other.failure, failure) || other.failure == failure)); + } + + @override + int get hashCode => Object.hash(runtimeType, failure); + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith => + __$$ServerErrorImplCopyWithImpl<_$ServerErrorImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return serverError(failure); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return serverError?.call(failure); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (serverError != null) { + return serverError(failure); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return serverError(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return serverError?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (serverError != null) { + return serverError(this); + } + return orElse(); + } +} + +abstract class _ServerError implements AuthFailure { + const factory _ServerError(final ApiFailure failure) = _$ServerErrorImpl; + + ApiFailure get failure; + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$UnexpectedErrorImplCopyWith<$Res> { + factory _$$UnexpectedErrorImplCopyWith( + _$UnexpectedErrorImpl value, + $Res Function(_$UnexpectedErrorImpl) then, + ) = __$$UnexpectedErrorImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$UnexpectedErrorImplCopyWithImpl<$Res> + extends _$AuthFailureCopyWithImpl<$Res, _$UnexpectedErrorImpl> + implements _$$UnexpectedErrorImplCopyWith<$Res> { + __$$UnexpectedErrorImplCopyWithImpl( + _$UnexpectedErrorImpl _value, + $Res Function(_$UnexpectedErrorImpl) _then, + ) : super(_value, _then); + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$UnexpectedErrorImpl implements _UnexpectedError { + const _$UnexpectedErrorImpl(); + + @override + String toString() { + return 'AuthFailure.unexpectedError()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$UnexpectedErrorImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return unexpectedError(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return unexpectedError?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (unexpectedError != null) { + return unexpectedError(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return unexpectedError(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return unexpectedError?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (unexpectedError != null) { + return unexpectedError(this); + } + return orElse(); + } +} + +abstract class _UnexpectedError implements AuthFailure { + const factory _UnexpectedError() = _$UnexpectedErrorImpl; +} + +/// @nodoc +abstract class _$$DynamicErrorMessageImplCopyWith<$Res> { + factory _$$DynamicErrorMessageImplCopyWith( + _$DynamicErrorMessageImpl value, + $Res Function(_$DynamicErrorMessageImpl) then, + ) = __$$DynamicErrorMessageImplCopyWithImpl<$Res>; + @useResult + $Res call({String erroMessage}); +} + +/// @nodoc +class __$$DynamicErrorMessageImplCopyWithImpl<$Res> + extends _$AuthFailureCopyWithImpl<$Res, _$DynamicErrorMessageImpl> + implements _$$DynamicErrorMessageImplCopyWith<$Res> { + __$$DynamicErrorMessageImplCopyWithImpl( + _$DynamicErrorMessageImpl _value, + $Res Function(_$DynamicErrorMessageImpl) _then, + ) : super(_value, _then); + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? erroMessage = null}) { + return _then( + _$DynamicErrorMessageImpl( + null == erroMessage + ? _value.erroMessage + : erroMessage // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$DynamicErrorMessageImpl implements _DynamicErrorMessage { + const _$DynamicErrorMessageImpl(this.erroMessage); + + @override + final String erroMessage; + + @override + String toString() { + return 'AuthFailure.dynamicErrorMessage(erroMessage: $erroMessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DynamicErrorMessageImpl && + (identical(other.erroMessage, erroMessage) || + other.erroMessage == erroMessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, erroMessage); + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith => + __$$DynamicErrorMessageImplCopyWithImpl<_$DynamicErrorMessageImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return dynamicErrorMessage(erroMessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return dynamicErrorMessage?.call(erroMessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (dynamicErrorMessage != null) { + return dynamicErrorMessage(erroMessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return dynamicErrorMessage(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return dynamicErrorMessage?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (dynamicErrorMessage != null) { + return dynamicErrorMessage(this); + } + return orElse(); + } +} + +abstract class _DynamicErrorMessage implements AuthFailure { + const factory _DynamicErrorMessage(final String erroMessage) = + _$DynamicErrorMessageImpl; + + String get erroMessage; + + /// Create a copy of AuthFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/auth/entities/login_entity.dart b/lib/domain/auth/entities/login_entity.dart new file mode 100644 index 0000000..e30c209 --- /dev/null +++ b/lib/domain/auth/entities/login_entity.dart @@ -0,0 +1,20 @@ +part of '../auth.dart'; + +@freezed +class Login with _$Login { + const factory Login({ + required String token, + required String refreshToken, + required String expiresAt, + required String refreshExpiresAt, + required User user, + }) = _Login; + + factory Login.empty() => Login( + token: '', + refreshToken: '', + expiresAt: '', + refreshExpiresAt: '', + user: User.empty(), + ); +} diff --git a/lib/domain/auth/entities/user_entity.dart b/lib/domain/auth/entities/user_entity.dart new file mode 100644 index 0000000..3f6cf70 --- /dev/null +++ b/lib/domain/auth/entities/user_entity.dart @@ -0,0 +1,30 @@ +part of '../auth.dart'; + +@freezed +class User with _$User { + const factory User({ + required String id, + required String organizationId, + required String outletId, + required String name, + required String email, + required String role, + required Map permissions, + required bool isActive, + required String createdAt, + required String updatedAt, + }) = _User; + + factory User.empty() => const User( + id: '', + organizationId: '', + outletId: '', + name: '', + email: '', + role: '', + permissions: {}, + isActive: false, + createdAt: '', + updatedAt: '', + ); +} diff --git a/lib/domain/auth/failures/auth_failure.dart b/lib/domain/auth/failures/auth_failure.dart new file mode 100644 index 0000000..bf304fd --- /dev/null +++ b/lib/domain/auth/failures/auth_failure.dart @@ -0,0 +1,9 @@ +part of '../auth.dart'; + +@freezed +sealed class AuthFailure with _$AuthFailure { + const factory AuthFailure.serverError(ApiFailure failure) = _ServerError; + const factory AuthFailure.unexpectedError() = _UnexpectedError; + const factory AuthFailure.dynamicErrorMessage(String erroMessage) = + _DynamicErrorMessage; +} diff --git a/lib/domain/auth/repositories/i_auth_repository.dart b/lib/domain/auth/repositories/i_auth_repository.dart new file mode 100644 index 0000000..def4bcb --- /dev/null +++ b/lib/domain/auth/repositories/i_auth_repository.dart @@ -0,0 +1,8 @@ +part of '../auth.dart'; + +abstract class IAuthRepository { + Future> login({ + required String email, + required String password, + }); +} diff --git a/lib/env.dart b/lib/env.dart index de21eb6..ddc3051 100644 --- a/lib/env.dart +++ b/lib/env.dart @@ -9,12 +9,12 @@ abstract class Env { @dev class DevEnv implements Env { @override - String get baseUrl => ''; // example value + String get baseUrl => 'https://api-pos.apskel.id'; // example value } @Injectable(as: Env) @prod class ProdEnv implements Env { @override - String get baseUrl => ''; + String get baseUrl => 'https://api-pos.apskel.id'; } diff --git a/lib/infrastructure/auth/auth_dtos.dart b/lib/infrastructure/auth/auth_dtos.dart new file mode 100644 index 0000000..b14169b --- /dev/null +++ b/lib/infrastructure/auth/auth_dtos.dart @@ -0,0 +1,9 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../domain/auth/auth.dart'; + +part 'auth_dtos.freezed.dart'; +part 'auth_dtos.g.dart'; + +part 'dto/login_dto.dart'; +part 'dto/user_dto.dart'; diff --git a/lib/infrastructure/auth/auth_dtos.freezed.dart b/lib/infrastructure/auth/auth_dtos.freezed.dart new file mode 100644 index 0000000..f509198 --- /dev/null +++ b/lib/infrastructure/auth/auth_dtos.freezed.dart @@ -0,0 +1,700 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// 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 'auth_dtos.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +LoginDto _$LoginDtoFromJson(Map json) { + return _LoginDto.fromJson(json); +} + +/// @nodoc +mixin _$LoginDto { + @JsonKey(name: "token") + String? get token => throw _privateConstructorUsedError; + @JsonKey(name: "refresh_token") + String? get refreshToken => throw _privateConstructorUsedError; + @JsonKey(name: "expires_at") + String? get expiresAt => throw _privateConstructorUsedError; + @JsonKey(name: "refresh_expires_at") + String? get refreshExpiresAt => throw _privateConstructorUsedError; + @JsonKey(name: "user") + UserDto? get user => throw _privateConstructorUsedError; + + /// Serializes this LoginDto to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $LoginDtoCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LoginDtoCopyWith<$Res> { + factory $LoginDtoCopyWith(LoginDto value, $Res Function(LoginDto) then) = + _$LoginDtoCopyWithImpl<$Res, LoginDto>; + @useResult + $Res call({ + @JsonKey(name: "token") String? token, + @JsonKey(name: "refresh_token") String? refreshToken, + @JsonKey(name: "expires_at") String? expiresAt, + @JsonKey(name: "refresh_expires_at") String? refreshExpiresAt, + @JsonKey(name: "user") UserDto? user, + }); + + $UserDtoCopyWith<$Res>? get user; +} + +/// @nodoc +class _$LoginDtoCopyWithImpl<$Res, $Val extends LoginDto> + implements $LoginDtoCopyWith<$Res> { + _$LoginDtoCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? token = freezed, + Object? refreshToken = freezed, + Object? expiresAt = freezed, + Object? refreshExpiresAt = freezed, + Object? user = freezed, + }) { + return _then( + _value.copyWith( + token: freezed == token + ? _value.token + : token // ignore: cast_nullable_to_non_nullable + as String?, + refreshToken: freezed == refreshToken + ? _value.refreshToken + : refreshToken // ignore: cast_nullable_to_non_nullable + as String?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as String?, + refreshExpiresAt: freezed == refreshExpiresAt + ? _value.refreshExpiresAt + : refreshExpiresAt // ignore: cast_nullable_to_non_nullable + as String?, + user: freezed == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserDto?, + ) + as $Val, + ); + } + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserDtoCopyWith<$Res>? get user { + if (_value.user == null) { + return null; + } + + return $UserDtoCopyWith<$Res>(_value.user!, (value) { + return _then(_value.copyWith(user: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$LoginDtoImplCopyWith<$Res> + implements $LoginDtoCopyWith<$Res> { + factory _$$LoginDtoImplCopyWith( + _$LoginDtoImpl value, + $Res Function(_$LoginDtoImpl) then, + ) = __$$LoginDtoImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + @JsonKey(name: "token") String? token, + @JsonKey(name: "refresh_token") String? refreshToken, + @JsonKey(name: "expires_at") String? expiresAt, + @JsonKey(name: "refresh_expires_at") String? refreshExpiresAt, + @JsonKey(name: "user") UserDto? user, + }); + + @override + $UserDtoCopyWith<$Res>? get user; +} + +/// @nodoc +class __$$LoginDtoImplCopyWithImpl<$Res> + extends _$LoginDtoCopyWithImpl<$Res, _$LoginDtoImpl> + implements _$$LoginDtoImplCopyWith<$Res> { + __$$LoginDtoImplCopyWithImpl( + _$LoginDtoImpl _value, + $Res Function(_$LoginDtoImpl) _then, + ) : super(_value, _then); + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? token = freezed, + Object? refreshToken = freezed, + Object? expiresAt = freezed, + Object? refreshExpiresAt = freezed, + Object? user = freezed, + }) { + return _then( + _$LoginDtoImpl( + token: freezed == token + ? _value.token + : token // ignore: cast_nullable_to_non_nullable + as String?, + refreshToken: freezed == refreshToken + ? _value.refreshToken + : refreshToken // ignore: cast_nullable_to_non_nullable + as String?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as String?, + refreshExpiresAt: freezed == refreshExpiresAt + ? _value.refreshExpiresAt + : refreshExpiresAt // ignore: cast_nullable_to_non_nullable + as String?, + user: freezed == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserDto?, + ), + ); + } +} + +/// @nodoc +@JsonSerializable() +class _$LoginDtoImpl extends _LoginDto { + const _$LoginDtoImpl({ + @JsonKey(name: "token") this.token, + @JsonKey(name: "refresh_token") this.refreshToken, + @JsonKey(name: "expires_at") this.expiresAt, + @JsonKey(name: "refresh_expires_at") this.refreshExpiresAt, + @JsonKey(name: "user") this.user, + }) : super._(); + + factory _$LoginDtoImpl.fromJson(Map json) => + _$$LoginDtoImplFromJson(json); + + @override + @JsonKey(name: "token") + final String? token; + @override + @JsonKey(name: "refresh_token") + final String? refreshToken; + @override + @JsonKey(name: "expires_at") + final String? expiresAt; + @override + @JsonKey(name: "refresh_expires_at") + final String? refreshExpiresAt; + @override + @JsonKey(name: "user") + final UserDto? user; + + @override + String toString() { + return 'LoginDto(token: $token, refreshToken: $refreshToken, expiresAt: $expiresAt, refreshExpiresAt: $refreshExpiresAt, user: $user)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoginDtoImpl && + (identical(other.token, token) || other.token == token) && + (identical(other.refreshToken, refreshToken) || + other.refreshToken == refreshToken) && + (identical(other.expiresAt, expiresAt) || + other.expiresAt == expiresAt) && + (identical(other.refreshExpiresAt, refreshExpiresAt) || + other.refreshExpiresAt == refreshExpiresAt) && + (identical(other.user, user) || other.user == user)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + token, + refreshToken, + expiresAt, + refreshExpiresAt, + user, + ); + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$LoginDtoImplCopyWith<_$LoginDtoImpl> get copyWith => + __$$LoginDtoImplCopyWithImpl<_$LoginDtoImpl>(this, _$identity); + + @override + Map toJson() { + return _$$LoginDtoImplToJson(this); + } +} + +abstract class _LoginDto extends LoginDto { + const factory _LoginDto({ + @JsonKey(name: "token") final String? token, + @JsonKey(name: "refresh_token") final String? refreshToken, + @JsonKey(name: "expires_at") final String? expiresAt, + @JsonKey(name: "refresh_expires_at") final String? refreshExpiresAt, + @JsonKey(name: "user") final UserDto? user, + }) = _$LoginDtoImpl; + const _LoginDto._() : super._(); + + factory _LoginDto.fromJson(Map json) = + _$LoginDtoImpl.fromJson; + + @override + @JsonKey(name: "token") + String? get token; + @override + @JsonKey(name: "refresh_token") + String? get refreshToken; + @override + @JsonKey(name: "expires_at") + String? get expiresAt; + @override + @JsonKey(name: "refresh_expires_at") + String? get refreshExpiresAt; + @override + @JsonKey(name: "user") + UserDto? get user; + + /// Create a copy of LoginDto + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$LoginDtoImplCopyWith<_$LoginDtoImpl> get copyWith => + throw _privateConstructorUsedError; +} + +UserDto _$UserDtoFromJson(Map json) { + return _UserDto.fromJson(json); +} + +/// @nodoc +mixin _$UserDto { + @JsonKey(name: "id") + String? get id => throw _privateConstructorUsedError; + @JsonKey(name: "organization_id") + String? get organizationId => throw _privateConstructorUsedError; + @JsonKey(name: "outlet_id") + String? get outletId => throw _privateConstructorUsedError; + @JsonKey(name: "name") + String? get name => throw _privateConstructorUsedError; + @JsonKey(name: "email") + String? get email => throw _privateConstructorUsedError; + @JsonKey(name: "role") + String? get role => throw _privateConstructorUsedError; + @JsonKey(name: "permissions") + Map? get permissions => throw _privateConstructorUsedError; + @JsonKey(name: "is_active") + bool? get isActive => throw _privateConstructorUsedError; + @JsonKey(name: "created_at") + String? get createdAt => throw _privateConstructorUsedError; + @JsonKey(name: "updated_at") + String? get updatedAt => throw _privateConstructorUsedError; + + /// Serializes this UserDto to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of UserDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $UserDtoCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserDtoCopyWith<$Res> { + factory $UserDtoCopyWith(UserDto value, $Res Function(UserDto) then) = + _$UserDtoCopyWithImpl<$Res, UserDto>; + @useResult + $Res call({ + @JsonKey(name: "id") String? id, + @JsonKey(name: "organization_id") String? organizationId, + @JsonKey(name: "outlet_id") String? outletId, + @JsonKey(name: "name") String? name, + @JsonKey(name: "email") String? email, + @JsonKey(name: "role") String? role, + @JsonKey(name: "permissions") Map? permissions, + @JsonKey(name: "is_active") bool? isActive, + @JsonKey(name: "created_at") String? createdAt, + @JsonKey(name: "updated_at") String? updatedAt, + }); +} + +/// @nodoc +class _$UserDtoCopyWithImpl<$Res, $Val extends UserDto> + implements $UserDtoCopyWith<$Res> { + _$UserDtoCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of UserDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? organizationId = freezed, + Object? outletId = freezed, + Object? name = freezed, + Object? email = freezed, + Object? role = freezed, + Object? permissions = freezed, + Object? isActive = freezed, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + organizationId: freezed == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String?, + outletId: freezed == outletId + ? _value.outletId + : outletId // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + role: freezed == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String?, + permissions: freezed == permissions + ? _value.permissions + : permissions // ignore: cast_nullable_to_non_nullable + as Map?, + isActive: freezed == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool?, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as String?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String?, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$UserDtoImplCopyWith<$Res> implements $UserDtoCopyWith<$Res> { + factory _$$UserDtoImplCopyWith( + _$UserDtoImpl value, + $Res Function(_$UserDtoImpl) then, + ) = __$$UserDtoImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + @JsonKey(name: "id") String? id, + @JsonKey(name: "organization_id") String? organizationId, + @JsonKey(name: "outlet_id") String? outletId, + @JsonKey(name: "name") String? name, + @JsonKey(name: "email") String? email, + @JsonKey(name: "role") String? role, + @JsonKey(name: "permissions") Map? permissions, + @JsonKey(name: "is_active") bool? isActive, + @JsonKey(name: "created_at") String? createdAt, + @JsonKey(name: "updated_at") String? updatedAt, + }); +} + +/// @nodoc +class __$$UserDtoImplCopyWithImpl<$Res> + extends _$UserDtoCopyWithImpl<$Res, _$UserDtoImpl> + implements _$$UserDtoImplCopyWith<$Res> { + __$$UserDtoImplCopyWithImpl( + _$UserDtoImpl _value, + $Res Function(_$UserDtoImpl) _then, + ) : super(_value, _then); + + /// Create a copy of UserDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? organizationId = freezed, + Object? outletId = freezed, + Object? name = freezed, + Object? email = freezed, + Object? role = freezed, + Object? permissions = freezed, + Object? isActive = freezed, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _$UserDtoImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + organizationId: freezed == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String?, + outletId: freezed == outletId + ? _value.outletId + : outletId // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + role: freezed == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as String?, + permissions: freezed == permissions + ? _value._permissions + : permissions // ignore: cast_nullable_to_non_nullable + as Map?, + isActive: freezed == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool?, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as String?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as String?, + ), + ); + } +} + +/// @nodoc +@JsonSerializable() +class _$UserDtoImpl extends _UserDto { + const _$UserDtoImpl({ + @JsonKey(name: "id") this.id, + @JsonKey(name: "organization_id") this.organizationId, + @JsonKey(name: "outlet_id") this.outletId, + @JsonKey(name: "name") this.name, + @JsonKey(name: "email") this.email, + @JsonKey(name: "role") this.role, + @JsonKey(name: "permissions") final Map? permissions, + @JsonKey(name: "is_active") this.isActive, + @JsonKey(name: "created_at") this.createdAt, + @JsonKey(name: "updated_at") this.updatedAt, + }) : _permissions = permissions, + super._(); + + factory _$UserDtoImpl.fromJson(Map json) => + _$$UserDtoImplFromJson(json); + + @override + @JsonKey(name: "id") + final String? id; + @override + @JsonKey(name: "organization_id") + final String? organizationId; + @override + @JsonKey(name: "outlet_id") + final String? outletId; + @override + @JsonKey(name: "name") + final String? name; + @override + @JsonKey(name: "email") + final String? email; + @override + @JsonKey(name: "role") + final String? role; + final Map? _permissions; + @override + @JsonKey(name: "permissions") + Map? get permissions { + final value = _permissions; + if (value == null) return null; + if (_permissions is EqualUnmodifiableMapView) return _permissions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + @JsonKey(name: "is_active") + final bool? isActive; + @override + @JsonKey(name: "created_at") + final String? createdAt; + @override + @JsonKey(name: "updated_at") + final String? updatedAt; + + @override + String toString() { + return 'UserDto(id: $id, organizationId: $organizationId, outletId: $outletId, name: $name, email: $email, role: $role, permissions: $permissions, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserDtoImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.organizationId, organizationId) || + other.organizationId == organizationId) && + (identical(other.outletId, outletId) || + other.outletId == outletId) && + (identical(other.name, name) || other.name == name) && + (identical(other.email, email) || other.email == email) && + (identical(other.role, role) || other.role == role) && + const DeepCollectionEquality().equals( + other._permissions, + _permissions, + ) && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + id, + organizationId, + outletId, + name, + email, + role, + const DeepCollectionEquality().hash(_permissions), + isActive, + createdAt, + updatedAt, + ); + + /// Create a copy of UserDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith => + __$$UserDtoImplCopyWithImpl<_$UserDtoImpl>(this, _$identity); + + @override + Map toJson() { + return _$$UserDtoImplToJson(this); + } +} + +abstract class _UserDto extends UserDto { + const factory _UserDto({ + @JsonKey(name: "id") final String? id, + @JsonKey(name: "organization_id") final String? organizationId, + @JsonKey(name: "outlet_id") final String? outletId, + @JsonKey(name: "name") final String? name, + @JsonKey(name: "email") final String? email, + @JsonKey(name: "role") final String? role, + @JsonKey(name: "permissions") final Map? permissions, + @JsonKey(name: "is_active") final bool? isActive, + @JsonKey(name: "created_at") final String? createdAt, + @JsonKey(name: "updated_at") final String? updatedAt, + }) = _$UserDtoImpl; + const _UserDto._() : super._(); + + factory _UserDto.fromJson(Map json) = _$UserDtoImpl.fromJson; + + @override + @JsonKey(name: "id") + String? get id; + @override + @JsonKey(name: "organization_id") + String? get organizationId; + @override + @JsonKey(name: "outlet_id") + String? get outletId; + @override + @JsonKey(name: "name") + String? get name; + @override + @JsonKey(name: "email") + String? get email; + @override + @JsonKey(name: "role") + String? get role; + @override + @JsonKey(name: "permissions") + Map? get permissions; + @override + @JsonKey(name: "is_active") + bool? get isActive; + @override + @JsonKey(name: "created_at") + String? get createdAt; + @override + @JsonKey(name: "updated_at") + String? get updatedAt; + + /// Create a copy of UserDto + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/infrastructure/auth/auth_dtos.g.dart b/lib/infrastructure/auth/auth_dtos.g.dart new file mode 100644 index 0000000..b27339d --- /dev/null +++ b/lib/infrastructure/auth/auth_dtos.g.dart @@ -0,0 +1,55 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'auth_dtos.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$LoginDtoImpl _$$LoginDtoImplFromJson(Map json) => + _$LoginDtoImpl( + token: json['token'] as String?, + refreshToken: json['refresh_token'] as String?, + expiresAt: json['expires_at'] as String?, + refreshExpiresAt: json['refresh_expires_at'] as String?, + user: json['user'] == null + ? null + : UserDto.fromJson(json['user'] as Map), + ); + +Map _$$LoginDtoImplToJson(_$LoginDtoImpl instance) => + { + 'token': instance.token, + 'refresh_token': instance.refreshToken, + 'expires_at': instance.expiresAt, + 'refresh_expires_at': instance.refreshExpiresAt, + 'user': instance.user, + }; + +_$UserDtoImpl _$$UserDtoImplFromJson(Map json) => + _$UserDtoImpl( + id: json['id'] as String?, + organizationId: json['organization_id'] as String?, + outletId: json['outlet_id'] as String?, + name: json['name'] as String?, + email: json['email'] as String?, + role: json['role'] as String?, + permissions: json['permissions'] as Map?, + isActive: json['is_active'] as bool?, + createdAt: json['created_at'] as String?, + updatedAt: json['updated_at'] as String?, + ); + +Map _$$UserDtoImplToJson(_$UserDtoImpl instance) => + { + 'id': instance.id, + 'organization_id': instance.organizationId, + 'outlet_id': instance.outletId, + 'name': instance.name, + 'email': instance.email, + 'role': instance.role, + 'permissions': instance.permissions, + 'is_active': instance.isActive, + 'created_at': instance.createdAt, + 'updated_at': instance.updatedAt, + }; diff --git a/lib/infrastructure/auth/datasources/local_data_provider.dart b/lib/infrastructure/auth/datasources/local_data_provider.dart new file mode 100644 index 0000000..26c460c --- /dev/null +++ b/lib/infrastructure/auth/datasources/local_data_provider.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:injectable/injectable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../common/constant/local_storage_key.dart'; +import '../../../domain/auth/auth.dart'; +import '../auth_dtos.dart'; + +@injectable +class AuthLocalDataProvider { + final SharedPreferences _sharedPreferences; + final String _logName = 'AuthLocalDataProvider'; + + AuthLocalDataProvider(this._sharedPreferences); + + Future saveToken(String token) async { + await _sharedPreferences.setString(LocalStorageKey.token, token); + } + + Future getToken() async { + return _sharedPreferences.getString(LocalStorageKey.token); + } + + Future deleteToken() async { + await _sharedPreferences.remove(LocalStorageKey.token); + } + + Future hasToken() async { + return _sharedPreferences.containsKey(LocalStorageKey.token); + } + + Future saveCurrentUser(UserDto user) async { + final userJsonString = jsonEncode(user.toJson()); + await _sharedPreferences.setString(LocalStorageKey.user, userJsonString); + } + + Future currentUser() async { + final userString = _sharedPreferences.getString(LocalStorageKey.user); + if (userString == null) return User.empty(); + + final Map userMap = jsonDecode(userString); + final userDto = UserDto.fromJson(userMap); + return userDto.toDomain(); + } + + Future deleteCurrentUser() async { + await _sharedPreferences.remove(LocalStorageKey.user); + } + + Future deleteAllAuth() async { + try { + await _sharedPreferences.remove(LocalStorageKey.token); + await _sharedPreferences.remove(LocalStorageKey.user); + } catch (e) { + log('deleteAllAuthError', name: _logName, error: e); + } + } +} diff --git a/lib/infrastructure/auth/datasources/remote_data_provider.dart b/lib/infrastructure/auth/datasources/remote_data_provider.dart new file mode 100644 index 0000000..7f787eb --- /dev/null +++ b/lib/infrastructure/auth/datasources/remote_data_provider.dart @@ -0,0 +1,44 @@ +import 'dart:developer'; + +import 'package:data_channel/data_channel.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../common/api/api_client.dart'; +import '../../../common/api/api_failure.dart'; +import '../../../common/url/api_path.dart'; +import '../../../domain/auth/auth.dart'; +import '../auth_dtos.dart'; + +@injectable +class AuthRemoteDataProvider { + final ApiClient _apiClient; + final String _logName = 'AuthRemoteDataProvider'; + + AuthRemoteDataProvider(this._apiClient); + + Future> login({ + required String email, + required String password, + }) async { + try { + final response = await _apiClient.post( + ApiPath.login, + data: {'email': email, 'password': password}, + ); + + if (response.data['code'] == 401) { + return DC.error( + AuthFailure.serverError( + ApiFailure.unauthorized('Incorrect email or password'), + ), + ); + } + + final dto = LoginDto.fromJson(response.data['data']); + return DC.data(dto); + } on ApiFailure catch (e, s) { + log('login', name: _logName, error: e, stackTrace: s); + return DC.error(AuthFailure.serverError(e)); + } + } +} diff --git a/lib/infrastructure/auth/dto/login_dto.dart b/lib/infrastructure/auth/dto/login_dto.dart new file mode 100644 index 0000000..313f107 --- /dev/null +++ b/lib/infrastructure/auth/dto/login_dto.dart @@ -0,0 +1,39 @@ +part of '../auth_dtos.dart'; + +@freezed +class LoginDto with _$LoginDto { + const LoginDto._(); + + const factory LoginDto({ + @JsonKey(name: "token") String? token, + @JsonKey(name: "refresh_token") String? refreshToken, + @JsonKey(name: "expires_at") String? expiresAt, + @JsonKey(name: "refresh_expires_at") String? refreshExpiresAt, + @JsonKey(name: "user") UserDto? user, + }) = _LoginDto; + + factory LoginDto.fromJson(Map json) => + _$LoginDtoFromJson(json); + + /// mapping ke domain + Login toDomain() => Login( + token: token ?? '', + refreshToken: refreshToken ?? '', + expiresAt: expiresAt ?? '', + refreshExpiresAt: refreshExpiresAt ?? '', + user: + user?.toDomain() ?? + User( + id: '', + organizationId: '', + outletId: '', + name: '', + email: '', + role: '', + permissions: {}, + isActive: false, + createdAt: '', + updatedAt: '', + ), + ); +} diff --git a/lib/infrastructure/auth/dto/user_dto.dart b/lib/infrastructure/auth/dto/user_dto.dart new file mode 100644 index 0000000..5f5d74e --- /dev/null +++ b/lib/infrastructure/auth/dto/user_dto.dart @@ -0,0 +1,35 @@ +part of '../auth_dtos.dart'; + +@freezed +class UserDto with _$UserDto { + const UserDto._(); + + const factory UserDto({ + @JsonKey(name: "id") String? id, + @JsonKey(name: "organization_id") String? organizationId, + @JsonKey(name: "outlet_id") String? outletId, + @JsonKey(name: "name") String? name, + @JsonKey(name: "email") String? email, + @JsonKey(name: "role") String? role, + @JsonKey(name: "permissions") Map? permissions, + @JsonKey(name: "is_active") bool? isActive, + @JsonKey(name: "created_at") String? createdAt, + @JsonKey(name: "updated_at") String? updatedAt, + }) = _UserDto; + + factory UserDto.fromJson(Map json) => + _$UserDtoFromJson(json); + + User toDomain() => User( + id: id ?? '', + organizationId: organizationId ?? '', + outletId: outletId ?? '', + name: name ?? '', + email: email ?? '', + role: role ?? '', + permissions: permissions ?? {}, + isActive: isActive ?? false, + createdAt: createdAt ?? '', + updatedAt: updatedAt ?? '', + ); +} diff --git a/lib/infrastructure/auth/repositories/auth_repository.dart b/lib/infrastructure/auth/repositories/auth_repository.dart new file mode 100644 index 0000000..fac3e27 --- /dev/null +++ b/lib/infrastructure/auth/repositories/auth_repository.dart @@ -0,0 +1,44 @@ +import 'dart:developer'; + +import 'package:dartz/dartz.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../domain/auth/auth.dart'; +import '../datasources/local_data_provider.dart'; +import '../datasources/remote_data_provider.dart'; + +@Injectable(as: IAuthRepository) +class AuthRepository implements IAuthRepository { + final AuthRemoteDataProvider _dataProvider; + final AuthLocalDataProvider _localDataProvider; + final String _logName = 'AuthRepository'; + + AuthRepository(this._dataProvider, this._localDataProvider); + + @override + Future> login({ + required String email, + required String password, + }) async { + try { + final result = await _dataProvider.login( + email: email, + password: password, + ); + + if (result.hasError) { + return left(result.error!); + } + + final auth = result.data!.toDomain(); + + await _localDataProvider.saveToken(auth.token); + await _localDataProvider.saveCurrentUser(result.data!.user!); + + return right(auth); + } catch (e, s) { + log('loginError', name: _logName, error: e, stackTrace: s); + return left(const AuthFailure.unexpectedError()); + } + } +} diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 52e1897..5c7f74e 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -9,6 +9,8 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:apskel_pos_flutter_v2/application/auth/bloc/login_form_bloc.dart' + as _i185; import 'package:apskel_pos_flutter_v2/common/api/api_client.dart' as _i457; import 'package:apskel_pos_flutter_v2/common/di/di_auto_route.dart' as _i729; import 'package:apskel_pos_flutter_v2/common/di/di_connectivity.dart' as _i807; @@ -17,7 +19,14 @@ import 'package:apskel_pos_flutter_v2/common/di/di_shared_preferences.dart' as _i135; import 'package:apskel_pos_flutter_v2/common/network/network_client.dart' as _i171; +import 'package:apskel_pos_flutter_v2/domain/auth/auth.dart' as _i776; import 'package:apskel_pos_flutter_v2/env.dart' as _i923; +import 'package:apskel_pos_flutter_v2/infrastructure/auth/datasources/local_data_provider.dart' + as _i204; +import 'package:apskel_pos_flutter_v2/infrastructure/auth/datasources/remote_data_provider.dart' + as _i370; +import 'package:apskel_pos_flutter_v2/infrastructure/auth/repositories/auth_repository.dart' + as _i941; import 'package:apskel_pos_flutter_v2/presentation/router/app_router.dart' as _i800; import 'package:connectivity_plus/connectivity_plus.dart' as _i895; @@ -51,10 +60,25 @@ extension GetItInjectableX on _i174.GetIt { () => _i171.NetworkClient(gh<_i895.Connectivity>()), ); gh.factory<_i923.Env>(() => _i923.DevEnv(), registerFor: {_dev}); + gh.factory<_i204.AuthLocalDataProvider>( + () => _i204.AuthLocalDataProvider(gh<_i460.SharedPreferences>()), + ); gh.lazySingleton<_i457.ApiClient>( () => _i457.ApiClient(gh<_i361.Dio>(), gh<_i923.Env>()), ); gh.factory<_i923.Env>(() => _i923.ProdEnv(), registerFor: {_prod}); + gh.factory<_i370.AuthRemoteDataProvider>( + () => _i370.AuthRemoteDataProvider(gh<_i457.ApiClient>()), + ); + gh.factory<_i776.IAuthRepository>( + () => _i941.AuthRepository( + gh<_i370.AuthRemoteDataProvider>(), + gh<_i204.AuthLocalDataProvider>(), + ), + ); + gh.factory<_i185.LoginFormBloc>( + () => _i185.LoginFormBloc(gh<_i776.IAuthRepository>()), + ); return this; } } diff --git a/lib/main.dart b/lib/main.dart index 40337de..0b9f9fd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,11 @@ void main() async { ), ); + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); + if (kReleaseMode) { debugPrint = (message, {wrapWidth}) => ''; } diff --git a/lib/presentation/components/button/button.dart b/lib/presentation/components/button/button.dart new file mode 100644 index 0000000..adcc7ca --- /dev/null +++ b/lib/presentation/components/button/button.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; + +import '../../../common/theme/theme.dart'; +import '../spaces/space.dart'; + +part 'elevated_button.dart'; diff --git a/lib/presentation/components/button/elevated_button.dart b/lib/presentation/components/button/elevated_button.dart new file mode 100644 index 0000000..8e0813b --- /dev/null +++ b/lib/presentation/components/button/elevated_button.dart @@ -0,0 +1,180 @@ +part of 'button.dart'; + +enum ButtonStyle { filled, outlined } + +class AppElevatedButton extends StatelessWidget { + const AppElevatedButton.filled({ + super.key, + required this.onPressed, + required this.label, + this.style = ButtonStyle.filled, + this.color = AppColor.primary, + this.textColor = Colors.white, + this.width, + this.height = 40.0, + this.borderRadius = 16.0, + this.icon, + this.disabled = false, + this.fontSize = 16.0, + this.elevation, + this.labelStyle, + this.mainAxisAlignment = MainAxisAlignment.center, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.isLoading = false, + }); + + const AppElevatedButton.outlined({ + super.key, + required this.onPressed, + required this.label, + this.style = ButtonStyle.outlined, + this.color = Colors.transparent, + this.textColor = AppColor.primary, + this.width, + this.height = 40.0, + this.borderRadius = 16.0, + this.icon, + this.disabled = false, + this.fontSize = 16.0, + this.elevation, + this.labelStyle, + this.mainAxisAlignment = MainAxisAlignment.center, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.isLoading = false, + }); + + final Function()? onPressed; + final String label; + final ButtonStyle style; + final Color color; + final Color textColor; + final double? width; + final double height; + final double borderRadius; + final double? elevation; + final Widget? icon; + final bool disabled; + final double fontSize; + final TextStyle? labelStyle; + final MainAxisAlignment mainAxisAlignment; + final CrossAxisAlignment crossAxisAlignment; + final bool isLoading; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: height, + width: width, + child: style == ButtonStyle.filled + ? ElevatedButton( + onPressed: disabled ? null : onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), + ), + elevation: elevation, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + ), + child: isLoading + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SpinKitFadingCircle(color: textColor, size: fontSize), + const SpaceWidth(10.0), + Text( + 'Loading...', + style: + labelStyle ?? + TextStyle( + color: disabled ? Colors.grey : textColor, + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + ), + ], + ) + : Row( + mainAxisAlignment: mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment, + children: [ + icon ?? const SizedBox.shrink(), + if (icon != null) const SizedBox(width: 10.0), + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + label, + style: + labelStyle ?? + TextStyle( + color: disabled ? Colors.grey : textColor, + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ) + : OutlinedButton( + onPressed: disabled ? null : onPressed, + style: OutlinedButton.styleFrom( + backgroundColor: color, + side: const BorderSide(color: AppColor.primary), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + ), + child: isLoading + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SpinKitFadingCircle(color: textColor, size: fontSize), + const SpaceWidth(10.0), + Text( + 'Loading...', + style: + labelStyle ?? + TextStyle( + color: disabled ? Colors.grey : textColor, + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + ), + ], + ) + : Row( + mainAxisAlignment: mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment, + mainAxisSize: MainAxisSize.min, + children: [ + icon ?? const SizedBox.shrink(), + if (icon != null) const SizedBox(width: 10.0), + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + label, + style: + labelStyle ?? + TextStyle( + color: disabled ? Colors.grey : textColor, + fontSize: fontSize, + fontWeight: FontWeight.w600, + ), + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/components/field/field.dart b/lib/presentation/components/field/field.dart new file mode 100644 index 0000000..211e4c1 --- /dev/null +++ b/lib/presentation/components/field/field.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +import '../../../common/theme/theme.dart'; +import '../spaces/space.dart'; + +part 'password_text_field.dart'; +part 'text_field.dart'; diff --git a/lib/presentation/components/field/password_text_field.dart b/lib/presentation/components/field/password_text_field.dart new file mode 100644 index 0000000..4a85fdc --- /dev/null +++ b/lib/presentation/components/field/password_text_field.dart @@ -0,0 +1,100 @@ +part of 'field.dart'; + +class AppPasswordTextFormField extends StatefulWidget { + final TextEditingController? controller; + final String label; + final Function(String value)? onChanged; + final TextInputType? keyboardType; + final TextInputAction? textInputAction; + final TextCapitalization? textCapitalization; + final bool showLabel; + final Widget? prefixIcon; + final Widget? suffixIcon; + final bool readOnly; + final int maxLines; + final String? Function(String?)? validator; + + const AppPasswordTextFormField({ + super.key, + this.controller, + required this.label, + this.onChanged, + this.keyboardType, + this.textInputAction, + this.textCapitalization, + this.showLabel = true, + this.prefixIcon, + this.suffixIcon, + this.readOnly = false, + this.maxLines = 1, + this.validator, + }); + + @override + State createState() => + _AppPasswordTextFormFieldState(); +} + +class _AppPasswordTextFormFieldState extends State { + bool isPasswordVisible = true; + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.showLabel) ...[ + Text( + widget.label, + style: AppStyle.md.copyWith(fontWeight: FontWeight.w700), + ), + const SpaceHeight(12.0), + ], + TextFormField( + controller: widget.controller, + onChanged: widget.onChanged, + obscureText: isPasswordVisible, + keyboardType: widget.keyboardType, + textInputAction: widget.textInputAction, + textCapitalization: + widget.textCapitalization ?? TextCapitalization.none, + readOnly: widget.readOnly, + maxLines: widget.maxLines, + validator: widget.validator, + decoration: InputDecoration( + prefixIcon: widget.prefixIcon, + suffixIcon: isPasswordVisible + ? IconButton( + icon: Icon( + Icons.visibility, + color: AppColor.textSecondary, + size: 20, + ), + constraints: const BoxConstraints(), + padding: const EdgeInsets.all(8), + onPressed: () { + setState(() { + isPasswordVisible = !isPasswordVisible; + }); + }, + ) + : IconButton( + icon: Icon( + Icons.visibility_off, + color: AppColor.textSecondary, + size: 20, + ), + constraints: const BoxConstraints(), + padding: const EdgeInsets.all(8), + onPressed: () { + setState(() { + isPasswordVisible = !isPasswordVisible; + }); + }, + ), + hintText: widget.label, + ), + ), + ], + ); + } +} diff --git a/lib/presentation/components/field/text_field.dart b/lib/presentation/components/field/text_field.dart new file mode 100644 index 0000000..4720b80 --- /dev/null +++ b/lib/presentation/components/field/text_field.dart @@ -0,0 +1,63 @@ +part of 'field.dart'; + +class AppTextFormField extends StatelessWidget { + final TextEditingController? controller; + final String label; + final Function(String value)? onChanged; + final bool obscureText; + final TextInputType? keyboardType; + final TextInputAction? textInputAction; + final TextCapitalization? textCapitalization; + final bool showLabel; + final Widget? prefixIcon; + final Widget? suffixIcon; + final bool readOnly; + final int maxLines; + final String? Function(String?)? validator; + + const AppTextFormField({ + super.key, + this.controller, + required this.label, + this.onChanged, + this.obscureText = false, + this.keyboardType, + this.textInputAction, + this.textCapitalization, + this.showLabel = true, + this.prefixIcon, + this.suffixIcon, + this.readOnly = false, + this.maxLines = 1, + this.validator, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (showLabel) ...[ + Text(label, style: AppStyle.md.copyWith(fontWeight: FontWeight.w700)), + const SpaceHeight(12.0), + ], + TextFormField( + controller: controller, + onChanged: onChanged, + obscureText: obscureText, + keyboardType: keyboardType, + textInputAction: textInputAction, + textCapitalization: textCapitalization ?? TextCapitalization.none, + readOnly: readOnly, + maxLines: maxLines, + validator: validator, + decoration: InputDecoration( + prefixIcon: prefixIcon, + suffixIcon: suffixIcon, + hintText: label, + ), + ), + ], + ); + } +} diff --git a/lib/presentation/components/spaces/space.dart b/lib/presentation/components/spaces/space.dart new file mode 100644 index 0000000..7975925 --- /dev/null +++ b/lib/presentation/components/spaces/space.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class SpaceHeight extends StatelessWidget { + final double height; + const SpaceHeight(this.height, {super.key}); + + @override + Widget build(BuildContext context) => SizedBox(height: height); +} + +class SpaceWidth extends StatelessWidget { + final double width; + const SpaceWidth(this.width, {super.key}); + + @override + Widget build(BuildContext context) => SizedBox(width: width); +} diff --git a/lib/presentation/components/toast/flushbar.dart b/lib/presentation/components/toast/flushbar.dart new file mode 100644 index 0000000..2141699 --- /dev/null +++ b/lib/presentation/components/toast/flushbar.dart @@ -0,0 +1,53 @@ +import 'package:another_flushbar/flushbar.dart'; +import 'package:flutter/material.dart'; + +import '../../../common/theme/theme.dart'; +import '../../../domain/auth/auth.dart'; + +class AppFlushbar { + static void showSuccess(BuildContext context, String message) { + Flushbar( + messageText: Text( + message, + style: AppStyle.md.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + ), + ), + icon: const Icon(Icons.check_circle, color: Colors.white), + duration: const Duration(seconds: 2), + flushbarPosition: FlushbarPosition.BOTTOM, + backgroundColor: AppColor.success, + borderRadius: BorderRadius.circular(12), + margin: const EdgeInsets.all(12), + ).show(context); + } + + static void showError(BuildContext context, String message) { + Flushbar( + messageText: Text( + message, + style: AppStyle.md.copyWith( + color: Colors.white, + fontWeight: FontWeight.w700, + ), + ), + icon: const Icon(Icons.error, color: Colors.white), + duration: const Duration(seconds: 3), + flushbarPosition: FlushbarPosition.BOTTOM, + backgroundColor: AppColor.error, + borderRadius: BorderRadius.circular(12), + margin: const EdgeInsets.all(12), + ).show(context); + } + + static void showAuthFailureToast(BuildContext context, AuthFailure failure) => + showError( + context, + failure.map( + serverError: (value) => value.failure.toStringFormatted(context), + dynamicErrorMessage: (value) => value.erroMessage, + unexpectedError: (value) => 'Terjadi kesalahan, silahkan coba lagi', + ), + ); +} diff --git a/lib/presentation/pages/auth/login/login_page.dart b/lib/presentation/pages/auth/login/login_page.dart new file mode 100644 index 0000000..304e63e --- /dev/null +++ b/lib/presentation/pages/auth/login/login_page.dart @@ -0,0 +1,106 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../application/auth/bloc/login_form_bloc.dart'; +import '../../../../common/constant/app_constant.dart'; +import '../../../../common/theme/theme.dart'; +import '../../../../injection.dart'; +import '../../../components/assets/assets.gen.dart'; +import '../../../components/button/button.dart'; +import '../../../components/spaces/space.dart'; +import '../../../components/toast/flushbar.dart'; +import 'widgets/email_field.dart'; +import 'widgets/password_field.dart'; + +@RoutePage() +class LoginPage extends StatelessWidget implements AutoRouteWrapper { + const LoginPage({super.key}); + + @override + Widget build(BuildContext context) { + return BlocListener( + listenWhen: (previous, current) => + previous.failureOrLoginOption != current.failureOrLoginOption, + listener: (context, state) { + state.failureOrLoginOption.fold( + () => null, + (either) => either.fold( + (f) => AppFlushbar.showAuthFailureToast(context, f), + (data) { + if (context.mounted) { + AppFlushbar.showSuccess( + context, + 'Login berhasil! Selamat datang, ${data.user.name}.', + ); + // context.read().add(AuthEvent.fetchCurrentUser()); + // context.router.replace(const MainRoute()); + } + }, + ), + ); + }, + child: Scaffold( + body: BlocBuilder( + builder: (context, state) { + return Form( + autovalidateMode: state.showErrorMessages + ? AutovalidateMode.always + : AutovalidateMode.disabled, + child: ListView( + padding: const EdgeInsets.symmetric( + horizontal: 260.0, + vertical: 20.0, + ), + children: [ + const SpaceHeight(60.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 130.0), + child: Assets.images.logo.image(width: 100, height: 100), + ), + const SpaceHeight(16.0), + Center( + child: Text( + AppConstant.appName, + style: AppStyle.lg.copyWith(fontWeight: FontWeight.w700), + ), + ), + const SpaceHeight(8.0), + Center( + child: Text( + 'Akses Login Kasir Resto', + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + ), + ), + ), + const SpaceHeight(20.0), + LoginEmailField(), + const SpaceHeight(12.0), + LoginPasswordField(), + const SpaceHeight(24.0), + AppElevatedButton.filled( + onPressed: state.isSubmitting + ? null + : () => context.read().add( + LoginFormEvent.submitted(), + ), + textColor: state.isSubmitting + ? AppColor.primary + : Colors.white, + label: 'Masuk', + isLoading: state.isSubmitting, + ), + ], + ), + ); + }, + ), + ), + ); + } + + @override + Widget wrappedRoute(BuildContext context) => + BlocProvider(create: (context) => getIt(), child: this); +} diff --git a/lib/presentation/pages/auth/login/widgets/email_field.dart b/lib/presentation/pages/auth/login/widgets/email_field.dart new file mode 100644 index 0000000..3be32ba --- /dev/null +++ b/lib/presentation/pages/auth/login/widgets/email_field.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../../application/auth/bloc/login_form_bloc.dart'; +import '../../../../../common/validator/validator.dart'; +import '../../../../components/field/field.dart'; + +class LoginEmailField extends StatelessWidget { + const LoginEmailField({super.key}); + + @override + Widget build(BuildContext context) { + return AppTextFormField( + label: 'Email', + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + validator: (value) => + AppValidator.validateEmail(context.read().state.email), + onChanged: (value) => + context.read().add(LoginFormEvent.emailChanged(value)), + ); + } +} diff --git a/lib/presentation/pages/auth/login/widgets/password_field.dart b/lib/presentation/pages/auth/login/widgets/password_field.dart new file mode 100644 index 0000000..9d95920 --- /dev/null +++ b/lib/presentation/pages/auth/login/widgets/password_field.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../../application/auth/bloc/login_form_bloc.dart'; +import '../../../../../common/validator/validator.dart'; +import '../../../../components/field/field.dart'; + +class LoginPasswordField extends StatelessWidget { + const LoginPasswordField({super.key}); + + @override + Widget build(BuildContext context) { + return AppPasswordTextFormField( + label: 'Password', + keyboardType: TextInputType.visiblePassword, + textInputAction: TextInputAction.done, + validator: (value) => AppValidator.validatePassword( + context.read().state.password, + ), + onChanged: (value) => context.read().add( + LoginFormEvent.passwordChanged(value), + ), + ); + } +} diff --git a/lib/presentation/pages/splash/splash_page.dart b/lib/presentation/pages/splash/splash_page.dart index e93dc90..dd30f1b 100644 --- a/lib/presentation/pages/splash/splash_page.dart +++ b/lib/presentation/pages/splash/splash_page.dart @@ -1,6 +1,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import '../../components/assets/assets.gen.dart'; +import '../../router/app_router.gr.dart'; + @RoutePage() class SplashPage extends StatefulWidget { const SplashPage({super.key}); @@ -10,8 +13,29 @@ class SplashPage extends StatefulWidget { } class _SplashPageState extends State { + _startDelay() => Future.delayed(const Duration(seconds: 2), _goNext); + + _goNext() => context.router.replace(const LoginRoute()); + + @override + void initState() { + _startDelay(); + super.initState(); + } + @override Widget build(BuildContext context) { - return Scaffold(body: Center(child: Text("Splash Page"))); + return Scaffold( + body: Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Assets.images.logo.image( + width: 100, + height: 100, + fit: BoxFit.contain, + ), + ), + ), + ); } } diff --git a/lib/presentation/router/app_router.dart b/lib/presentation/router/app_router.dart index 4c75faf..e66c2f8 100644 --- a/lib/presentation/router/app_router.dart +++ b/lib/presentation/router/app_router.dart @@ -5,7 +5,10 @@ import 'app_router.gr.dart'; class AppRouter extends RootStackRouter { @override List get routes => [ - // Splash - AutoRoute(page: SplashRoute.page, initial: true), - ]; + // Splash + AutoRoute(page: SplashRoute.page, initial: true), + + // Router + AutoRoute(page: LoginRoute.page), + ]; } diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index 841201c..1bec181 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -9,22 +9,40 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:apskel_pos_flutter_v2/presentation/pages/splash/splash_page.dart' +import 'package:apskel_pos_flutter_v2/presentation/pages/auth/login/login_page.dart' as _i1; -import 'package:auto_route/auto_route.dart' as _i2; +import 'package:apskel_pos_flutter_v2/presentation/pages/splash/splash_page.dart' + as _i2; +import 'package:auto_route/auto_route.dart' as _i3; /// generated route for -/// [_i1.SplashPage] -class SplashRoute extends _i2.PageRouteInfo { - const SplashRoute({List<_i2.PageRouteInfo>? children}) +/// [_i1.LoginPage] +class LoginRoute extends _i3.PageRouteInfo { + const LoginRoute({List<_i3.PageRouteInfo>? children}) + : super(LoginRoute.name, initialChildren: children); + + static const String name = 'LoginRoute'; + + static _i3.PageInfo page = _i3.PageInfo( + name, + builder: (data) { + return const _i1.LoginPage(); + }, + ); +} + +/// generated route for +/// [_i2.SplashPage] +class SplashRoute extends _i3.PageRouteInfo { + const SplashRoute({List<_i3.PageRouteInfo>? children}) : super(SplashRoute.name, initialChildren: children); static const String name = 'SplashRoute'; - static _i2.PageInfo page = _i2.PageInfo( + static _i3.PageInfo page = _i3.PageInfo( name, builder: (data) { - return const _i1.SplashPage(); + return const _i2.SplashPage(); }, ); } diff --git a/pubspec.lock b/pubspec.lock index 4d40389..6fa7bae 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.7.1" + another_flushbar: + dependency: "direct main" + description: + name: another_flushbar + sha256: "2b99671c010a7d5770acf5cb24c9f508b919c3a7948b6af9646e773e7da7b757" + url: "https://pub.dev" + source: hosted + version: "1.12.32" archive: dependency: transitive description: @@ -73,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d + url: "https://pub.dev" + source: hosted + version: "9.1.0" boolean_selector: dependency: transitive description: @@ -382,6 +398,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + url: "https://pub.dev" + source: hosted + version: "9.1.1" flutter_gen_core: dependency: transitive description: @@ -414,6 +438,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + sha256: "77850df57c00dc218bfe96071d576a8babec24cf58b2ed121c83cca4a2fdce7f" + url: "https://pub.dev" + source: hosted + version: "5.2.2" flutter_svg: dependency: "direct main" description: @@ -656,6 +688,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" nm: dependency: transitive description: @@ -776,6 +816,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.3" + provider: + dependency: transitive + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" pub_semver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 301b9d9..bcdb115 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,10 @@ dependencies: shared_preferences: ^2.5.3 firebase_core: ^4.2.0 firebase_crashlytics: ^5.0.3 + another_flushbar: ^1.12.32 + flutter_spinkit: ^5.2.2 + bloc: ^9.1.0 + flutter_bloc: ^9.1.1 dev_dependencies: flutter_test: