issues fixes

This commit is contained in:
VE10-Sanjeev 2025-05-31 17:08:06 +05:30
parent 3b24228080
commit ac303e666f
5 changed files with 570 additions and 15 deletions

View File

@ -0,0 +1,383 @@
import 'dart:convert';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import '../../../config/apiUrl.dart';
import '../../../services/apiService.dart';
import '../../../utils/auth_utils.dart';
import '../../../widgets/custom_user_form.dart';
class ChangePasswordDialogData extends StatefulWidget {
final dynamic isDesktop;
final dynamic layoutColor;
final dynamic updaterUserId;
final dynamic updaterEmail;
const ChangePasswordDialogData({
super.key,
this.isDesktop,
this.layoutColor,
this.updaterUserId,
this.updaterEmail
});
@override
ChangePasswordDialogDataState createState() => ChangePasswordDialogDataState();
}
class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
final ApiService apiService = ApiService();
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
String? loggeduserId;
String? updaterUserIdForAPI;
List<String> dataHeader = [
"email",
"changePassword",
"confirmPassword"
];
// @override
// void initState() {
// super.initState();
//
// for (var field in dataHeader) {
// controllers[field] = TextEditingController();
// }
//
// setState(() {
// controllers['email']?.text = widget.updaterEmail ?? '';
// controllers['changePassword']?.text = '';
// controllers['confirmPassword']?.text = '';
// });
// }
@override
void initState() {
super.initState();
print("widget.updaterEmail: ${widget.updaterEmail}");
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
setState(() {
controllers['email']?.text = widget.updaterEmail ;
controllers['changePassword']?.text = '';
controllers['confirmPassword']?.text = '';
updaterUserIdForAPI = widget.updaterUserId;
});
}
void _clearError() {
setState(() {
errorMessages.clear();
});
}
@override
void dispose() {
for (var controller in controllers.values) {
controller.dispose();
}
super.dispose();
}
bool validateData() {
errorMessages.clear();
final String? email = controllers["email"]?.text;
final String? changePassword = controllers["changePassword"]?.text;
final String? confirmPassword = controllers["confirmPassword"]?.text;
// Required fields check
if (email == null || email.trim().isEmpty) {
errorMessages["email"] = "Required";
}
if (changePassword == null || changePassword.trim().isEmpty) {
errorMessages["changePassword"] = "Required";
}
if (confirmPassword == null || confirmPassword.trim().isEmpty) {
errorMessages["confirmPassword"] = "Required";
}
// Password match check
if ((changePassword?.isNotEmpty ?? false) &&
(confirmPassword?.isNotEmpty ?? false) &&
changePassword != confirmPassword) {
errorMessages["changePassword"] = "Passwords do not match";
errorMessages["confirmPassword"] = "Passwords do not match";
}
// setState(() {}); // Update UI with any error messages
return errorMessages.isEmpty;
}
Future<void> handleSubmit() async {
loggeduserId = await getUserId();
setState(() {
// This triggers UI rebuild with error messages
if (validateData()) {
postData();
}
});
}
Future<void> postData() async {
// final remarksData = getData();
print('sss$updaterUserIdForAPI');
final loggedInUserId = await getUserId();
final password = controllers["changePassword"]?.text ?? '';
final confirmPassword = controllers["confirmPassword"]?.text ?? '';
final String apiUrldata = '$apiUrl/api/user/user-password/$updaterUserIdForAPI';
final token = await getToken();
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
try {
final uri = Uri.parse(apiUrldata);
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
final body = jsonEncode({
"password": password,
"updated_by": loggedInUserId,
});
final response = await http.put(uri, headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
print("Forex Details Created successfully!");
print("Response: ${response.body}");
_clearError();
Navigator.of(context).pop();
} else if (response.statusCode == 404) {
Navigator.of(context).pop();
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting plan: $e");
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30),
// contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 10),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Row 1: Title + Edit + Delete buttons
Row(
children: [
Text(
'Change Password',
style: GoogleFonts.poppins(fontSize: 15, color: Colors.black),
),
const Spacer(),
],
),
const SizedBox(height: 2),
Divider(
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
const SizedBox(height: 5),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Email",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
// width: isDesktop
// ? MediaQuery.of(context).size.width * 0.330
// : MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["email"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Email",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
),
if (errorMessages["email"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["email"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
SizedBox(
height: 10,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Change Password",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["changePassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Change Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
),
if (errorMessages["changePassword"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["changePassword"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
SizedBox(
height: 15,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Confirm Password",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["confirmPassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Confirm Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
),
if (errorMessages["confirmPassword"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["confirmPassword"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
child: ElevatedButton(
onPressed: () {
handleSubmit();
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text('Save',
style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)),
),
),
],
)
// : SizedBox.shrink(),
],
),
);
}
}

View File

@ -54,6 +54,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
String? userId;
String? orgId;
String? userIdApi;
String? token;
@ -201,7 +202,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
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"] ?? "";
@ -976,6 +977,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
personalDetailsKey: personalDetailsKey,
isDesktop: isDesktop, // pass isDesktop as a named argument
isViewMode: isViewMode,
userIdApi:userIdApi,
controllers: controllers,
errorMessages: errorMessages,
selectedGender: selectedGender,

View File

@ -1108,6 +1108,12 @@ class _OfficeDetailsState extends State<OfficeDetails> {
_selectedCheckOutDate = pickedDate;
widget.controllers["delegationStartDate"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
if (_selectedEndDate != null &&
_selectedEndDate!.isBefore(_selectedCheckOutDate!)) {
_selectedEndDate = null;
widget.controllers["delegationEndDate"]?.text = '';
}
});
}
}
@ -1173,6 +1179,10 @@ class _OfficeDetailsState extends State<OfficeDetails> {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime minDate = _selectedCheckOutDate != null
? _selectedCheckOutDate!
: today;
// Parse date from notifier if available, else use today
DateTime initialDate;
@ -1188,11 +1198,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
_selectedEndDate != null && _selectedEndDate!.isAfter(today)
? _selectedEndDate!
: today,
firstDate: today,
initialDate: _selectedEndDate != null &&
_selectedEndDate!.isAfter(minDate)
? _selectedEndDate!
: minDate,
firstDate: minDate,
lastDate: DateTime(2100),
);
@ -1201,8 +1211,6 @@ class _OfficeDetailsState extends State<OfficeDetails> {
_selectedEndDate = pickedDate;
widget.controllers["delegationEndDate"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
// textControllers["_forexEndDate"]?.text =
// DateFormat('dd-MM-yyyy').format(initialDate);
});
}
}

View File

@ -6,11 +6,14 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import '../../../services/apiService.dart';
import '../../../utils/auth_utils.dart';
import '../../../widgets/custom_user_form.dart';
import '../../../config/apiUrl.dart';
import 'change_password.dart';
class PersonalDetails extends StatefulWidget {
final GlobalKey<PersonalDetailsState> personalDetailsKey;
@ -31,6 +34,7 @@ class PersonalDetails extends StatefulWidget {
final String? selectedGender;
final String? selectedCountry;
final String? selectedRole;
final String? userIdApi;
// const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},);
const PersonalDetails(
@ -49,7 +53,7 @@ class PersonalDetails extends StatefulWidget {
this.onGenderChanged,
this.onCountryChanged,
this.onRoleChanged,
this.onUserTypeChanged})
this.onUserTypeChanged, this.userIdApi})
: super(key: key);
@override
@ -124,8 +128,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
Color? layoutColor;
Color? bodyColor;
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages2 = {};
@override
void initState() {
super.initState();
apiCountryData = null;
@ -144,6 +154,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
WidgetsBinding.instance.addPostFrameCallback((_) {
loadAllServices();
getOrganizationData();
loadInitialData();
});
}
@ -153,6 +164,21 @@ class PersonalDetailsState extends State<PersonalDetails> {
});
}
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<void> fetchRoles() async {
try {
final response = await apiService.fetchMasterDropdown();
@ -526,6 +552,24 @@ class PersonalDetailsState extends State<PersonalDetails> {
}
Widget _buildThirdRow(bool isDesktop) {
void _openPopup() {
final emailValue = widget.controllers["email"]?.text ?? "";
final updaterUserId = widget.userIdApi ?? "";
showDialog(
context: context,
builder: (context) {
return ChangePasswordDialogData(
updaterEmail: emailValue,
updaterUserId: updaterUserId,
layoutColor: layoutColor,
isDesktop: widget.isDesktop,
);
},
);
}
return Container(
color: Colors.white,
child: isDesktop
@ -535,8 +579,16 @@ class PersonalDetailsState extends State<PersonalDetails> {
if (!widget.apiselectedUser) ...[
buildPassword(),
SizedBox(width: 15),
buildRole()
]
else ...[
buildRole(),
SizedBox(height: 8, width: 15),
TextButton(
onPressed: () => _openPopup(),
child: Text("Change Password"),
),
],
buildRole()
],
)
: Column(
@ -544,9 +596,15 @@ class PersonalDetailsState extends State<PersonalDetails> {
children: [
if (!widget.apiselectedUser) ...[
buildPassword(),
SizedBox(height: 8)], //
buildRole()
SizedBox(height: 8),buildRole()
] else ...[
buildRole(),
SizedBox(height: 8),
TextButton(
onPressed: () => _openPopup(),
child: Text("Change Password"),
),
],
],
),
);
@ -1282,3 +1340,4 @@ class PersonalDetailsState extends State<PersonalDetails> {
// apiselectedUser != null
// ? SizedBox()
}

View File

@ -3093,6 +3093,109 @@ class TravellerDetailsState extends State<TravellerDetails> {
);
}
Widget buildVisaType2(entry) {
late Map<String, String> visaTypeMap; // Mapping country_code -> country_name
late List<String> visaTypeCodes; // List of country codes
// List<dynamic> purposeList = apiData?['visa_type_of_visa'];
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
print("purposeList - $purposeList");
visaTypeMap = {
for (var item in purposeList)
item['visa_type_id'] as String: item['visa_type_of_visa'] as String
};
// Extract only country codes for processing
visaTypeCodes = visaTypeMap.keys.toList();
// selectedPurpose ??= null;
String? selectedPurpose = entry['visa_type_of_visa'];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Visa Type",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74))
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: DropdownSearch<String>(
selectedItem: visaTypeMap[selectedPurpose],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
// constraints: BoxConstraints(maxHeight: 250),
itemBuilder: (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 6.0),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search Visa Type...",
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: visaTypeMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
),
),
dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Visa Type",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedPurpose = visaTypeMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
entry['visa_type_of_visa'] = selectedPurpose;
});
},
),
),
),
],
);
}
Widget buildVisaValidFrom(Map<String, dynamic> entry) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
@ -3132,7 +3235,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"ValidFrom",
"Valid From",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -3214,7 +3317,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Valid UpTo",
"Valid To",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,