import 'dart:convert'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; class ForexListWidget extends StatefulWidget { final List> forexList; final Function(bool, Map, String) onOpen; final Function(Map) onDeleteForex; final List? apiCountryData; final Map? apiData; final Function(String, bool) onAddNew; final bool isViewMode; const ForexListWidget({ super.key, required this.forexList, required this.onOpen, required this.onDeleteForex, required this.apiCountryData, required this.onAddNew, required this.apiData, required this.isViewMode, }); @override State createState() => _ForexListWidgetState(); } class _ForexListWidgetState extends State { Color? layoutColor; Color? secondColor; Color? thridColor; bool orgHasService = false; @override void initState() { super.initState(); loadInitialData(); } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? secondString = await getSecondaryColor(); String? thridString = await getTernaryColor(); // String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; secondColor = layoutString != null ? Color(int.parse(secondString!)) : Colors.orange; thridColor = layoutString != null ? Color(int.parse(thridString!)) : Colors.orangeAccent; // bodyColor = // bodyStringColor != null // ? Color(int.parse(bodyStringColor)) // : Colors.white; }); } void getOrgServices() async { final services = await getOrgServicesName(); print("getOrgServices storage: $services"); // just names final names = services.map((s) => s['name']).toList(); print("Names only: $names"); final hasFlight = names.contains("Forex"); setState(() { orgHasService = hasFlight; }); print("orgHasService: $orgHasService"); } @override Widget build(BuildContext context) { final isDesktop = MediaQuery.of(context).size.width > 1024; final ApiService apiService = ApiService(); return Padding( padding: isDesktop ? const EdgeInsets.all(16.0) : const EdgeInsets.all(5.0), child: Container( margin: const EdgeInsets.only(top: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header with title and button // Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // if ((!isDesktop) && forexList.isNotEmpty) // Text( // " ", // style: const TextStyle( // fontSize: 18, // fontWeight: FontWeight.bold, // ), // ), // MouseRegion( // cursor: isViewMode // ? SystemMouseCursors.forbidden // : SystemMouseCursors.click, // // child: GestureDetector( // onTap: isViewMode // ? null // : () { // print("New data"); // onAddNew("Forex", true); // }, // child: Row( // mainAxisSize: // MainAxisSize.min, // Ensures content fits nicely // children: [ // // Text( // // "Add New", // // style: TextStyle(fontSize: 13), // // ), // // SizedBox(width: 8), // spacing between icon and text // Icon( // Icons.add_circle_sharp, // size: 30, // color: Color(0xFF114D8B), // ) // ], // ), // ), // // onPressed: isViewMode // // ? null // // : () { // // print("New data"); // // // // }, // // child: Row( // // mainAxisSize: MainAxisSize.min, // // children: [ // // Icon( // // Icons.add_circle_sharp, // // size: 30, // // color: Color(0xFF114D8B), // // ) // // ], // // ), // ), // // MouseRegion( // // cursor: isViewMode // // ? SystemMouseCursors.forbidden // // : SystemMouseCursors.click, // // child: ElevatedButton( // // style: ElevatedButton.styleFrom( // // backgroundColor: Color(0xFF114D8B), // // foregroundColor: Colors.white, // // disabledBackgroundColor: Color(0xFF114D8B), // // disabledForegroundColor: Colors.white, // // shape: RoundedRectangleBorder( // // borderRadius: BorderRadius.circular(8), // // side: BorderSide(color: Color(0xFF114D8B), width: 2), // // ), // // padding: // // EdgeInsets.symmetric(horizontal: 20, vertical: 12), // // ), // // onPressed: isViewMode // // ? null // // : () { // // print("New data"); // // onAddNew("Forex", true); // // }, // // child: Row( // // mainAxisSize: // // MainAxisSize.min, // Ensures content fits nicely // // children: [ // // Icon( // // Icons.add_circle_sharp, // // size: 30, // // color: Color(0xFF114D8B), // // ) // // ], // // ), // // ), // // ), // ], // ), SizedBox(height: isDesktop ? 16 : 4), // Scroll behavior based on device _buildData(context, isDesktop), ], ), ), ); } Widget _buildData(BuildContext context, bool isDesktop) { final ApiService apiService = ApiService(); List> filteredList = widget.forexList.where((item) => item["is_active"] == "1").toList(); print("filteredList- $filteredList"); List visatypeList = widget.apiData?['visa_type_of_visa'] ?? []; List countryList = widget.apiCountryData ?? []; String getRequestForVisa(String? specialRequestKey) { if (specialRequestKey == null) return "N/A"; return visatypeList .firstWhere( (element) => element["dropdown_key"].toString() == specialRequestKey, orElse: () => {"dropdown_value": "N/A"}, )["dropdown_value"] .toString(); } String getRequestForCountry(String? countryCode) { if (countryCode == null) return "N/A"; return countryList .firstWhere( (element) => element["country_code"].toString() == countryCode, orElse: () => {"country_name": "N/A"}, )["country_name"] .toString(); } // String formatDate(String dateString) { // try { // DateTime date = DateTime.parse(dateString); // return DateFormat('d MMM yy').format(date); // Example: 4 Apr 25 // } catch (e) { // return "Invalid Date"; // } // } String formatDate(String dateString) { try { DateTime date = DateFormat("dd-MM-yyyy").parse(dateString); return DateFormat('dd MMM yy').format(date); // Example: 23 May 25 } catch (e) { return "Invalid Date"; } } String formatTime(String timeString) { try { final parts = timeString.split(':'); int hour = int.parse(parts[0]); int minute = int.parse(parts[1]); final now = DateTime.now(); DateTime dateTime; if (hour == 24 && minute == 0) { // 24:00 is treated as 00:00 on the next day dateTime = DateTime( now.year, now.month, now.day, ).add(const Duration(days: 1)); } else { if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { throw FormatException("Invalid hour or minute"); } dateTime = DateTime(now.year, now.month, now.day, hour, minute); } return DateFormat('HH:mm').format(dateTime); // 24-hour format } catch (e) { return "Invalid Time"; } } return ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: filteredList.length, itemBuilder: (context, index) { final item = filteredList[index]; final double transport = double.tryParse(item["transport"] ?? "0") ?? 0; final double accommodation = double.tryParse(item["accommodation"] ?? "0") ?? 0; final double telephone = double.tryParse(item["telephone"] ?? "0") ?? 0; final double perdiemAmount = double.tryParse(item["perdiem_amount"] ?? "0") ?? 0; final double calculatedValue = transport + accommodation + telephone; final String calculatedOtherExpenses = calculatedValue.toStringAsFixed( 0, ); final double finalTotal = calculatedValue + perdiemAmount; return Container( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), // color: Colors.orange.shade50, // color: Colors.white, color: isDesktop ? Colors.white : thridColor, boxShadow: isDesktop ? [ BoxShadow( color: Colors.black12, blurRadius: 3, offset: Offset(0, 1), ), ] : [], border: Border( top: BorderSide( color: Colors.white70, // Change color to match your theme width: 2, ), ), ), child: Padding( padding: isDesktop ? EdgeInsets.all(12) : EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( // mainAxisAlignment: MainAxisAlignment.end, children: [ Expanded( flex: 1, child: Text( getRequestForCountry(item["country_code"]!.toString()), style: GoogleFonts.poppins( fontSize: 14, fontWeight: isDesktop ? FontWeight.w800 : FontWeight.w600, color: !isDesktop ? Colors.black : Color(0xFF575A74), ), ), ), Spacer(), if (orgHasService) ...[ GestureDetector( onTap: () => widget.onOpen(true, item, "Forex"), child: Tooltip( message: 'Edit Forex Details', child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, height: 15, // color: Color(0xFF114D8B), color: !isDesktop ? Colors.black : Color(0xFF575A74), ), ), ), SizedBox(width: 10), GestureDetector( onTap: () => widget.onDeleteForex(item), child: Tooltip( message: 'Delete Forex Details', child: Image.asset( 'assets/images/IconsImg/delete.png', width: 20, height: 15, color: Colors.red, ), ), ), ], item['forex_id'] != null ? IconButton( icon: Icon( Icons.download, color: Color(0xFF114D8B), size: 18, ), onPressed: () { print("PDF- ${item['forex_id']}"); final forexIdString = item['forex_id']; final id = int.tryParse(forexIdString ?? ''); if (id != null) { apiService.getForexPdfDownload(context, id); } else { print("Invalid forex_id: $forexIdString"); } }, ) : SizedBox.shrink(), // or Container() ], ), // Divider(color: Colors.blueGrey.shade50), // SizedBox(height: 4), isDesktop ? Row( children: [ Expanded( flex: 2, child: Text( "Currency", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Duration", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Time Period ", style: GoogleFonts.poppins(fontSize: 11), ), ), // Expanded( // flex: 2, // child: Text( // " Total ", // style: TextStyle( // fontSize: 11, fontFamily: "Archivo"), // )), Expanded( flex: 2, child: Text( " Perdiem ", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Others ", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Total ", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Cash ", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Card ", style: GoogleFonts.poppins(fontSize: 11), ), ), Expanded( flex: 2, child: Text( " Comments", style: GoogleFonts.poppins(fontSize: 11), ), ), ], ) : SizedBox.shrink(), SizedBox(height: 8), isDesktop ? Row( children: [ Expanded( flex: 2, child: Text( item["currency"], style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( item["duration"], style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( "${formatDate(item["start_date"]!)} - ${formatDate(item["end_date"]!)}", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( "$perdiemAmount", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( "$calculatedOtherExpenses", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( "$finalTotal", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( item["deposit_on_cash"], style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: Text( item["deposit_on_card"], style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 10), Expanded( flex: 2, child: _buildComments( "Comments:", item["comments"] ?? "N/A", ), ), ], ) : Container( color: Colors.white, padding: const EdgeInsets.all(4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildRow("Currency", item["currency"]), SizedBox(height: 2), _buildRow("Duration", item["duration"]), SizedBox(height: 2), _buildRow( "Time Period", "${formatDate(item["start_date"]!)} - ${formatDate(item["end_date"]!)}", // "${item["start_date"]!} - ${item["end_date"]!}", ), SizedBox(height: 2), _buildRow("Perdiem", "$perdiemAmount"), SizedBox(height: 2), _buildRow("Others", "$calculatedOtherExpenses"), SizedBox(height: 2), _buildRow("Total", "$finalTotal"), SizedBox(height: 2), _buildRow("Cash", item["deposit_on_cash"]!), SizedBox(height: 2), _buildRow("Card", item["deposit_on_card"]!), SizedBox(height: 2), _buildRow("Comments", item["comments"] ?? "N/A"), ], ), ), // child: Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // // Top Row (Country, Currency, Total) // Wrap( // spacing: 20, // horizontal space between items // runSpacing: 10, // vertical space between rows // // Row( // // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // _infoBlock( // "Country code", // item["country_code"] ?? "N/A", // ), // _infoBlock("Currency", item["currency"] ?? "N/A"), // _infoBlock( // "Total Amount", // item["total_amount"]?.toString() ?? "0", // ), // ], // ), // const SizedBox(height: 12), // // // Second Row (Start Date, End Date, Duration) // Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // _infoBlock( // "Start Date", // item["start_date"] ?? "N/A", // ), // _infoBlock("End Date", item["end_date"] ?? "N/A"), // _infoBlock( // "Duration", // item["duration"]?.toString() ?? "N/A", // ), // ], // ), // const SizedBox(height: 16), // // // Comment label // Text( // "Comment", // style: GoogleFonts.poppins( // fontSize: 14, // fontWeight: FontWeight.w600, // ), // ), // const SizedBox(height: 8), // if (item["comments"] != null && // item["comments"].toString().trim().isNotEmpty) // Container( // width: double.infinity, // padding: const EdgeInsets.all(12), // decoration: BoxDecoration( // border: Border.all( // color: const Color(0xFF7098DD), // width: 1, // ), // borderRadius: BorderRadius.circular(6), // ), // child: Text( // item["comments"] ?? "N/A", // style: GoogleFonts.poppins( // fontSize: 12, // color: Colors.black87, // ), // ), // ) // else // const Text("N/A", style: TextStyle(fontSize: 12)), // ], // ), ], ), ), ); }, ); } Widget _buildComments(String title, String value) { // Define character limits based on title Map limits = { // "Special Request:": 30, "Comments:": 30, // Add more keys and limits if needed }; int limit = limits[title] ?? 50; // Default limit if title not found bool exceedsLimit = value.length > limit; String wrapText(String text, int maxLineLength) { final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)'); return pattern.allMatches(text).map((m) => m.group(0)!).join('\n'); } return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: exceedsLimit ? Tooltip( message: wrapText(value, 50), decoration: BoxDecoration( color: Colors.grey.shade200, // Black background borderRadius: BorderRadius.circular(6), ), textStyle: TextStyle( color: Colors.black, ), // Tooltip text color padding: EdgeInsets.all(8), preferBelow: false, child: Text( value.substring(0, limit) + "...", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), overflow: TextOverflow.ellipsis, ), ) : Text( value, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ); } Widget _infoBlock(String label, String value) { return Expanded( child: Padding( padding: const EdgeInsets.only(right: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87, ), ), const SizedBox(height: 4), Text( value, style: GoogleFonts.poppins(fontSize: 12, color: Colors.black87), ), ], ), ), ); } Widget _buildRow(String title, String value) { // Define character limits based on title Map limits = { // "Special Request:": 30, "Comments:": 10, // Add more keys and limits if needed }; int limit = limits[title] ?? 50; // Default limit if title not found bool exceedsLimit = value.length > limit; String wrapText(String text, int maxLineLength) { final pattern = RegExp('.{1,$maxLineLength}(\\s+|\$)'); return pattern.allMatches(text).map((m) => m.group(0)!).join('\n'); } return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text( title, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, ), ), ), SizedBox(width: 8), Expanded( child: exceedsLimit ? Tooltip( message: wrapText(value, 50), decoration: BoxDecoration( color: Colors.grey.shade200, // Black background borderRadius: BorderRadius.circular(6), ), textStyle: TextStyle( color: Colors.black, ), // Tooltip text color padding: EdgeInsets.all(8), preferBelow: false, child: Text( value.substring(0, limit) + "...", style: GoogleFonts.poppins(fontSize: 12), overflow: TextOverflow.ellipsis, ), ) : Text(value, style: GoogleFonts.poppins(fontSize: 12)), ), ], ); } }