ts-tat/lib/Screens/dialog/user_selection_dialog.dart
2025-04-19 17:40:19 +05:30

687 lines
23 KiB
Dart

import 'dart:convert';
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 Color layoutColorForUser;
final void Function(String, String, bool) onSubmit;
UserSelectionDialog({
Key? key,
required this.title,
required this.layoutColorForUser,
required this.onSubmit,
}) : 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);
});
print("Users fetched: ${_users.length}");
for (var user in _users) {
print("${user.firstName} ${user.lastName}");
}
} 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 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);
});
print("Users fetched: ${_users.length}");
for (var travvelr in _traveller) {
print("${travvelr.firstName} ${travvelr.lastName}");
}
} 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.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 _filterUsersTravellers...");
setState(() {
_filteredList.clear(); // Reset the list before filtering
if (query.isEmpty) {
_filteredList = [
..._users.map((user) => {"type": "user", "data": user}),
];
} 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}),
];
}
});
print("Filtered List:");
for (var item in _filteredList) {
var user = item["data"];
print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
}
}
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();
fetchUsers();
fetchTraveller();
}
@override
Widget build(BuildContext context) {
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: [
Text("Please Select User", style: TextStyle(fontSize: 14)),
SizedBox(height: 10),
// Search Field
TextField(
controller: _searchController,
onChanged: (query) {
setState(() {
_showTravellerForm = false;
});
widget.title == "Others"
? _filterUsersTravellers(query)
: _filterUsers(query);
},
decoration: InputDecoration(
hintText: "Search for a user",
hintStyle: TextStyle(fontSize: 14),
prefixIcon: Icon(Icons.search),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 1),
// borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blueAccent, width: 2),
),
),
),
SizedBox(height: 10),
if (widget.title == "Others") ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("or create a new traveler",
style: TextStyle(fontSize: 14, color: Color(0xFF575A74))),
TextButton(
onPressed: () {
setState(() {
_showTravellerForm = true;
_searchController.clear();
});
},
child: Text("Create",
style: TextStyle(
fontSize: 14, color: widget.layoutColorForUser)),
),
],
),
],
SizedBox(height: 5),
// User List or Message
_searchController.text.isNotEmpty
? SizedBox(
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child: _filteredList.isEmpty
? Center(
child: Text(
"No users found",
style:
TextStyle(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"
return ListTile(
title: Text(
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
subtitle: Text(
"ID: ${userType == "user" ? user.userId : user.travellerId}"),
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: () => Navigator.pop(context),
child: Text(
"Cancel",
),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColorForUser,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: widget.layoutColorForUser, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
print(
"Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(
_searchController.text, userIdSelected, isTraveller);
Navigator.pop(context);
},
child: Text("Submit"),
),
],
),
],
),
),
);
}
}
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) {
if (response.statusCode == 200 || response.statusCode == 201) {
final Map<String, dynamic> responseData =
jsonDecode(response.body); // Parse response
if (responseData["success"] == true &&
responseData.containsKey("data")) {
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);
// Close the dialog
Navigator.pop(context);
}
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Traveller added successfully!",
style: TextStyle(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 Traveler", style: TextStyle(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: TextStyle(color: Colors.grey)),
),
TextButton(
onPressed: () => _onSubmit(context),
child:
Text("Add", style: TextStyle(color: Colors.blueAccent)),
),
],
),
],
),
);
},
);
}
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,
),
];
}
}