Create Plan Validations
This commit is contained in:
parent
a82f4a271c
commit
4a1624acde
@ -75,7 +75,10 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Login Successful")),
|
||||
SnackBar(
|
||||
content: Text("Login Successful"),
|
||||
backgroundColor: Colors.green, // Set background to green
|
||||
),
|
||||
);
|
||||
context.go('/home'); // Navigate to home
|
||||
} else {
|
||||
|
||||
@ -56,7 +56,10 @@ class _LoginWidgetState extends State<LoginWidget>{
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Login Successful")),
|
||||
SnackBar(
|
||||
content: Text("Login Successful"),
|
||||
backgroundColor: Colors.green, // Set background to green
|
||||
),
|
||||
);
|
||||
context.go('/home'); // Login Success
|
||||
}
|
||||
|
||||
@ -10,9 +10,11 @@ class AccomodationScreen extends StatefulWidget {
|
||||
final Function(bool) onClose; // Callback function
|
||||
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.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser});
|
||||
|
||||
@override
|
||||
_AccomodationScreenState createState() => _AccomodationScreenState();
|
||||
@ -54,6 +56,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get accomadationData {
|
||||
|
||||
@ -65,6 +68,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
"checkout_date": _checkOutController.text,
|
||||
"checkout_time": _checkOutTimeController.text,
|
||||
"comments": _commentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
@ -74,8 +79,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
data["accomodation_id"] = widget.selectedItem!["accomodation_id"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -95,8 +98,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
_addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
_hotelNameController = initController("hotel_name");
|
||||
_checkInController = initController("checkin_date");
|
||||
@ -105,8 +106,17 @@ 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"));
|
||||
_checkInTimeController.addListener(() => _clearError("checkin_time"));
|
||||
_checkOutController.addListener(() => _clearError("checkout_date"));
|
||||
_checkOutTimeController.addListener(() => _clearError("checkout_time"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_destinationFocusNode.dispose();
|
||||
@ -120,16 +130,46 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
errorMessages.remove(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
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"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save accomadtion $accomadationData");
|
||||
widget.onSaveAccomadation(accomadationData); // Send object to parent
|
||||
print( "Handle Save accomadationData $accomadationData");
|
||||
|
||||
Map<String,dynamic> data = accomadationData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveAccomadation(accomadationData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
// Clear only if this is a new entry
|
||||
// if (widget.selectedItem == null) {
|
||||
// _commentsController.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@ -251,7 +291,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 (isDesktop)
|
||||
Spacer()
|
||||
@ -289,7 +336,15 @@ 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),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -428,7 +483,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 (isDesktop)
|
||||
Spacer()
|
||||
@ -473,7 +535,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 (isDesktop)
|
||||
Spacer()
|
||||
@ -520,7 +589,14 @@ 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 (isDesktop)
|
||||
Spacer()
|
||||
@ -566,6 +642,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["checkout_time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkout_time"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
@ -11,9 +11,10 @@ class BusScreen extends StatefulWidget {
|
||||
final Function(bool) onClose;
|
||||
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.onClose, this.apiData, required this.onSaveBus, required this.selectedItem, required this.loginUser});
|
||||
|
||||
@override
|
||||
_BusScreenState createState() => _BusScreenState();
|
||||
@ -33,6 +34,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
@ -58,6 +60,8 @@ class _BusScreenState extends State<BusScreen> {
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _buscommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
|
||||
@ -129,6 +133,11 @@ class _BusScreenState extends State<BusScreen> {
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
|
||||
_fromController.addListener(() => _clearError("from"));
|
||||
_toController.addListener(() => _clearError("to"));
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@ -145,18 +154,49 @@ class _BusScreenState extends State<BusScreen> {
|
||||
}
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
errorMessages.remove(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["from", "to","date","time"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $busData");
|
||||
widget.onSaveBus(busData); // Send object to parent
|
||||
print( "Handle Save accomadationData $busData");
|
||||
|
||||
Map<String,dynamic> data = busData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveBus(busData);
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -317,10 +357,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
}
|
||||
: null,
|
||||
} : null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
@ -332,86 +369,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _builClassType(bool isDesktop){
|
||||
|
||||
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Class *",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _hotelNameFocusNode, // Assign the correct focus node
|
||||
// controller: _hotelNameController,
|
||||
value: selectedPurpose,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
}
|
||||
: null,
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
// child: TextField(
|
||||
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
|
||||
// controller: _hotelNameController,
|
||||
// style: TextStyle(fontSize: 12),
|
||||
// decoration: InputDecoration(
|
||||
// labelText: "Select Class",
|
||||
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
|
||||
@ -494,6 +451,13 @@ class _BusScreenState extends State<BusScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["from"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -533,6 +497,13 @@ class _BusScreenState extends State<BusScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["to"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -580,6 +551,13 @@ class _BusScreenState extends State<BusScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -626,6 +604,13 @@ class _BusScreenState extends State<BusScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@ -7,17 +7,20 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class FlightScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final String? loginUser;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String,dynamic>) onSaveFlight;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
|
||||
|
||||
FlightScreen({required this.formData, required this.updateFormData, required this.apiData,
|
||||
required this.onClose});
|
||||
FlightScreen({ required this.apiData,required this.loginUser,
|
||||
required this.onClose, required this.onSaveFlight,required this.selectedItem});
|
||||
|
||||
@override
|
||||
_FlightScreenState createState() => _FlightScreenState();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +30,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
String? selectedTripType;
|
||||
Map<int, String?> selectedClasses = {}; // Store class selection for each trip
|
||||
String? selectedvisa_available;
|
||||
int multiTripRowCount = 1;
|
||||
|
||||
@ -37,33 +41,33 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
Map<String, bool> focusStates = {};
|
||||
Map<String, TextEditingController> textControllers = {};
|
||||
|
||||
Map<String, String> fieldErrors = {}; // Holds error messages
|
||||
|
||||
List<List<Widget>> rowBuilders = []; // Holds row widgets
|
||||
List<TextEditingController> controllers = []; // Dynamic controllers
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
late TextEditingController _fromController = TextEditingController();
|
||||
late TextEditingController _toController = TextEditingController();
|
||||
late TextEditingController _dateController = TextEditingController();
|
||||
late TextEditingController _timeController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
|
||||
bool _tripTypeFocused = false;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// _initializeRows();
|
||||
// List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||
// selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||
|
||||
|
||||
// Get trip type from widget.selectedItem
|
||||
selectedTripType = widget.selectedItem?["trip_type"] as String?;
|
||||
|
||||
// If null, set it to the first available value from purposeList
|
||||
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||
selectedTripType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||
|
||||
if (selectedTripType == null && purposeList.isNotEmpty) {
|
||||
selectedTripType = purposeList.first['dropdown_value'] as String?;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_initializeFields();
|
||||
getRowCount();
|
||||
@ -73,28 +77,6 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
print("Text Controllers Keys: ${textControllers.keys.toList()}");
|
||||
|
||||
|
||||
|
||||
// Initialize focus nodes and states dynamically
|
||||
// for (var field in dataHeader) {
|
||||
// textControllers["${field}1Controller"] =
|
||||
// TextEditingController(text: widget.formData[field] ?? "");
|
||||
// focusNodes["${field}1FocusNode"] = FocusNode();
|
||||
// focusStates["${field}1Focused"] = false;
|
||||
//
|
||||
// }
|
||||
|
||||
// for (var key in dataHeader) {
|
||||
// String controllerKey = "${key}1Controller";
|
||||
// textControllers[controllerKey] = TextEditingController(text: widget.formData[key] ?? "");
|
||||
//
|
||||
// textControllers[controllerKey]?.addListener(() {
|
||||
// print("$controllerKey updated: ${textControllers[controllerKey]?.text}");
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Add focus listeners
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
@ -103,37 +85,23 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
handleUpdateField();
|
||||
|
||||
// _addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
|
||||
// _addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
// _addFocusListener(_fromFocusNode, (focus) => _fromFocus = focus);
|
||||
// _addFocusListener(_toFocusNode, (focus) => _toFocus = focus);
|
||||
// _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
|
||||
// _addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
|
||||
// _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
// _commentsFocusNode.addListener(() {
|
||||
// setState(() {
|
||||
// _commentsFocus = _commentsFocusNode.hasFocus;
|
||||
// });
|
||||
// });
|
||||
|
||||
List<TextEditingController> controllers = [
|
||||
_tripTypeController, _hotelNameController, _fromController, _toController, _dateController,
|
||||
_timeController, _commentsController
|
||||
];
|
||||
|
||||
List<String> keys = [
|
||||
"_tripType", "_hotelName", "_from", "_Check_In_Time", "_Check_Out",
|
||||
"_Check_Out_Time", "_comments"
|
||||
];
|
||||
|
||||
for (int i = 0; i < controllers.length; i++) {
|
||||
controllers[i].addListener(() {
|
||||
widget.updateFormData("Flight", keys[i], controllers[i].text);
|
||||
});
|
||||
int rowCount = 1; // Default row count for One-way
|
||||
if (selectedTripType == "Roundtrip") {
|
||||
rowCount = 2; // Fixed for Roundtrip
|
||||
} else if (selectedTripType == "Multitrip") {
|
||||
rowCount = multiTripRowCount; // Dynamic row count for Multitrip
|
||||
}
|
||||
|
||||
// Loop through each row and add listeners to clear errors
|
||||
for (int i = 1; i <= rowCount; i++) {
|
||||
textControllers["_from${i}Controller"]?.addListener(() => _clearError("from_place_$i"));
|
||||
textControllers["_to${i}Controller"]?.addListener(() => _clearError("to_place_$i"));
|
||||
textControllers["_date${i}Controller"]?.addListener(() => _clearError("date_$i"));
|
||||
textControllers["_time${i}Controller"]?.addListener(() => _clearError("time_$i"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -180,7 +148,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
for (var field in dataHeader) {
|
||||
for (int i = 1; i <= rowCount; i++) {
|
||||
textControllers["${field}${i}Controller"] =
|
||||
TextEditingController(text: widget.formData[field]?.toString() ?? "");
|
||||
TextEditingController();
|
||||
|
||||
focusNodes["${field}${i}FocusNode"] = FocusNode();
|
||||
focusStates["${field}${i}Focused"] = false;
|
||||
@ -201,32 +169,6 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// void _initializeFields() {
|
||||
// textControllers.clear();
|
||||
// focusNodes.clear();
|
||||
// focusStates.clear();
|
||||
//
|
||||
// int rowCount = 1; // Default for Oneway
|
||||
// if (selectedTripType == "RoundTrip") {
|
||||
// rowCount = 2;
|
||||
// } else if (selectedTripType == "Multitrip") {
|
||||
// rowCount = multiTripRowCount;
|
||||
// }
|
||||
//
|
||||
// for (var field in dataHeader) {
|
||||
// for (int i = 1; i <= rowCount; i++) {
|
||||
// textControllers["${field}${i}Controller"] = TextEditingController(text: widget.formData[field] ?? "");
|
||||
// focusNodes["${field}${i}FocusNode"] = FocusNode();
|
||||
// focusStates["${field}${i}Focused"] = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// setState(() {}); // Ensure UI updates
|
||||
// }
|
||||
|
||||
// Call this function when adding more rows for Multitrip
|
||||
void addMultiTripRow() {
|
||||
setState(() {
|
||||
multiTripRowCount++; // Increment row count
|
||||
@ -242,6 +184,17 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
errorMessages.remove(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// _tripTypeFocusNode.dispose();
|
||||
@ -261,6 +214,195 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
|
||||
|
||||
Map<String, dynamic> get flightsData{
|
||||
|
||||
List<Map<String, dynamic>> trips = [];
|
||||
|
||||
int rowCount = 1; // Default for One-way
|
||||
|
||||
if (selectedTripType == "Roundtrip") {
|
||||
rowCount = 2; // Fixed count for Roundtrip
|
||||
} else if (selectedTripType == "Multitrip") {
|
||||
rowCount = multiTripRowCount; // Use dynamic count for Multitrip
|
||||
}
|
||||
|
||||
|
||||
for (int i = 1; i <= rowCount; i++) {
|
||||
trips.add({
|
||||
"class": selectedClasses[i] ,
|
||||
"from_place": textControllers["_from${i}Controller"]?.text ?? "",
|
||||
"to_place": textControllers["_to${i}Controller"]?.text ?? "",
|
||||
"date": textControllers["_date${i}Controller"]?.text ?? "",
|
||||
"time": textControllers["_time${i}Controller"]?.text ?? "",
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Map<String,dynamic> data ={
|
||||
"trip_type": selectedTripType,
|
||||
"comments": textControllers["_comments1Controller"]?.text ?? "",
|
||||
"visa_available": selectedvisa_available,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
"trips": trips,
|
||||
};
|
||||
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["flight_id"] != null && widget.selectedItem?["flight_id"] != 0) {
|
||||
data["flight_id"] = widget.selectedItem!["flight_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
void handleUpdateField() {
|
||||
if (widget.selectedItem != null) {
|
||||
|
||||
textControllers["_comments1Controller"] = initController("comments");
|
||||
// selectedTripType = widget.selectedItem!["trip_type"] as String?;
|
||||
// selectedvisa_available = widget.selectedItem!["visa_available"].toString();
|
||||
|
||||
if ( widget.selectedItem!["trip_type"] != null) {
|
||||
selectedTripType = widget.selectedItem!["trip_type"].toString();
|
||||
}
|
||||
|
||||
if ( widget.selectedItem!["visa_available"] != null) {
|
||||
selectedvisa_available = widget.selectedItem!["visa_available"].toString();
|
||||
}
|
||||
|
||||
// Extract trips from selectedItem
|
||||
List<dynamic> selectedTrips = widget.selectedItem!["trips"] ?? [];
|
||||
|
||||
// Ensure the selectedClasses map and textControllers are cleared before populating
|
||||
// selectedClasses.clear();
|
||||
// textControllers.clear();
|
||||
|
||||
// Set row count dynamically for Multitrip
|
||||
if (selectedTripType == "Multitrip") {
|
||||
multiTripRowCount = selectedTrips.length;
|
||||
}
|
||||
|
||||
// Loop through selected trips and populate text controllers
|
||||
for (int i = 0; i < selectedTrips.length; i++) {
|
||||
var trip = selectedTrips[i] as Map<String, dynamic>;
|
||||
int index = i + 1; // Use 1-based indexing to match the form
|
||||
|
||||
selectedClasses[index] = trip["class"].toString();
|
||||
textControllers["_from${index}Controller"] = TextEditingController(text: trip["from_place"]);
|
||||
textControllers["_to${index}Controller"] = TextEditingController(text: trip["to_place"]);
|
||||
textControllers["_date${index}Controller"] = TextEditingController(text: trip["date"]);
|
||||
textControllers["_time${index}Controller"] = TextEditingController(text: trip["time"]);
|
||||
}
|
||||
|
||||
print("Selected ITEM - ${widget.selectedItem}");
|
||||
print("Total Trips Loaded: ${selectedTrips.length}");
|
||||
print("Controllers Set: ${textControllers.keys}");
|
||||
}
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Controllers Key-Value Pairs:");
|
||||
|
||||
if (!validateFields()) {
|
||||
print("Validation failed. Please fill all required fields.");
|
||||
return;
|
||||
}
|
||||
|
||||
widget.onSaveFlight(flightsData); // Send object to parent
|
||||
widget.onClose(false);
|
||||
|
||||
print("FlightData - $flightsData");
|
||||
}
|
||||
|
||||
bool validateFields() {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
int rowCount = 1; // Default row count for One-way
|
||||
if (selectedTripType == "Roundtrip") {
|
||||
rowCount = 2; // Fixed for Roundtrip
|
||||
} else if (selectedTripType == "Multitrip") {
|
||||
rowCount = multiTripRowCount; // Dynamic row count for Multitrip
|
||||
}
|
||||
|
||||
// Loop through each trip row and validate required fields
|
||||
for (int i = 1; i <= rowCount; i++) {
|
||||
if (textControllers["_from${i}Controller"]?.text.trim().isEmpty ?? true) {
|
||||
errorMessages["from_place_$i"] = "Required";
|
||||
}
|
||||
if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) {
|
||||
errorMessages["to_place_$i"] = "Required";
|
||||
}
|
||||
if (textControllers["_date${i}Controller"]?.text.trim().isEmpty ?? true) {
|
||||
errorMessages["date_$i"] = "Required";
|
||||
}
|
||||
if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) {
|
||||
errorMessages["time_$i"] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {}); // Update UI to show error messages
|
||||
|
||||
return errorMessages.isEmpty; // Returns true if all required fields are filled
|
||||
}
|
||||
|
||||
void removeTrip(int index) {
|
||||
if (selectedTripType == "Multitrip" && multiTripRowCount > 1) {
|
||||
print("Delete Index - $index");
|
||||
|
||||
setState(() {
|
||||
multiTripRowCount--; // Reduce trip count
|
||||
|
||||
// Remove corresponding text controllers
|
||||
textControllers.remove("_from${index}Controller");
|
||||
textControllers.remove("_to${index}Controller");
|
||||
textControllers.remove("_date${index}Controller");
|
||||
textControllers.remove("_time${index}Controller");
|
||||
|
||||
|
||||
|
||||
// Step 2: Shift remaining textControllers keys
|
||||
Map<String, TextEditingController> updatedTextControllers = {};
|
||||
int newIndex = 1;
|
||||
for (int i = 1; i <= multiTripRowCount + 1; i++) {
|
||||
if (i == index) continue; // Skip the deleted one
|
||||
updatedTextControllers["_from${newIndex}Controller"] = textControllers["_from${i}Controller"]!;
|
||||
updatedTextControllers["_to${newIndex}Controller"] = textControllers["_to${i}Controller"]!;
|
||||
updatedTextControllers["_date${newIndex}Controller"] = textControllers["_date${i}Controller"]!;
|
||||
updatedTextControllers["_time${newIndex}Controller"] = textControllers["_time${i}Controller"]!;
|
||||
newIndex++;
|
||||
}
|
||||
textControllers = updatedTextControllers;
|
||||
|
||||
|
||||
// Shift the selectedClasses map BEFORE removing the index
|
||||
Map<int, String?> updatedClasses = {};
|
||||
newIndex = 1;
|
||||
|
||||
for (int i = 1; i <= selectedClasses.length; i++) {
|
||||
if (i == index) continue; // Skip the one being deleted
|
||||
updatedClasses[newIndex] = selectedClasses[i];
|
||||
newIndex++;
|
||||
}
|
||||
|
||||
selectedClasses = updatedClasses; // Update the map
|
||||
print("FlightData - $flightsData");
|
||||
print("Updated Trips: $multiTripRowCount");
|
||||
print("Updated Classes: $selectedClasses");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -345,7 +487,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
|
||||
// Iterate over rowBuilders based on selectedTripType
|
||||
if (selectedTripType == "Oneway")
|
||||
if (selectedTripType == "Oneway")
|
||||
...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
||||
|
||||
|
||||
@ -400,7 +542,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
// Create TextEditingController if it doesn't exist
|
||||
if (!textControllers.containsKey(keyController)) {
|
||||
textControllers[keyController] = TextEditingController(
|
||||
text: widget.formData[field]?.toString() ?? "",
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -523,11 +665,12 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
if (selectedTripType != "Multitrip") {
|
||||
multiTripRowCount = 1;
|
||||
}
|
||||
errorMessages.clear();
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> $selectedTripType");
|
||||
_initializeFields();
|
||||
|
||||
widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
|
||||
// _initializeRows();
|
||||
}
|
||||
: null,
|
||||
@ -556,7 +699,17 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(onPressed: (){},
|
||||
SizedBox(
|
||||
|
||||
width: isDesktop? 1000 : 80, // Ensure full width
|
||||
child: Divider(
|
||||
|
||||
thickness: 1, // Make it more visible
|
||||
),
|
||||
),
|
||||
IconButton(onPressed: (){
|
||||
removeTrip(index);
|
||||
},
|
||||
icon: Icon(Icons.delete),color: Colors.red,iconSize: 20,)
|
||||
|
||||
|
||||
@ -569,7 +722,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -583,7 +736,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedClasses[index] ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
return [
|
||||
@ -593,12 +746,15 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
if (selectedTripType == "Multitrip" && index >= 2)
|
||||
if (selectedTripType == "Multitrip" )
|
||||
isDesktop?
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [..._buildDelete(isDesktop, index)])
|
||||
: Row(mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [..._buildDelete(isDesktop, index)]),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.89,
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [..._buildDelete(isDesktop, index)]),
|
||||
)
|
||||
: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [..._buildDelete(isDesktop, index)]),
|
||||
|
||||
SizedBox(height: 20),
|
||||
|
||||
@ -622,7 +778,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
focusNode: focusNodes["_class${index}FocusNode"],
|
||||
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
// controller: _hotelNameController,
|
||||
value: selectedPurpose,
|
||||
value: selectedClasses[index] ,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
@ -632,10 +788,10 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
selectedClasses[index] = newValue;
|
||||
});
|
||||
|
||||
print(selectedPurpose);
|
||||
print( selectedClasses[index] );
|
||||
|
||||
}
|
||||
: null,
|
||||
@ -739,6 +895,18 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
if (errorMessages["from_place_$index"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -778,6 +946,13 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["to_place_$index"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -826,6 +1001,18 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
if (errorMessages["date_$index"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -873,6 +1060,14 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time_$index"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
@ -930,11 +1125,11 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = visa_available
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
selectedvisa_available = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
@ -985,7 +1180,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> $selectedvisa_available");
|
||||
|
||||
widget.updateFormData("Flight", "visa_available", newValue ?? "");
|
||||
|
||||
// _initializeRows();
|
||||
}
|
||||
: null,
|
||||
@ -1008,7 +1203,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog or screen
|
||||
widget.onClose(false);// Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -1027,7 +1222,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -19,11 +20,13 @@ class ForexScreen extends StatefulWidget {
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final List<dynamic>? apiCountryData;
|
||||
final Function(Map<String, dynamic>)onSaveForex;
|
||||
final String? loginUser;
|
||||
|
||||
|
||||
|
||||
ForexScreen({
|
||||
required this.onClose, this.apiData, required this.selectedItem, required this.apiCountryData,
|
||||
required this.onSaveForex });
|
||||
required this.onSaveForex, required this.loginUser });
|
||||
|
||||
@override
|
||||
_ForexScreenState createState() => _ForexScreenState();
|
||||
@ -35,6 +38,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
Map<String, String?> selectedValues = {};
|
||||
bool isChecked = false; // State variable for checkbox
|
||||
|
||||
int fifteenPercent = 0;
|
||||
int remainingAmount = 0;
|
||||
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
@ -56,18 +61,20 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
return date; // Return as is if parsing fails
|
||||
}
|
||||
}
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
String? selectedCountry;
|
||||
String? selectedCurrency;
|
||||
String? selectedDuration;
|
||||
String? selectedPerdiemAmount;
|
||||
String? CalculatedOtherExpenses;
|
||||
String? selectedQuotedAmount;
|
||||
|
||||
|
||||
Map<String, dynamic> get forexData {
|
||||
Map<String, dynamic> data ={
|
||||
"start_date": _formatDate(textControllers["_forexStartDate"]?.text),
|
||||
"end_date": _formatDate(textControllers["_forexEndDate"]?.text),
|
||||
"start_date": textControllers["_forexStartDate"]?.text,
|
||||
"end_date": textControllers["_forexEndDate"]?.text,
|
||||
"country_code": selectedCountry,
|
||||
"duration": selectedDuration,
|
||||
"currency": selectedCurrency,
|
||||
@ -81,8 +88,18 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
"deposit_on_cash": textControllers["_cash"]?.text,
|
||||
"delivery_location": textControllers["_deliveryLocation"]?.text,
|
||||
"comments": textControllers["_comments"]?.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?["forex_id"] != null && widget.selectedItem?["forex_id"] != 0) {
|
||||
data["forex_id"] = widget.selectedItem!["forex_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -138,7 +155,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
selectedCurrency = responseData["currency"] ?? selectedCurrency;
|
||||
selectedPerdiemAmount = responseData["perdiem_amount"]?.toString() ?? "";
|
||||
selectedDuration = responseData["duration"]?.toString() ?? "";
|
||||
selectedQuotedAmount = responseData["perdiem_amount"]?.toString() ?? "";
|
||||
});
|
||||
_onFieldChangedForOthers();
|
||||
_divideQuotedAmount();
|
||||
} else {
|
||||
print("Warning: Response does not contain expected fields.");
|
||||
}
|
||||
@ -152,15 +172,63 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidForexData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["start_date", "end_date", "country_code", "deposit_on_card", "deposit_on_cash", "card_number"];
|
||||
|
||||
// If have_card is "1", then delivery_location is required
|
||||
bool isCardChecked = data["have_card"] == "1";
|
||||
if (isCardChecked) {
|
||||
requiredFields.add("delivery_location");
|
||||
}
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "This field is required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save forexData $forexData");
|
||||
widget.onSaveForex(forexData); // Send object to parent
|
||||
|
||||
|
||||
Map<String, dynamic> data = forexData;
|
||||
|
||||
|
||||
if (!isValidForexData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveForex(forexData); // Send object to parent
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
|
||||
}
|
||||
|
||||
|
||||
DateTime? _parseDate(String date) {
|
||||
try {
|
||||
return DateFormat("yyyy-MM-dd").parse(date); // Change format if needed
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -195,6 +263,40 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
textControllers["_forexStartDate"]?.addListener(_onFieldChanged);
|
||||
textControllers["_forexEndDate"]?.addListener(_onFieldChanged);
|
||||
|
||||
handleUpdatedField();
|
||||
}
|
||||
|
||||
void handleUpdatedField(){
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null) {
|
||||
print("UPDATAED SELECTION");
|
||||
|
||||
textControllers["_forexStartDate"] = initController("start_date");
|
||||
textControllers["_forexEndDate"] = initController("end_date");
|
||||
textControllers["_transport"] = initController("transport");
|
||||
textControllers["_accomodation"] = initController("accommodation");
|
||||
textControllers["_telephone"] = initController("telephone");
|
||||
textControllers["_cardNumber"] = initController("card_number");
|
||||
textControllers["_card"] = initController("deposit_on_card");
|
||||
textControllers["_cash"]= initController("deposit_on_cash");
|
||||
textControllers["_deliveryLocation"]= initController("delivery_location");
|
||||
textControllers["_comments"]= initController("comments");
|
||||
|
||||
|
||||
// Set dropdown values
|
||||
selectedCountry = widget.selectedItem!["country_code"] as String?;
|
||||
selectedCurrency = widget.selectedItem!["currency"] as String?;
|
||||
selectedDuration = widget.selectedItem!["duration"] as String?;
|
||||
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
|
||||
isChecked = widget.selectedItem!["have_card"] == "1"; // Convert string to bool
|
||||
_onFieldChangedForOthers();
|
||||
setState(() {}); // Update the UI
|
||||
|
||||
// // Calculate other expenses (if applicable)
|
||||
// CalculatedOtherExpenses = calculateOtherExpenses();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
@ -239,9 +341,77 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
double transport = double.tryParse(textControllers["_transport"]?.text ?? "0") ?? 0;
|
||||
double accommodation = double.tryParse(textControllers["_accomodation"]?.text ?? "0") ?? 0;
|
||||
double telephone = double.tryParse(textControllers["_telephone"]?.text ?? "0") ?? 0;
|
||||
double calclateVal = (transport + accommodation + telephone) ;
|
||||
|
||||
CalculatedOtherExpenses = (transport + accommodation + telephone).toStringAsFixed(2);
|
||||
CalculatedOtherExpenses = (calclateVal).toStringAsFixed(2);
|
||||
|
||||
// Convert selectedPerdiemAmount to double before performing the addition
|
||||
double perdiemAmount = double.tryParse(selectedPerdiemAmount ?? "0") ?? 0;
|
||||
print("calclateVal - $calclateVal");
|
||||
|
||||
selectedQuotedAmount = ((perdiemAmount + calclateVal).toString() ?? 0) as String?;
|
||||
});
|
||||
_divideQuotedAmount();
|
||||
errorMessages.clear();
|
||||
}
|
||||
|
||||
void _divideQuotedAmount(){
|
||||
|
||||
int? quotedAmount = int.tryParse(selectedQuotedAmount!);
|
||||
print("DIVIDREFD - $selectedQuotedAmount -$quotedAmount");
|
||||
if (quotedAmount != null) {
|
||||
fifteenPercent = (quotedAmount * 15) ~/ 100; // Calculate 15% (integer division)
|
||||
remainingAmount = quotedAmount - fifteenPercent; // Subtract from total
|
||||
|
||||
textControllers["_cash"]?.text = fifteenPercent.toString();
|
||||
textControllers["_card"]?.text = remainingAmount.toString();
|
||||
print("15% Amount: $fifteenPercent");
|
||||
print("Remaining Amount: $remainingAmount");
|
||||
} else {
|
||||
print("Invalid number format in selectedQuotedAmount");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _validateCardAmount(String value) {
|
||||
|
||||
print("_validateCardAmount - $value - $remainingAmount");
|
||||
|
||||
|
||||
int? enteredAmount = int.tryParse(value) ;
|
||||
int? cashAmount = int.tryParse(textControllers["_cash"]!.text ?? "0");
|
||||
int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0");
|
||||
int? calculateAmnt = cashAmount! + enteredAmount!;
|
||||
|
||||
print("CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount");
|
||||
|
||||
|
||||
|
||||
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
||||
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
||||
} else {
|
||||
errorMessages["deposit_on_card"] = ""; // Clear error if valid
|
||||
}
|
||||
|
||||
// Refresh UI if using StatefulWidget
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
||||
void _validateCashAmount(String value) {
|
||||
|
||||
print("_validateCashAmount - $value - $fifteenPercent");
|
||||
|
||||
int? enteredAmount = int.tryParse(value);
|
||||
|
||||
if (enteredAmount == null || enteredAmount > fifteenPercent) {
|
||||
errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
|
||||
} else {
|
||||
errorMessages["deposit_on_cash"] = ""; // Clear error if valid
|
||||
}
|
||||
|
||||
// Refresh UI if using StatefulWidget
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
||||
@ -259,6 +429,24 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
}
|
||||
|
||||
|
||||
void _validateDates() {
|
||||
|
||||
print("VALiDATING DATES");
|
||||
|
||||
DateTime? startDate = _parseDate(textControllers["_forexStartDate"]?.text ?? "");
|
||||
DateTime? endDate = _parseDate(textControllers["_forexEndDate"]?.text ?? "");
|
||||
|
||||
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
|
||||
setState(() {
|
||||
errorMessages["end_date"] = "End date cannot be earlier than start date";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessages.remove("end_date");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -465,19 +653,40 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
// onTap: () async{
|
||||
// _selectCheckOutDate(context);
|
||||
//
|
||||
// },
|
||||
onTap: () async {
|
||||
await _selectCheckOutDate(context);
|
||||
|
||||
if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
|
||||
DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text);
|
||||
DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text);
|
||||
|
||||
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
|
||||
setState(() {
|
||||
errorMessages["end_date"] = "End date cannot be earlier than start date";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessages.remove("end_date");
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_forexStartDate"],
|
||||
controller: textControllers["_forexStartDate"],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
decoration: InputDecoration(
|
||||
labelText: "Select Date",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
labelStyle: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: Icon(Icons.calendar_today,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: const Icon(Icons.calendar_today,
|
||||
size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
@ -486,6 +695,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["start_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Select Start Date",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -514,7 +730,26 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectForexEndDate(context),
|
||||
// onTap: () => _selectForexEndDate(context),
|
||||
onTap: () async {
|
||||
await _selectForexEndDate(context);
|
||||
|
||||
if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
|
||||
DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text);
|
||||
DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text);
|
||||
|
||||
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
|
||||
setState(() {
|
||||
errorMessages["end_date"] = "End date cannot be earlier than start date";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessages.remove("end_date");
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_forexEndDate"],
|
||||
@ -535,6 +770,14 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["end_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["end_date"]!,
|
||||
// "Select End Date",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -596,7 +839,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Select Country",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -612,9 +861,9 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
return [
|
||||
|
||||
|
||||
if (isDesktop) Spacer() else SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
// if (isDesktop) Spacer() else SizedBox(
|
||||
// height: 8,
|
||||
// ),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -653,9 +902,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)SizedBox(width: 8,) else SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
// if (isDesktop)SizedBox(width: 8,) else SizedBox(
|
||||
// height: 8,
|
||||
// ),
|
||||
if (isDesktop)Spacer() else SizedBox(height: 8,),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -689,7 +939,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
|
||||
// if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,),
|
||||
if (isDesktop)Spacer() else SizedBox(height: 8,),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -730,6 +981,39 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
if (isDesktop)Spacer() else SizedBox(height: 8,),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Quoted Amount",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: focusStates["_perdiemAmount"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
color:Colors.transparent,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
// "amo",
|
||||
selectedQuotedAmount ?? "0",
|
||||
// selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount",
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// if (isDesktop)Spacer() else SizedBox(height: 8,),
|
||||
];
|
||||
}
|
||||
|
||||
@ -759,6 +1043,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
focusNode: focusNodes["_transport"],
|
||||
controller: textControllers["_transport"],
|
||||
onChanged: (value) => _onFieldChangedForOthers(),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
|
||||
],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Transport",
|
||||
@ -799,6 +1087,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
focusNode: focusNodes["_accomodation"],
|
||||
controller: textControllers["_accomodation"],
|
||||
onChanged: (value) => _onFieldChangedForOthers(),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
|
||||
],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Accomodation",
|
||||
@ -838,6 +1130,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
focusNode: focusNodes["_telephone"],
|
||||
controller: textControllers["_telephone"],
|
||||
onChanged: (value) => _onFieldChangedForOthers(),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
|
||||
],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Telephone",
|
||||
@ -944,7 +1240,12 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
child: TextField(
|
||||
focusNode: focusNodes["_cash"],
|
||||
controller: textControllers["_cash"],
|
||||
|
||||
style: const TextStyle(fontSize: 12),
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
_validateCashAmount(value); // Call validation when text changes
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Cash",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
@ -956,6 +1257,14 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["deposit_on_cash"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
// "Required",
|
||||
errorMessages["deposit_on_cash"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -988,6 +1297,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
focusNode: focusNodes["_card"],
|
||||
controller: textControllers["_card"],
|
||||
style: const TextStyle(fontSize: 12),
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
_validateCardAmount(value); // Call validation when text changes
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Card",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
@ -998,6 +1311,16 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["deposit_on_card"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["deposit_on_card"] !,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
@ -1082,6 +1405,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["card_number"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -1123,6 +1453,13 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["delivery_location"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
)
|
||||
:SizedBox.shrink()
|
||||
@ -1201,7 +1538,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog or screen
|
||||
widget.onClose(false); // Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
|
||||
@ -11,10 +11,12 @@ class InsuranceScreen extends StatefulWidget {
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveInsurance;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
|
||||
InsuranceScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveInsurance, required this.selectedItem});
|
||||
required this.onClose, required this.apiData, required this.onSaveInsurance,
|
||||
required this.selectedItem,required this.loginUser});
|
||||
|
||||
@override
|
||||
_InsuranceScreenState createState() => _InsuranceScreenState();
|
||||
@ -45,6 +47,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
String? selectedTripType;
|
||||
String? selectedInsuranceType;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get InsuranceData{
|
||||
Map<String, dynamic> data = {
|
||||
|
||||
@ -52,6 +56,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
"start_date": _startdateController.text,
|
||||
"end_date": _endDateController.text,
|
||||
"comments": _insuranceCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
@ -103,17 +109,53 @@ 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"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save InsuranceData $InsuranceData");
|
||||
widget.onSaveInsurance(InsuranceData); // Send object to parent
|
||||
|
||||
Map<String,dynamic> data = InsuranceData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveInsurance(InsuranceData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
// // Clear only if this is a new entry
|
||||
// if (widget.selectedItem == null) {
|
||||
// _commentsController.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
DateTime? _parseDate(String date) {
|
||||
try {
|
||||
return DateFormat("yyyy-MM-dd").parse(date); // Change format if needed
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
@ -284,6 +326,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedInsuranceType = newValue;
|
||||
if (selectedInsuranceType!.isNotEmpty) {
|
||||
errorMessages.remove("type_of_insurance");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
print(selectedInsuranceType);
|
||||
@ -375,7 +421,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
onTap: () async{
|
||||
await _selectCheckOutDate(context);
|
||||
if(_startdateController.text.isNotEmpty){
|
||||
setState(() {
|
||||
errorMessages.remove("start_date"); // Removes the key completely
|
||||
});
|
||||
}
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -396,7 +449,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
|
||||
if (errorMessages["start_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -423,7 +485,25 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectEndCheckOutDate(context),
|
||||
onTap: () async {
|
||||
await _selectEndCheckOutDate(context);
|
||||
|
||||
if (_endDateController.text.isNotEmpty) {
|
||||
DateTime? startDate = _parseDate(_startdateController.text);
|
||||
DateTime? endDate = _parseDate(_endDateController.text);
|
||||
|
||||
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
|
||||
setState(() {
|
||||
errorMessages["end_date"] = "End date cannot be earlier than start date";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessages.remove("end_date");
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -444,6 +524,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["end_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
// "Required",
|
||||
errorMessages["end_date"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
|
||||
@ -12,10 +12,11 @@ class MiscellaneousScreen extends StatefulWidget {
|
||||
final Function(Map<String, dynamic>) onSaveMiscellaneous;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final int? selectedIndex;
|
||||
final String? loginUser;
|
||||
|
||||
MiscellaneousScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveMiscellaneous,
|
||||
this.selectedItem, this.selectedIndex,});
|
||||
this.selectedItem, this.selectedIndex,required this.loginUser});
|
||||
|
||||
@override
|
||||
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
|
||||
@ -38,19 +39,23 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
|
||||
String? selectedSpecialType;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get miscellaneousData {
|
||||
Map<String, dynamic> data = {
|
||||
"special_request": selectedSpecialType,
|
||||
"comments": _commentsController.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?["miscellaneous_id"] != null && widget.selectedItem?["miscellaneous_id"] != 0) {
|
||||
data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"];
|
||||
}
|
||||
@ -81,8 +86,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
|
||||
@ -103,15 +106,46 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
errorMessages[field] = "This field is required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $miscellaneousData");
|
||||
widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent
|
||||
|
||||
Map<String,dynamic> data = miscellaneousData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
// Clear only if this is a new entry
|
||||
if (widget.selectedItem == null) {
|
||||
_commentsController.clear();
|
||||
}
|
||||
// if (widget.selectedItem == null) {
|
||||
// _commentsController.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@ -280,6 +314,13 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["special_request"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Select Type",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -319,6 +360,13 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["comments"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
)
|
||||
];
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
@ -10,9 +11,11 @@ class TaxiScreen extends StatefulWidget {
|
||||
final Function(bool) onClose;
|
||||
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.onClose, this.apiData, required this.onSavetaxi,
|
||||
required this.selectedItem,required this.loginUser});
|
||||
|
||||
@override
|
||||
_TaxiScreenState createState() => _TaxiScreenState();
|
||||
@ -22,6 +25,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
final FocusNode _destinationFocusNode = FocusNode();
|
||||
final FocusNode _locationFocusNode = FocusNode();
|
||||
@ -64,6 +68,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
"no_of_passengers": _numPassengerController.text,
|
||||
"car_type": selectedCarType,
|
||||
"comments": _taxiCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
// "updated_on": ,
|
||||
// "updated_by": ,
|
||||
|
||||
@ -117,6 +123,12 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
selectedCarType = widget.selectedItem!["car_type"].toString();
|
||||
}
|
||||
|
||||
_destinationController.addListener(() => _clearError("destination_city"));
|
||||
_locationController.addListener(() => _clearError("location_of_pickup"));
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
_numPassengerController.addListener(() => _clearError("no_of_passengers"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -143,18 +155,58 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
errorMessages.remove(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save taxiData $taxiData");
|
||||
widget.onSavetaxi(taxiData); // Send object to parent
|
||||
|
||||
Map<String,dynamic> data = taxiData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSavetaxi(taxiData);
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -306,6 +358,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
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
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Number of Passenger",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
@ -317,6 +373,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["no_of_passengers"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -519,7 +583,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -558,6 +629,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 (isDesktop)
|
||||
@ -605,7 +683,14 @@ 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 (isDesktop)
|
||||
Spacer()
|
||||
@ -651,7 +736,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -11,9 +11,10 @@ class TrainScreen extends StatefulWidget {
|
||||
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.onClose, this.apiData, required this.onSavetrain, required this.selectedItem, required this.loginUser});
|
||||
|
||||
@override
|
||||
_TrainScreenState createState() => _TrainScreenState();
|
||||
@ -51,18 +52,20 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
String? selectedClass;
|
||||
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String , dynamic> get trainData {
|
||||
Map<String, dynamic> data ={
|
||||
|
||||
"train_no": _trainNoController.text,
|
||||
"class": selectedClass,
|
||||
"from": _fromController.text,
|
||||
"to": _toController.text,
|
||||
"from_station": _fromController.text,
|
||||
"to_station": _toController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _trainCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
@ -124,8 +127,8 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
_trainCommentsController = initController("comments");
|
||||
_trainNoController = initController("train_no");
|
||||
_fromController = initController("from");
|
||||
_toController = initController("to");
|
||||
_fromController = initController("from_station");
|
||||
_toController = initController("to_station");
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
|
||||
@ -133,6 +136,13 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
if (widget.selectedItem != null && widget.selectedItem!["class"] != null) {
|
||||
selectedClass = widget.selectedItem!["class"].toString();
|
||||
}
|
||||
|
||||
_trainNoController.addListener(() => _clearError("train_no"));
|
||||
_fromController.addListener(() => _clearError("from_station"));
|
||||
_toController.addListener(() => _clearError("to_station"));
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -150,18 +160,53 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
}
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
errorMessages.remove(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save trainData $trainData");
|
||||
widget.onSavetrain(trainData); // Send object to parent
|
||||
print( "Handle Save accomadationData $trainData");
|
||||
|
||||
Map<String,dynamic> data = trainData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSavetrain(trainData);
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -260,8 +305,14 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
) :
|
||||
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),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
@ -390,7 +441,14 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
if (errorMessages["class"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -478,7 +536,14 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (errorMessages["from_station"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -517,7 +582,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 (isDesktop)
|
||||
Spacer()
|
||||
@ -564,7 +636,14 @@ 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 (isDesktop)
|
||||
Spacer()
|
||||
@ -610,6 +689,13 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@ -15,9 +15,12 @@ class VisaScreen extends StatefulWidget {
|
||||
final Function(bool) onClose;
|
||||
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.onClose,required this.onSaveVisa, this.apiData, required this.selectedItem,
|
||||
required this.apiCountryData, required this.loginUser});
|
||||
|
||||
@override
|
||||
_VisaScreenState createState() => _VisaScreenState();
|
||||
@ -52,13 +55,18 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
String? selectedPurpose;
|
||||
String? selectedCountry;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
|
||||
Map<String, dynamic> get visaData{
|
||||
Map<String, dynamic> data ={
|
||||
"type_of_visa" :selectedPurpose,
|
||||
"country": selectedCountry,
|
||||
// "country_code": selectedCountry,
|
||||
// "country": selectedCountry,
|
||||
"country_code": selectedCountry,
|
||||
"start_date": _dateController.text,
|
||||
"comments":_visaCommentsController.text
|
||||
"comments":_visaCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
@ -91,8 +99,9 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
if (widget.selectedItem != null && widget.selectedItem!["type_of_visa"] != null) {
|
||||
selectedPurpose = widget.selectedItem!["type_of_visa"].toString();
|
||||
}
|
||||
if (widget.selectedItem != null && widget.selectedItem!["selectedCountry"] != null) {
|
||||
selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
||||
if (widget.selectedItem != null && widget.selectedItem!["country_code"] != null) {
|
||||
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
||||
selectedCountry = widget.selectedItem!["country_code"] as String?;
|
||||
}
|
||||
|
||||
|
||||
@ -123,19 +132,45 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
|
||||
|
||||
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"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "This field is required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $visaData");
|
||||
widget.onSaveVisa(visaData); // Send object to parent
|
||||
print( "Handle Save visaData $visaData");
|
||||
|
||||
Map<String,dynamic> data = visaData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
widget.onSaveVisa(visaData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
// // Clear only if this is a new entry
|
||||
// if (widget.selectedItem == null) {
|
||||
// _visaCommentsController.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -233,8 +268,14 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
) :
|
||||
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),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
@ -336,6 +377,8 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// List<String> countryNames = countryList.map((item) => item['country_name'] as String).toList();
|
||||
//
|
||||
|
||||
|
||||
|
||||
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
||||
late List<String> countryCodes; // List of country codes
|
||||
|
||||
@ -350,10 +393,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// Extract only country codes for processing
|
||||
countryCodes = countryMap.keys.toList();
|
||||
|
||||
// Set default selected value
|
||||
if (selectedCountry == null && countryCodes.isNotEmpty) {
|
||||
selectedCountry = countryCodes.first;
|
||||
}
|
||||
selectedCountry ??= null;
|
||||
|
||||
// ____________
|
||||
DateTime? _selectedCheckOutDate;
|
||||
@ -432,40 +472,23 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
selectedCountry = countryMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
|
||||
if (selectedCountry!.isNotEmpty) {
|
||||
errorMessages.remove("country_code");
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
// CustomTextFieldWrapper(
|
||||
// isFocused: _isHotelNameFocused,
|
||||
// isDesktop: isDesktop,
|
||||
// child: SizedBox(
|
||||
// height: 40,
|
||||
//
|
||||
// child: DropdownButtonFormField<String>(
|
||||
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
|
||||
// // controller: _hotelNameController,
|
||||
// value: selectedCountry,
|
||||
// style: TextStyle(fontSize: 12),
|
||||
// decoration: InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// horizontal: 10), // Proper padding
|
||||
// ),
|
||||
// onChanged: countryList.isNotEmpty
|
||||
// ? (newValue) {
|
||||
// setState(() {
|
||||
// selectedCountry = newValue;
|
||||
// });
|
||||
// }
|
||||
// : null,
|
||||
// items: dropdownItems,
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -493,7 +516,15 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
onTap: ()async{
|
||||
await _selectCheckOutDate(context);
|
||||
if (_dateController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
errorMessages.remove("start_date");
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -514,6 +545,13 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["start_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@ -49,8 +49,12 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = accommodationList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return accommodationList.asMap() .entries.map((entry) {
|
||||
return filteredList.asMap() .entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
@ -72,7 +76,7 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Accommodation");
|
||||
onOpen(true, item, "Accomodation");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
|
||||
@ -43,7 +43,7 @@ class BusListWidget extends StatelessWidget{
|
||||
),
|
||||
columns: const [
|
||||
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Date')),
|
||||
@ -67,13 +67,18 @@ class BusListWidget extends StatelessWidget{
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = busList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return busList.asMap().entries.map((entry) {
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["from"]!)),
|
||||
DataCell(Text(item["to"]!)),
|
||||
DataCell(Text(item["date"]!)),
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FlightListWidget extends StatelessWidget {
|
||||
const FlightListWidget({super.key});
|
||||
final List<Map<String,dynamic>> flightList;
|
||||
final Function( bool,Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteFlight;
|
||||
|
||||
const FlightListWidget({super.key, required this.flightList, required this.onOpen, required this.onDeleteFlight});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,8 +31,8 @@ class FlightListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
@ -44,17 +48,24 @@ class FlightListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
List<Map<String, String>> data = [
|
||||
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
|
||||
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
|
||||
];
|
||||
|
||||
return data.map((bus) {
|
||||
List<Map<String, dynamic>> filteredList = flightList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
Map<String,dynamic> item = entry.value;
|
||||
print("Trip Type: ${item["trip_type"]}");
|
||||
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(bus["tripType"]!)),
|
||||
DataCell(Text(bus["class"]!)),
|
||||
DataCell(Text(bus["from"]!)),
|
||||
DataCell(Text(bus["to"]!)),
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["trip_type"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty ? item["trips"][0]["from_place"]?.toString() ?? "N/A" : "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty ? item["trips"][0]["to_place"]?.toString() ?? "N/A" : "N/A")),
|
||||
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -66,13 +77,13 @@ class FlightListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Flight");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteFlight(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -37,7 +37,7 @@ class ForexListWidget extends StatelessWidget{
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Forex Start Date')),
|
||||
DataColumn(label: Text('Forex End Date')),
|
||||
DataColumn(label: Text('Country')),
|
||||
@ -60,13 +60,17 @@ class ForexListWidget extends StatelessWidget{
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = forexList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return forexList.asMap().entries.map((entry) {
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
Map<String,dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["start_date"] ?? "N/A")),
|
||||
DataCell(Text(item["end_date"] ?? "N/A")),
|
||||
DataCell(Text(item["country_code"] ?? "N/A")),
|
||||
|
||||
@ -32,7 +32,7 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Insurance Type')),
|
||||
DataColumn(label: Text('Start Date')),
|
||||
DataColumn(label: Text('End Date')),
|
||||
@ -51,12 +51,16 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = insuranceList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return insuranceList.asMap().entries.map((entry) {
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
Map<String,dynamic> item = entry.value;
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["type_of_insurance"]!)),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Text(item["end_date"]!)),
|
||||
|
||||
@ -5,9 +5,10 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
final List<Map<String,dynamic>> miscellaneousList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Map<String, dynamic>? apiData;
|
||||
|
||||
const MiscellaneousListWidget({super.key, required this.miscellaneousList, required this.onOpen,
|
||||
required this.onDeleteMiscellaneous});
|
||||
required this.onDeleteMiscellaneous,required this.apiData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -33,10 +34,10 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Special Request')),
|
||||
DataColumn(label: Text('Comments')),
|
||||
DataColumn(label: Text('Created On')),
|
||||
// DataColumn(label: Text('Created On')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -52,16 +53,38 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
print("miscellaneousList - $miscellaneousList");
|
||||
List<dynamic> purposeList = apiData?['miscellaneous_special_request'] ?? [];
|
||||
|
||||
return miscellaneousList.asMap().entries.map( (entry) {
|
||||
print("purposeList - $purposeList");
|
||||
|
||||
String getRequestValue(String? specialRequestKey) {
|
||||
if (specialRequestKey == null) return "N/A";
|
||||
|
||||
return purposeList
|
||||
.firstWhere(
|
||||
(element) => element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> filteredList = miscellaneousList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return filteredList.asMap().entries.map( (entry) {
|
||||
int index = entry.key + 1; // To start index from 1
|
||||
Map<String, dynamic> item = entry.value;
|
||||
print(item);
|
||||
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["special_request"] ?? "N/A")),
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(specialRequestValue)),
|
||||
DataCell(Text(getRequestValue(item["special_request"]?.toString()))),
|
||||
DataCell(Text(item["comments"] ?? "N/A")),
|
||||
DataCell(Text(item["created_on"] ?? "N/A")),
|
||||
// DataCell(Text(item["created_on"] ?? "N/A")),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
|
||||
@ -30,7 +30,7 @@ class TaxiListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Destination')),
|
||||
DataColumn(label: Text('Location Of Pickup')),
|
||||
DataColumn(label: Text('Date')),
|
||||
@ -49,13 +49,17 @@ class TaxiListWidget extends StatelessWidget {
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = taxiList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return taxiList.asMap().entries.map((entry) {
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["destination_city"]!)),
|
||||
DataCell(Text(item["location_of_pickup"]!)),
|
||||
DataCell(Text(item["date"]!)),
|
||||
|
||||
@ -30,11 +30,11 @@ class TrainListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Train Number')),
|
||||
DataColumn(label: Text('Class')),
|
||||
// DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
// DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -51,15 +51,22 @@ class TrainListWidget extends StatelessWidget {
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
|
||||
return trainList.asMap().entries.map((entry) {
|
||||
List<Map<String, dynamic>> filteredList = trainList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
final Map<String,dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
|
||||
DataCell(Text(item["train_no"]!)),
|
||||
DataCell(Text(item["class"]!)),
|
||||
DataCell(Text(item["from"]!)),
|
||||
DataCell(Text(item["to"]!)),
|
||||
// DataCell(Text(item["class"]!)),
|
||||
DataCell(Text(item["from_station"]!)),
|
||||
// DataCell(Text(item["to_station"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
|
||||
@ -4,8 +4,11 @@ class VisaListWidget extends StatelessWidget {
|
||||
final List<Map<String, dynamic>> visaList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
const VisaListWidget({super.key, required this.visaList, required this.onOpen,required this.onDeleteMiscellaneous});
|
||||
const VisaListWidget({super.key, required this.visaList, required this.onOpen,
|
||||
required this.onDeleteMiscellaneous, required this.apiData, required this.apiCountryData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -31,7 +34,7 @@ class VisaListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Type of Visa')),
|
||||
DataColumn(label: Text('Country')),
|
||||
DataColumn(label: Text('Start Date')),
|
||||
@ -49,16 +52,36 @@ class VisaListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
List<Map<String, dynamic>> filteredList = visaList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return visaList.asMap().entries.map((entry){
|
||||
List<dynamic> visatypeList = apiData?['visa_type_of_visa'] ?? [];
|
||||
|
||||
String getRequestForVisa(String? specialRequestKey) {
|
||||
if (specialRequestKey == null) return "N/A";
|
||||
|
||||
return visatypeList
|
||||
.firstWhere(
|
||||
(element) => element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry){
|
||||
|
||||
int index = entry.key + 1; // To start index from 1
|
||||
Map<String, dynamic> item = entry.value;
|
||||
print(item);
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["type_of_visa"]!)),
|
||||
DataCell(Text(item["country"]!)),
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
// DataCell(Text(item["type_of_visa"]!)),
|
||||
DataCell( Text(getRequestForVisa( item["type_of_visa"]!.toString()))),
|
||||
DataCell(Text(item["country_code"]!)),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
|
||||
@ -28,6 +28,23 @@ class _CreatePlansState extends State<CreatePlan> {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
|
||||
final args = GoRouterState.of(context).extra as Map<String, dynamic>? ?? {};
|
||||
// final planData = args?['planData'];
|
||||
final bool isViewMode = args?['isViewMode'] ?? false;
|
||||
|
||||
final Map<String, dynamic> planData = args['planData'] as Map<String, dynamic>? ?? {};
|
||||
|
||||
|
||||
// print("isViewMode: $isViewMode");
|
||||
|
||||
// final bool isViewMode = true;
|
||||
// final planData = GoRouterState.of(context).extra as Map<String, dynamic>? ?? {};
|
||||
|
||||
|
||||
print("RECived palndata");
|
||||
// print("RECived palndata - ${planData}");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Column(
|
||||
@ -47,7 +64,10 @@ class _CreatePlansState extends State<CreatePlan> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"New Plan",
|
||||
isViewMode
|
||||
? "View Plan"
|
||||
: (planData.isNotEmpty ? "Update Plan" : "New Plan"),
|
||||
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
@ -70,7 +90,7 @@ class _CreatePlansState extends State<CreatePlan> {
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(26.0),
|
||||
child: CreateNewPlan(isDesktop: isDesktop),
|
||||
child: CreateNewPlan(isDesktop: isDesktop, selectedPlanData: planData, isViewMode : isViewMode),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -84,7 +104,9 @@ class _CreatePlansState extends State<CreatePlan> {
|
||||
|
||||
class CreateNewPlan extends StatefulWidget {
|
||||
final bool isDesktop;
|
||||
const CreateNewPlan({super.key, required this.isDesktop});
|
||||
final bool isViewMode;
|
||||
final Map<String, dynamic> selectedPlanData;
|
||||
const CreateNewPlan({super.key, required this.isDesktop, required this.selectedPlanData, required this.isViewMode});
|
||||
|
||||
@override
|
||||
_CreateNewPlansState createState() => _CreateNewPlansState();
|
||||
@ -103,6 +125,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
late String _selectedOption = "Option 1";
|
||||
// late String? _selectedIsBillable = "Billable";
|
||||
|
||||
String? selectedPlanId;
|
||||
|
||||
String? userDetails;
|
||||
String? userName;
|
||||
@ -111,6 +134,8 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
String? selectedplanUserId;
|
||||
bool? selectedIstravelUser;
|
||||
|
||||
|
||||
|
||||
Map<String, dynamic>? apiData; // Store API response here
|
||||
List<dynamic>? apiCountryData;
|
||||
List<dynamic>? apiCostData; // Store API response here
|
||||
@ -127,9 +152,14 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
Map<String, String?> validationErrors = {};
|
||||
List<Map<String, dynamic>> miscellaneousList = [];
|
||||
// List<Map<String, dynamic>> miscellaneousList = [{"special_request": 1, "comments": "posta", "indx": 1}];
|
||||
List<Map<String, dynamic>> visaList = [];
|
||||
List<Map<String, dynamic>> insuranceList = [];
|
||||
List<Map<String, dynamic>> accommodationList = [];
|
||||
List<Map<String, dynamic>> trainList = [];
|
||||
List<Map<String, dynamic>> busList = [];
|
||||
List<Map<String, dynamic>> taxiList = [];
|
||||
List<Map<String, dynamic>> forexList = [];
|
||||
List<Map<String, dynamic>> flightList = [];
|
||||
|
||||
//Getter Method
|
||||
Map<String, dynamic> get planData => {
|
||||
@ -144,30 +174,29 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
"functional_department": selectedFuncDept,
|
||||
"so_number": "12345",
|
||||
"status": "0",
|
||||
// "created_on": "2025-02-10 14:38:21",
|
||||
"created_by": selfId,
|
||||
// "updated_on": null,
|
||||
"updated_by": null,
|
||||
"updated_by": selfId,
|
||||
"is_active": "1",
|
||||
"flight":[],
|
||||
"accommodation": [],
|
||||
"bus": [],
|
||||
"insurance": [],
|
||||
"flight":flightList,
|
||||
"accomodation": accommodationList,
|
||||
"bus": busList,
|
||||
"taxi": taxiList,
|
||||
"train": trainList,
|
||||
"visa": visaList,
|
||||
"forex": forexList,
|
||||
"insurance": insuranceList,
|
||||
"miscellaneous": miscellaneousList,
|
||||
"taxi": [],
|
||||
"train": [],
|
||||
"visa": [],
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Function to update miscellaneous list
|
||||
void updateMiscellaneousData(List<Map<String, dynamic>> newMiscellaneousList) {
|
||||
setState(() {
|
||||
miscellaneousList = newMiscellaneousList; // Update miscellaneous data
|
||||
});
|
||||
print("Updated Miscellaneous Data in CreateNewPlan: $miscellaneousList");
|
||||
}
|
||||
// // Function to update miscellaneous list
|
||||
// void updateMiscellaneousData(List<Map<String, dynamic>> newMiscellaneousList) {
|
||||
// setState(() {
|
||||
// miscellaneousList = newMiscellaneousList; // Update miscellaneous data
|
||||
// });
|
||||
// print("Updated Miscellaneous Data in CreateNewPlan: $miscellaneousList");
|
||||
// }
|
||||
|
||||
|
||||
void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) {
|
||||
@ -176,7 +205,13 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
case "Miscellaneous":
|
||||
miscellaneousList = newList;
|
||||
break;
|
||||
case "Train":
|
||||
case "Visa":
|
||||
visaList = newList;
|
||||
break;
|
||||
case "Insurance":
|
||||
insuranceList = newList;
|
||||
break;
|
||||
case "Train":
|
||||
trainList = newList;
|
||||
break;
|
||||
case "Bus":
|
||||
@ -186,10 +221,13 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
taxiList = newList;
|
||||
break;
|
||||
case "Forex":
|
||||
taxiList = newList;
|
||||
forexList = newList;
|
||||
break;
|
||||
case "Accommodation":
|
||||
taxiList = newList;
|
||||
case "Flight":
|
||||
flightList = newList;
|
||||
break;
|
||||
case "Accomodation":
|
||||
accommodationList = newList;
|
||||
break;
|
||||
default:
|
||||
print("Unknown itinerary type: $type");
|
||||
@ -203,6 +241,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
super.initState();
|
||||
fetchUserDetails();
|
||||
|
||||
|
||||
fetchPlans();
|
||||
fetchCostCenter();
|
||||
fetchCountryList();
|
||||
@ -223,6 +262,8 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
});
|
||||
});
|
||||
|
||||
handleUpdateData();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@ -233,6 +274,68 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleUpdateData() {
|
||||
if (widget.selectedPlanData != null) {
|
||||
setState(() {
|
||||
|
||||
|
||||
planUsrId = widget.selectedPlanData['user_id'] ?? '';
|
||||
_tripTitleController.text = widget.selectedPlanData['trip_title'] ?? '';
|
||||
_descriptionController.text = widget.selectedPlanData['description'] ?? '';
|
||||
|
||||
_selectedTripType = widget.selectedPlanData['trip_type'];
|
||||
_selectedIsBillable = widget.selectedPlanData['is_billable'] == "1" ? "1" : "2";
|
||||
|
||||
// selectedCostCenterId = widget.selectedPlanData['cost_center_id']?.toString() ;
|
||||
// selectedPurpose = widget.selectedPlanData['purpose_of_travel']?.toString();
|
||||
// selectedFuncDept =widget.selectedPlanData['functional_department']?.toString();
|
||||
|
||||
if (widget.selectedPlanData!["cost_center_id"] != null) {
|
||||
selectedCostCenterId = widget.selectedPlanData!["cost_center_id"].toString();
|
||||
}
|
||||
|
||||
//
|
||||
if (widget.selectedPlanData!["purpose_of_travel"] != null) {
|
||||
selectedPurpose = widget.selectedPlanData!["purpose_of_travel"].toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (widget.selectedPlanData!["functional_department"] != null) {
|
||||
// selectedFuncDept = widget.selectedPlanData!["functional_department"].toString();
|
||||
selectedFuncDept = widget.selectedPlanData!["functional_department"];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Assign lists from selectedPlanData, ensuring they are properly formatted
|
||||
flightList = List<Map<String, dynamic>>.from(widget.selectedPlanData['flight'] ?? []);
|
||||
accommodationList = List<Map<String, dynamic>>.from(widget.selectedPlanData['accomodation'] ?? []);
|
||||
busList = List<Map<String, dynamic>>.from(widget.selectedPlanData['bus'] ?? []);
|
||||
taxiList = List<Map<String, dynamic>>.from(widget.selectedPlanData['taxi'] ?? []);
|
||||
trainList = List<Map<String, dynamic>>.from(widget.selectedPlanData['train'] ?? []);
|
||||
visaList = List<Map<String, dynamic>>.from(widget.selectedPlanData['visa'] ?? []);
|
||||
forexList = List<Map<String, dynamic>>.from(widget.selectedPlanData['forex'] ?? []);
|
||||
insuranceList = List<Map<String, dynamic>>.from(widget.selectedPlanData['insurance'] ?? []);
|
||||
miscellaneousList = List<Map<String, dynamic>>.from(widget.selectedPlanData['miscellaneous'] ?? []);
|
||||
|
||||
});
|
||||
|
||||
if (widget.selectedPlanData.containsKey('plan_id') && widget.selectedPlanData['plan_id'] != null) {
|
||||
print("Plan ID exists: ${widget.selectedPlanData['plan_id']}");
|
||||
selectedPlanId = widget.selectedPlanData['plan_id']?.toString();
|
||||
} else {
|
||||
print("Plan ID is missing or null");
|
||||
}
|
||||
|
||||
|
||||
print("updatedPlanDAta - $planData");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void getSelectedPlanFor(){
|
||||
if (!mounted) return;
|
||||
|
||||
@ -381,9 +484,13 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
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();
|
||||
}
|
||||
});
|
||||
print('plansJSON');
|
||||
|
||||
@ -482,39 +589,44 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
if(validateForm()){
|
||||
print("Form submitted successfully: $planData");
|
||||
postPlanData(planData);
|
||||
context.go('/listPlan');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> postPlanData(Map<String, dynamic> planData) async {
|
||||
final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan';
|
||||
final token = await getToken(); // Fetch token
|
||||
Future<void> postPlanData(Map<String, dynamic> planData) async {
|
||||
final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan';
|
||||
final token = await getToken(); // Fetch token
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode(planData), // Convert map to JSON
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("Plan submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
} else {
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
if (selectedPlanId != null && selectedPlanId!.isNotEmpty) {
|
||||
planData['plan_id'] = selectedPlanId; // Add plan_id for update
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode(planData), // Convert map to JSON
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("Plan submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
} else {
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -606,6 +718,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
focusNode: _tripTitleFocusNode,
|
||||
controller: _tripTitleController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Trip Title",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
@ -705,7 +818,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: DynamicItinerary(apiData: apiData, apiCountryData: apiCountryData,
|
||||
onItineraryUpdate: handleItineraryUpdate,
|
||||
onItineraryUpdate: handleItineraryUpdate,loginUser: selfId,selectedPlanData: planData, isViewMode:widget.isViewMode ,
|
||||
)), // Wrap with Expanded if needed
|
||||
],
|
||||
),
|
||||
@ -754,7 +867,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
onChanged: widget.isViewMode ? null : (newValue) {
|
||||
setState(() {
|
||||
selectedCostCenterId = newValue;
|
||||
});
|
||||
@ -790,15 +903,19 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
return Row(
|
||||
children: [
|
||||
Radio<String>(
|
||||
value: item['dropdown_key'], // Use dropdown_value as value
|
||||
// value: item['dropdown_key'], // Use dropdown_value as value
|
||||
|
||||
value: item['dropdown_key'].toString(),// Convert to String
|
||||
groupValue: _selectedIsBillable,
|
||||
activeColor: Colors.blueAccent,
|
||||
onChanged: (value) {
|
||||
onChanged: widget.isViewMode ? null :(value) {
|
||||
setState(() {
|
||||
_selectedIsBillable = value;
|
||||
});
|
||||
print("SELECBILL - $_selectedIsBillable");
|
||||
},
|
||||
),
|
||||
|
||||
Text(item['dropdown_value'] ?? ''), // Display dropdown_value
|
||||
SizedBox(width: 20), // Spacing
|
||||
],
|
||||
@ -882,7 +999,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
title: Text(option["title"]!),
|
||||
value: option["value"]!,
|
||||
groupValue: _selectedOption,
|
||||
onChanged: (value) {
|
||||
onChanged: widget.isViewMode ? null : (value) {
|
||||
setState(() {
|
||||
_selectedOption = value!;
|
||||
if(value == 'Option 2' || value == 'Option 3' ){
|
||||
@ -918,7 +1035,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
title: Text("Domestic"),
|
||||
value: "1",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
onChanged: widget.isViewMode ? null : (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
@ -941,7 +1058,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
title: Text("International"),
|
||||
value: "2",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
onChanged: widget.isViewMode ? null :(value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
@ -955,6 +1072,9 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
|
||||
Widget _buildNonDescriptionColumn (){
|
||||
// if (apiData == null) {
|
||||
// return Center(child: CircularProgressIndicator()); // Show loading indicator
|
||||
// }
|
||||
|
||||
// 'plan_purpose_of_travel' Starts ------------------------------------------------------
|
||||
|
||||
@ -962,46 +1082,65 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
// value: item['dropdown_key'],
|
||||
value: item['dropdown_key']?.toString(),
|
||||
// value: item['dropdown_key'].toString(),
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
// value: null,
|
||||
value: "1",
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
// 'plan_functional_department' Starts ---------------------------------------------
|
||||
// Ensure Selected Value Exists in the Dropdown List
|
||||
List<String> dropdownKeys = dropdownItems.map((e) => e.value ?? "").toList();
|
||||
|
||||
|
||||
|
||||
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value.toString() : "No options";
|
||||
|
||||
print("Dropdown Purpose List: ${dropdownItems.map((e) => e.value).toList()}");
|
||||
print("Selected Purpose: $selectedPurpose");
|
||||
|
||||
|
||||
|
||||
|
||||
// 'plan_functional_department' Starts ---------------------------------------------
|
||||
|
||||
List<dynamic> funcDeptList = apiData?['plan_functional_department'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownFuncDeptItems = funcDeptList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
// value: item['dropdown_key'],
|
||||
value: item['dropdown_key']?.toString(),
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
if (dropdownFuncDeptItems.isEmpty) {
|
||||
dropdownFuncDeptItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
// value: null,
|
||||
value: "1",
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value : null;
|
||||
// 'plan_functional_department' End
|
||||
// selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : null;
|
||||
selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : "No options";
|
||||
|
||||
return Column(
|
||||
print("Dropdown Functional Department List: ${dropdownFuncDeptItems.map((e) => e.value).toList()}");
|
||||
print("Selected Functional Department: $selectedFuncDept");
|
||||
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
@ -1021,7 +1160,10 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 45, // Set appropriate height
|
||||
child: DropdownButtonFormField<String>(
|
||||
child: apiData == null
|
||||
? Center(child: CircularProgressIndicator()) // Show loading inside dropdown
|
||||
:
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedPurpose,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
@ -1029,11 +1171,12 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
onChanged: widget.isViewMode ? null : purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print("selectedPurpose - $selectedPurpose");
|
||||
}
|
||||
: null,
|
||||
items: dropdownItems,
|
||||
@ -1065,7 +1208,10 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 45, // Set appropriate height
|
||||
child: DropdownButtonFormField<String>(
|
||||
child:
|
||||
apiData == null
|
||||
? Center(child: CircularProgressIndicator()) // Show loading inside dropdown
|
||||
:DropdownButtonFormField<String>(
|
||||
value: selectedFuncDept,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
@ -1073,7 +1219,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: funcDeptList.isNotEmpty
|
||||
onChanged: widget.isViewMode ? null : funcDeptList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedFuncDept = newValue;
|
||||
@ -1123,7 +1269,8 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
@ -1143,14 +1290,42 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
List<Widget> _buildSubmit(isDesktop){
|
||||
return[
|
||||
ElevatedButton(
|
||||
onPressed: (){},
|
||||
child: Text("Cancel")),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.blueAccent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: (){
|
||||
context.go('/listPlan');
|
||||
},
|
||||
child: Text("Cancel")
|
||||
|
||||
),
|
||||
SizedBox(width: 20,),
|
||||
ElevatedButton(
|
||||
onPressed: (){
|
||||
handleSubmit();
|
||||
},
|
||||
child: Text("Submit"))
|
||||
MouseRegion(
|
||||
cursor: widget.isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.isViewMode ? Colors.blueAccent : Colors.blueAccent, // Keep original color
|
||||
foregroundColor: widget.isViewMode ? Colors.white : Colors.white, // Keep original color
|
||||
disabledBackgroundColor: Colors.blueAccent, // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: widget.isViewMode ? null : handleSubmit, // Disable when in view mode
|
||||
child: Text("Submit"),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -24,8 +24,13 @@ import '../itnerary_list/visa_list.dart';
|
||||
class DynamicItinerary extends StatefulWidget {
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
final String? loginUser;
|
||||
final Function(String, List<Map<String, dynamic>>) onItineraryUpdate; // Updated Signature
|
||||
const DynamicItinerary({super.key, required this.apiData, required this.onItineraryUpdate, required this.apiCountryData});
|
||||
final Map<String,dynamic> selectedPlanData;
|
||||
final bool isViewMode ;
|
||||
|
||||
const DynamicItinerary({super.key, required this.apiData, required this.onItineraryUpdate,
|
||||
required this.apiCountryData, required this.loginUser,required this.selectedPlanData, required this.isViewMode});
|
||||
|
||||
|
||||
@override
|
||||
@ -44,103 +49,14 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
// List<Map<String, dynamic>> miscellaneousList = [];
|
||||
|
||||
Map<String, List<Map<String, dynamic>>> itineraryData = {
|
||||
"Train": [{
|
||||
"train_id": "2",
|
||||
"plan_id": "2",
|
||||
"class": "1",
|
||||
"train_no": "OJH65JHB87",
|
||||
"from": "Madurai",
|
||||
"to": "Chennai",
|
||||
"date": "2025-02-02",
|
||||
"comments": "1 st class AC",
|
||||
"created_on": "2025-02-10 14:38:21",
|
||||
"created_by": null,
|
||||
"updated_on": null,
|
||||
"updated_by": null,
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Bus": [{
|
||||
"bus_id": "2",
|
||||
"plan_id": "2",
|
||||
"from": "Chennai - OMR",
|
||||
"to": "Chennai - ECR",
|
||||
"date": "2025-02-15",
|
||||
"time": "12:00:00",
|
||||
"comments": "i need ac bus",
|
||||
"created_on": "2025-02-10 14:38:21",
|
||||
"created_by": null,
|
||||
"updated_on": null,
|
||||
"updated_by": null,
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Taxi": [{
|
||||
"taxi_id": "2",
|
||||
"plan_id": "2",
|
||||
"destination_city": "Madurai",
|
||||
"date": "2025-02-15",
|
||||
"time": "12:00:00",
|
||||
"location_of_pickup": "chennai - ECR",
|
||||
"car_required_for": "1",
|
||||
"no_of_passengers": "2",
|
||||
"car_type": "1",
|
||||
"comments": "Come Sharply",
|
||||
"created_by": "f",
|
||||
"updated_by": "g",
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Miscellaneous": [{
|
||||
"miscellaneous_id": "2",
|
||||
"plan_id": "2",
|
||||
"special_request": "2",
|
||||
"comments": "Please arrange one guide for me ",
|
||||
"created_by": null,
|
||||
"updated_by": null,
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Train": [],
|
||||
"Bus": [],
|
||||
"Taxi": [],
|
||||
"Miscellaneous": [],
|
||||
"Flight": [],
|
||||
"Accommodation": [{
|
||||
"accomodation_id": "2",
|
||||
"plan_id": "2",
|
||||
"destination_city": "chennai",
|
||||
"hotel_name": "The park",
|
||||
"checkin_date": "2025-02-14",
|
||||
"checkin_time": "03:00:00",
|
||||
"checkout_date": "2025-02-15",
|
||||
"checkout_time": "03:00:00",
|
||||
"comments": "A/c is must",
|
||||
"created_on": "2025-02-10 14:38:21",
|
||||
"created_by": null,
|
||||
"updated_on": null,
|
||||
"updated_by": null,
|
||||
"is_active": "0"
|
||||
}],
|
||||
"Insurance": [{
|
||||
"insurance_id": "2",
|
||||
"plan_id": "2",
|
||||
"start_date": "2025-02-01",
|
||||
"end_date": "2025-02-28",
|
||||
"type_of_insurance": "1",
|
||||
"comments": "Temp insurance",
|
||||
"created_on": "2025-02-10 14:38:21",
|
||||
"created_by": null,
|
||||
"updated_on": null,
|
||||
"updated_by": null,
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Visa": [{
|
||||
"visa_id": "2",
|
||||
"plan_id": "2",
|
||||
"country": "2",
|
||||
// "country_code": "2",
|
||||
"type_of_visa": "2",
|
||||
"start_date": "2025-02-01",
|
||||
"comments": "visa registered",
|
||||
"created_on": "2025-02-10 14:38:21",
|
||||
"created_by": null,
|
||||
"updated_on": null,
|
||||
"updated_by": null,
|
||||
"is_active": "1"
|
||||
}],
|
||||
"Accomodation": [],
|
||||
"Insurance": [],
|
||||
"Visa": [],
|
||||
"Forex": [],
|
||||
};
|
||||
|
||||
@ -152,12 +68,62 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
"Taxi": {}, // Stores data for the Car tab
|
||||
"Bus": {}, // Stores data for the Bus tab
|
||||
"Insurance": {}, // Stores data for the Bus tab
|
||||
"Accommodation": {},// Stores data for the Accommodation tab
|
||||
"Miscellaneous": {},// Stores data for the Accommodation tab
|
||||
"Accomodation": {},// Stores data for the Accomodation tab
|
||||
"Miscellaneous": {},
|
||||
"Forex": {},
|
||||
"Visa": {},// Stores data for the Accommodation tab
|
||||
"Visa": {},
|
||||
};
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
handleSelectedPlan();
|
||||
}
|
||||
|
||||
void handleSelectedPlan(){
|
||||
|
||||
|
||||
// Check if selectedPlanData has itinerary data
|
||||
if (hasAnyItineraryData()) {
|
||||
print("selectedPlanData HAS DATA");
|
||||
|
||||
setState(() {
|
||||
itineraryData = {
|
||||
"Train": List<Map<String, dynamic>>.from(widget.selectedPlanData['train'] ?? []),
|
||||
"Bus": List<Map<String, dynamic>>.from(widget.selectedPlanData['bus'] ?? []),
|
||||
"Taxi": List<Map<String, dynamic>>.from(widget.selectedPlanData['taxi'] ?? []),
|
||||
"Miscellaneous": List<Map<String, dynamic>>.from(widget.selectedPlanData['miscellaneous'] ?? []),
|
||||
"Flight": List<Map<String, dynamic>>.from(widget.selectedPlanData['flight'] ?? []),
|
||||
"Accomodation": List<Map<String, dynamic>>.from(widget.selectedPlanData['accomodation'] ?? []),
|
||||
"Insurance": List<Map<String, dynamic>>.from(widget.selectedPlanData['insurance'] ?? []),
|
||||
"Visa": List<Map<String, dynamic>>.from(widget.selectedPlanData['visa'] ?? []),
|
||||
"Forex": List<Map<String, dynamic>>.from(widget.selectedPlanData['forex'] ?? []),
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
print("No itinerary data available");
|
||||
}
|
||||
}
|
||||
|
||||
bool hasAnyItineraryData() {
|
||||
List<String> keys = [
|
||||
"train", "bus", "taxi", "miscellaneous", "flight",
|
||||
"accomodation", "insurance", "visa", "forex"
|
||||
];
|
||||
|
||||
for (String key in keys) {
|
||||
if (widget.selectedPlanData.containsKey(key) &&
|
||||
widget.selectedPlanData[key] is List &&
|
||||
(widget.selectedPlanData[key] as List).isNotEmpty) {
|
||||
return true; // At least one list has data
|
||||
}
|
||||
}
|
||||
return false; // No itinerary data available
|
||||
}
|
||||
|
||||
|
||||
void handleClose(bool value) {
|
||||
setState(() {
|
||||
selectedOption = "";
|
||||
@ -231,6 +197,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
if (itemId != -1) {
|
||||
print(" Updating existing item with id: $existingId");
|
||||
newData["is_active"] = "1";
|
||||
itemList[itemId] = newData;
|
||||
return;
|
||||
}
|
||||
@ -241,39 +208,57 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
int itemIndex = itemList.indexWhere((item) => item["indx"] == existingIndex);
|
||||
if (itemIndex != -1) {
|
||||
print("Updating existing item with indx: $existingIndex");
|
||||
newData["is_active"] = "1";
|
||||
itemList[itemIndex] = newData; // Update the item
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// CASE 3: New Entry (Assign new indx)
|
||||
print(" Creating new entry");
|
||||
newData["indx"] = itemList.length + 1; // Assign a new indx
|
||||
newData["is_active"] = "1";
|
||||
itemList.add(newData);
|
||||
|
||||
print(" Updated $type List: ${itineraryData[type]}");
|
||||
});
|
||||
|
||||
print( " onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
void handleItinerarydelete(String type, Map<String, dynamic> data){
|
||||
setState(() {
|
||||
if(!itineraryData.containsKey(type)){
|
||||
return;
|
||||
// if(!itineraryData.containsKey(type)){
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
itineraryData[type] = []; // Initialize if null
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> itemList = itineraryData[type]!;
|
||||
|
||||
|
||||
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = data[idKey];
|
||||
// int? existingId = data["id"];
|
||||
String? existingId = data["id"];
|
||||
// String? existingId = data["id"];
|
||||
int? existingIndex = data["indx"];
|
||||
|
||||
print("🗑️ Deleting item -> ID: $existingId, Index: $existingIndex");
|
||||
|
||||
// Delete by ID
|
||||
if(existingId != null && existingId != 0){
|
||||
itemList.removeWhere((item) => item["id"]?.toString() == existingId.toString());
|
||||
// itemList.removeWhere((item) => item[idKey]?.toString() == existingId.toString());
|
||||
|
||||
for (var item in itemList) {
|
||||
if (item[idKey]?.toString() == existingId.toString()) {
|
||||
item["is_active"] = 0; // Soft delete
|
||||
print("Updated is_active to 0 for ID: $existingId");
|
||||
}
|
||||
}
|
||||
itineraryData[type] = List.from(itemList);
|
||||
print("Deleted by ID: $existingId");
|
||||
}
|
||||
//Delete by Index
|
||||
@ -287,6 +272,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
itineraryData[type]= List.from(itemList);
|
||||
});
|
||||
print( " onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
@ -334,6 +320,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
break;
|
||||
case "Visa":
|
||||
selectedListWidget = VisaListWidget(visaList : itineraryData["Visa"]!,
|
||||
apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
onOpen: handleEdit,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Visa", data),);
|
||||
break;
|
||||
@ -342,69 +329,76 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
onOpen: handleEdit,
|
||||
onDeleteForex: (data) => handleItinerarydelete("Forex", data),);
|
||||
break;
|
||||
case "Accommodation":
|
||||
selectedListWidget = AccomodationListWidget(accommodationList: itineraryData["Accommodation"]!,
|
||||
case "Accomodation":
|
||||
selectedListWidget = AccomodationListWidget(accommodationList: itineraryData["Accomodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteAccommodation:(data) => handleItinerarydelete("Accommodation", data));
|
||||
onDeleteAccommodation:(data) => handleItinerarydelete("Accomodation", data));
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedListWidget = MiscellaneousListWidget(miscellaneousList: itineraryData["Miscellaneous"]!,
|
||||
onOpen: handleEdit,
|
||||
onOpen: handleEdit,apiData: widget.apiData,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Miscellaneous", data),
|
||||
);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
selectedListWidget = FlightListWidget();
|
||||
selectedListWidget = FlightListWidget(flightList : itineraryData["Flight"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteFlight: (data) => handleItinerarydelete("Flight", data)
|
||||
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (selectedOption) {
|
||||
case "Train":
|
||||
selectedWidget = TrainScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
selectedWidget = TrainScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSavetrain :(data) => handleItineraryUpdate("Train", data),
|
||||
selectedItem: selectedItem);
|
||||
|
||||
break;
|
||||
case "Taxi":
|
||||
selectedWidget = TaxiScreen(onClose: handleClose,apiData: widget.apiData,
|
||||
selectedWidget = TaxiScreen(onClose: handleClose,apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSavetaxi: (data)=> handleItineraryUpdate("Taxi", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Bus":
|
||||
selectedWidget = BusScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
selectedWidget = BusScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveBus: (data)=> handleItineraryUpdate("Bus", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedWidget = InsuranceScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
selectedWidget = InsuranceScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveInsurance:(data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
|
||||
case "Visa":
|
||||
selectedWidget = VisaScreen(onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
selectedWidget = VisaScreen(onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData, loginUser : widget.loginUser,
|
||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||
selectedItem: selectedItem,);
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedWidget = MiscellaneousScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
selectedWidget = MiscellaneousScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveMiscellaneous: (data) => handleItineraryUpdate("Miscellaneous", data),
|
||||
selectedItem: selectedItem, selectedIndex: selectedIndex, );
|
||||
break;
|
||||
case "Accommodation":
|
||||
selectedWidget = AccomodationScreen( onClose: handleClose,
|
||||
onSaveAccomadation: (data)=>handleItineraryUpdate("Accommodation", data),
|
||||
case "Accomodation":
|
||||
selectedWidget = AccomodationScreen( onClose: handleClose, loginUser : widget.loginUser,
|
||||
onSaveAccomadation: (data)=>handleItineraryUpdate("Accomodation", data),
|
||||
selectedItem: selectedItem, );
|
||||
break;
|
||||
case "Forex":
|
||||
selectedWidget = ForexScreen( onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
selectedWidget = ForexScreen( onClose: handleClose,apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
apiCountryData : widget.apiCountryData,
|
||||
onSaveForex: (data)=>handleItineraryUpdate("Forex", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
selectedWidget = FlightScreen(onClose: handleClose,formData: formData["Flight"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
selectedWidget = FlightScreen(onClose: handleClose,loginUser : widget.loginUser,
|
||||
onSaveFlight: (data)=>handleItineraryUpdate("Flight", data), apiData: widget.apiData,
|
||||
selectedItem: selectedItem
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -478,7 +472,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Bus"),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Accommodation"),
|
||||
_buildOption("Accomodation"),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Forex"),
|
||||
SizedBox(width: 20),
|
||||
@ -509,7 +503,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
SizedBox(width: 5),
|
||||
|
||||
SizedBox(width: 5),
|
||||
if (selectedListOption == title)
|
||||
if (selectedListOption == title && widget.isViewMode == false)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'package:frontend/data/models/plan.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@ -64,6 +65,48 @@ class _ListPlansState extends State<ListPlans>{
|
||||
}
|
||||
|
||||
|
||||
Future <Map<String,dynamic>> getViewPlan(String planId) async{
|
||||
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
||||
print("API URL: $apiUrldata");
|
||||
final token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.put(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
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{
|
||||
try {
|
||||
Map<String, dynamic> planData = await getViewPlan(planId);
|
||||
print("ViewAAA - $planData");
|
||||
|
||||
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;
|
||||
@ -134,83 +177,189 @@ class _ListPlansState extends State<ListPlans>{
|
||||
|
||||
List<Plan> plans = snapshot.data!; // Extract the list of plans
|
||||
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
// scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
|
||||
child: SizedBox(
|
||||
// constraints: isTabletOrDesktop
|
||||
// ? const BoxConstraints(maxWidth: double.infinity)
|
||||
// : BoxConstraints.tightFor(width: 600),
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
// 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
|
||||
|
||||
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 1300),
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
|
||||
child: Container(
|
||||
// color: Colors.amber,
|
||||
child: DataTable(
|
||||
// columnSpacing: 50.0,
|
||||
dividerThickness: 0.5, // Reduce the thickness of row dividers
|
||||
columnSpacing: 50.0, // Adjust spacing between columns
|
||||
dividerThickness: 0.5,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
|
||||
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
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,))),
|
||||
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) {
|
||||
|
||||
|
||||
|
||||
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.tripTitle, softWrap: true, overflow: TextOverflow.ellipsis)),
|
||||
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(
|
||||
Row(
|
||||
children:[
|
||||
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
print("View button clicked for ${plan.tripTitle}");
|
||||
viewPlan(plan.planId, isViewMode: true);
|
||||
|
||||
},
|
||||
child: const Text('View',
|
||||
style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
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(),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
class CustomDrawer extends StatelessWidget{
|
||||
@ -24,9 +25,9 @@ class CustomDrawer extends StatelessWidget{
|
||||
_buildDrawerItem(context, Icons.home,'Home', '/home'),
|
||||
_buildExpandableItem(context,Icons.assessment,'Plans',[
|
||||
_buildSubDrawerItem(context,'My Plans','/listPlan'),
|
||||
_buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildDrawerItem(context,Icons.login,'Login','/')
|
||||
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
||||
],
|
||||
);
|
||||
|
||||
@ -50,10 +51,16 @@ class CustomDrawer extends StatelessWidget{
|
||||
return ListTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
onTap: (){
|
||||
context.go(route);
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
onTap: () async {
|
||||
if (route == '/') {
|
||||
// Handle logout separately
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.clear(); // Clear stored token or session data
|
||||
context.go("/"); // Redirect to login instead of home
|
||||
} else {
|
||||
context.go(route);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -24,5 +24,7 @@ final GoRouter router = GoRouter(
|
||||
path: '/createPlan',
|
||||
builder: (context, state) => CreatePlan(),
|
||||
),
|
||||
|
||||
|
||||
],
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user