import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart'; import 'package:intl/intl.dart'; import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/utils/Pagination.dart'; import '../../../data/utils/validators.dart'; import '../../layouts/main_layout.dart'; import '../../layouts/responsive_layout.dart'; import '../../providers/manager_provider.dart'; import '../../themes/indicators/date_field_theme.dart'; import '../../themes/indicators/export_btn.dart'; import '../../themes/indicators/month_field_theme.dart'; import '../../themes/indicators/search_field_theme.dart'; import '../../widgets/custom_Stdate_EnDate_Filter.dart'; import '../../widgets/custom_action_popup.dart'; import '../../widgets/custom_month_filter.dart'; class IndividualAttendanceDetails extends ConsumerStatefulWidget { const IndividualAttendanceDetails({super.key}); @override ConsumerState createState() => IndividualAttendanceDetailsState(); } class IndividualAttendanceDetailsState extends ConsumerState { int currentPage = 1; int itemsPerPage = 10; late ApiService apiService; dynamic staffId; dynamic staffName; dynamic userId; final _formKey = GlobalKey(); // final monthController = TextEditingController(); final _monthFormKey = GlobalKey(); // List> dataVal = []; List> getStaffData = []; List> originalData = []; List> filteredData = []; bool isLoading = false; Map controllers = {}; List tabHeader = ['startDate', 'endDate', 'month']; @override void initState() { super.initState(); apiService = ApiService(); for (String field in tabHeader) { controllers[field] = TextEditingController(); } Future.microtask(() { final id = ref.read(managerIdProvider); staffId = ref.read(staffIndiviualAttendanceIdProvider); staffName = ref.read(staffIndiviualAttendanceNameIdProvider); userId = ref.read(userIdProvider); print( "A56 => r : $staffId | mId: $id | uId: $userId |staffName : $staffName", ); // Staff - sanjeev.p@venbainfotech.com - T E S T S - 9 evarukku 8th id is manager // Manager - venbalap08@gmail.com - T E S T M - 8 evarukku 8th id is manager // Agent - venba2026@gmail.com - T E S T A - 5 evarkku 8th id is manager // so here i used userId if (staffId != null && staffName != null) { getStaffList(userId, staffId); } }); } void filterDateRange1() { print("filterDateRange"); // if (!_formKey.currentState!.validate()) return; // final fromDate = controllers['startDate']?.text ?? ''; // final toDate = controllers['endDate']?.text ?? ''; print("filterDateRange1"); // Call with selected dates getStaffList(userId, staffId); } void filterMonth() { print('Month'); // ✅ Validate the form first // if (!_formKey.currentState!.validate()) { // // stop execution if validation fails // return; // } final fromDateText = controllers['month']?.text ?? ''; print('monthmonth - $fromDateText'); if (fromDateText.isEmpty) { print('No month selected'); return; } try { // Convert "10-2025" → "2025-10" // final parsedDate = DateFormat('MM-yyyy').parse(fromDateText); // final apiMonth = DateFormat('yyyy-MM').format(parsedDate); // Parse "Nov 2025" → DateTime final parsedDate = DateFormat('MMM yyyy').parse(fromDateText); final apiMonth = DateFormat('yyyy-MM').format(parsedDate); print('Converted month for API: $apiMonth'); // ✅ Call your API with the converted format getStaffList(userId, staffId, month: apiMonth); } catch (e) { print('Error parsing month: $e'); } } void refrshfilterDateRange() { setState(() { controllers['month']!.clear(); controllers['month']?.text = ''; // Reset the FormField validation _formKey.currentState?.reset(); _monthFormKey.currentState?.reset(); }); getStaffList(userId, staffId); } Future getStaffList(int managerId, role, {String month = ''}) async { final monthForApi = month.isNotEmpty ? month : DateFormat('yyyy-MM').format(DateTime.now()); print('A72 => Fns called => $managerId | $role | month: $monthForApi'); setState(() { isLoading = true; }); try { final response = await apiService.fetchAttndanceOfIndiviualStaffList( month: monthForApi, id: staffId, // toDate: controllers['endDate']?.text ?? '', ); if (response['status'] == 'success') { final data = response['data']; final fromDate = response['month'] ?? ''; print('FromDate : $fromDate'); print('Frdata : $data'); setState(() { print('test 1'); if (fromDate != null && fromDate.isNotEmpty) { // Parse the string to DateTime first // final parsedDate = DateFormat('yyyy-MM').parse(fromDate); // final monthText = DateFormat('MM-yyyy').format(parsedDate); // Parse the string "2025-10" to a DateTime final parsedDate = DateFormat('yyyy-MM').parse(fromDate); // Convert it to "Oct 2025" final monthText = DateFormat('MMM yyyy').format(parsedDate); controllers['month']?.text = monthText; } print('test 2'); getStaffData = (data as List) .map>( (item) => Map.from(item), ) .toList(); print('test 3'); originalData = getStaffData; filteredData = List.from(originalData); print('testfilteredData $filteredData'); }); } else { getStaffData = []; originalData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } List get _paginatedData { print('test 31'); // Sort descending by id first final sortedData = [...filteredData] ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); print('test 311- $sortedData'); print('test 311'); if (sortedData.isEmpty) return []; // Ensure currentPage is valid final maxPage = (sortedData.length / itemsPerPage).ceil(); final safePage = currentPage.clamp(1, maxPage); final startIndex = (safePage - 1) * itemsPerPage; final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); return sortedData.sublist(startIndex, endIndex); } Future handleEdit(item) async { // Navigator.pop(context); print('EDITStaff - ${item['id']}'); // dynamic id = data['id']; // context.go('/tabEnquiry/$id'); final prefs = await SharedPreferences.getInstance(); // ✅ Remove old value (if any) await prefs.remove('enqAgentDataId'); // setState(() { final id = item['id'].toString(); // ✅ Save the new id await prefs.setString('enqAgentDataId', id.toString()); ref.read(enquiryIdProvider.notifier).state = id; // }); // update provider context.go(AppRoutes.tabEnquiry); } void filterData(String query) { print("FilterDAta - $query"); setState(() { print('test 31f'); filteredData = getStaffData.where((item) { // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; return (item['date'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || (item['name'] ?? '-').toLowerCase().contains(query.toLowerCase()) || (item['login_time'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || (item['logout_time'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || (item['no_of_logged_in_time'] ?? '-').toLowerCase().contains( query.toLowerCase(), ); }).toList(); }); } final TextEditingController _searchStaffController = TextEditingController(); String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); return DateFormat('dd-MM-yyyy HH:mm').format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } } @override Widget build(BuildContext context) { return MainLayout( title: "Staff Attendance", body: SelectionArea( child: Container( // color: Colors.yellow.shade50, width: MediaQuery.of(context).size.width, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Container( height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { context.go(AppRoutes.allStaffAttendance); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ Tooltip( message: 'Back', child: IconButton( icon: Icon( Icons.arrow_left_sharp, size: ResponsiveLayout.isMobile(context) ? 25 : 25, color: const Color(0xFF425B5B), ), onPressed: () async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('attendanceIndvStaff'); await prefs.remove('attendanceIndvStaffName'); context.go(AppRoutes.allStaffAttendance); }, splashRadius: 18, hoverColor: Colors.black12, padding: const EdgeInsets.all(4), constraints: const BoxConstraints(), ), ), const SizedBox(width: 5), // spacing between icon and text Text( "Attendance", style: GoogleFonts.poppins( fontSize: ResponsiveLayout.isMobile(context) ? 11 : 14, fontWeight: FontWeight.w500, ), ), ], ), ), ), SizedBox(height: ResponsiveLayout.isMobile(context) ? 5 : 5), Expanded( child: Container( width: MediaQuery.of(context).size.width, padding: EdgeInsets.all(8.0), child: _buildContent(context), ), ), Container( // height: 20, width: MediaQuery.of(context).size.width, // color: Colors.green.shade50, child: PaginationControls( currentPage: currentPage, itemsPerPage: itemsPerPage, // totalItems: dataVal.length, totalItems: filteredData.length, // activeColor: layoutColor, // your theme color onPageChanged: (page) { setState(() { currentPage = page; }); }, onItemsPerPageChanged: (items) { setState(() { itemsPerPage = items; currentPage = 1; }); }, ), ), ], ), ),), ); } Widget _buildContent(BuildContext context) { return Column( children: [ Container( // height: 40, // color: Colors.pink, child: Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.start, children: [ MonthFilterRow( monthController: controllers['month']!, formKey: _monthFormKey, onFilter: () { print( 'Filter triggered with month: ${controllers['month']!.text}', ); filterMonth(); }, onRefresh: () { controllers['month']!.clear(); refrshfilterDateRange(); print('Month filter cleared'); }, ), Spacer(), ThemedSearchField( hintText: 'Search', backgroundColor: Color(0xFFFFFFFF), txtHeight: 30, // backgroundColor: Color(0xFFF6F8F8), onChanged: filterData, controller: _searchStaffController, txtwidth: ResponsiveLayout.isMobile(context) ? MediaQuery.of(context).size.width * 0.6 : MediaQuery.of(context).size.width * 0.15, ), ResponsiveLayout.isMobile(context) ? Spacer() : SizedBox(width: 10), ExportBtn( sheetName: "Staff Individual Attendance", fileName: "staff_individual_attendance_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, displayHeaders: [ "S.No.", "Name", "Date", "Login", "Logout", "Number Of Hours", ], keys: [ "sno", // handled internally as i + 1 "name", "date", "login_time", "logout_time", 'no_of_logged_in_time', ], ), ], ), ), SizedBox(height: 10), Container( decoration: BoxDecoration( color: Color(0xFFF1F5F9), // color: Color(0xFFEDF6F5), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: Row( children: [ Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)), Expanded(flex: 2, child: Text('Name', style: _headerStyle)), Expanded(flex: 2, child: Text('Date', style: _headerStyle)), Expanded(flex: 2, child: Text('Login', style: _headerStyle)), Expanded(flex: 2, child: Text('Logout', style: _headerStyle)), Expanded( flex: 2, child: Text('Number Of Hours', style: _headerStyle), ), ], ), ), ResponsiveLayout.isMobile(context) ? _buildDataTable(context) : Expanded( child: Container( color: Colors.white, child: _buildDataTable(context), ), ), ], ); } Widget _buildDataTable(BuildContext context) { print('test 32'); if (filteredData.isEmpty) { return const SizedBox( height: 50, child: Center(child: Text('No available data')), ); } final sortedData = [..._paginatedData]; print('test 4'); // Desktop: keep ListView.builder return ListView.builder( itemCount: sortedData.length + 1, itemBuilder: (context, index) { if (index == 0) return _buildHeader(); final startIndex = (currentPage - 1) * itemsPerPage; final item = sortedData[index - 1]; final sno = startIndex + index; return _buildDataRow(item, sno); }, ); } Widget _buildHeader() { return SizedBox.shrink(); } Widget _buildDataRow(Map item, sno) { print('test 5'); return Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), // margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( color: Colors.white, // color: Color(0xFFE0F7F9), border: const Border( bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), ), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), Expanded(flex: 2, child: Text(item['date'] ?? '-', style: _dataBold)), Expanded( flex: 2, child: Text(item['login_time'] ?? '-', style: _dataBold), ), Expanded( flex: 2, child: Text(item['logout_time'] ?? '-', style: _dataBold), ), Expanded( flex: 2, child: Text(item['no_of_logged_in_time'] ?? '-', style: _dataBold), ), ], ), ); } Widget buildStartDate(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Start Date', style: _textStyle), SizedBox(height: 5), ThemedMonthField( hintText: "Select Month", txtwidth: MediaQuery.of(context).size.width * 0.27, txtheight: 40, backgroundColor: const Color(0xFFECECEC), validator: (value) => Validators.requiredField(value, "date"), borderColor: Colors.grey.shade300, controller: controllers['startDate']!, onDateSelected: (date) { print("Picked Date: $date"); controllers['startDate']?.text = DateFormat('MM-yyyy').format(date); // controllers['date']?.text = date as String; }, ), ], ); } Widget buildEndDate(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('End Date', style: _textStyle), SizedBox(height: 5), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.16, // txtheight: 50, // backgroundColor: const Color(0xFFECECEC), validator: (value) { if (value == null || value.isEmpty) { return "End Date is required"; } final fromText = controllers['startDate']?.text ?? ''; if (fromText.isNotEmpty) { final startDate = DateFormat('dd-MM-yyyy').parse(fromText); final endDate = DateFormat('dd-MM-yyyy').parse(value); if (endDate.isBefore(startDate)) { return "End Date cannot be earlier than Start Date"; } } return null; // ✅ no error }, controller: controllers['endDate']!, lastDate: DateTime.now(), onDateSelected: (date) { print("Picked Date: $date"); controllers['endDate']?.text = DateFormat( 'dd-MM-yyyy', ).format(date); // controllers['date']?.text = date as String; }, ), ], ); } static final _dataBold = GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xFF000000), ); static final _dataSub = GoogleFonts.inter( fontSize: 10, fontWeight: FontWeight.w300, color: Color(0xFF585757), ); // static const _headerStyle = GoogleFonts.inter( // color: Colors.black, // fontWeight: FontWeight.bold, // ); static final _headerStyle = GoogleFonts.poppins( fontSize: 11.2, fontWeight: FontWeight.w500, color: Color(0xFF1E293B), ); static final _cardheaderStyle = GoogleFonts.inter( color: Colors.black, fontWeight: FontWeight.w600, fontSize: 12, ); static final _cardBodyStyle = GoogleFonts.inter( color: Color(0xFF545454), fontWeight: FontWeight.w400, fontSize: 12, ); static const _textStyle = TextStyle( fontSize: 12, fontWeight: FontWeight.w600, ); }