import 'dart:convert'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../config/apiUrl.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_forex.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; import 'package:http/http.dart' as http; class ForexScreen extends StatefulWidget { final Map? apiData; final Function(bool) onClose; final Map? selectedItem; final List? apiCountryData; final Function(Map)onSaveForex; final String? loginUser; ForexScreen({ required this.onClose, this.apiData, required this.selectedItem, required this.apiCountryData, required this.onSaveForex, required this.loginUser }); @override _ForexScreenState createState() => _ForexScreenState(); } class _ForexScreenState extends State { final GlobalKey _formKey = GlobalKey(); Map selectedValues = {}; bool isChecked = false; // State variable for checkbox int fifteenPercent = 0; int remainingAmount = 0; Map focusNodes = {}; Map focusStates = {}; Map textControllers = {}; List countryList = []; List dataHeader = ["_forexStartDate", "_forexEndDate", "_countries", "_duration", "_currency","_perdiemAmount", "_transport", "_accomodation", "_telephone", "_otherExpenses", "_cardNumber", "_currency","_card", "_cash","_checkForex", "_deliveryLocation","_comments"]; String _formatDate(String? date) { if (date == null || date.isEmpty) return ""; try { DateTime parsedDate = DateTime.parse(date); // Assuming input is YYYY-MM-DD return DateFormat("dd-MM-yyyy").format(parsedDate); // Convert to DD-MM-YYYY } catch (e) { print("Error formatting date: $e"); return date; // Return as is if parsing fails } } Map errorMessages = {}; String? selectedCountry; String? selectedCurrency; String? selectedDuration; String? selectedPerdiemAmount; String? CalculatedOtherExpenses; String? selectedQuotedAmount; Map get forexData { Map data ={ "start_date": textControllers["_forexStartDate"]?.text, "end_date": textControllers["_forexEndDate"]?.text, "country_code": selectedCountry, "duration": selectedDuration, "currency": selectedCurrency, "perdiem_amount": selectedPerdiemAmount, "transport": textControllers["_transport"]?.text, "accommodation": textControllers["_accomodation"]?.text, "telephone": textControllers["_telephone"]?.text, "have_card": isChecked ? "1" : "0", "card_number": textControllers["_cardNumber"]?.text, "deposit_on_card":textControllers["_card"]?.text, "deposit_on_cash": textControllers["_cash"]?.text, "delivery_location": textControllers["_deliveryLocation"]?.text, "comments": textControllers["_comments"]?.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; } else if (widget.selectedItem?["forex_id"] != null && widget.selectedItem?["forex_id"] != 0) { data["forex_id"] = widget.selectedItem!["forex_id"]; } } return data; } Map get getForexData { return { "country_code": selectedCountry, "start_date": _formatDate(textControllers["_forexStartDate"]?.text), "end_date": _formatDate(textControllers["_forexEndDate"]?.text), // "currency": selectedCurrency ?? "", }; } Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } Future postgetForexData(Map forexData) async { final String apiUrldata = '$apiUrl/api/plans/getForexPerdiem'; print("Sending Data: ${jsonEncode(forexData)}"); final token = await getToken(); // Fetch token if (token == null) { throw Exception('Token not found. Please log in.'); } try { final response = await http.post( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: jsonEncode(forexData), // Convert map to JSON ); if (response.statusCode == 200) { print("Plan submitted successfully!"); print("Response: ${response.body}"); final Map responseData = jsonDecode(response.body); // Ensure the response contains the expected keys before updating state if (responseData.containsKey("currency") && responseData.containsKey("perdiem_amount") && responseData.containsKey("duration")) { setState(() { // Update only if data is valid selectedCurrency = responseData["currency"] ?? selectedCurrency; selectedPerdiemAmount = responseData["perdiem_amount"]?.toString() ?? ""; selectedDuration = responseData["duration"]?.toString() ?? ""; selectedQuotedAmount = responseData["perdiem_amount"]?.toString() ?? ""; }); _onFieldChangedForOthers(); _divideQuotedAmount(); } else { print("Warning: Response does not contain expected fields."); } } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { print(" Error submitting plan: $e"); } } bool isValidForexData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = ["start_date", "end_date", "country_code", "deposit_on_card", "deposit_on_cash", "card_number"]; // If have_card is "1", then delivery_location is required bool isCardChecked = data["have_card"] == "1"; if (isCardChecked) { requiredFields.add("delivery_location"); } // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "This field is required"; } } return errorMessages.isEmpty; // Valid if there are no errors } void handleSave(){ print( "Handle Save forexData $forexData"); Map data = forexData; if (!isValidForexData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails }else { widget.onSaveForex(forexData); // Send object to parent } widget.onClose(false);// Close screen after saving } DateTime? _parseDate(String date) { try { return DateFormat("yyyy-MM-dd").parse(date); // Change format if needed } catch (e) { return null; } } TextEditingController initController(String key) { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } @override void initState() { super.initState(); // Initialize fields dynamically for (var field in dataHeader) { if (!textControllers.containsKey(field)) { textControllers[field] = TextEditingController(); } if (!focusNodes.containsKey(field)) { focusNodes[field] = FocusNode(); } if (!focusStates.containsKey(field)) { focusStates[field] = false; } } print("textControllers - $textControllers"); // Add focus listeners focusNodes.forEach((key, focusNode) { _addFocusListener(focusNode, (focus) { setState(() { focusStates[key] = focus; }); }); }); // Add listeners to text fields textControllers["_forexStartDate"]?.addListener(_onFieldChanged); textControllers["_forexEndDate"]?.addListener(_onFieldChanged); handleUpdatedField(); } void handleUpdatedField(){ // Set the selected value if available if (widget.selectedItem != null) { print("UPDATAED SELECTION"); textControllers["_forexStartDate"] = initController("start_date"); textControllers["_forexEndDate"] = initController("end_date"); textControllers["_transport"] = initController("transport"); textControllers["_accomodation"] = initController("accommodation"); textControllers["_telephone"] = initController("telephone"); textControllers["_cardNumber"] = initController("card_number"); textControllers["_card"] = initController("deposit_on_card"); textControllers["_cash"]= initController("deposit_on_cash"); textControllers["_deliveryLocation"]= initController("delivery_location"); textControllers["_comments"]= initController("comments"); // Set dropdown values selectedCountry = widget.selectedItem!["country_code"] as String?; selectedCurrency = widget.selectedItem!["currency"] as String?; selectedDuration = widget.selectedItem!["duration"] as String?; selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?; isChecked = widget.selectedItem!["have_card"] == "1"; // Convert string to bool _onFieldChangedForOthers(); setState(() {}); // Update the UI // // Calculate other expenses (if applicable) // CalculatedOtherExpenses = calculateOtherExpenses(); } } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } // ___________________________- // Check if all required fields have data bool _isForexDataComplete() { final data = getForexData; return (data["country_code"]?.isNotEmpty ?? false) && (data["start_date"]?.isNotEmpty ?? false) && (data["end_date"]?.isNotEmpty ?? false); } // Handle field changes void _onFieldChanged() { if (_isForexDataComplete()) { postgetForexData(getForexData); } } // Handle dropdown change void _onCountryChanged(String? newCountry) { setState(() { selectedCountry = newCountry; }); if (_isForexDataComplete()) { postgetForexData(getForexData); } } // _____________ End Forex Details ______________- void _onFieldChangedForOthers() { setState(() { double transport = double.tryParse(textControllers["_transport"]?.text ?? "0") ?? 0; double accommodation = double.tryParse(textControllers["_accomodation"]?.text ?? "0") ?? 0; double telephone = double.tryParse(textControllers["_telephone"]?.text ?? "0") ?? 0; double calclateVal = (transport + accommodation + telephone) ; CalculatedOtherExpenses = (calclateVal).toStringAsFixed(2); // Convert selectedPerdiemAmount to double before performing the addition double perdiemAmount = double.tryParse(selectedPerdiemAmount ?? "0") ?? 0; print("calclateVal - $calclateVal"); selectedQuotedAmount = ((perdiemAmount + calclateVal).toString() ?? 0) as String?; }); _divideQuotedAmount(); errorMessages.clear(); } void _divideQuotedAmount(){ int? quotedAmount = int.tryParse(selectedQuotedAmount!); print("DIVIDREFD - $selectedQuotedAmount -$quotedAmount"); if (quotedAmount != null) { fifteenPercent = (quotedAmount * 15) ~/ 100; // Calculate 15% (integer division) remainingAmount = quotedAmount - fifteenPercent; // Subtract from total textControllers["_cash"]?.text = fifteenPercent.toString(); textControllers["_card"]?.text = remainingAmount.toString(); print("15% Amount: $fifteenPercent"); print("Remaining Amount: $remainingAmount"); } else { print("Invalid number format in selectedQuotedAmount"); } } void _validateCardAmount(String value) { print("_validateCardAmount - $value - $remainingAmount"); int? enteredAmount = int.tryParse(value) ; int? cashAmount = int.tryParse(textControllers["_cash"]!.text ?? "0"); int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0"); int? calculateAmnt = cashAmount! + enteredAmount!; print("CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount"); if (enteredAmount == null || calculateAmnt > qouteAmount!) { errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount"; } else { errorMessages["deposit_on_card"] = ""; // Clear error if valid } // Refresh UI if using StatefulWidget setState(() {}); } void _validateCashAmount(String value) { print("_validateCashAmount - $value - $fifteenPercent"); int? enteredAmount = int.tryParse(value); if (enteredAmount == null || enteredAmount > fifteenPercent) { errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent"; } else { errorMessages["deposit_on_cash"] = ""; // Clear error if valid } // Refresh UI if using StatefulWidget setState(() {}); } @override void dispose() { for (var node in focusNodes.values) { node.dispose(); } // Dispose all dynamically created TextEditingControllers for (var controller in textControllers.values) { controller.dispose(); } super.dispose(); } void _validateDates() { print("VALiDATING DATES"); DateTime? startDate = _parseDate(textControllers["_forexStartDate"]?.text ?? ""); DateTime? endDate = _parseDate(textControllers["_forexEndDate"]?.text ?? ""); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { errorMessages["end_date"] = "End date cannot be earlier than start date"; }); } else { setState(() { errorMessages.remove("end_date"); }); } } @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isMobile = sizingInfo.isMobile; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Container( color: Color(0xFFF4F4FB), child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Align( alignment: Alignment.centerRight, child: InkWell( onTap: () { widget.onClose(false); }, child: Icon( Icons.close, size: 18, color: Color(0xFF575A74), ), ), ), Text("Forex List", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), SizedBox( height: 6, ), Padding( padding: const EdgeInsets.all(28.0), child: Center( child: Column(children: _buildAccomadtionForm(isDesktop)), ), ) ], ), ), ), ); }); } List _buildAccomadtionForm (bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; } // List> rowBuilders = [ // // _builClassType(isDesktop), // _buildSecondRow(isDesktop) // ]; List rowBuilders = [ ..._builClassType(isDesktop), // Spread the List Divider(), ..._buildSecondRow(isDesktop), // Spread the List ]; return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), SizedBox( height: 28, ), Text("Forex Details",style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF575A74)),), SizedBox(height: 8,), Divider(), SizedBox(height: 8,), ...buildResponsiveRow(_builClassType(isDesktop)), SizedBox(height: 8,), Divider(), SizedBox(height: 28,), ...buildResponsiveRow(_buildSecondRow(isDesktop)), ...buildResponsiveRow(_buildCardDetailsRow(isDesktop)), ...buildResponsiveRow(_buildFprexCard(isDesktop)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), ...buildResponsiveRow(_buildCommetsRow(isDesktop)), // Actions row remains a Row Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ]; } List _buildFirstRow(isDesktop) { DateTime? _selectedCheckOutDate; DateTime? _selectedEndDate; Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; textControllers["_forexStartDate"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } }; Future _selectForexEndDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedEndDate != null && _selectedEndDate!.isAfter(today) ? _selectedEndDate! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedEndDate) { setState(() { _selectedEndDate = pickedDate; textControllers["_forexEndDate"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } } late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes countryList = widget.apiCountryData ?? []; // Map country codes to country names countryMap = { for (var item in countryList) item['country_code'] as String: item['country_name'] as String }; // Extract only country codes for processing countryCodes = countryMap.keys.toList(); selectedCountry ??= null; // // Set default selected value // if (selectedCountry == null && countryCodes.isNotEmpty) { // selectedCountry = countryCodes.first; // } // -------------------------- End Selected Country Dropdown -------------------------------------- return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Forex Start Date", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_forexStartDate"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( // onTap: () async{ // _selectCheckOutDate(context); // // }, onTap: () async { await _selectCheckOutDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text); DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { errorMessages["end_date"] = "End date cannot be earlier than start date"; }); } else { setState(() { errorMessages.remove("end_date"); }); } } }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_forexStartDate"], controller: textControllers["_forexStartDate"], style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", labelStyle: const TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16), suffixIcon: const Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), if (errorMessages["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( "Select Start Date", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Forex End Date", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_forexEndDate"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: GestureDetector( // onTap: () => _selectForexEndDate(context), onTap: () async { await _selectForexEndDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text); DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { errorMessages["end_date"] = "End date cannot be earlier than start date"; }); } else { setState(() { errorMessages.remove("end_date"); }); } } }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_forexEndDate"], controller: textControllers["_forexEndDate"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), if (errorMessages["end_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["end_date"]!, // "Select End Date", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop)Spacer() else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Countries", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_countries"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: DropdownSearch( selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 1,), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", style: TextStyle(fontSize: 12), ), ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name selectedCountry = countryMap.entries .firstWhere((entry) => entry.value == newValue) .key; _onCountryChanged(selectedCountry); }); }, ), ), ), if (errorMessages["country_code"] != null) ...[ SizedBox(height: 5), // Space before error message Text( "Select Country", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), ]; } List _builClassType(bool isDesktop){ return [ // if (isDesktop) Spacer() else SizedBox( // height: 8, // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Durartion", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_durationFocused"] ?? false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "dur", // selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration", selectedDuration ?? "Duration", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), ), ), ), ), ], ), // if (isDesktop)SizedBox(width: 8,) else SizedBox( // height: 8, // ), if (isDesktop)Spacer() else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Currency *", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_currencyFocused"] ?? false, isDesktop: isDesktop, color: Colors.transparent, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "cur", // "${selectedCurrency}", selectedCurrency ?? "Currency", // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), ), ), ), ), ], ), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), if (isDesktop)Spacer() else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Perdiem Amount", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "amo", selectedPerdiemAmount ?? "Amount", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), ), ), ), ), ], ), if (isDesktop)Spacer() else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Quoted Amount", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "amo", selectedQuotedAmount ?? "0", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), ), ), ), ), ], ), // if (isDesktop)Spacer() else SizedBox(height: 8,), ]; } List _buildSecondRow(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Transport", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_transport"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_transport"], controller: textControllers["_transport"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Transport", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Accomodation", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_accomodation"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_accomodation"], controller: textControllers["_accomodation"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Accomodation", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Telephone", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_telephone"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_telephone"], controller: textControllers["_telephone"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Telephone", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Other Expenses", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( height: 40, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( CalculatedOtherExpenses?? "0", // focusNode: _toFocusNode, // controller: _toController, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), ), ), ), ), ], ), ]; } List _buildCardDetailsRow(bool isDesktop) { List purposeList = widget.apiData?['flight_class'] ?? []; List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( value: item['dropdown_value'], child: Text(item['dropdown_value']), )).toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text("No options available", style: TextStyle(color: Colors.grey)), ), ); } // Default selected value String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Cash*", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_cash"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_cash"], controller: textControllers["_cash"], style: const TextStyle(fontSize: 12), keyboardType: TextInputType.number, onChanged: (value) { _validateCashAmount(value); // Call validation when text changes }, decoration: const InputDecoration( labelText: "Cash", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["deposit_on_cash"] != null) ...[ SizedBox(height: 5), // Space before error message Text( // "Required", errorMessages["deposit_on_cash"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Card*", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_card"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_card"], controller: textControllers["_card"], style: const TextStyle(fontSize: 12), keyboardType: TextInputType.number, onChanged: (value) { _validateCardAmount(value); // Call validation when text changes }, decoration: const InputDecoration( labelText: "Card", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["deposit_on_card"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["deposit_on_card"] !, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox( height: 8, ), // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Currency*", // style: TextStyle( // fontSize: 12, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74)), // ), // SizedBox(height: 5), // CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_currency"] ?? false, // isDesktop: isDesktop, // child: SizedBox( // height: 40, // // child: DropdownButtonFormField( // focusNode: focusNodes["_currencyFocusNode"], // // controller: _hotelNameController, // value: selectedPurpose, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10), // Proper padding // ), // onChanged: purposeList.isNotEmpty // ? (newValue) { // setState(() { // selectedPurpose = newValue; // }); // } // : null, // items: dropdownItems, // ), // ), // ), // ], // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Card Number*", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldForexWrapper( isFocused: focusStates["_cardNumber"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["_cardNumber"], controller: textControllers["_cardNumber"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Card Number", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["card_number"] != null) ...[ SizedBox(height: 5), // Space before error message Text( "Required", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), ]; } List _buildThirdRow(bool isDesktop) { return [ isChecked? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Delivery Location", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["_deliveryLocation"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_deliveryLocation"], controller: textControllers["_deliveryLocation"], maxLines: 3, keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Location", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 4), ), ), ), if (errorMessages["delivery_location"] != null) ...[ SizedBox(height: 5), // Space before error message Text( "Required", style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ) :SizedBox.shrink() ]; } List _buildCommetsRow(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["_comments"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_comments"], controller: textControllers["_comments"], maxLines: 3, keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 4), ), ), ), ], ) ]; } List _buildFprexCard(bool isDesktop) { return [ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Checkbox( value: isChecked, side: BorderSide( color: Colors.grey, // Change border color width: 1, // Adjust thickness ), onChanged: (bool? value) { setState(() { isChecked = value!; }); }, ), Text( "Check If You Don't Have a forex Account", style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), ], ) ]; } List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Close", style: TextStyle(color: Colors.white, fontSize: 14), ), ), SizedBox(width: 10), // Space between buttons // Save Changes Button ElevatedButton( onPressed: () { handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Save Changes", style: TextStyle(color: Colors.white, fontSize: 14), ), ), ]; } }