diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index 9cdff8d..e8cca5e 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -1012,6 +1012,7 @@ class _CreateUserFormDetialsState extends State { return OfficeDetails( isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, + userIdApi:userIdApi, controllers: controllers, errorMessages: errorMessages, selectedLevel: selectedLevel, @@ -1062,12 +1063,14 @@ class _CreateUserFormDetialsState extends State { errorMessages: errorMessages, travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down passportFileUrlFromApi: passportFileUrlFromApi, + userIdApi:userIdApi, ); default: return PersonalDetails( personalDetailsKey: personalDetailsKey, isDesktop: isDesktop, // pass isDesktop as a named argument isViewMode: isViewMode, + userIdApi:userIdApi, controllers: controllers, errorMessages: errorMessages, selectedGender: selectedGender, diff --git a/lib/Screens/userManagement/create_user/office_details.dart b/lib/Screens/userManagement/create_user/office_details.dart index 10950f4..89ed579 100644 --- a/lib/Screens/userManagement/create_user/office_details.dart +++ b/lib/Screens/userManagement/create_user/office_details.dart @@ -15,6 +15,8 @@ class OfficeDetails extends StatefulWidget { final Map errorMessages; final bool isDesktop; final bool isViewMode; + final String? userIdApi; + final ValueChanged? onLevelChanged; final ValueChanged? onDepartmentChanged; @@ -48,6 +50,7 @@ class OfficeDetails extends StatefulWidget { this.onThirdApproverChanged, this.selectedSubstituteApprover, this.onFirstSubsApproverChanged, + this.userIdApi, }) : super(key: key); @override @@ -154,6 +157,28 @@ class _OfficeDetailsState extends State { fetchFindGroup(); } + Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + try { + + if (field == "employeeCode") { + field = "employee_code"; + } + + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.errorMessages[field] = response['message'] ?? "$label already exists"; + print("Duplicate found: ${response['message']}"); + return; + } else { + _clearError(field); + print("No duplicates found."); + } + } catch (e) { + print("Error in checkDuplicate: $e"); + } + } + void _clearError(String field) { setState(() { widget.errorMessages.remove(field); @@ -412,6 +437,7 @@ class _OfficeDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("employeeCode"); + apiCheckDuplicate("Employee Code", "employeeCode",value,widget.userIdApi); }, decoration: InputDecoration( labelText: "Employee Code", diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index 68b7c03..2f9113e 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -157,6 +157,34 @@ class PersonalDetailsState extends State { }); } + 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) { + _clearError(field); + widget.errorMessages[field] = "$label is invalid"; + print("Validation failed: $label is too short."); + return; // Skip the API call if input is invalid + } + + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.errorMessages[field] = response['message'] ?? "$label already exists"; + print("Duplicate found: ${response['message']}"); + return; + } else { + _clearError(field); + print("No duplicates found."); + } + } catch (e) { + print("Error in checkDuplicate: $e"); + } + } + + + + void _clearError(String field) { setState(() { widget.errorMessages.remove(field); @@ -1004,6 +1032,7 @@ class PersonalDetailsState extends State { controller: widget.controllers["email"], onChanged: (value) { _clearError("email"); + apiCheckDuplicate("Email", "email",value,widget.userIdApi); }, enabled: !widget.isViewMode, decoration: InputDecoration( @@ -1054,6 +1083,7 @@ class PersonalDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("mobile_no"); + apiCheckDuplicate("Mobile Number", "mobile_no",value,widget.userIdApi); }, keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ diff --git a/lib/Screens/userManagement/create_user/traveller_details.dart b/lib/Screens/userManagement/create_user/traveller_details.dart index df12175..b1e39de 100644 --- a/lib/Screens/userManagement/create_user/traveller_details.dart +++ b/lib/Screens/userManagement/create_user/traveller_details.dart @@ -31,6 +31,8 @@ class TravellerDetails extends StatefulWidget { final bool isViewMode; final Map? travelDetails; final String? passportFileUrlFromApi; + final String? userIdApi; + const TravellerDetails({ Key? key, @@ -40,6 +42,7 @@ class TravellerDetails extends StatefulWidget { required this.isViewMode, this.travelDetails, this.passportFileUrlFromApi, + this.userIdApi, }) : super(key: key); @override TravellerDetailsState createState() => TravellerDetailsState(); @@ -146,6 +149,26 @@ class TravellerDetailsState extends State { }); } + Future apiCheckDuplicate(String label, String field, String value, String? userId) async { + try { + if(field == "forex_card_num"){ + field = "forex_pre_paid_card_number"; + } + final response = await apiService.CheckDuplicate(label, field, value, userId); + if (response.isNotEmpty) { + _clearError(field); + widget.errorMessages[field] = response['message'] ?? "$label already exists"; + print("Duplicate found: ${response['message']}"); + return; + } else { + _clearError(field); + print("No duplicates found."); + } + } catch (e) { + print("Error in checkDuplicate: $e"); + } + } + void _clearError(String field) { setState(() { widget.errorMessages.remove(field); @@ -2308,6 +2331,7 @@ class TravellerDetailsState extends State { enabled: !widget.isViewMode, onChanged: (value) { _clearError("forex_card_num"); + apiCheckDuplicate("Forex Pre-Paid Card Number", "forex_card_num",value,widget.userIdApi); }, decoration: InputDecoration( labelText: "Card Number", @@ -2439,14 +2463,24 @@ class TravellerDetailsState extends State { SizedBox(width: 25), buildFrequentFlierInformation(entry), SizedBox(width: 15), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - removeFrequentFlierEntry(entry); - }); - }, + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeFrequentFlierEntry(entry); + }); + }, + ), + ), + ), ), + ], ) : Column( @@ -2457,7 +2491,8 @@ class TravellerDetailsState extends State { buildFrequentFlierInformation(entry), SizedBox(width: 15), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { removeFrequentFlierEntry(entry); @@ -2738,22 +2773,30 @@ class TravellerDetailsState extends State { child: widget.isDesktop ? Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - buildHotel(entry), - SizedBox(width: 25), - buildHotelMembershipNum(entry), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - // removeHotelLoyaltyEntry(localId); - removeHotelLoyaltyEntry(entry); - }); - }, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildHotel(entry), + SizedBox(width: 25), + buildHotelMembershipNum(entry), + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeHotelLoyaltyEntry(entry); + }); + }, + ), ), - ], - ) + ), + ), + ], + ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -2761,7 +2804,8 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildHotelMembershipNum(entry), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { // removeHotelLoyaltyEntry(localId); @@ -2987,13 +3031,22 @@ class TravellerDetailsState extends State { buildVisaValidFrom(entry), SizedBox(width: 15), buildVisaValidUpTo(entry), - IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), - onPressed: () { - setState(() { - removeVisaEntry(entry); - }); - }, + SizedBox( + height: 60, + child: Center( + child: Padding( + padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here + child: IconButton( + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', + onPressed: () { + setState(() { + removeVisaEntry(entry); + }); + }, + ), + ), + ), ), ], ) @@ -3008,7 +3061,8 @@ class TravellerDetailsState extends State { SizedBox(height: 8), buildVisaValidUpTo(entry), IconButton( - icon: Icon(Icons.cancel, color: Colors.red, size: 15), + icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15), + tooltip: 'Cancel the Details', onPressed: () { setState(() { // visaRowIds.remove(id); diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 99e61e8..7c98b36 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -1234,4 +1234,52 @@ class ApiService { } } + // ----------------------- User Management - CheckDuplicate --------------- + Future> CheckDuplicate(String label, String field, String value, String? userId) async { + String apiUrldata; + if (userId == null || userId.isEmpty) { + apiUrldata = '$apiUrl/api/checkDuplicate?$field=$value'; + } else { + apiUrldata = '$apiUrl/api/checkDuplicate?$field=$value&user_id=$userId'; + } + + 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); + + // Updated to match actual response structure + if (data['status'] == "exists") { + print("${data['field']} - $value already exists"); + return { + "message": "$label already exists", + "field": data['field'] + }; + } else { + print("$label - $value is a new value"); + return {}; + } + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else { + throw Exception('Failed to load checkDuplicate data. Status code: ${response.statusCode}'); + } + } + + +// ----------------------- User Management - CheckDuplicate end here ---------------------------------- + }