This commit is contained in:
Mohammed Al-Samarraie
2026-01-18 19:52:10 +03:00
parent 79b53b6303
commit 33099c4497
19 changed files with 1036 additions and 40 deletions

View File

@@ -10,6 +10,14 @@ import 'request_advance_scrren.dart';
import '../../models/leave_request.dart';
import '../../models/advance_request.dart';
import '../../core/services/request_service.dart';
import '../../core/di/injection_container.dart';
import '../../domain/usecases/get_vacations_usecase.dart';
import '../../domain/usecases/get_advances_usecase.dart';
import '../../domain/models/vacations_list_response_model.dart';
import '../../domain/models/vacation_response_model.dart';
import '../../domain/models/advances_list_response_model.dart';
import '../../domain/models/advance_request_model.dart';
import '../../core/error/failures.dart';
class HolidayScreen extends StatefulWidget {
final void Function(bool isScrollingDown)? onScrollEvent;
@@ -24,8 +32,12 @@ class _HolidayScreenState extends State<HolidayScreen> {
int activeTab = 0;
final RequestService _requestService = RequestService();
final GetVacationsUseCase _getVacationsUseCase = sl<GetVacationsUseCase>();
final GetAdvancesUseCase _getAdvancesUseCase = sl<GetAdvancesUseCase>();
List<LeaveRequest> _leaveRequests = [];
List<AdvanceRequest> _advanceRequests = [];
bool _isLoadingVacations = false;
bool _isLoadingAdvances = false;
final ScrollController _scrollController = ScrollController();
bool _showActions = true;
@@ -62,11 +74,18 @@ class _HolidayScreenState extends State<HolidayScreen> {
}
void _initializeData() async {
_leaveRequests = await _requestService.getLeaveRequests();
_advanceRequests = await _requestService.getAdvanceRequests();
// Load from API
_loadVacationsFromAPI();
_loadAdvancesFromAPI();
// Also listen to local changes (for newly created requests)
_requestService.leaveRequestsStream.listen((requests) {
if (mounted) setState(() => _leaveRequests = requests);
if (mounted) {
setState(() {
// Merge with API data if needed
_leaveRequests = requests;
});
}
});
_requestService.advanceRequestsStream.listen((requests) {
@@ -74,6 +93,140 @@ class _HolidayScreenState extends State<HolidayScreen> {
});
}
Future<void> _loadVacationsFromAPI() async {
setState(() {
_isLoadingVacations = true;
});
final result = await _getVacationsUseCase();
result.fold(
(failure) {
if (mounted) {
setState(() {
_isLoadingVacations = false;
});
// Load from local service as fallback
_requestService.getLeaveRequests().then((requests) {
if (mounted) {
setState(() {
_leaveRequests = requests;
});
}
});
}
},
(response) {
if (mounted && response.data != null) {
setState(() {
_leaveRequests = response.data!.items
.map((vacation) => _convertVacationToLeaveRequest(vacation))
.toList();
_isLoadingVacations = false;
});
}
},
);
}
LeaveRequest _convertVacationToLeaveRequest(VacationDataModel vacation) {
// Convert state (0=waiting, 1=approved, 2=denied) to status string
String status = "waiting";
if (vacation.state == 1) {
status = "approved";
} else if (vacation.state == 2) {
status = "denied";
}
// Convert type to Arabic name
String leaveTypeName = _getArabicVacationTypeName(vacation.type);
// Check if it's timed leave (same day but different times)
bool isTimedLeave = vacation.startDate.year == vacation.endDate.year &&
vacation.startDate.month == vacation.endDate.month &&
vacation.startDate.day == vacation.endDate.day &&
vacation.startDate.hour != vacation.endDate.hour;
return LeaveRequest(
id: vacation.id,
leaveType: leaveTypeName,
isTimedLeave: isTimedLeave,
fromDate: vacation.startDate,
toDate: vacation.endDate,
fromTime: TimeOfDay.fromDateTime(vacation.startDate),
toTime: TimeOfDay.fromDateTime(vacation.endDate),
reason: vacation.reason,
requestDate: vacation.createdAt ?? vacation.startDate,
status: status,
);
}
String _getArabicVacationTypeName(int type) {
switch (type) {
case 1:
return 'أجازة زمنية';
case 2:
return 'إجازة مرضية';
case 3:
return 'إجازة مدفوعة';
case 4:
return 'إجازة غير مدفوعة';
default:
return 'إجازة';
}
}
Future<void> _loadAdvancesFromAPI() async {
setState(() {
_isLoadingAdvances = true;
});
final result = await _getAdvancesUseCase();
result.fold(
(failure) {
if (mounted) {
setState(() {
_isLoadingAdvances = false;
});
// Load from local service as fallback
_requestService.getAdvanceRequests().then((requests) {
if (mounted) {
setState(() {
_advanceRequests = requests;
});
}
});
}
},
(response) {
if (mounted && response.data != null) {
setState(() {
_advanceRequests = response.data!.items
.map((advance) => _convertAdvanceToAdvanceRequest(advance))
.toList();
_isLoadingAdvances = false;
});
}
},
);
}
AdvanceRequest _convertAdvanceToAdvanceRequest(AdvanceDataModel advance) {
// Convert state (0=waiting, 1=approved, 2=denied) to status string
String status = "waiting";
if (advance.state == 1) {
status = "approved";
} else if (advance.state == 2) {
status = "denied";
}
return AdvanceRequest(
id: advance.id,
amount: advance.amount,
reason: advance.reason,
status: status,
);
}
@override
Widget build(BuildContext context) {
return Stack(
@@ -259,6 +412,19 @@ class _HolidayScreenState extends State<HolidayScreen> {
// ----------------------------------------------------------------
Widget _buildLeaveRequestsSliver() {
if (_isLoadingVacations) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: Center(
child: CircularProgressIndicator(
color: Color(0xFF8EFDC2),
),
),
),
);
}
if (_leaveRequests.isEmpty) {
return SliverToBoxAdapter(
child: Padding(
@@ -287,6 +453,19 @@ class _HolidayScreenState extends State<HolidayScreen> {
}
Widget _buildAdvanceRequestsSliver() {
if (_isLoadingAdvances) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: Center(
child: CircularProgressIndicator(
color: Color(0xFF8EFDC2),
),
),
),
);
}
if (_advanceRequests.isEmpty) {
return SliverToBoxAdapter(
child: Padding(

View File

@@ -5,6 +5,11 @@ import '../widgets/settings_bar.dart';
import '../widgets/onboarding_button.dart';
import '../../models/advance_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_advance_usecase.dart';
import '../../domain/models/advance_request_model.dart';
import '../../core/error/failures.dart';
class RequestAdvanceScreen extends StatefulWidget {
const RequestAdvanceScreen({super.key});
@@ -23,16 +28,30 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
// Use the singleton instance
final RequestService _requestService = RequestService();
// Use case
final CreateAdvanceUseCase _createAdvanceUseCase = sl<CreateAdvanceUseCase>();
String _getFailureMessage(Failure failure) {
if (failure is ServerFailure) {
return failure.message;
} else if (failure is NetworkFailure) {
return failure.message;
}
return 'حدث خطأ غير متوقع';
}
// Method to save the advance request
Future<void> _saveAdvanceRequest() async {
if (amountController.text.isEmpty || reasonController.text.isEmpty) {
// Show an error message if fields are 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;
}
@@ -40,45 +59,96 @@ class _RequestAdvanceScreenState extends State<RequestAdvanceScreen> {
final amount = double.tryParse(amountController.text);
if (amount == null || amount <= 0) {
// Show an error message if amount is invalid
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 advance request with default status "waiting"
final advanceRequest = AdvanceRequest(
id: DateTime.now().millisecondsSinceEpoch.toString(),
amount: amount,
reason: reasonController.text,
status: "waiting", // Default status
);
// Get employee ID
final employeeId = await sl<UserLocalDataSource>().getCachedEmployeeId();
if (employeeId == null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('خطأ: لم يتم العثور على رقم الموظف'),
backgroundColor: Colors.red,
),
);
}
return;
}
// Show loading indicator
if (mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(child: CircularProgressIndicator()),
);
}
try {
// Save the advance request
await _requestService.addAdvanceRequest(advanceRequest);
// Show a success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('تم إرسال طلب السلفة بنجاح'),
backgroundColor: Colors.green,
),
// Create advance request model
final advanceRequestModel = AdvanceRequestModel(
employeeId: employeeId,
date: DateTime.now(),
amount: amount,
reason: reasonController.text,
);
// Navigate back to the previous screen
Navigator.pop(context);
final result = await _createAdvanceUseCase(advanceRequestModel);
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 advanceRequest = AdvanceRequest(
id: response.data?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
amount: amount,
reason: reasonController.text,
status: "waiting", // Default status
);
_requestService.addAdvanceRequest(advanceRequest);
// 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,
),
);
}
}
}