OrgLevel Data, UserDetails

This commit is contained in:
venbaittech 2025-04-08 16:14:01 +05:30
parent 1f28816021
commit 886bef2baa
24 changed files with 3302 additions and 2972 deletions

View File

@ -4,23 +4,27 @@ import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart'; import '../../config/apiUrl.dart';
import '../../data/models/Searchtraveller.dart'; import '../../data/models/Searchtraveller.dart';
import '../../data/models/searchUser.dart'; import '../../data/models/searchUser.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_traveller.dart'; import '../../widgets/custom_text_traveller.dart';
class UserSelectionDialog extends StatefulWidget{ class UserSelectionDialog extends StatefulWidget {
final String title; final String title;
final void Function(String,String, bool) onSubmit; final void Function(String, String, bool) onSubmit;
UserSelectionDialog({Key? key, required this.title, required this.onSubmit,}) : super(key: key); UserSelectionDialog({
Key? key,
required this.title,
required this.onSubmit,
}) : super(key: key);
@override @override
_UserSelectionDialogState createState() => _UserSelectionDialogState(); _UserSelectionDialogState createState() => _UserSelectionDialogState();
} }
class _UserSelectionDialogState extends State<UserSelectionDialog>{ class _UserSelectionDialogState extends State<UserSelectionDialog> {
TextEditingController _controller = TextEditingController(); TextEditingController _controller = TextEditingController();
TextEditingController _searchController = TextEditingController(); TextEditingController _searchController = TextEditingController();
// List<String> _filteredUsers = []; // List<String> _filteredUsers = [];
@ -31,21 +35,23 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
List<Map<String, dynamic>> _filteredList = []; List<Map<String, dynamic>> _filteredList = [];
List<SearchTraveler> _filteredTraveller = []; List<SearchTraveler> _filteredTraveller = [];
String userIdSelected = " "; String userIdSelected = " ";
bool isTraveller = false;
bool isTraveller = false;
bool _showTravellerForm = false; bool _showTravellerForm = false;
final _formKey = GlobalKey<FormState>(); String? orgId;
final _formKey = GlobalKey<FormState>();
Future<String?> getToken() async { Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token'); return prefs.getString('auth_token');
} }
Future<void> fetchUsers() async { Future<void> fetchUsers() async {
final String apiUrldata = '$apiUrl/api/users'; orgId = await getOrgId();
// final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
final String apiUrldata = '$apiUrl/api/users?org_id=$orgId';
try { try {
final token = await getToken(); final token = await getToken();
@ -62,8 +68,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
final Map<String,dynamic> responseBody = json.decode(response.body); final Map<String, dynamic> responseBody = json.decode(response.body);
print("API Response: $responseBody"); // Debugging print("API Response: $responseBody"); // Debugging
@ -88,18 +93,69 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
for (var user in _users) { for (var user in _users) {
print("${user.firstName} ${user.lastName}"); print("${user.firstName} ${user.lastName}");
} }
} else { } else {
throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
} }
} else { } else {
throw Exception('Failed to load users. Status Code: ${response.statusCode}'); throw Exception(
'Failed to load users. Status Code: ${response.statusCode}');
} }
} catch (e) { } catch (e) {
print("Error fetching users: $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) { void _filterUsers1(String query) {
print("Filtering users..."); print("Filtering users...");
setState(() { setState(() {
@ -115,7 +171,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.alternateMobileNo ?? "" user.alternateMobileNo ?? ""
]; ];
return searchFields.any((field) => field.contains(query.toLowerCase())); return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).toList(); }).toList();
} }
}); });
@ -126,7 +183,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
} }
} }
void _filterUsers(String query) { void _filterUsers(String query) {
print("Filtering _filterUsersTravellers..."); print("Filtering _filterUsersTravellers...");
setState(() { setState(() {
@ -146,9 +202,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.mobileNo ?? "", user.mobileNo ?? "",
user.alternateMobileNo ?? "" user.alternateMobileNo ?? ""
]; ];
return searchFields.any((field) => field.contains(query.toLowerCase())); return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((user) => {"type": "user", "data": user}), }).map((user) => {"type": "user", "data": user}),
]; ];
} }
}); });
@ -156,7 +212,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
print("Filtered List:"); print("Filtered List:");
for (var item in _filteredList) { for (var item in _filteredList) {
var user = item["data"]; var user = item["data"];
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
} }
} }
@ -168,7 +225,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
if (query.isEmpty) { if (query.isEmpty) {
_filteredList = [ _filteredList = [
..._users.map((user) => {"type": "user", "data": user}), ..._users.map((user) => {"type": "user", "data": user}),
..._traveller.map((traveller) => {"type": "traveller", "data": traveller}), ..._traveller
.map((traveller) => {"type": "traveller", "data": traveller}),
]; ];
} else { } else {
_filteredList = [ _filteredList = [
@ -180,9 +238,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.mobileNo ?? "", user.mobileNo ?? "",
user.alternateMobileNo ?? "" user.alternateMobileNo ?? ""
]; ];
return searchFields.any((field) => field.contains(query.toLowerCase())); return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((user) => {"type": "user", "data": user}), }).map((user) => {"type": "user", "data": user}),
..._traveller.where((traveller) { ..._traveller.where((traveller) {
List<String> searchFields = [ List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(), "${traveller.firstName} ${traveller.lastName}".toLowerCase(),
@ -190,7 +248,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
traveller.travellerId.toLowerCase() ?? "", traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "", traveller.mobileNo ?? "",
]; ];
return searchFields.any((field) => field.contains(query.toLowerCase())); return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((traveller) => {"type": "traveller", "data": traveller}), }).map((traveller) => {"type": "traveller", "data": traveller}),
]; ];
} }
@ -199,58 +258,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
print("Filtered List:"); print("Filtered List:");
for (var item in _filteredList) { for (var item in _filteredList) {
var user = item["data"]; var user = item["data"];
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); print(
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
} }
} }
Future<void> fetchTraveller() async {
final String apiUrldata = '$apiUrl/api/travellers';
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");
}
}
@override @override
void initState() { void initState() {
@ -268,7 +279,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
width: 400, // Adjust width as needed width: 400, // Adjust width as needed
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily mainAxisSize:
MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [ children: [
Text("Please Select User", style: TextStyle(fontSize: 14)), Text("Please Select User", style: TextStyle(fontSize: 14)),
SizedBox(height: 10), SizedBox(height: 10),
@ -280,15 +292,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
setState(() { setState(() {
_showTravellerForm = false; _showTravellerForm = false;
}); });
widget.title == "Others"? _filterUsersTravellers(query): widget.title == "Others"
_filterUsers(query); ? _filterUsersTravellers(query)
: _filterUsers(query);
}, },
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search for a user", hintText: "Search for a user",
hintStyle: TextStyle(fontSize: 14), hintStyle: TextStyle(fontSize: 14),
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), border:
OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blueAccent, width: 2), borderSide: BorderSide(color: Colors.blueAccent, width: 2),
@ -301,7 +314,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text("or create a new traveler", style: TextStyle(fontSize: 14, color: Color(0xFF575A74))), Text("or create a new traveler",
style: TextStyle(fontSize: 14, color: Color(0xFF575A74))),
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
@ -309,7 +323,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
_searchController.clear(); _searchController.clear();
}); });
}, },
child: Text("Create", style: TextStyle(fontSize: 14, color: Colors.blueAccent)), child: Text("Create",
style:
TextStyle(fontSize: 14, color: Colors.blueAccent)),
), ),
], ],
), ),
@ -320,42 +336,51 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
// User List or Message // User List or Message
_searchController.text.isNotEmpty _searchController.text.isNotEmpty
? SizedBox( ? SizedBox(
height: 300, // Limit height to avoid overflow height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty // child: _filteredUsers.isEmpty
child: _filteredList.isEmpty child: _filteredList.isEmpty
? Center( ? Center(
child: Text( child: Text(
"No users found", "No users found",
style: TextStyle(fontSize: 14, color: Colors.grey), style:
), TextStyle(fontSize: 14, color: Colors.grey),
) ),
: ListView.builder( )
// itemCount: _filteredUsers.length, : ListView.builder(
itemCount: _filteredList.length, // itemCount: _filteredUsers.length,
itemBuilder: (context, index) { itemCount: _filteredList.length,
// final user = _filteredUsers[index]; itemBuilder: (context, index) {
// final user = _filteredUsers[index];
final item = _filteredList[index]; final item = _filteredList[index];
final user = item["data"]; // Extract user object final user = item["data"]; // Extract user object
final userType = item["type"]; // "user" or "traveller" final userType =
item["type"]; // "user" or "traveller"
return ListTile( return ListTile(
title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"), title: Text(
subtitle: Text("ID: ${userType == "user" ? user.userId : user.travellerId}"), "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
onTap: () { subtitle: Text(
String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"; "ID: ${userType == "user" ? user.userId : user.travellerId}"),
setState(() { onTap: () {
_searchController.text = selectedUser; String selectedUser =
userIdSelected = userType == "user" ? user.userId : user.travellerId; "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
isTraveller = userType == "traveller"; setState(() {
}); _searchController.text = selectedUser;
print("Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," userIdSelected = userType == "user"
" isTraveller: $userIdSelected"); ? user.userId
}, : user.travellerId;
); isTraveller = userType == "traveller";
}, });
), print(
) : SizedBox.shrink(), "Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected");
},
);
},
),
)
: SizedBox.shrink(),
// Traveler Form // Traveler Form
if (_showTravellerForm) if (_showTravellerForm)
@ -366,13 +391,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: TravelerForm( child: TravelerForm(
formKey: _formKey, formKey: _formKey,
onSubmit: (String fullName, String travellerId, bool isTraveller) { onSubmit: (String fullName, String travellerId,
widget.onSubmit(fullName, travellerId,isTraveller); // Pass the data up bool isTraveller) {
widget.onSubmit(fullName, travellerId,
isTraveller); // Pass the data up
}, },
firstNameController: TextEditingController(), firstNameController: TextEditingController(),
lastNameController: TextEditingController(), lastNameController: TextEditingController(),
emailController: TextEditingController(), emailController: TextEditingController(),
mobileController: TextEditingController(), mobileController: TextEditingController(),
orgId: orgId,
), ),
), ),
), ),
@ -392,7 +420,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: Text("Cancel",), child: Text(
"Cancel",
),
), ),
SizedBox(width: 10), SizedBox(width: 10),
ElevatedButton( ElevatedButton(
@ -406,8 +436,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { onPressed: () {
print("Submitting: ${_searchController.text}, ID: $userIdSelected"); print(
widget.onSubmit(_searchController.text,userIdSelected,isTraveller); "Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(
_searchController.text, userIdSelected, isTraveller);
Navigator.pop(context); Navigator.pop(context);
}, },
child: Text("Submit"), child: Text("Submit"),
@ -419,7 +451,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
), ),
); );
} }
} }
class TravelerForm extends StatefulWidget { class TravelerForm extends StatefulWidget {
@ -427,25 +458,24 @@ class TravelerForm extends StatefulWidget {
final TextEditingController lastNameController; final TextEditingController lastNameController;
final TextEditingController emailController; final TextEditingController emailController;
final TextEditingController mobileController; final TextEditingController mobileController;
final String? orgId;
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
final void Function(String, String, bool) onSubmit; final void Function(String, String, bool) onSubmit;
TravelerForm({ TravelerForm(
required this.formKey, {required this.formKey,
required this.firstNameController, required this.orgId,
required this.lastNameController, required this.firstNameController,
required this.emailController, required this.lastNameController,
required this.mobileController, required this.emailController,
required this.onSubmit required this.mobileController,
}); required this.onSubmit});
@override @override
_TravelerFormState createState() => _TravelerFormState(); _TravelerFormState createState() => _TravelerFormState();
} }
class _TravelerFormState extends State<TravelerForm> { class _TravelerFormState extends State<TravelerForm> {
Future<String?> getToken() async { Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token'); return prefs.getString('auth_token');
@ -472,27 +502,29 @@ class _TravelerFormState extends State<TravelerForm> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Email is required'; return 'Email is required';
} }
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(value)) { if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
.hasMatch(value)) {
return 'Enter a valid email address'; return 'Enter a valid email address';
} }
return null; return null;
} }
void _onSubmit(BuildContext context) { Future<void> _onSubmit(BuildContext context) async {
bool isValid = _validateForm(); bool isValid = _validateForm();
print("Form Validation Result: $isValid"); print("Form Validation Result: $isValid");
if (isValid) { if (isValid) {
print("Validation Success"); print("Validation Success");
_submitForm(context); _submitForm(context);
} else { } else {
print("Validation Failed"); // This should now print if validation fails print("Validation Failed"); // This should now print if validation fails
} }
} }
Future<void> _submitForm(BuildContext context) async { Future<void> _submitForm(BuildContext context) async {
Map<String, String> requestBody = { Map<String, String> requestBody = {
"org_id": widget.orgId!,
"first_name": widget.firstNameController.text, "first_name": widget.firstNameController.text,
"last_name": widget.lastNameController.text, "last_name": widget.lastNameController.text,
"email": widget.emailController.text, "email": widget.emailController.text,
@ -516,24 +548,24 @@ class _TravelerFormState extends State<TravelerForm> {
body: jsonEncode(requestBody), body: jsonEncode(requestBody),
); );
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
final Map<String, dynamic> responseData = jsonDecode(response.body); // Parse response final Map<String, dynamic> responseData =
if (responseData["success"] == true && responseData.containsKey("data")) { jsonDecode(response.body); // Parse response
if (responseData["success"] == true &&
responseData.containsKey("data")) {
final travellerData = responseData["data"]; final travellerData = responseData["data"];
String travellerId = travellerData["traveller_id"]; String travellerId = travellerData["traveller_id"];
String firstName = travellerData["first_name"]; String firstName = travellerData["first_name"];
String lastName = travellerData["last_name"]; String lastName = travellerData["last_name"];
print("Traveller Added: ID: $travellerId, Name: $firstName $lastName"); print(
"Traveller Added: ID: $travellerId, Name: $firstName $lastName");
// Pass data to callback // Pass data to callback
widget.onSubmit("$firstName $lastName", travellerId, true); widget.onSubmit("$firstName $lastName", travellerId, true);
// Close the dialog // Close the dialog
Navigator.pop(context); Navigator.pop(context);
} }
@ -542,10 +574,11 @@ class _TravelerFormState extends State<TravelerForm> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
"Traveller added successfully!", "Traveller added successfully!",
style: TextStyle(color: Colors.white), // Set text color style: TextStyle(color: Colors.white), // Set text color
),
backgroundColor: Colors.green,
), ),
backgroundColor: Colors.green,),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@ -564,7 +597,8 @@ class _TravelerFormState extends State<TravelerForm> {
return ResponsiveBuilder( return ResponsiveBuilder(
builder: (context, sizingInfo) { builder: (context, sizingInfo) {
double widthFactor; double widthFactor;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) { if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) {
widthFactor = 0.23; widthFactor = 0.23;
@ -599,7 +633,8 @@ class _TravelerFormState extends State<TravelerForm> {
), ),
TextButton( TextButton(
onPressed: () => _onSubmit(context), onPressed: () => _onSubmit(context),
child: Text("Add", style: TextStyle(color: Colors.blueAccent)), child:
Text("Add", style: TextStyle(color: Colors.blueAccent)),
), ),
], ],
), ),

View File

@ -6,15 +6,16 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class AccomodationScreen extends StatefulWidget { class AccomodationScreen extends StatefulWidget {
final Function(bool) onClose; // Callback function final Function(bool) onClose; // Callback function
final Function(Map<String,dynamic>) onSaveAccomadation; final Function(Map<String, dynamic>) onSaveAccomadation;
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
AccomodationScreen(
AccomodationScreen({ {required this.onClose,
required this.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser}); required this.onSaveAccomadation,
required this.selectedItem,
required this.loginUser});
@override @override
_AccomodationScreenState createState() => _AccomodationScreenState(); _AccomodationScreenState createState() => _AccomodationScreenState();
@ -31,7 +32,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
final FocusNode _checkOutTimeFocusNode = FocusNode(); final FocusNode _checkOutTimeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _destinationController = TextEditingController(); late TextEditingController _destinationController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _checkInController = TextEditingController(); late TextEditingController _checkInController = TextEditingController();
@ -59,11 +59,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, dynamic> get accomadationData { Map<String, dynamic> get accomadationData {
Map<String, dynamic> data = {
Map<String,dynamic> data ={
"destination_city": _destinationController.text, "destination_city": _destinationController.text,
"hotel_name": _hotelNameController.text, "hotel_name": _hotelNameController.text,
"checkin_date": _checkInController.text , "checkin_date": _checkInController.text,
"checkin_time": _checkInTimeController.text, "checkin_time": _checkInTimeController.text,
"checkout_date": _checkOutController.text, "checkout_date": _checkOutController.text,
"checkout_time": _checkOutTimeController.text, "checkout_time": _checkOutTimeController.text,
@ -73,9 +72,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["accomodation_id"] != null && widget.selectedItem?["accomodation_id"] != 0) { } else if (widget.selectedItem?["accomodation_id"] != null &&
widget.selectedItem?["accomodation_id"] != 0) {
data["accomodation_id"] = widget.selectedItem!["accomodation_id"]; data["accomodation_id"] = widget.selectedItem!["accomodation_id"];
} }
} }
@ -86,16 +87,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return TextEditingController(text: widget.selectedItem?[key] ?? ""); return TextEditingController(text: widget.selectedItem?[key] ?? "");
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocused = focus); _addFocusListener(
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); _destinationFocusNode, (focus) => _destinationFocused = focus);
_addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus); _addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
_addFocusListener(_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus); _addFocusListener(
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus); _addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
_addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus); _addFocusListener(
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city"); _destinationController = initController("destination_city");
@ -106,7 +110,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
_checkOutTimeController = initController("checkout_time"); _checkOutTimeController = initController("checkout_time");
_commentsController = initController("comments"); _commentsController = initController("comments");
_destinationController.addListener(() => _clearError("destination_city")); _destinationController.addListener(() => _clearError("destination_city"));
_hotelNameController.addListener(() => _clearError("hotel_name")); _hotelNameController.addListener(() => _clearError("hotel_name"));
_checkInController.addListener(() => _clearError("checkin_date")); _checkInController.addListener(() => _clearError("checkin_date"));
@ -115,8 +118,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
_checkOutTimeController.addListener(() => _clearError("checkout_time")); _checkOutTimeController.addListener(() => _clearError("checkout_time"));
} }
@override @override
void dispose() { void dispose() {
_destinationFocusNode.dispose(); _destinationFocusNode.dispose();
@ -137,12 +138,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}); });
} }
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["destination_city", "hotel_name","checkin_date","checkin_time","checkout_date", List<String> requiredFields = [
"checkout_time"]; "destination_city",
"hotel_name",
"checkin_date",
"checkin_time",
"checkout_date",
"checkout_time"
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -154,25 +162,22 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save accomadationData $accomadationData");
void handleSave(){ Map<String, dynamic> data = accomadationData;
print( "Handle Save accomadationData $accomadationData");
Map<String,dynamic> data = accomadationData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSaveAccomadation(accomadationData); widget.onSaveAccomadation(accomadationData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -201,10 +206,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
Text("Accomodation Booking", Text("Accomodation Booking",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -245,11 +251,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isDesktop isDesktop
? Row( ? Row(
children: _buildThirdRow(isDesktop), children: _buildThirdRow(isDesktop),
) )
: Column( : Column(
children: _buildThirdRow(isDesktop), children: _buildThirdRow(isDesktop),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Row( Row(
@ -275,6 +281,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _destinationFocused, isFocused: _destinationFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
@ -291,14 +300,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
if (errorMessages["destination_city"] != null) ...[ if (errorMessages["destination_city"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["destination_city"]!, errorMessages["destination_city"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -320,6 +329,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
@ -336,15 +348,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
if (errorMessages["hotel_name"] != null) ...[
if (errorMessages["hotel_name"] != null) ...[ SizedBox(height: 5), // Space before error message
SizedBox(height: 5), // Space before error message Text(
Text( errorMessages["hotel_name"]!,
errorMessages["hotel_name"]!, style: TextStyle(color: Colors.red, fontSize: 12),
style: TextStyle(color: Colors.red, fontSize: 12), ),
), ],
], ],
],
), ),
]; ];
} }
@ -359,9 +370,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today) initialDate:
? _selectedCheckInDate! _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
: today, ? _selectedCheckInDate!
: today,
firstDate: today, firstDate: today,
lastDate: DateTime(2100), lastDate: DateTime(2100),
); );
@ -395,7 +407,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
} }
//-------------------------------Check-In End //-------------------------------Check-In End
DateTime? _selectedCheckOutDate; DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime; TimeOfDay? _selectedCheckOutTime;
@ -405,9 +416,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -417,7 +427,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
_checkOutController.text = DateFormat('yyyy-MM-dd').format(pickedDate); _checkOutController.text =
DateFormat('yyyy-MM-dd').format(pickedDate);
}); });
} }
} }
@ -442,9 +453,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
} }
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -483,14 +491,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
if (errorMessages["checkin_date"] != null) ...[ if (errorMessages["checkin_date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["checkin_date"]!, errorMessages["checkin_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -535,14 +543,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
if (errorMessages["checkin_time"] != null) ...[ if (errorMessages["checkin_time"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["checkin_time"]!, errorMessages["checkin_time"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -566,7 +574,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer( child: AbsorbPointer(
@ -586,17 +593,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["checkout_date"] != null) ...[ if (errorMessages["checkout_date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["checkout_date"]!, errorMessages["checkout_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -634,12 +640,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey), Icon(Icons.access_time, size: 16, color: Colors.grey),
), ),
), ),
), ),
), ),
), ),
), ),
if (errorMessages["checkout_time"] != null) ...[ if (errorMessages["checkout_time"] != null) ...[
@ -671,7 +676,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,

View File

@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class BusScreen extends StatefulWidget { class BusScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final Function(bool) onClose; final Function(bool) onClose;
final Function(Map<String, dynamic>)onSaveBus; final Function(Map<String, dynamic>) onSaveBus;
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
BusScreen({ BusScreen(
required this.onClose, this.apiData, required this.onSaveBus, required this.selectedItem, required this.loginUser}); {required this.onClose,
this.apiData,
required this.onSaveBus,
required this.selectedItem,
required this.loginUser});
@override @override
_BusScreenState createState() => _BusScreenState(); _BusScreenState createState() => _BusScreenState();
@ -52,27 +55,26 @@ class _BusScreenState extends State<BusScreen> {
bool _timeFocus = false; bool _timeFocus = false;
bool _commentsFocus = false; bool _commentsFocus = false;
Map<String, dynamic> get busData {
Map<String, dynamic> get busData{ Map<String, dynamic> data = {
Map<String,dynamic> data = { "from": _fromController.text,
"from": _fromController.text, "to": _toController.text,
"to": _toController.text, "date": _dateController.text,
"date": _dateController.text, "time": _timeController.text,
"time": _timeController.text, "comments": _buscommentsController.text,
"comments": _buscommentsController.text, "created_by": widget.loginUser,
"created_by": widget.loginUser, "updated_by": widget.loginUser,
"updated_by": widget.loginUser,
}; };
if (widget.selectedItem != null) {
if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null &&
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["bus_id"] != null && widget.selectedItem?["bus_id"] != 0) { } else if (widget.selectedItem?["bus_id"] != null &&
data["bus_id"] = widget.selectedItem!["bus_id"]; widget.selectedItem?["bus_id"] != 0) {
} data["bus_id"] = widget.selectedItem!["bus_id"];
} }
}
return data; return data;
} }
@ -91,41 +93,39 @@ class _BusScreenState extends State<BusScreen> {
}); });
}); });
_hotelNameFocusNode.addListener(() { _hotelNameFocusNode.addListener(() {
setState(() { setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus; _isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
}); });
_fromFocusNode.addListener(() { });
setState(() { _fromFocusNode.addListener(() {
_fromFocus = _fromFocusNode.hasFocus; setState(() {
}); _fromFocus = _fromFocusNode.hasFocus;
}); });
});
_toFocusNode.addListener(() { _toFocusNode.addListener(() {
setState(() { setState(() {
_toFocus = _toFocusNode.hasFocus; _toFocus = _toFocusNode.hasFocus;
});
}); });
_dateFocusNode.addListener(() { });
setState(() { _dateFocusNode.addListener(() {
_dateFocus = _fromFocusNode.hasFocus; setState(() {
}); _dateFocus = _fromFocusNode.hasFocus;
}); });
});
_timeFocusNode.addListener(() { _timeFocusNode.addListener(() {
setState(() { setState(() {
_timeFocus = _timeFocusNode.hasFocus; _timeFocus = _timeFocusNode.hasFocus;
});
}); });
});
_commentsFocusNode.addListener(() { _commentsFocusNode.addListener(() {
setState(() { setState(() {
_commentsFocus = _commentsFocusNode.hasFocus; _commentsFocus = _commentsFocusNode.hasFocus;
});
}); });
});
_buscommentsController = initController("comments"); _buscommentsController = initController("comments");
_fromController = initController("from"); _fromController = initController("from");
@ -137,7 +137,6 @@ class _BusScreenState extends State<BusScreen> {
_toController.addListener(() => _clearError("to")); _toController.addListener(() => _clearError("to"));
_dateController.addListener(() => _clearError("date")); _dateController.addListener(() => _clearError("date"));
_timeController.addListener(() => _clearError("time")); _timeController.addListener(() => _clearError("time"));
} }
@override @override
@ -153,7 +152,6 @@ class _BusScreenState extends State<BusScreen> {
super.dispose(); super.dispose();
} }
void _clearError(String field) { void _clearError(String field) {
if (mounted && errorMessages.containsKey(field)) { if (mounted && errorMessages.containsKey(field)) {
setState(() { setState(() {
@ -161,11 +159,12 @@ class _BusScreenState extends State<BusScreen> {
}); });
} }
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["from", "to","date","time"]; List<String> requiredFields = ["from", "to", "date", "time"];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -177,26 +176,22 @@ class _BusScreenState extends State<BusScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save accomadationData $busData");
void handleSave(){ Map<String, dynamic> data = busData;
print( "Handle Save accomadationData $busData");
Map<String,dynamic> data = busData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSaveBus(busData); widget.onSaveBus(busData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -225,8 +220,10 @@ class _BusScreenState extends State<BusScreen> {
), ),
), ),
Text("Bus Booking List", Text("Bus Booking List",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -244,7 +241,7 @@ class _BusScreenState extends State<BusScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -258,7 +255,6 @@ class _BusScreenState extends State<BusScreen> {
]; ];
return [ return [
// ...buildResponsiveRow(_buildFirstRow(isDesktop)), // ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container // Iterate over rowBuilders and wrap each in a responsive container
@ -274,10 +270,7 @@ class _BusScreenState extends State<BusScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -290,14 +283,9 @@ class _BusScreenState extends State<BusScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop))
Column(
children: _buildTripType(isDesktop)
)
], ],
), ),
if (isDesktop) if (isDesktop)
@ -306,34 +294,32 @@ class _BusScreenState extends State<BusScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? []; List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; String? selectedPurpose =
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
@ -341,37 +327,33 @@ class _BusScreenState extends State<BusScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose, value: selectedPurpose,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedPurpose = newValue; selectedPurpose = newValue;
}); });
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); print(
} : null, "Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
}
: null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate; DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime; TimeOfDay? _selectedCheckOutTime;
@ -381,9 +363,8 @@ class _BusScreenState extends State<BusScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -419,7 +400,6 @@ class _BusScreenState extends State<BusScreen> {
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -436,7 +416,7 @@ class _BusScreenState extends State<BusScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _fromFocusNode, focusNode: _fromFocusNode,
controller: _fromController, controller: _fromController,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -446,7 +426,6 @@ class _BusScreenState extends State<BusScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
), ),
@ -482,7 +461,6 @@ class _BusScreenState extends State<BusScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _toFocusNode, focusNode: _toFocusNode,
controller: _toController, controller: _toController,
@ -528,7 +506,6 @@ class _BusScreenState extends State<BusScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer( child: AbsorbPointer(
@ -548,7 +525,6 @@ class _BusScreenState extends State<BusScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["date"] != null) ...[ if (errorMessages["date"] != null) ...[
@ -596,12 +572,11 @@ class _BusScreenState extends State<BusScreen> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey), Icon(Icons.access_time, size: 16, color: Colors.grey),
), ),
), ),
), ),
), ),
), ),
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
@ -613,8 +588,6 @@ class _BusScreenState extends State<BusScreen> {
], ],
], ],
), ),
]; ];
} }
@ -635,7 +608,7 @@ class _BusScreenState extends State<BusScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,
@ -644,7 +617,7 @@ class _BusScreenState extends State<BusScreen> {
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Description", labelText: "Comments",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey), labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
@ -662,7 +635,7 @@ class _BusScreenState extends State<BusScreen> {
// Close Button // Close Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
widget.onClose(false);// Close the dialog or screen widget.onClose(false); // Close the dialog or screen
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,17 +6,18 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class InsuranceScreen extends StatefulWidget { class InsuranceScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final Function(bool) onClose; final Function(bool) onClose;
final Function(Map<String, dynamic>) onSaveInsurance; final Function(Map<String, dynamic>) onSaveInsurance;
final Map<String,dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
InsuranceScreen(
InsuranceScreen({ {required this.onClose,
required this.onClose, required this.apiData, required this.onSaveInsurance, required this.apiData,
required this.selectedItem,required this.loginUser}); required this.onSaveInsurance,
required this.selectedItem,
required this.loginUser});
@override @override
_InsuranceScreenState createState() => _InsuranceScreenState(); _InsuranceScreenState createState() => _InsuranceScreenState();
@ -33,72 +34,79 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
final FocusNode _dateFocusNode = FocusNode(); final FocusNode _dateFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startdateController = TextEditingController(); late TextEditingController _startdateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController(); late TextEditingController _endDateController = TextEditingController();
late TextEditingController _insuranceCommentsController = TextEditingController(); late TextEditingController _insuranceCommentsController =
TextEditingController();
bool _isHotelNameFocused = false; bool _isHotelNameFocused = false;
bool _dateFocus = false; bool _dateFocus = false;
bool _commentsFocus = false; bool _commentsFocus = false;
String? selectedTripType; String? selectedTripType;
String? selectedInsuranceType; String? selectedInsuranceType;
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, dynamic> get InsuranceData{ Map<String, dynamic> get InsuranceData {
Map<String, dynamic> data = { Map<String, dynamic> data = {
"type_of_insurance": selectedInsuranceType, "type_of_insurance": selectedInsuranceType,
"start_date": _startdateController.text, "start_date": _startdateController.text,
"end_date": _endDateController.text, "end_date": _endDateController.text,
"comments": _insuranceCommentsController.text, "comments": _insuranceCommentsController.text,
"created_by": widget.loginUser, "created_by": widget.loginUser,
"updated_by": widget.loginUser, "updated_by": widget.loginUser,
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) { } else if (widget.selectedItem?["insurance_id"] != null &&
widget.selectedItem?["insurance_id"] != 0) {
data["insurance_id"] = widget.selectedItem!["insurance_id"]; data["insurance_id"] = widget.selectedItem!["insurance_id"];
} }
} }
return data; return data;
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_hotelNameFocusNode.addListener(() { _hotelNameFocusNode.addListener(() {
setState(() {_isHotelNameFocused = _hotelNameFocusNode.hasFocus;});}); setState(() {
_dateFocusNode.addListener(() { _isHotelNameFocused = _hotelNameFocusNode.hasFocus;
setState(() {_dateFocus = _fromFocusNode.hasFocus;});}); });
_commentsFocusNode.addListener(() { });
setState(() {_commentsFocus = _commentsFocusNode.hasFocus;});}); _dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_insuranceCommentsController = _insuranceCommentsController =
TextEditingController(text: widget.selectedItem?["comments"] ?? ""); TextEditingController(text: widget.selectedItem?["comments"] ?? "");
_startdateController = _startdateController =
TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
_endDateController = _endDateController =
TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) { if (widget.selectedItem != null &&
selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString(); widget.selectedItem!["type_of_insurance"] != null) {
selectedInsuranceType =
widget.selectedItem!["type_of_insurance"].toString();
} }
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) { void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() { node.addListener(() {
setState(() { setState(() {
@ -107,15 +115,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}); });
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["type_of_insurance", "start_date","end_date"]; List<String> requiredFields = [
"type_of_insurance",
"start_date",
"end_date"
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -127,22 +135,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save InsuranceData $InsuranceData");
void handleSave(){ Map<String, dynamic> data = InsuranceData;
print( "Handle Save InsuranceData $InsuranceData");
Map<String,dynamic> data = InsuranceData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSaveInsurance(InsuranceData); widget.onSaveInsurance(InsuranceData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
DateTime? _parseDate(String date) { DateTime? _parseDate(String date) {
@ -153,9 +159,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
} }
} }
@override @override
void dispose() { void dispose() {
_tripTypeFocusNode.dispose(); _tripTypeFocusNode.dispose();
@ -165,8 +168,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
super.dispose(); super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -195,8 +196,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
), ),
Text("Insurance Booking List", Text("Insurance Booking List",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -214,7 +217,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -228,7 +231,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
]; ];
return [ return [
...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container // Iterate over rowBuilders and wrap each in a responsive container
@ -244,10 +246,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -260,14 +259,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop))
Column(
children: _buildTripType(isDesktop)
)
], ],
), ),
if (isDesktop) if (isDesktop)
@ -276,81 +270,74 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList =
List<Widget> _buildTripType(bool isDesktop){ widget.apiData?['insurance_type_of_insurance'] ?? [];
List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; selectedInsuranceType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedInsuranceType, value: selectedInsuranceType,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedInsuranceType = newValue; selectedInsuranceType = newValue;
if (selectedInsuranceType!.isNotEmpty) { if (selectedInsuranceType!.isNotEmpty) {
errorMessages.remove("type_of_insurance"); errorMessages.remove("type_of_insurance");
} }
});
}); print(selectedInsuranceType);
}
print(selectedInsuranceType);
}
: null, : null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate; DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime; TimeOfDay? _selectedCheckOutTime;
@ -360,9 +347,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -372,21 +358,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) { if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() { setState(() {
_selectedCheckOutDate = pickedDate; _selectedCheckOutDate = pickedDate;
_startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); _startdateController.text =
DateFormat('yyyy-MM-dd').format(pickedDate);
}); });
} }
} }
Future<void> _selectEndCheckOutDate(BuildContext context) async { Future<void> _selectEndCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day); DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -402,7 +387,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -417,15 +401,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _dateFocus, isFocused: _dateFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () async{ onTap: () async {
await _selectCheckOutDate(context); await _selectCheckOutDate(context);
if(_startdateController.text.isNotEmpty){ if (_startdateController.text.isNotEmpty) {
setState(() { setState(() {
errorMessages.remove("start_date"); // Removes the key completely errorMessages
.remove("start_date"); // Removes the key completely
}); });
} }
}, },
@ -446,11 +433,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["start_date"] != null) ...[ if (errorMessages["start_date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
@ -458,7 +442,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -466,7 +450,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -481,9 +464,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _dateFocus, isFocused: _dateFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () async { onTap: () async {
await _selectEndCheckOutDate(context); await _selectEndCheckOutDate(context);
@ -492,9 +477,12 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
DateTime? startDate = _parseDate(_startdateController.text); DateTime? startDate = _parseDate(_startdateController.text);
DateTime? endDate = _parseDate(_endDateController.text); DateTime? endDate = _parseDate(_endDateController.text);
if (startDate != null && endDate != null && endDate.isBefore(startDate)) { if (startDate != null &&
endDate != null &&
endDate.isBefore(startDate)) {
setState(() { setState(() {
errorMessages["end_date"] = "End date cannot be earlier than start date"; errorMessages["end_date"] =
"End date cannot be earlier than start date";
}); });
} else { } else {
setState(() { setState(() {
@ -503,7 +491,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
} }
} }
}, },
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _dateFocusNode, focusNode: _dateFocusNode,
@ -521,7 +508,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["end_date"] != null) ...[ if (errorMessages["end_date"] != null) ...[
@ -532,7 +518,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
@ -541,8 +526,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
@ -563,7 +546,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,
@ -609,7 +592,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
handleSave(); handleSave();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save backgroundColor: Colors.blue, // Primary color for save

View File

@ -6,7 +6,6 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class MiscellaneousScreen extends StatefulWidget { class MiscellaneousScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final Function(bool) onClose; final Function(bool) onClose;
final Function(Map<String, dynamic>) onSaveMiscellaneous; final Function(Map<String, dynamic>) onSaveMiscellaneous;
@ -14,9 +13,13 @@ class MiscellaneousScreen extends StatefulWidget {
final int? selectedIndex; final int? selectedIndex;
final String? loginUser; final String? loginUser;
MiscellaneousScreen({ MiscellaneousScreen(
required this.onClose, required this.apiData, required this.onSaveMiscellaneous, {required this.onClose,
this.selectedItem, this.selectedIndex,required this.loginUser}); required this.apiData,
required this.onSaveMiscellaneous,
this.selectedItem,
this.selectedIndex,
required this.loginUser});
@override @override
_MiscellaneousScreenState createState() => _MiscellaneousScreenState(); _MiscellaneousScreenState createState() => _MiscellaneousScreenState();
@ -49,14 +52,14 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
"comments": _commentsController.text, "comments": _commentsController.text,
"created_by": widget.loginUser, "created_by": widget.loginUser,
"updated_by": widget.loginUser, "updated_by": widget.loginUser,
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["miscellaneous_id"] != null &&
} else if (widget.selectedItem?["miscellaneous_id"] != null && widget.selectedItem?["miscellaneous_id"] != 0) { widget.selectedItem?["miscellaneous_id"] != 0) {
data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"]; data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"];
} }
} }
@ -64,9 +67,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
return data; return data;
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -90,13 +90,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
TextEditingController(text: widget.selectedItem?["comments"] ?? ""); TextEditingController(text: widget.selectedItem?["comments"] ?? "");
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["special_request"] != null) { if (widget.selectedItem != null &&
widget.selectedItem!["special_request"] != null) {
selectedSpecialType = widget.selectedItem!["special_request"].toString(); selectedSpecialType = widget.selectedItem!["special_request"].toString();
} }
} }
@override @override
void dispose() { void dispose() {
_tripTypeFocusNode.dispose(); _tripTypeFocusNode.dispose();
@ -105,16 +104,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
super.dispose(); super.dispose();
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["special_request", "comments"]; List<String> requiredFields = ["special_request", "comments"];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) { if (data[field] == null || data[field].toString().trim().isEmpty) {
@ -125,30 +120,26 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save miscellaneousData $miscellaneousData");
Map<String, dynamic> data = miscellaneousData;
void handleSave(){
print( "Handle Save miscellaneousData $miscellaneousData");
Map<String,dynamic> data = miscellaneousData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
// Clear only if this is a new entry // Clear only if this is a new entry
// if (widget.selectedItem == null) { // if (widget.selectedItem == null) {
// _commentsController.clear(); // _commentsController.clear();
// } // }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -178,8 +169,10 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
), ),
), ),
Text("Miscellaneous Booking List", Text("Miscellaneous Booking List",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -197,7 +190,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -205,9 +198,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
]; ];
} }
return [ return [
...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildFirstRow(isDesktop)),
...buildResponsiveRow(_buildThirdRow(isDesktop)), ...buildResponsiveRow(_buildThirdRow(isDesktop)),
@ -220,10 +211,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -236,14 +224,9 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop))
Column(
children: _buildTripType(isDesktop)
)
], ],
), ),
if (isDesktop) if (isDesktop)
@ -252,66 +235,64 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<dynamic> purposeList =
List<Widget> _buildTripType(bool isDesktop){ widget.apiData?['miscellaneous_special_request'] ?? [];
List<dynamic> purposeList = widget.apiData?['miscellaneous_special_request'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedSpecialType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options"; selectedSpecialType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedSpecialType, value: selectedSpecialType,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedSpecialType = newValue; selectedSpecialType = newValue;
}); });
print(selectedSpecialType); print(selectedSpecialType);
}
}
: null, : null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
if (errorMessages["special_request"] != null) ...[ if (errorMessages["special_request"] != null) ...[
@ -324,8 +305,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
]; ];
} }
List<Widget> _buildThirdRow(bool isDesktop) { List<Widget> _buildThirdRow(bool isDesktop) {
return [ return [
Column( Column(
@ -343,7 +322,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,
@ -352,7 +331,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Description", labelText: "Comments",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey), labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
@ -377,9 +356,8 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
// Close Button // Close Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
_commentsController.clear(); _commentsController.clear();
widget.onClose(false);// Close the dialog or screen widget.onClose(false); // Close the dialog or screen
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color
@ -398,7 +376,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
handleSave(); handleSave();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save backgroundColor: Colors.blue, // Primary color for save

View File

@ -9,13 +9,16 @@ import '../../widgets/custom_text_itnerary_sub.dart';
class TaxiScreen extends StatefulWidget { class TaxiScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final Function(bool) onClose; final Function(bool) onClose;
final Function(Map<String,dynamic>) onSavetaxi; final Function(Map<String, dynamic>) onSavetaxi;
final Map<String,dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
TaxiScreen({ TaxiScreen(
required this.onClose, this.apiData, required this.onSavetaxi, {required this.onClose,
required this.selectedItem,required this.loginUser}); this.apiData,
required this.onSavetaxi,
required this.selectedItem,
required this.loginUser});
@override @override
_TaxiScreenState createState() => _TaxiScreenState(); _TaxiScreenState createState() => _TaxiScreenState();
@ -55,11 +58,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
String? selectedReqTaxi; String? selectedReqTaxi;
String? selectedCarType; String? selectedCarType;
Map<String, dynamic> get taxiData {
Map<String , dynamic> get taxiData { Map<String, dynamic> data = {
Map<String, dynamic> data ={
"destination_city": _destinationController.text, "destination_city": _destinationController.text,
"date": _dateController.text, "date": _dateController.text,
"time": _timeController.text, "time": _timeController.text,
@ -72,13 +72,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
"updated_by": widget.loginUser, "updated_by": widget.loginUser,
// "updated_on": , // "updated_on": ,
// "updated_by": , // "updated_by": ,
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["taxi_id"] != null && widget.selectedItem?["taxi_id"] != 0) { } else if (widget.selectedItem?["taxi_id"] != null &&
widget.selectedItem?["taxi_id"] != 0) {
data["taxi_id"] = widget.selectedItem!["taxi_id"]; data["taxi_id"] = widget.selectedItem!["taxi_id"];
} }
} }
@ -94,17 +95,16 @@ class _TaxiScreenState extends State<TaxiScreen> {
void initState() { void initState() {
super.initState(); super.initState();
_addFocusListener(
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocus = focus); _destinationFocusNode, (focus) => _destinationFocus = focus);
_addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus); _addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus);
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus); _addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
_addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus); _addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus);
_addFocusListener(_numPassengerFocusNode, (focus) => _numPassengerFocus = focus); _addFocusListener(
_numPassengerFocusNode, (focus) => _numPassengerFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city"); _destinationController = initController("destination_city");
_dateController = initController("date"); _dateController = initController("date");
_timeController = initController("time"); _timeController = initController("time");
@ -112,14 +112,15 @@ class _TaxiScreenState extends State<TaxiScreen> {
_numPassengerController = initController("no_of_passengers"); _numPassengerController = initController("no_of_passengers");
_taxiCommentsController = initController("comments"); _taxiCommentsController = initController("comments");
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["car_required_for"] != null) { if (widget.selectedItem != null &&
widget.selectedItem!["car_required_for"] != null) {
selectedReqTaxi = widget.selectedItem!["car_required_for"].toString(); selectedReqTaxi = widget.selectedItem!["car_required_for"].toString();
} }
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["car_type"] != null) { if (widget.selectedItem != null &&
widget.selectedItem!["car_type"] != null) {
selectedCarType = widget.selectedItem!["car_type"].toString(); selectedCarType = widget.selectedItem!["car_type"].toString();
} }
@ -128,8 +129,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
_dateController.addListener(() => _clearError("date")); _dateController.addListener(() => _clearError("date"));
_timeController.addListener(() => _clearError("time")); _timeController.addListener(() => _clearError("time"));
_numPassengerController.addListener(() => _clearError("no_of_passengers")); _numPassengerController.addListener(() => _clearError("no_of_passengers"));
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) { void _addFocusListener(FocusNode node, Function(bool) updateState) {
@ -140,8 +139,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
}); });
} }
@override @override
void dispose() { void dispose() {
_destinationFocusNode.dispose(); _destinationFocusNode.dispose();
@ -153,7 +150,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
super.dispose(); super.dispose();
} }
void _clearError(String field) { void _clearError(String field) {
if (mounted && errorMessages.containsKey(field)) { if (mounted && errorMessages.containsKey(field)) {
setState(() { setState(() {
@ -162,12 +158,17 @@ class _TaxiScreenState extends State<TaxiScreen> {
} }
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["destination_city", "location_of_pickup","no_of_passengers","date","time"]; List<String> requiredFields = [
"destination_city",
"location_of_pickup",
"no_of_passengers",
"date",
"time"
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -179,27 +180,22 @@ class _TaxiScreenState extends State<TaxiScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save taxiData $taxiData");
void handleSave(){ Map<String, dynamic> data = taxiData;
print( "Handle Save taxiData $taxiData");
Map<String,dynamic> data = taxiData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSavetaxi(taxiData); widget.onSavetaxi(taxiData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -228,8 +224,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
), ),
), ),
Text("Taxi Booking List", Text("Taxi Booking List",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -247,7 +245,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -261,7 +259,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
]; ];
return [ return [
// Iterate over rowBuilders and wrap each in a responsive container // Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)), ...rowBuilders.expand((row) => buildResponsiveRow(row)),
@ -277,30 +274,29 @@ class _TaxiScreenState extends State<TaxiScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
List<dynamic> purposeList = widget.apiData?['taxt_car_type'] ?? []; List<dynamic> purposeList = widget.apiData?['taxt_car_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedCarType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; selectedCarType ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
Column( Column(
@ -314,12 +310,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop))
Column(
children: _buildTripType(isDesktop)
)
], ],
), ),
if (isDesktop) if (isDesktop)
@ -328,7 +321,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -345,13 +337,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _numPassengerFocusNode, focusNode: _numPassengerFocusNode,
controller: _numPassengerController, controller: _numPassengerController,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
keyboardType: TextInputType.numberWithOptions(decimal: true), keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal FilteringTextInputFormatter.allow(RegExp(
r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
], ],
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Number of Passenger", labelText: "Number of Passenger",
@ -359,7 +352,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
), ),
@ -371,7 +363,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
@ -396,24 +387,23 @@ class _TaxiScreenState extends State<TaxiScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _toFocusNode, // Assign the correct focus node focusNode: _toFocusNode, // Assign the correct focus node
value: selectedCarType, value: selectedCarType,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedCarType = newValue; selectedCarType = newValue;
}); });
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
} }
: null, : null,
items: dropdownItems, items: dropdownItems,
@ -428,73 +418,69 @@ class _TaxiScreenState extends State<TaxiScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['taxi_car_required_for'] ?? []; List<dynamic> purposeList = widget.apiData?['taxi_car_required_for'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedReqTaxi ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; selectedReqTaxi ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _taxiReqFocused, isFocused: _taxiReqFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _taxiReqFocusNode, // Assign the correct focus node focusNode: _taxiReqFocusNode, // Assign the correct focus node
value: selectedReqTaxi, value: selectedReqTaxi,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedReqTaxi = newValue; selectedReqTaxi = newValue;
}); });
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); print(
} "Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
}
: null, : null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate; DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime; TimeOfDay? _selectedCheckOutTime;
@ -504,9 +490,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -542,7 +527,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -559,7 +543,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _destinationFocusNode, focusNode: _destinationFocusNode,
controller: _destinationController, controller: _destinationController,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -569,19 +553,18 @@ class _TaxiScreenState extends State<TaxiScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
), ),
), ),
if (errorMessages["destination_city"] != null) ...[ if (errorMessages["destination_city"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -605,7 +588,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _locationFocusNode, focusNode: _locationFocusNode,
controller: _locationController, controller: _locationController,
@ -620,13 +602,13 @@ class _TaxiScreenState extends State<TaxiScreen> {
), ),
), ),
), ),
if (errorMessages["location_of_pickup"] != null) ...[ if (errorMessages["location_of_pickup"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
@ -651,7 +633,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer( child: AbsorbPointer(
@ -671,17 +652,16 @@ class _TaxiScreenState extends State<TaxiScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["date"] != null) ...[ if (errorMessages["date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -719,22 +699,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey), Icon(Icons.access_time, size: 16, color: Colors.grey),
), ),
), ),
), ),
), ),
), ),
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
]; ];
} }
@ -756,8 +735,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,
controller: _taxiCommentsController, controller: _taxiCommentsController,
@ -783,7 +763,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
// Close Button // Close Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
widget.onClose(false);// Close the dialog or screen widget.onClose(false); // Close the dialog or screen
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color backgroundColor: Colors.grey[400], // Light grey color

View File

@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class TrainScreen extends StatefulWidget { class TrainScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final Function(Map<String, dynamic>)onSavetrain; final Function(Map<String, dynamic>) onSavetrain;
final Function(bool) onClose; final Function(bool) onClose;
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
TrainScreen({ TrainScreen(
required this.onClose, this.apiData, required this.onSavetrain, required this.selectedItem, required this.loginUser}); {required this.onClose,
this.apiData,
required this.onSavetrain,
required this.selectedItem,
required this.loginUser});
@override @override
_TrainScreenState createState() => _TrainScreenState(); _TrainScreenState createState() => _TrainScreenState();
@ -33,7 +36,6 @@ class _TrainScreenState extends State<TrainScreen> {
final FocusNode _timeFocusNode = FocusNode(); final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _trainNoController = TextEditingController(); late TextEditingController _trainNoController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController(); late TextEditingController _fromController = TextEditingController();
@ -54,11 +56,10 @@ class _TrainScreenState extends State<TrainScreen> {
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String , dynamic> get trainData { Map<String, dynamic> get trainData {
Map<String, dynamic> data ={ Map<String, dynamic> data = {
"train_no": _trainNoController.text,
"train_no": _trainNoController.text, "class": selectedClass,
"class": selectedClass,
"from_station": _fromController.text, "from_station": _fromController.text,
"to_station": _toController.text, "to_station": _toController.text,
"date": _dateController.text, "date": _dateController.text,
@ -69,9 +70,11 @@ class _TrainScreenState extends State<TrainScreen> {
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["train_id"] != null && widget.selectedItem?["train_id"] != 0) { } else if (widget.selectedItem?["train_id"] != null &&
widget.selectedItem?["train_id"] != 0) {
data["train_id"] = widget.selectedItem!["train_id"]; data["train_id"] = widget.selectedItem!["train_id"];
} }
} }
@ -83,47 +86,45 @@ class _TrainScreenState extends State<TrainScreen> {
return TextEditingController(text: widget.selectedItem?[key] ?? ""); return TextEditingController(text: widget.selectedItem?[key] ?? "");
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_trainNoFocusNode.addListener(() { _trainNoFocusNode.addListener(() {
setState(() { setState(() {
_trainNoFocused = _trainNoFocusNode.hasFocus; _trainNoFocused = _trainNoFocusNode.hasFocus;
}); });
}); });
_hotelNameFocusNode.addListener(() { _hotelNameFocusNode.addListener(() {
setState(() { setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus; _isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
}); });
_fromFocusNode.addListener(() { });
setState(() { _fromFocusNode.addListener(() {
_fromFocus = _fromFocusNode.hasFocus; setState(() {
}); _fromFocus = _fromFocusNode.hasFocus;
}); });
_toFocusNode.addListener(() { });
setState(() { _toFocusNode.addListener(() {
_toFocus = _toFocusNode.hasFocus; setState(() {
}); _toFocus = _toFocusNode.hasFocus;
}); });
_dateFocusNode.addListener(() { });
setState(() { _dateFocusNode.addListener(() {
_dateFocus = _fromFocusNode.hasFocus; setState(() {
}); _dateFocus = _fromFocusNode.hasFocus;
}); });
_timeFocusNode.addListener(() { });
setState(() { _timeFocusNode.addListener(() {
_timeFocus = _timeFocusNode.hasFocus; setState(() {
}); _timeFocus = _timeFocusNode.hasFocus;
}); });
_commentsFocusNode.addListener(() { });
setState(() { _commentsFocusNode.addListener(() {
_commentsFocus = _commentsFocusNode.hasFocus; setState(() {
}); _commentsFocus = _commentsFocusNode.hasFocus;
}); });
});
_trainCommentsController = initController("comments"); _trainCommentsController = initController("comments");
_trainNoController = initController("train_no"); _trainNoController = initController("train_no");
@ -134,7 +135,7 @@ class _TrainScreenState extends State<TrainScreen> {
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["class"] != null) { if (widget.selectedItem != null && widget.selectedItem!["class"] != null) {
selectedClass = widget.selectedItem!["class"].toString(); selectedClass = widget.selectedItem!["class"].toString();
} }
_trainNoController.addListener(() => _clearError("train_no")); _trainNoController.addListener(() => _clearError("train_no"));
@ -142,10 +143,8 @@ class _TrainScreenState extends State<TrainScreen> {
_toController.addListener(() => _clearError("to_station")); _toController.addListener(() => _clearError("to_station"));
_dateController.addListener(() => _clearError("date")); _dateController.addListener(() => _clearError("date"));
_timeController.addListener(() => _clearError("time")); _timeController.addListener(() => _clearError("time"));
} }
@override @override
void dispose() { void dispose() {
_trainNoFocusNode.dispose(); _trainNoFocusNode.dispose();
@ -159,7 +158,6 @@ class _TrainScreenState extends State<TrainScreen> {
super.dispose(); super.dispose();
} }
void _clearError(String field) { void _clearError(String field) {
if (mounted && errorMessages.containsKey(field)) { if (mounted && errorMessages.containsKey(field)) {
setState(() { setState(() {
@ -168,12 +166,18 @@ class _TrainScreenState extends State<TrainScreen> {
} }
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["train_no", "class","from_station", "to_station","date","time"]; List<String> requiredFields = [
"train_no",
"class",
"from_station",
"to_station",
"date",
"time"
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -185,28 +189,22 @@ class _TrainScreenState extends State<TrainScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save accomadationData $trainData");
void handleSave(){ Map<String, dynamic> data = trainData;
print( "Handle Save accomadationData $trainData");
Map<String,dynamic> data = trainData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSavetrain(trainData); widget.onSavetrain(trainData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -235,8 +233,10 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
Text("Train Booking List", Text("Train Booking List",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -254,7 +254,7 @@ class _TrainScreenState extends State<TrainScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -268,7 +268,6 @@ class _TrainScreenState extends State<TrainScreen> {
]; ];
return [ return [
...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container // Iterate over rowBuilders and wrap each in a responsive container
@ -284,10 +283,7 @@ class _TrainScreenState extends State<TrainScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -300,20 +296,16 @@ class _TrainScreenState extends State<TrainScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop)),
Column(
children: _buildTripType(isDesktop)
),
if (errorMessages["train_no"] != null) ...[ if (errorMessages["train_no"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
@ -322,39 +314,40 @@ class _TrainScreenState extends State<TrainScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? []; List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_value'], value: item['dropdown_value'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; String? selectedPurpose =
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _trainNoFocused, isFocused: _trainNoFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
@ -369,36 +362,34 @@ class _TrainScreenState extends State<TrainScreen> {
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
), ),
), ),
]; ];
} }
List<Widget> _builClassType(bool isDesktop) {
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['train_class'] ?? []; List<dynamic> purposeList = widget.apiData?['train_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedClass ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; selectedClass ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
Column( Column(
@ -415,9 +406,11 @@ class _TrainScreenState extends State<TrainScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController, // controller: _hotelNameController,
@ -425,38 +418,33 @@ class _TrainScreenState extends State<TrainScreen> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedClass = newValue; selectedClass = newValue;
}); });
} }
: null, : null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
if (errorMessages["class"] != null) ...[ if (errorMessages["class"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate; DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime; TimeOfDay? _selectedCheckOutTime;
@ -466,9 +454,8 @@ class _TrainScreenState extends State<TrainScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -504,7 +491,6 @@ class _TrainScreenState extends State<TrainScreen> {
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -521,7 +507,7 @@ class _TrainScreenState extends State<TrainScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _fromFocusNode, focusNode: _fromFocusNode,
controller: _fromController, controller: _fromController,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -531,19 +517,18 @@ class _TrainScreenState extends State<TrainScreen> {
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
), ),
), ),
), ),
), ),
if (errorMessages["from_station"] != null) ...[ if (errorMessages["from_station"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -567,7 +552,6 @@ class _TrainScreenState extends State<TrainScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _toFocusNode, focusNode: _toFocusNode,
controller: _toController, controller: _toController,
@ -582,14 +566,14 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
), ),
if (errorMessages["to_station"] != null) ...[ if (errorMessages["to_station"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -613,7 +597,6 @@ class _TrainScreenState extends State<TrainScreen> {
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer( child: AbsorbPointer(
@ -633,17 +616,16 @@ class _TrainScreenState extends State<TrainScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["date"] != null) ...[ if (errorMessages["date"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
@ -681,12 +663,11 @@ class _TrainScreenState extends State<TrainScreen> {
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey), Icon(Icons.access_time, size: 16, color: Colors.grey),
), ),
), ),
), ),
), ),
), ),
), ),
if (errorMessages["time"] != null) ...[ if (errorMessages["time"] != null) ...[
@ -698,8 +679,6 @@ class _TrainScreenState extends State<TrainScreen> {
], ],
], ],
), ),
]; ];
} }
@ -720,7 +699,7 @@ class _TrainScreenState extends State<TrainScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,

View File

@ -7,20 +7,21 @@ import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
class VisaScreen extends StatefulWidget { class VisaScreen extends StatefulWidget {
final Map<String, dynamic>? apiData; final Map<String, dynamic>? apiData;
final List<dynamic>? apiCountryData; final List<dynamic>? apiCountryData;
final Function(bool) onClose; final Function(bool) onClose;
final Function(Map<String,dynamic>) onSaveVisa; final Function(Map<String, dynamic>) onSaveVisa;
final Map<String, dynamic>? selectedItem; final Map<String, dynamic>? selectedItem;
final String? loginUser; final String? loginUser;
VisaScreen(
VisaScreen({ {required this.onClose,
required this.onClose,required this.onSaveVisa, this.apiData, required this.selectedItem, required this.onSaveVisa,
required this.apiCountryData, required this.loginUser}); this.apiData,
required this.selectedItem,
required this.apiCountryData,
required this.loginUser});
@override @override
_VisaScreenState createState() => _VisaScreenState(); _VisaScreenState createState() => _VisaScreenState();
@ -38,7 +39,6 @@ class _VisaScreenState extends State<VisaScreen> {
final FocusNode _dateFocusNode = FocusNode(); final FocusNode _dateFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController(); late TextEditingController _fromController = TextEditingController();
@ -57,36 +57,36 @@ class _VisaScreenState extends State<VisaScreen> {
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, dynamic> get visaData {
Map<String, dynamic> get visaData{ Map<String, dynamic> data = {
Map<String, dynamic> data ={ "type_of_visa": selectedPurpose,
"type_of_visa" :selectedPurpose, // "country": selectedCountry,
// "country": selectedCountry,
"country_code": selectedCountry, "country_code": selectedCountry,
"start_date": _dateController.text, "start_date": _dateController.text,
"comments":_visaCommentsController.text, "comments": _visaCommentsController.text,
"created_by": widget.loginUser, "created_by": widget.loginUser,
"updated_by": widget.loginUser, "updated_by": widget.loginUser,
}; };
if (widget.selectedItem != null) { if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"]; data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["visa_id"] != null && widget.selectedItem?["visa_id"] != 0) { } else if (widget.selectedItem?["visa_id"] != null &&
widget.selectedItem?["visa_id"] != 0) {
data["visa_id"] = widget.selectedItem!["visa_id"]; data["visa_id"] = widget.selectedItem!["visa_id"];
} }
} }
return data; return data;
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus); _addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); _addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
@ -96,20 +96,17 @@ class _VisaScreenState extends State<VisaScreen> {
TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
// Set the selected value if available // Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["type_of_visa"] != null) { if (widget.selectedItem != null &&
widget.selectedItem!["type_of_visa"] != null) {
selectedPurpose = widget.selectedItem!["type_of_visa"].toString(); selectedPurpose = widget.selectedItem!["type_of_visa"].toString();
} }
if (widget.selectedItem != null && widget.selectedItem!["country_code"] != null) { if (widget.selectedItem != null &&
widget.selectedItem!["country_code"] != null) {
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString(); // selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
selectedCountry = widget.selectedItem!["country_code"] as String?; selectedCountry = widget.selectedItem!["country_code"] as String?;
} }
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) { void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() { node.addListener(() {
setState(() { setState(() {
@ -131,12 +128,15 @@ class _VisaScreenState extends State<VisaScreen> {
super.dispose(); super.dispose();
} }
bool isValidData(Map<String, dynamic> data) { bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors errorMessages.clear(); // Reset errors
// Required fields that must not be empty // Required fields that must not be empty
List<String> requiredFields = ["type_of_visa", "country_code","start_date"]; List<String> requiredFields = [
"type_of_visa",
"country_code",
"start_date"
];
// Check validation for each field // Check validation for each field
for (String field in requiredFields) { for (String field in requiredFields) {
@ -148,29 +148,22 @@ class _VisaScreenState extends State<VisaScreen> {
return errorMessages.isEmpty; // Valid if there are no errors return errorMessages.isEmpty; // Valid if there are no errors
} }
void handleSave() {
print("Handle Save visaData $visaData");
Map<String, dynamic> data = visaData;
void handleSave(){
print( "Handle Save visaData $visaData");
Map<String,dynamic> data = visaData;
if (!isValidData(data)) { if (!isValidData(data)) {
print("Validation Failed: Required fields are missing."); print("Validation Failed: Required fields are missing.");
setState(() {}); setState(() {});
return; // Stop execution if validation fails return; // Stop execution if validation fails
}else { } else {
widget.onSaveVisa(visaData); widget.onSaveVisa(visaData);
} }
widget.onClose(false);// Close screen after saving widget.onClose(false); // Close screen after saving
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -199,8 +192,10 @@ class _VisaScreenState extends State<VisaScreen> {
), ),
), ),
Text("Visa Registration", Text("Visa Registration",
style: style: TextStyle(
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox( SizedBox(
height: 6, height: 6,
), ),
@ -218,7 +213,7 @@ class _VisaScreenState extends State<VisaScreen> {
}); });
} }
List<Widget> _buildAccomadtionForm (bool isDesktop) { List<Widget> _buildAccomadtionForm(bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) { List<Widget> buildResponsiveRow(List<Widget> children) {
return [ return [
isDesktop ? Row(children: children) : Column(children: children), isDesktop ? Row(children: children) : Column(children: children),
@ -226,12 +221,9 @@ class _VisaScreenState extends State<VisaScreen> {
]; ];
} }
List<List<Widget>> rowBuilders = [ List<List<Widget>> rowBuilders = [_buildSecondRow(isDesktop)];
_buildSecondRow(isDesktop)
];
return [ return [
...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container // Iterate over rowBuilders and wrap each in a responsive container
@ -247,10 +239,7 @@ class _VisaScreenState extends State<VisaScreen> {
]; ];
} }
List<Widget> _buildFirstRow(isDesktop) { List<Widget> _buildFirstRow(isDesktop) {
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -263,20 +252,16 @@ class _VisaScreenState extends State<VisaScreen> {
color: Color(0xFF575A74)), color: Color(0xFF575A74)),
), ),
SizedBox(height: 5), SizedBox(height: 5),
isDesktop
isDesktop ? Row(children: _buildTripType(isDesktop) ? Row(children: _buildTripType(isDesktop))
) : : Column(children: _buildTripType(isDesktop)),
Column(
children: _buildTripType(isDesktop)
),
if (errorMessages["type_of_visa"] != null) ...[ if (errorMessages["type_of_visa"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
@ -285,75 +270,69 @@ class _VisaScreenState extends State<VisaScreen> {
SizedBox( SizedBox(
height: 8, height: 8,
), ),
]; ];
} }
List<Widget> _buildTripType(bool isDesktop) {
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? []; List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>( .map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'], value: item['dropdown_key'],
child: Text(item['dropdown_value']), child: Text(item['dropdown_value']),
)).toList(); ))
.toList();
if (dropdownItems.isEmpty) { if (dropdownItems.isEmpty) {
dropdownItems.add( dropdownItems.add(
DropdownMenuItem<String>( DropdownMenuItem<String>(
value: null, value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)), child: Text("No options available",
style: TextStyle(color: Colors.grey)),
), ),
); );
} }
// Default selected value // Default selected value
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; selectedPurpose ??=
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _tripTypeFocused, isFocused: _tripTypeFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose, value: selectedPurpose,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( contentPadding:
horizontal: 10), // Proper padding EdgeInsets.symmetric(horizontal: 10), // Proper padding
), ),
onChanged: purposeList.isNotEmpty onChanged: purposeList.isNotEmpty
? (newValue) { ? (newValue) {
setState(() { setState(() {
selectedPurpose = newValue; selectedPurpose = newValue;
}); });
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); print(
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
}
}
: null, : null,
items: dropdownItems, items: dropdownItems,
), ),
), ),
), ),
]; ];
} }
List<Widget> _buildSecondRow(bool isDesktop) { List<Widget> _buildSecondRow(bool isDesktop) {
// List<dynamic> countryList = widget.apiCountryData ?? []; // List<dynamic> countryList = widget.apiCountryData ?? [];
// //
@ -380,12 +359,12 @@ class _VisaScreenState extends State<VisaScreen> {
late Map<String, String> countryMap; // Mapping country_code -> country_name late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes late List<String> countryCodes; // List of country codes
countryList = widget.apiCountryData ?? []; countryList = widget.apiCountryData ?? [];
// Map country codes to country names // Map country codes to country names
countryMap = { countryMap = {
for (var item in countryList) item['country_code'] as String: item['country_name'] as String for (var item in countryList)
item['country_code'] as String: item['country_name'] as String
}; };
// Extract only country codes for processing // Extract only country codes for processing
@ -403,9 +382,8 @@ class _VisaScreenState extends State<VisaScreen> {
DateTime? pickedDate = await showDatePicker( DateTime? pickedDate = await showDatePicker(
context: context, context: context,
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate! ? _selectedCheckOutDate!
: today, : today,
firstDate: today, firstDate: today,
@ -420,9 +398,7 @@ class _VisaScreenState extends State<VisaScreen> {
} }
} }
return [ return [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -437,12 +413,15 @@ class _VisaScreenState extends State<VisaScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, isFocused: _isHotelNameFocused,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownSearch<String>( child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry], selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality showSearchBox: true, // Enables search functionality
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search Country...", hintText: "Search Country...",
@ -450,14 +429,17 @@ class _VisaScreenState extends State<VisaScreen> {
), ),
), ),
), ),
items: countryMap.values.toList(), items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1,), contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
), ),
), ),
dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
selectedItem ?? "Select Country", selectedItem ?? "Select Country",
@ -474,22 +456,20 @@ class _VisaScreenState extends State<VisaScreen> {
if (selectedCountry!.isNotEmpty) { if (selectedCountry!.isNotEmpty) {
errorMessages.remove("country_code"); errorMessages.remove("country_code");
} }
}); });
}, },
), ),
), ),
), ),
if (errorMessages["country_code"] != null) ...[ if (errorMessages["country_code"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
"Required", "Required",
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
if (isDesktop) if (isDesktop)
Spacer() Spacer()
else else
@ -510,19 +490,20 @@ class _VisaScreenState extends State<VisaScreen> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _dateFocus, isFocused: _dateFocus,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: ()async{ onTap: () async {
await _selectCheckOutDate(context); await _selectCheckOutDate(context);
if (_dateController.text.isNotEmpty) { if (_dateController.text.isNotEmpty) {
setState(() { setState(() {
errorMessages.remove("start_date"); errorMessages.remove("start_date");
}); });
} }
},
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _dateFocusNode, focusNode: _dateFocusNode,
@ -540,7 +521,6 @@ class _VisaScreenState extends State<VisaScreen> {
), ),
), ),
), ),
), ),
), ),
if (errorMessages["start_date"] != null) ...[ if (errorMessages["start_date"] != null) ...[
@ -552,9 +532,6 @@ class _VisaScreenState extends State<VisaScreen> {
], ],
], ],
), ),
]; ];
} }
@ -575,7 +552,7 @@ class _VisaScreenState extends State<VisaScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop width: isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66, : MediaQuery.of(context).size.width * 0.66,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, focusNode: _commentsFocusNode,
@ -584,7 +561,7 @@ class _VisaScreenState extends State<VisaScreen> {
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Description", labelText: "Comments",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey), labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
@ -621,7 +598,7 @@ class _VisaScreenState extends State<VisaScreen> {
// Save Changes Button // Save Changes Button
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
handleSave(); handleSave();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save backgroundColor: Colors.blue, // Primary color for save

File diff suppressed because it is too large Load Diff

View File

@ -11,80 +11,87 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
class ListPlans extends StatefulWidget {
class ListPlans extends StatefulWidget{
const ListPlans({super.key}); const ListPlans({super.key});
@override @override
_ListPlansState createState() => _ListPlansState(); _ListPlansState createState() => _ListPlansState();
} }
class _ListPlansState extends State<ListPlans> {
class _ListPlansState extends State<ListPlans>{
late Future<List<Plan>> futurePlans; late Future<List<Plan>> futurePlans;
String? userId; String? userId;
String? orgId;
String? token; String? token;
@override @override
void initState(){ void initState() {
super.initState(); super.initState();
getToken(); getToken();
initializeData(); initializeData();
// futurePlans = fetchPlans(); // futurePlans = fetchPlans();
} }
Future<void> initializeData ()async{ Future<void> initializeData() async {
token = await getToken(); token = await getToken();
userId = await getUserId(); userId = await getUserId();
orgId = await getOrgId();
if(token == null || userId == null){ if (token == null || userId == null) {
print("Token or USerId missing"); print("Token or USerId missing");
return; return;
} } else {
else{
setState(() { setState(() {
futurePlans = fetchPlans(); futurePlans = fetchPlans();
}); });
} }
} }
Future<String?> getUserId() async { Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data'); final String? userDataString = prefs.getString('user_data');
if(userDataString != null){ if (userDataString != null) {
try{ try {
final Map<String,dynamic> userData = jsonDecode(userDataString); final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["user_id"]?.toString(); return userData["user_id"]?.toString();
}catch(e){ } catch (e) {
return null; return null;
} }
} }
return null; return null;
} }
Future<String?> getOrgId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["org_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getToken() async { Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token'); return prefs.getString('auth_token');
} }
// Fetch API Data // Fetch API Data
Future<List<Plan>> fetchPlans() async { Future<List<Plan>> fetchPlans() async {
// final String apiUrldata = '$apiUrl/api/plans'; // final String apiUrldata = '$apiUrl/api/plans';
final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId';
// api/plans?org_id=1&user_id=1
// final token = await getToken(); // final token = await getToken();
if (token == null) { if (token == null) {
throw Exception('Token not found. Please log in.'); throw Exception('Token not found. Please log in.');
} }
@ -92,7 +99,7 @@ class _ListPlansState extends State<ListPlans>{
final response = await http.get( final response = await http.get(
Uri.parse(apiUrldata), Uri.parse(apiUrldata),
headers: { headers: {
'Authorization': 'Bearer $token', // Add token here 'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
); );
@ -106,8 +113,7 @@ class _ListPlansState extends State<ListPlans>{
} }
} }
Future<Map<String, dynamic>> getViewPlan(String planId) async {
Future <Map<String,dynamic>> getViewPlan(String planId) async{
final String apiUrldata = '$apiUrl/api/plans/find/$planId'; final String apiUrldata = '$apiUrl/api/plans/find/$planId';
print("API URL: $apiUrldata"); print("API URL: $apiUrldata");
// final token = await getToken(); // final token = await getToken();
@ -119,36 +125,32 @@ class _ListPlansState extends State<ListPlans>{
final response = await http.put( final response = await http.put(
Uri.parse(apiUrldata), Uri.parse(apiUrldata),
headers: { headers: {
'Authorization': 'Bearer $token', // Add token here 'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
final Map<String,dynamic>? resData = json.decode(response.body); final Map<String, dynamic>? resData = json.decode(response.body);
return resData?["data"]; return resData?["data"];
} else { } else {
throw Exception('Failed to load plans'); throw Exception('Failed to load plans');
} }
} }
void viewPlan(String planId, {bool isViewMode = false}) async {
void viewPlan(String planId, {bool isViewMode = false}) async{
try { try {
Map<String, dynamic> planData = await getViewPlan(planId); Map<String, dynamic> planData = await getViewPlan(planId);
print("ViewAAA - $planData"); print("ViewAAA - $planData");
context.go('/createPlan',extra: {'planData': planData, 'isViewMode': isViewMode} ); context.go('/createPlan',
extra: {'planData': planData, 'isViewMode': isViewMode});
} catch (e) { } catch (e) {
print("Error fetching plan: $e"); print("Error fetching plan: $e");
} }
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) { return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
@ -192,13 +194,22 @@ class _ListPlansState extends State<ListPlans>{
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: Colors.blueAccent), backgroundColor: Colors.blueAccent),
onPressed: () { onPressed: () {
context.go('/createPlan'); context.go('/createPlan', extra: {
// 'apiCountryData': apiCountryData,
'orgId': orgId,
});
if (!isDesktop) Navigator.pop(context); if (!isDesktop) Navigator.pop(context);
}, },
child: Row( child: Row(
children: [ children: [
Icon(Icons.add_circle,color: Colors.white,), Icon(
SizedBox(width: 5,), Icons.add_circle,
color: Colors.white,
),
SizedBox(
width: 5,
),
Text('NewPlan'), Text('NewPlan'),
], ],
), ),
@ -206,220 +217,290 @@ class _ListPlansState extends State<ListPlans>{
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
FutureBuilder<List<Plan>>( FutureBuilder<List<Plan>>(
future: futurePlans, // Use the futurePlans variable future: futurePlans, // Use the futurePlans variable
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}")); return Center(
} else if (!snapshot.hasData || snapshot.data!.isEmpty) { child: Padding(
return const Center(child: Text("No plans available")); padding: const EdgeInsets.all(16.0),
} child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
color: Colors.redAccent,
size: 60,
),
SizedBox(height: 16),
Text(
"Oops!",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.redAccent,
),
),
SizedBox(height: 8),
Text(
"No Plans Available For This User",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
SizedBox(height: 20),
Text(
" Please Create Plan",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
),
),
SizedBox(height: 20),
// ElevatedButton.icon(
// onPressed: () {
// // Optional: retry logic or navigation
// },
// icon: Icon(Icons.refresh),
// label: Text("Try Again"),
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.blueAccent,
// ),
// ),
],
),
),
);
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text("No plans available"));
}
List<Plan> plans = snapshot.data!; // Extract the list of plans List<Plan> plans = snapshot.data!; // Extract the list of plans
// Ensure planId is sorted in descending order
plans.sort((a, b) => int.parse(b.planId.toString())
.compareTo(int.parse(a.planId.toString())));
// Ensure planId is sorted in descending order // return ResponsiveBuilder(
plans.sort((a, b) => int.parse(b.planId.toString()).compareTo(int.parse(a.planId.toString()))); // builder: (context, sizingInfo) {
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
//
// return SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: Container(
// color: Colors.grey,
// child: SizedBox(
// width: MediaQuery.of(context).size.width ,
// child: SingleChildScrollView(
// scrollDirection: Axis.vertical,
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
//
// // constraints: isTabletOrDesktop
// // ? const BoxConstraints(maxWidth: double.infinity)
// // : BoxConstraints.tightFor(width: 600),
//
//
// child: DataTable(
// // columnSpacing: 50.0,
// dividerThickness: 0.5, // Reduce the thickness of row dividers
// border: TableBorder(
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
// ),
// columns: const [
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// //
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// ],
// rows: plans.map((plan) {
// return DataRow(cells: [
// DataCell(Text(plan.planId)),
// // DataCell(Text(plan.tripTitle)),
// DataCell(Row(
// children: [
// Flexible(
// child: Text(
// plan.tripTitle,
// softWrap: true,
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
// ),
// ),
// ],
// )),
//
//
// DataCell(Text(plan.tripType)),
// DataCell(Text(plan.costCenter)),
// // DataCell(Text(plan.functionalDepartment)),
// // DataCell(Text(plan.purposeOfTravel)),
// // DataCell(Text(plan.description)),
// //
// DataCell(Text(plan.isBillable)),
// DataCell(Text(plan.status)),
// DataCell(
// TextButton(
// onPressed: () {
// viewPlan(plan.planId);
// print("View button clicked for ${plan.planId}");
// },
// child: const Text('View',
// style: TextStyle(color: Colors.blueAccent)),
// ),
// ),
// ]);
// }).toList(),
// ),
//
//
// ),
// ),
// ),
// );
//
// },
// );
return Expanded(
child: SingleChildScrollView(
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
scrollDirection: Axis.vertical,
child: SizedBox(
width: MediaQuery.of(context).size.width * 1.5,
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen
// width: double.infinity , // Ensure table is wider than screen
child: SingleChildScrollView(
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
scrollDirection: Axis
.horizontal, // Inner wrapper for vertical scrolling
// return ResponsiveBuilder( child: ConstrainedBox(
// builder: (context, sizingInfo) { constraints: BoxConstraints(minWidth: 1300),
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop; // width: MediaQuery.of(context).size.width ,
//
// return SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: Container(
// color: Colors.grey,
// child: SizedBox(
// width: MediaQuery.of(context).size.width ,
// child: SingleChildScrollView(
// scrollDirection: Axis.vertical,
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
//
// // constraints: isTabletOrDesktop
// // ? const BoxConstraints(maxWidth: double.infinity)
// // : BoxConstraints.tightFor(width: 600),
//
//
// child: DataTable(
// // columnSpacing: 50.0,
// dividerThickness: 0.5, // Reduce the thickness of row dividers
// border: TableBorder(
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
// ),
// columns: const [
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// //
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// ],
// rows: plans.map((plan) {
// return DataRow(cells: [
// DataCell(Text(plan.planId)),
// // DataCell(Text(plan.tripTitle)),
// DataCell(Row(
// children: [
// Flexible(
// child: Text(
// plan.tripTitle,
// softWrap: true,
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
// ),
// ),
// ],
// )),
//
//
// DataCell(Text(plan.tripType)),
// DataCell(Text(plan.costCenter)),
// // DataCell(Text(plan.functionalDepartment)),
// // DataCell(Text(plan.purposeOfTravel)),
// // DataCell(Text(plan.description)),
// //
// DataCell(Text(plan.isBillable)),
// DataCell(Text(plan.status)),
// DataCell(
// TextButton(
// onPressed: () {
// viewPlan(plan.planId);
// print("View button clicked for ${plan.planId}");
// },
// child: const Text('View',
// style: TextStyle(color: Colors.blueAccent)),
// ),
// ),
// ]);
// }).toList(),
// ),
//
//
// ),
// ),
// ),
// );
//
// },
// );
return Expanded( child: Container(
child: SingleChildScrollView( // color: Colors.amber,
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling child: DataTable(
scrollDirection: Axis.vertical, columnSpacing:
child: SizedBox( 50.0, // Adjust spacing between columns
width: MediaQuery.of(context).size.width * 1.5, dividerThickness: 0.5,
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen border: TableBorder(
// width: double.infinity , // Ensure table is wider than screen horizontalInside: BorderSide(
width: 0.5, color: Colors.grey.shade200),
child: SingleChildScrollView(
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
scrollDirection: Axis.horizontal, // Inner wrapper for vertical scrolling
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 1300),
// width: MediaQuery.of(context).size.width ,
child: Container(
// color: Colors.amber,
child: DataTable(
columnSpacing: 50.0, // Adjust spacing between columns
dividerThickness: 0.5,
border: TableBorder(
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200),
),
columns: const [
DataColumn(label: Text('Plan ID', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Trip Title', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Trip Type', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Cost Center', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Is Billable', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Status', style: TextStyle(fontWeight: FontWeight.bold))),
DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.bold))),
],
rows: plans.map((plan) {
return DataRow(cells: [
DataCell(Text(plan.planId)),
DataCell(Text(plan.tripTitle, softWrap: true, overflow: TextOverflow.ellipsis)),
DataCell(Text(plan.tripType)),
DataCell(Text(plan.costCenter)),
DataCell(Text(plan.isBillable)),
DataCell(
Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), // Padding for better look
decoration: BoxDecoration(
color: plan.status == "Active" ? Colors.green.shade50 : Colors.grey.shade50, // Background color
borderRadius: BorderRadius.circular(10), // Rounded corners
),
child: Text(
plan.status,
style: TextStyle(
color: plan.status == "Active" ? Colors.green : Colors.grey, // Text color
fontWeight: FontWeight.bold, // Optional: Make text bold
),
),
),
),
DataCell(
Row(
children:[
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
viewPlan(plan.planId, isViewMode: true);
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
viewPlan(plan.planId, isViewMode: false);
},
),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// deletePlan(plan.planId);
// },
// ),
]
)
),
]);
}).toList(),
), ),
columns: const [
DataColumn(
label: Text('Plan ID',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Trip Title',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Trip Type',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Cost Center',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Is Billable',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Status',
style: TextStyle(
fontWeight: FontWeight.bold))),
DataColumn(
label: Text('Actions',
style: TextStyle(
fontWeight: FontWeight.bold))),
],
rows: plans.map((plan) {
return DataRow(cells: [
DataCell(Text(plan.planId)),
DataCell(Text(plan.tripTitle,
softWrap: true,
overflow: TextOverflow.ellipsis)),
DataCell(Text(plan.tripType)),
DataCell(Text(plan.costCenter)),
DataCell(Text(plan.isBillable)),
DataCell(
Container(
padding: const EdgeInsets.symmetric(
vertical: 5,
horizontal:
10), // Padding for better look
decoration: BoxDecoration(
color: plan.status == "Active"
? Colors.green.shade50
: Colors
.grey.shade50, // Background color
borderRadius: BorderRadius.circular(
10), // Rounded corners
),
child: Text(
plan.status,
style: TextStyle(
color: plan.status == "Active"
? Colors.green
: Colors.grey, // Text color
fontWeight: FontWeight
.bold, // Optional: Make text bold
),
),
),
),
DataCell(Row(children: [
IconButton(
icon: Icon(Icons.remove_red_eye,
color: Colors.blue),
onPressed: () {
viewPlan(plan.planId, isViewMode: true);
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
viewPlan(plan.planId, isViewMode: false);
},
),
// IconButton(
// icon: Icon(Icons.delete, color: Colors.red),
// onPressed: () {
// deletePlan(plan.planId);
// },
// ),
])),
]);
}).toList(),
), ),
), ),
), ),
), ),
), ),
); ),
);
},
),
},
),
], ],
), ),
); );
} }
} }

View File

@ -6,6 +6,8 @@ import 'package:responsive_builder/responsive_builder.dart';
import '../../routes/custom_appBar.dart'; import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_user_form.dart';
class Policy extends StatefulWidget { class Policy extends StatefulWidget {
const Policy({super.key}); const Policy({super.key});
@ -18,6 +20,7 @@ class _PolicyState extends State<Policy> {
late String policyType = "domestic"; late String policyType = "domestic";
int? selectedServiceIndex = 1; int? selectedServiceIndex = 1;
late String selectedService = "Train"; late String selectedService = "Train";
String? _selectedTripType;
bool showClass = true; bool showClass = true;
bool showCost = true; bool showCost = true;
@ -42,136 +45,206 @@ class _PolicyState extends State<Policy> {
} }
Widget buildPolicyLayout(bool isDesktop) { Widget buildPolicyLayout(bool isDesktop) {
return Container( return SingleChildScrollView(
margin: isDesktop scrollDirection: Axis.vertical,
? EdgeInsets.all(20.0) child: Container(
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), margin: isDesktop
height: MediaQuery.of(context).size.height, ? EdgeInsets.all(20.0)
decoration: BoxDecoration( : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
border: isDesktop height: MediaQuery.of(context).size.height,
? Border.all( decoration: BoxDecoration(
width: 3, border: isDesktop
color: Color(0xFFF7F7FB), ? Border.all(
) width: 2,
: null, color: Color(0xFFF7F7FB),
), )
child: Column( : null,
mainAxisAlignment: MainAxisAlignment.start, color: Color(0xFFF7F7FB),
children: [ ),
Container( child: Column(
color: Color(0xFFF7F7FB), mainAxisAlignment: MainAxisAlignment.start,
child: Column( children: [
mainAxisAlignment: MainAxisAlignment.start, Container(
children: [ // color: Color(0xFFF7F7FB),
Container( child: Column(
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), mainAxisAlignment: MainAxisAlignment.start,
// color: Colors.white, // Background to avoid overlapping children: [
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, Container(
child: Row( padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
mainAxisAlignment: MainAxisAlignment.center, // color: Colors.white, // Background to avoid overlapping
crossAxisAlignment: CrossAxisAlignment.end, color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
children: [ child: Row(
Text( mainAxisAlignment: MainAxisAlignment.center,
"Choose Policy Type", crossAxisAlignment: CrossAxisAlignment.end,
style: TextStyle( children: [
fontSize: 18, Text(
color: Colors.black, "Choose Policy Type",
style: TextStyle(
fontSize: 18,
color: Colors.black,
),
), ),
), ],
], ),
), ),
),
// Container( // Container(
// child: Row( // child: Row(
// children: [ // children: [
// Expanded( // Expanded(
// child: GestureDetector( // child: GestureDetector(
// onTap: () { // onTap: () {
// setState(() { // setState(() {
// policyType = "domestic"; // policyType = "domestic";
// }); // });
// }, // },
// child: Container( // child: Container(
// padding: EdgeInsets.all(10), // padding: EdgeInsets.all(10),
// color: policyType == "domestic" // color: policyType == "domestic"
// ? Colors.blueAccent.shade100 // ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7), // : Color(0xFFEBEBF7),
// // color: Colors.blue.shade300, // // color: Colors.blue.shade300,
// child: Column( // child: Column(
// children: [ // children: [
// Text( // Text(
// "Domestic", // "Domestic",
// style: TextStyle( // style: TextStyle(
// color: policyType == "domestic" // color: policyType == "domestic"
// ? Colors.white // ? Colors.white
// : Colors.black87, // : Colors.black87,
// fontSize: 15, // fontSize: 15,
// fontWeight: FontWeight.bold), // fontWeight: FontWeight.bold),
// ), // ),
// ], // ],
// ), // ),
// ), // ),
// ), // ),
// ), // ),
// Expanded( // Expanded(
// child: GestureDetector( // child: GestureDetector(
// onTap: () { // onTap: () {
// setState(() { // setState(() {
// policyType = "international"; // policyType = "international";
// }); // });
// }, // },
// child: Container( // child: Container(
// padding: EdgeInsets.all(10), // padding: EdgeInsets.all(10),
// // color: Color(0xFFEBEBF7), // // color: Color(0xFFEBEBF7),
// color: policyType == "international" // color: policyType == "international"
// ? Colors.blueAccent.shade100 // ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7), // : Color(0xFFEBEBF7),
// // color: Color(0xFFE3F2FD), // // color: Color(0xFFE3F2FD),
// //
// child: Column( // child: Column(
// children: [ // children: [
// Text( // Text(
// "International", // "International",
// style: TextStyle( // style: TextStyle(
// color: policyType == "international" // color: policyType == "international"
// ? Colors.white // ? Colors.white
// : Colors.black87, // : Colors.black87,
// fontSize: 15, // fontSize: 15,
// fontWeight: FontWeight.bold), // fontWeight: FontWeight.bold),
// ), // ),
// ], // ],
// ), // ),
// ), // ),
// )), // )),
// ], // ],
// ), // ),
// ), // ),
// //
], ],
),
), ),
), isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
SizedBox( Container(
height: 10, // color: Colors.amber,
), // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
isDesktop padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
? Expanded( child: Column(
child: Row( children: [
children: [ Row(
_buildPolicyCategoryList(isDesktop), children: [
_buildPolicyCategory(isDesktop), Column(
], crossAxisAlignment: CrossAxisAlignment.start,
), children: [
) Text("Policy Name",
: Expanded( style: TextStyle(
child: Column( fontSize: 12,
children: [ fontWeight: FontWeight.w200,
_buildPolicyCategoryList(isDesktop), color: Colors.black)),
_buildPolicyCategory(isDesktop), SizedBox(height: 5),
], CustomTextFieldUserWrapper(
), isFocused: false,
) isDesktop: isDesktop,
], child: SizedBox(
height: 40,
child: TextField(
style: TextStyle(fontSize: 12),
// controller: controllers["Fname"],
// enabled: !isViewMode,
onChanged: (value) {},
decoration: InputDecoration(
labelText: "Policy Name",
labelStyle: TextStyle(
fontSize: 12, color: Colors.grey),
floatingLabelBehavior:
FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
],
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Policy Type",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.black)),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildTripType(isDesktop),
)
],
)
],
),
],
)),
SizedBox(
height: 10,
),
isDesktop
? Expanded(
child: Row(
children: [
_buildPolicyCategoryList(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
)
: Expanded(
child: Column(
children: [
_buildPolicyCategoryList(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
)
],
),
), ),
); );
} }
@ -224,8 +297,10 @@ class _PolicyState extends State<Policy> {
return SizedBox( return SizedBox(
width: isDesktop ? 180 : null, width: isDesktop ? 180 : null,
height: isDesktop height: isDesktop
? max((MediaQuery.of(context).size.height * 0.09), 10) ? max((MediaQuery.of(context).size.height * 0.075), 10)
: 45, : 45,
// max((MediaQuery.of(context).size.height * 0.09), 10)
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
print("Selected Services - $service - $index"); print("Selected Services - $service - $index");
@ -288,4 +363,64 @@ class _PolicyState extends State<Policy> {
selectedTab: selectedService)), selectedTab: selectedService)),
); );
} }
List<Widget> _buildTripType(bool isDesktop) {
return [
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
width: isDesktop ? 170 : 140,
isFocused: _selectedTripType == "1",
isDesktop: isDesktop,
child: SizedBox(
height: 35,
child: Material(
color: Colors.transparent,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
dense: true,
title: Text("Domestic"),
value: "1",
groupValue: _selectedTripType,
onChanged: (value) {
setState(() {
_selectedTripType = value!;
});
},
),
),
),
),
isDesktop ? SizedBox(width: 28) : SizedBox(width: 15),
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
width: isDesktop ? 180 : 180,
isFocused: _selectedTripType == "2",
isDesktop: isDesktop,
child: SizedBox(
height: 35,
child: Material(
color: Colors.transparent,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
dense: true,
title: Text("International"),
value: "2",
groupValue: _selectedTripType,
onChanged: (value) {
setState(() {
_selectedTripType = value!;
});
},
),
),
),
),
];
}
} }

View File

@ -42,6 +42,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5),
Row( Row(
@ -56,22 +57,13 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
) )
], ],
), ),
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
Padding(
padding: widget.isDesktop
? const EdgeInsets.all(8.0)
: const EdgeInsets.all(1.0),
child: Row(
children: _buildTripType(widget.isDesktop),
),
),
if (widget.isClass!) if (widget.isClass!)
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5), widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Container( child: Container(
// color:Colors.grey, // color: Colors.grey,
padding: padding:
const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5), const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5),
child: Column( child: Column(
@ -181,19 +173,20 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
children: [ children: [
Expanded( Expanded(
child: Container( child: Container(
color: Colors.grey.shade100,
width: widget.isDesktop width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.63 ? MediaQuery.of(context).size.width * 0.63
: 600, : 600,
child: Column( child: Column(
children: [ children: [
Container( Container(
margin: const EdgeInsets.only(right: 20), margin: const EdgeInsets.only(right: 0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade50, color: Colors.grey.shade100,
border: Border.all( border: Border.all(
color: Colors.grey.shade50, color: Colors.grey.shade100,
), ),
borderRadius: BorderRadius.circular(8)), ),
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 10, bottom: 10, left: 35, right: 35), top: 10, bottom: 10, left: 35, right: 35),
child: Row( child: Row(
@ -232,7 +225,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
child: Container( child: Container(
// color: Colors.grey, // color: Colors.grey,
margin: const EdgeInsets.only(right: 20), margin: const EdgeInsets.only(right: 20),
color: Colors.grey.shade50, color: Colors.grey.shade100,
child: Column(children: [ child: Column(children: [
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
@ -511,57 +504,4 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
], ],
); );
} }
List<Widget> _buildTripType(bool isMobile) {
return [
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
width: 120,
isFocused: _selectedTripType == "1",
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
child: Material(
color: Colors.transparent,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
dense: true,
title: Text("Domestic"),
value: "1",
groupValue: _selectedTripType,
onChanged: (value) {
setState(() {
_selectedTripType = value!;
});
},
),
),
),
),
SizedBox(width: 20),
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
width: 150,
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
isFocused: _selectedTripType == "2",
isDesktop: widget.isDesktop,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text("International"),
value: "2",
groupValue: _selectedTripType,
onChanged: (value) {
setState(() {
_selectedTripType = value!;
});
},
),
),
];
}
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:html' as html; import 'dart:html' as html;
import 'dart:typed_data'; // Import for Uint8List import 'dart:typed_data'; // Import for Uint8List
@ -8,11 +9,14 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart' as http_parser;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:http_parser/http_parser.dart';
import '../../../config/apiUrl.dart'; import '../../../config/apiUrl.dart';
import '../../../routes/custom_appBar.dart'; import '../../../routes/custom_appBar.dart';
@ -31,6 +35,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
String? userId; String? userId;
String? orgId;
String? token; String? token;
@ -67,8 +72,11 @@ class _CreateUserFormState extends State<CreateUserForm> {
String? selectedFileNames; String? selectedFileNames;
Uint8List? passportDocumentBytes; Uint8List? passportDocumentBytes;
String? passportFileUrlFromApi;
String? base64PDF; String? base64PDF;
html.File? passportFile;
List<String> dataHeader = [ List<String> dataHeader = [
"Fname", "Fname",
"Lname", "Lname",
@ -109,12 +117,13 @@ class _CreateUserFormState extends State<CreateUserForm> {
"address": controllers["address"]?.text, "address": controllers["address"]?.text,
"gender": selectedGender, "gender": selectedGender,
"postal_code": controllers["postalCode"]?.text, "postal_code": controllers["postalCode"]?.text,
"country": selectedCountry, "country_code": selectedCountry,
"employee_code": controllers["employeeCode"]?.text, "employee_code": controllers["employeeCode"]?.text,
"user_type": selectedUserType, "user_type": selectedUserType,
"role_id": selectedRole, "role_id": selectedRole,
"department_id": selectedDepartment, "department_id": selectedDepartment,
"group_id": selectedLevel, "group_id": selectedLevel,
"first_approver": selectedFirstApprover, "first_approver": selectedFirstApprover,
@ -122,21 +131,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
"third_approver": selectedThirdApprover, "third_approver": selectedThirdApprover,
"passport_number": controllers["passportNumber"]?.text, "passport_number": controllers["passportNumber"]?.text,
"place_of_issue": controllers["placeOfIssue"]?.text, "place_of_issue": controllers["placeOfIssue"]?.text,
"passport_document": base64PDF, "passport_document": passportFile,
"date_of_issue": controllers["dateOfIssue"]?.text, "date_of_issue": controllers["dateOfIssue"]?.text,
"date_of_expiry": controllers["dateOfExpiry"]?.text, "date_of_expiry": controllers["dateOfExpiry"]?.text,
"created_by": userId, "created_by": userId,
"is_active": "1", "is_active": "1",
// "passport_fileData": base64PDF, "org_id": orgId,
// "passport_fileData": passportFile,
}; };
return data; return data;
} }
void updateData() { Future<void> updateData() async {
// Ensure apiselectedUser is not null before printing // Ensure apiselectedUser is not null before printing
if (apiselectedUser != null) { if (apiselectedUser != null) {
print("API Selected User Has Data - $apiselectedUser"); print("API Selected User Has Data - $widget.apiselectedUser");
setState(() { setState(() {
// Wrap in setState to update the UI // Wrap in setState to update the UI
@ -165,15 +175,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
apiselectedUser?["date_of_expiry"] ?? ""; apiselectedUser?["date_of_expiry"] ?? "";
selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; selectedCountry = apiselectedUser?["country_code"]?.toString() ?? "";
selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? "";
base64PDF = selectedUserType = selectedUserType =
apiselectedUser?["passport_document"]?.toString().trim() ?? "";
selectedUserType =
apiselectedUser?["user_type"]?.toString().trim() ?? ""; apiselectedUser?["user_type"]?.toString().trim() ?? "";
selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? "";
selectedDepartment = // selectedDepartment =
apiselectedUser?["department_id"]?.toString().trim() ?? ""; // apiselectedUser?["department_id"]?.toString().trim() ?? "";
if (apiselectedUser?["department_id"] != null) {
selectedDepartment = apiselectedUser!["department_id"].toString();
}
// print(
// "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} ");
selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? "";
selectedFirstApprover = selectedFirstApprover =
@ -184,6 +201,19 @@ class _CreateUserFormState extends State<CreateUserForm> {
apiselectedUser?["third_approver"]?.toString() ?? ""; apiselectedUser?["third_approver"]?.toString() ?? "";
print("Updated selectedGender: $selectedGender"); // Debugging print("Updated selectedGender: $selectedGender"); // Debugging
// Load passport document from API
String? apiDocPath = apiselectedUser?["passport_document"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
passportFileUrlFromApi = apiDocPath;
selectedFileNames =
apiDocPath.split('/').last; // Extract filename from path
passportFile = null; // No local file selected yet
} else {
passportFileUrlFromApi = null;
selectedFileNames = null;
passportFile = null;
}
}); });
} else { } else {
print("API Selected User Has Data - No data available yet"); print("API Selected User Has Data - No data available yet");
@ -195,37 +225,29 @@ class _CreateUserFormState extends State<CreateUserForm> {
super.initState(); super.initState();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// Step 1: Set 'reloaded' flag before page unload
html.window.onBeforeUnload.listen((event) {
html.window.localStorage['reloaded'] = 'true';
});
// apiCountryData = extraData['apiCountryData']; // Extract apiCountryData // apiCountryData = extraData['apiCountryData']; // Extract apiCountryData
// futureUsers = extraData['apiUserData']; // Extract futureUsers (Future<List<dynamic>>) // futureUsers = extraData['apiUserData']; // Extract futureUsers (Future<List<dynamic>>)
apiCountryData = null; apiCountryData = null;
apiUserData = null; apiUserData = null;
apiselectedUser = null; // apiselectedUser = null;
apiCostData = null; apiCostData = null;
apiRoleData = null; apiRoleData = null;
// Delay accessing context until the widget is fully initialized
// WidgetsBinding.instance.addPostFrameCallback((_) {
// setState(() {
// apiCountryData = (GoRouterState.of(context).extra as Map<String, dynamic>)['apiCountryData'];
// apiUserData = (GoRouterState.of(context).extra as Map<String, dynamic>)['apiUserData'];
// apiselectedUser = (GoRouterState.of(context).extra as Map<String, dynamic>)['selectedUser'];
//
//
// userList = apiUserData ?? [];
// userMap = {
// for (var user in userList)
// user['user_id'] as String: "${user['first_name']} ${user['last_name']}"
// };
//
// userIdsApi = userMap.keys.toList();
//
//
// });
// });
// Initialize controllers for each field // Initialize controllers for each field
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
// final wasReloaded = html.window.localStorage['reloaded'] == 'true';
//
// if (wasReloaded) {
// html.window.localStorage.remove('reloaded'); // Clear it
// context.go('/listUser'); // Navigate using go_router
// }
final extraData = final extraData =
GoRouterState.of(context).extra as Map<String, dynamic>?; GoRouterState.of(context).extra as Map<String, dynamic>?;
@ -250,6 +272,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
isEditProfile = extraData['isEditProfile'] ?? false; isEditProfile = extraData['isEditProfile'] ?? false;
}); });
print("selectedUser: $apiselectedUser");
// Add another post-frame callback to check after setState // Add another post-frame callback to check after setState
await Future.delayed(Duration( await Future.delayed(Duration(
milliseconds: 100)); // Optional delay to ensure UI has updated milliseconds: 100)); // Optional delay to ensure UI has updated
@ -296,7 +320,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
setState(() { setState(() {
// apiUserData = users; // apiUserData = users;
apiUserData = users.where((user) => user["role_id"] == "3").toList(); apiUserData = users.where((user) => user["role_id"] == "4").toList();
print("APIUSerDATa - $apiUserData"); print("APIUSerDATa - $apiUserData");
@ -383,7 +407,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
super.dispose(); super.dispose();
} }
void handleSubmit() { void handleSubmit() async {
print("USR Detail Submit"); print("USR Detail Submit");
printFormData(); printFormData();
@ -396,6 +420,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
return; // Stop execution if validation fails return; // Stop execution if validation fails
} else { } else {
print("USERDETAILS : $userDetials"); print("USERDETAILS : $userDetials");
orgId = await getOrgId();
createUserData(userDetials); createUserData(userDetials);
} }
} }
@ -464,133 +490,102 @@ class _CreateUserFormState extends State<CreateUserForm> {
uploadInput.onChange.listen((e) { uploadInput.onChange.listen((e) {
final file = uploadInput.files!.first; final file = uploadInput.files!.first;
final reader = html.FileReader();
reader.readAsArrayBuffer(file); // Ensure the file is a PDF
reader.onLoadEnd.listen((event) { if (!file.type.contains("pdf")) {
print('File picked: ${file.name}'); print("Error: Not a PDF file");
print('File size: ${file.size} bytes'); return;
}
// Ensure the file is a PDF // 🔹 File size check: Ensure it does not exceed 3MB
if (!file.type.contains("pdf")) { int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
print("Error: Not a PDF file"); if (file.size > maxFileSize) {
return; print('Error: File size exceeds 3MB');
} return;
}
setState(() { setState(() {
selectedFileNames = file.name; // Store file name selectedFileNames = file.name;
passportDocumentBytes = reader.result as Uint8List; // Store file data passportFile = file;
passportFileUrlFromApi = null;
// 🔹 Convert to Base64 properly
base64PDF = base64Encode(passportDocumentBytes!);
print('Base64 Length: ${base64PDF!.length}');
print('Base64 (first 50 chars): ${base64PDF!.substring(0, 50)}');
// Ensure Base64 starts with "JVBERi0x"
if (!base64PDF!.startsWith("JVBERi0x")) {
print("Error: Base64 does not start with 'JVBERi0x'");
return;
}
// 🔹 File size check: Ensure it does not exceed 3MB
int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
if (file.size > maxFileSize) {
print('Error: File size exceeds 3MB');
return;
}
});
}); });
print('PDF File selected: ${file.name}');
}); });
} }
// void pickPDFWeb() {
// html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
// uploadInput.accept = '.pdf';
// uploadInput.click();
//
// uploadInput.onChange.listen((e) {
// final file = uploadInput.files!.first;
// final reader = html.FileReader();
//
// reader.readAsArrayBuffer(file);
// reader.onLoadEnd.listen((event) {
// print('File picked: ${file.name}');
// print('File size: ${file.size} bytes');
//
//
// // Update the state with the selected file name
// setState(() {
// selectedFileNames = file.name; // Store only one file name
// passportDocumentBytes = reader.result as Uint8List; // Store file data
//
// // 🔹 Convert file to Base64
// base64PDF = base64Encode(passportDocumentBytes! as List<int>);
// // File size check: Ensure the file size does not exceed 3MB
// int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
//
// if (file.size > maxFileSize) {
// print('Error: File size exceeds 3MB');
// // You can show an error message here if necessary
// // For example:
// // showError('File size cannot exceed 3MB');
// return;
// }
//
// print('File size: ${file.size} bytes');
// print('Base64 Data: $base64PDF'); // Debugging
// });
//
//
//
// });
// });
// }
Future<void> createUserData(Map<String, dynamic> userData) async { Future<void> createUserData(Map<String, dynamic> userData) async {
bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty; final bool isUpdating =
final String apiUrldata = isUpdating apiselectedUser != null && apiselectedUser!.isNotEmpty;
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}' final uri = Uri.parse(
: '$apiUrl/api/users/create'; isUpdating
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}'
: '$apiUrl/api/users/create',
);
if (token == null) { if (token == null) {
throw Exception('Token not found. Please log in.'); throw Exception('Token not found. Please log in.');
} }
// Add user_id only if updating // Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
// If updating, spoof the method Laravel-style
if (isUpdating) { if (isUpdating) {
userData['user_id'] = apiselectedUser?["user_id"]; request.fields['_method'] = 'PUT';
request.fields['user_id'] = apiselectedUser!["user_id"].toString();
} }
// Add all non-null and non-empty user data fields
userData.forEach((key, value) {
if (value != null && value.toString().trim().isNotEmpty) {
request.fields[key] = value.toString();
}
});
// Attach file if selected
if (passportFile != null) {
try {
final reader = html.FileReader();
reader.readAsArrayBuffer(passportFile!);
await reader.onLoad.first;
final data = reader.result as Uint8List;
final multipartFile = http.MultipartFile.fromBytes(
'passport_document',
data,
filename: passportFile!.name,
);
request.files.add(multipartFile);
print("📎 File attached: ${passportFile!.name}");
} catch (e) {
print("❌ Failed to read file: $e");
}
} else {
print("⚠️ No passport file selected.");
}
print("🚀 Sending request with fields: ${request.fields}");
try { try {
final response = isUpdating final streamedResponse = await request.send();
? await http.put( final response = await http.Response.fromStream(streamedResponse);
Uri.parse(apiUrldata), print("Response status: ${response.statusCode}");
headers: { print("Response body: ${response.body}");
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(userData),
)
: await http.post(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(userData),
);
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
print("Plan submitted successfully!"); print("✅ User submitted successfully!");
print("Response: ${response.body}"); print("📨 Response: ${response.body}");
context.go('/listUser'); context.go('/listUser');
} else { } else {
print("Failed to submit plan. Status: ${response.statusCode}"); print("❌ Submission failed. Status: ${response.statusCode}");
print("Error: ${response.body}"); print("📨 Body: ${response.body}");
} }
} catch (e) { } catch (e) {
print(" Error submitting plan: $e"); print("🔥 Error submitting user: $e");
} }
} }
@ -755,6 +750,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
child: isDesktop child: isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Expanded(child: _buildFirstRowLeftColumn(isDesktop)), // Expanded(child: _buildFirstRowLeftColumn(isDesktop)),
// SizedBox(width: 20), // SizedBox(width: 20),
@ -1136,7 +1132,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
_selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today)
? _selectedDateOfBirth! ? _selectedDateOfBirth!
: today, : today,
firstDate: today, firstDate: DateTime(1900),
lastDate: DateTime(2100), lastDate: DateTime(2100),
); );
@ -1436,21 +1432,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
), ),
SizedBox(height: 3), SizedBox(height: 3),
apiselectedUser != null apiselectedUser != null
? Row( ? SizedBox()
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Change Password",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w200,
color: Colors.black)),
SizedBox(height: 5),
],
),
],
)
: Row( : Row(
children: [ children: [
Column( Column(
@ -1876,7 +1858,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
), ),
), ),
SizedBox(height: 10), SizedBox(height: 10),
if (base64PDF != null) if (passportFile != null || passportFileUrlFromApi != null)
// Centers the text // Centers the text
Container( Container(
@ -1890,57 +1872,46 @@ class _CreateUserFormState extends State<CreateUserForm> {
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
print('DOWLOAS- $base64PDF '); print('DOWNLOAD - $passportFile');
if (base64PDF != null && base64PDF!.isNotEmpty) { if (passportFile != null) {
try { try {
// Step 1: Clean the Base64 string // Step 1: Create a Blob directly from the file
String cleanedBase64 = base64PDF! final blob = html.Blob(
.replaceAll("\n", "") // Remove newlines [passportFile!], 'application/pdf');
.replaceAll(
"\r", "") // Remove carriage returns
.replaceAll(" ", "") // Remove spaces
.trim(); // Trim any whitespace
// Step 2: Ensure valid Base64 length (multiple of 4) // Step 2: Generate a download URL from the Blob
while (cleanedBase64.length % 4 != 0) {
cleanedBase64 += "_"; // Add '=' padding
}
// Step 3: Decode the cleaned Base64
Uint8List bytes;
try {
bytes = base64Decode(cleanedBase64);
} catch (e) {
print("Base64 decoding failed: $e");
return;
}
// Step 4: Create a Blob for download
final blob =
html.Blob([bytes], 'application/pdf');
final url = final url =
html.Url.createObjectUrlFromBlob(blob); html.Url.createObjectUrlFromBlob(blob);
// Step 5: Trigger the file download // Step 3: Create an invisible anchor to trigger download
final anchor = html.AnchorElement(href: url) final anchor = html.AnchorElement(href: url)
..setAttribute("download", ..setAttribute("download",
selectedFileNames ?? "document.pdf") selectedFileNames ?? "document.pdf")
..style.display = "none"; ..style.display = "none";
// Step 4: Add anchor to DOM and click it
html.document.body!.append(anchor); html.document.body!.append(anchor);
anchor.click(); anchor.click();
// Step 6: Clean up // Step 5: Clean up
anchor.remove(); anchor.remove();
html.Url.revokeObjectUrl(url); html.Url.revokeObjectUrl(url);
print("Download successful!"); print("Download triggered successfully!");
} catch (e) { } catch (e) {
print("Error downloading file: $e"); print("Error during download: $e");
} }
} else if (passportFileUrlFromApi != null) {
// Trigger file download from the server path
final anchor = html.AnchorElement(
href: passportFileUrlFromApi!)
..target = 'blank'
..download =
selectedFileNames ?? "document.pdf"
..click();
} else { } else {
print("No file to download."); print("No file available to download.");
} }
}, },
child: Container( child: Container(

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
@ -18,6 +19,7 @@ class _UserListScreenState extends State<UserListScreen> {
late Future<List<dynamic>> futureUsers; late Future<List<dynamic>> futureUsers;
List<dynamic>? apiCountryData; List<dynamic>? apiCountryData;
String? selectedUserId; String? selectedUserId;
String? orgId;
Future<String?> getToken() async { Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
@ -25,7 +27,8 @@ class _UserListScreenState extends State<UserListScreen> {
} }
Future<List<dynamic>> fetchUsers() async { Future<List<dynamic>> fetchUsers() async {
final String apiUrlData = '$apiUrl/api/users'; orgId = await getOrgId();
final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
final String? token = await getToken(); final String? token = await getToken();
print("Fetch Users"); print("Fetch Users");
@ -110,7 +113,59 @@ class _UserListScreenState extends State<UserListScreen> {
print("handDel - $userId"); print("handDel - $userId");
} }
void handleToggleUserStatus(String userId, String currentStatus) async { Future<void> createUserData(
Map<String, dynamic> userData, String userId, String newStatus) async {
final uri = Uri.parse('$apiUrl/api/users/update/$userId');
final String? token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
// If updating, spoof the method Laravel-style
request.fields['_method'] = 'PUT';
request.fields['user_id'] = userId;
print("STatus 2 - $newStatus");
// Add all non-null and non-empty user data fields
userData.forEach((key, value) {
if (value != null && value.toString().trim().isNotEmpty) {
request.fields[key] = value.toString();
}
});
request.fields['is_active'] = newStatus;
print("🚀 Sending request with fields: ${request.fields}");
try {
final streamedResponse = await request.send();
final response = await http.Response.fromStream(streamedResponse);
print("Response status: ${response.statusCode}");
print("Response body: ${response.body}");
if (response.statusCode == 200 || response.statusCode == 201) {
print("✅ User Status submitted successfully! ");
print("📨 Response: ${response.body}");
refreshUserList();
} else {
print("❌ Submission failed. Status: ${response.statusCode}");
print("📨 Body: ${response.body}");
}
} catch (e) {
print("🔥 Error submitting user: $e");
}
}
void handleToggleUserStatus(String userId, String currentStatus,
Map<String, dynamic> userData) async {
print("Toggling user status - $userId (Current: $currentStatus)"); print("Toggling user status - $userId (Current: $currentStatus)");
final String apiUrlData = final String apiUrlData =
@ -125,28 +180,32 @@ class _UserListScreenState extends State<UserListScreen> {
// Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1") // Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1")
String newStatus = (currentStatus == "1") ? "0" : "1"; String newStatus = (currentStatus == "1") ? "0" : "1";
try { print("STatus 1 - $newStatus");
final response = await http.put(
Uri.parse(apiUrlData),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode({
"is_active": newStatus // Set new status dynamically
}),
);
if (response.statusCode == 200) { createUserData(userData, userId, newStatus);
print("User status updated successfully to $newStatus!");
refreshUserList(); // Refresh users list after update // try {
} else { // final response = await http.put(
print("Failed to update user status. Status: ${response.statusCode}"); // Uri.parse(apiUrlData),
print("Error: ${response.body}"); // headers: {
} // 'Authorization': 'Bearer $token',
} catch (e) { // 'Content-Type': 'application/json',
print("Error updating user status: $e"); // },
} // body: jsonEncode({
// "is_active": newStatus // Set new status dynamically
// }),
// );
//
// if (response.statusCode == 200) {
// print("User status updated successfully to $newStatus!");
// refreshUserList(); // Refresh users list after update
// } else {
// print("Failed to update user status. Status: ${response.statusCode}");
// print("Error: ${response.body}");
// }
// } catch (e) {
// print("Error updating user status: $e");
// }
} }
// Refresh user list after update // Refresh user list after update
@ -207,10 +266,12 @@ class _UserListScreenState extends State<UserListScreen> {
// Print the resolved value // Print the resolved value
print("CREATELIAS - $users"); print("CREATELIAS - $users");
context.go("/CreateUserDetails", extra: { context.go("/CreateUserDetails"
// 'apiCountryData': apiCountryData, // extra: {
'apiUserData': users, // // 'apiCountryData': apiCountryData,
}); // 'apiUserData': users,
// }
);
if (!isDesktop) Navigator.pop(context); if (!isDesktop) Navigator.pop(context);
}, },
child: Row( child: Row(
@ -235,7 +296,60 @@ class _UserListScreenState extends State<UserListScreen> {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator()); return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}")); return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
color: Colors.redAccent,
size: 60,
),
SizedBox(height: 16),
Text(
"Oops!",
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.redAccent,
),
),
SizedBox(height: 8),
Text(
"No User Available",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
SizedBox(height: 20),
Text(
" Please Create NewUser",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
),
),
SizedBox(height: 20),
// ElevatedButton.icon(
// onPressed: () {
// // Optional: retry logic or navigation
// },
// icon: Icon(Icons.refresh),
// label: Text("Try Again"),
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.blueAccent,
// ),
// ),
],
),
),
);
} else if (!snapshot.hasData || snapshot.data!.isEmpty) { } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text("No users found")); return Center(child: Text("No users found"));
} }
@ -467,8 +581,8 @@ class _UserListScreenState extends State<UserListScreen> {
)), )),
DataCell(GestureDetector( DataCell(GestureDetector(
onTap: () { onTap: () {
handleToggleUserStatus( handleToggleUserStatus(user['user_id'],
user['user_id'], user['is_active']); user['is_active'], user);
}, },
child: Text( child: Text(
user['is_active'] == "1" user['is_active'] == "1"
@ -519,6 +633,13 @@ class _UserListScreenState extends State<UserListScreen> {
? null ? null
: () { : () {
print("USER: $user"); print("USER: $user");
// final userJson = jsonEncode(
// user); // Convert user map to string
// final encodedUser =
// Uri.encodeComponent(
// userJson);
context.go( context.go(
"/CreateUserDetails", "/CreateUserDetails",
extra: { extra: {
@ -529,25 +650,6 @@ class _UserListScreenState extends State<UserListScreen> {
}, },
), ),
), ),
MouseRegion(
cursor: user['is_active'] == "0"
? SystemMouseCursors.forbidden
: SystemMouseCursors.click,
child: IconButton(
icon: Icon(Icons.delete,
color: user['is_active'] == "0"
? Colors.grey
: Colors.redAccent),
onPressed: user['is_active'] == "0"
? null
: () {
print(
"USER ID: ${user['user_id']}");
var userId = user['user_id'];
handleDelete(userId);
},
),
),
], ],
), ),
), ),

View File

@ -5,86 +5,77 @@ import 'package:go_router/go_router.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class CustomDrawer extends StatefulWidget {
class CustomDrawer extends StatefulWidget{ final bool isDesktop;
const CustomDrawer({super.key, required this.isDesktop});
final bool isDesktop;
const CustomDrawer({super.key, required this.isDesktop});
@override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer>{
String? token;
Map<String,dynamic>? userData;
Map<String, dynamic>? fetchedUserData;
Map<String, dynamic> userDetails = {};
@override
void initState() {
super.initState();
initializeData();
}
Future<void> initializeData() async{
print("initializeDatainitializeData");
token = await getToken();
fetchedUserData = await getUserData();
if(token == null || fetchedUserData == null)
{
print("Token or USerId missing");
return;
}
setState(() {
userData = fetchedUserData;
});
}
Future<String?> getToken() async{
final prefs = await SharedPreferences.getInstance();
return prefs.getString("auth_token");
}
Future <Map<String,dynamic>?> getUserData() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if(userDataString != null){
try{
userDetails = jsonDecode(userDataString);
return{
"user_id" : userDetails["user_id"].toString(),
"name" : "${userDetails["first_name"]} ${userDetails["last_name"]}",
"email" : userDetails["email"] ?? "",
};
}catch (e) {
print("Error decoding user data: $e");
return null;
}
}
return null;
}
@override @override
Widget build(BuildContext context){ _CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
String? token;
Map<String, dynamic>? userData;
Map<String, dynamic>? fetchedUserData;
Map<String, dynamic> userDetails = {};
@override
void initState() {
super.initState();
initializeData();
}
Future<void> initializeData() async {
print("initializeDatainitializeData");
token = await getToken();
fetchedUserData = await getUserData();
if (token == null || fetchedUserData == null) {
print("Token or USerId missing");
return;
}
setState(() {
userData = fetchedUserData;
});
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("auth_token");
}
Future<Map<String, dynamic>?> getUserData() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
userDetails = jsonDecode(userDataString);
return {
"user_id": userDetails["user_id"].toString(),
"name": "${userDetails["first_name"]} ${userDetails["last_name"]}",
"email": userDetails["email"] ?? "",
};
} catch (e) {
print("Error decoding user data: $e");
return null;
}
}
return null;
}
@override
Widget build(BuildContext context) {
Widget drawerContent = Container( Widget drawerContent = Container(
color: Color(0xFFF3F3FA), color: Color(0xFFF3F3FA),
child: Column( child: Column(
children: [ children: [
GestureDetector( GestureDetector(
onTap:(){ onTap: () {
print("ONTAP Custom"); print("ONTAP Custom");
print("ONTAP Custom- $userDetails ");
context.go( context.go(
"/CreateUserDetails", "/CreateUserDetails",
extra: { extra: {
@ -94,69 +85,71 @@ class _CustomDrawerState extends State<CustomDrawer>{
}, },
); );
}, },
child :SizedBox( child: SizedBox(
height: 80, height: 80,
child: Container( child: Container(
color: Color(0xFFF3F3FA), color: Color(0xFFF3F3FA),
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
width: double.infinity, width: double.infinity,
child: Row( child: Row(
children: [
children: [ Padding(
Padding( padding: const EdgeInsets.all(2.0),
padding: const EdgeInsets.all(2.0), child: Container(
child: Container( height: 50,
height: 50, width: 50,
width: 50, decoration: BoxDecoration(
decoration:BoxDecoration( color: Colors.blueAccent, shape: BoxShape.circle),
color: Colors.blueAccent, child: Column(
shape: BoxShape.circle mainAxisAlignment: MainAxisAlignment.center,
) , children: [
child: Column( Text(
mainAxisAlignment: MainAxisAlignment.center, userData?["name"]?.isNotEmpty == true
children: [ Text( ? userData!["name"]![0].toUpperCase()
userData?["name"]?.isNotEmpty == true : "N/A",
? userData!["name"]![0].toUpperCase() style: TextStyle(
: "N/A", color: Colors.white, fontSize: 25),
style: TextStyle(color: Colors.white, fontSize: 25), ),
),],), ],
), ),
), ),
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
userData?["name"] ?? "N/A", userData?["name"] ?? "N/A",
style: TextStyle(color: Colors.black87, fontSize: 11), style:
), TextStyle(color: Colors.black87, fontSize: 11),
Text( ),
userData?["email"] ?? "N/A", Text(
style: TextStyle(color: Colors.black45, fontSize: 10), userData?["email"] ?? "N/A",
), style:
TextStyle(color: Colors.black45, fontSize: 10),
],) ),
],) ],
)
],
)),
), ),
), ),
), _buildDrawerItem(context, Icons.home, 'Home', '/home'),
_buildDrawerItem(context, Icons.home,'Home', '/home'), _buildExpandableItem(context, Icons.assessment, 'Plans', [
_buildExpandableItem(context,Icons.assessment,'Plans',[ _buildSubDrawerItem(context, 'My Travel Request', '/listPlan'),
_buildSubDrawerItem(context,'My Travel Request','/listPlan'),
// _buildSubDrawerItem(context,'PlanB','/PlanB') // _buildSubDrawerItem(context,'PlanB','/PlanB')
]), ]),
_buildExpandableItem(context,Icons.account_circle_outlined,'User ',[ _buildExpandableItem(
_buildSubDrawerItem(context,'User List','/listUser'), context, Icons.account_circle_outlined, 'User ', [
_buildSubDrawerItem(context, 'User List', '/listUser'),
// _buildSubDrawerItem(context,'PlanB','/PlanB') // _buildSubDrawerItem(context,'PlanB','/PlanB')
]), ]),
_buildExpandableItem(context, Icons.policy, 'Policy ', [
_buildExpandableItem(context,Icons.policy,'Policy ',[ _buildSubDrawerItem(context, 'Policy', '/Policy'),
_buildSubDrawerItem(context,'Policy','/Policy'),
// _buildSubDrawerItem(context,'PlanB','/PlanB') // _buildSubDrawerItem(context,'PlanB','/PlanB')
]), ]),
_buildDrawerItem(context,Icons.logout,'Logout','/') _buildDrawerItem(context, Icons.logout, 'Logout', '/')
], ],
), ),
); );
@ -169,17 +162,17 @@ class _CustomDrawerState extends State<CustomDrawer>{
); );
} else { } else {
// Drawer for Mobile & Tablet** // Drawer for Mobile & Tablet**
return Drawer(child: ListView(padding: EdgeInsets.zero, children: [drawerContent])); return Drawer(
child: ListView(padding: EdgeInsets.zero, children: [drawerContent]));
} }
} }
/// **Reusable Drawer Item** /// **Reusable Drawer Item**
Widget _buildDrawerItem(BuildContext context, IconData icon, String title, String route) Widget _buildDrawerItem(
{ BuildContext context, IconData icon, String title, String route) {
return ListTile( return ListTile(
leading: Icon(icon), leading: Icon(icon),
title: Text(title), title: Text(title),
onTap: () async { onTap: () async {
if (route == '/') { if (route == '/') {
// Handle logout separately // Handle logout separately
@ -189,11 +182,11 @@ class _CustomDrawerState extends State<CustomDrawer>{
} else { } else {
context.go(route); context.go(route);
} }
} });
);
} }
Widget _buildExpandableItem(BuildContext context, IconData icon, String title, List<Widget>children){ Widget _buildExpandableItem(BuildContext context, IconData icon, String title,
List<Widget> children) {
return ExpansionTile( return ExpansionTile(
leading: Icon(icon), leading: Icon(icon),
title: Text(title), title: Text(title),
@ -203,16 +196,13 @@ class _CustomDrawerState extends State<CustomDrawer>{
); );
} }
Widget _buildSubDrawerItem(BuildContext context, String title, String route) Widget _buildSubDrawerItem(BuildContext context, String title, String route) {
{
return ListTile( return ListTile(
title: Text(title), title: Text(title),
onTap: (){ onTap: () {
context.go(route); context.go(route);
if (!widget.isDesktop) Navigator.pop(context); if (!widget.isDesktop) Navigator.pop(context);
}, },
); );
} }
} }

View File

@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:frontend/Screens/authentication/login/login_page.dart'; import 'package:frontend/Screens/authentication/login/login_page.dart';
import 'package:frontend/Screens/authentication/loginPage1.dart'; import 'package:frontend/Screens/authentication/loginPage1.dart';
@ -11,7 +12,6 @@ import 'package:go_router/go_router.dart';
final GoRouter router = GoRouter( final GoRouter router = GoRouter(
routes: [ routes: [
GoRoute( GoRoute(
path: '/', path: '/',
builder: (context, state) => LoginPage(), builder: (context, state) => LoginPage(),
@ -35,11 +35,27 @@ final GoRouter router = GoRouter(
GoRoute( GoRoute(
path: '/CreateUserDetails', path: '/CreateUserDetails',
builder: (context, state) => CreateUserForm(), builder: (context, state) => CreateUserForm(),
// builder: (context, state) {
// final userParam = state.uri.queryParameters['user'];
//
// final isEditProfile =
// state.uri.queryParameters['isEditProfile'] == 'true';
// final isViewMode = state.uri.queryParameters['isViewMode'] == 'true';
//
// final user = userParam != null
// ? jsonDecode(Uri.decodeComponent(userParam))
// : null;
//
// return CreateUserForm(
// apiselectedUser: user,
// isEditProfile: isEditProfile,
// isViewMode: isViewMode,
// );
// }
), ),
GoRoute( GoRoute(
path: '/Policy', path: '/Policy',
builder: (context,state) => Policy(), builder: (context, state) => Policy(),
), ),
], ],
); );

View File

@ -3,11 +3,8 @@ import 'package:frontend/utils/auth_utils.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../../config/apiUrl.dart'; import '../../config/apiUrl.dart';
class ApiService { class ApiService {
Future<List<dynamic>> fetchCountryList() async { Future<List<dynamic>> fetchCountryList() async {
final String apiUrldata = '$apiUrl/api/getcountryMaster'; final String apiUrldata = '$apiUrl/api/getcountryMaster';
final token = await getToken(); final token = await getToken();
@ -29,7 +26,8 @@ class ApiService {
print("Country - $data"); print("Country - $data");
if (!data.containsKey('data') || data['data'] is! List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception("Invalid response format: 'data' field is missing or not a List"); throw Exception(
"Invalid response format: 'data' field is missing or not a List");
} }
return data['data']; return data['data'];
@ -42,7 +40,8 @@ class ApiService {
} }
Future<List<dynamic>> fetchUsers() async { Future<List<dynamic>> fetchUsers() async {
final String apiUrlData = '$apiUrl/api/users'; String? ordId = await getOrgId();
final String apiUrlData = '$apiUrl/api/users?org_id=$ordId';
final String? token = await getToken(); final String? token = await getToken();
print("Fetch Users"); print("Fetch Users");
@ -68,7 +67,6 @@ class ApiService {
} }
} }
Future<List> fetchCostCenter() async { Future<List> fetchCostCenter() async {
final String apiUrldata = '$apiUrl/api/getCostCenterMaster'; final String apiUrldata = '$apiUrl/api/getCostCenterMaster';
@ -95,27 +93,26 @@ class ApiService {
final data = json.decode(response.body); final data = json.decode(response.body);
print(data); print(data);
if (!data.containsKey('data') || data['data'] is!List) { if (!data.containsKey('data') || data['data'] is! List) {
throw Exception("Invalid response format: 'data' field is missing or not a List"); throw Exception(
"Invalid response format: 'data' field is missing or not a List");
} }
List <dynamic> plansJson = data['data']; // 'data' is a Map, not a List List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List
// setState(() { // setState(() {
// apiCostData = plansJson; // Store API response in state // apiCostData = plansJson; // Store API response in state
// if(apiCostData!.isNotEmpty){ // if(apiCostData!.isNotEmpty){
// selectedCostCenterId =apiCostData?.first['department_id']; // selectedCostCenterId =apiCostData?.first['department_id'];
// } // }
// if (apiCostData != null && apiCostData!.isNotEmpty) { // if (apiCostData != null && apiCostData!.isNotEmpty) {
// selectedCostCenterId ??= apiCostData!.first['department_id']?.toString(); // selectedCostCenterId ??= apiCostData!.first['department_id']?.toString();
// } // }
// }); // });
print('plansJSON'); print('plansJSON');
return plansJson; return plansJson;
} catch (e) { } catch (e) {
throw Exception('Error parsing response: $e'); throw Exception('Error parsing response: $e');
} }
@ -137,20 +134,23 @@ class ApiService {
Uri.parse(apiUrldata), Uri.parse(apiUrldata),
headers: { headers: {
'Authorization': 'Bearer $token', 'Authorization': 'Bearer $token',
'Content-Type': 'application/json',},); 'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) { if (response.statusCode == 200) {
try { try {
final data = json.decode(response.body); final data = json.decode(response.body);
print(data); print(data);
if (!data.containsKey('data') || data['data'] is! Map) { if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception("Invalid response format: 'data' field is missing or not a Map"); throw Exception(
"Invalid response format: 'data' field is missing or not a Map");
} }
Map<String, dynamic> plansJson = data['data']; // 'data' is a Map, not a List Map<String, dynamic> plansJson =
data['data']; // 'data' is a Map, not a List
return plansJson; return plansJson;
} catch (e) { } catch (e) {
throw Exception('Error parsing response: $e'); throw Exception('Error parsing response: $e');
} }
@ -158,6 +158,4 @@ class ApiService {
throw Exception('Failed to load plans'); throw Exception('Failed to load plans');
} }
} }
} }

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
Future<String?> getToken() async { Future<String?> getToken() async {
@ -9,3 +11,18 @@ Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
return prefs.getString('userId'); return prefs.getString('userId');
} }
Future<String?> getOrgId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["org_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}

View File

@ -30,7 +30,7 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
return Container( return Container(
width: widget.width ?? // Use custom width if provided, else default width: widget.width ?? // Use custom width if provided, else default
(widget.isDesktop (widget.isDesktop
? MediaQuery.of(context).size.width * 0.4 ? MediaQuery.of(context).size.width * 0.3
: MediaQuery.of(context).size.width * 0.85), : MediaQuery.of(context).size.width * 0.85),
padding: widget.padding, padding: widget.padding,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -42,14 +42,13 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
), ),
boxShadow: widget.isFocused boxShadow: widget.isFocused
? [ ? [
BoxShadow( BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3), color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10, blurRadius: 10,
spreadRadius: 2, spreadRadius: 2,
offset: Offset(0, 4), offset: Offset(0, 4),
),
), ]
]
: [], : [],
), ),
child: widget.child, child: widget.child,

View File

@ -21,16 +21,18 @@ class CustomTextFieldForexWrapper extends StatefulWidget {
}); });
@override @override
_CustomTextFieldForexWrapperState createState() => _CustomTextFieldForexWrapperState(); _CustomTextFieldForexWrapperState createState() =>
_CustomTextFieldForexWrapperState();
} }
class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrapper> { class _CustomTextFieldForexWrapperState
extends State<CustomTextFieldForexWrapper> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: widget.width ?? // Use custom width if provided, else default width: widget.width ?? // Use custom width if provided, else default
(widget.isDesktop (widget.isDesktop
? MediaQuery.of(context).size.width * 0.25 ? MediaQuery.of(context).size.width * 0.2
: MediaQuery.of(context).size.width * 0.8), : MediaQuery.of(context).size.width * 0.8),
padding: widget.padding, padding: widget.padding,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -43,14 +45,13 @@ class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrappe
), ),
boxShadow: widget.isFocused boxShadow: widget.isFocused
? [ ? [
BoxShadow( BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3), color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10, blurRadius: 10,
spreadRadius: 2, spreadRadius: 2,
offset: Offset(0, 4), offset: Offset(0, 4),
),
), ]
]
: [], : [],
), ),
child: widget.child, child: widget.child,

View File

@ -17,6 +17,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.0" version: "2.12.0"
bcrypt:
dependency: "direct main"
description:
name: bcrypt
sha256: "9dc3f234d5935a76917a6056613e1a6d9b53f7fa56f98e24cd49b8969307764b"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -49,6 +57,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -105,6 +121,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "7.0.1"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "36a1652d99cb6bf8ccc8b9f43aded1fd60b234d23ce78af422c07f950a436ef7"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -118,6 +142,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3"
url: "https://pub.dev"
source: hosted
version: "2.0.27"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -145,7 +177,7 @@ packages:
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
http_parser: http_parser:
dependency: transitive dependency: "direct main"
description: description:
name: http_parser name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
@ -453,6 +485,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
url: "https://pub.dev"
source: hosted
version: "5.12.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -462,5 +502,5 @@ packages:
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
sdks: sdks:
dart: ">=3.7.0-0 <4.0.0" dart: ">=3.7.0 <4.0.0"
flutter: ">=3.27.0" flutter: ">=3.27.0"

View File

@ -43,6 +43,7 @@ dependencies:
dropdown_search: ^5.0.6 dropdown_search: ^5.0.6
file_picker: ^10.0.0 file_picker: ^10.0.0
bcrypt: ^1.1.3 bcrypt: ^1.1.3
http_parser: ^4.1.2
dev_dependencies: dev_dependencies: