diff --git a/assets/images/IconsImg/Frame.png b/assets/images/IconsImg/Frame.png new file mode 100644 index 0000000..7271a16 Binary files /dev/null and b/assets/images/IconsImg/Frame.png differ diff --git a/assets/images/IconsImg/ViceVersaArrow.png b/assets/images/IconsImg/ViceVersaArrow.png new file mode 100644 index 0000000..2b08801 Binary files /dev/null and b/assets/images/IconsImg/ViceVersaArrow.png differ diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 87b6d1a..84235dd 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -651,6 +651,16 @@ class _ListAllPlansState extends State { ), ), columns: [ + DataColumn( + label: Text( + 'S.NO', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), + DataColumn( label: Text( 'Trip ID', @@ -669,6 +679,16 @@ class _ListAllPlansState extends State { ), ), ), + + DataColumn( + label: Text( + 'Trip Type', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ), DataColumn( label: Text( 'Emp Code', @@ -687,15 +707,7 @@ class _ListAllPlansState extends State { ), ), ), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), + DataColumn( label: Text( 'Created On', @@ -725,9 +737,21 @@ class _ListAllPlansState extends State { ), ], rows: - paginatedPlans.map((plan) { + // paginatedPlans.map((plan) + paginatedPlans.asMap().entries.map((entry) { + int index = entry.key; + var plan = entry.value; return DataRow( cells: [ + DataCell( + Text( + "${index + 1}", + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), DataCell( Text( plan.planId, @@ -748,6 +772,15 @@ class _ListAllPlansState extends State { overflow: TextOverflow.ellipsis, ), ), + DataCell( + Text( + plan.tripType, + style: TextStyle( + fontSize: 13, + fontFamily: "Inter", + ), + ), + ), DataCell( Text( plan.employeeCode ?? " - ", @@ -768,15 +801,7 @@ class _ListAllPlansState extends State { ), ), ), - DataCell( - Text( - plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), + DataCell( Text( _formatDate(plan.createdOn), @@ -867,26 +892,30 @@ class _ListAllPlansState extends State { ); }, ), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: + false, + ); + }, ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - false, - ); - }, - ), IconButton( icon: Icon( Icons diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 647c3b4..372f6d4 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -97,7 +97,7 @@ class _ApprovalListState extends State { setState(() { futurePlans = fetchPlans(); }); - + print("Token availa"); // Wait for futurePlans to be fetched and update allPlans futurePlans.then((plans) { setState(() { @@ -186,7 +186,9 @@ class _ApprovalListState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); + print('plansJson1'); List plansJson = data['data']; + print('plansJson $plansJson'); return plansJson.map((json) => Plan.fromJson(json)).toList(); } else { throw Exception('Failed to load plans'); @@ -345,6 +347,8 @@ class _ApprovalListState extends State { (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || + (plan.approverName?.toLowerCase().contains(lowerQuery) ?? + false) || (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); @@ -672,7 +676,7 @@ class _ApprovalListState extends State { ), DataColumn( label: Text( - 'Emp Code', + 'Traveller', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, @@ -681,7 +685,7 @@ class _ApprovalListState extends State { ), DataColumn( label: Text( - 'Traveller', + 'Approver', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, @@ -751,19 +755,40 @@ class _ApprovalListState extends State { ), ), DataCell( - Text( - plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.center, + + children: [ + SizedBox(height: 1), + Text( + plan.userName.isNotEmpty + ? plan.userName + : plan.travellerName, + style: TextStyle( + fontSize: 12, + fontFamily: "Inter", + ), + ), + SizedBox(height: 3), + Expanded( + child: Text( + "(${plan.employeeCode})" ?? + " - ", + style: GoogleFonts.poppins( + fontSize: 10, + // fontFamily: "Inter", + ), + ), + ), + ], ), ), DataCell( Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, + plan.approverName ?? " ", style: TextStyle( fontSize: 13, fontFamily: "Inter", @@ -875,37 +900,44 @@ class _ApprovalListState extends State { ); }, ), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + print( + "Approver Edit : ${plan.approverId}", + ); + print( + "Approver Edit2 : ${plan.delegaterId}", + ); + print( + "Approver Edit3 : ${plan.approver_status}", + ); + Navigator.pop( + context, + ); + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + plan.approver_status, + isViewMode: + false, + isApprover: + true, + ); + }, ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - print( - "Approver Edit : ${plan.approverId}", - ); - print( - "Approver Edit2 : ${plan.delegaterId}", - ); - Navigator.pop( - context, - ); - ApiService.viewPlanForApprover( - context, - plan.planId, - plan.approverId, - plan.delegaterId, - plan.approver_status, - isViewMode: - false, - isApprover: - true, - ); - }, - ), IconButton( icon: Icon( Icons @@ -1130,27 +1162,37 @@ class _ApprovalListState extends State { ); }, ), - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, + if (plan.statusValue == + "Pending Approval" || + plan.statusValue == + "Partially Approved") + IconButton( + icon: Image.asset( + 'assets/images/IconsImg/edit.png', + width: 20, + height: 15, + ), + tooltip: + 'Edit The Trip Details', + onPressed: () { + Navigator.pop( + context, + ); + print( + "Approver Edit3 : ${plan.approver_status}", + ); + + ApiService.viewPlanForApprover( + context, + plan.planId, + plan.approverId, + plan.delegaterId, + plan.approver_status, + isViewMode: false, + isApprover: true, + ); + }, ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop(context); - ApiService.viewPlanForApprover( - context, - plan.planId, - plan.approverId, - plan.delegaterId, - plan.approver_status, - isViewMode: false, - isApprover: true, - ); - }, - ), IconButton( icon: Icon( Icons.cancel_rounded, diff --git a/lib/Screens/forex/forexDetails.dart b/lib/Screens/forex/forexDetails.dart index 78002e5..bed6d4f 100644 --- a/lib/Screens/forex/forexDetails.dart +++ b/lib/Screens/forex/forexDetails.dart @@ -127,7 +127,6 @@ class ForexDataState extends State { selectedCountryName = data['country_name']; // For dropdown label or display selectedCurrency = data['currency']; // Optional if used elsewhere - controllers['currency']?.text = data['currency'] ?? ''; controllers['cash']?.text = data['cash_percentage'] ?? ''; controllers['card']?.text = data['card_percentage'] ?? ''; @@ -336,6 +335,16 @@ class ForexDataState extends State { selectedCountry ??= null; + void setCurrencyFromSelectedCountry(String selectedCountryCode) { + final matchedCountry = countryList.firstWhere( + (item) => item['country_code'] == selectedCountryCode, + orElse: () => {}, + ); + + final currency = matchedCountry['currency'] ?? ''; + controllers["currency"]?.text = currency; + } + return AlertDialog( backgroundColor: Colors.white, contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30), @@ -426,6 +435,7 @@ class ForexDataState extends State { .firstWhere((entry) => entry.value == newValue) .key; selectedCountryName = newValue; + setCurrencyFromSelectedCountry(selectedCountry!); }); }, ), @@ -457,6 +467,7 @@ class ForexDataState extends State { isFocused: false, isDesktop: widget.isDesktop, color: Colors.transparent, + // width: isDesktop // ? MediaQuery.of(context).size.width * 0.330 // : MediaQuery.of(context).size.width * 0.66, @@ -464,9 +475,13 @@ class ForexDataState extends State { height: 40, child: TextField( controller: controllers["currency"], - style: const TextStyle(fontSize: 12), + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.black, + ), decoration: const InputDecoration( labelText: "Currency", + enabled: false, labelStyle: TextStyle(fontSize: 11, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 9cc1058..b880436 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -94,8 +94,8 @@ class _ForexScreenState extends State { final parts = date.split('-'); if (parts.length == 3) { parsedDate = DateTime( - int.parse(parts[2]), // year - int.parse(parts[1]), // month + int.parse(parts[2]), // year + int.parse(parts[1]), // month int.parse(parts[0]), // day ); } else { @@ -356,6 +356,9 @@ class _ForexScreenState extends State { print("card - $card"); print("cash - $cash"); + _validateCardAmount(card); + _validateCashAmount(cash); + if (!isValidForexData(data)) { // && errorMessages.isNotEmpty) { print("Validation Failed: Required fields are missing."); @@ -411,7 +414,6 @@ class _ForexScreenState extends State { textControllers["_forexStartDate"]?.addListener(_onFieldChanged); textControllers["_forexEndDate"]?.addListener(_onFieldChanged); - flightFirstTripDateNotifier = ValueNotifier(null); flightLastTripDateNotifier = ValueNotifier(null); @@ -428,26 +430,62 @@ class _ForexScreenState extends State { // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); - final parsedDate = DateFormat( - "dd-MM-yyyy", - ).parse(flightFirstTripDateNotifier.value ?? ''); - if (parsedDate != null) { - textControllers["_forexStartDate"]?.text = DateFormat( - 'dd-MM-yyyy', - ).format(parsedDate); + // ----------------------------------- + + // final parsedDate = DateFormat( + // "dd-MM-yyyy", + // ).parse(flightFirstTripDateNotifier.value ?? ''); + // if (parsedDate != null) { + // textControllers["_forexStartDate"]?.text = DateFormat( + // 'dd-MM-yyyy', + // ).format(parsedDate); + // } + final flightDateStr = flightFirstTripDateNotifier.value; + + if (flightDateStr != null && flightDateStr.trim().isNotEmpty) { + try { + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightDateStr.trim()); + textControllers["_forexStartDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedDate); + } catch (e) { + print("Error parsing flightFirstTripDate: $e"); + // Optionally show an error message to the user or log it + } + } else { + print("flightFirstTripDateNotifier is null or empty"); } - final parsedEndDate = DateFormat( - "dd-MM-yyyy", - ).parse(flightLastTripDateNotifier.value ?? ''); - if (parsedEndDate != null) { - textControllers["_forexEndDate"]?.text = DateFormat( - 'dd-MM-yyyy', - ).format(parsedEndDate); + final flightDateEnd = flightLastTripDateNotifier.value; + + if (flightDateEnd != null && flightDateEnd.trim().isNotEmpty) { + try { + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightDateEnd.trim()); + textControllers["_forexEndDate"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedDate); + } catch (e) { + print("Error parsing flightEndTripDate: $e"); + // Optionally show an error message to the user or log it + } + } else { + print("flightEndTripDateNotifier is null or empty"); } + // final parsedEndDate = DateFormat( + // "dd-MM-yyyy", + // ).parse(flightLastTripDateNotifier.value ?? ''); + // if (parsedEndDate != null) { + // textControllers["_forexEndDate"]?.text = DateFormat( + // 'dd-MM-yyyy', + // ).format(parsedEndDate); + // } + handleUpdatedField(); - }); } @@ -468,16 +506,22 @@ class _ForexScreenState extends State { print("UPDATAED SELECTION"); textControllers["_forexStartDate"] = initController("start_date"); - textControllers["_forexStartDate"]?.addListener(_onFieldChanged); // Reattach + textControllers["_forexStartDate"]?.addListener( + _onFieldChanged, + ); // Reattach textControllers["_forexEndDate"] = initController("end_date"); - textControllers["_forexEndDate"]?.addListener(_onFieldChanged); // Reattach + textControllers["_forexEndDate"]?.addListener( + _onFieldChanged, + ); // Reattach 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["_deliveryLocation"] = initController( + "delivery_location", + ); textControllers["_comments"] = initController("comments"); // Set dropdown values @@ -571,8 +615,8 @@ class _ForexScreenState extends State { // final startDate = DateFormat('dd/mm/yyyy').parse(startDateString); // final endDate = DateFormat('dd/mm/yyyy').parse(endDateString); - final startDate = DateFormat('dd-MM-yyyy').parse(startDateString.trim()) ; - final endDate = DateFormat('dd-MM-yyyy').parse(endDateString.trim()) ; + final startDate = DateFormat('dd-MM-yyyy').parse(startDateString.trim()); + final endDate = DateFormat('dd-MM-yyyy').parse(endDateString.trim()); print("Calculate 4"); @@ -1162,7 +1206,8 @@ class _ForexScreenState extends State { errorMessages["end_date"]!, style: const TextStyle(color: Colors.red, fontSize: 12), maxLines: 2, // Allow it to wrap onto two lines - overflow: TextOverflow.ellipsis, // Add ellipsis if it still overflows + overflow: + TextOverflow.ellipsis, // Add ellipsis if it still overflows ), ], ], @@ -2129,7 +2174,7 @@ class _ForexScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Delivery Location", + "Delivery Location *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -2149,6 +2194,9 @@ class _ForexScreenState extends State { child: TextField( focusNode: focusNodes["_deliveryLocation"], controller: textControllers["_deliveryLocation"], + onChanged: (value) { + errorMessages.remove("delivery_location"); + }, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Location", @@ -2194,10 +2242,10 @@ class _ForexScreenState extends State { onChanged: (bool? value) { setState(() { isChecked = value!; - if (isChecked) { - textControllers["_cardNumber"] - ?.clear(); // Clear the value when isChecked is true - } + // if (isChecked) { + // textControllers["_cardNumber"] + // ?.clear(); // Clear the value when isChecked is true + // } }); }, ), diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index f46fc26..3784e7a 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -45,10 +45,13 @@ class _InsuranceScreenState extends State { late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _startDateController = TextEditingController(); + late TextEditingController _dateOfBirthController = TextEditingController(); late TextEditingController _endDateController = TextEditingController(); late TextEditingController _insuranceCommentsController = TextEditingController(); late TextEditingController _nomineeController = TextEditingController(); + late TextEditingController _nomineerelationController = + TextEditingController(); bool _isHotelNameFocused = false; bool _startDateFocus = false; @@ -65,9 +68,12 @@ class _InsuranceScreenState extends State { Map data = { "type_of_insurance": selectedInsuranceType, "start_date": _startDateController.text, + "end_date": _endDateController.text, "comments": _insuranceCommentsController.text, "nominee_name": _nomineeController.text, + "nominee_dob": _dateOfBirthController.text, + "nominee_relationship": _nomineerelationController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; @@ -125,6 +131,13 @@ class _InsuranceScreenState extends State { _endDateController = TextEditingController( text: widget.selectedItem?['end_date'] ?? "", ); + + _dateOfBirthController = TextEditingController( + text: widget.selectedItem?['nominee_dob'] ?? "", + ); + _nomineerelationController = TextEditingController( + text: widget.selectedItem?['nominee_relationship'] ?? "", + ); _nomineeController = TextEditingController( text: widget.selectedItem?['nominee_name'] ?? "", ); @@ -147,15 +160,24 @@ class _InsuranceScreenState extends State { // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); - final parsedDate = DateFormat("dd-MM-yyyy").parse(flightFirstTripDateNotifier.value ?? ''); - if (parsedDate != null) { _startDateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); } + final parsedDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightFirstTripDateNotifier.value ?? ''); + if (parsedDate != null) { + _startDateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); + } - final parsedEndDate = DateFormat("dd-MM-yyyy").parse(flightLastTripDateNotifier.value ?? ''); - if (parsedEndDate != null) { _endDateController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate); } + final parsedEndDate = DateFormat( + "dd-MM-yyyy", + ).parse(flightLastTripDateNotifier.value ?? ''); + if (parsedEndDate != null) { + _endDateController.text = DateFormat( + 'dd-MM-yyyy', + ).format(parsedEndDate); + } print("parsedDate $parsedDate"); print("parsedEndDate $parsedEndDate"); - }); } @@ -203,6 +225,9 @@ class _InsuranceScreenState extends State { "type_of_insurance", "start_date", "end_date", + "nominee_name", + "nominee_dob", + "nominee_relationship", ]; // Check validation for each field @@ -311,6 +336,7 @@ class _InsuranceScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop), _buildSecondRow(isDesktop), + _buildSecondSubRow(isDesktop), ]; return [ @@ -752,7 +778,7 @@ class _InsuranceScreenState extends State { child: DropdownSearch>( items: purposeList.cast>(), selectedItem: purposeList.firstWhere( - (item) => item['dropdown_key'] == selectedInsuranceType, + (item) => item['dropdown_key'] == selectedInsuranceType, orElse: () => {}, ), itemAsString: (item) => item['dropdown_value'] ?? '', @@ -779,7 +805,10 @@ class _InsuranceScreenState extends State { dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 10,vertical: 10,), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), ), ), dropdownBuilder: (context, selectedItem) { @@ -801,58 +830,59 @@ class _InsuranceScreenState extends State { ); }, onChanged: - purposeList.isNotEmpty - ? (Map? newValue) { - setState(() { - selectedInsuranceType= newValue?['dropdown_key']; - print("selected Insurance Type: ${selectedInsuranceType}"); - }); - } - : null, - ), - ), - ), - - ], - ), - - if (isDesktop) Spacer() else SizedBox(height: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Nominee", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: _nomineeFocus, - isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, - // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, - child: SizedBox( - height: 40, - child: TextField( - focusNode: _nomineeFocusNode, - controller: _nomineeController, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - labelText: "Nominee", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), - ), + purposeList.isNotEmpty + ? (Map? newValue) { + setState(() { + selectedInsuranceType = newValue?['dropdown_key']; + print( + "selected Insurance Type: ${selectedInsuranceType}", + ); + }); + } + : null, ), ), ), ], ), + + // if (isDesktop) Spacer() else SizedBox(height: 8), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Nominee", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox(height: 5), + // CustomTextFieldWrapper( + // isFocused: _nomineeFocus, + // isDesktop: isDesktop, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, + // child: SizedBox( + // height: 40, + // child: TextField( + // focusNode: _nomineeFocusNode, + // controller: _nomineeController, + // style: TextStyle(fontSize: 12), + // decoration: InputDecoration( + // labelText: "Nominee", + // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + // floatingLabelBehavior: FloatingLabelBehavior.never, + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(vertical: 16), + // // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), + // ), + // ), + // ), + // ), + // ], + // ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -869,7 +899,7 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _startDateFocus, isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, child: SizedBox( height: 40, child: GestureDetector( @@ -928,7 +958,7 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _endDateFocus, isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, child: SizedBox( height: 40, child: GestureDetector( @@ -982,7 +1012,8 @@ class _InsuranceScreenState extends State { errorMessages["end_date"]!, style: const TextStyle(color: Colors.red, fontSize: 12), maxLines: 2, // Allow it to wrap onto two lines - overflow: TextOverflow.ellipsis, // Add ellipsis if it still overflows + overflow: + TextOverflow.ellipsis, // Add ellipsis if it still overflows ), ], ], @@ -991,6 +1022,229 @@ class _InsuranceScreenState extends State { ]; } + List _buildSecondSubRow(bool isDesktop) { + List purposeList = + widget.apiData?['insurance_type_of_insurance'] ?? []; + // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; + + List> dropdownItems = + purposeList + .map( + (item) => DropdownMenuItem( + value: item['dropdown_key'], + 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 + selectedInsuranceType ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; + + // ---------------------------------------------------------------- + + DateTime? _selectedCheckDOBDate; + + Future _selectCheckDOBOutDate(BuildContext context) async { + DateTime now = DateTime.now(); + DateTime today = DateTime(now.year, now.month, now.day); + + // Parse date from notifier if available, else use today + DateTime initialDate; + + initialDate = today; + + final pickedDate = await showDatePicker( + context: context, + initialDate: initialDate, + firstDate: DateTime(1900), + lastDate: today, + initialEntryMode: DatePickerEntryMode.calendarOnly, + ); + + // DateTime? pickedDate = await showDatePicker( + // context: context, + // initialDate: _selectedCheckOutDate != null && + // _selectedCheckOutDate!.isAfter(today) + // ? _selectedCheckOutDate! + // : today, + // firstDate: today, + // lastDate: DateTime(2100), + // ); + + if (pickedDate != null && pickedDate != _selectedCheckDOBDate) { + setState(() { + _selectedCheckDOBDate = pickedDate; + _dateOfBirthController.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); + }); + } + } + + return [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Nominee*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: _nomineeFocus, + isDesktop: isDesktop, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, + child: SizedBox( + height: 40, + child: TextField( + focusNode: _nomineeFocusNode, + controller: _nomineeController, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Nominee", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), + ), + ), + ), + ), + if (errorMessages["nominee_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Date of Birth*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: _startDateFocus, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, + child: SizedBox( + height: 40, + child: GestureDetector( + onTap: () async { + await _selectCheckDOBOutDate(context); + // await _selectDateOfBirth(context); + if (_dateOfBirthController.text.isNotEmpty) { + setState(() { + errorMessages.remove( + "nominee_dob", + ); // Removes the key completely + }); + } + }, + child: AbsorbPointer( + child: TextField( + focusNode: _startDateFocusNode, + controller: _dateOfBirthController, + readOnly: true, + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Select ", + 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["nominee_dob"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Relationship*", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74), + ), + ), + SizedBox(height: 5), + + // + CustomTextFieldWrapper( + isFocused: false, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, + child: SizedBox( + height: 40, + child: TextField( + // focusNode: _nomineeFocusNode, + controller: _nomineerelationController, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Nominee Relationship", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), + ), + ), + ), + ), + if (errorMessages["nominee_relationship"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), + ], + ], + ), + if (isDesktop) Spacer() else SizedBox(height: 8), + ]; + } + List _buildThirdRow(bool isDesktop) { return [ Column( diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index 23cce10..54919a5 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -51,10 +51,15 @@ class _FlightListWidgetState extends State { Map tempCountryMap = {}; for (var country in result) { - String city = country['City'] ?? ''; - String airport = country['Airport'] ?? ''; - String displayName = '${country['City']} - ${country['Airport']}'; - // String displayName = '${country['City']} | ${country['Airport']}'; + // String city = country['City'] ?? ''; + // String airport = country['Airport'] ?? ''; + // String displayName = '${country['City']} - ${country['Airport']}'; + // // String displayName = '${country['City']} | ${country['Airport']}'; + + final code = country['Code'] ?? ''; + final city = country['City'] ?? ''; + final airport = country['Airport'] ?? ''; + final displayName = '$city ($code)\n$airport'; tempCountryMap[country['Code']] = displayName; } @@ -346,7 +351,7 @@ class _FlightListWidgetState extends State { ), ), Expanded( - flex: 4, + flex: 5, child: Text( "Sector", style: GoogleFonts.poppins(fontSize: 11), @@ -374,13 +379,38 @@ class _FlightListWidgetState extends State { if (isDesktop) if (item["trips"] != null && item["trips"].isNotEmpty) ...item["trips"].map((trip) { + print("werewrewrTrip - $trip"); + String fromPlaceCountry = countryMap[trip["from_place"]?.toString()] ?? "Unknown Country"; + + final parts = fromPlaceCountry.split('\n'); + final cityAndCode = parts[0]; + final fromairport = parts.length > 1 ? parts[1] : ''; + + // Extract city and code from "City (CODE)" + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); + final fromcity = cityMatch?.group(1) ?? ''; + final fromcode = cityMatch?.group(2) ?? ''; + String toPlaceCountry = countryMap[trip["to_place"]?.toString()] ?? "Unknown Country"; + final toparts = toPlaceCountry.split('\n'); + final tocityAndCode = toparts[0]; + final toairport = toparts.length > 1 ? toparts[1] : ''; + + // Extract city and code from "City (CODE)" + final tocityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(tocityAndCode); + final tocity = tocityMatch?.group(1) ?? ''; + final tocode = tocityMatch?.group(2) ?? ''; + return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( @@ -398,36 +428,74 @@ class _FlightListWidgetState extends State { ), Expanded( flex: 4, - child: Column( + child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "$fromPlaceCountry", - // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w700, + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "$fromcity ($fromcode)", + // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + Text( + "$fromairport", + // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + style: GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + ], ), ), - Padding( - padding: const EdgeInsets.only(left: 10), - child: Image.asset( - 'assets/images/IconsImg/alternate.png', - width: 20, - height: 20, + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: Image.asset( + 'assets/images/IconsImg/Frame.png', + width: 20, + height: 20, + ), ), ), - Text( - "$toPlaceCountry", - // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w700, + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "$tocity ($tocode)", + // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + Text( + "$toairport", + // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + style: GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + ], ), ), ], ), ), + Spacer(), Expanded( flex: 2, child: Text( @@ -478,6 +546,7 @@ class _FlightListWidgetState extends State { "Sector", "${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}", ), + _buildKeyValueRow( "Date", formatDate(trip["date"] ?? ""), diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index bf3913f..3379e45 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -54,7 +54,8 @@ class _TrainListWidgetState extends State { return AlertDialog( title: const Text('Select Trip Type'), content: const Text( - 'Please select a trip type before adding a Train.'), + 'Please select a trip type before adding a Train.', + ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), @@ -109,21 +110,24 @@ class _TrainListWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: MouseRegion( - cursor: widget.isViewMode - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, + cursor: + widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, child: GestureDetector( - onTap: widget.isViewMode - ? null - : () { - print("New data"); - checkClass(); - }, + onTap: + widget.isViewMode + ? null + : () { + print("New data"); + checkClass(); + }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely @@ -137,7 +141,7 @@ class _TrainListWidgetState extends State { Icons.add_circle_sharp, size: 30, color: Color(0xFF114D8B), - ) + ), ], ), ), @@ -193,9 +197,9 @@ class _TrainListWidgetState extends State { ], ), const SizedBox(height: 16), - // Scroll behavior based on device - _buildData(context, isDesktop) + // Scroll behavior based on device + _buildData(context, isDesktop), ], ), ), @@ -268,8 +272,11 @@ class _TrainListWidgetState extends State { 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)); + 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"); @@ -356,109 +363,188 @@ class _TrainListWidgetState extends State { ), ], ), - Divider( - color: Colors.blueGrey.shade50, - ), + Divider(color: Colors.blueGrey.shade50), SizedBox(height: 4), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - " Class", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - " Planned Trips", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - " Date", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - Expanded( - flex: 2, - child: Text( - " Comments", - style: GoogleFonts.poppins( - fontSize: 11, - ), - )), - ], - ) + children: [ + Expanded( + flex: 2, + child: Text( + " Class", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 4, + child: Text( + " Planned Trips", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 2, + child: Text( + " Date", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + Expanded( + flex: 2, + child: Text( + " Comments", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + ], + ) : SizedBox.shrink(), SizedBox(height: 4), isDesktop ? Row( - children: [ - Expanded( - flex: 2, - child: Text( - "${getRequestForTrainClass(item["class"]!.toString())} ", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - )), - SizedBox(width: 10), - Expanded( - flex: 2, - child: Text( - "$fromPlaceCountry - $toPlaceCountry", - - // "${item["from_station"]!} - ${(item["to_station"])}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - )), - SizedBox(width: 10), - Expanded( - flex: 2, - child: Text( - "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + children: [ + Expanded( + flex: 2, + child: Text( + "${getRequestForTrainClass(item["class"]!.toString())} ", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, ), ), - SizedBox(width: 10), - Expanded( - flex: 2, - child: _buildComments( - "Comments:", item["comments"] ?? "N/A"), + ), + SizedBox(width: 10), + Expanded( + flex: 3, + + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Expanded( + // flex: 1, + // child: Text( + // "$fromPlaceCountry", + // // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w700, + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Padding( + // padding: const EdgeInsets.only(left: 10), + // child: Image.asset( + // 'assets/images/IconsImg/Frame.png', + // width: 20, + // height: 20, + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Text( + // "$toPlaceCountry", + // // "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}", + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w700, + // ), + // ), + // ), + // ], + // ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "$fromPlaceCountry ", + + // "${item["from_station"]!} - ${(item["to_station"])}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: Image.asset( + 'assets/images/IconsImg/Frame.png', + width: 20, + height: 20, + ), + ), + ), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "$toPlaceCountry", + + // "$fromPlaceCountry - $toPlaceCountry", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ], ), - ], - ) + ), + Spacer(), + // SizedBox(width: 10), + Expanded( + flex: 2, + child: Text( + "${formatDate(item["date"]!)} ${formatTime(item["time"]!)}", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox(width: 10), + Expanded( + flex: 2, + child: _buildComments( + "Comments:", + item["comments"] ?? "N/A", + ), + ), + ], + ) : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildRow( - "Class:", - getRequestForTrainClass( - item["class"]!.toString())), - _buildRow("From:", item["from_station"]!), - _buildRow("To:", item["to_station"]!), - _buildDateTimeRow( - "Date:", - formatDate(item["date"]!), - formatTime(item["time"]!), - ), - _buildRow("Comments:", item["comments"] ?? "N/A"), - ], - ) + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRow( + "Class:", + getRequestForTrainClass(item["class"]!.toString()), + ), + _buildRow("From:", item["from_station"]!), + _buildRow("To:", item["to_station"]!), + _buildDateTimeRow( + "Date:", + formatDate(item["date"]!), + formatTime(item["time"]!), + ), + _buildRow("Comments:", item["comments"] ?? "N/A"), + ], + ), ], ), ), @@ -487,31 +573,37 @@ class _TrainListWidgetState extends State { 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) + "...", + 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: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - fontFamily: "Archivo"), - overflow: TextOverflow.ellipsis), - ) - : Text( - value, - style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: "Archivo", + ), + overflow: TextOverflow.ellipsis, + ), + ) + : Text( + value, + style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, - fontFamily: "Archivo"), - ), + fontFamily: "Archivo", + ), + ), ), ], ); @@ -538,36 +630,30 @@ class _TrainListWidgetState extends State { children: [ Text( title, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - ), + 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, - ), - ), + 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)), ), ], ); @@ -579,18 +665,10 @@ class _TrainListWidgetState extends State { children: [ Text( title, - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 12, - ), + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 12), ), SizedBox(width: 8), - Expanded( - child: Text( - "$date, $time", - style: TextStyle(fontSize: 12), - ), - ), + Expanded(child: Text("$date, $time", style: TextStyle(fontSize: 12))), ], ); } diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index cee265f..c809e65 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -282,6 +282,8 @@ class _CreatePlansState extends State { context.go('/listAllPlan'); } else if (currentUri == "/createPlan") { context.go('/listPlan'); + } else if (currentUri == "/approver/plans") { + context.go('/approvallist'); } else { isApprover ? context.go('/approvallist') : context.go('/listPlan'); } @@ -1173,17 +1175,18 @@ class CreateNewPlansState extends State { print("ViewAAA - $planData"); print("Call viewPlanForApprover"); - ApiService.viewPlanForApprover( - context, - data['plan_id'], - data['approverId'], - data['delegaterId'], - data['approver_status'], - - isViewMode: false, - isApprover: true, - ); - + // ApiService.viewPlanForApprover( + // context, + // data['plan_id'], + // data['approverId'], + // data['delegaterId'], + // data['approver_status'], + // + // isViewMode: false, + // isApprover: true, + // ); + // Close loading dialog (ONLY if still mounted) + if (mounted) Navigator.of(context, rootNavigator: true).pop(); context.go('/approvallist'); } else { print("$methodName failed. Status: ${response.statusCode}"); @@ -3172,6 +3175,11 @@ class CreateNewPlansState extends State { statusValue = "Approved"; }); callApproveAPI(selectedPlanId!, planData['user_id']); + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const SavingLoader(), + ); } else if (result is String) { // User rejected with remarks _remarksController.text = result; diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index d6d265f..9036a43 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -153,7 +153,9 @@ class _CreateUserFormDetialsState extends State { "address": controllers["address"]?.text, "gender": selectedGender, // "gender": personalDetailsKey.currentState?.selectedGender, + // "agent_supported_service_ids": selectedServiceIds, "agent_supported_service_ids": selectedServiceIds, + "postal_code": controllers["postalCode"]?.text, "country_code": selectedCountry, "employee_code": controllers["employeeCode"]?.text, @@ -720,7 +722,7 @@ class _CreateUserFormDetialsState extends State { if (start_Date != null && end_Date != null && end_Date.isBefore(start_Date)) { - print("End date cannot be earlier than start date"); + print("Invalid Date Range"); return; } } catch (e) { @@ -1420,6 +1422,7 @@ class _CreateUserFormDetialsState extends State { disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), @@ -1482,9 +1485,10 @@ class _CreateUserFormDetialsState extends State { disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.white, width: 2), + side: BorderSide(color: layoutColor, width: 2), ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 18), + + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), ), onPressed: handleBack, // onPressed: diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart index 6c58f9c..49b1fd2 100644 --- a/lib/Screens/userManagement/create_user/office_details.dart +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -457,7 +457,7 @@ class _OfficeDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["employee_code"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index f3e969e..d972825 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -314,8 +314,8 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 20), - _buildPassportDataRow1(widget.isDesktop), - SizedBox(height: 10), + // _buildPassportDataRow1(widget.isDesktop), + // SizedBox(height: 10), _buildFirstRow(widget.isDesktop), SizedBox(height: 10), _buildSecondRow(widget.isDesktop), @@ -451,7 +451,7 @@ class PersonalDetailsState extends State { } else { selectedServiceIds.add({"service_id": serviceId}); } - + print("selectedServiceIdsUser --- $selectedServiceIds"); // Call the parent's callback widget.onServiceIdsChanged!(selectedServiceIds); }); @@ -802,7 +802,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["first_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -848,7 +848,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["last_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1168,7 +1168,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["email"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1229,7 +1229,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["mobile_no"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1281,7 +1281,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["alternate_mobile_no"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1612,7 +1612,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["role_id"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1703,7 +1703,7 @@ class PersonalDetailsState extends State { SizedBox(height: 5), // Space before error message Text( widget.errorMessages["password"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart index 2d7a6d1..e93e168 100644 --- a/lib/Screens/userManagement/create_user/traveller_details.dart +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -195,7 +195,7 @@ class TravellerDetailsState extends State { end_Date != null && end_Date.isBefore(start_Date)) { setState(() { - errorMessages["date_of_expiry"] = "End date cannot be earlier"; + errorMessages["date_of_expiry"] = "Invalid Date Range"; }); print("End date cannot be earlier than start date"); } @@ -227,28 +227,44 @@ class TravellerDetailsState extends State { // } // } - // valid_from: 20-06-2025, valid_upto: 19-06-2025 - // DateTime? valid_from = data?['valid_from']; - // DateTime? valid_upto = data?['valid_upto']; - // - // if (valid_from != null && - // valid_upto != null && - // valid_from.toString().isNotEmpty && - // valid_upto.toString().isNotEmpty) { - // try { - // final format = DateFormat("dd-MM-yyyy"); - // final checkValidFrom = format.parse("$valid_from"); - // final checkValidUpto = format.parse("$valid_upto"); - // - // if (checkValidUpto.isBefore(checkValidFrom)) { - // // return "valid upto cannot be earlier than valid from"; - // } - // } catch (e) { - // // return "valid upto cannot be earlier than valid from"; - // - // // errorMessages["end_date"] = "Invalid date format"; - // } - // } + // ✅ Visa entries: Validate valid_from < valid_upto for each entry + for (var entry in visaEntries) { + final localId = entry["local_id"]; + final fromText = entry["controller_valid_from"]?.text.trim(); + final toText = entry["controller_valid_upto"]?.text.trim(); + + if (fromText != null && + toText != null && + fromText.isNotEmpty && + toText.isNotEmpty) { + try { + final fromDate = format.parseStrict(fromText); + final toDate = format.parseStrict(toText); + + if (toDate.isBefore(fromDate)) { + print("VisaErroer1"); + errorMessages["valid_upto${entry['local_id']}"] = + "Invalid date range"; + // errorMessages["valid_upto_$localId"] = + // "Valid Upto can't be before Valid From"; + } else { + print("VisaErroer2"); + // errorMessages.remove("valid_upto$localId"); + errorMessages.remove("valid_upto${entry['local_id']}"); + } + } catch (e) { + print("VisaErroer3"); + errorMessages["valid_upto${entry['local_id']}"] = + "Invalid date format"; + } + } + } + + if (errorMessages.isNotEmpty) { + print("Validation failed with errors: $errorMessages"); + setState(() {}); // Refresh UI to show errors + return false; + } print("travel validation end"); return true; } @@ -1379,7 +1395,7 @@ class TravellerDetailsState extends State { SizedBox(height: 5), // Space before error message Text( errorMessages["passport_number"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -1458,8 +1474,7 @@ class TravellerDetailsState extends State { // Revalidate expiry if (_selectedDateOfExpiry != null && _selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) { - errorMessages["date_of_expiry"] = - "Expiry date cannot be earlier than Issue date"; + errorMessages["date_of_expiry"] = "Invalid Date Range"; } else { errorMessages.remove("date_of_expiry"); } @@ -1585,8 +1600,7 @@ class TravellerDetailsState extends State { if (_selectedDateOfIssue != null && _selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) { - errorMessages["date_of_expiry"] = - "Expiry date cannot be earlier than Issue date"; + errorMessages["date_of_expiry"] = "Invalid Date Range"; } else { errorMessages.remove("date_of_expiry"); } @@ -1668,7 +1682,7 @@ class TravellerDetailsState extends State { SizedBox(height: 5), // Space before error message Text( errorMessages["date_of_expiry"]!, - style: const TextStyle(color: Colors.red, fontSize: 10), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 10), maxLines: 2, // Allow it to wrap onto two lines overflow: TextOverflow.ellipsis, // Add ellipsis if it still overflows @@ -2202,7 +2216,7 @@ class TravellerDetailsState extends State { vertical: 8, ), child: Text( - item['dropdown_value'] ?? '', + item['dropdown_value'] ?? 'Select', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, @@ -2214,7 +2228,7 @@ class TravellerDetailsState extends State { dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - hintText: "Select Seat", + hintText: "Select", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, @@ -2227,7 +2241,7 @@ class TravellerDetailsState extends State { dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( - "Select Seat", // fallback text + "Select ", // fallback text style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, @@ -2235,7 +2249,7 @@ class TravellerDetailsState extends State { ); } return Text( - selectedItem['dropdown_value'] ?? "Select Seat", + selectedItem['dropdown_value'] ?? "Select ", style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), ); }, @@ -2615,7 +2629,7 @@ class TravellerDetailsState extends State { vertical: 8, ), child: Text( - item['dropdown_value'] ?? '', + item['dropdown_value'] ?? 'Select Seat', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, @@ -2905,7 +2919,7 @@ class TravellerDetailsState extends State { SizedBox(height: 5), // Space before error message Text( errorMessages["forex_pre_paid_card_number"]!, - style: TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], ], @@ -3642,7 +3656,16 @@ class TravellerDetailsState extends State { .map((e) => _buildVisaDataRow1(e, isDesktop)), // _buildVisaDataRow1(isDesktop), - IconForVisa(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + " * I confirm my passport is valid for next 6 months", + style: GoogleFonts.poppins(fontSize: 10), + ), + IconForVisa(), + ], + ), ], ); } @@ -3663,6 +3686,7 @@ class TravellerDetailsState extends State { buildVisaValidFrom(entry), SizedBox(width: 15), buildVisaValidUpTo(entry), + SizedBox( height: 60, child: Center( @@ -3699,6 +3723,7 @@ class TravellerDetailsState extends State { buildVisaValidFrom(entry), SizedBox(height: 8), buildVisaValidUpTo(entry), + IconButton( icon: Icon( Icons.remove_circle_sharp, @@ -4165,6 +4190,9 @@ class TravellerDetailsState extends State { initialEntryMode: DatePickerEntryMode.calendarOnly, ); + print("CheckOut - $_selectedCheckOutDate"); + print("CheckIn - $_selectedCheckInDate"); + if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckInDate = pickedDate; @@ -4175,10 +4203,14 @@ class TravellerDetailsState extends State { // Revalidate expiry if (_selectedCheckOutDate != null && _selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) { - errorMessages["valid_upto"] = - "valid upto cannot be earlier than valid from"; + print("Entry - $entry"); + print("errorMessages - $errorMessages"); + errorMessages["valid_upto${entry['local_id']}"] = + "Invalid Date Range"; + // errorMessages["valid_upto"] = "Invalid Date Range"; } else { - errorMessages.remove("valid_upto"); + errorMessages.remove("valid_upto${entry['local_id']}"); + // errorMessages.remove("valid_upto"); } }); } @@ -4293,10 +4325,10 @@ class TravellerDetailsState extends State { if (_selectedCheckInDate != null && _selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) { - errorMessages["valid_upto"] = - "valid upto cannot be earlier than valid from"; + errorMessages["valid_upto${entry['local_id']}"] = + "Invalid Date Range"; } else { - errorMessages.remove("valid_upto"); + errorMessages.remove("valid_upto${entry['local_id']}"); } }); } @@ -4325,6 +4357,7 @@ class TravellerDetailsState extends State { height: 40, child: GestureDetector( onTap: () async { + errorMessages.remove("valid_upto${entry['local_id']}"); await _selectValidUpToDate(context); }, child: AbsorbPointer( @@ -4351,11 +4384,11 @@ class TravellerDetailsState extends State { ), ), ), - if (errorMessages["valid_upto"] != null) ...[ + if (errorMessages["valid_upto${entry['local_id']}"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - errorMessages["valid_upto"]!, - style: const TextStyle(color: Colors.red, fontSize: 10), + errorMessages["valid_upto${entry['local_id']}"]!, + style: GoogleFonts.poppins(color: Colors.red, fontSize: 10), maxLines: 2, // Allow it to wrap onto two lines overflow: TextOverflow.ellipsis, // Add ellipsis if it still overflows diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index ab9b547..9e10cdb 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -111,7 +111,7 @@ class _UserListScreenState extends State { Future> fetchUsers() async { orgId = await getOrgId(); - final String apiUrlData = '$apiUrl/api/users?org_id=$orgId'; + final String apiUrlData = '$apiUrl/api/users?org_id=$orgId&for=table_view'; final String? token = await getToken(); print("Fetch Users"); @@ -294,7 +294,13 @@ class _UserListScreenState extends State { // Refresh user list after update void refreshUserList() { setState(() { - futureUsers = fetchUsers(); // Re-fetch users after status update + futureUsers = fetchUsers(); + + futureUsers.then((users) { + setState(() { + allUsers = users; + }); + }); // Re-fetch users after status update // Wait for futurePlans to be fetched and update allPlans }); } @@ -724,25 +730,28 @@ class _UserListScreenState extends State { ), ), DataCell( - GestureDetector( - onTap: () { - handleToggleUserStatus( - user['user_id'], - user['is_active'], - user, - ); - }, - child: Text( - user['is_active'] == "1" - ? "Active" - : "Inactive", - style: TextStyle( - color: - user['is_active'] == "1" - ? Colors.green - : Colors.grey, - fontFamily: "Inter", - fontWeight: FontWeight.w400, + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + handleToggleUserStatus( + user['user_id'], + user['is_active'], + user, + ); + }, + child: Text( + user['is_active'] == "1" + ? "Active" + : "Inactive", + style: TextStyle( + color: + user['is_active'] == "1" + ? Colors.green + : Colors.grey, + fontFamily: "Inter", + fontWeight: FontWeight.w400, + ), ), ), ), @@ -1146,37 +1155,36 @@ class _UserListScreenState extends State { // ) // : buildMobileCardView(paginatedUser), // ), - Expanded( child: - isDesktop - ? (searchController.text.isNotEmpty && - filteredUsers.isEmpty - ? Center( - child: Text( - "No users found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredUsers.isEmpty - ? Center( - child: Text( - "No users found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : buildMobileCardView(paginatedUser)), + isDesktop + ? (searchController.text.isNotEmpty && + filteredUsers.isEmpty + ? Center( + child: Text( + "No users found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: table, + )) + : (searchController.text.isNotEmpty && + filteredUsers.isEmpty + ? Center( + child: Text( + "No users found", + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.grey, + ), + ), + ) + : buildMobileCardView(paginatedUser)), // child: isDesktop // ? SingleChildScrollView( // scrollDirection: Axis.vertical, diff --git a/lib/data/models/plan.dart b/lib/data/models/plan.dart index be2f706..882e330 100644 --- a/lib/data/models/plan.dart +++ b/lib/data/models/plan.dart @@ -15,6 +15,7 @@ class Plan { final String userName; final String travellerName; + final String approverName; final String statusValue; final String createdOn; final String? approverId; @@ -35,6 +36,7 @@ class Plan { required this.isBillable, required this.userName, required this.travellerName, + required this.approverName, required this.statusValue, required this.createdOn, this.approverId, @@ -44,24 +46,36 @@ class Plan { factory Plan.fromJson(Map json) { print('Parsing Plan from JSON: $json'); return Plan( - planId: json['plan_id'], - employeeCode: json['employee_code'], - tripTitle: json['trip_title'], - tripType: json['trip_type_value'], - approver_status: json['approver_status'], + planId: json['plan_id']!.toString(), + employeeCode: json['employee_code']!.toString(), + tripTitle: json['trip_title']!.toString(), + tripType: json['trip_type_value']!.toString(), + approver_status: + json["approver_status"] == null + ? '' + : json['approver_status']!.toString(), status: json['status'] == "0" ? "Inactive" : "Active", - costCenter: json['cost_center_value'] ?? '', - functionalDepartment: json['functional_department_value'] ?? '', - purposeOfTravel: json['purpose_of_travel_value'] ?? '', - soNumber: json['so_number'] ?? '', - description: json['description'] ?? '', - isBillable: json['is_billable_value'] ?? '', - userName: json["user_name"] ?? '', - travellerName: json["traveller_name"] ?? '', - statusValue: json["status_value"] ?? '', - createdOn: json["created_on"] ?? '', - approverId: json['approver_id'], - delegaterId: json['delegater_id'], + costCenter: json['cost_center_value']!.toString(), + functionalDepartment: json['functional_department_value']!.toString(), + purposeOfTravel: json['purpose_of_travel_value']!.toString(), + soNumber: json['so_number']!.toString(), + description: json['description']!.toString(), + isBillable: json['is_billable_value']!.toString(), + userName: json["user_name"]!.toString(), + travellerName: + json["traveller_name"] == null + ? '' + : json["traveller_name"]!.toString(), + approverName: + json["approver_name"] == null + ? '' + : json["approver_name"]!.toString(), + statusValue: json["status_value"]!.toString(), + createdOn: json["created_on"], + approverId: + json["approver_id"] == null ? '' : json['approver_id']!.toString(), + delegaterId: + json["delegater_id"] == null ? '' : json['delegater_id']!.toString(), ); } } diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index b6c515a..e247f48 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -618,8 +618,8 @@ class ApiService { BuildContext context, String planId, String? approverId, - String? approver_status, - String? delegaterId, { + String? delegaterId, + String? approver_status, { bool isViewMode = false, bool isApprover = true, }) async { diff --git a/lib/widgets/popup_listPlan_action.dart b/lib/widgets/popup_listPlan_action.dart index b839220..0d8f83e 100644 --- a/lib/widgets/popup_listPlan_action.dart +++ b/lib/widgets/popup_listPlan_action.dart @@ -21,7 +21,6 @@ class PlanPopupMenu extends StatelessWidget { required this.layoutColor, }) : super(key: key); - @override Widget build(BuildContext context) { return Align( @@ -30,91 +29,99 @@ class PlanPopupMenu extends StatelessWidget { color: Colors.white, padding: EdgeInsets.zero, offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 15, - ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4), - // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: Icon(Icons.remove_red_eye, size: 18), - onPressed: () { - Navigator.pop(context); - ApiService.viewPlan(context, plan.planId, - isViewMode: true); - }, - ), - - // IconButton( - // icon: Image.asset('assets/images/IconsImg/edit.png', - // width: 20, height: 15), - // onPressed: () { - // Navigator.pop(context); - // ApiService.viewPlan(context, plan.planId, - // isViewMode: false); - // }, - // ), - - IconButton( - icon: Icon(Icons.cancel_rounded, size: 18), - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon(Icons.download, - color: Color(0xFF114D8B), size: 18), - onPressed: () { - Navigator.pop(context); - apiService.getPdfDownload(plan.planId); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, + icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 15), + itemBuilder: + (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 2, vertical: 4), + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.remove_red_eye, size: 18), + onPressed: () { + Navigator.pop(context); + ApiService.viewPlan( + context, + plan.planId, + isViewMode: true, + ); + }, ), - onPressed: () { - showDialog( - context: context, - builder: (context) => CommentModalList( - // planId: plan.planId, - planId: plan.planId.toString(), - layoutColorForUser: layoutColor!, - role: "User"), - ); - }), - IconButton( - icon: const Icon( - Icons.comment, - color: Color(0xFF475569), - size: 11, + + // IconButton( + // icon: Image.asset('assets/images/IconsImg/edit.png', + // width: 20, height: 15), + // onPressed: () { + // Navigator.pop(context); + // ApiService.viewPlan(context, plan.planId, + // isViewMode: false); + // }, + // ), + IconButton( + icon: Icon(Icons.cancel_rounded, size: 18), + onPressed: () { + Navigator.pop(context); + deletePlan(plan.planId); + }, ), - onPressed: () { - showDialog( - context: context, - builder: (context) => CommentModal( - // planId: plan.planId, - planId: plan.planId.toString(), - layoutColorForUser: layoutColor!, - role: "Travel Agent"), - ); - }), - ], + IconButton( + icon: Icon( + Icons.download, + color: Color(0xFF114D8B), + size: 18, + ), + onPressed: () { + Navigator.pop(context); + apiService.getPdfDownload(plan.planId); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color(0xFF475569), + size: 11, + ), + onPressed: () { + showDialog( + context: context, + builder: + (context) => CommentModalList( + // planId: plan.planId, + planId: plan.planId.toString(), + layoutColorForUser: layoutColor!, + role: "User", + ), + ); + }, + ), + IconButton( + icon: const Icon( + Icons.comment, + color: Color(0xFF475569), + size: 11, + ), + onPressed: () { + showDialog( + context: context, + builder: + (context) => CommentModal( + // planId: plan.planId, + planId: plan.planId.toString(), + layoutColorForUser: layoutColor!, + role: "Travel Agent", + ), + ); + }, + ), + ], + ), + ), ), - ), - ), - ], + ], ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index 58aee17..a8f42c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,6 +101,8 @@ flutter: - assets/images/login/microsoft.png - assets/images/IconsImg/path.png - assets/images/IconsImg/alternate.png + - assets/images/IconsImg/ViceVersaArrow.png + - assets/images/IconsImg/Frame.png - assets/images/IconsImg/delete.png - assets/images/IconsImg/edit.png - assets/images/IconsImg/planPdf_icon.png