ts-tat/lib/Screens/dialog/user_selection_dialog.dart
venba-Inspriron-3558 179e1db5d7 minor issues fixes 6
2025-06-25 18:28:58 +05:30

893 lines
30 KiB
Dart

import 'dart:convert';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../data/models/Searchtraveller.dart';
import '../../data/models/searchUser.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_traveller.dart';
class UserSelectionDialog extends StatefulWidget {
final String title;
final String? currentUser;
final Color layoutColorForUser;
final void Function(String, String, bool) onSubmit;
final void Function() onClose;
UserSelectionDialog({
Key? key,
required this.title,
required this.layoutColorForUser,
required this.onSubmit,
required this.onClose,
required this.currentUser,
}) : super(key: key);
@override
_UserSelectionDialogState createState() => _UserSelectionDialogState();
}
class _UserSelectionDialogState extends State<UserSelectionDialog> {
TextEditingController _controller = TextEditingController();
TextEditingController _searchController = TextEditingController();
// List<String> _filteredUsers = [];
// List<String> _users = [];
List<SearchUser> _users = [];
List<SearchTraveler> _traveller = [];
List<SearchUser> _filteredUsers = [];
List<Map<String, dynamic>> _filteredList = [];
List<SearchTraveler> _filteredTraveller = [];
String userIdSelected = " ";
bool isTraveller = false;
bool _showTravellerForm = false;
String? orgId;
final _formKey = GlobalKey<FormState>();
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<void> fetchUsers() async {
orgId = await getOrgId();
// final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
final String apiUrldata = '$apiUrl/api/users?org_id=$orgId';
try {
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) {
final Map<String, dynamic> responseBody = json.decode(response.body);
print("API Response: $responseBody"); // Debugging
if (responseBody.containsKey('data') && responseBody['data'] is List) {
List<dynamic> userList = responseBody['data'];
// Extract first name and last name correctly
// setState(() {
// _users = userList
// .where((user) => user['first_name'] != null && user['last_name'] != null)
// .map((user) => "${user['first_name']} ${user['last_name']}")
// .toList();
// _filteredUsers = List.from(_users);
// });
setState(() {
_users = userList.map((user) => SearchUser.fromJson(user)).toList();
_filteredUsers = List.from(_users);
_filterUsers("");
});
print("filtered user === ${_filteredUsers.length}");
print("Users fetched: ${_users.length}");
for (var user in _users) {
print("${user.firstName} ${user.lastName} ${user.empCode}");
}
} else {
throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}",
);
}
} else {
throw Exception(
'Failed to load users. Status Code: ${response.statusCode}',
);
}
} catch (e) {
print("Error fetching users: $e");
}
}
Future<void> fetchTraveller() async {
orgId = await getOrgId();
final String apiUrldata = '$apiUrl/api/travellers?org_id=$orgId';
try {
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) {
final Map<String, dynamic> responseBody = json.decode(response.body);
print("API Traveller Response: $responseBody"); // Debugging
if (responseBody.containsKey('data') && responseBody['data'] is List) {
List<dynamic> travellerList = responseBody['data'];
setState(() {
_traveller =
travellerList
.map((user) => SearchTraveler.fromJson(user))
.toList();
_filteredTraveller = List.from(_traveller);
_filterTravellers("");
});
print("Users fetched: ${_users.length}");
for (var travvelr in _traveller) {
print(
"${travvelr.firstName} ${travvelr.lastName} ${travvelr.mobileNo}",
);
}
} else {
throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}",
);
}
} else {
throw Exception(
'Failed to load users. Status Code: ${response.statusCode}',
);
}
} catch (e) {
print("Error fetching traveller: $e");
}
}
// void _filterUsers1(String query) {
// print("Filtering users...");
// setState(() {
// if (query.isEmpty) {
// _filteredUsers = List.from(_users);
// } else {
// _filteredUsers =
// _users.where((user) {
// List<String> searchFields = [
// "${user.firstName} ${user.lastName}".toLowerCase(),
// user.email.toLowerCase() ?? "",
// user.empCode?.toLowerCase() ?? "",
// user.userId.toLowerCase() ?? "",
// user.mobileNo ?? "",
// user.alternateMobileNo ?? "",
// ];
//
// return searchFields.any(
// (field) => field.contains(query.toLowerCase()),
// );
// }).toList();
// }
// });
//
// print("Filtered Users:");
// for (var user in _filteredUsers) {
// print("${user.firstName} ${user.lastName}");
// }
// }
void _filterUsers(String query) {
print("Filtering _filterUsers... ${_filteredUsers.length}");
setState(() {
_filteredList.clear(); // Reset the list before filtering
final excludedUserId = widget.currentUser.toString();
if (query.isEmpty) {
// ✅ Return all users excluding self and roleId == 5
_filteredList = _users.where((user) =>
user.userId.toString() != excludedUserId && user.roleId.toString() != "5")
.map((user) => {"type": "user", "data": user})
.toList();
// _filteredList = [
// // ..._users.map((user) => {"type": "user", "data": user}),
// ..._users
// .where(
// (user) =>
// (user.userId.toString() == excludedUserId &&
// user.roleId.toString() == "5"),
// ) // ⛔ Exclude user ID 4
// .map((user) => {"type": "user", "data": user}),
// ];
} else {
_filteredList = [
..._users
.where((user) {
print("usersLLL : ${user}");
if (user.userId.toString() == excludedUserId ||
user.roleId.toString() == "5")
return false;
List<String> searchFields = [
"${user.firstName} ${user.lastName}".toLowerCase(),
user.email.toLowerCase() ?? "",
user.userId.toLowerCase() ?? "",
user.mobileNo ?? "",
user.alternateMobileNo ?? "",
user.empCode?.toLowerCase() ?? "",
];
return searchFields.any(
(field) => field.contains(query.toLowerCase()),
);
})
.map((user) => {"type": "user", "data": user}),
];
}
});
// setState(() {
// print("Called 2.1");
// print("h1 - ${_filteredUsers}");
// if (query.isEmpty) {
// _filteredList = _filteredUsers
// .map((user) => {"type": "user", "data": user})
// .toList();
// }
//
// });
for (var item in _filteredList) {
var user = item["data"];
print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
}
}
void _filterTravellers(String query) {
print("Filtering _filterTravellers...");
setState(() {
_filteredList.clear(); // Reset the list before filtering
if (query.isEmpty) {
_filteredList = _traveller
.map((traveller) => {"type": "traveller", "data": traveller})
.toList();
// _filteredList = [
// ..._users.map((user) => {"type": "user", "data": user}),
// ];
} else {
_filteredList = [
..._traveller
.where((traveller) {
List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(),
traveller.email.toLowerCase() ?? "",
traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "",
];
return searchFields.any(
(field) => field.contains(query.toLowerCase()),
);
})
.map((traveller) => {"type": "traveller", "data": traveller}),
];
}
});
print("Filtered List:");
for (var item in _filteredList) {
var user = item["data"];
print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
}
}
void _filterByTitle(String query) {
print("filtered by title");
if (widget.title == "Others (Non Employee)") {
_filterTravellers(query);
} else {
_filterUsers(query);
}
}
void _filterUsersTravellers(String query) {
print("Filtering _filterUsersTravellers...");
setState(() {
_filteredList.clear(); // Reset the list before filtering
if (query.isEmpty) {
_filteredList = [
..._users.map((user) => {"type": "user", "data": user}),
..._traveller.map(
(traveller) => {"type": "traveller", "data": traveller},
),
];
} else {
_filteredList = [
..._users
.where((user) {
List<String> searchFields = [
"${user.firstName} ${user.lastName}".toLowerCase(),
user.email.toLowerCase() ?? "",
user.userId.toLowerCase() ?? "",
user.mobileNo ?? "",
user.alternateMobileNo ?? "",
];
return searchFields.any(
(field) => field.contains(query.toLowerCase()),
);
})
.map((user) => {"type": "user", "data": user}),
..._traveller
.where((traveller) {
List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(),
traveller.email.toLowerCase() ?? "",
traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "",
];
return searchFields.any(
(field) => field.contains(query.toLowerCase()),
);
})
.map((traveller) => {"type": "traveller", "data": traveller}),
];
}
});
print("Filtered List:");
for (var item in _filteredList) {
var user = item["data"];
print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}",
);
}
}
@override
void initState() {
super.initState();
if (widget.title == "Others (Non Employee)"){ fetchTraveller(); }
else{ fetchUsers(); }
print("SelffsdfcurrentUser - ${widget.currentUser}");
_filterByTitle("");
}
@override
Widget build(BuildContext context) {
final isValid = _searchController.text.trim().isNotEmpty &&
userIdSelected.trim().isNotEmpty;
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Container(
color: Colors.white,
width: 400, // Adjust width as needed
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize:
MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [
widget.title == "Others (Non Employee)"
? Text(
"Please Select Other User",
style: GoogleFonts.poppins(fontSize: 14),
)
: Text(
"Please Select Other Employee",
style: GoogleFonts.poppins(fontSize: 14),
),
SizedBox(height: 10),
// Search Field
TextField(
controller: _searchController,
onChanged: (query) {
setState(() {
_showTravellerForm = false;
});
widget.title == "Others (Non Employee)"
? _filterTravellers(query)
: _filterUsers(query);
},
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
hintText: "Search for a user",
hintStyle: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey.shade200, width: 1),
// borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.grey.shade200, width: 1),
),
),
),
SizedBox(height: 10),
if (widget.title == "Others (Non Employee)") ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"or create a new traveller",
style: GoogleFonts.poppins(
fontSize: 14,
color: Color(0xFF575A74),
),
),
TextButton(
onPressed: () {
setState(() {
_showTravellerForm = true;
_searchController.clear();
});
},
child: Text(
"Create",
style: GoogleFonts.poppins(
fontSize: 14,
color: widget.layoutColorForUser,
),
),
),
],
),
],
SizedBox(height: 5),
// User List or Message
// _searchController.text.isNotEmpty
// ?
if (!_showTravellerForm)
SizedBox(
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child:
_filteredList.isEmpty
? Center(
child: Text(
"No users found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: ListView.builder(
// itemCount: _filteredUsers.length,
itemCount: _filteredList.length,
itemBuilder: (context, index) {
// final user = _filteredUsers[index];
final item = _filteredList[index];
final user = item["data"]; // Extract user object
final userType =
item["type"]; // "user" or "traveller"
if (user is Map<String, dynamic>) {
print("userLsirer - ${jsonEncode(user)}"); // pretty JSON-like string
} else {
print("userLsirer - $user"); // fallback
}
final isSelected = userIdSelected == (userType == "user" ? user.userId : user.travellerId);
return ListTile(
// hoverColor: ,
title: Text(
"${user.firstName ?? "Unknown"}" "${(user.lastName?.isNotEmpty ?? false) ? " ${user.lastName}" : ""}",
style: GoogleFonts.poppins(fontSize: 11 , color: isSelected ? widget.layoutColorForUser : Colors.black),
),
subtitle:
userType == "user"
? Text(
"Employee ID: ${user.empCode ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color: isSelected ? widget.layoutColorForUser : Colors.black,
),
)
: Text(
"Mobile : ${user.mobileNo ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color: isSelected ? widget.layoutColorForUser : Colors.black,
),
),
onTap: () {
String selectedUser =
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
userIdSelected =
userType == "user"
? user.userId
: user.travellerId;
isTraveller = userType == "traveller";
});
print(
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected",
);
},
);
},
),
),
// : SizedBox.shrink(),
// Traveler Form
if (_showTravellerForm)
Container(
height: 300, // Set a defined height
// color: Color(0xFFF4F4FB),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TravelerForm(
formKey: _formKey,
onSubmit: (
String fullName,
String travellerId,
bool isTraveller,
) {
widget.onSubmit(
fullName,
travellerId,
isTraveller,
); // Pass the data up
},
firstNameController: TextEditingController(),
lastNameController: TextEditingController(),
emailController: TextEditingController(),
mobileController: TextEditingController(),
orgId: orgId,
),
),
),
// Actions
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: widget.layoutColorForUser,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: widget.layoutColorForUser,
width: 2,
),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
widget.onClose();
Navigator.pop(context);
},
child: Text(
"Cancel",
style: GoogleFonts.poppins(fontSize: 11),
),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isValid ? widget.layoutColorForUser : Colors.grey,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
// color: widget.layoutColorForUser,
color: isValid ? widget.layoutColorForUser : Colors.grey,
width: isValid ? 2 : 0,
),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: isValid ? () {
print("Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(
_searchController.text,
userIdSelected,
isTraveller,
);
Navigator.pop(context);
}
: null,
child: Text(
"Save",
style: GoogleFonts.poppins(fontSize: 11),
),
),
],
),
],
),
),
);
}
}
class TravelerForm extends StatefulWidget {
final TextEditingController firstNameController;
final TextEditingController lastNameController;
final TextEditingController emailController;
final TextEditingController mobileController;
final String? orgId;
final GlobalKey<FormState> formKey;
final void Function(String, String, bool) onSubmit;
TravelerForm({
required this.formKey,
required this.orgId,
required this.firstNameController,
required this.lastNameController,
required this.emailController,
required this.mobileController,
required this.onSubmit,
});
@override
_TravelerFormState createState() => _TravelerFormState();
}
class _TravelerFormState extends State<TravelerForm> {
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
bool _validateForm() {
if (widget.formKey.currentState != null) {
return widget.formKey.currentState!.validate();
}
return false; // Return false when form is not initialized
}
String? validateMobile(String? value) {
if (value == null || value.isEmpty) {
return 'Mobile number is required';
}
if (!RegExp(r'^[0-9]{10}$').hasMatch(value)) {
return 'Enter a valid 10-digit mobile number';
}
return null;
}
String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
).hasMatch(value)) {
return 'Enter a valid email address';
}
return null;
}
Future<void> _onSubmit(BuildContext context) async {
bool isValid = _validateForm();
print("Form Validation Result: $isValid");
if (isValid) {
print("Validation Success");
_submitForm(context);
} else {
print("Validation Failed"); // This should now print if validation fails
}
}
Future<void> _submitForm(BuildContext context) async {
Map<String, String> requestBody = {
"org_id": widget.orgId!,
"first_name": widget.firstNameController.text,
"last_name": widget.lastNameController.text,
"email": widget.emailController.text,
"mobile": widget.mobileController.text,
};
String apiUrlData = '$apiUrl/api/travellers/create';
try {
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
print("response1");
var response = await http.post(
Uri.parse(apiUrlData),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(requestBody),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData.containsKey("data")) {
// Parse response
print("responseData- $responseData ");
final travellerData = responseData["data"];
String travellerId = travellerData["traveller_id"];
String firstName = travellerData["first_name"];
String lastName = travellerData["last_name"];
print(
"Traveller Added: ID: $travellerId, Name: $firstName $lastName",
);
// // Pass data to callback
// widget.onSubmit("$firstName $lastName", travellerId, true);
String fullName = "${firstName.trim()} ${lastName.trim()}";
widget.onSubmit(fullName, travellerId, true);
// Close the dialog
Navigator.pop(context);
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Traveller added successfully!",
style: GoogleFonts.poppins(
color: Colors.white,
), // ✅ Set text color
),
backgroundColor: Colors.green,
),
);
} else {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Error: ${response.body}")));
}
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Failed to connect to server.")));
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
double widthFactor;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) {
widthFactor = 0.23;
} else if (sizingInfo.deviceScreenType == DeviceScreenType.tablet) {
widthFactor = 0.8;
} else {
widthFactor = 1.0;
}
return Form(
key: widget.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Create Traveller",
style: GoogleFonts.poppins(color: Colors.black54),
),
SizedBox(height: 7),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: _buildFormFirstRow(isDesktop)),
),
),
SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
widget.formKey.currentState?.reset();
},
child: Text(
"Clear",
style: GoogleFonts.poppins(color: Colors.grey),
),
),
TextButton(
onPressed: () => _onSubmit(context),
child: Text(
"Add",
style: GoogleFonts.poppins(color: Color(0xFF114D8B)),
),
),
],
),
],
),
);
},
);
}
List<Widget> _buildFormFirstRow(bool isDesktop) {
return [
CustomTextField(
controller: widget.firstNameController,
labelText: "First Name",
validator: (value) => value!.isEmpty ? "Enter first name" : null,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.lastNameController,
labelText: "Last Name",
validator: (value) => value!.isEmpty ? "Enter last name" : null,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.emailController,
labelText: "Email",
keyboardType: TextInputType.emailAddress,
validator: validateEmail,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.mobileController,
labelText: "Mobile Number",
keyboardType: TextInputType.phone,
validator: validateMobile,
),
];
}
}