diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 05e1b14..3050614 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -509,7 +509,8 @@ class _ApprovalListState extends State { Future _showDetails(id) async { _dialogShown = true; - final String apiUrldata = '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; + final String apiUrldata = + '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; final String? token = await getToken(); String label = ""; List> approverData = []; @@ -530,7 +531,9 @@ class _ApprovalListState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); label = data['data']['model_heading'] ?? ""; - approverData = List>.from(data['data']['approver_data'] ?? []); + approverData = List>.from( + data['data']['approver_data'] ?? [], + ); } else { print('Failed to load'); label = "Error - Failed to load data"; @@ -543,76 +546,104 @@ class _ApprovalListState extends State { showDialog( context: context, - builder: (context) => AlertDialog( - title: Text( - label, - style: GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (approverData.isNotEmpty) - SizedBox( - height: 250, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: DataTable( - columns: [ - // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - DataColumn(label: Text('Status', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - ], - rows: approverData.asMap().entries.map((entry) { - final index = entry.key + 1; - final item = entry.value; - - final approverIdKey = item.keys.firstWhere((k) => k.endsWith('_id'), orElse: () => ''); - final statusKey = item.keys.firstWhere((k) => k.endsWith('_status'), orElse: () => ''); - - final approverId = item[approverIdKey]?.toString() ?? '-'; - final statusText = item[statusKey]?.toString() ?? 'Unknown'; - - return DataRow( - cells: [ - // DataCell(Text('$index')), - // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), - DataCell(SizedBox(child: Text(statusText, overflow: TextOverflow.ellipsis))), + builder: + (context) => AlertDialog( + title: Text( + label, + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (approverData.isNotEmpty) + SizedBox( + height: 250, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: [ + // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), + DataColumn( + label: Text( + 'Status', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), ], - ); - }).toList(), + rows: + approverData.asMap().entries.map((entry) { + final index = entry.key + 1; + final item = entry.value; + + final approverIdKey = item.keys.firstWhere( + (k) => k.endsWith('_id'), + orElse: () => '', + ); + final statusKey = item.keys.firstWhere( + (k) => k.endsWith('_status'), + orElse: () => '', + ); + + final approverId = + item[approverIdKey]?.toString() ?? '-'; + final statusText = + item[statusKey]?.toString() ?? 'Unknown'; + + return DataRow( + cells: [ + // DataCell(Text('$index')), + // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), + DataCell( + SizedBox( + child: Text( + statusText, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ) + else + Text( + "-- no data. --", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w300, ), ), - ), - ) - else - Text("-- no data. --", style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w300)), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + _dialogShown = false; + }, + child: Text("Close"), + ), ], ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - _dialogShown = false; - }, - child: Text("Close"), - ), - ], - ), ); } - - - Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { @@ -688,7 +719,10 @@ class _ApprovalListState extends State { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, - padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), + padding: + isDesktop + ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) + : EdgeInsets.all(3.0), height: isDesktop ? MediaQuery.of(context).size.height * 0.98 @@ -788,10 +822,10 @@ class _ApprovalListState extends State { isDesktop ? SizedBox.shrink() : Row( - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - width: MediaQuery.of(context).size.width * 0.8, + width: MediaQuery.of(context).size.width * 0.82, height: 35, child: TextField( controller: searchController, @@ -858,7 +892,8 @@ class _ApprovalListState extends State { // fontWeight: FontWeight.bold, // color: Colors.redAccent)), SizedBox(height: adjHgt / 4), - Text("No Data Found", + Text( + "No Data Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, @@ -1138,14 +1173,20 @@ class _ApprovalListState extends State { height: 25, alignment: Alignment.center, decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(8), + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, + ), ), child: Text( plan.statusValue, textAlign: TextAlign.center, style: TextStyle( - color: getStatusTextColor(plan.statusValue), + color: getStatusTextColor( + plan.statusValue, + ), fontSize: 12, fontFamily: "Inter", fontWeight: FontWeight.w400, @@ -1412,8 +1453,12 @@ class _ApprovalListState extends State { vertical: 4, ), decoration: BoxDecoration( - color: getStatusColor(plan.statusValue), - borderRadius: BorderRadius.circular(8), + color: getStatusColor( + plan.statusValue, + ), + borderRadius: BorderRadius.circular( + 8, + ), ), child: Text( plan.statusValue, diff --git a/lib/Screens/organization/themeColor.dart b/lib/Screens/organization/themeColor.dart index d086a44..f2090a3 100644 --- a/lib/Screens/organization/themeColor.dart +++ b/lib/Screens/organization/themeColor.dart @@ -31,7 +31,10 @@ class _ColorThemePickerWidgetState extends State { Color(0xFF448AFF), // BlueAccent Color(0xFF027E87), // Blue Shade Color(0xFF12B24B), // Green - Color(0xFF2ECC71), // Emerald Green + Color(0xFFe30f01), // Emerald Green + // Color(0xFFef3030), // Emerald Green + Color(0xFFf26157), // Emerald Green + // Color(0xFF2ECC71), // Emerald Green Color(0xFFFF9800), // Orange Color(0xFFE67E22), // Orange 2 Color(0xFFBF4C0D), // Orange dark orange 3 diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index d56b564..cb994e8 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -61,10 +61,10 @@ class _CreateUserFormDetialsState extends State { bool isCheckingToken = false; bool isDisable = false; + bool isValid = false; final FocusNode submitButtonFocusNode = FocusNode(); - String? token; late bool isapiselectedUser = false; @@ -180,7 +180,7 @@ class _CreateUserFormDetialsState extends State { "first_approver": selectedFirstApprover, "second_approver": selectedSecondApprover, "third_approver": selectedThirdApprover, - "exceptional_approver" : selectedExceptionalApprover, + "exceptional_approver": selectedExceptionalApprover, "delegated_to_user_id": selectedSubstituteApprover, // "passport_number": controllers["passportNumber"]?.text, // "place_of_issue": controllers["placeOfIssue"]?.text, @@ -784,9 +784,10 @@ class _CreateUserFormDetialsState extends State { final currentTab = tabKeys[currentIndex]; if (currentTab == "personal") { - isValid = isValidData(userDetials); + // isValid = isValidData(userDetials); + isValid = await isValidData(userDetials); } else if (currentTab == "office") { - isValid = isValidDataTwo(userDetials); + isValid = await isValidDataTwo(userDetials); } else { isValid = true; // Travel tab might not need validation at this point } @@ -827,7 +828,7 @@ class _CreateUserFormDetialsState extends State { Map data = userDetials; print("userDetials ====> $userDetials"); - if (!isValidData(data)) { + if (!await isValidData(data)) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); setState(() {}); @@ -942,7 +943,7 @@ class _CreateUserFormDetialsState extends State { // Map data = userDetials; - if (!isValidData(data) && isValidDataTwo(data)) { + if (!await isValidData(data) && !await isValidDataTwo(data)) { print("USERDETAILS : $userDetials"); print("Validation Failed: Required fields are missing."); setState(() {}); @@ -958,7 +959,72 @@ class _CreateUserFormDetialsState extends State { if (mounted) Navigator.of(context, rootNavigator: true).pop(); } - bool isValidData(Map data) { + // 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(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 @@ -987,7 +1053,43 @@ class _CreateUserFormDetialsState extends State { 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 && @@ -1006,12 +1108,43 @@ class _CreateUserFormDetialsState extends State { 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 } - bool isValidDataTwo(Map data) { + Future isValidDataTwo(Map data) async { errorMessages.clear(); // Reset errors // Required fields that must not be empty @@ -1021,6 +1154,20 @@ class _CreateUserFormDetialsState extends State { 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(() {}); + } } } @@ -1364,26 +1511,27 @@ class _CreateUserFormDetialsState extends State { children: [ if (apiselectedUser != null) isEditProfile - ? Text( "Profile", - style: GoogleFonts.poppins( - fontSize: isDesktop ? 15 : 12, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ) + ? 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'), - ], - ) + isDesktop: isDesktop, + breadcrumbItems: [ + BreadcrumbItem( + title: 'Users', + tooltip: 'Go To Users', + onTap: (context) { + context.go("/listUser"); + }, + ), + BreadcrumbItem(title: 'Edit User'), + ], + ) else BreadcrumbNavigation( isDesktop: isDesktop, @@ -1584,28 +1732,37 @@ class _CreateUserFormDetialsState extends State { return GestureDetector( onTap: () { - setState(() { + setState(() async { bool isValid = false; final currentTab = selectedTab; if (currentTab == "personal") { - isValid = isValidData(userDetials); + // isValid = isValidData(userDetials); + isValid = await isValidData(userDetials); if (isValid) { - selectedTab = entry.key; + setState(() { + selectedTab = entry.key; + }); } } else if (currentTab == "office" && targetTab == "personal") { - selectedTab = entry.key; - } else if (currentTab == "office") { - isValid = isValidDataTwo(userDetials); - if (isValid) { + 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 - selectedTab = entry.key; + setState(() { + selectedTab = entry.key; + }); } }); }, @@ -1759,12 +1916,15 @@ class _CreateUserFormDetialsState extends State { isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, - child: Focus( // 👈 Wrap with Focus here + child: Focus( + // 👈 Wrap with Focus here focusNode: submitButtonFocusNode, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: - isViewMode ? layoutColor : layoutColor, // Keep original color + isViewMode + ? layoutColor + : layoutColor, // Keep original color foregroundColor: isViewMode ? Colors.white diff --git a/lib/Screens/userManagement/create_user/personal_details.dart b/lib/Screens/userManagement/create_user/personal_details.dart index 619395b..2a19bc8 100644 --- a/lib/Screens/userManagement/create_user/personal_details.dart +++ b/lib/Screens/userManagement/create_user/personal_details.dart @@ -229,6 +229,18 @@ class PersonalDetailsState extends State { _clearError(field); widget.errorMessages[field] = "$label is invalid"; print("Validation failed: $label is too short."); + // widget.controllers["mobileNumber"]?.clear(); + + final controller = widget.controllers["mobileNumber"]; + if (controller != null) { + print("Before clear: ${controller.text}"); + // controller.clear(); + print("After clear: ${controller.text}"); + } + + // Make sure this setState rebuilds the widget that owns the TextField! + if (mounted) setState(() {}); + // setState(() {}); return; // Skip the API call if input is invalid } @@ -241,7 +253,7 @@ class PersonalDetailsState extends State { if (response.isNotEmpty) { // _clearError(field); widget.errorMessages[field] = - response['message'] ?? "$label Already Exists"; + response['message'] ?? "$label already exists"; print("Duplicate found: ${response['message']}"); return; } else { @@ -1410,6 +1422,14 @@ class PersonalDetailsState extends State { ), ), if (widget.errorMessages["mobile_no"] != null) ...[ + // Builder( + // builder: (context) { + // WidgetsBinding.instance.addPostFrameCallback((_) { + // widget.controllers["mobileNumber"]?.clear(); + // }); + // return const SizedBox.shrink(); + // }, + // ), SizedBox(height: 5), // Space before error message Text( widget.errorMessages["mobile_no"]!,