import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; // Import for Uint8List import 'package:bcrypt/bcrypt.dart'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:frontend/Screens/userManagement/create_user/personal_details.dart'; import 'package:frontend/Screens/userManagement/create_user/traveller_details.dart'; import 'package:frontend/Screens/userManagement/create_user/office_details.dart'; import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart' as http_parser; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http_parser/http_parser.dart'; import '../../../config/apiUrl.dart'; import '../../../routes/custom_appBar.dart'; import '../../../routes/custom_drawer.dart'; import '../../../services/apiService.dart'; import '../../../widgets/custom_breadcrumb_navigation.dart'; import '../../../widgets/custom_text_field.dart'; import '../../../widgets/custom_text_forex.dart'; import '../../../widgets/custom_user_form.dart'; import '../../../widgets/saving_loader.dart'; import 'package:universal_html/html.dart' as html; class CreateUserFormDetials extends StatefulWidget { @override _CreateUserFormDetialsState createState() => _CreateUserFormDetialsState(); } class _CreateUserFormDetialsState extends State { final ApiService apiService = ApiService(); final GlobalKey personalDetailsKey = GlobalKey(); final GlobalKey travellerDetailsKey = GlobalKey(); final GlobalKey officeDetailsKey = GlobalKey(); // late List?> travelDetailsData; Map? travelDetailsData; Map? travelDetailsDataFromAPI; Map errorMessagesTravel = {}; late TabController _tabController; String? userId; String? orgId; String? roleId; String? userIdApi; bool isCheckingToken = false; bool isDisable = false; bool isValid = false; final FocusNode submitButtonFocusNode = FocusNode(); String? token; late bool isapiselectedUser = false; // Map to store controllers dynamically final Map controllers = {}; bool isViewMode = false; bool isEditProfile = false; // late final List? apiCountryData ; late List? apiCountryData; late List? apiCostData; late List? apiRoleData; late List? apiUserData; Map? apiselectedUser; List> selectedServiceIds = []; late List userList; late Future> futureUsers; List? resolvedUserData; late List userIdsApi; late Map userMap; Map errorMessages = {}; String? selectedTab; String? selectedCountry; String? selectedGender; // String? selectedGender = personalDetailsKey.currentState?.selectedGender; String? selectedUserType; String? selectedRole; String? selectedLevel; String? selectedDepartment; String? selectedFirstApprover; String? selectedSecondApprover; String? selectedThirdApprover; String? selectedSubstituteApprover; String? selectedExceptionalApprover; String? selectedFileNames; Uint8List? passportDocumentBytes; String? passportFileUrlFromApi; String? base64PDF; html.File? passportFile; List dataHeader = [ "Fname", "Lname", "email", "password", "dob", "gender", "mobileNumber", "alternateMobile", "address", "postalCode", "country", // "passportNumber", // "placeOfIssue", // "passportDoc", "userType", "roleId", "deptId", "levelId", "firstApproval", "secondApproval", "thirdApproval", "employeeCode", "delegationStartDate", "delegationEndDate", // "dateOfIssue", // "dateOfExpiry", "changePassword", ]; Color? layoutColor; Color? bodyColor; bool setSelectesUserType = false; Map get userDetials { Map data = { "first_name": controllers["Fname"]?.text, "last_name": controllers["Lname"]?.text, "email": controllers["email"]?.text, "password": controllers["password"]?.text, "mobile_no": controllers["mobileNumber"]?.text, "alternate_mobile_no": controllers["alternateMobile"]?.text, "date_of_birth": controllers["dob"]?.text, "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, "delegation_start_date": controllers["delegationStartDate"]?.text, "delegation_end_date": controllers["delegationEndDate"]?.text, "user_type": selectedUserType, "role_id": selectedRole, "department_id": selectedDepartment, "group_id": selectedLevel, "first_approver": selectedFirstApprover, "second_approver": selectedSecondApprover, "third_approver": selectedThirdApprover, "fourth_approver": selectedExceptionalApprover, // "exceptional_approver": selectedExceptionalApprover, "delegated_to_user_id": selectedSubstituteApprover, // "passport_number": controllers["passportNumber"]?.text, // "place_of_issue": controllers["placeOfIssue"]?.text, // "passport_document": passportFile, // "date_of_issue": controllers["dateOfIssue"]?.text, // "date_of_expiry": controllers["dateOfExpiry"]?.text, "created_by": userId, "is_active": "1", "org_id": orgId, "travel_details": travelDetailsData ?? {}, // "passport_fileData": passportFile, }; return data; } void handleUserTypeChange(bool isTravelAgent) { print("handleUserTypeChange- $isTravelAgent"); setState(() { setSelectesUserType = isTravelAgent; }); } Future updateData() async { // Ensure apiselectedUser is not null before printing if (apiselectedUser != null) { print("API Selected User Has Data - $widget.apiselectedUser"); setState(() { // ✅ Wrap in setState to update the UI if (apiselectedUser != null && apiselectedUser!.isNotEmpty) { isapiselectedUser = true; print("API Selected User Has Data - $apiselectedUser"); } userIdApi = apiselectedUser?["user_id"] ?? ""; controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? ""; controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? ""; controllers["email"]?.text = apiselectedUser?["email"] ?? ""; controllers["password"]?.text = ""; // controllers["password"]?.text = apiselectedUser?["password"] ?? ""; controllers["mobileNumber"]?.text = apiselectedUser?["mobile_no"] ?? ""; controllers["alternateMobile"]?.text = apiselectedUser?["alternate_mobile_no"] ?? ""; controllers["dob"]?.text = apiselectedUser?["date_of_birth"] ?? ""; controllers["address"]?.text = apiselectedUser?["address"] ?? ""; controllers["postalCode"]?.text = apiselectedUser?["postal_code"] ?? ""; controllers["employeeCode"]?.text = apiselectedUser?["employee_code"] ?? ""; controllers["delegationStartDate"]?.text = apiselectedUser?["delegation_start_date"] ?? ""; controllers["delegationEndDate"]?.text = apiselectedUser?["delegation_end_date"] ?? ""; controllers["passportNumber"]?.text = apiselectedUser?["passport_number"] ?? ""; controllers["placeOfIssue"]?.text = apiselectedUser?["place_of_issue"] ?? ""; if (apiselectedUser?["date_of_issue"] != null) { controllers["dateOfIssue"]?.text = apiselectedUser?["date_of_issue"] ?? ""; } if (apiselectedUser?["date_of_expiry"] != null) { controllers["dateOfExpiry"]?.text = apiselectedUser?["date_of_expiry"] ?? ""; } if (apiselectedUser?["country_code"] != null) { selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; } if (apiselectedUser?["gender"] != null) { selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; } if (apiselectedUser?["user_type"] != null) { selectedUserType = apiselectedUser?["user_type"]?.toString().trim() ?? ""; } if (apiselectedUser?["role_id"] != null) { selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; } if (apiselectedUser?["group_id"] != null) { selectedLevel = apiselectedUser?["group_id"]?.toString().trim() ?? ""; } // selectedDepartment = // apiselectedUser?["department_id"]?.toString().trim() ?? ""; if (apiselectedUser?["department_id"] != null) { selectedDepartment = apiselectedUser!["department_id"].toString(); } // print( // "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} "); if (apiselectedUser?["level_id"] != null) { selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; } if (apiselectedUser?["first_approver"] != null) { selectedFirstApprover = apiselectedUser?["first_approver"]?.toString() ?? ""; } if (apiselectedUser?["second_approver"] != null) { selectedSecondApprover = apiselectedUser?["second_approver"]?.toString() ?? ""; } if (apiselectedUser?["third_approver"] != null) { selectedThirdApprover = apiselectedUser?["third_approver"]?.toString() ?? ""; } // if (apiselectedUser?["exceptional_approver"] != null) { // selectedExceptionalApprover = // apiselectedUser?["exceptional_approver"]?.toString() ?? ""; // } if (apiselectedUser?["fourth_approver"] != null) { selectedExceptionalApprover = apiselectedUser?["fourth_approver"]?.toString() ?? ""; } if (apiselectedUser?["delegated_to_user_id"] != null) { print( "UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}", ); selectedSubstituteApprover = apiselectedUser?["delegated_to_user_id"]?.toString() ?? ""; print("UPDADele1- $selectedSubstituteApprover"); } try { final raw = apiselectedUser!["agent_supported_service_ids"]; // Fix the invalid JSON (dangerous if the format changes) final fixedJson = raw.replaceAllMapped( RegExp(r'(\w+):'), // matches `service_id:` (match) => '"${match.group(1)}":', ); List decodedList = jsonDecode(fixedJson); selectedServiceIds = decodedList.map>((item) { final map = Map.from(item); return {"service_id": map['service_id'].toString()}; }).toList(); } catch (e) { print("❌ Error decoding fixed agent_supported_service_ids: $e"); selectedServiceIds = []; } print("Updated selectedGender: $selectedGender"); // Debugging travelDetailsDataFromAPI = apiselectedUser?["travel_details"]; print("travelDetailsDataFromAPI00 - $travelDetailsDataFromAPI"); // if (travelDetails != null) { // WidgetsBinding.instance.addPostFrameCallback((_) { // // Safe to call updateTravel now // travellerDetailsKey.currentState?.updateTravel(); // }); // } // Optionally load other travel-related fields like meal/seat preferences, etc. }); } else { print("API Selected User Has Data - No data available yet"); } } @override // void initState() { // super.initState(); // prepareForNewEntry(); // selectedTab = "personal"; // // // WidgetsFlutterBinding.ensureInitialized(); // // // Step 1: Set 'reloaded' flag before page unload // // html.window.onBeforeUnload.listen((event) { // // html.window.localStorage['reloaded'] = 'true'; // // }); // // apiCountryData = null; // apiUserData = null; // // apiselectedUser = null; // apiCostData = null; // apiRoleData = null; // // // Initialize controllers for each field // // WidgetsBinding.instance.addPostFrameCallback((_) async { // // final wasReloaded = html.window.localStorage['reloaded'] == 'true'; // // // // if (wasReloaded) { // // html.window.localStorage.remove('reloaded'); // Clear it // // context.go('/listUser'); // Navigate using go_router // // } // // final extraData = // GoRouterState.of(context).extra as Map?; // // if (extraData != null) { // print("extraData: ${extraData['selectedUser']}"); // // setState(() { // // apiCountryData = extraData['apiCountryData']; // // apiUserData = extraData['apiUserData']; // // // // userList = apiUserData ?? []; // // userMap = { // // for (var user in userList) // // user['user_id'].toString(): "${user['first_name']} ${user['last_name']}" // // }; // // userIdsApi = userMap.keys.toList(); // // // Handle selectedUser as a Map (not a List) // apiselectedUser = // extraData['selectedUser'] // as Map?; // Cast it as a Map // isViewMode = extraData['isViewMode'] ?? false; // isEditProfile = extraData['isEditProfile'] ?? false; // }); // // print("selectedUser: $apiselectedUser"); // // // Add another post-frame callback to check after setState // // await Future.delayed(Duration( // // milliseconds: 100)); // Optional delay to ensure UI has updated // updateData(); // } // // initializeData(); // fetchCountries(); // fetchDepartment(); // fetchUsers(); // fetchRoles(); // loadInitialData(); // }); // // for (var field in dataHeader) { // controllers[field] = TextEditingController(); // } // } void initState() { super.initState(); setState(() { isCheckingToken = true; }); WidgetsBinding.instance.addPostFrameCallback((_) { _checkAuthAndLoadData(); // Safe place to use context }); } void _checkAuthAndLoadData() async { final roleUser = await getRoleUser(); setState(() { isCheckingToken = true; }); final String? token = await getToken(); if (!mounted) return; if (token == null || token.isEmpty) { context.go("/"); return; } if (roleUser != null && (roleUser == 'Org Admin' || roleUser == 'Travel Admin')) { setState(() { isCheckingToken = false; }); prepareForNewEntry(); selectedTab = "personal"; apiCountryData = null; apiUserData = null; apiCostData = null; apiRoleData = null; for (var field in dataHeader) { controllers[field] = TextEditingController(); } /// ✅ Now this is safe here try { final extraData = GoRouterState.of(context).extra; if (extraData != null && extraData is Map) { setState(() { apiselectedUser = extraData['selectedUser'] as Map?; isViewMode = extraData['isViewMode'] ?? false; isEditProfile = extraData['isEditProfile'] ?? false; }); updateData(); } } catch (e) { print("Router extra error: $e"); } // Final async data fetches initializeData(); fetchCountries(); fetchDepartment(); fetchUsers(); fetchRoles(); loadInitialData(); } else { apiService.logout(context); } } void _checkAuthAndLoadData22() async { final String? token = await getToken(); // Your async function to get token if (!mounted) return; if (token == null || token.isEmpty) { // Token doesn't exist → redirect to login context.go( "/", ); // or use: router.go("/") if you're using `GoRouter` directly setState(() { isCheckingToken = false; }); return; } else { if (!mounted) return; // Future.microtask(() { prepareForNewEntry(); selectedTab = "personal"; // WidgetsFlutterBinding.ensureInitialized(); // Step 1: Set 'reloaded' flag before page unload // html.window.onBeforeUnload.listen((event) { // html.window.localStorage['reloaded'] = 'true'; // }); apiCountryData = null; apiUserData = null; // apiselectedUser = null; apiCostData = null; apiRoleData = null; // Initialize controllers for each field // WidgetsBinding.instance.addPostFrameCallback((_) async { // if (!mounted) return; // final wasReloaded = html.window.localStorage['reloaded'] == 'true'; // // if (wasReloaded) { // html.window.localStorage.remove('reloaded'); // Clear it // context.go('/listUser'); // Navigate using go_router // } final extraData = GoRouterState.of(context).extra as Map?; if (extraData != null) { print("extraData: ${extraData['selectedUser']}"); setState(() { // apiCountryData = extraData['apiCountryData']; // apiUserData = extraData['apiUserData']; // // userList = apiUserData ?? []; // userMap = { // for (var user in userList) // user['user_id'].toString(): "${user['first_name']} ${user['last_name']}" // }; // userIdsApi = userMap.keys.toList(); // Handle selectedUser as a Map (not a List) apiselectedUser = extraData['selectedUser'] as Map?; // Cast it as a Map isViewMode = extraData['isViewMode'] ?? false; isEditProfile = extraData['isEditProfile'] ?? false; }); print("selectedUser: $apiselectedUser"); // Add another post-frame callback to check after setState // await Future.delayed(Duration( // milliseconds: 100)); // Optional delay to ensure UI has updated updateData(); } initializeData(); fetchCountries(); fetchDepartment(); fetchUsers(); fetchRoles(); loadInitialData(); for (var field in dataHeader) { controllers[field] = TextEditingController(); } setState(() { isCheckingToken = false; }); // }); } } Future fetchCountries() async { try { List countries = await apiService.fetchCountryList(context); setState(() { apiCountryData = countries; }); } catch (e) { print('Error fetching country list: $e'); } } Future fetchDepartment() async { try { List department = await apiService.fetchCostCenter(context); setState(() { apiCostData = department; }); } catch (e) { print('Error fetching department list: $e'); } } Future fetchUsers() async { try { List users = await apiService.fetchUsers(context); setState(() { // apiUserData = users; apiUserData = users.where((user) => user["role_id"] == "4").toList(); print("APIUSerDATa - $apiUserData"); userList = apiUserData ?? []; userMap = { for (var user in userList) user['user_id'].toString(): "${user['first_name']} ${user['last_name']}", }; userIdsApi = userMap.keys.toList(); }); } catch (e) { print('Error fetching country list: $e'); } } Future fetchRoles() async { try { final response = await apiService.fetchMasterDropdown(context); if (response is Map && response.containsKey("role")) { List roleList = response["role"]; // Extract the list setState(() { apiRoleData = roleList; }); print("APIROLEData - $apiRoleData"); } else { print("Error: 'role' key not found or response is not a Map."); } } catch (e) { print('Error fetching role list: $e'); } } void printFormData() { for (var field in dataHeader) { print("$field: ${controllers[field]?.text}"); } } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } Future initializeData() async { token = await getToken(); userId = await getUserId(); roleId = await getRoleId(); if (token == null || userId == null) { print("Token or USerId missing"); print("Retrieved Token: $token"); print("Retrieved UserId: $userId"); return; } else { setState(() {}); } } Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } Future getUserId() async { final prefs = await SharedPreferences.getInstance(); final userData = prefs.getString('user_data'); if (userData != null) { final decodedData = jsonDecode(userData); // Return the user_id from the decoded data return decodedData['user_id']?.toString(); } return null; } Future getRoleId() async { final prefs = await SharedPreferences.getInstance(); final userData = prefs.getString('user_data'); if (userData != null) { final decodedData = jsonDecode(userData); // Return the user_id from the decoded data return decodedData['role_id']?.toString(); } return null; } @override void dispose() { // Dispose all controllers to prevent memory leaks for (var controller in controllers.values) { controller.dispose(); } super.dispose(); } void prepareForNewEntry() { for (var controller in controllers.values) { controller.clear(); } } void handleGoBack() async { print("hello, please Go Back"); printFormData(); } void handleBack() async { print("USR Detail back"); final tabs = { "personal": "Personal Details", "office": "Office Details", "travel": "Travel Details", }; final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"] final currentIndex = tabKeys.indexOf(selectedTab ?? "travel"); print("currentIndex - $currentIndex"); if (currentIndex > 0) { // Move to next tab setState(() { selectedTab = tabKeys[currentIndex - 1]; }); } else { // Final step — submit or show done print("All tabs completed!"); // Submit the full form here } } void handleNext() async { print("USR Detail Next"); printFormData(); // travelDetailsData = [travellerDetailsKey.currentState?.travel_Detials]; travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; // errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages; print("TRAVEL DETAILS FROM CHILD: $travelDetailsData"); Map data = userDetials; print("USERDETAILS : $data"); final tabs = { "personal": "Personal Details", "office": "Office Details", "travel": "Travel Details", }; final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"] final currentIndex = tabKeys.indexOf(selectedTab ?? "personal"); print("currentIndex - $currentIndex"); bool isValid = false; final currentTab = tabKeys[currentIndex]; if (currentTab == "personal") { // isValid = isValidData(userDetials); isValid = await isValidData(userDetials); } else if (currentTab == "office") { isValid = await isValidDataTwo(userDetials); } else { isValid = true; // Travel tab might not need validation at this point } if (!isValid) { print("Validation Failed on $currentTab: $userDetials"); setState(() {}); // To trigger UI update showing errors return; } if (currentIndex < tabKeys.length - 1) { // Move to next tab setState(() { selectedTab = tabKeys[currentIndex + 1]; }); } else { // Final step — submit or show done print("All tabs completed!"); // Submit the full form here } } void closeLoaderIfOpen() { if (mounted) { Navigator.of(context, rootNavigator: true).pop(); } } void handleSubmit() async { print("USR Detail Submit"); // printFormData(); print("travel validation"); bool isValid = travellerDetailsKey.currentState?.boolValidation() ?? false; print("Start loader at: ${DateTime.now()}"); showDialog( context: context, barrierDismissible: false, builder: (context) => const SavingLoader(), ); if (selectedRole == "5" || setSelectesUserType == true) { print("NO validation"); Map data = userDetials; print("userDetials ====> $userDetials"); if (!await isValidData(data)) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); closeLoaderIfOpen(); setState(() {}); return; // Stop execution if validation fails } else { print("USERDETAILS : $userDetials"); orgId = await getOrgId(); createUserData(userDetials); } } else { print("isValid- $isValid"); if (!isValid) { print("Validation failed. Please check the inputs."); closeLoaderIfOpen(); setState(() {}); return; // ❌ STOP execution here if not valid } travelDetailsData = travellerDetailsKey.currentState?.travel_Detials; print("travelDetailsData - $travelDetailsData"); print("travelDetailsData - $travelDetailsData"); // errorMessagesTravel = travellerDetailsKey.currentState!.errorMessages; print("TRAVEL DETAILS FROM CHILD"); // print("TRAVEL DETAILS FROM CHILD: $travelDetailsData"); passportFile = travellerDetailsKey.currentState?.passportFile; print("passportFile : $passportFile"); Map data = userDetials; print("USERDETAILS : $data"); // block-submit-here final format = DateFormat("dd-MM-yyyy"); DateTime? start_Date; DateTime? end_Date; try { if (travelDetailsData?['date_of_issue'] != null && travelDetailsData!['date_of_issue'].toString().isNotEmpty) { start_Date = format.parse(travelDetailsData!['date_of_issue']); } if (travelDetailsData?['date_of_expiry'] != null && travelDetailsData!['date_of_expiry'].toString().isNotEmpty) { end_Date = format.parse(travelDetailsData!['date_of_expiry']); } if (start_Date != null && end_Date != null && end_Date.isBefore(start_Date)) { print("Invalid date range"); return; } } catch (e) { print("Invalid passport date format"); return; } // Additional validation starts - travelDetailsData passport // DateTime? start_Date = travelDetailsData?['date_of_issue']; // DateTime? end_Date = travelDetailsData?['date_of_expiry']; // // if (start_Date != null && // end_Date != null && // start_Date.toString().isNotEmpty && // end_Date.toString().isNotEmpty) { // try { // final format = DateFormat("dd-MM-yyyy"); // final checkStartDate = format.parse("$start_Date"); // final checkEndDate = format.parse("$end_Date"); // // if (checkEndDate.isBefore(checkStartDate)) { // // return "End date cannot be earlier than start date";; // return; // } // } catch (e) { // // return "End date cannot be earlier than start date"; // return; // // errorMessages["end_date"] = "Invalid date format"; // } // } // valid_from: 20-06-2025, valid_upto: 19-06-2025 DateTime? validFrom = data['valid_from']; DateTime? validUpto = data['valid_upto']; if (validFrom != null && validUpto != null && validFrom.toString().isNotEmpty && validUpto.toString().isNotEmpty) { try { final format = DateFormat("dd-MM-yyyy"); final checkValidFrom = format.parse("$validFrom"); final checkValidUpto = format.parse("$validUpto"); if (checkValidUpto.isBefore(checkValidFrom)) { // return "valid upto cannot be earlier than valid from"; return; } } catch (e) { // return "valid upto cannot be earlier than valid from"; return; // errorMessages["end_date"] = "Invalid date format"; } } // Map data = userDetials; if (!await isValidData(data) && !await isValidDataTwo(data)) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); closeLoaderIfOpen(); setState(() {}); return; // Stop execution if validation fails } else { print("USERDETAILS : $userDetials"); orgId = await getOrgId(); createUserData(userDetials); } } if (mounted) Navigator.of(context, rootNavigator: true).pop(); } // Future apiCheckDuplicate( // String label, // String field, // String value, // String? userId, // ) async // { // try { // // Basic validation: Check mobile number length // if (field == "mobile_no" && value.length != 10) { // return true; // Skip the API call if input is invalid // } // // final response = await apiService.CheckDuplicate( // label, // field, // value, // userId, // ); // if (response.isNotEmpty) { // // _clearError(field); // errorMessages[field] = response['message'] ?? "$label Already Exists"; // print("Duplicate found: ${response['message']}"); // // setState(() {}); // return true; // } else { // _clearError(field); // print("No duplicates found."); // // return false; // } // } catch (e) { // print("Error in checkDuplicate: $e"); // return false; // } // } Future apiCheckDuplicate( String label, String field, String value, String? userId, ) { return apiService.CheckDuplicate(context, label, field, value, userId) .then((response) { if (response.isNotEmpty) { errorMessages[field] = response['message'] ?? "$label Already Exists"; print("Duplicate found: ${response['message']}"); // setState(() {}); print("Duplicate found: true"); return true; } else { _clearError(field); print("No duplicates found."); return false; } }) .catchError((e) { print("Error in checkDuplicate: $e"); return false; }); } Future isValidData(Map data) async { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = [ "first_name", "last_name", "email", "mobile_no", "role_id", // "employeeCode", ]; if (apiselectedUser == null) { requiredFields.add("password"); } // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } } // Mobile number validation for both mobile_no and alternate_mobile_no if (data["mobile_no"] != null && data["mobile_no"].toString().isNotEmpty) { if (!RegExp(r"^\d{10}$").hasMatch(data["mobile_no"].toString())) { errorMessages["mobile_no"] = "Enter 10 digits"; // Invalid mobile number format } else { bool isDuplicate = await apiCheckDuplicate( "Mobile Number", "mobile_no", data["mobile_no"]!, userIdApi, ); if (isDuplicate) { print("REsduplica1 - $isDuplicate"); errorMessages["mobile_no"] = "Mobile Number already exist"; // errorMessages is already updated inside apiCheckDuplicate // setState(() {}); } } // else { // apiCheckDuplicate( // "Mobile Number", // "mobile_no", // data["mobile_no"]!, // userIdApi, // ).then((isDuplicate) { // print("REsduplica - $isDuplicate"); // // if (isDuplicate) { // print("REsduplica1 - $isDuplicate"); // errorMessages["mobile_no"] = "Mobile Number Already Exist"; // // errorMessages is already updated inside apiCheckDuplicate // // setState(() {}); // } else { // // Optional: do something if no duplicate found // } // }); // // // errorMessages["mobile_no"] = "Mobile Number already exist"; // } } if (data["alternate_mobile_no"] != null && data["alternate_mobile_no"].toString().isNotEmpty) { if (!RegExp( r"^\d{10}$", ).hasMatch(data["alternate_mobile_no"].toString())) { errorMessages["alternate_mobile_no"] = "Enter 10 digits"; // Invalid mobile number format } } // Email validation if (data["email"] != null && data["email"].toString().isNotEmpty) { if (!RegExp( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ).hasMatch(data["email"].toString())) { errorMessages["email"] = "Invalid email format"; // Invalid email format } else { bool isDuplicate = await apiCheckDuplicate( "Email", "email", data["email"]!, userIdApi, ); if (isDuplicate) { errorMessages["email"] = "Email already exist"; } } // else { // apiCheckDuplicate("Email", "email", data["email"]!, userIdApi).then(( // isDuplicate, // ) { // print("REsduplica - $isDuplicate"); // // if (isDuplicate) { // print("REsduplica1 - $isDuplicate"); // errorMessages["email"] = "Email Already Exist"; // // errorMessages is already updated inside apiCheckDuplicate // // setState(() {}); // } // }); // // // errorMessages["mobile_no"] = "Mobile Number already exist"; // } } print("ISEMPTY"); return errorMessages.isEmpty; // Valid if there are no errors } Future isValidDataTwo(Map data) async { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = ["employee_code"]; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } else { bool isDuplicate = await apiCheckDuplicate( "Employee Code", "employee_code", data["employee_code"]!, userIdApi, ); if (isDuplicate) { print("REsduplica1 - $isDuplicate"); errorMessages["employee_code"] = "Employee Code already exist"; // errorMessages is already updated inside apiCheckDuplicate // setState(() {}); } } } return errorMessages.isEmpty; // Valid if there are no errors } void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { errorMessages.remove(field); }); } } void pickPDFWeb() { html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); uploadInput.accept = '.pdf'; uploadInput.click(); uploadInput.onChange.listen((e) { final file = uploadInput.files!.first; // Ensure the file is a PDF if (!file.type.contains("pdf")) { print("Error: Not a PDF file"); return; } // 🔹 File size check: Ensure it does not exceed 3MB int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes if (file.size > maxFileSize) { print('Error: File size exceeds 3MB'); return; } setState(() { selectedFileNames = file.name; passportFile = file; passportFileUrlFromApi = null; }); print('PDF File selected: ${file.name}'); }); } Future createUserData(Map userData) async { final bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty; final uri = Uri.parse( isUpdating ? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}' : '$apiUrl/api/users/create', ); if (token == null) { throw Exception('Token not found. Please log in.'); } // Use MultipartRequest (POST only) final request = http.MultipartRequest('POST', uri); request.headers['Authorization'] = 'Bearer $token'; request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456'; // If updating, spoof the method Laravel-style if (isUpdating) { request.fields['_method'] = 'PUT'; request.fields['user_id'] = apiselectedUser!["user_id"].toString(); } // userData.forEach((key, value) { // // if (value == null) { // // // skip nulls // // return; // // } // // if (key == 'travel_details' && value is Map) { // // Encode travel_details map as JSON string // request.fields[key] = jsonEncode(value); // print("✅ Encoded travel_details: ${request.fields[key]}"); // } else { // // If the value is empty or only whitespace, add it as empty string // if (value.toString().trim().isEmpty) { // request.fields[key] = ''; // } else { // // Otherwise add the actual string value // request.fields[key] = value.toString(); // } // } // }); userData.forEach((key, value) { // if (value != null && value.toString().trim().isNotEmpty) { if (key == 'travel_details' && value is Map) { // ✅ Encode travel_details as a proper JSON string request.fields[key] = jsonEncode(value); print("✅ Encoded travel_details1: ${request.fields[key]}"); } else if (key == 'agent_supported_service_ids' && value is List) { request.fields[key] = jsonEncode(value); print("✅ Encoded agent_supported_service_ids: ${request.fields[key]}"); } else { // request.fields[key] = jsonEncode(value); print("✅ Encoded travel_details:${key} - ${value}"); request.fields[key] = value.toString(); print("✅ Encoded travel_details2: ${request.fields[key]}"); } // } }); // Attach file if selected if (passportFile != null) { try { final reader = html.FileReader(); reader.readAsArrayBuffer(passportFile!); await reader.onLoad.first; final data = reader.result as Uint8List; final multipartFile = http.MultipartFile.fromBytes( 'passport_document', data, filename: passportFile!.name, ); request.files.add(multipartFile); print("📎 File attached: ${passportFile!.name}"); } catch (e) { print("❌ Failed to read file: $e"); } } else { print("⚠️ No passport file selected."); } print("🚀 Sending request with fields: ${request.fields}"); try { final streamedResponse = await request.send(); final response = await http.Response.fromStream(streamedResponse); print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); if (response.statusCode == 200 || response.statusCode == 201) { // dispose(); print("✅ User submitted successfully!"); print("📨 Response: ${response.body}"); await apiService.handleTokenRefresh(context, userId!); if (roleId == "4") { context.go('/listPlan'); } else { context.go('/listUser'); } } else { print("❌ Submission failed. Status: ${response.statusCode}"); print("📨 Body: ${response.body}"); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text("User Creation Failed"), content: Text( "There was a problem in creating user. Please try again.", ), actions: [ TextButton( child: Text("OK"), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } } catch (e) { print("🔥 Error submitting user: $e"); } } void handleChangePassword(String enteredPassword) { var hashedPassword = apiselectedUser?["password"]; // Get stored bcrypt hash print("HAshPAssword - $hashedPassword "); print("enteredPassword - $enteredPassword "); if (hashedPassword != null && hashedPassword.isNotEmpty) { bool isMatch = BCrypt.checkpw( enteredPassword, hashedPassword, ); // Compare passwords setState(() { // ✅ Ensure UI updates if (isMatch) { errorMessages["password"] = "New password is not similar to old password"; print(" Password match!"); } else { print(" Password NOT match!"); errorMessages.remove( "password", ); // Clear error if password is different } }); } else { print("No stored password found."); } } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; if (isCheckingToken) { return Center(child: CircularProgressIndicator()); } return Scaffold( backgroundColor: Color(0xFFf5f5f5), // appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '), // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( padding: isDesktop ? EdgeInsets.symmetric( horizontal: MediaQuery.of(context).size.width * 0.1, // 30% of screen width as horizontal padding vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) : EdgeInsets.all(0), child: Row( children: [Expanded(child: buildData(isDesktop, context))], ), ), ); }, ); } Widget buildData(bool isDesktop, context) { return Container( margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, padding: const EdgeInsets.only(left: 10, right: 10, top: 8), decoration: BoxDecoration( color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), child: Column( children: [ Expanded( child: Container( height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, child: Padding( padding: EdgeInsets.all(0.0), child: _buildUserDetails(isDesktop), ), ), ), Container( color: Colors.white, child: Padding( padding: const EdgeInsets.all(8.0), child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (selectedTab != "personal") ..._buildBack(isDesktop, layoutColor!), Spacer(), // spacing between buttons // Next or Submit based on role or user type if (selectedTab == "travel" || selectedRole == "5" || setSelectesUserType == true) ..._buildSubmit(isDesktop, layoutColor!) else ..._buildNext(isDesktop, layoutColor!), // (selectedTab == "travel" || // selectedRole == "5" || // setSelectesUserType == true) // ? _buildSubmit(isDesktop, layoutColor!) // : _buildNext( // isDesktop, // layoutColor!, // ), // _buildGoBack(isDesktop, layoutColor!), ], ) : Row( mainAxisAlignment: MainAxisAlignment.end, children: (selectedTab == "travel" || selectedRole == "5" || setSelectesUserType == true) ? _buildSubmit(isDesktop, layoutColor!) : _buildNext(isDesktop, layoutColor!), ), ), ), ], ), ); } Widget _buildUserDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Text( // apiselectedUser != null ? "Profile" : "Create New User", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 15 : 12, // fontWeight: FontWeight.w600, // color: Colors.black, // ), // ), //old very first // if (isEditProfile) // IconButton( // icon: Icon( // Icons.edit, // color: Color(0xFF114D8B), // size: 14, // ), // onPressed: () { // setState(() { // isViewMode = !isViewMode; // }); // }) Row( children: [ if (apiselectedUser != null) isEditProfile ? Text( "Profile", style: GoogleFonts.poppins( fontSize: isDesktop ? 15 : 12, fontWeight: FontWeight.w600, color: Colors.black, ), ) : BreadcrumbNavigation( isDesktop: isDesktop, breadcrumbItems: [ BreadcrumbItem( title: 'Users', tooltip: 'Go To Users', onTap: (context) { context.go("/listUser"); }, ), BreadcrumbItem(title: 'Edit User'), ], ) else BreadcrumbNavigation( isDesktop: isDesktop, breadcrumbItems: [ BreadcrumbItem( title: 'Users', tooltip: 'Go To Users', onTap: (context) { context.go("/listUser"); }, ), BreadcrumbItem(title: 'Create User'), ], ), ], ), SizedBox(height: 18), isDesktop ? buildTabsForUser() : SingleChildScrollView( scrollDirection: Axis.horizontal, child: buildTabsForUser(), ), Container( // color: Colors.yellow.shade50, height: MediaQuery.of(context).size.height * 0.64, child: Row( children: [ Expanded(child: buildTabContents(isDesktop, isViewMode)), ], ), ), ], ), ), ], ); } Widget buildTabContents(isDesktop, isViewMode) { switch (selectedTab) { case "personal": return PersonalDetails( personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, isEditProfile: isEditProfile, userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, selectedCountry: selectedCountry, selectedRole: selectedRole, apiselectedUser: isapiselectedUser, onUserTypeChanged: handleUserTypeChange, onGenderChanged: (gender) { setState(() { selectedGender = gender; }); }, onCountryChanged: (country) { setState(() { selectedCountry = country; }); }, onRoleChanged: (role) { setState(() { print("Roleu $role"); selectedRole = role; }); }, onServiceIdsChanged: (List> updatedList) { setState(() { selectedServiceIds = updatedList; }); }, initialSelectedServices: selectedServiceIds, ); case "office": return OfficeDetails( isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedLevel: selectedLevel, selectedDepartment: selectedDepartment, selectedFirstApprover: selectedFirstApprover, selectedSecondApprover: selectedSecondApprover, selectedThirdApprover: selectedThirdApprover, selectedExceptionalApprover: selectedExceptionalApprover, selectedSubstituteApprover: selectedSubstituteApprover, onLevelChanged: (gender) { setState(() { selectedLevel = gender; }); }, onDepartmentChanged: (country) { setState(() { selectedDepartment = country; }); }, onFirstApproverChanged: (role) { setState(() { selectedFirstApprover = role; }); }, onSecondApproverChanged: (role) { setState(() { selectedSecondApprover = role; }); }, onThirdApproverChanged: (role) { setState(() { selectedThirdApprover = role; }); }, onExceptionalApproverChanged: (role) { setState(() { selectedExceptionalApprover = role; }); }, onFirstSubsApproverChanged: (role) { setState(() { selectedSubstituteApprover = role; print("onFirstSubsApproverChanged - $selectedSubstituteApprover"); }); }, ); case "travel": final fullName = "${controllers["Fname"]?.text ?? ""} ${controllers["Lname"]?.text ?? ""}" .trim(); return TravellerDetails( key: travellerDetailsKey, isDesktop: isDesktop, isViewMode: isViewMode, controllers: controllers, errorMessages: errorMessages, travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down passportFileUrlFromApi: passportFileUrlFromApi, userIdApi: userIdApi, fullName: fullName, ); default: return PersonalDetails( personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, isEditProfile: isEditProfile, userIdApi: userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, selectedCountry: selectedCountry, apiselectedUser: isapiselectedUser, onUserTypeChanged: handleUserTypeChange, onServiceIdsChanged: (List> updatedList) { setState(() { selectedServiceIds = updatedList; }); }, ); } } Widget buildTabsForUser() { // final tabs = { // "personal": "Personal Details", // "office": "Office Details", // "travel": "Travel Details", // }; final Map allTabs = { "personal": "Personal Details", "office": "Office Details", "travel": "Travel Details", }; Map getTabs(bool setSelectesUserType) { if (setSelectesUserType || selectedRole == "5") { return {"personal": "Personal Details"}; } else { return allTabs; } } final tabs = getTabs(setSelectesUserType); return Row( crossAxisAlignment: CrossAxisAlignment.end, // important children: tabs.entries.map((entry) { final targetTab = entry.key; print("TargetsTAb: $targetTab"); final isSelected = selectedTab == entry.key; print("isSelected: $isSelected"); return GestureDetector( onTap: () { setState(() async { bool isValid = false; final currentTab = selectedTab; if (currentTab == "personal") { // isValid = isValidData(userDetials); isValid = await isValidData(userDetials); if (isValid) { setState(() { selectedTab = entry.key; }); } } else if (currentTab == "office" && targetTab == "personal") { setState(() { selectedTab = entry.key; }); } else if (currentTab == "office") { isValid = await isValidDataTwo(userDetials); if (isValid) { setState(() { selectedTab = entry.key; }); } } else { isValid = true; // Travel tab might not need validation at this point setState(() { selectedTab = entry.key; }); } }); }, child: Padding( padding: const EdgeInsets.only( right: 24.0, ), // space between tabs child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( entry.value, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: isSelected ? Color(0xFF114D8B) : Color(0xFF475569), ), ), const SizedBox(height: 8), AnimatedContainer( duration: Duration(milliseconds: 300), height: 2, width: isSelected ? 50 : 0, // small line color: Color(0xFF114D8B), ), ], ), ), ); }).toList(), ); } // ---- Submit --------------------------------------- List _buildGoBack(isDesktop, Color layoutColor) { return [ MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color foregroundColor: isViewMode ? Colors.white : Colors.white, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: handleGoBack, child: Text("Back"), ), ), ]; } List _buildNext(isDesktop, Color layoutColor) { return [ MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color foregroundColor: isViewMode ? Colors.white : Colors.white, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: handleNext, // onPressed: // isViewMode ? null : handleNext, // Disable when in view mode child: Text("Next"), ), ), ]; } List _buildBack(isDesktop, Color layoutColor) { return [ MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, child: TextButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? Colors.white : Colors.white, // Keep original color foregroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), ), onPressed: handleBack, // onPressed: // isViewMode ? null : handleNext, // Disable when in view mode child: Text("Back"), ), ), ]; } List _buildSubmit(isDesktop, Color layoutColor) { return [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: layoutColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { isEditProfile ? context.go('/listPlan') : context.go('/listUser'); }, child: Text("Cancel"), ), SizedBox(width: 20), if (!isViewMode) MouseRegion( cursor: isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, child: Focus( // 👈 Wrap with Focus here focusNode: submitButtonFocusNode, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isViewMode ? layoutColor : layoutColor, // Keep original color foregroundColor: isViewMode ? Colors.white : Colors.white, // Keep original color disabledBackgroundColor: layoutColor, // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: layoutColor, width: 2), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: isViewMode ? null : handleSubmit, // Disable when in view mode child: Text("Submit"), ), ), ), ]; } } class DatabaseHelper { // Example: Singleton pattern to ensure only one instance of the class exists static final DatabaseHelper _instance = DatabaseHelper._internal(); // Private constructor to prevent external instantiation DatabaseHelper._internal(); // Public method to access the instance static DatabaseHelper get instance => _instance; // Add your database methods here Future fetchData() async { // Implement data fetching logic } }