import 'dart:convert'; 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'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:universal_html/html.dart' as html; import 'dart:typed_data'; import 'package:flutter/foundation.dart' show kIsWeb; import 'dart:io' show File; // Only used in mobile/desktop import 'package:path_provider/path_provider.dart'; import 'package:open_file/open_file.dart'; /// Optional per-field Excel cell formatter. Use when raw map values differ from /// on-screen display (dates, status labels, etc.). Key must match [keys] entries. typedef ExportValueFormatter = String Function( dynamic value, Map row, ); class ExportBtn extends HookWidget { final String sheetName; final String fileName; final List> data; final List? displayHeaders; // dynamic headers // final List headers; // dynamic headers final List? keys; // dynamic headers final bool? txt; /// When set, these keys are exported as the returned string (not raw int/double). final Map? valueFormatters; const ExportBtn({ super.key, this.txt, required this.sheetName, required this.fileName, required this.data, // required this.headers, required this.displayHeaders, this.keys, this.valueFormatters, }); @override Widget build(BuildContext context) { 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, valueFormatters: valueFormatters, ); 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(7.0), decoration: BoxDecoration( color: const Color(0xFF2E7D6E), // 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: 15) : null, Image.asset( "assets/miscellaneous/export.png", height: 13, width: 13, ), // 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 displayHeaders, required List keys, required String fileName, Map? valueFormatters, }) async { if (displayHeaders.length != keys.length) { throw ArgumentError('displayHeaders and keys must have same length'); } try { final excel = Excel.createExcel(); // Rename default sheet if needed if (excel.sheets.containsKey('Sheet1')) { try { excel.rename('Sheet1', sheetName); } catch (_) {} } // Get the sheet final sheet = excel[sheetName]!; // ✅ 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(); final formatters = valueFormatters; for (var i = 0; i < reversedData.length; i++) { final rowMap = reversedData[i]; final row = []; // Inside ExcelExporter class -> exportToExcel method for (var key in keys) { dynamic value; if (key == 'sno' || key.toLowerCase() == 's.no' || key.toLowerCase() == 'sno.') { value = i + 1; } else if (formatters != null && formatters.containsKey(key)) { final formatted = formatters[key]!(rowMap[key], rowMap); final text = formatted.trim().isEmpty ? '-' : formatted; row.add(TextCellValue(text)); continue; } else if (key.toLowerCase() == 'is_active') { final rawVal = rowMap[key]; value = (rawVal == 1 || rawVal == '1') ? 'Active' : 'Inactive'; } else { // FIX: Ensure we extract the value and check if it's "null" as a string value = rowMap[key]; if (value == "null") value = null; } if (value == null || value.toString().trim().isEmpty) { row.add(TextCellValue('-')); // This ensures the column isn't empty } 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); } final excelBytes = excel.encode(); if (excelBytes == null) throw Exception('Failed to encode Excel file'); if (kIsWeb) { // Web download final base64Str = base64Encode(excelBytes); final dataUri = 'data:application/octet-stream;base64,$base64Str'; final anchor = html.AnchorElement(href: dataUri) ..setAttribute('download', '$fileName.xlsx') ..click(); } else { // Mobile/Desktop save + open final dir = await getApplicationDocumentsDirectory(); final filePath = '${dir.path}/$fileName.xlsx'; final file = File(filePath); await file.writeAsBytes(excelBytes, flush: true); await OpenFile.open(file.path); } } 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 // var excel = Excel.createExcel(); // // // Rename default sheet // if (excel.sheets.containsKey('Sheet1')) { // excel.rename('Sheet1', sheetName); // } // // // Get the sheet // Sheet sheetObject = excel[sheetName]!; // // // Add headers // sheetObject.appendRow(keys.map((h) => TextCellValue(h)).toList()); // // // Add data // for (var rowData in data) { // var row = []; // for (var key in keys) { // final value = rowData[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: 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(); // } else { // // Mobile/Desktop: save file // 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"); // } // } // }