This commit is contained in:
Mohammed Al-Samarraie
2026-01-13 13:07:31 +03:00
parent ac8a769ff0
commit fa4bee4771
15 changed files with 578 additions and 45 deletions

View File

@@ -0,0 +1,77 @@
import 'package:dio/dio.dart';
import '../../core/error/exceptions.dart';
import '../../core/network/api_client.dart';
import '../dto/login_dto.dart';
import '../dto/login_response_dto.dart';
abstract class AuthRemoteDataSource {
Future<LoginResponseDto> login(LoginDto dto);
}
class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
final ApiClient apiClient;
AuthRemoteDataSourceImpl({required this.apiClient});
@override
Future<LoginResponseDto> login(LoginDto dto) async {
try {
final response = await apiClient.post(
'/Auth/login',
data: dto.toJson(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = response.data;
if (responseData is Map<String, dynamic>) {
return LoginResponseDto.fromJson(responseData);
} else {
throw ServerException(
message: 'استجابة غير صحيحة من الخادم',
statusCode: response.statusCode,
);
}
} else {
throw ServerException(
message: 'فشل تسجيل الدخول',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.receiveTimeout) {
throw NetworkException(message: 'انتهت مهلة الاتصال');
} else if (e.type == DioExceptionType.connectionError) {
throw NetworkException(message: 'لا يوجد اتصال بالانترنيت');
} else if (e.response?.statusCode == 500) {
throw ServerException(message: 'خطأ في الخادم يرجى المحاولة لاحقا');
} else if (e.response != null) {
final message = e.response?.data?['message'] ??
e.response?.data?['error'] ??
'فشل تسجيل الدخول';
// Check for invalid credentials
final customMessage =
message.toString().toLowerCase().contains('invalid') ||
message.toString().toLowerCase().contains('incorrect')
? 'رقم الهاتف أو كلمة المرور غير صحيحة'
: message.toString().toLowerCase().contains('not found')
? 'المستخدم غير موجود'
: message;
throw ServerException(
message: customMessage,
statusCode: e.response?.statusCode,
);
} else {
throw NetworkException(message: 'خطأ في الانترنيت يرجى المحاولة لاحقا');
}
} catch (e) {
if (e is ServerException || e is NetworkException) {
rethrow;
}
throw ServerException(message: 'خطأ غير متوقع');
}
}
}

View File

@@ -0,0 +1,16 @@
class LoginDto {
final String phoneNumber;
final String password;
LoginDto({
required this.phoneNumber,
required this.password,
});
Map<String, dynamic> toJson() {
return {
'phoneNumber': phoneNumber,
'password': password,
};
}
}

View File

@@ -0,0 +1,85 @@
class LoginResponseDto {
final int statusCode;
final bool isSuccess;
final String message;
final LoginDataDto? data;
LoginResponseDto({
required this.statusCode,
required this.isSuccess,
required this.message,
this.data,
});
factory LoginResponseDto.fromJson(Map<String, dynamic> json) {
return LoginResponseDto(
statusCode: json['statusCode'] ?? 0,
isSuccess: json['isSuccess'] ?? false,
message: json['message'] ?? '',
data: json['data'] != null ? LoginDataDto.fromJson(json['data']) : null,
);
}
Map<String, dynamic> toJson() {
return {
'statusCode': statusCode,
'isSuccess': isSuccess,
'message': message,
'data': data?.toJson(),
};
}
}
class LoginDataDto {
final String? token;
final String? id;
final String? employeeId;
final String? username;
final String? fullName;
final String? role;
final String? email;
final String? phoneNumber;
final List<String>? permissions;
LoginDataDto({
this.token,
this.id,
this.employeeId,
this.username,
this.fullName,
this.role,
this.email,
this.phoneNumber,
this.permissions,
});
factory LoginDataDto.fromJson(Map<String, dynamic> json) {
return LoginDataDto(
token: json['token'],
id: json['id'],
employeeId: json['employeeId'],
username: json['username'],
fullName: json['fullName'],
role: json['role'],
email: json['email'],
phoneNumber: json['phoneNumber'],
permissions: json['permissions'] != null
? List<String>.from(json['permissions'])
: null,
);
}
Map<String, dynamic> toJson() {
return {
'token': token,
'id': id,
'employeeId': employeeId,
'username': username,
'fullName': fullName,
'role': role,
'email': email,
'phoneNumber': phoneNumber,
'permissions': permissions,
};
}
}

View File

@@ -0,0 +1,65 @@
import 'package:dartz/dartz.dart';
import '../../core/error/exceptions.dart';
import '../../core/error/failures.dart';
import '../datasources/auth_remote_data_source.dart';
import '../datasources/user_local_data_source.dart';
import '../dto/login_dto.dart';
import '../dto/login_response_dto.dart';
import '../../domain/models/login_request.dart';
import '../../domain/models/login_response_model.dart';
import '../../domain/repositories/auth_repository.dart';
class AuthRepositoryImpl implements AuthRepository {
final AuthRemoteDataSource remoteDataSource;
final UserLocalDataSource localDataSource;
AuthRepositoryImpl({
required this.remoteDataSource,
required this.localDataSource,
});
@override
Future<Either<Failure, LoginResponseModel>> login(LoginRequest request) async {
try {
final dto = LoginDto(
phoneNumber: request.phoneNumber,
password: request.password,
);
final responseDto = await remoteDataSource.login(dto);
// Cache the token locally
if (responseDto.data?.token != null) {
await localDataSource.cacheUserToken(responseDto.data!.token!);
}
// Convert DTO to Model
final responseModel = LoginResponseModel(
statusCode: responseDto.statusCode,
isSuccess: responseDto.isSuccess,
message: responseDto.message,
data: responseDto.data != null
? LoginDataModel(
token: responseDto.data!.token,
id: responseDto.data!.id,
employeeId: responseDto.data!.employeeId,
username: responseDto.data!.username,
fullName: responseDto.data!.fullName,
role: responseDto.data!.role,
email: responseDto.data!.email,
phoneNumber: responseDto.data!.phoneNumber,
permissions: responseDto.data!.permissions,
)
: null,
);
return Right(responseModel);
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on NetworkException catch (e) {
return Left(NetworkFailure(e.message));
} catch (e) {
return Left(ServerFailure('خطأ غير متوقع: $e'));
}
}
}