import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../../Models/hrmModels/attendanceRequestListResponse.dart'; import '../../Models/ordersModels/commonResponse.dart'; import '../../Utils/app_colors.dart'; import '../../services/api_calling.dart'; import '../HomeScreenNotifier.dart'; class Attendancelistprovider extends ChangeNotifier { attendanceRequestListResponse? _response; bool _isLoading = false; String? _errorMessage; attendanceRequestListResponse? get response => _response; bool get isLoading => _isLoading; String? get errorMessage => _errorMessage; // Filter states String _selectedType = "All"; String _selectedDateRange = "This Month"; DateTimeRange? _customDateRange; String get selectedType => _selectedType; String get selectedDateRange => _selectedDateRange; DateTimeRange? get customDateRange => _customDateRange; // Addition of attendance CommonResponse? _addResponse; CommonResponse? get addResponse => _addResponse; bool _isSubmitting = false; bool get isSubmitting => _isSubmitting; // Date controllers for filter UI final TextEditingController fromDateController = TextEditingController(); final TextEditingController toDateController = TextEditingController(); // For manual attendance date field final TextEditingController dateController = TextEditingController(); DateTime? _selectedDate; DateTime? get selectedDate => _selectedDate; // Type options for filter final List typeOptions = ["All", "Check In", "Check Out", "Check In/Out"]; // Date range options for filter final List dateRangeOptions = [ "All", "Today", "Yesterday", "This Month", "Past 7 days", "Last Month", "Custom", ]; bool isDateValid() { if (_selectedDate == null) return false; DateTime today = DateTime.now(); DateTime yesterday = today.subtract(const Duration(days: 1)); // normalize (remove time part) DateTime normalizedSelected = DateTime(_selectedDate!.year, _selectedDate!.month, _selectedDate!.day); DateTime normalizedToday = DateTime(today.year, today.month, today.day); DateTime normalizedYesterday = DateTime(yesterday.year, yesterday.month, yesterday.day); return normalizedSelected == normalizedToday || normalizedSelected == normalizedYesterday; } String? validateManualAttendance({//its working or not required String type, required String? checkInTime, required String? checkInLoc, required File? checkInProof, required String? checkOutTime, required String? checkOutLoc, required File? checkOutProof, required String? checkInDesc, required String? checkOutDesc, }) { if (!isDateValid()) { return "Date must be today or yesterday"; } if (type.isEmpty) return "Please select type"; if (type == "Check In") { if ((checkInTime ?? "").isEmpty || (checkInLoc ?? "").isEmpty || (checkInDesc ?? "").isEmpty || checkInProof == null) { return "Please fill all Check In fields"; } } if (type == "Check Out") { if ((checkOutTime ?? "").isEmpty || (checkOutLoc ?? "").isEmpty || (checkOutDesc ?? "").isEmpty || checkOutProof == null) { return "Please fill all Check Out fields"; } } if (type == "Check In/Out") { if ((checkInTime ?? "").isEmpty || (checkInLoc ?? "").isEmpty || (checkInDesc ?? "").isEmpty || checkInProof == null || (checkOutTime ?? "").isEmpty || (checkOutLoc ?? "").isEmpty || (checkOutDesc ?? "").isEmpty || checkOutProof == null) { return "Please fill all Check In & Check Out fields"; } } return null; // everything ok } CommonResponse? _RejectResponse; CommonResponse? get RejectResponse => _RejectResponse; /// Fetch attendance request list with filters Future fetchAttendanceRequests(BuildContext context, mode, {String? type, String? dateRange, DateTimeRange? customRange}) async { _isLoading = true; _errorMessage = null; notifyListeners(); try { final provider = Provider.of(context, listen: false); // Update filter states if provided if (type != null) _selectedType = type; if (dateRange != null) _selectedDateRange = dateRange; if (customRange != null) _customDateRange = customRange; // Calculate date range based on selection final dateParams = _getDateRangeParams(_selectedDateRange, _customDateRange); // Convert "All" type to empty string for API final apiType = _selectedType == "All" ? "" : _selectedType; final result = await ApiCalling.attendanceRequestListAPI( provider.empId, provider.session, apiType, dateParams['from']!, dateParams['to']!, mode ); debugPrint('Fetching attendance from: ${dateParams['from']} to: ${dateParams['to']}'); if (result != null) { _response = result; if (_response?.requestList == null || _response!.requestList!.isEmpty) { _errorMessage = "No attendance records found!"; } } else { _errorMessage = "No data found!"; } } catch (e) { _errorMessage = "Error: $e"; debugPrint('Error fetching attendance: $e'); } _isLoading = false; notifyListeners(); } /// --- Add Attendance Request --- Future addAttendanceRequest( BuildContext context, { required String process, required String type, required String loc, required String checkDate, String? checkInTime, String? checkInLoc, File? checkInProof, String? checkOutTime, String? checkOutLoc, File? checkOutProof, String? note, }) async { _isSubmitting = true; _errorMessage = null; _addResponse = null; notifyListeners(); try { final homeProvider = Provider.of(context, listen: false); final result = await ApiCalling.addAttendanceRequestAPI( sessionId: homeProvider.session, empId: homeProvider.empId, process: process, type: type, loc: loc, checkDate: checkDate, checkInTime: checkInTime, checkInLoc: checkInLoc, checkInProof: checkInProof, checkOutTime: checkOutTime, checkOutLoc: checkOutLoc, checkOutProof: checkOutProof, note: note, ); if (result != null) { _addResponse = result; if (result.error != null && result.error!.isNotEmpty) { _errorMessage = result.error; } else { _addResponse = result; } } else { _errorMessage = "Failed to submit request!"; } } catch (e) { _errorMessage = "Error submitting request: $e"; } _isSubmitting = false; notifyListeners(); } Future rejectApproveAttendanceRequest({ required String session, required String empId, required String mode, required String type, required String remarks, required String id, }) async { _isSubmitting = true; _errorMessage = null; _RejectResponse = null; notifyListeners(); try { final result = await ApiCalling.attendanceRequestApproveRejectAPI( session, empId, mode, type, remarks, id, ); print("*********************************object"); if (result != null) { _RejectResponse = result; if (result.error != null && result.error!.isNotEmpty) { _errorMessage = result.error; } else { debugPrint("Attendance request $type successfully."); } } else { _errorMessage = "Failed to process attendance request!"; } } catch (e) { _errorMessage = "Error processing attendance request: $e"; } _isSubmitting = false; notifyListeners(); } /// Apply filters coming from bottom sheet void updateFiltersFromSheet( mode, BuildContext context, { required String type, required String selectedValue, DateTimeRange? customRange, }) { _selectedType = type; _selectedDateRange = selectedValue; _customDateRange = customRange; fetchAttendanceRequests( context, mode, type: _selectedType, dateRange: _selectedDateRange, customRange: _customDateRange, ); } /// Set type filter and refresh data void setTypeFilter(BuildContext context, String type, mode) { _selectedType = type; fetchAttendanceRequests(context, mode); } /// Set date range filter and refresh data void setDateRangeFilter(BuildContext context, String dateRange, mode, {DateTimeRange? customRange}) { _selectedDateRange = dateRange; if (customRange != null) { _customDateRange = customRange; fromDateController.text = _formatDate(customRange.start); toDateController.text = _formatDate(customRange.end); } fetchAttendanceRequests(context, mode); } /// Clear all filters and refresh data void clearFilters(BuildContext context, mode) { _selectedType = "All"; _selectedDateRange = "This Month"; _customDateRange = null; fromDateController.clear(); toDateController.clear(); fetchAttendanceRequests(context, mode); } /// Reset form and data void resetForm(BuildContext context, mode) { _response = null; _errorMessage = null; clearFilters(context, mode); notifyListeners(); } /// Get date range parameters for API Map _getDateRangeParams(String dateRange, DateTimeRange? customRange) { final now = DateTime.now(); final formatter = DateFormat("dd MMM yyyy"); late DateTime from; late DateTime to; switch (dateRange) { case "All": from = DateTime(now.year - 1); to = now; break; case "Today": from = now; to = now; break; case "Yesterday": from = now.subtract(const Duration(days: 1)); to = now.subtract(const Duration(days: 1)); break; case "This Month": from = DateTime(now.year, now.month, 1); to = DateTime(now.year, now.month + 1, 0); break; case "Past 7 days": from = now.subtract(const Duration(days: 6)); to = now; break; case "Last Month": from = DateTime(now.year, now.month - 1, 1); to = DateTime(now.year, now.month, 0); break; case "Custom": if (customRange != null) { from = customRange.start; to = customRange.end; } else { from = now.subtract(const Duration(days: 30)); to = now; } break; default: from = now; to = now; } return { "from": formatter.format(from), "to": formatter.format(to), }; } /// Format date for display String _formatDate(DateTime date) { return DateFormat("dd MMM yyyy").format(date); } /// Apply filters and refresh data void applyFilters(BuildContext context, mode) { fetchAttendanceRequests( context, mode, type: _selectedType, dateRange: _selectedDateRange, customRange: _customDateRange, ); } /// Set Selected Date void setSelectedDate(DateTime date) { _selectedDate = date; dateController.text = DateFormat("dd MMM yyyy").format(date); notifyListeners(); } /// Show Cupertino Date Picker void showDatePickerDialog(BuildContext context) { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Normalize selected date (strip time) if (_selectedDate == null) { setSelectedDate(today); } else { _selectedDate = DateTime( _selectedDate!.year, _selectedDate!.month, _selectedDate!.day, ); } // Always reset before showing _selectedDate = today; setSelectedDate(today); showCupertinoModalPopup( context: context, builder: (BuildContext context) => Container( height: 250, padding: const EdgeInsets.only(top: 6.0), margin: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), color: CupertinoColors.systemBackground.resolveFrom(context), child: SafeArea( top: false, child: Column( children: [ // Cancel + Done Buttons SizedBox( height: 55, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ CupertinoButton( child: Text( 'Cancel', style: TextStyle( fontFamily: "JakartaMedium", color: AppColors.app_blue, ), ), onPressed: () => Navigator.pop(context), ), CupertinoButton( child: Text( 'Done', style: TextStyle( fontFamily: "JakartaMedium", color: AppColors.app_blue, ), ), onPressed: () { // Ensure we save only date without time final pickedDate = _selectedDate ?? today; setSelectedDate(DateTime( pickedDate.year, pickedDate.month, pickedDate.day, )); Navigator.pop(context); }, ), ], ), ), // Cupertino Date Picker Expanded( child: CupertinoDatePicker( dateOrder: DatePickerDateOrder.dmy, initialDateTime: _selectedDate ?? today, minimumDate: today.subtract(const Duration(days: 2)), maximumDate: today, mode: CupertinoDatePickerMode.date, use24hFormat: true, showDayOfWeek: true, onDateTimeChanged: (DateTime newDate) { // Always normalize new date to midnight setSelectedDate(DateTime(newDate.year, newDate.month, newDate.day)); }, ), ), ], ), ), ), ); } }