Plan Creation
This commit is contained in:
parent
7b1e7cf624
commit
a82f4a271c
@ -21,6 +21,35 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
Future<void> storeUserDetails(String token) async{
|
||||
try{
|
||||
final parts = token.split('.');
|
||||
if (parts.length != 3) throw Exception('Invalid token format');
|
||||
|
||||
final payload = json.decode(
|
||||
utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))
|
||||
);
|
||||
|
||||
final userData = payload['data'];
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('auth_token', token);
|
||||
await prefs.setString('user_data', jsonEncode(userData)); // Store full user data
|
||||
|
||||
if(userData != null){
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.setString('auth_token', token);
|
||||
await pref.setString('user_data', jsonEncode(userData));
|
||||
}
|
||||
|
||||
}
|
||||
catch(e){
|
||||
print('Error decoding token: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _login(BuildContext context) async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
const String url = '$apiUrl/auth/login';
|
||||
@ -40,12 +69,10 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
print("data- $data");
|
||||
|
||||
final token = data['token']; // Assuming the token is in response
|
||||
final userId = data['user_id'].toString();
|
||||
// final userId = data['user_id'].toString();
|
||||
|
||||
await storeUserDetails(token);
|
||||
|
||||
// Save token to SharedPreferences
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('auth_token', token);
|
||||
await prefs.setString('userId', userId);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Login Successful")),
|
||||
|
||||
@ -6,14 +6,15 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../data/models/Searchtraveller.dart';
|
||||
import '../../data/models/searchUser.dart';
|
||||
import '../../widgets/custom_text_traveller.dart';
|
||||
|
||||
class UserSelectionDialog extends StatefulWidget{
|
||||
final String title;
|
||||
final void Function(String) onSubmit;
|
||||
final void Function(String,String, bool) onSubmit;
|
||||
|
||||
UserSelectionDialog({Key? key, required this.title, required this.onSubmit}) : super(key: key);
|
||||
UserSelectionDialog({Key? key, required this.title, required this.onSubmit,}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserSelectionDialogState createState() => _UserSelectionDialogState();
|
||||
@ -25,7 +26,13 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
// List<String> _filteredUsers = [];
|
||||
// List<String> _users = [];
|
||||
List<SearchUser> _users = [];
|
||||
List<SearchTraveler> _traveller = [];
|
||||
List<SearchUser> _filteredUsers = [];
|
||||
List<Map<String, dynamic>> _filteredList = [];
|
||||
List<SearchTraveler> _filteredTraveller = [];
|
||||
String userIdSelected = " ";
|
||||
bool isTraveller = false;
|
||||
|
||||
bool _showTravellerForm = false;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
@ -93,7 +100,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
}
|
||||
}
|
||||
|
||||
void _filterUsers(String query) {
|
||||
void _filterUsers1(String query) {
|
||||
print("Filtering users...");
|
||||
setState(() {
|
||||
if (query.isEmpty) {
|
||||
@ -104,7 +111,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
"${user.firstName} ${user.lastName}".toLowerCase(),
|
||||
user.email.toLowerCase() ?? "",
|
||||
user.userId.toLowerCase() ?? "",
|
||||
user.travellerId ?? "",
|
||||
user.mobileNo ?? "",
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
@ -121,12 +127,136 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
}
|
||||
|
||||
|
||||
void _filterUsers(String query) {
|
||||
print("Filtering _filterUsersTravellers...");
|
||||
setState(() {
|
||||
_filteredList.clear(); // Reset the list before filtering
|
||||
|
||||
if (query.isEmpty) {
|
||||
_filteredList = [
|
||||
..._users.map((user) => {"type": "user", "data": user}),
|
||||
];
|
||||
} else {
|
||||
_filteredList = [
|
||||
..._users.where((user) {
|
||||
List<String> searchFields = [
|
||||
"${user.firstName} ${user.lastName}".toLowerCase(),
|
||||
user.email.toLowerCase() ?? "",
|
||||
user.userId.toLowerCase() ?? "",
|
||||
user.mobileNo ?? "",
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((user) => {"type": "user", "data": user}),
|
||||
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
print("Filtered List:");
|
||||
for (var item in _filteredList) {
|
||||
var user = item["data"];
|
||||
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
}
|
||||
}
|
||||
|
||||
void _filterUsersTravellers(String query) {
|
||||
print("Filtering _filterUsersTravellers...");
|
||||
setState(() {
|
||||
_filteredList.clear(); // Reset the list before filtering
|
||||
|
||||
if (query.isEmpty) {
|
||||
_filteredList = [
|
||||
..._users.map((user) => {"type": "user", "data": user}),
|
||||
..._traveller.map((traveller) => {"type": "traveller", "data": traveller}),
|
||||
];
|
||||
} else {
|
||||
_filteredList = [
|
||||
..._users.where((user) {
|
||||
List<String> searchFields = [
|
||||
"${user.firstName} ${user.lastName}".toLowerCase(),
|
||||
user.email.toLowerCase() ?? "",
|
||||
user.userId.toLowerCase() ?? "",
|
||||
user.mobileNo ?? "",
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((user) => {"type": "user", "data": user}),
|
||||
|
||||
..._traveller.where((traveller) {
|
||||
List<String> searchFields = [
|
||||
"${traveller.firstName} ${traveller.lastName}".toLowerCase(),
|
||||
traveller.email.toLowerCase() ?? "",
|
||||
traveller.travellerId.toLowerCase() ?? "",
|
||||
traveller.mobileNo ?? "",
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((traveller) => {"type": "traveller", "data": traveller}),
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
print("Filtered List:");
|
||||
for (var item in _filteredList) {
|
||||
var user = item["data"];
|
||||
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
}
|
||||
}
|
||||
Future<void> fetchTraveller() async {
|
||||
final String apiUrldata = '$apiUrl/api/travellers';
|
||||
|
||||
try {
|
||||
final token = await getToken();
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String,dynamic> responseBody = json.decode(response.body);
|
||||
|
||||
|
||||
print("API Response: $responseBody"); // Debugging
|
||||
|
||||
if (responseBody.containsKey('data') && responseBody['data'] is List) {
|
||||
List<dynamic> travellerList = responseBody['data'];
|
||||
|
||||
|
||||
setState(() {
|
||||
_traveller = travellerList.map((user) => SearchTraveler.fromJson(user)).toList();
|
||||
_filteredTraveller = List.from(_traveller);
|
||||
});
|
||||
|
||||
print("Users fetched: ${_users.length}");
|
||||
for (var travvelr in _traveller) {
|
||||
print("${travvelr.firstName} ${travvelr.lastName}");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load users. Status Code: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching traveller: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchUsers();
|
||||
fetchTraveller();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -150,7 +280,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
setState(() {
|
||||
_showTravellerForm = false;
|
||||
});
|
||||
widget.title == "Others"? _filterUsersTravellers(query):
|
||||
_filterUsers(query);
|
||||
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a user",
|
||||
@ -189,7 +321,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
_searchController.text.isNotEmpty
|
||||
? SizedBox(
|
||||
height: 300, // Limit height to avoid overflow
|
||||
child: _filteredUsers.isEmpty
|
||||
// child: _filteredUsers.isEmpty
|
||||
child: _filteredList.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No users found",
|
||||
@ -197,18 +330,27 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: _filteredUsers.length,
|
||||
// itemCount: _filteredUsers.length,
|
||||
itemCount: _filteredList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final user = _filteredUsers[index];
|
||||
// final user = _filteredUsers[index];
|
||||
|
||||
final item = _filteredList[index];
|
||||
final user = item["data"]; // Extract user object
|
||||
final userType = item["type"]; // "user" or "traveller"
|
||||
|
||||
return ListTile(
|
||||
title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
|
||||
subtitle: Text("ID: ${user.userId}"),
|
||||
subtitle: Text("ID: ${userType == "user" ? user.userId : user.travellerId}"),
|
||||
onTap: () {
|
||||
String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
|
||||
setState(() {
|
||||
_searchController.text = selectedUser;
|
||||
userIdSelected = userType == "user" ? user.userId : user.travellerId;
|
||||
isTraveller = userType == "traveller";
|
||||
});
|
||||
print("Selected: $selectedUser, ID: ${user.userId}");
|
||||
print("Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
|
||||
" isTraveller: $userIdSelected");
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -224,6 +366,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TravelerForm(
|
||||
formKey: _formKey,
|
||||
onSubmit: (String fullName, String travellerId, bool isTraveller) {
|
||||
widget.onSubmit(fullName, travellerId,isTraveller); // Pass the data up
|
||||
},
|
||||
firstNameController: TextEditingController(),
|
||||
lastNameController: TextEditingController(),
|
||||
emailController: TextEditingController(),
|
||||
@ -261,7 +406,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
widget.onSubmit(_searchController.text);
|
||||
print("Submitting: ${_searchController.text}, ID: $userIdSelected");
|
||||
widget.onSubmit(_searchController.text,userIdSelected,isTraveller);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text("Submit"),
|
||||
@ -282,6 +428,7 @@ class TravelerForm extends StatefulWidget {
|
||||
final TextEditingController emailController;
|
||||
final TextEditingController mobileController;
|
||||
final GlobalKey<FormState> formKey;
|
||||
final void Function(String, String, bool) onSubmit;
|
||||
|
||||
TravelerForm({
|
||||
required this.formKey,
|
||||
@ -289,6 +436,7 @@ class TravelerForm extends StatefulWidget {
|
||||
required this.lastNameController,
|
||||
required this.emailController,
|
||||
required this.mobileController,
|
||||
required this.onSubmit
|
||||
});
|
||||
|
||||
@override
|
||||
@ -371,8 +519,26 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final Map<String, dynamic> responseData = jsonDecode(response.body); // Parse response
|
||||
if (responseData["success"] == true && responseData.containsKey("data")) {
|
||||
final travellerData = responseData["data"];
|
||||
|
||||
String travellerId = travellerData["traveller_id"];
|
||||
String firstName = travellerData["first_name"];
|
||||
String lastName = travellerData["last_name"];
|
||||
|
||||
print("Traveller Added: ID: $travellerId, Name: $firstName $lastName");
|
||||
|
||||
// Pass data to callback
|
||||
widget.onSubmit("$firstName $lastName", travellerId, true);
|
||||
|
||||
|
||||
// Close the dialog
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
|
||||
@ -6,12 +6,13 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class AccomodationScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose; // Callback function
|
||||
|
||||
AccomodationScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose,});
|
||||
final Function(bool) onClose; // Callback function
|
||||
final Function(Map<String,dynamic>) onSaveAccomadation;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
|
||||
AccomodationScreen({
|
||||
required this.onClose, required this.onSaveAccomadation, required this.selectedItem});
|
||||
|
||||
@override
|
||||
_AccomodationScreenState createState() => _AccomodationScreenState();
|
||||
@ -28,7 +29,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
final FocusNode _checkOutTimeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _destinationController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
@ -46,105 +46,65 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
bool _checkOutTimeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) onFocusChange) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
onFocusChange(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Map<String, dynamic> get accomadationData {
|
||||
|
||||
Map<String,dynamic> data ={
|
||||
"destination_city": _destinationController.text,
|
||||
"hotel_name": _hotelNameController.text,
|
||||
"checkin_date": _checkInController.text ,
|
||||
"checkin_time": _checkInTimeController.text,
|
||||
"checkout_date": _checkOutController.text,
|
||||
"checkout_time": _checkOutTimeController.text,
|
||||
"comments": _commentsController.text,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["accomodation_id"] != null && widget.selectedItem?["accomodation_id"] != 0) {
|
||||
data["accomodation_id"] = widget.selectedItem!["accomodation_id"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_destinationFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_destinationFocused = _destinationFocusNode.hasFocus;
|
||||
});
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_checkInFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_checkInFocus = _checkInFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_checkInTimeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_checkInTimeFocus = _checkInTimeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_checkOutFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_checkInFocus = _checkInFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_checkOutTimeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_checkInTimeFocus = _checkOutTimeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
});
|
||||
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocused = focus);
|
||||
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
|
||||
_addFocusListener(_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
|
||||
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
|
||||
_addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
|
||||
List<String> fields = [
|
||||
"_destination",
|
||||
"_hotelName",
|
||||
"_Check_In",
|
||||
"_Check_In_Time",
|
||||
"_Check_Out",
|
||||
"_Check_Out_Time",
|
||||
"_comments"
|
||||
];
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
_hotelNameController = initController("hotel_name");
|
||||
_checkInController = initController("checkin_date");
|
||||
_checkInTimeController = initController("checkin_time");
|
||||
_checkOutController = initController("checkout_date");
|
||||
_checkOutTimeController = initController("checkout_time");
|
||||
_commentsController = initController("comments");
|
||||
|
||||
_destinationController =
|
||||
TextEditingController(text: widget.formData["destination"] ?? "");
|
||||
_hotelNameController =
|
||||
TextEditingController(text: widget.formData["_hotelName"] ?? "");
|
||||
_checkInController =
|
||||
TextEditingController(text: widget.formData["_Check_In"] ?? "");
|
||||
_checkInTimeController =
|
||||
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
|
||||
_checkOutController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_checkOutTimeController =
|
||||
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
_destinationController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "destination", _destinationController.text);
|
||||
}); // Save data when user types
|
||||
_hotelNameController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_hotelName", _hotelNameController.text);
|
||||
});
|
||||
_checkInController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_Check_In", _checkInController.text);
|
||||
});
|
||||
_checkInTimeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_Check_In_Time", _checkInTimeController.text);
|
||||
});
|
||||
_checkOutController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_Check_Out", _checkOutController.text);
|
||||
});
|
||||
_checkOutTimeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_Check_Out_Time", _checkOutTimeController.text);
|
||||
});
|
||||
_commentsController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Accommodation", "_comments", _commentsController.text);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -160,6 +120,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save accomadtion $accomadationData");
|
||||
widget.onSaveAccomadation(accomadationData); // 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();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -642,7 +615,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
// 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
|
||||
@ -661,7 +634,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -6,13 +6,14 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class BusScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
|
||||
BusScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, this.apiData});
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>)onSaveBus;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
|
||||
BusScreen({
|
||||
required this.onClose, this.apiData, required this.onSaveBus, required this.selectedItem});
|
||||
|
||||
@override
|
||||
_BusScreenState createState() => _BusScreenState();
|
||||
@ -39,7 +40,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
late TextEditingController _toController = TextEditingController();
|
||||
late TextEditingController _dateController = TextEditingController();
|
||||
late TextEditingController _timeController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
late TextEditingController _buscommentsController = TextEditingController();
|
||||
|
||||
bool _tripTypeFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
@ -49,6 +50,33 @@ class _BusScreenState extends State<BusScreen> {
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
|
||||
Map<String, dynamic> get busData{
|
||||
Map<String,dynamic> data = {
|
||||
"from": _fromController.text,
|
||||
"to": _toController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _buscommentsController.text,
|
||||
};
|
||||
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["bus_id"] != null && widget.selectedItem?["bus_id"] != 0) {
|
||||
data["bus_id"] = widget.selectedItem!["bus_id"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -57,6 +85,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
setState(() {
|
||||
_tripTypeFocused = _tripTypeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
@ -91,54 +120,15 @@ class _BusScreenState extends State<BusScreen> {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
_tripTypeController =
|
||||
TextEditingController(text: widget.formData["tripType"] ?? "");
|
||||
_hotelNameController =
|
||||
TextEditingController(text: widget.formData["_hotelName"] ?? "");
|
||||
_fromController =
|
||||
TextEditingController(text: widget.formData["_from"] ?? "");
|
||||
_toController =
|
||||
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
|
||||
_dateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_timeController =
|
||||
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
_buscommentsController = initController("comments");
|
||||
_fromController = initController("from");
|
||||
_toController = initController("to");
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
|
||||
// Save data when user types
|
||||
_tripTypeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_tripType", _tripTypeController.text);
|
||||
}); // Save data when user types
|
||||
_hotelNameController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_hotelName", _hotelNameController.text);
|
||||
});
|
||||
_fromController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_from", _fromController.text);
|
||||
});
|
||||
_toController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_Check_In_Time", _toController.text);
|
||||
});
|
||||
_dateController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_Check_Out", _dateController.text);
|
||||
});
|
||||
_timeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_Check_Out_Time", _timeController.text);
|
||||
});
|
||||
_commentsController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Bus", "_comments", _commentsController.text);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -150,12 +140,23 @@ class _BusScreenState extends State<BusScreen> {
|
||||
_toController.dispose();
|
||||
_dateController.dispose();
|
||||
_timeController.dispose();
|
||||
_commentsController.dispose();
|
||||
_buscommentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $busData");
|
||||
widget.onSaveBus(busData); // 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();
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -277,7 +278,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -318,7 +319,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
}
|
||||
: null,
|
||||
|
||||
@ -654,7 +654,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
controller: _buscommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
@ -677,7 +677,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
// 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
|
||||
@ -696,7 +696,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,13 +6,15 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class InsuranceScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
|
||||
InsuranceScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, required this.apiData});
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveInsurance;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
|
||||
|
||||
InsuranceScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveInsurance, required this.selectedItem});
|
||||
|
||||
@override
|
||||
_InsuranceScreenState createState() => _InsuranceScreenState();
|
||||
@ -26,17 +28,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
final FocusNode _toFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _startdateController = TextEditingController();
|
||||
late TextEditingController _endDateController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
late TextEditingController _insuranceCommentsController = TextEditingController();
|
||||
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _dateFocus = false;
|
||||
@ -44,62 +43,53 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
|
||||
String? selectedTripType;
|
||||
String? selectedInsuranceType;
|
||||
|
||||
Map<String, dynamic> get InsuranceData{
|
||||
Map<String, dynamic> data = {
|
||||
|
||||
"type_of_insurance": selectedInsuranceType,
|
||||
"start_date": _startdateController.text,
|
||||
"end_date": _endDateController.text,
|
||||
"comments": _insuranceCommentsController.text,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) {
|
||||
data["insurance_id"] = widget.selectedItem!["insurance_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// _tripTypeFocusNode.addListener(() {
|
||||
// setState(() {
|
||||
// _tripTypeFocused = _tripTypeFocusNode.hasFocus;
|
||||
// });
|
||||
// });
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
setState(() {_isHotelNameFocused = _hotelNameFocusNode.hasFocus;});});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {_dateFocus = _fromFocusNode.hasFocus;});});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
setState(() {_commentsFocus = _commentsFocusNode.hasFocus;});});
|
||||
|
||||
|
||||
|
||||
|
||||
_tripTypeController =
|
||||
TextEditingController(text: widget.formData["tripType"] ?? "");
|
||||
_insuranceCommentsController =
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
_startdateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
|
||||
_endDateController =
|
||||
TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
_tripTypeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Insurance", "_tripType", _tripTypeController.text);
|
||||
}); // Save data when user types
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) {
|
||||
selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString();
|
||||
}
|
||||
|
||||
_startdateController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Insurance", "_Check_Out", _startdateController.text);
|
||||
});
|
||||
|
||||
_commentsController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Insurance", "_comments", _commentsController.text);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -111,12 +101,25 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save InsuranceData $InsuranceData");
|
||||
widget.onSaveInsurance(InsuranceData); // 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();
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_tripTypeController.dispose();
|
||||
_startdateController.dispose();
|
||||
_commentsController.dispose();
|
||||
_insuranceCommentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -244,7 +247,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -258,7 +261,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
return [
|
||||
@ -270,7 +273,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedPurpose,
|
||||
value: selectedInsuranceType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
@ -280,10 +283,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
selectedInsuranceType = newValue;
|
||||
});
|
||||
|
||||
print(selectedPurpose);
|
||||
print(selectedInsuranceType);
|
||||
|
||||
}
|
||||
: null,
|
||||
@ -298,85 +301,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
@ -554,7 +478,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
controller: _insuranceCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
@ -577,7 +501,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
// 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
|
||||
@ -596,7 +520,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -6,13 +6,16 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class MiscellaneousScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
|
||||
MiscellaneousScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, required this.apiData});
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveMiscellaneous;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final int? selectedIndex;
|
||||
|
||||
MiscellaneousScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveMiscellaneous,
|
||||
this.selectedItem, this.selectedIndex,});
|
||||
|
||||
@override
|
||||
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
|
||||
@ -25,36 +28,46 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
final FocusNode _toFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _startdateController = TextEditingController();
|
||||
late TextEditingController _endDateController = TextEditingController();
|
||||
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _dateFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
|
||||
String? selectedTripType;
|
||||
String? selectedSpecialType;
|
||||
|
||||
Map<String, dynamic> get miscellaneousData {
|
||||
Map<String, dynamic> data = {
|
||||
"special_request": selectedSpecialType,
|
||||
"comments": _commentsController.text,
|
||||
};
|
||||
|
||||
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"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// _tripTypeFocusNode.addListener(() {
|
||||
// setState(() {
|
||||
// _tripTypeFocused = _tripTypeFocusNode.hasFocus;
|
||||
// });
|
||||
// });
|
||||
if (widget.selectedItem == null) {
|
||||
_commentsController.clear();
|
||||
}
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
@ -62,13 +75,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
});
|
||||
});
|
||||
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
@ -77,50 +83,37 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
|
||||
|
||||
|
||||
_tripTypeController =
|
||||
TextEditingController(text: widget.formData["tripType"] ?? "");
|
||||
_startdateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
_tripTypeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Miscellaneous", "_tripType", _tripTypeController.text);
|
||||
}); // Save data when user types
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["special_request"] != null) {
|
||||
selectedSpecialType = widget.selectedItem!["special_request"].toString();
|
||||
}
|
||||
|
||||
_startdateController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Miscellaneous", "_Check_Out", _startdateController.text);
|
||||
});
|
||||
|
||||
_commentsController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Miscellaneous", "_comments", _commentsController.text);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_tripTypeController.dispose();
|
||||
_startdateController.dispose();
|
||||
_commentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $miscellaneousData");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -140,6 +133,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_commentsController.clear();
|
||||
widget.onClose(false);
|
||||
},
|
||||
child: Icon(
|
||||
@ -177,18 +171,11 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
List<List<Widget>> rowBuilders = [
|
||||
// _builClassType(isDesktop),
|
||||
_buildSecondRow(isDesktop)
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// // Iterate over rowBuilders and wrap each in a responsive container
|
||||
// ...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
||||
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
|
||||
// Actions row remains a Row
|
||||
@ -244,7 +231,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -258,7 +245,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedSpecialType = dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
|
||||
selectedSpecialType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
|
||||
|
||||
|
||||
return [
|
||||
@ -287,7 +274,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
@ -298,240 +284,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
}
|
||||
|
||||
|
||||
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? selectedSpecialType = 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: selectedSpecialType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedSpecialType = 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) {
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
Future<void> _selectCheckOutDate(BuildContext context) async {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime today = DateTime(now.year, now.month, now.day);
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
_startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _selectEndCheckOutDate(BuildContext context) async {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime today = DateTime(now.year, now.month, now.day);
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
_endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Start Date",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
controller: _startdateController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Select Date",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: Icon(Icons.calendar_today,
|
||||
size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"End Date",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectEndCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
controller: _endDateController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Select Date",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: Icon(Icons.calendar_today,
|
||||
size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildThirdRow(bool isDesktop) {
|
||||
return [
|
||||
@ -577,7 +329,9 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog or screen
|
||||
|
||||
_commentsController.clear();
|
||||
widget.onClose(false);// Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -596,7 +350,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -6,13 +6,13 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class TaxiScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String,dynamic>) onSavetaxi;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
|
||||
TaxiScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, this.apiData});
|
||||
TaxiScreen({
|
||||
required this.onClose, this.apiData, required this.onSavetaxi, required this.selectedItem});
|
||||
|
||||
@override
|
||||
_TaxiScreenState createState() => _TaxiScreenState();
|
||||
@ -23,86 +23,101 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
final FocusNode _destinationFocusNode = FocusNode();
|
||||
final FocusNode _locationFocusNode = FocusNode();
|
||||
final FocusNode _taxiReqFocusNode = FocusNode();
|
||||
final FocusNode _toFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _numPassengerFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
late TextEditingController _fromController = TextEditingController();
|
||||
late TextEditingController _toController = TextEditingController();
|
||||
late TextEditingController _destinationController = TextEditingController();
|
||||
late TextEditingController _locationController = TextEditingController();
|
||||
late TextEditingController _dateController = TextEditingController();
|
||||
late TextEditingController _timeController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
late TextEditingController _numPassengerController = TextEditingController();
|
||||
late TextEditingController _taxiCommentsController = TextEditingController();
|
||||
|
||||
bool _tripTypeFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _fromFocus = false;
|
||||
bool _destinationFocus = false;
|
||||
bool _locationFocus = false;
|
||||
bool _toFocus = false;
|
||||
bool _dateFocus = false;
|
||||
bool _taxiReqFocused = false;
|
||||
bool _numPassengerFocus = false;
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
String? selectedReqTaxi;
|
||||
String? selectedCarType;
|
||||
|
||||
|
||||
Map<String , dynamic> get taxiData {
|
||||
Map<String, dynamic> data ={
|
||||
|
||||
|
||||
"destination_city": _destinationController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"location_of_pickup": _locationController.text,
|
||||
"car_required_for": selectedReqTaxi,
|
||||
"no_of_passengers": _numPassengerController.text,
|
||||
"car_type": selectedCarType,
|
||||
"comments": _taxiCommentsController.text,
|
||||
// "updated_on": ,
|
||||
// "updated_by": ,
|
||||
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["taxi_id"] != null && widget.selectedItem?["taxi_id"] != 0) {
|
||||
data["taxi_id"] = widget.selectedItem!["taxi_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
|
||||
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
|
||||
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(_fromFocusNode, (focus) => _fromFocus = focus);
|
||||
_addFocusListener(_toFocusNode, (focus) => _toFocus = focus);
|
||||
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocus = focus);
|
||||
_addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus);
|
||||
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
|
||||
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
|
||||
_addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus);
|
||||
_addFocusListener(_numPassengerFocusNode, (focus) => _numPassengerFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
// _commentsFocusNode.addListener(() {
|
||||
// setState(() {
|
||||
// _commentsFocus = _commentsFocusNode.hasFocus;
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
_locationController = initController("location_of_pickup");
|
||||
_numPassengerController = initController("no_of_passengers");
|
||||
_taxiCommentsController = initController("comments");
|
||||
|
||||
_tripTypeController =
|
||||
TextEditingController(text: widget.formData["tripType"] ?? "");
|
||||
_hotelNameController =
|
||||
TextEditingController(text: widget.formData["_hotelName"] ?? "");
|
||||
_fromController =
|
||||
TextEditingController(text: widget.formData["_from"] ?? "");
|
||||
_toController =
|
||||
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
|
||||
_dateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_timeController =
|
||||
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
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);
|
||||
});
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["car_required_for"] != null) {
|
||||
selectedReqTaxi = widget.selectedItem!["car_required_for"].toString();
|
||||
}
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["car_type"] != null) {
|
||||
selectedCarType = widget.selectedItem!["car_type"].toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
@ -113,21 +128,33 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_tripTypeController.dispose();
|
||||
_hotelNameController.dispose();
|
||||
_fromController.dispose();
|
||||
_toController.dispose();
|
||||
_destinationFocusNode.dispose();
|
||||
_locationFocusNode.dispose();
|
||||
_destinationController.dispose();
|
||||
_dateController.dispose();
|
||||
_timeController.dispose();
|
||||
_commentsController.dispose();
|
||||
_taxiCommentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save taxiData $taxiData");
|
||||
widget.onSavetaxi(taxiData); // 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();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -213,7 +240,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -227,7 +254,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedCarType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
return [
|
||||
@ -271,16 +298,16 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _fromFocus,
|
||||
isFocused: _numPassengerFocus,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _fromFocusNode,
|
||||
controller: _fromController,
|
||||
focusNode: _numPassengerFocusNode,
|
||||
controller: _numPassengerController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Destination",
|
||||
labelText: "Number of Passenger",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -316,8 +343,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedPurpose,
|
||||
focusNode: _toFocusNode, // Assign the correct focus node
|
||||
value: selectedCarType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
@ -327,12 +354,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
selectedCarType = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
}
|
||||
: null,
|
||||
|
||||
@ -360,7 +385,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -374,19 +399,19 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedReqTaxi ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isFocused: _taxiReqFocused,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedPurpose,
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _taxiReqFocusNode, // Assign the correct focus node
|
||||
value: selectedReqTaxi,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
@ -396,12 +421,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
selectedReqTaxi = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
}
|
||||
: null,
|
||||
|
||||
@ -415,86 +437,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
|
||||
@ -558,13 +500,13 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _fromFocus,
|
||||
isFocused: _destinationFocus,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _fromFocusNode,
|
||||
controller: _fromController,
|
||||
focusNode: _destinationFocusNode,
|
||||
controller: _destinationController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Destination",
|
||||
@ -597,14 +539,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _toFocus,
|
||||
isFocused: _locationFocus,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _toFocusNode,
|
||||
controller: _toController,
|
||||
focusNode: _locationFocusNode,
|
||||
controller: _locationController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Location of Pickup",
|
||||
@ -711,8 +653,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -737,7 +677,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
controller: _taxiCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
@ -760,7 +700,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
// 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
|
||||
@ -779,7 +719,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -6,19 +6,20 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class TrainScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
|
||||
TrainScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, this.apiData});
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(Map<String, dynamic>)onSavetrain;
|
||||
final Function(bool) onClose;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
|
||||
TrainScreen({
|
||||
required this.onClose, this.apiData, required this.onSavetrain, required this.selectedItem});
|
||||
|
||||
@override
|
||||
_BusScreenState createState() => _BusScreenState();
|
||||
_TrainScreenState createState() => _TrainScreenState();
|
||||
}
|
||||
|
||||
class _BusScreenState extends State<TrainScreen> {
|
||||
class _TrainScreenState extends State<TrainScreen> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
@ -31,7 +32,6 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _trainNoController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
@ -39,7 +39,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
late TextEditingController _toController = TextEditingController();
|
||||
late TextEditingController _dateController = TextEditingController();
|
||||
late TextEditingController _timeController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
late TextEditingController _trainCommentsController = TextEditingController();
|
||||
|
||||
bool _trainNoFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
@ -49,11 +49,43 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
String? selectedClass;
|
||||
|
||||
|
||||
|
||||
Map<String , dynamic> get trainData {
|
||||
Map<String, dynamic> data ={
|
||||
|
||||
"train_no": _trainNoController.text,
|
||||
"class": selectedClass,
|
||||
"from": _fromController.text,
|
||||
"to": _toController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _trainCommentsController.text,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["train_id"] != null && widget.selectedItem?["train_id"] != 0) {
|
||||
data["train_id"] = widget.selectedItem!["train_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TextEditingController initController(String key) {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_trainNoFocusNode.addListener(() {
|
||||
_trainNoFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_trainNoFocused = _trainNoFocusNode.hasFocus;
|
||||
});
|
||||
@ -68,7 +100,6 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
_fromFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_toFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_toFocus = _toFocusNode.hasFocus;
|
||||
@ -79,13 +110,11 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_timeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_timeFocus = _timeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
@ -93,53 +122,20 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
});
|
||||
|
||||
|
||||
_trainCommentsController = initController("comments");
|
||||
_trainNoController = initController("train_no");
|
||||
_fromController = initController("from");
|
||||
_toController = initController("to");
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
|
||||
_trainNoController =
|
||||
TextEditingController(text: widget.formData["trainNo"] ?? "");
|
||||
_hotelNameController =
|
||||
TextEditingController(text: widget.formData["_hotelName"] ?? "");
|
||||
_fromController =
|
||||
TextEditingController(text: widget.formData["_from"] ?? "");
|
||||
_toController =
|
||||
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
|
||||
_dateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_timeController =
|
||||
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
_trainNoController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_trainNo", _trainNoController.text);
|
||||
}); // Save data when user types
|
||||
_hotelNameController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_hotelName", _hotelNameController.text);
|
||||
});
|
||||
_fromController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_from", _fromController.text);
|
||||
});
|
||||
_toController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_Check_In_Time", _toController.text);
|
||||
});
|
||||
_dateController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_Check_Out", _dateController.text);
|
||||
});
|
||||
_timeController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_Check_Out_Time", _timeController.text);
|
||||
});
|
||||
_commentsController.addListener(() {
|
||||
widget.updateFormData(
|
||||
"Train", "_comments", _commentsController.text);
|
||||
});
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["class"] != null) {
|
||||
selectedClass = widget.selectedItem!["class"].toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_trainNoFocusNode.dispose();
|
||||
@ -149,11 +145,22 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
_toController.dispose();
|
||||
_dateController.dispose();
|
||||
_timeController.dispose();
|
||||
_commentsController.dispose();
|
||||
_trainCommentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save trainData $trainData");
|
||||
widget.onSavetrain(trainData); // 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();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -312,29 +319,6 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
),
|
||||
),
|
||||
|
||||
// child: DropdownButtonFormField<String>(
|
||||
// focusNode: _trainNoFocusNode, // Assign the correct focus node
|
||||
// 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;
|
||||
// });
|
||||
// print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
//
|
||||
//
|
||||
// widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
// }
|
||||
// : null,
|
||||
//
|
||||
// items: dropdownItems,
|
||||
// ),
|
||||
|
||||
|
||||
),
|
||||
@ -349,7 +333,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -363,7 +347,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedClass ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
Column(
|
||||
@ -386,7 +370,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _hotelNameFocusNode, // Assign the correct focus node
|
||||
// controller: _hotelNameController,
|
||||
value: selectedPurpose,
|
||||
value: selectedClass,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
@ -396,25 +380,14 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
selectedClass = 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),
|
||||
// ),
|
||||
// ),
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -665,12 +638,12 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
controller: _trainCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -688,7 +661,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Close the dialog or screen
|
||||
widget.onClose(false);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -707,7 +680,7 @@ class _BusScreenState extends State<TrainScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
@ -6,13 +7,17 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class VisaScreen extends StatefulWidget {
|
||||
final Map<String, String> formData;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String tab, String key, String value) updateFormData;
|
||||
final Function(bool) onClose;
|
||||
|
||||
VisaScreen({required this.formData, required this.updateFormData,
|
||||
required this.onClose, this.apiData});
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String,dynamic>) onSaveVisa;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
|
||||
VisaScreen({
|
||||
required this.onClose,required this.onSaveVisa, this.apiData, required this.selectedItem, required this.apiCountryData});
|
||||
|
||||
@override
|
||||
_VisaScreenState createState() => _VisaScreenState();
|
||||
@ -23,15 +28,13 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
List<dynamic> countryList = [];
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
final FocusNode _toFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
late Map<String, TextEditingController> _controllers;
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
@ -39,72 +42,65 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
late TextEditingController _toController = TextEditingController();
|
||||
late TextEditingController _dateController = TextEditingController();
|
||||
late TextEditingController _timeController = TextEditingController();
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
late TextEditingController _visaCommentsController = TextEditingController();
|
||||
|
||||
bool _tripTypeFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _fromFocus = false;
|
||||
bool _toFocus = false;
|
||||
bool _dateFocus = false;
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
String? selectedPurpose;
|
||||
String? selectedCountry;
|
||||
|
||||
Map<String, dynamic> get visaData{
|
||||
Map<String, dynamic> data ={
|
||||
"type_of_visa" :selectedPurpose,
|
||||
"country": selectedCountry,
|
||||
// "country_code": selectedCountry,
|
||||
"start_date": _dateController.text,
|
||||
"comments":_visaCommentsController.text
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["visa_id"] != null && widget.selectedItem?["visa_id"] != 0) {
|
||||
data["visa_id"] = widget.selectedItem!["visa_id"];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
|
||||
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
|
||||
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(_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;
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
|
||||
_tripTypeController =
|
||||
TextEditingController(text: widget.formData["tripType"] ?? "");
|
||||
_hotelNameController =
|
||||
TextEditingController(text: widget.formData["_hotelName"] ?? "");
|
||||
_fromController =
|
||||
TextEditingController(text: widget.formData["_from"] ?? "");
|
||||
_toController =
|
||||
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
|
||||
_visaCommentsController =
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
_dateController =
|
||||
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
|
||||
_timeController =
|
||||
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
|
||||
_commentsController =
|
||||
TextEditingController(text: widget.formData["_comments"] ?? "");
|
||||
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
|
||||
|
||||
// Save data when user types
|
||||
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);
|
||||
});
|
||||
// Set the selected value if available
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
@ -122,11 +118,23 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
_toController.dispose();
|
||||
_dateController.dispose();
|
||||
_timeController.dispose();
|
||||
_commentsController.dispose();
|
||||
_visaCommentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $visaData");
|
||||
widget.onSaveVisa(visaData); // Send object to parent
|
||||
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) {
|
||||
@ -184,7 +192,6 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
|
||||
List<List<Widget>> rowBuilders = [
|
||||
// _builClassType(isDesktop),
|
||||
_buildSecondRow(isDesktop)
|
||||
];
|
||||
|
||||
@ -249,7 +256,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -263,7 +270,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
return [
|
||||
@ -290,7 +297,6 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
widget.updateFormData("Flight", "trip_type", newValue ?? "");
|
||||
}
|
||||
: null,
|
||||
|
||||
@ -304,110 +310,51 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
|
||||
|
||||
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(
|
||||
"Country",
|
||||
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) {
|
||||
|
||||
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
|
||||
// List<dynamic> countryList = widget.apiCountryData ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
//
|
||||
// List<DropdownMenuItem<String>> dropdownItems = countryList
|
||||
// .map((item)=>DropdownMenuItem<String>(
|
||||
// value: item['country_code'], // Use 'country_code' from API response
|
||||
// child: Text(item['country_name']),
|
||||
// )).toList();
|
||||
//
|
||||
// if (dropdownItems.isEmpty) {
|
||||
// dropdownItems.add(
|
||||
// DropdownMenuItem<String>(
|
||||
// value: null,
|
||||
// child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// // Default selected value
|
||||
// selectedCountry ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
// List<String> countryNames = countryList.map((item) => item['country_name'] as String).toList();
|
||||
//
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
||||
late List<String> countryCodes; // List of country codes
|
||||
|
||||
|
||||
countryList = widget.apiCountryData ?? [];
|
||||
|
||||
// Map country codes to country names
|
||||
countryMap = {
|
||||
for (var item in countryList) item['country_code'] as String: item['country_name'] as String
|
||||
};
|
||||
|
||||
// Extract only country codes for processing
|
||||
countryCodes = countryMap.keys.toList();
|
||||
|
||||
// Set default selected value
|
||||
if (selectedCountry == null && countryCodes.isNotEmpty) {
|
||||
selectedCountry = countryCodes.first;
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
|
||||
|
||||
// ____________
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
@ -454,41 +401,71 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
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
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true, // Enables search functionality
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Country...",
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1,),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Country",
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
// Find the country_code based on selected country_name
|
||||
selectedCountry = countryMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
});
|
||||
}
|
||||
: 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),
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
),
|
||||
// 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,
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
|
||||
@ -502,7 +479,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Date",
|
||||
"Start Date",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -566,7 +543,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _commentsController,
|
||||
controller: _visaCommentsController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
@ -589,7 +566,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// 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
|
||||
@ -608,7 +585,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: Implement save logic
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AccomodationListWidget extends StatelessWidget {
|
||||
const AccomodationListWidget({super.key});
|
||||
|
||||
final List<Map<String,dynamic>> accommodationList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteAccommodation;
|
||||
|
||||
const AccomodationListWidget({super.key, required this.accommodationList, required this.onOpen, required this.onDeleteAccommodation});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -26,10 +31,11 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Destination City')),
|
||||
DataColumn(label: Text('Hotel Name')),
|
||||
DataColumn(label: Text('CheckIn Date')),
|
||||
DataColumn(label: Text('CheckOut Date')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -42,17 +48,19 @@ class AccomodationListWidget 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) {
|
||||
|
||||
return accommodationList.asMap() .entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
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")), // Index column
|
||||
DataCell(Text(item["destination_city"]!)),
|
||||
DataCell(Text(item["hotel_name"]!)),
|
||||
DataCell(Text(item["checkin_date"]!)),
|
||||
DataCell(Text(item["checkout_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -64,13 +72,13 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Accommodation");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteAccommodation(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -8,61 +8,12 @@ import '../../config/apiUrl.dart';
|
||||
import '../../data/models/plan.dart';
|
||||
|
||||
|
||||
class BusListWidget extends StatefulWidget{
|
||||
const BusListWidget({super.key});
|
||||
class BusListWidget extends StatelessWidget{
|
||||
final List<Map<String, dynamic>> busList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteBus;
|
||||
const BusListWidget({super.key, required this.busList, required this.onOpen, required this.onDeleteBus});
|
||||
|
||||
@override
|
||||
_BusListWidgetState createState() => _BusListWidgetState();
|
||||
|
||||
}
|
||||
|
||||
class _BusListWidgetState extends State<BusListWidget> {
|
||||
// const BusListWidget({super.key});
|
||||
|
||||
|
||||
|
||||
late Future<List<Plan>> futurePlans;
|
||||
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
super.initState();
|
||||
futurePlans = fetchPlans();
|
||||
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
|
||||
// Fetch API Data
|
||||
Future<List<Plan>> fetchPlans() async {
|
||||
final String apiUrldata = '$apiUrl/api/plans';
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
List<dynamic> plansJson = data['data'];
|
||||
return plansJson.map((json) => Plan.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -91,10 +42,12 @@ Future<List<Plan>> fetchPlans() async {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Date')),
|
||||
DataColumn(label: Text('Time')),
|
||||
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
@ -113,17 +66,18 @@ Future<List<Plan>> fetchPlans() async {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
return busList.asMap().entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
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")), // Index column
|
||||
DataCell(Text(item["from"]!)),
|
||||
DataCell(Text(item["to"]!)),
|
||||
DataCell(Text(item["date"]!)),
|
||||
DataCell(Text(item["time"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -135,13 +89,13 @@ Future<List<Plan>> fetchPlans() async {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Bus");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteBus(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,67 +1,14 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../data/models/plan.dart';
|
||||
|
||||
|
||||
class ForexListWidget extends StatefulWidget{
|
||||
const ForexListWidget({super.key});
|
||||
class ForexListWidget extends StatelessWidget{
|
||||
final List<Map<String,dynamic>> forexList;
|
||||
final Function(bool, Map<String,dynamic>, String)onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteForex;
|
||||
|
||||
@override
|
||||
_ForexListWidgetState createState() => _ForexListWidgetState();
|
||||
|
||||
}
|
||||
|
||||
class _ForexListWidgetState extends State<ForexListWidget> {
|
||||
// const BusListWidget({super.key});
|
||||
|
||||
|
||||
late Future<List<Plan>> futurePlans;
|
||||
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
super.initState();
|
||||
futurePlans = fetchPlans();
|
||||
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
|
||||
// Fetch API Data
|
||||
Future<List<Plan>> fetchPlans() async {
|
||||
final String apiUrldata = '$apiUrl/api/plans';
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
List<dynamic> plansJson = data['data'];
|
||||
return plansJson.map((json) => Plan.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
const ForexListWidget({super.key, required this.forexList, required this.onOpen, required this.onDeleteForex});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -90,11 +37,11 @@ class _ForexListWidgetState extends State<ForexListWidget> {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Forex Start Date')),
|
||||
DataColumn(label: Text('Forex End Date')),
|
||||
DataColumn(label: Text('Country')),
|
||||
DataColumn(label: Text('Perdiem Amount')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -112,17 +59,18 @@ class _ForexListWidgetState extends State<ForexListWidget> {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
return forexList.asMap().entries.map((entry) {
|
||||
|
||||
Map<String,dynamic> item = entry.value;
|
||||
|
||||
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")), // Index column
|
||||
DataCell(Text(item["start_date"] ?? "N/A")),
|
||||
DataCell(Text(item["end_date"] ?? "N/A")),
|
||||
DataCell(Text(item["country_code"] ?? "N/A")),
|
||||
DataCell(Text(item["perdiem_amount"] ?? "N/A")),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -134,13 +82,13 @@ class _ForexListWidgetState extends State<ForexListWidget> {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Forex");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteForex(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import 'dart:js_interop';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class InsuranceListWidget extends StatelessWidget {
|
||||
const InsuranceListWidget({super.key});
|
||||
final List<Map<String,dynamic>> insuranceList;
|
||||
final Function(bool, Map<String,dynamic>, String)onOpen;
|
||||
final Function(Map<String,dynamic>)onDeleteInsurance;
|
||||
const InsuranceListWidget({super.key, required this.insuranceList, required this.onOpen,required this.onDeleteInsurance});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +32,10 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Insurance Type')),
|
||||
DataColumn(label: Text('Start Date')),
|
||||
DataColumn(label: Text('End Date')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -45,17 +50,16 @@ class InsuranceListWidget 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) {
|
||||
|
||||
return insuranceList.asMap().entries.map((entry) {
|
||||
|
||||
Map<String,dynamic> item = entry.value;
|
||||
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")), // Index column
|
||||
DataCell(Text(item["type_of_insurance"]!)),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Text(item["end_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -67,13 +71,13 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Insurance");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteInsurance(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MiscellaneousListWidget extends StatelessWidget {
|
||||
const MiscellaneousListWidget({super.key});
|
||||
|
||||
final List<Map<String,dynamic>> miscellaneousList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
|
||||
const MiscellaneousListWidget({super.key, required this.miscellaneousList, required this.onOpen,
|
||||
required this.onDeleteMiscellaneous});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +33,10 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Special Request')),
|
||||
DataColumn(label: Text('Comments')),
|
||||
DataColumn(label: Text('Created On')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -45,17 +51,17 @@ class MiscellaneousListWidget 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"},
|
||||
];
|
||||
print("miscellaneousList - $miscellaneousList");
|
||||
|
||||
return data.map((bus) {
|
||||
return miscellaneousList.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(bus["tripType"]!)),
|
||||
DataCell(Text(bus["class"]!)),
|
||||
DataCell(Text(bus["from"]!)),
|
||||
DataCell(Text(bus["to"]!)),
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["special_request"] ?? "N/A")),
|
||||
DataCell(Text(item["comments"] ?? "N/A")),
|
||||
DataCell(Text(item["created_on"] ?? "N/A")),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -67,12 +73,14 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Miscellaneous");
|
||||
// Edit action
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteMiscellaneous(item);
|
||||
// Delete action
|
||||
},
|
||||
),
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TaxiListWidget extends StatelessWidget {
|
||||
const TaxiListWidget({super.key});
|
||||
final List<Map<String,dynamic>> taxiList;
|
||||
final Function(bool, Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteTaxi;
|
||||
|
||||
const TaxiListWidget({super.key, required this.taxiList, required this.onOpen, required this.onDeleteTaxi});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -26,10 +30,11 @@ class TaxiListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Destination')),
|
||||
DataColumn(label: Text('Location Of Pickup')),
|
||||
DataColumn(label: Text('Date')),
|
||||
DataColumn(label: Text('Taxi Required For')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -43,17 +48,18 @@ class TaxiListWidget 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) {
|
||||
|
||||
return taxiList.asMap().entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
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")), // Index column
|
||||
DataCell(Text(item["destination_city"]!)),
|
||||
DataCell(Text(item["location_of_pickup"]!)),
|
||||
DataCell(Text(item["date"]!)),
|
||||
DataCell(Text(item["car_required_for"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -65,13 +71,13 @@ class TaxiListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Taxi");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteTaxi(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TrainListWidget extends StatelessWidget {
|
||||
const TrainListWidget({super.key});
|
||||
final List<Map<String,dynamic>> trainList;
|
||||
final Function(bool, Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteTrain;
|
||||
const TrainListWidget({super.key, required this.trainList, required this.onOpen, required this.onDeleteTrain});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,7 +30,8 @@ class TrainListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Train Number')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
@ -45,17 +49,17 @@ class TrainListWidget 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) {
|
||||
|
||||
return trainList.asMap().entries.map((entry) {
|
||||
final Map<String,dynamic> item = entry.value;
|
||||
|
||||
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["train_no"]!)),
|
||||
DataCell(Text(item["class"]!)),
|
||||
DataCell(Text(item["from"]!)),
|
||||
DataCell(Text(item["to"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -67,13 +71,13 @@ class TrainListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Train");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteTrain(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class VisaListWidget extends StatelessWidget {
|
||||
const VisaListWidget({super.key});
|
||||
final List<Map<String, dynamic>> visaList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
|
||||
const VisaListWidget({super.key, required this.visaList, required this.onOpen,required this.onDeleteMiscellaneous});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +31,10 @@ class VisaListWidget extends StatelessWidget {
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('Class')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Type of Visa')),
|
||||
DataColumn(label: Text('Country')),
|
||||
DataColumn(label: Text('Start Date')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
@ -45,17 +49,17 @@ class VisaListWidget 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) {
|
||||
return visaList.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(bus["tripType"]!)),
|
||||
DataCell(Text(bus["class"]!)),
|
||||
DataCell(Text(bus["from"]!)),
|
||||
DataCell(Text(bus["to"]!)),
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["type_of_visa"]!)),
|
||||
DataCell(Text(item["country"]!)),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
@ -67,13 +71,13 @@ class VisaListWidget extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
// Edit action
|
||||
onOpen(true, item, "Visa");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
// Delete action
|
||||
onDeleteMiscellaneous(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -11,6 +11,7 @@ import '../../config/apiUrl.dart';
|
||||
import '../../data/models/plan.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../widgets/custom_radio_button.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../dialog/user_selection_dialog.dart';
|
||||
|
||||
@ -90,36 +91,190 @@ class CreateNewPlan extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
final FocusNode _textFieldFocusNode = FocusNode(); // Declare FocusNode
|
||||
bool _isTextFieldFocused = false;
|
||||
|
||||
final TextEditingController _tripTitleController = TextEditingController();
|
||||
final TextEditingController _descriptionController = TextEditingController();
|
||||
|
||||
final FocusNode _tripTitleFocusNode = FocusNode();
|
||||
final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode
|
||||
|
||||
bool _isTripTitleFocused = false;
|
||||
bool _isdescriptionFocused = false;
|
||||
late String _selectedOption = "Option 1";
|
||||
late String? _selectedIsBillable = "Billable";
|
||||
Map<String, dynamic>? storedData;
|
||||
// late String? _selectedIsBillable = "Billable";
|
||||
|
||||
|
||||
String? userDetails;
|
||||
String? userName;
|
||||
String? selfId;
|
||||
String? otherUserName;
|
||||
String? selectedplanUserId;
|
||||
bool? selectedIstravelUser;
|
||||
|
||||
Map<String, dynamic>? apiData; // Store API response here
|
||||
List<dynamic>? apiCountryData;
|
||||
List<dynamic>? apiCostData; // Store API response here
|
||||
bool isLoading = true; // Track loading state
|
||||
|
||||
String? planUsrId;
|
||||
String? planTravlrId;
|
||||
String? _selectedTripType;
|
||||
String? selectedCostCenterId;
|
||||
String? _selectedIsBillable ;
|
||||
String? selectedFuncDept;
|
||||
String? selectedPurpose;
|
||||
|
||||
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>> trainList = [];
|
||||
List<Map<String, dynamic>> busList = [];
|
||||
List<Map<String, dynamic>> taxiList = [];
|
||||
|
||||
//Getter Method
|
||||
Map<String, dynamic> get planData => {
|
||||
"user_id": planUsrId,
|
||||
"traveller_id": planTravlrId,
|
||||
"trip_title": _tripTitleController.text,
|
||||
"trip_type": _selectedTripType,
|
||||
"cost_center_id": selectedCostCenterId,
|
||||
"is_billable": _selectedIsBillable,
|
||||
"purpose_of_travel": selectedPurpose,
|
||||
"description": _descriptionController.text,
|
||||
"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,
|
||||
"is_active": "1",
|
||||
"flight":[],
|
||||
"accommodation": [],
|
||||
"bus": [],
|
||||
"insurance": [],
|
||||
"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");
|
||||
}
|
||||
|
||||
|
||||
void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) {
|
||||
setState(() {
|
||||
switch (type) {
|
||||
case "Miscellaneous":
|
||||
miscellaneousList = newList;
|
||||
break;
|
||||
case "Train":
|
||||
trainList = newList;
|
||||
break;
|
||||
case "Bus":
|
||||
busList = newList;
|
||||
break;
|
||||
case "Taxi":
|
||||
taxiList = newList;
|
||||
break;
|
||||
case "Forex":
|
||||
taxiList = newList;
|
||||
break;
|
||||
case "Accommodation":
|
||||
taxiList = newList;
|
||||
break;
|
||||
default:
|
||||
print("Unknown itinerary type: $type");
|
||||
}
|
||||
});
|
||||
print("Updated $type List: $newList");
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
void initState(){
|
||||
super.initState();
|
||||
fetchPlans();
|
||||
fetchUserDetails();
|
||||
|
||||
_textFieldFocusNode.addListener(() {
|
||||
fetchPlans();
|
||||
fetchCostCenter();
|
||||
fetchCountryList();
|
||||
|
||||
// _tripTitleController.addListener(() {
|
||||
// print("Current Value: ${_tripTitleController.text}");
|
||||
// });
|
||||
|
||||
_tripTitleFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isTextFieldFocused = _textFieldFocusNode.hasFocus;
|
||||
_isTripTitleFocused = _tripTitleFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_descriptionFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isdescriptionFocused = _descriptionFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textFieldFocusNode.dispose();
|
||||
_tripTitleFocusNode.dispose();
|
||||
_descriptionFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void getSelectedPlanFor(){
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
if(selectedplanUserId != null){
|
||||
|
||||
if(selectedIstravelUser!){
|
||||
planUsrId = "";
|
||||
planTravlrId = selectedplanUserId;
|
||||
}else {
|
||||
planUsrId = selectedplanUserId;
|
||||
planTravlrId = "";
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
planUsrId = selfId;
|
||||
planTravlrId = "";
|
||||
}
|
||||
});
|
||||
|
||||
print("USER ID - $planUsrId , TRAVELLER ID - $planTravlrId");
|
||||
}
|
||||
|
||||
|
||||
void fetchUserDetails() async {
|
||||
final details = await getUserDetails();
|
||||
|
||||
print("details- $details");
|
||||
|
||||
|
||||
if (details != null) {
|
||||
setState(() {
|
||||
userDetails = details.toString(); // Store the full Map
|
||||
userName = details['name']; // Extract the name
|
||||
selfId = details['user_id'];
|
||||
});
|
||||
}
|
||||
|
||||
print("userDetails - $selfId");
|
||||
getSelectedPlanFor();
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@ -132,7 +287,20 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
return prefs.getString('userId');
|
||||
}
|
||||
|
||||
Future <Map<String,String>?> getUserDetails() async{
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final userData = prefs.getString('user_data');
|
||||
|
||||
if(userData!= null){
|
||||
final decodedData = jsonDecode(userData);
|
||||
|
||||
return {
|
||||
'user_id': decodedData['user_id'].toString(),
|
||||
'name': "${decodedData['first_name']} ${decodedData['last_name']}",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Future<void> fetchPlans() async {
|
||||
@ -167,7 +335,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
Map<String, dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
setState(() {
|
||||
apiData = data['data']; // Store API response in state
|
||||
apiData = plansJson; // Store API response in state
|
||||
isLoading = false;
|
||||
});
|
||||
|
||||
@ -180,22 +348,176 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchCostCenter() async {
|
||||
final String apiUrldata = '$apiUrl/api/getCostCenterMaster';
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
Future<void> getStoredData() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
String? jsonString = prefs.getString('api_response');
|
||||
// final userId = await getUserId();
|
||||
|
||||
if (jsonString != null) {
|
||||
storedData = json.decode(jsonString);
|
||||
print('Stored Data: $storedData');
|
||||
} else {
|
||||
storedData = null;
|
||||
// print("SUSRTRT- $userId");
|
||||
//
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
try {
|
||||
final data = json.decode(response.body);
|
||||
print(data);
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is!List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
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'];
|
||||
}
|
||||
});
|
||||
print('plansJSON');
|
||||
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Future<void> fetchCountryList() async {
|
||||
final String apiUrldata = '$apiUrl/api/getcountryMaster';
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
// final userId = await getUserId();
|
||||
|
||||
// print("SUSRTRT- $userId");
|
||||
//
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
try {
|
||||
final data = json.decode(response.body);
|
||||
print("Country - $data");
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is!List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
|
||||
List <dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
|
||||
if (data['data'] is List) {
|
||||
List<dynamic> plansJson = data['data'];
|
||||
print("plansJson.length - ${plansJson.length}");
|
||||
} else {
|
||||
print("The 'data' key does not contain a list.");
|
||||
}
|
||||
|
||||
setState(() {
|
||||
apiCountryData = plansJson; // Store API response in state
|
||||
|
||||
});
|
||||
print('plansJSONContry - $plansJson');
|
||||
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Submit
|
||||
|
||||
bool validateForm(){
|
||||
validationErrors.clear(); // Clear previous errors
|
||||
|
||||
// Ensure either "user_id" or "traveller_id" is provided
|
||||
if ((planUsrId == null || planUsrId!.isEmpty) && (planTravlrId == null || planTravlrId!.isEmpty)) {
|
||||
validationErrors["user_id"] = "Either User ID or Traveller ID is required";
|
||||
validationErrors["traveller_id"] = "Either User ID or Traveller ID is required";
|
||||
}
|
||||
|
||||
final requiredFields = {
|
||||
"trip_type": _selectedTripType,
|
||||
"cost_center_id": selectedCostCenterId,
|
||||
"functional_department": selectedFuncDept,
|
||||
"purpose_of_travel": selectedPurpose,
|
||||
};
|
||||
|
||||
for (var entry in requiredFields.entries) {
|
||||
if (entry.value == null || entry.value!.isEmpty) {
|
||||
validationErrors[entry.key] = "${entry.key.replaceAll('_', ' ').toUpperCase()} is required";
|
||||
}
|
||||
}
|
||||
|
||||
return validationErrors.isEmpty; // Returns true if no errors
|
||||
}
|
||||
|
||||
void handleSubmit() {
|
||||
setState(() {
|
||||
if(validateForm()){
|
||||
print("Form submitted successfully: $planData");
|
||||
postPlanData(planData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isMobile = sizingInfo.isMobile;
|
||||
@ -210,14 +532,36 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Plan This Trip For", // Your label
|
||||
style: TextStyle(
|
||||
// Text(
|
||||
// "Plan This Trip For : ${userName} ", // Your label
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF575A74)),
|
||||
// ),
|
||||
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
text: "Plan This Trip For: ", // Static text
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
color: Color(0xFF575A74), // Default color
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: otherUserName ?? userName ?? " ", // Dynamic username
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blueAccent, // Change this to any color
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
SizedBox(height: 7),
|
||||
isMobile
|
||||
? SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -254,12 +598,13 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isTextFieldFocused,
|
||||
isFocused: _isTripTitleFocused,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: _textFieldFocusNode,
|
||||
focusNode: _tripTitleFocusNode,
|
||||
controller: _tripTitleController,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Trip Title",
|
||||
@ -285,7 +630,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Trip Type", // Your label
|
||||
"Trip Type *", // Your label
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -302,6 +647,14 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
: Row(
|
||||
children: _buildTripType(isMobile),
|
||||
),
|
||||
if (validationErrors["trip_type"] != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
validationErrors["trip_type"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 10),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -336,6 +689,8 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
_buildNonDescriptionColumn(),
|
||||
SizedBox(height: 15),
|
||||
_buildDescriptionColumn(isDesktop),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
@ -349,10 +704,19 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: DynamicItinerary(apiData: apiData)), // Wrap with Expanded if needed
|
||||
Expanded(child: DynamicItinerary(apiData: apiData, apiCountryData: apiCountryData,
|
||||
onItineraryUpdate: handleItineraryUpdate,
|
||||
)), // Wrap with Expanded if needed
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 15),
|
||||
isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _buildSubmit(isDesktop),)
|
||||
:Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildSubmit(isDesktop),)
|
||||
|
||||
],
|
||||
);
|
||||
@ -362,6 +726,9 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
/// Extracted helper function
|
||||
List<Widget> _buildCostIsBillable() {
|
||||
|
||||
List<dynamic> purposeList = apiData?['plan_is_billable'] ?? [];
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -380,72 +747,114 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
child: SizedBox(
|
||||
height: 45, // Set appropriate height
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: "Option 1",
|
||||
value: selectedCostCenterId,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
items: [
|
||||
DropdownMenuItem(value: "Option 1", child: Text("Option 1")),
|
||||
DropdownMenuItem(value: "Option 2", child: Text("Option 2")),
|
||||
],
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
selectedCostCenterId = newValue;
|
||||
});
|
||||
},
|
||||
items:apiCostData?.map<DropdownMenuItem<String>>((item){
|
||||
return DropdownMenuItem(
|
||||
value: item['department_id'], // ID as value
|
||||
child: Text(item['name'] ?? "Unknown"),
|
||||
);
|
||||
}).toList(),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 25),
|
||||
|
||||
SizedBox(width: 25,height: 5,),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Is Billable ", // Your label
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Radio<String>(
|
||||
value: "Billable",
|
||||
groupValue: _selectedIsBillable,
|
||||
activeColor: Colors.blueAccent,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedIsBillable = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
Text("Billable"),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 20), // Spacing
|
||||
Row(
|
||||
children: [
|
||||
Radio<String>(
|
||||
value: "Non Billable",
|
||||
groupValue: _selectedIsBillable,
|
||||
activeColor: Colors.blueAccent,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedIsBillable = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
Text("Non Billable"),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Is Billable ", // Your label
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
children: purposeList.map<Widget>((item) {
|
||||
return Row(
|
||||
children: [
|
||||
Radio<String>(
|
||||
value: item['dropdown_key'], // Use dropdown_value as value
|
||||
groupValue: _selectedIsBillable,
|
||||
activeColor: Colors.blueAccent,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedIsBillable = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
Text(item['dropdown_value'] ?? ''), // Display dropdown_value
|
||||
SizedBox(width: 20), // Spacing
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
|
||||
])
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Is Billable ", // Your label
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF575A74)),
|
||||
// ),
|
||||
// SizedBox(height: 5),
|
||||
// Row(
|
||||
// children: [
|
||||
// Row(
|
||||
// children: [
|
||||
// Radio<String>(
|
||||
// value: "Billable",
|
||||
// groupValue: _selectedIsBillable,
|
||||
// activeColor: Colors.blueAccent,
|
||||
// onChanged: (value) {
|
||||
// setState(() {
|
||||
// _selectedIsBillable = value;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// Text("Billable"),
|
||||
// ],
|
||||
// ),
|
||||
// SizedBox(width: 20), // Spacing
|
||||
// Row(
|
||||
// children: [
|
||||
// Radio<String>(
|
||||
// value: "Non Billable",
|
||||
// groupValue: _selectedIsBillable,
|
||||
// activeColor: Colors.blueAccent,
|
||||
// onChanged: (value) {
|
||||
// setState(() {
|
||||
// _selectedIsBillable = value;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// Text("Non Billable"),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
];
|
||||
}
|
||||
|
||||
@ -490,11 +899,12 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
List<Widget> _buildTripType(bool isMobile) {
|
||||
return [
|
||||
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
width: 120,
|
||||
isFocused: _selectedOption == "Option 1",
|
||||
isFocused: _selectedTripType == "1",
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
@ -506,11 +916,11 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: true,
|
||||
title: Text("Domestic"),
|
||||
value: "Option 1",
|
||||
groupValue: _selectedOption,
|
||||
value: "1",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedOption = value!;
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -522,22 +932,24 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
color: Color(0xFFF4F4FB),
|
||||
width: 150,
|
||||
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
isFocused: _selectedOption == "Option 2",
|
||||
isFocused: _selectedTripType == "2",
|
||||
isDesktop: widget.isDesktop,
|
||||
child: RadioListTile<String>(
|
||||
activeColor: Colors.blueAccent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
title: Text("International"),
|
||||
value: "Option 2",
|
||||
groupValue: _selectedOption,
|
||||
value: "2",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedOption = value!;
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -546,11 +958,11 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
// 'plan_purpose_of_travel' Starts ------------------------------------------------------
|
||||
|
||||
List<dynamic> purposeList = apiData?['plan_purpose_of_travel'] ?? [];
|
||||
List<dynamic> purposeList = apiData?['plan_purpose_of_travel'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -564,7 +976,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
// 'plan_functional_department' Starts ---------------------------------------------
|
||||
|
||||
@ -572,7 +984,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownFuncDeptItems = funcDeptList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
|
||||
@ -586,7 +998,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedFuncDept = dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value : null;
|
||||
selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value : null;
|
||||
// 'plan_functional_department' End
|
||||
|
||||
return Column(
|
||||
@ -661,7 +1073,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
onChanged: funcDeptList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedFuncDept = newValue;
|
||||
@ -701,12 +1113,13 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false, // Dropdown doesn't use focus
|
||||
isFocused: _isdescriptionFocused,
|
||||
width: isDesktop? MediaQuery.of(context).size.width * 0.5 :
|
||||
MediaQuery.of(context).size.width * 0.85 ,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: TextField(
|
||||
focusNode: _textFieldFocusNode,
|
||||
focusNode: _descriptionFocusNode,
|
||||
controller: _descriptionController,
|
||||
maxLines: 6,
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
@ -727,6 +1140,19 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSubmit(isDesktop){
|
||||
return[
|
||||
ElevatedButton(
|
||||
onPressed: (){},
|
||||
child: Text("Cancel")),
|
||||
SizedBox(width: 20,),
|
||||
ElevatedButton(
|
||||
onPressed: (){
|
||||
handleSubmit();
|
||||
},
|
||||
child: Text("Submit"))
|
||||
];
|
||||
}
|
||||
|
||||
void _showInputDialog(String title){
|
||||
showDialog(
|
||||
@ -734,8 +1160,14 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
builder: (BuildContext context){
|
||||
return UserSelectionDialog(
|
||||
title:title,
|
||||
onSubmit: (input){
|
||||
print("USer entered : $input");
|
||||
onSubmit: (input,userId,isTraveller){
|
||||
setState(() {
|
||||
otherUserName = input;
|
||||
selectedplanUserId = userId;
|
||||
selectedIstravelUser = isTraveller;
|
||||
});
|
||||
print("USer entered : $otherUserName $userId $isTraveller");
|
||||
getSelectedPlanFor();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,7 +23,10 @@ import '../itnerary_list/visa_list.dart';
|
||||
|
||||
class DynamicItinerary extends StatefulWidget {
|
||||
final Map<String, dynamic>? apiData;
|
||||
const DynamicItinerary({super.key, required this.apiData});
|
||||
final List<dynamic>? apiCountryData;
|
||||
final Function(String, List<Map<String, dynamic>>) onItineraryUpdate; // Updated Signature
|
||||
const DynamicItinerary({super.key, required this.apiData, required this.onItineraryUpdate, required this.apiCountryData});
|
||||
|
||||
|
||||
@override
|
||||
_DynamicItineraryState createState() => _DynamicItineraryState();
|
||||
@ -35,6 +38,112 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
String selectedListOption = "";
|
||||
|
||||
bool isSelected = false;
|
||||
Map<String, dynamic>? selectedItem;
|
||||
int? selectedIndex;
|
||||
|
||||
// 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"
|
||||
}],
|
||||
"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"
|
||||
}],
|
||||
"Forex": [],
|
||||
};
|
||||
|
||||
|
||||
// Store form values for each tab
|
||||
final Map<String, Map<String, String>> formData = {
|
||||
@ -51,8 +160,24 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
void handleClose(bool value) {
|
||||
setState(() {
|
||||
selectedOption = "";
|
||||
isSelected = value;
|
||||
selectedIndex = null;
|
||||
selectedItem = null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleEdit(bool value,selectedItem, title) {
|
||||
setState(() {
|
||||
selectedOption = title;
|
||||
isSelected = value;
|
||||
this.selectedItem = selectedItem;
|
||||
|
||||
});
|
||||
|
||||
print(selectedItem);
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
@ -77,30 +202,156 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
}
|
||||
|
||||
|
||||
void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
||||
setState(() {
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
itineraryData[type] = []; // Initialize if null
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> itemList = itineraryData[type]!;
|
||||
|
||||
print("newData - $newData");
|
||||
|
||||
// int? existingId = newData["id"];
|
||||
// String? existingId = newData["id"];
|
||||
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = newData[idKey];
|
||||
|
||||
|
||||
print("Looking for ID using key: $idKey, Found ID: $existingId");
|
||||
|
||||
int? existingIndex = newData["indx"];
|
||||
print("existingIndex - $existingIndex, existingId - $existingId");
|
||||
|
||||
// CASE 2: Update using id if available
|
||||
if (existingId != null && existingId != 0) {
|
||||
// int itemId = itemList.indexWhere((item) => item["id"] == existingId);
|
||||
int itemId = itemList.indexWhere((item) => item[idKey]?.toString() == existingId.toString());
|
||||
|
||||
if (itemId != -1) {
|
||||
print(" Updating existing item with id: $existingId");
|
||||
itemList[itemId] = newData;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE 1: Update if indx exists in list
|
||||
if (existingIndex != null && existingIndex != 0) {
|
||||
int itemIndex = itemList.indexWhere((item) => item["indx"] == existingIndex);
|
||||
if (itemIndex != -1) {
|
||||
print("Updating existing item with indx: $existingIndex");
|
||||
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
|
||||
itemList.add(newData);
|
||||
|
||||
print(" Updated $type List: ${itineraryData[type]}");
|
||||
});
|
||||
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
void handleItinerarydelete(String type, Map<String, dynamic> data){
|
||||
setState(() {
|
||||
if(!itineraryData.containsKey(type)){
|
||||
return;
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> itemList = itineraryData[type]!;
|
||||
// int? 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());
|
||||
print("Deleted by ID: $existingId");
|
||||
}
|
||||
//Delete by Index
|
||||
else if(existingIndex != null && existingId !=0){
|
||||
itemList.removeWhere((item) => item["indx"] == existingIndex);
|
||||
print("Deleted by ID: $existingIndex");
|
||||
}
|
||||
else{
|
||||
print("No Valid Deletion");
|
||||
}
|
||||
|
||||
itineraryData[type]= List.from(itemList);
|
||||
});
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
|
||||
// void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
||||
// setState(() {
|
||||
// if (!itineraryData.containsKey(type)) {
|
||||
// itineraryData[type] = []; // Initialize if null
|
||||
// }
|
||||
//
|
||||
// // Assign an index based on the current list length
|
||||
// int newIndex = itineraryData[type]!.length + 1;
|
||||
// newData["indx"] = newIndex; // Add an ID field
|
||||
//
|
||||
// itineraryData[type]!.add(newData); // Append new object
|
||||
// });
|
||||
//
|
||||
// widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
// print("Updated $type List: ${itineraryData[type]}");
|
||||
// }
|
||||
|
||||
|
||||
|
||||
switch (selectedListOption) {
|
||||
case "Train":
|
||||
selectedListWidget = TrainListWidget();
|
||||
selectedListWidget = TrainListWidget( trainList : itineraryData["Train"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteTrain: (data)=> handleItinerarydelete("Train", data),
|
||||
);
|
||||
break;
|
||||
case "Taxi":
|
||||
selectedListWidget = TaxiListWidget();
|
||||
selectedListWidget = TaxiListWidget( taxiList : itineraryData["Taxi"]! ,
|
||||
onOpen: handleEdit,
|
||||
onDeleteTaxi: (data)=> handleItinerarydelete("Taxi", data),);
|
||||
break;
|
||||
case "Bus":
|
||||
selectedListWidget = BusListWidget();
|
||||
selectedListWidget = BusListWidget(busList : itineraryData["Bus"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteBus: (data) => handleItinerarydelete("Bus", data),);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedListWidget = InsuranceListWidget();
|
||||
selectedListWidget = InsuranceListWidget(insuranceList : itineraryData["Insurance"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteInsurance:(data) => handleItinerarydelete("Insurance", data),);
|
||||
break;
|
||||
case "Visa":
|
||||
selectedListWidget = VisaListWidget();
|
||||
selectedListWidget = VisaListWidget(visaList : itineraryData["Visa"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Visa", data),);
|
||||
break;
|
||||
case "Forex":
|
||||
selectedListWidget = ForexListWidget();
|
||||
selectedListWidget = ForexListWidget(forexList: itineraryData["Forex"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteForex: (data) => handleItinerarydelete("Forex", data),);
|
||||
break;
|
||||
case "Accommodation":
|
||||
selectedListWidget = AccomodationListWidget();
|
||||
selectedListWidget = AccomodationListWidget(accommodationList: itineraryData["Accommodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteAccommodation:(data) => handleItinerarydelete("Accommodation", data));
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedListWidget = MiscellaneousListWidget();
|
||||
selectedListWidget = MiscellaneousListWidget(miscellaneousList: itineraryData["Miscellaneous"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Miscellaneous", data),
|
||||
);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
@ -110,29 +361,46 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
switch (selectedOption) {
|
||||
case "Train":
|
||||
selectedWidget = TrainScreen(onClose: handleClose,formData: formData["Train"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
// selectedWidget = TrainScreen(formData: formData["Train"]!);
|
||||
selectedWidget = TrainScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
onSavetrain :(data) => handleItineraryUpdate("Train", data),
|
||||
selectedItem: selectedItem);
|
||||
|
||||
break;
|
||||
case "Taxi":
|
||||
selectedWidget = TaxiScreen(onClose: handleClose, formData: formData["Taxi"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
selectedWidget = TaxiScreen(onClose: handleClose,apiData: widget.apiData,
|
||||
onSavetaxi: (data)=> handleItineraryUpdate("Taxi", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Bus":
|
||||
selectedWidget = BusScreen(onClose: handleClose, formData: formData["Bus"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
selectedWidget = BusScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
onSaveBus: (data)=> handleItineraryUpdate("Bus", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedWidget = InsuranceScreen(onClose: handleClose, formData: formData["Insurance"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
selectedWidget = InsuranceScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
onSaveInsurance:(data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Visa":
|
||||
selectedWidget = VisaScreen(onClose: handleClose, formData: formData["Insurance"]!, updateFormData: updateFormData, apiData: widget.apiData,);
|
||||
break;
|
||||
|
||||
case "Visa":
|
||||
selectedWidget = VisaScreen(onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||
selectedItem: selectedItem,);
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedWidget =MiscellaneousScreen(formData: formData["Miscellaneous"]!, updateFormData: updateFormData, onClose: handleClose, apiData: widget.apiData);
|
||||
break;
|
||||
selectedWidget = MiscellaneousScreen(onClose: handleClose, apiData: widget.apiData,
|
||||
onSaveMiscellaneous: (data) => handleItineraryUpdate("Miscellaneous", data),
|
||||
selectedItem: selectedItem, selectedIndex: selectedIndex, );
|
||||
break;
|
||||
case "Accommodation":
|
||||
selectedWidget = AccomodationScreen( onClose: handleClose,formData: formData["Accommodation"]!, updateFormData: updateFormData);
|
||||
selectedWidget = AccomodationScreen( onClose: handleClose,
|
||||
onSaveAccomadation: (data)=>handleItineraryUpdate("Accommodation", data),
|
||||
selectedItem: selectedItem, );
|
||||
break;
|
||||
case "Forex":
|
||||
selectedWidget = ForexScreen( onClose: handleClose,formData: formData["Forex"]!, updateFormData: updateFormData, apiData: widget.apiData);
|
||||
selectedWidget = ForexScreen( onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
onSaveForex: (data)=>handleItineraryUpdate("Forex", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
@ -194,7 +462,6 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
],
|
||||
|
||||
);
|
||||
@ -248,6 +515,7 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
setState(() {
|
||||
selectedOption = title;
|
||||
isSelected = true;
|
||||
selectedItem = null;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
|
||||
@ -138,74 +138,78 @@ class _ListPlansState extends State<ListPlans>{
|
||||
builder: (context, sizingInfo) {
|
||||
bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
||||
|
||||
return 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 ,
|
||||
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)),
|
||||
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 ,
|
||||
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,))),
|
||||
//
|
||||
DataCell(Text(plan.isBillable)),
|
||||
DataCell(Text(plan.status)),
|
||||
DataCell(
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
print("View button clicked for ${plan.tripTitle}");
|
||||
},
|
||||
child: const Text('View',
|
||||
style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
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: () {
|
||||
print("View button clicked for ${plan.tripTitle}");
|
||||
},
|
||||
child: const Text('View',
|
||||
style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
);
|
||||
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
31
lib/data/models/Searchtraveller.dart
Normal file
31
lib/data/models/Searchtraveller.dart
Normal file
@ -0,0 +1,31 @@
|
||||
class SearchTraveler {
|
||||
final String travellerId;
|
||||
final String firstName;
|
||||
final String lastName;
|
||||
final String email;
|
||||
final String mobileNo;
|
||||
|
||||
|
||||
SearchTraveler({
|
||||
required this.travellerId,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.email,
|
||||
required this.mobileNo,
|
||||
|
||||
});
|
||||
|
||||
|
||||
factory SearchTraveler.fromJson(Map<String, dynamic> json){
|
||||
return SearchTraveler(
|
||||
|
||||
travellerId: json['traveller_id'].toString(),
|
||||
firstName: json['first_name'].toString()?? '',
|
||||
lastName: json['last_name'].toString()?? '',
|
||||
email: json['email'].toString()?? '',
|
||||
mobileNo: json['mobile_no'].toString()?? '',
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
class SearchUser{
|
||||
final String userId;
|
||||
final String travellerId;
|
||||
final String firstName;
|
||||
final String lastName;
|
||||
final String email;
|
||||
@ -9,7 +8,6 @@ class SearchUser{
|
||||
|
||||
SearchUser({
|
||||
required this.userId,
|
||||
required this.travellerId,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.email,
|
||||
@ -21,7 +19,6 @@ class SearchUser{
|
||||
factory SearchUser.fromJson(Map<String, dynamic> json){
|
||||
return SearchUser(
|
||||
userId: json['user_id'].toString(),
|
||||
travellerId: json['traveller_id'].toString(),
|
||||
firstName: json['first_name'].toString()?? '',
|
||||
lastName: json['last_name'].toString()?? '',
|
||||
email: json['email'].toString()?? '',
|
||||
|
||||
58
lib/widgets/custom_text_forex.dart
Normal file
58
lib/widgets/custom_text_forex.dart
Normal file
@ -0,0 +1,58 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomTextFieldForexWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
final bool isFocused;
|
||||
final bool isDesktop;
|
||||
final double? width;
|
||||
final Color? color;
|
||||
final VoidCallback? onFocusChange; // Callback for focus handling
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
const CustomTextFieldForexWrapper({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.isFocused,
|
||||
required this.isDesktop,
|
||||
this.width,
|
||||
this.color = Colors.white,
|
||||
this.onFocusChange,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 12),
|
||||
});
|
||||
|
||||
@override
|
||||
_CustomTextFieldForexWrapperState createState() => _CustomTextFieldForexWrapperState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrapper> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width ?? // Use custom width if provided, else default
|
||||
(widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.25
|
||||
: MediaQuery.of(context).size.width * 0.8),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 2.0 : 0.5,
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -65,6 +65,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dropdown_search:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dropdown_search
|
||||
sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.6"
|
||||
easy_stepper:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -40,6 +40,7 @@ dependencies:
|
||||
shared_preferences: ^2.5.2
|
||||
easy_stepper: ^0.8.5+1
|
||||
intl: ^0.20.2
|
||||
dropdown_search: ^5.0.6
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user