diff --git a/lib/presentation/screens/Enquiry/enquiryList.dart b/lib/presentation/screens/Enquiry/enquiryList.dart index 09549f0..4af1c83 100644 --- a/lib/presentation/screens/Enquiry/enquiryList.dart +++ b/lib/presentation/screens/Enquiry/enquiryList.dart @@ -571,15 +571,35 @@ class EnquiryListState extends ConsumerState { fileName: "enquiry_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ - "id", + + // headers: [], + displayHeaders: [ + "S.No.", + "Received Date & Time", + "Vehicle.No.", + "Insurer", + "Updated Date", + "Status", + "Remarks", + ], + keys: [ + "sno", // handled internally as i + 1 "created_on", - "updated_on", "reg_no", "insurer_name", + "updated_on", "status", "remarks", ], + // headers: [ + // "id", + // "created_on", + // "updated_on", + // "reg_no", + // "insurer_name", + // "status", + // "remarks", + // ], ), SizedBox(width: 10), diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart index d3e67d1..c76ccdb 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart @@ -255,7 +255,18 @@ class claimListState extends ConsumerState { txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ + displayHeaders: [ + "S.No.", + "Vehicle.No.", + "Insurer", + "Policy No.", + "Policy Expiry Date", + "Claim Type", + "Claim No.", + "Status", + ], + keys: [ + "sno", // handled internally as i + 1 "reg_no", "insurer_name", "policy_no", diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart index 026cfa0..f335a33 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart @@ -252,7 +252,16 @@ class endosementState extends ConsumerState { txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ + displayHeaders: [ + "S.No.", + "Vehicle.No.", + "Insurer", + "Policy No.", + "Endorsement Type", + "Endorsement No.", + ], + keys: [ + "sno", // handled internally as i + 1 "reg_no", "insurer_name", "policy_number", diff --git a/lib/presentation/screens/StaffAttendance/attendanceAllDetails.dart b/lib/presentation/screens/StaffAttendance/attendanceAllDetails.dart index 785c8f6..8a11664 100644 --- a/lib/presentation/screens/StaffAttendance/attendanceAllDetails.dart +++ b/lib/presentation/screens/StaffAttendance/attendanceAllDetails.dart @@ -409,7 +409,12 @@ class AttendanceAllDetailsState extends ConsumerState { fileName: "staff_attendance_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: ["staff_id", "staff_name", "month", "login_count"], + + displayHeaders: ["S.No.", "Staff Name", "Month", "Days"], + keys: [ + "sno", // handled internally as i + 1 + "staff_name", "month", "login_count", + ], ), ], ), diff --git a/lib/presentation/screens/StaffAttendance/individual_Attendance.dart b/lib/presentation/screens/StaffAttendance/individual_Attendance.dart index e0e133f..6a8f8dc 100644 --- a/lib/presentation/screens/StaffAttendance/individual_Attendance.dart +++ b/lib/presentation/screens/StaffAttendance/individual_Attendance.dart @@ -253,6 +253,7 @@ class IndividualAttendanceDetailsState return (item['date'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || + (item['name'] ?? '-').toLowerCase().contains(query.toLowerCase()) || (item['login_time'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || @@ -321,7 +322,7 @@ class IndividualAttendanceDetailsState ), const SizedBox(width: 5), // spacing between icon and text Text( - "Attendance For $staffName", + "Attendance", style: GoogleFonts.inter( fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18, fontWeight: FontWeight.w600, @@ -416,8 +417,17 @@ class IndividualAttendanceDetailsState fileName: "staff_individual_attendance_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ - "id", + displayHeaders: [ + "S.No.", + "Name", + "Date", + "Login", + "Logout", + "Number Of Hours", + ], + keys: [ + "sno", // handled internally as i + 1 + "name", "date", "login_time", "logout_time", @@ -438,6 +448,7 @@ class IndividualAttendanceDetailsState 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)), @@ -502,6 +513,7 @@ class IndividualAttendanceDetailsState 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( diff --git a/lib/presentation/screens/UserManagement/Agent/agent.dart b/lib/presentation/screens/UserManagement/Agent/agent.dart index 83158ba..d40cf8c 100644 --- a/lib/presentation/screens/UserManagement/Agent/agent.dart +++ b/lib/presentation/screens/UserManagement/Agent/agent.dart @@ -528,6 +528,9 @@ class AgentState extends ConsumerState { SizedBox(width: 10), ThemedFormField( controller: controllers['code']!, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9]')), + ], validator: (value) => Validators.requiredField(value, "id"), txtwidth: MediaQuery.of(context).size.width * 0.26, ), diff --git a/lib/presentation/screens/UserManagement/Agent/agentList.dart b/lib/presentation/screens/UserManagement/Agent/agentList.dart index ec824a3..b481821 100644 --- a/lib/presentation/screens/UserManagement/Agent/agentList.dart +++ b/lib/presentation/screens/UserManagement/Agent/agentList.dart @@ -280,12 +280,21 @@ class AgentListState extends ConsumerState { ? true : false, data: filteredData, - headers: [ - "id", + displayHeaders: [ + 'S.No.', + 'Partner Name', + 'Email', + 'Phone Number', + 'Id', + 'Address', + 'Status', + ], + keys: [ + "sno", // handled internally as i + 1 "name", - "agent_code", "email", "mobile", + "agent_code", "address", "is_active", ], diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart index a44a6ce..2ce6dad 100644 --- a/lib/presentation/screens/UserManagement/Staff/staffList.dart +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -255,12 +255,20 @@ class StaffListState extends ConsumerState { ? true : false, data: filteredData, - headers: [ - "id", + displayHeaders: [ + 'S.No.', + 'Staff Name', + 'Role', + 'Email', + 'Phone Number', + 'Status', + ], + keys: [ + "sno", // handled internally as i + 1 "name", + "role", "email", "mobile", - "role", "is_active", ], ), diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart index 64d7ea0..a406e9b 100644 --- a/lib/presentation/screens/dashboard/dashboard.dart +++ b/lib/presentation/screens/dashboard/dashboard.dart @@ -1234,7 +1234,7 @@ class othersPendings extends StatelessWidget { flex: 2, child: Text( // stringFlag + " Issued", - 'Pending Enquiries', + 'Total Assigned', textAlign: TextAlign.center, style: _headerStyle, ), @@ -1344,69 +1344,81 @@ class othersPendings extends StatelessWidget { ), Expanded( flex: 2, - child: InkWell( - onTap: () { - print( - 'Awaiting Proposal : ${row['awaiting_quotation']}', - ); + child: Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + onTap: () { + print( + 'Awaiting Proposal : ${row['awaiting_quotation']}', + ); - handleDashboardNavigation( - context, - status: "Awaiting Proposal", - staffId: row['staff_id'] ?? '', - role: role, - ); - }, - child: Text( - row['awaiting_quotation'] ?? "", - // row['total_approval_pending'] ?? "", - textAlign: TextAlign.center, - style: _tableDataStyle, + handleDashboardNavigation( + context, + status: "Awaiting Proposal", + staffId: row['staff_id'] ?? '', + role: role, + ); + }, + child: Text( + row['awaiting_quotation'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), ), ), ), Expanded( flex: 2, - child: InkWell( - onTap: () async { - print( - 'Awaiting Approval : ${row['pending_quotation_approval']} - ${row['staff_id']}', - ); - handleDashboardNavigation( - context, - status: "Proposal Created", - staffId: row['staff_id'] ?? '', - role: role, - ); - }, - child: Text( - row['pending_quotation_approval'] ?? "", - // row['total_approval_pending'] ?? "", - textAlign: TextAlign.center, - style: _tableDataStyle, + child: Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + onTap: () async { + print( + 'Awaiting Approval : ${row['pending_quotation_approval']} - ${row['staff_id']}', + ); + handleDashboardNavigation( + context, + status: "Proposal Created", + staffId: row['staff_id'] ?? '', + role: role, + ); + }, + child: Text( + row['pending_quotation_approval'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), ), ), ), Expanded( flex: 2, - child: InkWell( - onTap: () { - print( - 'Awaiting Policy : ${row['awaiting_policy']}', - ); + child: Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + onTap: () { + print( + 'Awaiting Policy : ${row['awaiting_policy']}', + ); - handleDashboardNavigation( - context, - status: "Proposal Accepted", - staffId: row['staff_id'] ?? '', - role: role, - ); - }, - child: Text( - row['awaiting_policy'] ?? "", - // row['total_approval_pending'] ?? "", - textAlign: TextAlign.center, - style: _tableDataStyle, + handleDashboardNavigation( + context, + status: "Proposal Accepted", + staffId: row['staff_id'] ?? '', + role: role, + ); + }, + child: Text( + row['awaiting_policy'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), ), ), ), diff --git a/lib/presentation/screens/handler/enquiryList.dart b/lib/presentation/screens/handler/enquiryList.dart index d006f99..1f319c5 100644 --- a/lib/presentation/screens/handler/enquiryList.dart +++ b/lib/presentation/screens/handler/enquiryList.dart @@ -764,14 +764,27 @@ class EnquiryHandlerState extends ConsumerState { fileName: "Enquiry_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ + displayHeaders: [ + "Received Date & Time", + "Partner", + "Assigned To", + "Insurer", + "Vehicle.No.", + "Insured Name", + "Assigned Date & Time", + "Premium", + "Payment Mode", + "Policy Number", + "Status", + ], + keys: [ "created_on", - "updated_on", - "reg_no", "agent_name", "assigned_to_name", "insurer_name", + "reg_no", "insured_name", + "updated_on", "premium_amount", "payment_mode", "policy_number", @@ -887,26 +900,29 @@ class EnquiryHandlerState extends ConsumerState { flex: 2, child: Text('Received Date', style: _headerStyle), ), - Expanded( - flex: 2, - child: Text('Assigned Date', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Vehicle.No', style: _headerStyle), - ), + Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), Expanded( flex: 2, child: Text('Assigned To', style: _headerStyle), ), Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), + Expanded( flex: 2, - child: Padding( - padding: EdgeInsets.only(left: 8.0), - child: Text('Insured Name', style: _headerStyle), - ), + child: Text('Vehicle.No', style: _headerStyle), + ), + Expanded( + flex: 2, + // child: Padding( + // padding: EdgeInsets.only(left: 8.0), + child: Text('Insured Name', style: _headerStyle), + // ), + ), + + Expanded( + flex: 2, + child: Text('Assigned Date', style: _headerStyle), ), Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), Expanded( @@ -1023,19 +1039,7 @@ class EnquiryHandlerState extends ConsumerState { maxLines: 3, ), ), - Expanded( - flex: 2, - child: Text( - _formatDate(item['updated_on']) ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text(item['reg_no'] ?? '-', style: _dataBold), - ), + Expanded( flex: 2, child: Text(item['agent_name'] ?? '-', style: _dataBold), @@ -1053,6 +1057,10 @@ class EnquiryHandlerState extends ConsumerState { maxLines: 3, ), ), + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), Expanded( flex: 2, @@ -1063,6 +1071,16 @@ class EnquiryHandlerState extends ConsumerState { maxLines: 3, ), ), + + Expanded( + flex: 2, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), Expanded( flex: 2, child: Text( diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart index ebda11a..269603a 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart @@ -827,17 +827,28 @@ class EnquiryStaffState extends ConsumerState { fileName: "Enquiry_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ + displayHeaders: [ + "Received Date & Time", + "Partner", + "Assigned To", + "Insurer", + "Vehicle.No.", + "Insured Name", + "Assigned Date & Time", + "Status", + ], + keys: [ "created_on", - "updated_on", - "reg_no", "agent_name", "assigned_to_name", "insurer_name", + "reg_no", "insured_name", - "premium_amount", - "payment_mode", - "policy_number", + // "assigned_to_name", + "updated_on", + // "premium_amount", + // "payment_mode", + // "policy_number", "status", ], ), @@ -859,6 +870,10 @@ class EnquiryStaffState extends ConsumerState { child: Text('Received Date & Time', style: _headerStyle), ), Expanded(flex: 2, child: Text('Partner ', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Assigned To ', style: _headerStyle), + ), Expanded(flex: 4, child: Text('Insurer', style: _headerStyle)), Expanded( flex: 3, @@ -955,6 +970,15 @@ class EnquiryStaffState extends ConsumerState { maxLines: 2, ), ), + Expanded( + flex: 2, + child: Text( + item['assigned_to_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 2, + ), + ), Expanded( flex: 4, child: Text( @@ -968,61 +992,65 @@ class EnquiryStaffState extends ConsumerState { Expanded( flex: 3, child: Builder( - builder: (buttonContext) => InkWell( - hoverColor: const Color(0xffD9EBE8), - focusColor: const Color(0xffD9EBE8), - splashColor: const Color(0xffD9EBE8), - highlightColor: const Color(0xffD9EBE8), - onTap: () async { - // ✅ Get overlay and button position RELATIVE to InkWell - final RenderBox button = - buttonContext.findRenderObject() as RenderBox; - final RenderBox overlay = - Overlay.of(buttonContext).context.findRenderObject() - as RenderBox; + builder: (buttonContext) => Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + // hoverColor: const Color(0xffD9EBE8), + // focusColor: const Color(0xffD9EBE8), + // splashColor: const Color(0xffD9EBE8), + // highlightColor: const Color(0xffD9EBE8), + onTap: () async { + // ✅ Get overlay and button position RELATIVE to InkWell + final RenderBox button = + buttonContext.findRenderObject() as RenderBox; + final RenderBox overlay = + Overlay.of(buttonContext).context.findRenderObject() + as RenderBox; - final Offset position = button.localToGlobal( - Offset.zero, - ancestor: overlay, - ); + final Offset position = button.localToGlobal( + Offset.zero, + ancestor: overlay, + ); - await showMenu( - context: buttonContext, - position: RelativeRect.fromLTRB( - position.dx, - position.dy + button.size.height, - overlay.size.width - position.dx, - 0, - ), - items: [ - PopupMenuItem( - child: Column( - mainAxisSize: MainAxisSize.min, - children: _buildPopupDownload( - buttonContext, - item, - item['id'], - item['reg_no'], + await showMenu( + context: buttonContext, + position: RelativeRect.fromLTRB( + position.dx, + position.dy + button.size.height, + overlay.size.width - position.dx, + 0, + ), + items: [ + PopupMenuItem( + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupDownload( + buttonContext, + item, + item['id'], + item['reg_no'], + ), ), ), - ), - ], - color: Colors.white, - ); - }, - child: Tooltip( - message: - 'Click and Download Enquiry Files', // what appears on hover/long press - waitDuration: const Duration(milliseconds: 500), // optional - showDuration: const Duration(seconds: 2), // optional - child: Text( - item['reg_no'] ?? '-', - style: _dataBold, - overflow: TextOverflow - .ellipsis, // optional, if text might overflow + ], + color: Colors.white, + ); + }, + child: Tooltip( + message: + 'Click and Download Enquiry Files', // what appears on hover/long press + waitDuration: const Duration(milliseconds: 500), // optional + showDuration: const Duration(seconds: 2), // optional + child: Text( + item['reg_no'] ?? '-', + style: _dataBold, + overflow: TextOverflow + .ellipsis, // optional, if text might overflow + ), ), + // child: Text(item['reg_no'] ?? '-', style: _dataBold), ), - // child: Text(item['reg_no'] ?? '-', style: _dataBold), ), ), ), @@ -1049,32 +1077,36 @@ class EnquiryStaffState extends ConsumerState { ), Expanded( flex: 3, - child: InkWell( - onTap: () async { - var status = item['status']; - var enqId = item['id']; + child: Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + onTap: () async { + var status = item['status']; + var enqId = item['id']; - final prefs = await SharedPreferences.getInstance(); + final prefs = await SharedPreferences.getInstance(); - // ✅ Remove old value (if any) - await prefs.remove('enqStaffDataId'); + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); - // ✅ Save the new id - await prefs.setString('enqStaffDataId', enqId.toString()); - ref.read(quotationStaffIdProvider.notifier).state = enqId; - buildStatusActions(context, status, enqId); - }, - child: Tooltip( - message: 'Click to View or Process Enquiry', // - waitDuration: const Duration(milliseconds: 500), // optional - showDuration: const Duration(seconds: 2), // optional - child: Text( - item['status'] ?? '-', - style: _dataBold, - overflow: TextOverflow.ellipsis, // + // ✅ Save the new id + await prefs.setString('enqStaffDataId', enqId.toString()); + ref.read(quotationStaffIdProvider.notifier).state = enqId; + buildStatusActions(context, status, enqId); + }, + child: Tooltip( + message: 'Click to View or Process Enquiry', // + waitDuration: const Duration(milliseconds: 500), // optional + showDuration: const Duration(seconds: 2), // optional + child: Text( + item['status'] ?? '-', + style: _dataBold, + overflow: TextOverflow.ellipsis, // + ), ), + // child: Text(item['status'] ?? '-', style: _dataBold), ), - // child: Text(item['status'] ?? '-', style: _dataBold), ), ), ], diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index 685ba72..a8f8be5 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -588,15 +588,26 @@ class policylistState extends ConsumerState { fileName: "Policy_list", data: filteredData, txt: !ResponsiveLayout.isMobile(context) ? true : false, - headers: [ - "reg_no", + displayHeaders: [ + "Received Date & Time", + "Partner", + "Assigned To", + "Insurer", + "Vehicle.No.", + "Insured Name", + "Premium", + "Payment Mode", + "Policy Number", + ], + keys: [ + "updated_on", "agent_name", "assigned_to_name", "insurer_name", + "reg_no", "insured_name", "premium_amount", "payment_mode", - "updated_on", "policy_number", ], ), diff --git a/lib/presentation/themes/indicators/export_btn.dart b/lib/presentation/themes/indicators/export_btn.dart index 4891197..3137179 100644 --- a/lib/presentation/themes/indicators/export_btn.dart +++ b/lib/presentation/themes/indicators/export_btn.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:excel/excel.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:nhance_partner/data/utils/toastNotification.dart'; import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; @@ -21,91 +22,171 @@ class ExportBtn extends HookWidget { final String sheetName; final String fileName; final List> data; - final List headers; // dynamic headers + final List? displayHeaders; // dynamic headers + // final List headers; // dynamic headers + final List? keys; // dynamic headers final bool? txt; + const ExportBtn({ super.key, this.txt, required this.sheetName, required this.fileName, required this.data, - required this.headers, + + // required this.headers, + required this.displayHeaders, + this.keys, }); @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - ExcelExporter.exportToExcel( - sheetName: sheetName, - data: data, - headers: headers, - fileName: fileName, - ); - }, - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: const Color(0xFF425B5B), - borderRadius: BorderRadius.circular(8.0), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - ?txt! - ? Text( - 'Export', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 14, - ), - ) - : null, - ?txt! ? SizedBox(width: 20) : null, - Image.asset( - "assets/miscellaneous/export.png", - height: 25, - width: 25, - ), - // Icon(Icons.input_sharp, color: Colors.white), - // Image.asset("assets/miscellaneous/export", height: 15, width: 15), - ], + return Tooltip( + message: 'Export File', + child: InkWell( + onTap: () async { + print('TAV ExcelExporter'); + final scaffold = ScaffoldMessenger.of(context); + try { + await ExcelExporter.exportToExcel( + sheetName: sheetName, + data: data, + displayHeaders: displayHeaders!, + keys: keys!, + fileName: fileName, + ); + ToastHelper.showSuccessToast(context, 'Export finished'); + } catch (e) { + ToastHelper.showErrorToast(context, 'Export failed: $e'); + } + }, + // onTap: () { + // print('TAV ExcelExporter'); + // + // ExcelExporter.exportToExcel( + // sheetName: sheetName, + // data: data, + // headers: headers, + // fileName: fileName, + // keys: keys!, + // ); + // }, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: const Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ?txt! + ? Text( + 'Export', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ) + : null, + ?txt! ? SizedBox(width: 20) : null, + Image.asset( + "assets/miscellaneous/export.png", + height: 25, + width: 25, + ), + // Icon(Icons.input_sharp, color: Colors.white), + // Image.asset("assets/miscellaneous/export", height: 15, width: 15), + ], + ), ), ), ); } } - - class ExcelExporter { + /// Export to an Excel file. + /// - [displayHeaders]: labels to show in Excel (e.g. "S.No.", "Status") + /// - [keys]: actual field names in your data map static Future exportToExcel({ required String sheetName, required List> data, - required List headers, + required List displayHeaders, + required List keys, required String fileName, }) async { - try { - // Create Excel - var excel = Excel.createExcel(); + if (displayHeaders.length != keys.length) { + throw ArgumentError('displayHeaders and keys must have same length'); + } - // Rename default sheet + try { + final excel = Excel.createExcel(); + + // Rename default sheet if needed if (excel.sheets.containsKey('Sheet1')) { - excel.rename('Sheet1', sheetName); + try { + excel.rename('Sheet1', sheetName); + } catch (_) {} } // Get the sheet - Sheet sheetObject = excel[sheetName]!; + final sheet = excel[sheetName]!; - // Add headers - sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); + // ✅ Add headers as CellValues + sheet.appendRow(displayHeaders.map((h) => TextCellValue(h)).toList()); + + // ✅ Add data rows + // for (var i = 0; i < data.length; i++) { + // final rowMap = data[i]; + // final row = []; + // + // for (var key in keys) { + // dynamic value; + // if (key == 'sno' || + // key.toLowerCase() == 's.no' || + // key.toLowerCase() == 'sno.') { + // value = i + 1; // serial number + // } else { + // value = rowMap[key]; + // } + // + // if (value == null) { + // row.add(TextCellValue('-')); + // } else if (value is int) { + // row.add(IntCellValue(value)); + // } else if (value is double) { + // row.add(DoubleCellValue(value)); + // } else { + // row.add(TextCellValue(value.toString())); + // } + // } + // + // sheet.appendRow(row); + // } + + // ✅ Reverse the data list so latest entries come first + final reversedData = data.reversed.toList(); + + for (var i = 0; i < reversedData.length; i++) { + final rowMap = reversedData[i]; + final row = []; + + for (var key in keys) { + dynamic value; + if (key == 'sno' || + key.toLowerCase() == 's.no' || + key.toLowerCase() == 'sno.') { + value = i + 1; // serial number (descending) + } else if (key.toLowerCase() == 'is_active') { + // ✅ Handle Active/Inactive display + final rawVal = rowMap[key]; + value = (rawVal == 1 || rawVal == '1') ? 'Active' : 'Inactive'; + } else { + value = rowMap[key]; + } - // Add data - for (var rowData in data) { - var row = []; - for (var key in headers) { - final value = rowData[key]; if (value == null) { row.add(TextCellValue('-')); } else if (value is int) { @@ -116,56 +197,62 @@ class ExcelExporter { row.add(TextCellValue(value.toString())); } } - sheetObject.appendRow(row); + + sheet.appendRow(row); } - var bytes = excel.encode()!; + final excelBytes = excel.encode(); + if (excelBytes == null) throw Exception('Failed to encode Excel file'); if (kIsWeb) { - // ✅ Web: base64 download instead of Blob - final base64Str = base64Encode(bytes); - final dataUri = "data:application/octet-stream;base64,$base64Str"; + // Web download + final base64Str = base64Encode(excelBytes); + final dataUri = 'data:application/octet-stream;base64,$base64Str'; final anchor = html.AnchorElement(href: dataUri) - ..setAttribute("download", "$fileName.xlsx") + ..setAttribute('download', '$fileName.xlsx') ..click(); } else { - // Mobile/Desktop: save file + // Mobile/Desktop save + open final dir = await getApplicationDocumentsDirectory(); - final file = File('${dir.path}/$fileName.xlsx'); - await file.writeAsBytes(bytes); + final filePath = '${dir.path}/$fileName.xlsx'; + final file = File(filePath); + await file.writeAsBytes(excelBytes, flush: true); await OpenFile.open(file.path); } - } catch (e) { - print("Error exporting Excel: $e"); + } catch (e, st) { + debugPrint('❌ Excel export error: $e\n$st'); + rethrow; } } } - // class ExcelExporter { // static Future exportToExcel({ // required String sheetName, // required List> data, // required List headers, // required String fileName, +// required List keys, // }) async { // try { -// // Create Excel and delete default sheet +// // Create Excel // var excel = Excel.createExcel(); +// +// // Rename default sheet // if (excel.sheets.containsKey('Sheet1')) { -// excel.delete('Sheet1'); +// excel.rename('Sheet1', sheetName); // } // -// // Create your sheet -// Sheet sheetObject = excel[sheetName]; // now this is the only sheet +// // Get the sheet +// Sheet sheetObject = excel[sheetName]!; // // // Add headers -// sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); +// sheetObject.appendRow(keys.map((h) => TextCellValue(h)).toList()); // // // Add data // for (var rowData in data) { // var row = []; -// for (var key in headers) { +// for (var key in keys) { // final value = rowData[key]; // if (value == null) { // row.add(TextCellValue('-')); @@ -183,13 +270,12 @@ class ExcelExporter { // var bytes = excel.encode()!; // // if (kIsWeb) { -// // Web: download -// final blob = html.Blob([bytes]); -// final url = html.Url.createObjectUrlFromBlob(blob); -// final anchor = html.AnchorElement(href: url) +// // ✅ Web: base64 download instead of Blob +// final base64Str = base64Encode(bytes); +// final dataUri = "data:application/octet-stream;base64,$base64Str"; +// final anchor = html.AnchorElement(href: dataUri) // ..setAttribute("download", "$fileName.xlsx") // ..click(); -// html.Url.revokeObjectUrl(url); // } else { // // Mobile/Desktop: save file // final dir = await getApplicationDocumentsDirectory(); @@ -202,66 +288,3 @@ class ExcelExporter { // } // } // } - - -//--------------------------------------------------------------------------- - -// class ExcelExporter { -// static Future exportToExcel({ -// required String sheetName, -// required List> data, -// required List headers, -// required String fileName, -// }) async { -// try { -// // var excel = Excel.createExcel(); -// // Sheet sheetObject = excel[sheetName]; -// -// // Delete default sheet -// var excel = Excel.createExcel(); -// excel.delete('Sheet1'); // remove default -// Sheet sheetObject = excel[sheetName]; -// -// // Add headers -// sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); -// -// // Add data -// for (int i = 0; i < data.length; i++) { -// var row = []; -// for (var key in headers) { -// final value = data[i][key]; -// if (value == null) { -// row.add(TextCellValue('-')); -// } else if (value is int) { -// row.add(IntCellValue(value)); -// } else if (value is double) { -// row.add(DoubleCellValue(value)); -// } else { -// row.add(TextCellValue(value.toString())); -// } -// } -// sheetObject.appendRow(row); -// } -// -// var bytes = excel.encode()!; -// -// if (kIsWeb) { -// // ✅ Web: download as file -// final blob = html.Blob([bytes]); -// final url = html.Url.createObjectUrlFromBlob(blob); -// final anchor = html.AnchorElement(href: url) -// ..setAttribute("download", "$fileName.xlsx") -// ..click(); -// html.Url.revokeObjectUrl(url); -// } else { -// // ✅ Mobile/Desktop: save to file system -// final dir = await getApplicationDocumentsDirectory(); -// final file = File('${dir.path}/$fileName.xlsx'); -// await file.writeAsBytes(bytes); -// await OpenFile.open(file.path); -// } -// } catch (e) { -// print("Error exporting Excel: $e"); -// } -// } -// }