import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../widgets/app_background.dart'; 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}); @override State createState() => _RequestLeaveScreenState(); } class _RequestLeaveScreenState extends State { // Dropdown value - initialize with default String leaveType = "إجازة مرضية "; // Toggle switch bool isTimedLeave = false; // Date & time selectors DateTime? fromDate = DateTime.now(); DateTime? toDate = DateTime.now(); TimeOfDay? fromTime = const TimeOfDay(hour: 12, minute: 00); TimeOfDay? toTime = const TimeOfDay(hour: 12, minute: 00); // Text controller for reason final TextEditingController reasonController = TextEditingController(); // 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!; DateTime? newDate = await showDatePicker( context: context, initialDate: initial, firstDate: DateTime(2020), lastDate: DateTime(2035), builder: (context, child) { return Theme(data: ThemeData.dark(), child: child!); }, ); if (newDate != null) { setState(() { if (isFrom) { fromDate = newDate; } else { toDate = newDate; } }); } } /// PICK TIME Future pickTime(bool isFrom) async { TimeOfDay initial = isFrom ? fromTime! : toTime!; TimeOfDay? newTime = await showTimePicker( context: context, initialTime: initial, builder: (context, child) { return Theme(data: ThemeData.dark(), child: child!); }, ); if (newTime != null) { setState(() { if (isFrom) { fromTime = newTime; } else { toTime = newTime; } }); } } @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 if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('الرجاء إدخال السبب'), backgroundColor: Colors.red, ), ); } return; } // 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 { // Create vacation request final vacationRequest = VacationRequest( employeeId: employeeId, startDate: finalStartDate, endDate: finalEndDate, reason: reasonController.text, type: typeValue, ); 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) { if (mounted) { Navigator.pop(context); // Close loading dialog ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('حدث خطأ غير متوقع: $e'), backgroundColor: Colors.red, ), ); } } } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, // ✅ IMPORTANT body: AppBackground( child: SafeArea( child: Column( children: [ // In your RequestLeaveScreen's build method, replace the SettingsBar with this: SettingsBar( selectedIndex: -1, onTap: (_) {}, showBackButton: true, onBackTap: () => Navigator.pop(context), iconPaths: const [ "assets/images/user.svg", "assets/images/bell.svg", ], ), // Title // const SizedBox(height: 30), Flexible( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 25), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Fixed alignment for "طلب أجازة" const Align( alignment: Alignment.topRight, child: Text( "طلب أجازة ", style: TextStyle( color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 10), //============================= // DROPDOWN: نوع الإجازة //============================= Align( alignment: Alignment.centerRight, child: const Text( "نوع الإجازة", style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, ), ), ), const SizedBox(height: 5), // Modified dropdown with disabled state Directionality( textDirection: TextDirection.rtl, child: Opacity( opacity: isTimedLeave ? 0.45 : 1.0, child: IgnorePointer( ignoring: isTimedLeave, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 20, ), height: 58, decoration: BoxDecoration( color: isTimedLeave ? Colors.grey[300] : Colors.white, borderRadius: BorderRadius.circular(14), boxShadow: const [ BoxShadow( color: Color(0x22000000), blurRadius: 8, offset: Offset(0, 3), ), ], ), child: DropdownButtonHideUnderline( child: _vacationTypes.isEmpty ? const Center( child: Padding( padding: EdgeInsets.all(8.0), child: CircularProgressIndicator(), ), ) : DropdownButton( value: _selectedVacationTypeValue, 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) { 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(), ), ), ), ), ), ), const SizedBox(height: 25), //============================= // PERFECT CUSTOM TOGGLE (NEW) //============================= GestureDetector( onTap: () { setState(() { isTimedLeave = !isTimedLeave; // Set leave type to TimeOff (value: 1) when toggle is ON if (isTimedLeave) { final timeOffType = _vacationTypes .firstWhere((t) => t.value == 1, orElse: () => _vacationTypes.first); _selectedVacationTypeValue = timeOffType.value; leaveType = _getArabicName(timeOffType.name); } }); }, child: Row( children: [ // ---------- TOGGLE ---------- AnimatedContainer( duration: const Duration(milliseconds: 250), width: 75, height: 30, padding: const EdgeInsets.symmetric( horizontal: 4, ), decoration: BoxDecoration( color: isTimedLeave ? const Color( 0xFF0A6B4A, ) // ON green track : const Color( 0xFF9E9E9E, ), // OFF grey track borderRadius: BorderRadius.circular(40), ), child: Align( alignment: isTimedLeave ? Alignment.centerRight : Alignment.centerLeft, child: AnimatedContainer( duration: const Duration(milliseconds: 250), width: 30, height: 30, decoration: BoxDecoration( color: isTimedLeave ? const Color(0xFF12BE85) // ON knob : Colors.white, // OFF knob shape: BoxShape.circle, boxShadow: [ BoxShadow( color: const Color(0x2D000000), blurRadius: 6, offset: const Offset(0, 2), ), ], ), ), ), ), const SizedBox(width: 14), // ---------- Dot (ALWAYS visible) ---------- Container( width: 10, height: 10, decoration: const BoxDecoration( color: Color(0xFF32C59A), shape: BoxShape.circle, ), ), const SizedBox(width: 6), // ---------- Line (ALWAYS visible) ---------- Expanded( child: Container( height: 2, color: const Color(0xFF32C59A), ), ), const SizedBox(width: 12), // ---------- Label ---------- const Text( "إجازة زمنية", style: TextStyle( color: Colors.white, fontSize: 19, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(height: 20), // ============================= // DATE PICKER BOX // ============================= Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFFF4F4F4), borderRadius: BorderRadius.circular(16), boxShadow: const [ BoxShadow( color: Color(0x33000000), blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, // ALIGN RIGHT children: [ // TEXT aligned right const Text( "فترة الإجازة", style: TextStyle( color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 8), // 🟢 YOUR CUSTOM SVG ICON SvgPicture.asset( "assets/images/calendar.svg", // <-- replace with your icon filename width: 24, height: 24, color: Color( 0xFF007C46, ), // Optional: match your green ), ], ), const SizedBox(height: 15), // From date row _dateRow( label: "من", date: fromDate!, time: fromTime!, onDateTap: isTimedLeave ? null : () => pickDate(true), onTimeTap: () => pickTime(true), ), const SizedBox(height: 15), // To date row _dateRow( label: "الى", date: toDate!, time: toTime!, onDateTap: isTimedLeave ? null : () => pickDate(false), onTimeTap: () => pickTime(false), ), ], ), ), const SizedBox(height: 10), // ============================= // REASON TEXTFIELD (Two Containers) // ============================= Align( alignment: Alignment.centerRight, child: Directionality( textDirection: TextDirection.rtl, child: const Text( "السبب", style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, ), ), ), ), // OUTER BORDER CONTAINER Container( padding: const EdgeInsets.all( 12, ), // border thickness space decoration: BoxDecoration( border: Border.all( color: Color(0xFF00FFAA), // green border width: 0.5, ), borderRadius: BorderRadius.circular(14), ), // INNER TEXTFIELD CONTAINER child: Container( height: 70, padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 10, ), decoration: BoxDecoration( color: const Color(0xFFEAEAEA), borderRadius: BorderRadius.circular(12), ), // Added Directionality to fix text direction child: Directionality( textDirection: TextDirection.rtl, child: TextField( controller: reasonController, maxLines: 5, textAlign: TextAlign .right, // Added this to align text to the right decoration: const InputDecoration( border: InputBorder.none, hintText: "اكتب السبب", // Added placeholder text hintStyle: TextStyle(color: Colors.grey), ), ), ), ), ), const SizedBox(height: 20), // CONFIRM BUTTON Center( child: OnboardingButton( text: "تأكيد الطلب", backgroundColor: const Color(0xFFD1FEF0), textColor: Colors.black, // Changed to black onPressed: _saveLeaveRequest, // Call the save method ), ), ], ), ), ), ), ], ), ), ), ); } // =============================================================== // CUSTOM DATE ROW WIDGET // =============================================================== Widget _dateRow({ required String label, required DateTime date, required TimeOfDay time, required VoidCallback? onDateTap, required VoidCallback onTimeTap, }) { bool dateDisabled = onDateTap == null; return Container( padding: const EdgeInsets.symmetric(horizontal: 12), height: 55, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ // ----------------------- // DATE PART (can be disabled) // ----------------------- Opacity( opacity: dateDisabled ? 0.45 : 1, child: IgnorePointer( ignoring: dateDisabled, child: GestureDetector( onTap: onDateTap, child: Row( children: [ const Icon(Icons.arrow_drop_down), const SizedBox(width: 4), Text( "${date.year}-${date.month}-${date.day}", style: const TextStyle(fontSize: 16), ), const SizedBox(width: 10), Text( _weekday(date.weekday), style: const TextStyle(fontSize: 16), ), ], ), ), ), ), const Spacer(), // ----------------------- // TIME PART (always active) // ----------------------- GestureDetector( onTap: onTimeTap, child: Text( "${time.hour}:${time.minute.toString().padLeft(2, '0')}", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), ), const SizedBox(width: 10), Text( label, style: const TextStyle(color: Colors.black, fontSize: 15), ), ], ), ); } String _weekday(int day) { switch (day) { case 1: return "الإثنين"; case 2: return "الثلاثاء"; case 3: return "الأربعاء"; case 4: return "الخميس"; case 5: return "الجمعة"; case 6: return "السبت"; case 7: return "الأحد"; default: return ""; } } }