1790 lines
58 KiB
Dart
1790 lines
58 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io' as html;
|
|
|
|
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_quill/flutter_quill_internal.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 '../create_user/change_password.dart';
|
|
|
|
class PersonalDetails extends StatefulWidget {
|
|
final GlobalKey<PersonalDetailsState> personalDetailsKey;
|
|
final Map<String, TextEditingController> controllers;
|
|
final Map<String, String> errorMessages;
|
|
final bool isDesktop;
|
|
final bool isViewMode;
|
|
final bool apiselectedUser;
|
|
|
|
final Function(List<Map<String, dynamic>>)? onServiceIdsChanged;
|
|
|
|
final ValueChanged<String?>? onGenderChanged;
|
|
final ValueChanged<String?>? onCountryChanged;
|
|
final ValueChanged<String?>? onRoleChanged;
|
|
final void Function(bool)? onUserTypeChanged;
|
|
final List<Map<String, dynamic>>? initialSelectedServices;
|
|
|
|
final String? selectedGender;
|
|
final String? selectedCountry;
|
|
final String? selectedRole;
|
|
final String? userIdApi;
|
|
|
|
// const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},);
|
|
const PersonalDetails({
|
|
Key? key,
|
|
this.selectedGender,
|
|
this.selectedCountry,
|
|
this.selectedRole,
|
|
this.onServiceIdsChanged,
|
|
required this.personalDetailsKey,
|
|
this.initialSelectedServices,
|
|
required this.controllers,
|
|
required this.errorMessages,
|
|
required this.isDesktop,
|
|
required this.isViewMode,
|
|
required this.apiselectedUser,
|
|
this.onGenderChanged,
|
|
this.onCountryChanged,
|
|
this.onRoleChanged,
|
|
this.onUserTypeChanged,
|
|
this.userIdApi,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
PersonalDetailsState createState() => PersonalDetailsState();
|
|
}
|
|
|
|
class PersonalDetailsState extends State<PersonalDetails> {
|
|
final ApiService apiService = ApiService();
|
|
|
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
|
late List<String> countryCodes; // List of country codes
|
|
late List<dynamic>? apiCountryData;
|
|
late List<dynamic>? apiRoleData;
|
|
List<dynamic>? apiAllServices;
|
|
Map<String, dynamic>? selectedOrg;
|
|
List<Map<String, dynamic>> selectedServiceIds = [];
|
|
Map<String, FocusNode> focusNodes = {};
|
|
Map<String, bool> focusStates = {};
|
|
|
|
String? get selectedGenderValue => selectedGender;
|
|
String? get selectedCountryValue => selectedCountry;
|
|
String? get selectedRoleValue => selectedRole;
|
|
|
|
late bool isTravelAgent = false;
|
|
|
|
String? selectedTab;
|
|
|
|
String? selectedCountry;
|
|
String? selectedGender;
|
|
|
|
String? selectedUserType;
|
|
String? selectedRole;
|
|
String? selectedLevel;
|
|
String? selectedDepartment;
|
|
String? selectedFirstApprover;
|
|
String? selectedSecondApprover;
|
|
String? selectedThirdApprover;
|
|
|
|
String? selectedFileNames;
|
|
Uint8List? passportDocumentBytes;
|
|
String? passportFileUrlFromApi;
|
|
String? base64PDF;
|
|
|
|
html.File? passportFile;
|
|
|
|
List<String> dataHeader = [
|
|
"Fname",
|
|
"Lname",
|
|
"Cname",
|
|
"email",
|
|
"password",
|
|
"dob",
|
|
"gender",
|
|
"mobileNumber",
|
|
"alternateMobile",
|
|
"address",
|
|
"postalCode",
|
|
"country",
|
|
"passportNumber",
|
|
"placeOfIssue",
|
|
"passportDoc",
|
|
"userType",
|
|
"roleId",
|
|
"deptId",
|
|
"levelId",
|
|
"firstApproval",
|
|
"secondApproval",
|
|
"thirdApproval",
|
|
"employeeCode",
|
|
"dateOfIssue",
|
|
"dateOfExpiry",
|
|
"changePassword",
|
|
];
|
|
|
|
Color? layoutColor;
|
|
Color? bodyColor;
|
|
|
|
final Map<String, TextEditingController> controllers = {};
|
|
Map<String, String> errorMessages2 = {};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
apiCountryData = null;
|
|
|
|
apiRoleData = null;
|
|
selectedGender = widget.selectedGender;
|
|
selectedCountry = widget.selectedCountry;
|
|
selectedRole = widget.selectedRole;
|
|
fetchRoles();
|
|
fetchCountries();
|
|
selectedServiceIds = List<Map<String, dynamic>>.from(
|
|
widget.initialSelectedServices ?? [],
|
|
);
|
|
|
|
print("selectedServiceIdsAPI - $selectedServiceIds");
|
|
|
|
for (var field in dataHeader) {
|
|
focusNodes["${field}FocusNode"] = FocusNode();
|
|
focusStates["${field}Focused"] = false;
|
|
}
|
|
|
|
for (var key in focusNodes.keys) {
|
|
_addFocusListener(focusNodes[key]!, (focus) {
|
|
setState(() {
|
|
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
|
});
|
|
});
|
|
}
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
loadAllServices();
|
|
getOrganizationData();
|
|
loadInitialData();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (var node in focusNodes.values) {
|
|
node.dispose();
|
|
}
|
|
|
|
super.dispose();
|
|
}
|
|
|
|
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
|
node.addListener(() {
|
|
setState(() {
|
|
updateState(node.hasFocus);
|
|
});
|
|
});
|
|
}
|
|
|
|
Future<void> 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(
|
|
context,
|
|
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);
|
|
});
|
|
}
|
|
|
|
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(context);
|
|
|
|
if (response is Map<String, dynamic> && response.containsKey("role")) {
|
|
List<dynamic> roleList = response["role"]; // Extract the list
|
|
|
|
if (!mounted) return;
|
|
setState(() {
|
|
final filteredRole =
|
|
roleList.where((item) => item['dropdown_key'] == '5').toList();
|
|
apiRoleData = filteredRole;
|
|
});
|
|
print("APIROLEData - $apiRoleData");
|
|
} else {
|
|
print("Error: 'role' key not found or response is not a Map.");
|
|
}
|
|
} catch (e) {
|
|
print('Error fetching role list: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> fetchCountries() async {
|
|
try {
|
|
List<dynamic> countries = await apiService.fetchCountryList(context);
|
|
setState(() {
|
|
apiCountryData = countries;
|
|
});
|
|
} catch (e) {
|
|
print('Error fetching country list: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> getOrganizationData() async {
|
|
try {
|
|
print("getUpdatedServices");
|
|
|
|
final result = await apiService.fetchOrganization(context);
|
|
print("UUPdatedServices - $result");
|
|
setState(() {
|
|
final servicesRaw = selectedOrg?['services_ids'];
|
|
|
|
List<dynamic> services;
|
|
|
|
if (servicesRaw is String) {
|
|
try {
|
|
services = jsonDecode(servicesRaw);
|
|
} catch (e) {
|
|
print('❌ Failed to decode services_ids: $e');
|
|
services = [];
|
|
}
|
|
} else if (servicesRaw is List) {
|
|
services = servicesRaw;
|
|
} else {
|
|
services = [];
|
|
}
|
|
|
|
if (selectedServiceIds.isEmpty && services.isNotEmpty) {
|
|
selectedServiceIds =
|
|
services.map<Map<String, dynamic>>((item) {
|
|
final map = Map<String, dynamic>.from(item);
|
|
return {"service_id": map['service_id'].toString()};
|
|
}).toList();
|
|
}
|
|
|
|
// selectedServiceIds = services.map<Map<String, dynamic>>((item) {
|
|
// // force cast or copy to a regular map
|
|
// final map = Map<String, dynamic>.from(item);
|
|
// return {
|
|
// "service_id": map['service_id'].toString(),
|
|
// };
|
|
// }).toList();
|
|
});
|
|
|
|
// orgId = await getOrgId();
|
|
|
|
print("selectedOrg - $selectedOrg");
|
|
} catch (e) {
|
|
print('Error fetching updatedServices list: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
height: MediaQuery.of(context).size.height,
|
|
decoration: BoxDecoration(
|
|
border: Border(
|
|
top: BorderSide(
|
|
color: Colors.blueGrey.shade300, // Set your desired color
|
|
width: 0.09, // Set border thickness
|
|
),
|
|
),
|
|
),
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 10),
|
|
_buildFirstRow(widget.isDesktop),
|
|
SizedBox(height: 10),
|
|
// _buildSecondRow(widget.isDesktop),
|
|
// SizedBox(height: 10),
|
|
_buildThirdRow(widget.isDesktop),
|
|
SizedBox(height: 10),
|
|
_buildForthRow(widget.isDesktop),
|
|
SizedBox(height: 10),
|
|
_buildServices(widget.isDesktop),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> loadAllServices() async {
|
|
try {
|
|
final result = await apiService.fetchAllServices(context);
|
|
setState(() {
|
|
// apiAllServices = result;
|
|
setState(() {
|
|
apiAllServices =
|
|
result
|
|
..sort((a, b) => (a['order'] ?? 0).compareTo(b['order'] ?? 0));
|
|
});
|
|
});
|
|
print("Fetched services: $apiAllServices");
|
|
} catch (e) {
|
|
print('Error fetching role list: $e');
|
|
}
|
|
}
|
|
|
|
Widget _buildServices(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Services",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.black,
|
|
),
|
|
// style: TextStyle(
|
|
// fontFamily: "Archivo",
|
|
// fontSize: 14,
|
|
// fontWeight: FontWeight.w600,
|
|
// color: Color(0xFF212121)),
|
|
),
|
|
SizedBox(height: 10),
|
|
|
|
if (widget.errorMessages["agent_supported_service_ids"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Align(
|
|
alignment: Alignment.center,
|
|
child: Text(
|
|
widget.errorMessages["agent_supported_service_ids"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
),
|
|
],
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Color(0xFFF4F4FB)),
|
|
borderRadius: BorderRadius.circular(1),
|
|
// color: bodyColor,
|
|
// color: Color(0xFFF5F5F5),
|
|
color: Colors.white,
|
|
),
|
|
padding: EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5),
|
|
child:
|
|
isDesktop
|
|
? SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(children: _buildOptions()),
|
|
),
|
|
)
|
|
: Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(children: _buildOptions()),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildOptions() {
|
|
if (apiAllServices == null) return [];
|
|
|
|
return apiAllServices!.map((service) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 20.0),
|
|
child: _buildOption(service),
|
|
);
|
|
}).toList();
|
|
}
|
|
|
|
Widget _buildOption(Map<String, dynamic> service) {
|
|
String name = service['name'];
|
|
String iconUrl = service['icon']; // Can be empty string
|
|
|
|
IconData fallbackIcon = _getLocalIconForService(name);
|
|
|
|
String serviceId = service['service_id'].toString();
|
|
// bool isSelected = selectedServiceIds.contains(serviceId);
|
|
bool isSelected = selectedServiceIds.any(
|
|
(item) => item["service_id"] == serviceId,
|
|
);
|
|
|
|
return GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
String serviceId = service['service_id'].toString();
|
|
|
|
// Check if already selected
|
|
int existingIndex = selectedServiceIds.indexWhere(
|
|
(item) => item["service_id"] == serviceId,
|
|
);
|
|
|
|
if (existingIndex != -1) {
|
|
selectedServiceIds.removeAt(existingIndex);
|
|
} else {
|
|
selectedServiceIds.add({"service_id": serviceId});
|
|
}
|
|
print("selectedServiceIdsUser --- $selectedServiceIds");
|
|
print(
|
|
"selectedServiceIdsUser1 --- ${json.encode(selectedServiceIds)}",
|
|
);
|
|
// Call the parent's callback
|
|
widget.onServiceIdsChanged!(selectedServiceIds);
|
|
// widget.onServiceIdsChanged!(selectedServiceIds);
|
|
});
|
|
},
|
|
child: Row(
|
|
children: [
|
|
iconUrl.isNotEmpty
|
|
? Image.network(
|
|
iconUrl,
|
|
width: 18,
|
|
height: 18,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Icon(
|
|
fallbackIcon,
|
|
size: 18,
|
|
color:
|
|
isSelected == name
|
|
? Color(0xFF114D8B)
|
|
: Color(0xFF475569),
|
|
);
|
|
},
|
|
)
|
|
: Icon(
|
|
fallbackIcon,
|
|
size: 18,
|
|
color:
|
|
isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
|
|
),
|
|
|
|
SizedBox(width: 5),
|
|
|
|
SizedBox(width: 5),
|
|
Text(
|
|
name,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
|
|
fontWeight:
|
|
isSelected == name ? FontWeight.bold : FontWeight.w500,
|
|
),
|
|
),
|
|
|
|
SizedBox(width: 5),
|
|
// if (selectedListOption == title && widget.isViewMode == false)
|
|
Container(
|
|
height: 15,
|
|
width: 15,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.rectangle,
|
|
border: Border.all(
|
|
color: isSelected ? Colors.green : Colors.grey.shade400,
|
|
width: isSelected ? 2 : 1,
|
|
),
|
|
),
|
|
child: Icon(
|
|
Icons.check,
|
|
size: 10,
|
|
|
|
color: isSelected ? Colors.green : Colors.white10,
|
|
// color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
IconData _getLocalIconForService(String name) {
|
|
switch (name.toLowerCase()) {
|
|
case 'flight':
|
|
return Icons.flight_takeoff_outlined;
|
|
case 'train':
|
|
return Icons.train_outlined;
|
|
case 'bus':
|
|
return Icons.directions_bus;
|
|
case 'taxi':
|
|
return Icons.local_taxi_outlined;
|
|
case 'accomodation':
|
|
return Icons.local_hotel_outlined;
|
|
case 'forex':
|
|
return Icons.attach_money_outlined;
|
|
case 'insurance':
|
|
return Icons.list_alt_outlined;
|
|
case 'visa':
|
|
return Icons.badge_outlined;
|
|
case 'miscellaneous':
|
|
return Icons.card_giftcard_outlined;
|
|
default:
|
|
return Icons.circle_notifications;
|
|
}
|
|
}
|
|
|
|
Widget _buildFirstRow(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildFirstNameField(),
|
|
Spacer(),
|
|
buildLastNameField(),
|
|
Spacer(), // Space after Last Name
|
|
buildCompanyNameField(),
|
|
Spacer(),
|
|
// buildGenderField(),
|
|
// SizedBox(width: 15),
|
|
// buildDobField(),
|
|
buildMobileField(),
|
|
Spacer(),
|
|
buildAlternateMobileField(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildFirstNameField(),
|
|
SizedBox(height: 8), // Vertical space
|
|
buildLastNameField(),
|
|
SizedBox(height: 8),
|
|
buildCompanyNameField(),
|
|
SizedBox(height: 8),
|
|
// buildGenderField(),
|
|
// SizedBox(height: 8),
|
|
// buildDobField(),
|
|
buildMobileField(),
|
|
SizedBox(height: 8), // Space after Last Name
|
|
buildAlternateMobileField(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// Widget _buildSecondRow(bool isDesktop) {
|
|
// return Container(
|
|
// color: Colors.white,
|
|
// child:
|
|
// widget.isDesktop
|
|
// ? Row(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// // buildEmailField(),
|
|
// // Spacer(),
|
|
// // buildMobileField(),
|
|
// // Spacer(), // Space after Last Name
|
|
// // buildAlternateMobileField(),
|
|
// ],
|
|
// )
|
|
// : Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// // buildMobileField(),
|
|
// // SizedBox(height: 8), // Vertical space
|
|
// // buildAlternateMobileField(),
|
|
// // SizedBox(height: 8),
|
|
// // buildEmailField(),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
|
|
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
|
|
? !widget.apiselectedUser
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (!widget.apiselectedUser) ...[
|
|
buildEmailField(),
|
|
// SizedBox(
|
|
// width: MediaQuery.of(context).size.width * 0.014,
|
|
// ),
|
|
Spacer(),
|
|
buildPassword(),
|
|
// SizedBox(
|
|
// width: MediaQuery.of(context).size.width * 0.015,
|
|
// ),
|
|
Spacer(),
|
|
buildRole(),
|
|
],
|
|
],
|
|
)
|
|
: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildEmailField(),
|
|
SizedBox(width: 10),
|
|
// Spacer(),
|
|
// Container(
|
|
// width:
|
|
// isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.25
|
|
// : MediaQuery.of(context).size.width * 0.8,
|
|
// child: Row(
|
|
// mainAxisAlignment: MainAxisAlignment.end,
|
|
// children: [
|
|
// TextButton(
|
|
// onPressed: () => _openPopup(),
|
|
// style: TextButton.styleFrom(
|
|
// foregroundColor: Color(0xFF114D8B),
|
|
// textStyle: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// fontWeight: FontWeight.w400,
|
|
// ),
|
|
// ),
|
|
// child: Text("Change Password"),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
buildchangPswd(),
|
|
// SizedBox(height: 1),
|
|
Spacer(),
|
|
buildRole(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (!widget.apiselectedUser) ...[
|
|
buildEmailField(),
|
|
SizedBox(height: 8),
|
|
buildPassword(),
|
|
SizedBox(height: 8),
|
|
buildRole(),
|
|
] else ...[
|
|
Container(
|
|
width:
|
|
isDesktop
|
|
? MediaQuery.of(context).size.width * 0.25
|
|
: MediaQuery.of(context).size.width * 0.8,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
TextButton(
|
|
onPressed: () => _openPopup(),
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: Color(0xFF114D8B),
|
|
textStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
child: Text("Change Password"),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 1),
|
|
buildRole(),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildForthRow(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildAddress(),
|
|
Spacer(),
|
|
// SizedBox(width: 15),
|
|
buildCountryField(),
|
|
Spacer(),
|
|
// SizedBox(width: 15),
|
|
buildPostalCodeField(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildAddress(),
|
|
SizedBox(height: 8), //
|
|
buildCountryField(),
|
|
SizedBox(height: 8), // Vertical space
|
|
buildPostalCodeField(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildFirstNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"First Name *",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["FnameFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["FnameFocusNode"],
|
|
controller: widget.controllers["Fname"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("first_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "FirstName",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["first_name"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["first_name"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildLastNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Last Name*",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["LnameFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: TextStyle(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["LnameFocusNode"],
|
|
controller: widget.controllers["Lname"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("last_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "LastName",
|
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["last_name"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["last_name"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildCompanyNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Company Name",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["CnameFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: TextStyle(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["CnameFocusNode"],
|
|
controller: widget.controllers["Cname"],
|
|
enabled: !widget.isViewMode,
|
|
decoration: InputDecoration(
|
|
labelText: "CompanyName",
|
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildGenderField() {
|
|
List<String> genderOptions = ["Male", "Female"];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Gender",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownSearch<String>(
|
|
selectedItem: selectedGender,
|
|
enabled: !widget.isViewMode,
|
|
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: false, // Set true if you want search
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 200),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Container(
|
|
color: Colors.white,
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 6,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
),
|
|
items: genderOptions,
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
|
),
|
|
),
|
|
dropdownBuilder:
|
|
(context, selectedItem) => Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Gender",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (String? newValue) {
|
|
setState(() {
|
|
selectedGender = newValue;
|
|
print("selectedGender - $selectedGender");
|
|
});
|
|
widget.onGenderChanged?.call(newValue);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildDobField() {
|
|
DateTime? _selectedDateOfBirth;
|
|
|
|
Future<void> _selectCheckDateOfBirth(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate:
|
|
_selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today)
|
|
? _selectedDateOfBirth!
|
|
: today,
|
|
firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
lastDate: today,
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
if (pickedDate != null && pickedDate != _selectedDateOfBirth) {
|
|
setState(() {
|
|
_selectedDateOfBirth = pickedDate;
|
|
widget.controllers["dob"]?.text = DateFormat(
|
|
'dd-MM-yyyy',
|
|
).format(pickedDate);
|
|
});
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Date of Birth",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap:
|
|
widget.isViewMode
|
|
? null
|
|
: () async {
|
|
await _selectCheckDateOfBirth(context);
|
|
if (widget.controllers["dob"]!.text.isNotEmpty) {
|
|
setState(() {
|
|
// errorMessages.remove("start_date");
|
|
});
|
|
}
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
controller: widget.controllers["dob"],
|
|
readOnly: true,
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
decoration: InputDecoration(
|
|
labelText: "Select Date",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
suffixIcon: Icon(
|
|
Icons.calendar_today,
|
|
size: 16,
|
|
color: Color(0xFF8B8FB2),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildchangPswd() {
|
|
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(
|
|
width:
|
|
isDesktop
|
|
? MediaQuery.of(context).size.width * 0.25
|
|
: MediaQuery.of(context).size.width * 0.8,
|
|
child: Column(
|
|
// mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 30),
|
|
TextButton(
|
|
onPressed: () => _openPopup(),
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: Color(0xFF114D8B),
|
|
textStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
child: Text("Change Password"),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildEmailField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Email *",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["emailFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
focusNode: focusNodes["emailFocusNode"],
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: widget.controllers["email"],
|
|
onChanged: (value) {
|
|
_clearError("email");
|
|
apiCheckDuplicate("Email", "email", value, widget.userIdApi);
|
|
},
|
|
enabled: !widget.isViewMode,
|
|
decoration: InputDecoration(
|
|
labelText: "Email",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["email"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["email"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildMobileField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Mobile Number *",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["mobileNumberFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["mobileNumberFocusNode"],
|
|
controller: widget.controllers["mobileNumber"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("mobile_no");
|
|
apiCheckDuplicate(
|
|
"Mobile Number",
|
|
"mobile_no",
|
|
value,
|
|
widget.userIdApi,
|
|
);
|
|
},
|
|
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(
|
|
RegExp(r'^\d*\.?\d*$'),
|
|
), // Allow only positive numbers with optional decimal
|
|
],
|
|
decoration: InputDecoration(
|
|
labelText: "Mobile Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["mobile_no"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["mobile_no"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildAlternateMobileField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Alternate Mobile Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
// isFocused: false,
|
|
isFocused: focusStates["alternateMobileFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.12
|
|
: null,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
focusNode: focusNodes["alternateMobileFocusNode"],
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: widget.controllers["alternateMobile"],
|
|
enabled: !widget.isViewMode,
|
|
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(
|
|
RegExp(r'^\d*\.?\d*$'),
|
|
), // Allow only positive numbers with optional decimal
|
|
],
|
|
decoration: InputDecoration(
|
|
labelText: "Alternate Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["alternate_mobile_no"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["alternate_mobile_no"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildCountryField() {
|
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
|
late List<String> countryCodes; // List of country codes
|
|
List<dynamic> countryList = apiCountryData ?? [];
|
|
|
|
countryList = apiCountryData ?? [];
|
|
|
|
// Map country codes to country names
|
|
// countryMap = {
|
|
// for (var item in countryList)
|
|
// item['country_code'] as String: item['country_name'] as String,
|
|
// // item['currency'] as String
|
|
// };
|
|
countryMap = {
|
|
for (var country in countryList)
|
|
(country['country_code'] ?? ''):
|
|
'${country['country_name'] ?? ''} (${country['country_code'] ?? ''})',
|
|
};
|
|
|
|
// Extract only country codes for processing
|
|
countryCodes = countryMap.keys.toList();
|
|
|
|
selectedCountry ??= null;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Country",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["countryFocusNode"] ?? false,
|
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
width: double.infinity,
|
|
child: Focus(
|
|
focusNode: focusNodes["countryFocusNode"],
|
|
onFocusChange: (hasFocus) {
|
|
setState(() {
|
|
focusStates["countryFocused"] = hasFocus;
|
|
});
|
|
},
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// Request focus when user taps
|
|
focusNodes["countryFocusNode"]?.requestFocus();
|
|
},
|
|
child: DropdownSearch<String>(
|
|
selectedItem: countryMap[selectedCountry],
|
|
enabled: !widget.isViewMode,
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true, // Enables search functionality
|
|
fit: FlexFit.loose, // Allows flexible height
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
itemBuilder:
|
|
// (context, item, isSelected) => Container(
|
|
// padding: EdgeInsets.symmetric(
|
|
// horizontal: 10,
|
|
// vertical: 6,
|
|
// ),
|
|
// child: Text(
|
|
// item,
|
|
// style: GoogleFonts.poppins(fontSize: 11.5),
|
|
// ),
|
|
// ),
|
|
(context, item, isSelected) {
|
|
print("contryItem - $item");
|
|
final match = RegExp(
|
|
r'^(.*)\s\((.*)\)$',
|
|
).firstMatch(item);
|
|
final countryName = match?.group(1) ?? '';
|
|
final countryCode = match?.group(2) ?? '';
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10.0,
|
|
vertical: 6.0,
|
|
),
|
|
child: Padding(
|
|
// padding: const EdgeInsets.all(3.0),
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 2,
|
|
vertical: 3,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
countryName,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
Text(
|
|
countryCode,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11.5,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
constraints: BoxConstraints(maxHeight: 180),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Country...",
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 3,
|
|
vertical: 3,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
items: countryMap.values.toList(),
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
// border: InputBorder.none,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide(
|
|
color:
|
|
(focusStates["countryFocused"] ?? false)
|
|
? (layoutColor ?? Colors.blue)
|
|
: Colors.white,
|
|
// width: 0.5,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
(focusStates["countryFocused"] ?? false)
|
|
? (layoutColor ?? Colors.blue)
|
|
: Colors.white,
|
|
// width: 0.5,
|
|
// const Color(0xFFD6D5E6),
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: (layoutColor ?? Colors.grey),
|
|
width: 1,
|
|
),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 10.0,
|
|
vertical: 8.0,
|
|
),
|
|
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
|
),
|
|
),
|
|
dropdownBuilder:
|
|
(context, selectedItem) => Align(
|
|
// Center-align selected item
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Country",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
|
|
// onChanged: widget.isViewMode
|
|
// ? null
|
|
// : (String? newValue) {
|
|
// setState(() {
|
|
// // Find the country_code based on selected country_name
|
|
// selectedCountry = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
//
|
|
// // if (selectedCountry!.isNotEmpty) {
|
|
// // errorMessages.remove("country_code");
|
|
// // }
|
|
// });
|
|
// widget.onCountryChanged?.call(newValue);
|
|
// },
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (String? newValue) {
|
|
if (newValue == null) return;
|
|
|
|
final countryCode =
|
|
countryMap.entries
|
|
.firstWhere(
|
|
(entry) => entry.value == newValue,
|
|
)
|
|
.key;
|
|
|
|
setState(() {
|
|
selectedCountry = countryCode;
|
|
});
|
|
|
|
widget.onCountryChanged?.call(
|
|
countryCode,
|
|
); // ✅ send code not name
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildPostalCodeField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Postal Code",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["postalCodeFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
focusNode: focusNodes["postalCodeFocusNode"],
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: widget.controllers["postalCode"],
|
|
enabled: !widget.isViewMode,
|
|
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(
|
|
RegExp(r'^\d*\.?\d*$'),
|
|
), // Allow only positive numbers with optional decimal
|
|
],
|
|
decoration: InputDecoration(
|
|
labelText: "Postal Code",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildRole() {
|
|
Map<String, String> roleMap = {
|
|
for (var item in apiRoleData ?? [])
|
|
item['dropdown_key'] as String: item['dropdown_value'] as String,
|
|
};
|
|
|
|
// List<String> roleNames = roleMap.values.toList();
|
|
List<String> roleNames = roleMap.values.take(5).toList();
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Role",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownSearch<String>(
|
|
selectedItem: roleMap[selectedRole],
|
|
enabled: false, //enabled: !widget.isViewMode,
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true,
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Container(
|
|
color: Colors.white,
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 6,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Role...",
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
|
),
|
|
),
|
|
),
|
|
items: roleNames,
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
|
),
|
|
),
|
|
dropdownBuilder:
|
|
(context, selectedItem) => Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Role",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (String? newValue) {
|
|
widget.errorMessages.remove("role_id");
|
|
if (newValue == null) return;
|
|
final selectedKey =
|
|
roleMap.entries
|
|
.firstWhere((entry) => entry.value == newValue)
|
|
.key;
|
|
|
|
setState(() {
|
|
selectedRole = selectedKey;
|
|
isTravelAgent = selectedKey == "5";
|
|
});
|
|
widget.onUserTypeChanged?.call(isTravelAgent);
|
|
widget.onRoleChanged?.call(selectedKey);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["role_id"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["role_id"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildAddress() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Address",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["addressFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 80,
|
|
child: TextField(
|
|
focusNode: focusNodes["addressFocusNode"],
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: widget.controllers["address"],
|
|
enabled: !widget.isViewMode,
|
|
maxLines: 2,
|
|
keyboardType: TextInputType.multiline,
|
|
decoration: InputDecoration(
|
|
labelText: "Address",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildPassword() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Password *",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserWrapper(
|
|
isFocused: focusStates["passwordFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
focusNode: focusNodes["passwordFocusNode"],
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: widget.controllers["password"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("password");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Password",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (widget.errorMessages["password"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
widget.errorMessages["password"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
// apiselectedUser != null
|
|
// ? SizedBox()
|
|
}
|