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:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../data/models/Searchtraveller.dart';
import '../../data/models/searchUser.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_traveller.dart';
class UserSelectionDialog extends StatefulWidget{
class UserSelectionDialog extends StatefulWidget {
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
_UserSelectionDialogState createState() => _UserSelectionDialogState();
}
class _UserSelectionDialogState extends State<UserSelectionDialog>{
class _UserSelectionDialogState extends State<UserSelectionDialog> {
TextEditingController _controller = TextEditingController();
TextEditingController _searchController = TextEditingController();
// List<String> _filteredUsers = [];
@ -31,21 +35,23 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
List<Map<String, dynamic>> _filteredList = [];
List<SearchTraveler> _filteredTraveller = [];
String userIdSelected = " ";
bool isTraveller = false;
bool isTraveller = false;
bool _showTravellerForm = false;
final _formKey = GlobalKey<FormState>();
String? orgId;
final _formKey = GlobalKey<FormState>();
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
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 {
final token = await getToken();
@ -62,8 +68,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
);
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
@ -88,18 +93,69 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
for (var user in _users) {
print("${user.firstName} ${user.lastName}");
}
} 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 {
throw Exception('Failed to load users. Status Code: ${response.statusCode}');
throw Exception(
'Failed to load users. Status Code: ${response.statusCode}');
}
} catch (e) {
print("Error fetching users: $e");
}
}
Future<void> fetchTraveller() async {
orgId = await getOrgId();
final String apiUrldata = '$apiUrl/api/travellers?org_id=$orgId';
try {
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final Map<String, dynamic> responseBody = json.decode(response.body);
print("API Response: $responseBody"); // Debugging
if (responseBody.containsKey('data') && responseBody['data'] is List) {
List<dynamic> travellerList = responseBody['data'];
setState(() {
_traveller = travellerList
.map((user) => SearchTraveler.fromJson(user))
.toList();
_filteredTraveller = List.from(_traveller);
});
print("Users fetched: ${_users.length}");
for (var travvelr in _traveller) {
print("${travvelr.firstName} ${travvelr.lastName}");
}
} else {
throw Exception(
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
}
} else {
throw Exception(
'Failed to load users. Status Code: ${response.statusCode}');
}
} catch (e) {
print("Error fetching traveller: $e");
}
}
void _filterUsers1(String query) {
print("Filtering users...");
setState(() {
@ -115,7 +171,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.alternateMobileNo ?? ""
];
return searchFields.any((field) => field.contains(query.toLowerCase()));
return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).toList();
}
});
@ -126,7 +183,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
}
}
void _filterUsers(String query) {
print("Filtering _filterUsersTravellers...");
setState(() {
@ -146,9 +202,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.mobileNo ?? "",
user.alternateMobileNo ?? ""
];
return searchFields.any((field) => field.contains(query.toLowerCase()));
return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((user) => {"type": "user", "data": user}),
];
}
});
@ -156,7 +212,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
print("Filtered List:");
for (var item in _filteredList) {
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) {
_filteredList = [
..._users.map((user) => {"type": "user", "data": user}),
..._traveller.map((traveller) => {"type": "traveller", "data": traveller}),
..._traveller
.map((traveller) => {"type": "traveller", "data": traveller}),
];
} else {
_filteredList = [
@ -180,9 +238,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
user.mobileNo ?? "",
user.alternateMobileNo ?? ""
];
return searchFields.any((field) => field.contains(query.toLowerCase()));
return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((user) => {"type": "user", "data": user}),
..._traveller.where((traveller) {
List<String> searchFields = [
"${traveller.firstName} ${traveller.lastName}".toLowerCase(),
@ -190,7 +248,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
traveller.travellerId.toLowerCase() ?? "",
traveller.mobileNo ?? "",
];
return searchFields.any((field) => field.contains(query.toLowerCase()));
return searchFields
.any((field) => field.contains(query.toLowerCase()));
}).map((traveller) => {"type": "traveller", "data": traveller}),
];
}
@ -199,58 +258,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
print("Filtered List:");
for (var item in _filteredList) {
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
void initState() {
@ -268,7 +279,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
width: 400, // Adjust width as needed
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily
mainAxisSize:
MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [
Text("Please Select User", style: TextStyle(fontSize: 14)),
SizedBox(height: 10),
@ -280,15 +292,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
setState(() {
_showTravellerForm = false;
});
widget.title == "Others"? _filterUsersTravellers(query):
_filterUsers(query);
widget.title == "Others"
? _filterUsersTravellers(query)
: _filterUsers(query);
},
decoration: InputDecoration(
hintText: "Search for a user",
hintStyle: TextStyle(fontSize: 14),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blueAccent, width: 2),
@ -301,7 +314,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
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(
onPressed: () {
setState(() {
@ -309,7 +323,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
_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
_searchController.text.isNotEmpty
? SizedBox(
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child: _filteredList.isEmpty
? Center(
child: Text(
"No users found",
style: TextStyle(fontSize: 14, color: Colors.grey),
),
)
: ListView.builder(
// itemCount: _filteredUsers.length,
itemCount: _filteredList.length,
itemBuilder: (context, index) {
// final user = _filteredUsers[index];
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child: _filteredList.isEmpty
? Center(
child: Text(
"No users found",
style:
TextStyle(fontSize: 14, color: Colors.grey),
),
)
: ListView.builder(
// itemCount: _filteredUsers.length,
itemCount: _filteredList.length,
itemBuilder: (context, index) {
// final user = _filteredUsers[index];
final item = _filteredList[index];
final user = item["data"]; // Extract user object
final userType = item["type"]; // "user" or "traveller"
final item = _filteredList[index];
final user = item["data"]; // Extract user object
final userType =
item["type"]; // "user" or "traveller"
return ListTile(
title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
subtitle: Text("ID: ${userType == "user" ? user.userId : user.travellerId}"),
onTap: () {
String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
userIdSelected = userType == "user" ? user.userId : user.travellerId;
isTraveller = userType == "traveller";
});
print("Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected");
},
);
},
),
) : SizedBox.shrink(),
return ListTile(
title: Text(
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
subtitle: Text(
"ID: ${userType == "user" ? user.userId : user.travellerId}"),
onTap: () {
String selectedUser =
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
userIdSelected = userType == "user"
? user.userId
: user.travellerId;
isTraveller = userType == "traveller";
});
print(
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected");
},
);
},
),
)
: SizedBox.shrink(),
// Traveler Form
if (_showTravellerForm)
@ -366,13 +391,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: const EdgeInsets.all(16.0),
child: TravelerForm(
formKey: _formKey,
onSubmit: (String fullName, String travellerId, bool isTraveller) {
widget.onSubmit(fullName, travellerId,isTraveller); // Pass the data up
onSubmit: (String fullName, String travellerId,
bool isTraveller) {
widget.onSubmit(fullName, travellerId,
isTraveller); // Pass the data up
},
firstNameController: TextEditingController(),
lastNameController: TextEditingController(),
emailController: TextEditingController(),
mobileController: TextEditingController(),
orgId: orgId,
),
),
),
@ -392,7 +420,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () => Navigator.pop(context),
child: Text("Cancel",),
child: Text(
"Cancel",
),
),
SizedBox(width: 10),
ElevatedButton(
@ -406,8 +436,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
print("Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(_searchController.text,userIdSelected,isTraveller);
print(
"Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(
_searchController.text, userIdSelected, isTraveller);
Navigator.pop(context);
},
child: Text("Submit"),
@ -419,7 +451,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
),
);
}
}
class TravelerForm extends StatefulWidget {
@ -427,25 +458,24 @@ class TravelerForm extends StatefulWidget {
final TextEditingController lastNameController;
final TextEditingController emailController;
final TextEditingController mobileController;
final String? orgId;
final GlobalKey<FormState> formKey;
final void Function(String, String, bool) onSubmit;
TravelerForm({
required this.formKey,
required this.firstNameController,
required this.lastNameController,
required this.emailController,
required this.mobileController,
required this.onSubmit
});
TravelerForm(
{required this.formKey,
required this.orgId,
required this.firstNameController,
required this.lastNameController,
required this.emailController,
required this.mobileController,
required this.onSubmit});
@override
_TravelerFormState createState() => _TravelerFormState();
}
class _TravelerFormState extends State<TravelerForm> {
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
@ -472,27 +502,29 @@ class _TravelerFormState extends State<TravelerForm> {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(value)) {
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
.hasMatch(value)) {
return 'Enter a valid email address';
}
return null;
}
void _onSubmit(BuildContext context) {
Future<void> _onSubmit(BuildContext context) async {
bool isValid = _validateForm();
print("Form Validation Result: $isValid");
if (isValid) {
print("Validation Success");
_submitForm(context);
} else {
print("Validation Failed"); // This should now print if validation fails
}
}
Future<void> _submitForm(BuildContext context) async {
Map<String, String> requestBody = {
"org_id": widget.orgId!,
"first_name": widget.firstNameController.text,
"last_name": widget.lastNameController.text,
"email": widget.emailController.text,
@ -516,24 +548,24 @@ class _TravelerFormState extends State<TravelerForm> {
body: jsonEncode(requestBody),
);
if (response.statusCode == 200 || response.statusCode == 201) {
if (response.statusCode == 200 || response.statusCode == 201) {
final Map<String, dynamic> responseData = jsonDecode(response.body); // Parse response
if (responseData["success"] == true && responseData.containsKey("data")) {
final Map<String, dynamic> responseData =
jsonDecode(response.body); // Parse response
if (responseData["success"] == true &&
responseData.containsKey("data")) {
final travellerData = responseData["data"];
String travellerId = travellerData["traveller_id"];
String firstName = travellerData["first_name"];
String lastName = travellerData["last_name"];
print("Traveller Added: ID: $travellerId, Name: $firstName $lastName");
print(
"Traveller Added: ID: $travellerId, Name: $firstName $lastName");
// Pass data to callback
widget.onSubmit("$firstName $lastName", travellerId, true);
// Close the dialog
Navigator.pop(context);
}
@ -542,10 +574,11 @@ class _TravelerFormState extends State<TravelerForm> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Traveller added successfully!",
style: TextStyle(color: Colors.white), // Set text color
"Traveller added successfully!",
style: TextStyle(color: Colors.white), // Set text color
),
backgroundColor: Colors.green,
),
backgroundColor: Colors.green,),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
@ -564,7 +597,8 @@ class _TravelerFormState extends State<TravelerForm> {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
double widthFactor;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) {
widthFactor = 0.23;
@ -599,7 +633,8 @@ class _TravelerFormState extends State<TravelerForm> {
),
TextButton(
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';
class AccomodationScreen extends StatefulWidget {
final Function(bool) onClose; // Callback function
final Function(Map<String,dynamic>) onSaveAccomadation;
final Function(Map<String, dynamic>) onSaveAccomadation;
final Map<String, dynamic>? selectedItem;
final String? loginUser;
AccomodationScreen({
required this.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser});
AccomodationScreen(
{required this.onClose,
required this.onSaveAccomadation,
required this.selectedItem,
required this.loginUser});
@override
_AccomodationScreenState createState() => _AccomodationScreenState();
@ -31,7 +32,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
final FocusNode _checkOutTimeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _destinationController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _checkInController = TextEditingController();
@ -59,11 +59,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
Map<String, String> errorMessages = {};
Map<String, dynamic> get accomadationData {
Map<String,dynamic> data ={
Map<String, dynamic> data = {
"destination_city": _destinationController.text,
"hotel_name": _hotelNameController.text,
"checkin_date": _checkInController.text ,
"checkin_date": _checkInController.text,
"checkin_time": _checkInTimeController.text,
"checkout_date": _checkOutController.text,
"checkout_time": _checkOutTimeController.text,
@ -73,9 +72,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
};
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"];
} 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"];
}
}
@ -86,16 +87,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return TextEditingController(text: widget.selectedItem?[key] ?? "");
}
@override
void initState() {
super.initState();
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocused = focus);
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(
_destinationFocusNode, (focus) => _destinationFocused = focus);
_addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
_addFocusListener(_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
_addFocusListener(
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
_addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
_addFocusListener(
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city");
@ -106,7 +110,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
_checkOutTimeController = initController("checkout_time");
_commentsController = initController("comments");
_destinationController.addListener(() => _clearError("destination_city"));
_hotelNameController.addListener(() => _clearError("hotel_name"));
_checkInController.addListener(() => _clearError("checkin_date"));
@ -115,8 +118,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
_checkOutTimeController.addListener(() => _clearError("checkout_time"));
}
@override
void dispose() {
_destinationFocusNode.dispose();
@ -137,12 +138,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
});
}
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> requiredFields = ["destination_city", "hotel_name","checkin_date","checkin_time","checkout_date",
"checkout_time"];
List<String> requiredFields = [
"destination_city",
"hotel_name",
"checkin_date",
"checkin_time",
"checkout_date",
"checkout_time"
];
// Check validation for each field
for (String field in requiredFields) {
@ -154,25 +162,22 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return errorMessages.isEmpty; // Valid if there are no errors
}
void handleSave() {
print("Handle Save accomadationData $accomadationData");
void handleSave(){
print( "Handle Save accomadationData $accomadationData");
Map<String,dynamic> data = accomadationData;
Map<String, dynamic> data = accomadationData;
if (!isValidData(data)) {
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
}else {
} else {
widget.onSaveAccomadation(accomadationData);
}
widget.onClose(false);// Close screen after saving
widget.onClose(false); // Close screen after saving
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
@ -201,10 +206,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
Text("Accomodation Booking",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
@ -245,11 +251,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isDesktop
? Row(
children: _buildThirdRow(isDesktop),
)
children: _buildThirdRow(isDesktop),
)
: Column(
children: _buildThirdRow(isDesktop),
),
children: _buildThirdRow(isDesktop),
),
SizedBox(height: 10),
Row(
@ -275,6 +281,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
CustomTextFieldWrapper(
isFocused: _destinationFocused,
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
@ -291,14 +300,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
if (errorMessages["destination_city"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["destination_city"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
if (errorMessages["destination_city"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["destination_city"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
@ -320,6 +329,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
@ -336,15 +348,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
if (errorMessages["hotel_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["hotel_name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
if (errorMessages["hotel_name"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["hotel_name"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
];
}
@ -359,9 +370,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
? _selectedCheckInDate!
: today,
initialDate:
_selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
? _selectedCheckInDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
@ -395,7 +407,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}
//-------------------------------Check-In End
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
@ -405,9 +416,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
initialDate: _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
@ -417,7 +427,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_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 [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -483,14 +491,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
if (errorMessages["checkin_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkin_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
if (errorMessages["checkin_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkin_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
@ -535,14 +543,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
if (errorMessages["checkin_time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkin_time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
if (errorMessages["checkin_time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkin_time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
@ -566,7 +574,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
@ -586,17 +593,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
),
),
),
if (errorMessages["checkout_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkout_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
if (errorMessages["checkout_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["checkout_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
@ -634,12 +640,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
if (errorMessages["checkout_time"] != null) ...[
@ -671,7 +676,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,

View File

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

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

View File

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

View File

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

View File

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

View File

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

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_drawer.dart';
class ListPlans extends StatefulWidget{
class ListPlans extends StatefulWidget {
const ListPlans({super.key});
@override
_ListPlansState createState() => _ListPlansState();
}
class _ListPlansState extends State<ListPlans>{
class _ListPlansState extends State<ListPlans> {
late Future<List<Plan>> futurePlans;
String? userId;
String? orgId;
String? token;
@override
void initState(){
void initState() {
super.initState();
getToken();
initializeData();
// futurePlans = fetchPlans();
}
Future<void> initializeData ()async{
Future<void> initializeData() async {
token = await getToken();
userId = await getUserId();
orgId = await getOrgId();
if(token == null || userId == null){
if (token == null || userId == null) {
print("Token or USerId missing");
return;
}
else{
} else {
setState(() {
futurePlans = fetchPlans();
});
}
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if(userDataString != null){
try{
final Map<String,dynamic> userData = jsonDecode(userDataString);
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["user_id"]?.toString();
}catch(e){
} catch (e) {
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 {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
// Fetch API Data
Future<List<Plan>> fetchPlans() async {
// 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();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
@ -92,7 +99,7 @@ class _ListPlansState extends State<ListPlans>{
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Authorization': 'Bearer $token', // Add token here
'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';
print("API URL: $apiUrldata");
// final token = await getToken();
@ -119,36 +125,32 @@ class _ListPlansState extends State<ListPlans>{
final response = await http.put(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
},
);
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"];
} else {
throw Exception('Failed to load plans');
}
}
void viewPlan(String planId, {bool isViewMode = false}) async{
void viewPlan(String planId, {bool isViewMode = false}) async {
try {
Map<String, dynamic> planData = await getViewPlan(planId);
print("ViewAAA - $planData");
context.go('/createPlan',extra: {'planData': planData, 'isViewMode': isViewMode} );
context.go('/createPlan',
extra: {'planData': planData, 'isViewMode': isViewMode});
} catch (e) {
print("Error fetching plan: $e");
}
}
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
@ -192,13 +194,22 @@ class _ListPlansState extends State<ListPlans>{
foregroundColor: Colors.white,
backgroundColor: Colors.blueAccent),
onPressed: () {
context.go('/createPlan');
context.go('/createPlan', extra: {
// 'apiCountryData': apiCountryData,
'orgId': orgId,
});
if (!isDesktop) Navigator.pop(context);
},
child: Row(
children: [
Icon(Icons.add_circle,color: Colors.white,),
SizedBox(width: 5,),
Icon(
Icons.add_circle,
color: Colors.white,
),
SizedBox(
width: 5,
),
Text('NewPlan'),
],
),
@ -206,220 +217,290 @@ class _ListPlansState extends State<ListPlans>{
],
),
const SizedBox(height: 10),
FutureBuilder<List<Plan>>(
future: futurePlans, // Use the futurePlans variable
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}"));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text("No plans available"));
}
FutureBuilder<List<Plan>>(
future: futurePlans, // Use the futurePlans variable
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
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 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
plans.sort((a, b) => int.parse(b.planId.toString()).compareTo(int.parse(a.planId.toString())));
// return ResponsiveBuilder(
// 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(
// 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(),
// ),
//
//
// ),
// ),
// ),
// );
//
// },
// );
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 1300),
// width: MediaQuery.of(context).size.width ,
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
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(),
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(),
),
),
),
),
),
);
},
),
),
);
},
),
],
),
);
}
}

View File

@ -6,6 +6,8 @@ import 'package:responsive_builder/responsive_builder.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_user_form.dart';
class Policy extends StatefulWidget {
const Policy({super.key});
@ -18,6 +20,7 @@ class _PolicyState extends State<Policy> {
late String policyType = "domestic";
int? selectedServiceIndex = 1;
late String selectedService = "Train";
String? _selectedTripType;
bool showClass = true;
bool showCost = true;
@ -42,136 +45,206 @@ class _PolicyState extends State<Policy> {
}
Widget buildPolicyLayout(bool isDesktop) {
return Container(
margin: isDesktop
? EdgeInsets.all(20.0)
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
border: isDesktop
? Border.all(
width: 3,
color: Color(0xFFF7F7FB),
)
: null,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
color: Color(0xFFF7F7FB),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
// color: Colors.white, // Background to avoid overlapping
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"Choose Policy Type",
style: TextStyle(
fontSize: 18,
color: Colors.black,
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: isDesktop
? EdgeInsets.all(20.0)
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
border: isDesktop
? Border.all(
width: 2,
color: Color(0xFFF7F7FB),
)
: null,
color: Color(0xFFF7F7FB),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
// color: Color(0xFFF7F7FB),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
// color: Colors.white, // Background to avoid overlapping
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"Choose Policy Type",
style: TextStyle(
fontSize: 18,
color: Colors.black,
),
),
),
],
],
),
),
),
// Container(
// child: Row(
// children: [
// Expanded(
// child: GestureDetector(
// onTap: () {
// setState(() {
// policyType = "domestic";
// });
// },
// child: Container(
// padding: EdgeInsets.all(10),
// color: policyType == "domestic"
// ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7),
// // color: Colors.blue.shade300,
// child: Column(
// children: [
// Text(
// "Domestic",
// style: TextStyle(
// color: policyType == "domestic"
// ? Colors.white
// : Colors.black87,
// fontSize: 15,
// fontWeight: FontWeight.bold),
// ),
// ],
// ),
// ),
// ),
// ),
// Expanded(
// child: GestureDetector(
// onTap: () {
// setState(() {
// policyType = "international";
// });
// },
// child: Container(
// padding: EdgeInsets.all(10),
// // color: Color(0xFFEBEBF7),
// color: policyType == "international"
// ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7),
// // color: Color(0xFFE3F2FD),
//
// child: Column(
// children: [
// Text(
// "International",
// style: TextStyle(
// color: policyType == "international"
// ? Colors.white
// : Colors.black87,
// fontSize: 15,
// fontWeight: FontWeight.bold),
// ),
// ],
// ),
// ),
// )),
// ],
// ),
// ),
//
],
// Container(
// child: Row(
// children: [
// Expanded(
// child: GestureDetector(
// onTap: () {
// setState(() {
// policyType = "domestic";
// });
// },
// child: Container(
// padding: EdgeInsets.all(10),
// color: policyType == "domestic"
// ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7),
// // color: Colors.blue.shade300,
// child: Column(
// children: [
// Text(
// "Domestic",
// style: TextStyle(
// color: policyType == "domestic"
// ? Colors.white
// : Colors.black87,
// fontSize: 15,
// fontWeight: FontWeight.bold),
// ),
// ],
// ),
// ),
// ),
// ),
// Expanded(
// child: GestureDetector(
// onTap: () {
// setState(() {
// policyType = "international";
// });
// },
// child: Container(
// padding: EdgeInsets.all(10),
// // color: Color(0xFFEBEBF7),
// color: policyType == "international"
// ? Colors.blueAccent.shade100
// : Color(0xFFEBEBF7),
// // color: Color(0xFFE3F2FD),
//
// child: Column(
// children: [
// Text(
// "International",
// style: TextStyle(
// color: policyType == "international"
// ? Colors.white
// : Colors.black87,
// fontSize: 15,
// fontWeight: FontWeight.bold),
// ),
// ],
// ),
// ),
// )),
// ],
// ),
// ),
//
],
),
),
),
SizedBox(
height: 10,
),
isDesktop
? Expanded(
child: Row(
children: [
_buildPolicyCategoryList(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
)
: Expanded(
child: Column(
children: [
_buildPolicyCategoryList(isDesktop),
_buildPolicyCategory(isDesktop),
],
),
)
],
isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
Container(
// color: Colors.amber,
// color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
child: Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Policy Name",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.black)),
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(
width: isDesktop ? 180 : null,
height: isDesktop
? max((MediaQuery.of(context).size.height * 0.09), 10)
? max((MediaQuery.of(context).size.height * 0.075), 10)
: 45,
// max((MediaQuery.of(context).size.height * 0.09), 10)
child: GestureDetector(
onTap: () {
print("Selected Services - $service - $index");
@ -288,4 +363,64 @@ class _PolicyState extends State<Policy> {
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) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5),
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!)
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
Row(
children: [
Expanded(
child: Container(
// color:Colors.grey,
// color: Colors.grey,
padding:
const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5),
child: Column(
@ -181,19 +173,20 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
children: [
Expanded(
child: Container(
color: Colors.grey.shade100,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.63
: 600,
child: Column(
children: [
Container(
margin: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.only(right: 0),
decoration: BoxDecoration(
color: Colors.grey.shade50,
border: Border.all(
color: Colors.grey.shade50,
),
borderRadius: BorderRadius.circular(8)),
color: Colors.grey.shade100,
border: Border.all(
color: Colors.grey.shade100,
),
),
padding: const EdgeInsets.only(
top: 10, bottom: 10, left: 35, right: 35),
child: Row(
@ -232,7 +225,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
child: Container(
// color: Colors.grey,
margin: const EdgeInsets.only(right: 20),
color: Colors.grey.shade50,
color: Colors.grey.shade100,
child: Column(children: [
Container(
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:html' as html;
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/material.dart';
import 'package:flutter/services.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart' as http_parser;
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http_parser/http_parser.dart';
import '../../../config/apiUrl.dart';
import '../../../routes/custom_appBar.dart';
@ -31,6 +35,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
final ApiService apiService = ApiService();
String? userId;
String? orgId;
String? token;
@ -67,8 +72,11 @@ class _CreateUserFormState extends State<CreateUserForm> {
String? selectedFileNames;
Uint8List? passportDocumentBytes;
String? passportFileUrlFromApi;
String? base64PDF;
html.File? passportFile;
List<String> dataHeader = [
"Fname",
"Lname",
@ -109,12 +117,13 @@ class _CreateUserFormState extends State<CreateUserForm> {
"address": controllers["address"]?.text,
"gender": selectedGender,
"postal_code": controllers["postalCode"]?.text,
"country": selectedCountry,
"country_code": selectedCountry,
"employee_code": controllers["employeeCode"]?.text,
"user_type": selectedUserType,
"role_id": selectedRole,
"department_id": selectedDepartment,
"group_id": selectedLevel,
"first_approver": selectedFirstApprover,
@ -122,21 +131,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
"third_approver": selectedThirdApprover,
"passport_number": controllers["passportNumber"]?.text,
"place_of_issue": controllers["placeOfIssue"]?.text,
"passport_document": base64PDF,
"passport_document": passportFile,
"date_of_issue": controllers["dateOfIssue"]?.text,
"date_of_expiry": controllers["dateOfExpiry"]?.text,
"created_by": userId,
"is_active": "1",
// "passport_fileData": base64PDF,
"org_id": orgId,
// "passport_fileData": passportFile,
};
return data;
}
void updateData() {
Future<void> updateData() async {
// Ensure apiselectedUser is not null before printing
if (apiselectedUser != null) {
print("API Selected User Has Data - $apiselectedUser");
print("API Selected User Has Data - $widget.apiselectedUser");
setState(() {
// Wrap in setState to update the UI
@ -165,15 +175,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
apiselectedUser?["date_of_expiry"] ?? "";
selectedCountry = apiselectedUser?["country_code"]?.toString() ?? "";
selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? "";
base64PDF =
apiselectedUser?["passport_document"]?.toString().trim() ?? "";
selectedUserType =
selectedUserType = selectedUserType =
apiselectedUser?["user_type"]?.toString().trim() ?? "";
selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? "";
selectedDepartment =
apiselectedUser?["department_id"]?.toString().trim() ?? "";
// selectedDepartment =
// 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() ?? "";
selectedFirstApprover =
@ -184,6 +201,19 @@ class _CreateUserFormState extends State<CreateUserForm> {
apiselectedUser?["third_approver"]?.toString() ?? "";
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 {
print("API Selected User Has Data - No data available yet");
@ -195,37 +225,29 @@ class _CreateUserFormState extends State<CreateUserForm> {
super.initState();
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
// futureUsers = extraData['apiUserData']; // Extract futureUsers (Future<List<dynamic>>)
apiCountryData = null;
apiUserData = null;
apiselectedUser = null;
// apiselectedUser = null;
apiCostData = 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
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 =
GoRouterState.of(context).extra as Map<String, dynamic>?;
@ -250,6 +272,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
isEditProfile = extraData['isEditProfile'] ?? false;
});
print("selectedUser: $apiselectedUser");
// Add another post-frame callback to check after setState
await Future.delayed(Duration(
milliseconds: 100)); // Optional delay to ensure UI has updated
@ -296,7 +320,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
setState(() {
// apiUserData = users;
apiUserData = users.where((user) => user["role_id"] == "3").toList();
apiUserData = users.where((user) => user["role_id"] == "4").toList();
print("APIUSerDATa - $apiUserData");
@ -383,7 +407,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
super.dispose();
}
void handleSubmit() {
void handleSubmit() async {
print("USR Detail Submit");
printFormData();
@ -396,6 +420,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
return; // Stop execution if validation fails
} else {
print("USERDETAILS : $userDetials");
orgId = await getOrgId();
createUserData(userDetials);
}
}
@ -464,133 +490,102 @@ class _CreateUserFormState extends State<CreateUserForm> {
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');
// Ensure the file is a PDF
if (!file.type.contains("pdf")) {
print("Error: Not a PDF file");
return;
}
// Ensure the file is a PDF
if (!file.type.contains("pdf")) {
print("Error: Not a PDF file");
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;
}
setState(() {
selectedFileNames = file.name; // Store file name
passportDocumentBytes = reader.result as Uint8List; // Store file data
// 🔹 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;
}
});
setState(() {
selectedFileNames = file.name;
passportFile = file;
passportFileUrlFromApi = null;
});
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 {
bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty;
final String apiUrldata = isUpdating
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}'
: '$apiUrl/api/users/create';
final bool isUpdating =
apiselectedUser != null && apiselectedUser!.isNotEmpty;
final uri = Uri.parse(
isUpdating
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}'
: '$apiUrl/api/users/create',
);
if (token == null) {
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) {
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 {
final response = isUpdating
? await http.put(
Uri.parse(apiUrldata),
headers: {
'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),
);
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("Plan submitted successfully!");
print("Response: ${response.body}");
print("✅ User submitted successfully!");
print("📨 Response: ${response.body}");
context.go('/listUser');
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
print("❌ Submission failed. Status: ${response.statusCode}");
print("📨 Body: ${response.body}");
}
} catch (e) {
print(" Error submitting plan: $e");
print("🔥 Error submitting user: $e");
}
}
@ -755,6 +750,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
child: isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Expanded(child: _buildFirstRowLeftColumn(isDesktop)),
// SizedBox(width: 20),
@ -1136,7 +1132,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
_selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today)
? _selectedDateOfBirth!
: today,
firstDate: today,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
);
@ -1436,21 +1432,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
),
SizedBox(height: 3),
apiselectedUser != null
? Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Change Password",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w200,
color: Colors.black)),
SizedBox(height: 5),
],
),
],
)
? SizedBox()
: Row(
children: [
Column(
@ -1876,7 +1858,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
),
),
SizedBox(height: 10),
if (base64PDF != null)
if (passportFile != null || passportFileUrlFromApi != null)
// Centers the text
Container(
@ -1890,57 +1872,46 @@ class _CreateUserFormState extends State<CreateUserForm> {
children: [
GestureDetector(
onTap: () {
print('DOWLOAS- $base64PDF ');
print('DOWNLOAD - $passportFile');
if (base64PDF != null && base64PDF!.isNotEmpty) {
if (passportFile != null) {
try {
// Step 1: Clean the Base64 string
String cleanedBase64 = base64PDF!
.replaceAll("\n", "") // Remove newlines
.replaceAll(
"\r", "") // Remove carriage returns
.replaceAll(" ", "") // Remove spaces
.trim(); // Trim any whitespace
// Step 1: Create a Blob directly from the file
final blob = html.Blob(
[passportFile!], 'application/pdf');
// Step 2: Ensure valid Base64 length (multiple of 4)
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');
// Step 2: Generate a download URL from the Blob
final url =
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)
..setAttribute("download",
selectedFileNames ?? "document.pdf")
..style.display = "none";
// Step 4: Add anchor to DOM and click it
html.document.body!.append(anchor);
anchor.click();
// Step 6: Clean up
// Step 5: Clean up
anchor.remove();
html.Url.revokeObjectUrl(url);
print("Download successful!");
print("Download triggered successfully!");
} 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 {
print("No file to download.");
print("No file available to download.");
}
},
child: Container(

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:responsive_builder/responsive_builder.dart';
@ -18,6 +19,7 @@ class _UserListScreenState extends State<UserListScreen> {
late Future<List<dynamic>> futureUsers;
List<dynamic>? apiCountryData;
String? selectedUserId;
String? orgId;
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
@ -25,7 +27,8 @@ class _UserListScreenState extends State<UserListScreen> {
}
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();
print("Fetch Users");
@ -110,7 +113,59 @@ class _UserListScreenState extends State<UserListScreen> {
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)");
final String apiUrlData =
@ -125,28 +180,32 @@ class _UserListScreenState extends State<UserListScreen> {
// Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1")
String newStatus = (currentStatus == "1") ? "0" : "1";
try {
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
}),
);
print("STatus 1 - $newStatus");
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");
}
createUserData(userData, userId, newStatus);
// try {
// 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) {
// 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
@ -207,10 +266,12 @@ class _UserListScreenState extends State<UserListScreen> {
// Print the resolved value
print("CREATELIAS - $users");
context.go("/CreateUserDetails", extra: {
// 'apiCountryData': apiCountryData,
'apiUserData': users,
});
context.go("/CreateUserDetails"
// extra: {
// // 'apiCountryData': apiCountryData,
// 'apiUserData': users,
// }
);
if (!isDesktop) Navigator.pop(context);
},
child: Row(
@ -235,7 +296,60 @@ class _UserListScreenState extends State<UserListScreen> {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} 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) {
return Center(child: Text("No users found"));
}
@ -467,8 +581,8 @@ class _UserListScreenState extends State<UserListScreen> {
)),
DataCell(GestureDetector(
onTap: () {
handleToggleUserStatus(
user['user_id'], user['is_active']);
handleToggleUserStatus(user['user_id'],
user['is_active'], user);
},
child: Text(
user['is_active'] == "1"
@ -519,6 +633,13 @@ class _UserListScreenState extends State<UserListScreen> {
? null
: () {
print("USER: $user");
// final userJson = jsonEncode(
// user); // Convert user map to string
// final encodedUser =
// Uri.encodeComponent(
// userJson);
context.go(
"/CreateUserDetails",
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:shared_preferences/shared_preferences.dart';
class CustomDrawer extends StatefulWidget{
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;
}
class CustomDrawer extends StatefulWidget {
final bool isDesktop;
const CustomDrawer({super.key, required this.isDesktop});
@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(
color: Color(0xFFF3F3FA),
child: Column(
children: [
GestureDetector(
onTap:(){
onTap: () {
print("ONTAP Custom");
print("ONTAP Custom- $userDetails ");
context.go(
"/CreateUserDetails",
extra: {
@ -94,69 +85,71 @@ class _CustomDrawerState extends State<CustomDrawer>{
},
);
},
child :SizedBox(
height: 80,
child: Container(
color: Color(0xFFF3F3FA),
padding: EdgeInsets.all(16),
width: double.infinity,
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
height: 50,
width: 50,
decoration:BoxDecoration(
color: Colors.blueAccent,
shape: BoxShape.circle
) ,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ Text(
userData?["name"]?.isNotEmpty == true
? userData!["name"]![0].toUpperCase()
: "N/A",
style: TextStyle(color: Colors.white, fontSize: 25),
),],),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
userData?["name"] ?? "N/A",
style: TextStyle(color: Colors.black87, fontSize: 11),
),
Text(
userData?["email"] ?? "N/A",
style: TextStyle(color: Colors.black45, fontSize: 10),
),
],)
],)
child: SizedBox(
height: 80,
child: Container(
color: Color(0xFFF3F3FA),
padding: EdgeInsets.all(16),
width: double.infinity,
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.blueAccent, shape: BoxShape.circle),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
userData?["name"]?.isNotEmpty == true
? userData!["name"]![0].toUpperCase()
: "N/A",
style: TextStyle(
color: Colors.white, fontSize: 25),
),
],
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
userData?["name"] ?? "N/A",
style:
TextStyle(color: Colors.black87, fontSize: 11),
),
Text(
userData?["email"] ?? "N/A",
style:
TextStyle(color: Colors.black45, fontSize: 10),
),
],
)
],
)),
),
),
),
_buildDrawerItem(context, Icons.home,'Home', '/home'),
_buildExpandableItem(context,Icons.assessment,'Plans',[
_buildSubDrawerItem(context,'My Travel Request','/listPlan'),
_buildDrawerItem(context, Icons.home, 'Home', '/home'),
_buildExpandableItem(context, Icons.assessment, 'Plans', [
_buildSubDrawerItem(context, 'My Travel Request', '/listPlan'),
// _buildSubDrawerItem(context,'PlanB','/PlanB')
]),
_buildExpandableItem(context,Icons.account_circle_outlined,'User ',[
_buildSubDrawerItem(context,'User List','/listUser'),
_buildExpandableItem(
context, Icons.account_circle_outlined, 'User ', [
_buildSubDrawerItem(context, 'User List', '/listUser'),
// _buildSubDrawerItem(context,'PlanB','/PlanB')
]),
_buildExpandableItem(context,Icons.policy,'Policy ',[
_buildSubDrawerItem(context,'Policy','/Policy'),
_buildExpandableItem(context, Icons.policy, 'Policy ', [
_buildSubDrawerItem(context, 'Policy', '/Policy'),
// _buildSubDrawerItem(context,'PlanB','/PlanB')
]),
_buildDrawerItem(context,Icons.logout,'Logout','/')
],
_buildDrawerItem(context, Icons.logout, 'Logout', '/')
],
),
);
@ -169,17 +162,17 @@ class _CustomDrawerState extends State<CustomDrawer>{
);
} else {
// 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**
Widget _buildDrawerItem(BuildContext context, IconData icon, String title, String route)
{
Widget _buildDrawerItem(
BuildContext context, IconData icon, String title, String route) {
return ListTile(
leading: Icon(icon),
title: Text(title),
leading: Icon(icon),
title: Text(title),
onTap: () async {
if (route == '/') {
// Handle logout separately
@ -189,11 +182,11 @@ class _CustomDrawerState extends State<CustomDrawer>{
} else {
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(
leading: Icon(icon),
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(
title: Text(title),
onTap: (){
onTap: () {
context.go(route);
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/loginPage1.dart';
@ -11,7 +12,6 @@ import 'package:go_router/go_router.dart';
final GoRouter router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => LoginPage(),
@ -35,11 +35,27 @@ final GoRouter router = GoRouter(
GoRoute(
path: '/CreateUserDetails',
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(
path: '/Policy',
builder: (context,state) => Policy(),
path: '/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 '../../config/apiUrl.dart';
class ApiService {
Future<List<dynamic>> fetchCountryList() async {
final String apiUrldata = '$apiUrl/api/getcountryMaster';
final token = await getToken();
@ -29,7 +26,8 @@ class ApiService {
print("Country - $data");
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'];
@ -42,7 +40,8 @@ class ApiService {
}
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();
print("Fetch Users");
@ -68,7 +67,6 @@ class ApiService {
}
}
Future<List> fetchCostCenter() async {
final String apiUrldata = '$apiUrl/api/getCostCenterMaster';
@ -95,27 +93,26 @@ class ApiService {
final data = json.decode(response.body);
print(data);
if (!data.containsKey('data') || data['data'] is!List) {
throw Exception("Invalid response format: 'data' field is missing or not a List");
if (!data.containsKey('data') || data['data'] is! 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(() {
// apiCostData = plansJson; // Store API response in state
// if(apiCostData!.isNotEmpty){
// selectedCostCenterId =apiCostData?.first['department_id'];
// }
// if(apiCostData!.isNotEmpty){
// selectedCostCenterId =apiCostData?.first['department_id'];
// }
// if (apiCostData != null && apiCostData!.isNotEmpty) {
// selectedCostCenterId ??= apiCostData!.first['department_id']?.toString();
// }
// if (apiCostData != null && apiCostData!.isNotEmpty) {
// selectedCostCenterId ??= apiCostData!.first['department_id']?.toString();
// }
// });
print('plansJSON');
return plansJson;
} catch (e) {
throw Exception('Error parsing response: $e');
}
@ -137,20 +134,23 @@ class ApiService {
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',},);
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
try {
final data = json.decode(response.body);
print(data);
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;
} catch (e) {
throw Exception('Error parsing response: $e');
}
@ -158,6 +158,4 @@ class ApiService {
throw Exception('Failed to load plans');
}
}
}

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
Future<String?> getToken() async {
@ -9,3 +11,18 @@ Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
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(
width: widget.width ?? // Use custom width if provided, else default
(widget.isDesktop
? MediaQuery.of(context).size.width * 0.4
? MediaQuery.of(context).size.width * 0.3
: MediaQuery.of(context).size.width * 0.85),
padding: widget.padding,
decoration: BoxDecoration(
@ -42,14 +42,13 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
),
boxShadow: widget.isFocused
? [
BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10,
spreadRadius: 2,
offset: Offset(0, 4),
),
]
BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10,
spreadRadius: 2,
offset: Offset(0, 4),
),
]
: [],
),
child: widget.child,

View File

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

View File

@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -49,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -105,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description: flutter
@ -118,6 +142,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct dev"
description: flutter
@ -145,7 +177,7 @@ packages:
source: hosted
version: "1.3.0"
http_parser:
dependency: transitive
dependency: "direct main"
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
@ -453,6 +485,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
url: "https://pub.dev"
source: hosted
version: "5.12.0"
xdg_directories:
dependency: transitive
description:
@ -462,5 +502,5 @@ packages:
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.7.0-0 <4.0.0"
dart: ">=3.7.0 <4.0.0"
flutter: ">=3.27.0"

View File

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