diff --git a/lib/core/di/injection_container.dart b/lib/core/di/injection_container.dart index 4cc12c6..b61c9bd 100644 --- a/lib/core/di/injection_container.dart +++ b/lib/core/di/injection_container.dart @@ -7,11 +7,16 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../network/api_client.dart'; import '../../data/datasources/auth_remote_data_source.dart'; import '../../data/datasources/user_local_data_source.dart'; +import '../../data/datasources/vacation_remote_data_source.dart'; import '../../data/repositories/auth_repository_impl.dart'; +import '../../data/repositories/vacation_repository_impl.dart'; import '../../domain/repositories/auth_repository.dart'; +import '../../domain/repositories/vacation_repository.dart'; import '../../domain/usecases/login_usecase.dart'; import '../../domain/usecases/attendance_login_usecase.dart'; import '../../domain/usecases/attendance_logout_usecase.dart'; +import '../../domain/usecases/create_vacation_usecase.dart'; +import '../../domain/usecases/get_vacation_types_usecase.dart'; import '../../presentation/blocs/login/login_bloc.dart'; final sl = GetIt.instance; @@ -61,4 +66,16 @@ Future initializeDependencies() async { sl.registerLazySingleton(() => AttendanceLoginUsecase(repository: sl())); sl.registerLazySingleton(() => AttendanceLogoutUseCase(repository: sl())); + + // Vacation + sl.registerLazySingleton( + () => VacationRemoteDataSourceImpl(apiClient: sl()), + ); + + sl.registerLazySingleton( + () => VacationRepositoryImpl(remoteDataSource: sl()), + ); + + sl.registerLazySingleton(() => CreateVacationUseCase(repository: sl())); + sl.registerLazySingleton(() => GetVacationTypesUseCase(repository: sl())); } diff --git a/lib/data/datasources/vacation_remote_data_source.dart b/lib/data/datasources/vacation_remote_data_source.dart new file mode 100644 index 0000000..af3a7d4 --- /dev/null +++ b/lib/data/datasources/vacation_remote_data_source.dart @@ -0,0 +1,124 @@ +import 'package:dio/dio.dart'; +import '../../core/error/exceptions.dart'; +import '../../core/network/api_client.dart'; +import '../dto/vacation_request_dto.dart'; +import '../dto/vacation_response_dto.dart'; +import '../dto/vacation_type_dto.dart'; + +abstract class VacationRemoteDataSource { + Future createVacation(VacationRequestDto request); + Future getVacationTypes(); +} + +class VacationRemoteDataSourceImpl implements VacationRemoteDataSource { + final ApiClient apiClient; + + VacationRemoteDataSourceImpl({required this.apiClient}); + + @override + Future createVacation(VacationRequestDto request) async { + try { + final response = await apiClient.post( + '/Vacation', + data: request.toJson(), + ); + + if (response.statusCode == 200 || response.statusCode == 201) { + final responseData = response.data; + + if (responseData is Map) { + return VacationResponseDto.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'] ?? + 'فشل إنشاء طلب الإجازة'; + + throw ServerException( + message: message.toString(), + statusCode: e.response?.statusCode, + ); + } else { + throw NetworkException(message: 'خطأ في الانترنيت يرجى المحاولة لاحقا'); + } + } catch (e) { + if (e is ServerException || e is NetworkException) { + rethrow; + } + print('خطأ غير متوقع: $e'); + throw ServerException(message: 'خطأ غير متوقع'); + } + } + + @override + Future getVacationTypes() async { + try { + final response = await apiClient.get('/enums/vacation-types'); + + if (response.statusCode == 200) { + final responseData = response.data; + + if (responseData is Map) { + return VacationTypesResponseDto.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'] ?? + 'فشل جلب أنواع الإجازات'; + + throw ServerException( + message: message.toString(), + statusCode: e.response?.statusCode, + ); + } else { + throw NetworkException(message: 'خطأ في الانترنيت يرجى المحاولة لاحقا'); + } + } catch (e) { + if (e is ServerException || e is NetworkException) { + rethrow; + } + print('خطأ غير متوقع: $e'); + throw ServerException(message: 'خطأ غير متوقع'); + } + } +} diff --git a/lib/data/dto/vacation_request_dto.dart b/lib/data/dto/vacation_request_dto.dart new file mode 100644 index 0000000..ebac630 --- /dev/null +++ b/lib/data/dto/vacation_request_dto.dart @@ -0,0 +1,25 @@ +class VacationRequestDto { + final String employeeId; + final DateTime startDate; + final DateTime endDate; + final String reason; + final int type; + + VacationRequestDto({ + required this.employeeId, + required this.startDate, + required this.endDate, + required this.reason, + required this.type, + }); + + Map toJson() { + return { + 'employeeId': employeeId, + 'startDate': startDate.toIso8601String(), + 'endDate': endDate.toIso8601String(), + 'reason': reason, + 'type': type, + }; + } +} diff --git a/lib/data/dto/vacation_response_dto.dart b/lib/data/dto/vacation_response_dto.dart new file mode 100644 index 0000000..d9f1e17 --- /dev/null +++ b/lib/data/dto/vacation_response_dto.dart @@ -0,0 +1,89 @@ +class VacationResponseDto { + final int statusCode; + final bool isSuccess; + final String message; + final VacationDataDto? data; + + VacationResponseDto({ + required this.statusCode, + required this.isSuccess, + required this.message, + this.data, + }); + + factory VacationResponseDto.fromJson(Map json) { + return VacationResponseDto( + statusCode: json['statusCode'] ?? 0, + isSuccess: json['isSuccess'] ?? false, + message: json['message'] ?? '', + data: json['data'] != null ? VacationDataDto.fromJson(json['data']) : null, + ); + } +} + +class VacationDataDto { + final String employeeId; + final String? employeeFullName; + final DateTime startDate; + final DateTime endDate; + final String reason; + final String? submittedBy; + final String? submittedByUser; + final int state; + final int type; + final String id; + final DateTime? createdAt; + final DateTime? updatedAt; + final DateTime? deletedAt; + final bool? isDeleted; + + VacationDataDto({ + required this.employeeId, + this.employeeFullName, + required this.startDate, + required this.endDate, + required this.reason, + this.submittedBy, + this.submittedByUser, + required this.state, + required this.type, + required this.id, + this.createdAt, + this.updatedAt, + this.deletedAt, + this.isDeleted, + }); + + factory VacationDataDto.fromJson(Map json) { + return VacationDataDto( + employeeId: json['employeeId']?.toString() ?? '', + employeeFullName: json['employeeFullName'], + startDate: _parseDateTime(json['startDate'])!, + endDate: _parseDateTime(json['endDate'])!, + reason: json['reason']?.toString() ?? '', + submittedBy: json['submittedBy'], + submittedByUser: json['submittedByUser'], + state: json['state'] ?? 0, + type: json['type'] ?? 0, + id: json['id']?.toString() ?? '', + createdAt: _parseDateTime(json['createdAt']), + updatedAt: _parseDateTime(json['updatedAt']), + deletedAt: _parseDateTime(json['deletedAt']), + isDeleted: json['isDeleted'], + ); + } + + static DateTime? _parseDateTime(dynamic value) { + if (value == null) return null; + if (value is DateTime) return value; + if (value is String) { + try { + return DateTime.parse(value); + } catch (e) { + print('Error parsing date: $value - $e'); + return null; + } + } + return null; + } +} diff --git a/lib/data/dto/vacation_type_dto.dart b/lib/data/dto/vacation_type_dto.dart new file mode 100644 index 0000000..2683fd2 --- /dev/null +++ b/lib/data/dto/vacation_type_dto.dart @@ -0,0 +1,43 @@ +class VacationTypesResponseDto { + final int statusCode; + final bool isSuccess; + final String? message; + final List data; + + VacationTypesResponseDto({ + required this.statusCode, + required this.isSuccess, + this.message, + required this.data, + }); + + factory VacationTypesResponseDto.fromJson(Map json) { + return VacationTypesResponseDto( + statusCode: json['statusCode'] ?? 0, + isSuccess: json['isSuccess'] ?? false, + message: json['message'], + data: json['data'] != null + ? (json['data'] as List) + .map((item) => VacationTypeDto.fromJson(item)) + .toList() + : [], + ); + } +} + +class VacationTypeDto { + final int value; + final String name; + + VacationTypeDto({ + required this.value, + required this.name, + }); + + factory VacationTypeDto.fromJson(Map json) { + return VacationTypeDto( + value: json['value'] ?? 0, + name: json['name']?.toString() ?? '', + ); + } +} diff --git a/lib/data/repositories/vacation_repository_impl.dart b/lib/data/repositories/vacation_repository_impl.dart new file mode 100644 index 0000000..f7bcc74 --- /dev/null +++ b/lib/data/repositories/vacation_repository_impl.dart @@ -0,0 +1,93 @@ +import 'package:dartz/dartz.dart'; +import '../../core/error/exceptions.dart'; +import '../../core/error/failures.dart'; +import '../datasources/vacation_remote_data_source.dart'; +import '../dto/vacation_request_dto.dart'; +import '../../domain/models/vacation_request.dart'; +import '../../domain/models/vacation_response_model.dart'; +import '../../domain/models/vacation_type_model.dart'; +import '../../domain/repositories/vacation_repository.dart'; + +class VacationRepositoryImpl implements VacationRepository { + final VacationRemoteDataSource remoteDataSource; + + VacationRepositoryImpl({required this.remoteDataSource}); + + @override + Future> createVacation( + VacationRequest request, + ) async { + try { + final dto = VacationRequestDto( + employeeId: request.employeeId, + startDate: request.startDate, + endDate: request.endDate, + reason: request.reason, + type: request.type, + ); + + final responseDto = await remoteDataSource.createVacation(dto); + + // Convert DTO to Model + final responseModel = VacationResponseModel( + statusCode: responseDto.statusCode, + isSuccess: responseDto.isSuccess, + message: responseDto.message, + data: responseDto.data != null + ? VacationDataModel( + employeeId: responseDto.data!.employeeId, + employeeFullName: responseDto.data!.employeeFullName, + startDate: responseDto.data!.startDate, + endDate: responseDto.data!.endDate, + reason: responseDto.data!.reason, + submittedBy: responseDto.data!.submittedBy, + submittedByUser: responseDto.data!.submittedByUser, + state: responseDto.data!.state, + type: responseDto.data!.type, + id: responseDto.data!.id, + createdAt: responseDto.data!.createdAt, + updatedAt: responseDto.data!.updatedAt, + deletedAt: responseDto.data!.deletedAt, + isDeleted: responseDto.data!.isDeleted, + ) + : 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')); + } + } + + @override + Future> getVacationTypes() async { + try { + final responseDto = await remoteDataSource.getVacationTypes(); + + // Convert DTO to Model + final responseModel = VacationTypesResponseModel( + statusCode: responseDto.statusCode, + isSuccess: responseDto.isSuccess, + message: responseDto.message, + data: responseDto.data + .map((dto) => VacationTypeModel( + value: dto.value, + name: dto.name, + )) + .toList(), + ); + + 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')); + } + } +} diff --git a/lib/domain/models/vacation_request.dart b/lib/domain/models/vacation_request.dart new file mode 100644 index 0000000..96b8254 --- /dev/null +++ b/lib/domain/models/vacation_request.dart @@ -0,0 +1,15 @@ +class VacationRequest { + final String employeeId; + final DateTime startDate; + final DateTime endDate; + final String reason; + final int type; + + VacationRequest({ + required this.employeeId, + required this.startDate, + required this.endDate, + required this.reason, + required this.type, + }); +} diff --git a/lib/domain/models/vacation_response_model.dart b/lib/domain/models/vacation_response_model.dart new file mode 100644 index 0000000..bac222a --- /dev/null +++ b/lib/domain/models/vacation_response_model.dart @@ -0,0 +1,47 @@ +class VacationResponseModel { + final int statusCode; + final bool isSuccess; + final String message; + final VacationDataModel? data; + + VacationResponseModel({ + required this.statusCode, + required this.isSuccess, + required this.message, + this.data, + }); +} + +class VacationDataModel { + final String employeeId; + final String? employeeFullName; + final DateTime startDate; + final DateTime endDate; + final String reason; + final String? submittedBy; + final String? submittedByUser; + final int state; + final int type; + final String id; + final DateTime? createdAt; + final DateTime? updatedAt; + final DateTime? deletedAt; + final bool? isDeleted; + + VacationDataModel({ + required this.employeeId, + this.employeeFullName, + required this.startDate, + required this.endDate, + required this.reason, + this.submittedBy, + this.submittedByUser, + required this.state, + required this.type, + required this.id, + this.createdAt, + this.updatedAt, + this.deletedAt, + this.isDeleted, + }); +} diff --git a/lib/domain/models/vacation_type_model.dart b/lib/domain/models/vacation_type_model.dart new file mode 100644 index 0000000..8152c3c --- /dev/null +++ b/lib/domain/models/vacation_type_model.dart @@ -0,0 +1,23 @@ +class VacationTypeModel { + final int value; + final String name; + + VacationTypeModel({ + required this.value, + required this.name, + }); +} + +class VacationTypesResponseModel { + final int statusCode; + final bool isSuccess; + final String? message; + final List data; + + VacationTypesResponseModel({ + required this.statusCode, + required this.isSuccess, + this.message, + required this.data, + }); +} diff --git a/lib/domain/repositories/vacation_repository.dart b/lib/domain/repositories/vacation_repository.dart new file mode 100644 index 0000000..be5479c --- /dev/null +++ b/lib/domain/repositories/vacation_repository.dart @@ -0,0 +1,12 @@ +import 'package:dartz/dartz.dart'; +import '../../core/error/failures.dart'; +import '../models/vacation_request.dart'; +import '../models/vacation_response_model.dart'; +import '../models/vacation_type_model.dart'; + +abstract class VacationRepository { + Future> createVacation( + VacationRequest request, + ); + Future> getVacationTypes(); +} diff --git a/lib/domain/usecases/create_vacation_usecase.dart b/lib/domain/usecases/create_vacation_usecase.dart new file mode 100644 index 0000000..b2ffc0e --- /dev/null +++ b/lib/domain/usecases/create_vacation_usecase.dart @@ -0,0 +1,15 @@ +import 'package:dartz/dartz.dart'; +import '../../core/error/failures.dart'; +import '../models/vacation_request.dart'; +import '../models/vacation_response_model.dart'; +import '../repositories/vacation_repository.dart'; + +class CreateVacationUseCase { + final VacationRepository repository; + + CreateVacationUseCase({required this.repository}); + + Future> call(VacationRequest request) { + return repository.createVacation(request); + } +} diff --git a/lib/domain/usecases/get_vacation_types_usecase.dart b/lib/domain/usecases/get_vacation_types_usecase.dart new file mode 100644 index 0000000..97244dd --- /dev/null +++ b/lib/domain/usecases/get_vacation_types_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; +import '../../core/error/failures.dart'; +import '../models/vacation_type_model.dart'; +import '../repositories/vacation_repository.dart'; + +class GetVacationTypesUseCase { + final VacationRepository repository; + + GetVacationTypesUseCase({required this.repository}); + + Future> call() { + return repository.getVacationTypes(); + } +} diff --git a/lib/presentation/screens/request_leave_screen.dart b/lib/presentation/screens/request_leave_screen.dart index 78d24bf..8e7d7bd 100644 --- a/lib/presentation/screens/request_leave_screen.dart +++ b/lib/presentation/screens/request_leave_screen.dart @@ -5,6 +5,13 @@ import '../widgets/settings_bar.dart'; import '../widgets/onboarding_button.dart'; import '../../models/leave_request.dart'; import '../../core/services/request_service.dart'; +import '../../core/di/injection_container.dart'; +import '../../data/datasources/user_local_data_source.dart'; +import '../../domain/usecases/create_vacation_usecase.dart'; +import '../../domain/usecases/get_vacation_types_usecase.dart'; +import '../../domain/models/vacation_request.dart'; +import '../../domain/models/vacation_type_model.dart'; +import '../../core/error/failures.dart'; class RequestLeaveScreen extends StatefulWidget { const RequestLeaveScreen({super.key}); @@ -33,6 +40,14 @@ class _RequestLeaveScreenState extends State { // Use the singleton instance final RequestService _requestService = RequestService(); + // Use cases + final CreateVacationUseCase _createVacationUseCase = sl(); + final GetVacationTypesUseCase _getVacationTypesUseCase = sl(); + + // Vacation types from API + List _vacationTypes = []; + int? _selectedVacationTypeValue; // Store selected type value instead of string + /// PICK DATE Future pickDate(bool isFrom) async { DateTime initial = isFrom ? fromDate! : toDate!; @@ -79,52 +94,211 @@ class _RequestLeaveScreenState extends State { } } + @override + void initState() { + super.initState(); + _loadVacationTypes(); + } + + Future _loadVacationTypes() async { + final result = await _getVacationTypesUseCase(); + result.fold( + (failure) { + // Silently fail - user can still submit with default types + print('Failed to load vacation types: ${_getFailureMessage(failure)}'); + }, + (response) { + if (mounted) { + setState(() { + _vacationTypes = response.data; + // Set default to SickLeave (value: 2) if available + if (_vacationTypes.isNotEmpty) { + final sickLeave = _vacationTypes.firstWhere( + (type) => type.value == 2, + orElse: () => _vacationTypes.first, + ); + _selectedVacationTypeValue = sickLeave.value; + leaveType = _getArabicName(sickLeave.name); + } + }); + } + }, + ); + } + + String _getArabicName(String apiName) { + // Map API names to Arabic display names + switch (apiName) { + case 'TimeOff': + return 'أجازة زمنية'; + case 'SickLeave': + return 'إجازة مرضية'; + case 'PaidLeave': + return 'إجازة مدفوعة'; + case 'UnpaidLeave': + return 'إجازة غير مدفوعة'; + default: + return apiName; + } + } + + int _getVacationTypeValue() { + // Use selected value if available, otherwise fallback to mapping + if (_selectedVacationTypeValue != null) { + return _selectedVacationTypeValue!; + } + // Fallback: Map display names to API type values + if (leaveType.contains("مرضية") || leaveType == "SickLeave") { + return 2; // SickLeave + } else if (leaveType.contains("مدفوعة") && !leaveType.contains("غير")) { + return 3; // PaidLeave + } else if (leaveType.contains("غير مدفوعة") || leaveType == "UnpaidLeave") { + return 4; // UnpaidLeave + } else if (leaveType.contains("زمنية") || leaveType == "TimeOff") { + return 1; // TimeOff + } + return 1; // Default to TimeOff + } + + String _getFailureMessage(Failure failure) { + if (failure is ServerFailure) { + return failure.message; + } else if (failure is NetworkFailure) { + return failure.message; + } + return 'حدث خطأ غير متوقع'; + } + // Method to save the leave request Future _saveLeaveRequest() async { if (reasonController.text.isEmpty) { // Show an error message if reason is empty - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('الرجاء إدخال السبب'), - backgroundColor: Colors.red, - ), - ); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('الرجاء إدخال السبب'), + backgroundColor: Colors.red, + ), + ); + } return; } - // Create a new leave request with default status "waiting" - final leaveRequest = LeaveRequest( - id: DateTime.now().millisecondsSinceEpoch.toString(), - leaveType: leaveType, // Use the current leaveType value - isTimedLeave: isTimedLeave, - fromDate: fromDate!, - toDate: toDate!, - fromTime: fromTime!, - toTime: toTime!, - reason: reasonController.text, - requestDate: DateTime.now(), - status: "waiting", // Default status - ); + // Get employee ID + final employeeId = await sl().getCachedEmployeeId(); + if (employeeId == null) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('خطأ: لم يتم العثور على رقم الموظف'), + backgroundColor: Colors.red, + ), + ); + } + return; + } + + // Prepare dates - if timed leave, use same date with time differences + DateTime finalStartDate = fromDate!; + DateTime finalEndDate = toDate!; + + if (isTimedLeave) { + // For timed leave, use the same date but with different times + finalStartDate = DateTime( + fromDate!.year, + fromDate!.month, + fromDate!.day, + fromTime!.hour, + fromTime!.minute, + ); + finalEndDate = DateTime( + fromDate!.year, + fromDate!.month, + fromDate!.day, + toTime!.hour, + toTime!.minute, + ); + } else { + // For regular leave, use dates at midnight + finalStartDate = DateTime(fromDate!.year, fromDate!.month, fromDate!.day); + finalEndDate = DateTime(toDate!.year, toDate!.month, toDate!.day); + } + + // Get vacation type value + final typeValue = _getVacationTypeValue(); + + // Show loading indicator + if (mounted) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center(child: CircularProgressIndicator()), + ); + } try { - // Save the leave request - await _requestService.addLeaveRequest(leaveRequest); - - // Show a success message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('تم إرسال طلب الأجازة بنجاح'), - backgroundColor: Colors.green, - ), + // Create vacation request + final vacationRequest = VacationRequest( + employeeId: employeeId, + startDate: finalStartDate, + endDate: finalEndDate, + reason: reasonController.text, + type: typeValue, ); - // Navigate back to the previous screen - Navigator.pop(context); + final result = await _createVacationUseCase(vacationRequest); + + if (mounted) { + Navigator.pop(context); // Close loading dialog + + result.fold( + (failure) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(_getFailureMessage(failure)), + backgroundColor: Colors.red, + ), + ); + }, + (response) { + // Also save locally for UI display + final leaveRequest = LeaveRequest( + id: response.data?.id ?? DateTime.now().millisecondsSinceEpoch.toString(), + leaveType: leaveType, + isTimedLeave: isTimedLeave, + fromDate: fromDate!, + toDate: toDate!, + fromTime: fromTime!, + toTime: toTime!, + reason: reasonController.text, + requestDate: DateTime.now(), + status: "waiting", // Default status + ); + _requestService.addLeaveRequest(leaveRequest); + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('تم إرسال طلب الأجازة بنجاح'), + backgroundColor: Colors.green, + ), + ); + + // Navigate back to the previous screen + Navigator.pop(context); + }, + ); + } } catch (e) { - // Show an error message if something went wrong - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('حدث خطأ: $e'), backgroundColor: Colors.red), - ); + if (mounted) { + Navigator.pop(context); // Close loading dialog + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('حدث خطأ غير متوقع: $e'), + backgroundColor: Colors.red, + ), + ); + } } } @@ -216,68 +390,51 @@ class _RequestLeaveScreenState extends State { ], ), child: DropdownButtonHideUnderline( - child: DropdownButton( - value: leaveType, - icon: const Icon( - Icons.keyboard_arrow_down_rounded, - color: Colors.black, - size: 28, - ), - style: const TextStyle( - color: Colors.black, - fontSize: 17, - ), - isExpanded: true, - onChanged: (value) { - setState(() { - leaveType = value!; - // Set toggle based on selected value - isTimedLeave = value == "أجازة زمنية"; - }); - }, - items: [ - DropdownMenuItem( - value: "إجازة مرضية ", - child: Directionality( - textDirection: TextDirection.rtl, - child: Align( - alignment: Alignment.centerRight, - child: Text("إجازة مرضية "), + child: _vacationTypes.isEmpty + ? const Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), ), - ), - ), - DropdownMenuItem( - value: "إجازة مدفوعة", - child: Directionality( - textDirection: TextDirection.rtl, - child: Align( - alignment: Alignment.centerRight, - child: Text("إجازة مدفوعة"), + ) + : DropdownButton( + value: _selectedVacationTypeValue, + icon: const Icon( + Icons.keyboard_arrow_down_rounded, + color: Colors.black, + size: 28, ), - ), - ), - DropdownMenuItem( - value: "إجازة غير مدفوعة", - child: Directionality( - textDirection: TextDirection.rtl, - child: Align( - alignment: Alignment.centerRight, - child: Text("إجازة غير مدفوعة"), + style: const TextStyle( + color: Colors.black, + fontSize: 17, ), + isExpanded: true, + onChanged: (value) { + if (value != null) { + setState(() { + _selectedVacationTypeValue = value; + final selectedType = _vacationTypes + .firstWhere((t) => t.value == value); + leaveType = _getArabicName(selectedType.name); + // Set toggle based on selected value (TimeOff = 1) + isTimedLeave = value == 1; + }); + } + }, + items: _vacationTypes.map((type) { + final arabicName = _getArabicName(type.name); + return DropdownMenuItem( + value: type.value, + child: Directionality( + textDirection: TextDirection.rtl, + child: Align( + alignment: Alignment.centerRight, + child: Text(arabicName), + ), + ), + ); + }).toList(), ), - ), - DropdownMenuItem( - value: "أجازة زمنية", - child: Directionality( - textDirection: TextDirection.rtl, - child: Align( - alignment: Alignment.centerRight, - child: Text("أجازة زمنية"), - ), - ), - ), - ], - ), ), ), ), @@ -293,9 +450,12 @@ class _RequestLeaveScreenState extends State { onTap: () { setState(() { isTimedLeave = !isTimedLeave; - // Set leave type to "أجازة زمنية" when toggle is ON + // Set leave type to TimeOff (value: 1) when toggle is ON if (isTimedLeave) { - leaveType = "أجازة زمنية"; + final timeOffType = _vacationTypes + .firstWhere((t) => t.value == 1, orElse: () => _vacationTypes.first); + _selectedVacationTypeValue = timeOffType.value; + leaveType = _getArabicName(timeOffType.name); } }); },