diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index 1a81a43..68d3815 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -509,7 +509,10 @@ class _VisaScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, - width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownSearch( @@ -564,6 +567,65 @@ class _VisaScreenState extends State { ], ], ), + + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Type of Visa", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: _tripTypeFocused, + isDesktop: isDesktop, + width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.34 + // : MediaQuery.of(context).size.width * 0.66, + child: SizedBox( + height: 40, + child: DropdownButtonFormField( + focusNode: _tripTypeFocusNode, // Assign the correct focus node + 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; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } + : null, + + items: dropdownItems, + ), + ), + ), + if (errorMessages["type_of_visa"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ), if (isDesktop) Spacer() else diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index bb0a6de..0c55db6 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -582,6 +582,7 @@ class _CreateUserFormDetialsState extends State { travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages; + print("TRAVEL DETAILS FROM CHILD: $travelDetailsData"); Map data = userDetials; @@ -654,7 +655,21 @@ class _CreateUserFormDetialsState extends State { void handleSubmit() async { print("USR Detail Submit"); // printFormData(); - travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; + + bool isValid = travellerDetailsKey.currentState?.boolValidation() ?? false; + + print("isValid- $isValid"); + if (!isValid) { + print("Validation failed. Please check the inputs."); + setState(() {}); + return; // ❌ STOP execution here if not valid + } + + travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; + + + + print("travelDetailsData - $travelDetailsData"); print("travelDetailsData - $travelDetailsData"); errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages; @@ -673,8 +688,7 @@ class _CreateUserFormDetialsState extends State { // Map data = userDetials; if (!isValidData(data) && - isValidDataTwo(data) && - errorMessagesTravel.isNotEmpty) { + isValidDataTwo(data) ) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); setState(() {}); diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart index 02f80f7..ac72ac4 100644 --- a/lib/Screens/userManagement/create_user/office_details.dart +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -391,7 +391,7 @@ class _OfficeDetailsState extends State { buildDelegationStartDateField(isDesktop), Spacer(), // Space after Last Name buildDelegationEndDateField(isDesktop), - SizedBox(width: 15), + SizedBox(width: 16), buildReset(isDesktop), ], ) @@ -1475,7 +1475,7 @@ class _OfficeDetailsState extends State { ), SizedBox(height: 5), CustomTextFieldUserWrapper( - width: isDesktop ? MediaQuery.of(context).size.width * 0.18 : null, + width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index 3472508..e4ae82b 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -639,7 +639,7 @@ class PersonalDetailsState extends State { children: [ if (!widget.apiselectedUser) ...[ buildPassword(), - SizedBox(width: 15), + SizedBox(width: MediaQuery.of(context).size.width*0.015), buildRole(), ] else ...[ // TextButton( @@ -736,9 +736,11 @@ class PersonalDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ buildAddress(), - SizedBox(width: 15), + Spacer(), + // SizedBox(width: 15), buildCountryField(), - SizedBox(width: 15), + Spacer(), + // SizedBox(width: 15), buildPostalCodeField(), ], ) diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart index 0eb20c1..a52d0c5 100644 --- a/lib/Screens/userManagement/create_user/traveller_details.dart +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -154,6 +154,31 @@ class TravellerDetailsState extends State { }); } + bool boolValidation() { + final passportText = controllers["passportNumber"]?.text ?? ""; + + if (_selectedTripType == "Indian") { + // Match exactly 8 alphanumeric characters + final isValid = RegExp(r'^[a-zA-Z0-9]{8}$').hasMatch(passportText); + + if (!isValid) { + setState(() { + errorMessages["passport_number"] = + "Enter 8 characters"; + }); + return false; + } else { + setState(() { + errorMessages.remove("passport_number"); + }); + } + } + + return true; + } + + + Future apiCheckDuplicate( String label, String field, @@ -708,7 +733,8 @@ class TravellerDetailsState extends State { Future loadCountryList() async { final newTripType = "2"; - final result = await apiService.fetchFlightsCountryList(newTripType); + final result = await apiService.fetchAirlineList(); + // final result = await apiService.fetchFlightsCountryList(newTripType); print("resultFlight: $result "); @@ -1212,6 +1238,7 @@ class TravellerDetailsState extends State { : (String? newValue) { setState(() { _selectedTripType = newValue; + controllers["passportNumber"]?.clear(); print("_selectedTripType - $_selectedTripType"); }); }, @@ -1254,20 +1281,6 @@ class TravellerDetailsState extends State { ], onChanged: (value) { _clearError("passport_number"); - final regex = RegExp(r'^[a-zA-Z0-9]{0,8}$'); - if (!regex.hasMatch(value)) { - setState(() { - if (_selectedTripType == "Indian") { - errorMessages["passport_number"] = - "Only 8 characters allowed (letters & numbers)"; - } - }); - return; - } else { - setState(() { - errorMessages.remove("passport_number"); - }); - } apiCheckDuplicate( "Passport Number", "passport_number", @@ -1438,23 +1451,42 @@ class TravellerDetailsState extends State { DateTime today = DateTime(now.year, now.month, now.day); // DateTime today = DateTime(now.year now.day, now.month,); - // Parse dateOfIssue safely - DateTime firstDate; + DateTime? validFromDate; + try { - firstDate = DateFormat( - "yyyy-MM-dd", - ).parse(controllers["dateOfIssue"]!.text); + String? fromDateText = controllers["dateOfIssue"]?.text.trim(); + print("dateOfIssue From Text: $fromDateText"); + if (fromDateText!.isNotEmpty) { + validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText); + print("dateOfIssue Valid From: $validFromDate"); + } } catch (e) { - firstDate = today; // fallback if parsing fails + print("Error parsing valid from date: $e"); } + // Use max(today, validFromDate) as firstDate + // DateTime firstDate = today; + DateTime firstDate = validFromDate ?? today; + + // Parse dateOfIssue safely + // DateTime firstDate; + // try { + // firstDate = DateFormat( + // "yyyy-MM-dd", + // ).parse(controllers["dateOfIssue"]!.text); + // } catch (e) { + // firstDate = today; // fallback if parsing fails + // } + DateTime? pickedDate = await showDatePicker( context: context, - initialDate: - _selectedDateOfExpiry != null && - _selectedDateOfExpiry!.isAfter(firstDate) - ? _selectedDateOfExpiry! - : today, + // initialDate: + // _selectedDateOfExpiry != null && + // _selectedDateOfExpiry!.isAfter(firstDate) + // ? _selectedDateOfExpiry! + // : today, + + initialDate:firstDate, firstDate: firstDate, // firstDate: DateTime(1900), lastDate: DateTime(2100), @@ -1857,6 +1889,7 @@ class TravellerDetailsState extends State { dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, + hintText: "Select Seat", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, @@ -1877,7 +1910,7 @@ class TravellerDetailsState extends State { ); } return Text( - selectedItem['dropdown_value'] ?? '', + selectedItem['dropdown_value'] ?? "Select Seat", style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), ); }, @@ -2027,7 +2060,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, @@ -2729,9 +2762,14 @@ class TravellerDetailsState extends State { countryList = apiAirlineCountryData!; print("TestcountryList2 - $countryList"); + // countryMap = { + // for (var country in countryList) + // country['Code'] as String: '${country['City']} - ${country['Airport']}', + // }; + countryMap = { for (var country in countryList) - country['Code'] as String: '${country['City']} - ${country['Airport']}', + country['airline'] as String: '${country['airline_name']}', }; countryCodes = countryMap.keys.toList(); @@ -2777,10 +2815,10 @@ class TravellerDetailsState extends State { ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( - width: - widget.isDesktop - ? MediaQuery.of(context).size.width * 0.17 - : null, + // width: + // widget.isDesktop + // ? MediaQuery.of(context).size.width * 0.17 + // : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( @@ -3825,21 +3863,45 @@ class TravellerDetailsState extends State { Future _selectValidUpToDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); + print("buildVisaValidUpTo "); + print("buildVisaValidUpTo - ${entry["controller_valid_from"].text}"); + // Parse date from notifier if available, else use today DateTime initialDate = today; // Use previously selected date if valid - if (_selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(today)) { - initialDate = _selectedCheckOutDate!; + // if (_selectedCheckOutDate != null && + // _selectedCheckOutDate!.isAfter(today)) { + // initialDate = _selectedCheckOutDate!; + // } + + DateTime? validFromDate; + + try { + String fromDateText = entry["controller_valid_from"].text.trim(); + print("Valid From Text: $fromDateText"); + if (fromDateText.isNotEmpty) { + validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText); + print("Parsed Valid From: $validFromDate"); + } + } catch (e) { + print("Error parsing valid from date: $e"); } + // Use max(today, validFromDate) as firstDate + // DateTime firstDate = today; + DateTime firstDate = validFromDate ?? today; + if (validFromDate != null && validFromDate.isAfter(today)) { + firstDate = validFromDate; + } final pickedDate = await showDatePicker( context: context, // initialDate: initialDate, // firstDate: initialDate, - firstDate: DateTime(1900), + // firstDate: DateTime(1900), + initialDate: firstDate, + firstDate: firstDate, lastDate: DateTime(2100), ); diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index c4097ff..ab9b547 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -1137,15 +1137,54 @@ class _UserListScreenState extends State { child: Column( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + // Expanded( + // child: + // isDesktop + // ? SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedUser), + // ), + Expanded( child: - isDesktop - ? SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, // <-- your existing table - ) - : 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, + // child: table, // <-- your existing table + // ) + // : buildMobileCardView(paginatedPlans), ), + PaginationControls( currentPage: currentPage, itemsPerPage: itemsPerPage, diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 1b1155f..272aaa5 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -102,6 +102,41 @@ class ApiService { throw Exception('Failed to load country list'); } } + Future> fetchAirlineList() async { + final String apiUrldata = '$apiUrl/api/getAirlineMaster'; + final token = await getToken(); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + try { + final data = json.decode(response.body); + print("Airline - $data"); + + if (!data.containsKey('data') || data['data'] is! List) { + throw Exception( + "Invalid response format: 'data' field is missing or not a List", + ); + } + + return data['data']; + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load country list'); + } + } Future> fetchUsers() async { String? ordId = await getOrgId(); diff --git a/pubspec.lock b/pubspec.lock index e4884cf..94a6c9d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.12.0" bcrypt: dependency: "direct main" description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.2" ffi: dependency: transitive description: @@ -465,18 +465,18 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: @@ -1014,10 +1014,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.3.1" vsc_quill_delta_to_html: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 24d8c13..cbf239d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: -# sdk: ^3.6.1 + # sdk: ^3.6.1 sdk: ^3.7.2 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -44,7 +44,7 @@ dependencies: responsive_builder: ^0.7.1 shared_preferences: ^2.5.2 easy_stepper: ^0.8.5+1 - intl: ^0.20.2 + intl: ^0.19.0 dropdown_search: ^5.0.6 file_picker: ^10.0.0 bcrypt: ^1.1.3 @@ -134,4 +134,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package + # see https://flutter.dev/to/font-from-packagej \ No newline at end of file