From a82f4a271c270c05019b2fa09d4c2c401e079224 Mon Sep 17 00:00:00 2001 From: Venba Team Date: Wed, 26 Mar 2025 08:46:22 +0530 Subject: [PATCH] Plan Creation --- .../authentication/login/login_widget.dart | 37 +- lib/Screens/dialog/user_selection_dialog.dart | 188 ++- lib/Screens/itnerary/accomodations.dart | 173 ++- lib/Screens/itnerary/bus.dart | 114 +- lib/Screens/itnerary/forex.dart | 1046 +++++++++-------- lib/Screens/itnerary/insurance.dart | 206 +--- lib/Screens/itnerary/miscellaneous.dart | 358 +----- lib/Screens/itnerary/taxi.dart | 288 ++--- lib/Screens/itnerary/train.dart | 177 ++- lib/Screens/itnerary/visa.dart | 357 +++--- .../itnerary_list/accomodation_list.dart | 40 +- lib/Screens/itnerary_list/bus_list.dart | 88 +- lib/Screens/itnerary_list/forex_list.dart | 96 +- lib/Screens/itnerary_list/insurance_list.dart | 36 +- .../itnerary_list/miscellaneous_list.dart | 36 +- lib/Screens/itnerary_list/taxi_list.dart | 38 +- lib/Screens/itnerary_list/train_list.dart | 30 +- lib/Screens/itnerary_list/visa_list.dart | 36 +- lib/Screens/plans/create_plans.dart | 634 ++++++++-- .../plans/dynamic_itinerary_stepper.dart | 312 ++++- lib/Screens/plans/list_plans.dart | 136 +-- lib/data/models/Searchtraveller.dart | 31 + lib/data/models/searchUser.dart | 3 - lib/widgets/custom_text_forex.dart | 58 + pubspec.lock | 8 + pubspec.yaml | 1 + 26 files changed, 2532 insertions(+), 1995 deletions(-) create mode 100644 lib/data/models/Searchtraveller.dart create mode 100644 lib/widgets/custom_text_forex.dart diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index cd94039..836bd7b 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -21,6 +21,35 @@ class _LoginWidgetState extends State { final TextEditingController _passwordController = TextEditingController(); bool _obscureText = true; + Future 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 { 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")), diff --git a/lib/Screens/dialog/user_selection_dialog.dart b/lib/Screens/dialog/user_selection_dialog.dart index 0a0e0be..be3b13d 100644 --- a/lib/Screens/dialog/user_selection_dialog.dart +++ b/lib/Screens/dialog/user_selection_dialog.dart @@ -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{ // List _filteredUsers = []; // List _users = []; List _users = []; + List _traveller = []; List _filteredUsers = []; + List> _filteredList = []; + List _filteredTraveller = []; + String userIdSelected = " "; + bool isTraveller = false; + bool _showTravellerForm = false; final _formKey = GlobalKey(); @@ -93,7 +100,7 @@ class _UserSelectionDialogState extends State{ } } - void _filterUsers(String query) { + void _filterUsers1(String query) { print("Filtering users..."); setState(() { if (query.isEmpty) { @@ -104,7 +111,6 @@ class _UserSelectionDialogState extends State{ "${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{ } + 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 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 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 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 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 responseBody = json.decode(response.body); + + + print("API Response: $responseBody"); // Debugging + + if (responseBody.containsKey('data') && responseBody['data'] is List) { + List 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{ 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{ _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{ ), ) : 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{ 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{ 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 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 { if (response.statusCode == 200 || response.statusCode == 201) { + if (response.statusCode == 200 || response.statusCode == 201) { + final Map 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( diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 81af321..c5453b9 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -6,12 +6,13 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class AccomodationScreen extends StatefulWidget { - final Map 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) onSaveAccomadation; + final Map? selectedItem; + + AccomodationScreen({ + required this.onClose, required this.onSaveAccomadation, required this.selectedItem}); @override _AccomodationScreenState createState() => _AccomodationScreenState(); @@ -28,7 +29,6 @@ class _AccomodationScreenState extends State { final FocusNode _checkOutTimeFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late Map _controllers; late TextEditingController _destinationController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); @@ -46,105 +46,65 @@ class _AccomodationScreenState extends State { bool _checkOutTimeFocus = false; bool _commentsFocus = false; + void _addFocusListener(FocusNode node, Function(bool) onFocusChange) { + node.addListener(() { + setState(() { + onFocusChange(node.hasFocus); + }); + }); + } + + + Map get accomadationData { + + Map 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 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 { 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index 7603910..8c12584 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -6,13 +6,14 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class BusScreen extends StatefulWidget { - final Map formData; - final Map? 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? apiData; + final Function(bool) onClose; + final Function(Map)onSaveBus; + final Map? 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 { 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 { bool _timeFocus = false; bool _commentsFocus = false; + + Map get busData{ + Map 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 { setState(() { _tripTypeFocused = _tripTypeFocusNode.hasFocus; }); + }); _hotelNameFocusNode.addListener(() { setState(() { @@ -91,54 +120,15 @@ class _BusScreenState extends State { _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 { _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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -318,7 +319,6 @@ class _BusScreenState extends State { print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - widget.updateFormData("Flight", "trip_type", newValue ?? ""); } : null, @@ -654,7 +654,7 @@ class _BusScreenState extends State { : 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 0bb374a..6fec1e6 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -1,18 +1,29 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/apiUrl.dart'; import '../../widgets/custom_text_field.dart'; +import '../../widgets/custom_text_forex.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; +import 'package:http/http.dart' as http; class ForexScreen extends StatefulWidget { - final Map formData; - final Map? apiData; - final Function(String tab, String key, String value) updateFormData; - final Function(bool) onClose; - ForexScreen({required this.formData, required this.updateFormData, - required this.onClose, this.apiData}); + final Map? apiData; + final Function(bool) onClose; + final Map? selectedItem; + final List? apiCountryData; + final Function(Map)onSaveForex; + + + ForexScreen({ + required this.onClose, this.apiData, required this.selectedItem, required this.apiCountryData, + required this.onSaveForex }); @override _ForexScreenState createState() => _ForexScreenState(); @@ -24,142 +35,226 @@ class _ForexScreenState extends State { Map selectedValues = {}; bool isChecked = false; // State variable for checkbox - 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 _controllers; + Map focusNodes = {}; + Map focusStates = {}; + Map textControllers = {}; + List countryList = []; + + + List dataHeader = ["_forexStartDate", "_forexEndDate", "_countries", "_duration", + "_currency","_perdiemAmount", "_transport", "_accomodation", "_telephone", "_otherExpenses", + "_cardNumber", "_currency","_card", "_cash","_checkForex", "_deliveryLocation","_comments"]; + + String _formatDate(String? date) { + if (date == null || date.isEmpty) return ""; + try { + DateTime parsedDate = DateTime.parse(date); // Assuming input is YYYY-MM-DD + return DateFormat("dd-MM-yyyy").format(parsedDate); // Convert to DD-MM-YYYY + } catch (e) { + print("Error formatting date: $e"); + return date; // Return as is if parsing fails + } + } + + String? selectedCountry; + String? selectedCurrency; + String? selectedDuration; + String? selectedPerdiemAmount; + String? CalculatedOtherExpenses; + + + Map get forexData { + Map data ={ + "start_date": _formatDate(textControllers["_forexStartDate"]?.text), + "end_date": _formatDate(textControllers["_forexEndDate"]?.text), + "country_code": selectedCountry, + "duration": selectedDuration, + "currency": selectedCurrency, + "perdiem_amount": selectedPerdiemAmount, + "transport": textControllers["_transport"]?.text, + "accommodation": textControllers["_accomodation"]?.text, + "telephone": textControllers["_telephone"]?.text, + "have_card": isChecked ? "1" : "0", + "card_number": textControllers["_cardNumber"]?.text, + "deposit_on_card":textControllers["_card"]?.text, + "deposit_on_cash": textControllers["_cash"]?.text, + "delivery_location": textControllers["_deliveryLocation"]?.text, + "comments": textControllers["_comments"]?.text, + }; + + return data; + } + + Map get getForexData { + return { + "country_code": selectedCountry, + "start_date": _formatDate(textControllers["_forexStartDate"]?.text), + "end_date": _formatDate(textControllers["_forexEndDate"]?.text), + // "currency": selectedCurrency ?? "", + }; + } + + + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + Future postgetForexData(Map forexData) async { + final String apiUrldata = '$apiUrl/api/plans/getForexPerdiem'; + + print("Sending Data: ${jsonEncode(forexData)}"); + + 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(forexData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Plan submitted successfully!"); + print("Response: ${response.body}"); + + final Map responseData = jsonDecode(response.body); + + // Ensure the response contains the expected keys before updating state + if (responseData.containsKey("currency") && + responseData.containsKey("perdiem_amount") && + responseData.containsKey("duration")) { + + setState(() { // Update only if data is valid + selectedCurrency = responseData["currency"] ?? selectedCurrency; + selectedPerdiemAmount = responseData["perdiem_amount"]?.toString() ?? ""; + selectedDuration = responseData["duration"]?.toString() ?? ""; + }); + } else { + print("Warning: Response does not contain expected fields."); + } + + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting plan: $e"); + } + } + + void handleSave(){ + + print( "Handle Save forexData $forexData"); + widget.onSaveForex(forexData); // Send object to parent + widget.onClose(false);// Close screen after saving + + } - late TextEditingController _tripTypeController = TextEditingController(); - late TextEditingController _hotelNameController = TextEditingController(); - late TextEditingController _fromController = TextEditingController(); - late TextEditingController _toController = TextEditingController(); - late TextEditingController _dateController = TextEditingController(); - late TextEditingController _endDateController = TextEditingController(); - late TextEditingController _timeController = TextEditingController(); - late TextEditingController _commentsController = TextEditingController(); - bool _tripTypeFocused = false; - bool _isHotelNameFocused = false; - bool _fromFocus = false; - bool _toFocus = false; - bool _dateFocus = false; - bool _timeFocus = false; - bool _commentsFocus = false; @override void initState() { super.initState(); - _tripTypeFocusNode.addListener(() { + // Initialize fields dynamically + for (var field in dataHeader) { + if (!textControllers.containsKey(field)) { + textControllers[field] = TextEditingController(); + } + if (!focusNodes.containsKey(field)) { + focusNodes[field] = FocusNode(); + } + if (!focusStates.containsKey(field)) { + focusStates[field] = false; + } + } + print("textControllers - $textControllers"); + + // Add focus listeners + focusNodes.forEach((key, focusNode) { + _addFocusListener(focusNode, (focus) { + setState(() { + focusStates[key] = focus; + }); + }); + }); + + + + // Add listeners to text fields + textControllers["_forexStartDate"]?.addListener(_onFieldChanged); + textControllers["_forexEndDate"]?.addListener(_onFieldChanged); + + } + + void _addFocusListener(FocusNode node, Function(bool) updateState) { + node.addListener(() { setState(() { - _tripTypeFocused = _tripTypeFocusNode.hasFocus; + updateState(node.hasFocus); }); - - _hotelNameFocusNode.addListener(() { - setState(() { - _isHotelNameFocused = _hotelNameFocusNode.hasFocus; - }); - }); - _fromFocusNode.addListener(() { - setState(() { - _fromFocus = _fromFocusNode.hasFocus; - }); - }); - - _toFocusNode.addListener(() { - setState(() { - _toFocus = _toFocusNode.hasFocus; - }); - }); - _dateFocusNode.addListener(() { - setState(() { - _dateFocus = _fromFocusNode.hasFocus; - }); - }); - - _timeFocusNode.addListener(() { - setState(() { - _timeFocus = _timeFocusNode.hasFocus; - }); - }); - - _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"] ?? ""); - _dateController = - TextEditingController(text: widget.formData["_Check_Out"] ?? ""); - _endDateController = - TextEditingController(text: widget.formData["_Check_In"] ?? ""); - _timeController = - TextEditingController(text: widget.formData["_Check_Out_Time"] ?? ""); - _commentsController = - TextEditingController(text: widget.formData["_comments"] ?? ""); - - // 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); - }); - _endDateController.addListener(() { - widget.updateFormData( - "Bus", "_Check_In", _endDateController.text); - }); - _timeController.addListener(() { - widget.updateFormData( - "Bus", "_Check_Out_Time", _timeController.text); - }); - _commentsController.addListener(() { - widget.updateFormData( - "Bus", "_comments", _commentsController.text); }); } + // ___________________________- + + // Check if all required fields have data + bool _isForexDataComplete() { + final data = getForexData; + return (data["country_code"]?.isNotEmpty ?? false) && + (data["start_date"]?.isNotEmpty ?? false) && + (data["end_date"]?.isNotEmpty ?? false); + } + + // Handle field changes + void _onFieldChanged() { + if (_isForexDataComplete()) { + postgetForexData(getForexData); + } + } + // Handle dropdown change + void _onCountryChanged(String? newCountry) { + setState(() { + selectedCountry = newCountry; + }); + + if (_isForexDataComplete()) { + postgetForexData(getForexData); + } + } + + // _____________ End Forex Details ______________- + + void _onFieldChangedForOthers() { + setState(() { + double transport = double.tryParse(textControllers["_transport"]?.text ?? "0") ?? 0; + double accommodation = double.tryParse(textControllers["_accomodation"]?.text ?? "0") ?? 0; + double telephone = double.tryParse(textControllers["_telephone"]?.text ?? "0") ?? 0; + + CalculatedOtherExpenses = (transport + accommodation + telephone).toStringAsFixed(2); + }); + } + + @override void dispose() { - _tripTypeFocusNode.dispose(); - _tripTypeController.dispose(); - _hotelNameController.dispose(); - _fromController.dispose(); - _toController.dispose(); - _dateController.dispose(); - _endDateController.dispose(); - _timeController.dispose(); - _commentsController.dispose(); + for (var node in focusNodes.values) { + node.dispose(); + } + + // Dispose all dynamically created TextEditingControllers + for (var controller in textControllers.values) { + controller.dispose(); + } super.dispose(); } @@ -215,33 +310,48 @@ class _ForexScreenState extends State { List _buildAccomadtionForm (bool isDesktop) { List buildResponsiveRow(List children) { return [ + + isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; } - List> rowBuilders = [ + // List> rowBuilders = [ + // + // _builClassType(isDesktop), + // _buildSecondRow(isDesktop) + // ]; - _builClassType(isDesktop), - _buildSecondRow(isDesktop) + List rowBuilders = [ + ..._builClassType(isDesktop), // Spread the List + Divider(), + ..._buildSecondRow(isDesktop), // Spread the List ]; + + return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), + SizedBox( + height: 28, + ), Text("Forex Details",style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF575A74)),), - + SizedBox(height: 8,), Divider(), - SizedBox( - height: 8, - ), - // Iterate over rowBuilders and wrap each in a responsive container - ...rowBuilders.expand((row) => buildResponsiveRow(row)), + SizedBox(height: 8,), + + ...buildResponsiveRow(_builClassType(isDesktop)), + SizedBox(height: 8,), + Divider(), + SizedBox(height: 28,), + ...buildResponsiveRow(_buildSecondRow(isDesktop)), ...buildResponsiveRow(_buildCardDetailsRow(isDesktop)), @@ -249,6 +359,8 @@ class _ForexScreenState extends State { ...buildResponsiveRow(_buildThirdRow(isDesktop)), + ...buildResponsiveRow(_buildCommetsRow(isDesktop)), + // Actions row remains a Row Row( mainAxisAlignment: MainAxisAlignment.end, @@ -281,13 +393,14 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); + textControllers["_forexStartDate"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } }; Future _selectForexEndDate(BuildContext context) async { + DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); @@ -303,12 +416,36 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedEndDate) { setState(() { _selectedEndDate = pickedDate; - _endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); + textControllers["_forexEndDate"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); }); } } + late Map countryMap; // Mapping country_code -> country_name + late List 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(); + + selectedCountry ??= null; + + + // // Set default selected value + // if (selectedCountry == null && countryCodes.isNotEmpty) { + // selectedCountry = countryCodes.first; + // } + + // -------------------------- End Selected Country Dropdown -------------------------------------- + return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -321,8 +458,8 @@ class _ForexScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: _dateFocus, + CustomTextFieldForexWrapper( + isFocused: focusStates["_forexStartDate"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, @@ -331,8 +468,8 @@ class _ForexScreenState extends State { onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( child: TextField( - focusNode: _dateFocusNode, - controller: _dateController, + focusNode: focusNodes["_forexStartDate"], + controller: textControllers["_forexStartDate"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", @@ -368,8 +505,10 @@ class _ForexScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: _dateFocus, + CustomTextFieldForexWrapper( + + isFocused: focusStates["_forexEndDate"] ?? false, + isDesktop: isDesktop, child: SizedBox( height: 40, @@ -378,8 +517,8 @@ class _ForexScreenState extends State { onTap: () => _selectForexEndDate(context), child: AbsorbPointer( child: TextField( - focusNode: _dateFocusNode, - controller: _endDateController, + focusNode: focusNodes["_forexEndDate"], + controller: textControllers["_forexEndDate"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", @@ -400,161 +539,80 @@ class _ForexScreenState extends State { ), - ]; - } - - - - List _buildTripType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_trip_type'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), - ), - ); - } - - // Default selected value - String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - - - return [ - CustomTextFieldWrapper( - isFocused: _isHotelNameFocused, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - - child: DropdownButtonFormField( - focusNode: _tripTypeFocusNode, // Assign the correct focus node - value: selectedPurpose, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - - - widget.updateFormData("Flight", "trip_type", newValue ?? ""); - } - : null, - - items: dropdownItems, - ), - - - ), - ), - ]; - } - - - List _builClassType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_class'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), - ), - ); - } - - // Default selected value - String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - - return [ - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop)Spacer() else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Contries", + "Countries", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: _isHotelNameFocused, + CustomTextFieldForexWrapper( + isFocused: focusStates["_countries"] ?? false, isDesktop: isDesktop, + child: SizedBox( height: 40, - - child: DropdownButtonFormField( - 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( + 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) { - setState(() { - selectedPurpose = newValue; - }); - } - : null, - items: dropdownItems, - ), + items: countryMap.values.toList(), - // 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), - // ), - // ), + 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(() { + // Find the country_code based on selected country_name + selectedCountry = countryMap.entries + .firstWhere((entry) => entry.value == newValue) + .key; + _onCountryChanged(selectedCountry); + }); + + }, + ), ), ), ], ), - if (isDesktop) - SizedBox( - width: 8, - ) - else - SizedBox( + + ]; + } + + + + + List _builClassType(bool isDesktop){ + + + return [ + + + if (isDesktop) Spacer() else SizedBox( height: 8, ), Column( @@ -569,7 +627,7 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, + isFocused: focusStates["_durationFocused"] ?? false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( @@ -578,9 +636,9 @@ class _ForexScreenState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - " 1000 ", - // focusNode: _toFocusNode, - // controller: _toController, + // "dur", + // selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration", + selectedDuration ?? "Duration", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", @@ -595,12 +653,7 @@ class _ForexScreenState extends State { ), ], ), - if (isDesktop) - SizedBox( - width: 8, - ) - else - SizedBox( + if (isDesktop)SizedBox(width: 8,) else SizedBox( height: 8, ), Column( @@ -615,56 +668,28 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _isHotelNameFocused, + isFocused: focusStates["_currencyFocused"] ?? false, isDesktop: isDesktop, + color: Colors.transparent, child: SizedBox( height: 40, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + // "cur", + // "${selectedCurrency}", + selectedCurrency ?? "Currency", + // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), - child: DropdownButtonFormField( - 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), - // ), - // ), ), ), ], ), - if (isDesktop) - SizedBox( - width: 8, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -677,7 +702,7 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, + isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( @@ -686,9 +711,9 @@ class _ForexScreenState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - "Rs ", - // focusNode: _toFocusNode, - // controller: _toController, + // "amo", + selectedPerdiemAmount ?? "Amount", + // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", @@ -703,17 +728,15 @@ class _ForexScreenState extends State { ), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop)Spacer() else SizedBox(height: 8,), + ]; } List _buildSecondRow(bool isDesktop) { + + return [ Column( @@ -728,21 +751,21 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _fromFocus, + isFocused: focusStates["_transport"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( - focusNode: _fromFocusNode, - controller: _fromController, + focusNode: focusNodes["_transport"], + controller: textControllers["_transport"], + onChanged: (value) => _onFieldChangedForOthers(), style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "From", + labelText: "Transport", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -767,17 +790,18 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, + isFocused: focusStates["_accomodation"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( - focusNode: _toFocusNode, - controller: _toController, + focusNode: focusNodes["_accomodation"], + controller: textControllers["_accomodation"], + onChanged: (value) => _onFieldChangedForOthers(), style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "To", + labelText: "Accomodation", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -806,16 +830,17 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _fromFocus, + isFocused: focusStates["_telephone"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( - focusNode: _fromFocusNode, - controller: _fromController, + focusNode: focusNodes["_telephone"], + controller: textControllers["_telephone"], + onChanged: (value) => _onFieldChangedForOthers(), style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "From", + labelText: "Telephone", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -845,7 +870,7 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, + isFocused: false, isDesktop: isDesktop, color:Colors.transparent, child: SizedBox( @@ -854,7 +879,7 @@ class _ForexScreenState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - "Rs ", + CalculatedOtherExpenses?? "0", // focusNode: _toFocusNode, // controller: _toController, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), @@ -899,131 +924,7 @@ class _ForexScreenState extends State { String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Card Number*", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: _fromFocus, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: TextField( - focusNode: _fromFocusNode, - controller: _fromController, - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "From", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Currency*", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - - child: DropdownButtonFormField( - 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, - ), - ), - ), - ], - ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Card*", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: _toFocus, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - - child: TextField( - focusNode: _toFocusNode, - controller: _toController, - style: const TextStyle(fontSize: 12), - decoration: const InputDecoration( - labelText: "To", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 16), - ), - ), - ), - ), - ], - ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1035,17 +936,17 @@ class _ForexScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - isFocused: _fromFocus, + CustomTextFieldForexWrapper( + isFocused: focusStates["_cash"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: TextField( - focusNode: _fromFocusNode, - controller: _fromController, + focusNode: focusNodes["_cash"], + controller: textControllers["_cash"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( - labelText: "From", + labelText: "Cash", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -1058,13 +959,138 @@ class _ForexScreenState extends State { ], ), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Card*", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldForexWrapper( + isFocused: focusStates["_card"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + + child: TextField( + focusNode: focusNodes["_card"], + controller: textControllers["_card"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Card", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + + + + + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Currency*", + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74)), + // ), + // SizedBox(height: 5), + // CustomTextFieldItnerarySubWrapper( + // isFocused: focusStates["_currency"] ?? false, + // isDesktop: isDesktop, + // child: SizedBox( + // height: 40, + // + // child: DropdownButtonFormField( + // focusNode: focusNodes["_currencyFocusNode"], + // // 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, + // ), + // ), + // ), + // ], + // ), + + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Card Number*", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldForexWrapper( + isFocused: focusStates["_cardNumber"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + focusNode: focusNodes["_cardNumber"], + controller: textControllers["_cardNumber"], + style: const TextStyle(fontSize: 12), + decoration: const InputDecoration( + labelText: "Card Number", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 16), + + ), + ), + ), + ), + ], + ), + ]; } List _buildThirdRow(bool isDesktop) { return [ + isChecked? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1077,19 +1103,60 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldWrapper( - isFocused: _commentsFocus, // Dropdown doesn't use focus + isFocused: focusStates["_deliveryLocation"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.66, child: TextField( - focusNode: _commentsFocusNode, - controller: _commentsController, + focusNode: focusNodes["_deliveryLocation"], + controller: textControllers["_deliveryLocation"], maxLines: 3, keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( - labelText: "Description", + labelText: "Location", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 4), + ), + ), + ), + ], + ) + :SizedBox.shrink() + ]; + } + + + List _buildCommetsRow(bool isDesktop) { + return [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Comments", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: focusStates["_comments"] ?? false, // Dropdown doesn't use focus + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.4 + : MediaQuery.of(context).size.width * 0.66, + child: TextField( + focusNode: focusNodes["_comments"], + controller: textControllers["_comments"], + maxLines: 3, + keyboardType: TextInputType.multiline, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -1102,6 +1169,7 @@ class _ForexScreenState extends State { ]; } + List _buildFprexCard(bool isDesktop) { return [ Row( @@ -1152,7 +1220,7 @@ class _ForexScreenState extends State { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index 77c9259..8f663b0 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -6,13 +6,15 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class InsuranceScreen extends StatefulWidget { - final Map formData; - final Map? 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? apiData; + final Function(bool) onClose; + final Function(Map) onSaveInsurance; + final Map? 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 { 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 _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 { String? selectedTripType; + String? selectedInsuranceType; + + Map get InsuranceData{ + Map 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 { }); } + + + 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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -258,7 +261,7 @@ class _InsuranceScreenState extends State { } // 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 { child: DropdownButtonFormField( 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 { onChanged: purposeList.isNotEmpty ? (newValue) { setState(() { - selectedPurpose = newValue; + selectedInsuranceType = newValue; }); - print(selectedPurpose); + print(selectedInsuranceType); } : null, @@ -298,85 +301,6 @@ class _InsuranceScreenState extends State { } - List _builClassType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_class'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - 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( - 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 _buildSecondRow(bool isDesktop) { @@ -554,7 +478,7 @@ class _InsuranceScreenState extends State { : 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index be5232d..dd38027 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -6,13 +6,16 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class MiscellaneousScreen extends StatefulWidget { - final Map formData; - final Map? 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? apiData; + final Function(bool) onClose; + final Function(Map) onSaveMiscellaneous; + final Map? 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 { 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 _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 get miscellaneousData { + Map 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 { }); }); - _dateFocusNode.addListener(() { - setState(() { - _dateFocus = _fromFocusNode.hasFocus; - }); - }); - - _commentsFocusNode.addListener(() { setState(() { _commentsFocus = _commentsFocusNode.hasFocus; @@ -77,50 +83,37 @@ class _MiscellaneousScreenState extends State { - - _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 { alignment: Alignment.centerRight, child: InkWell( onTap: () { + _commentsController.clear(); widget.onClose(false); }, child: Icon( @@ -177,18 +171,11 @@ class _MiscellaneousScreenState extends State { ]; } - List> 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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -258,7 +245,7 @@ class _MiscellaneousScreenState extends State { } // 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 { } : null, - items: dropdownItems, ), @@ -298,240 +284,6 @@ class _MiscellaneousScreenState extends State { } - List _builClassType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_class'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - 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( - 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 _buildSecondRow(bool isDesktop) { - - - DateTime? _selectedCheckOutDate; - TimeOfDay? _selectedCheckOutTime; - - Future _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 _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 _buildThirdRow(bool isDesktop) { return [ @@ -577,7 +329,9 @@ class _MiscellaneousScreenState extends State { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 3ef7ec9..854b4f3 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -6,13 +6,13 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class TaxiScreen extends StatefulWidget { - final Map formData; final Map? apiData; - final Function(String tab, String key, String value) updateFormData; final Function(bool) onClose; + final Function(Map) onSavetaxi; + final Map? 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 { Map 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 _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 get taxiData { + Map 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 controllers = [ - _tripTypeController, _hotelNameController, _fromController, _toController, _dateController, - _timeController, _commentsController - ]; - - List 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 { }); } + + @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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -227,7 +254,7 @@ class _TaxiScreenState extends State { } // 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 { ), 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 { height: 40, child: DropdownButtonFormField( - 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 { 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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -374,19 +399,19 @@ class _TaxiScreenState extends State { } // 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( - focusNode: _tripTypeFocusNode, // Assign the correct focus node - value: selectedPurpose, + child: DropdownButtonFormField( + 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 { 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 { } - List _builClassType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_class'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - 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( - 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 _buildSecondRow(bool isDesktop) { @@ -558,13 +500,13 @@ class _TaxiScreenState extends State { ), 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 { ), 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 { ), ], ), - - ]; } @@ -737,7 +677,7 @@ class _TaxiScreenState extends State { : 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index 540abe9..614aaf1 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -6,19 +6,20 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class TrainScreen extends StatefulWidget { - final Map formData; - final Map? 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? apiData; + final Function(Map)onSavetrain; + final Function(bool) onClose; + final Map? selectedItem; + + TrainScreen({ + required this.onClose, this.apiData, required this.onSavetrain, required this.selectedItem}); @override - _BusScreenState createState() => _BusScreenState(); + _TrainScreenState createState() => _TrainScreenState(); } -class _BusScreenState extends State { +class _TrainScreenState extends State { final GlobalKey _formKey = GlobalKey(); Map selectedValues = {}; @@ -31,7 +32,6 @@ class _BusScreenState extends State { final FocusNode _timeFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late Map _controllers; late TextEditingController _trainNoController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); @@ -39,7 +39,7 @@ class _BusScreenState extends State { 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 { bool _timeFocus = false; bool _commentsFocus = false; + String? selectedClass; + + + + Map get trainData { + Map 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 { _fromFocus = _fromFocusNode.hasFocus; }); }); - _toFocusNode.addListener(() { setState(() { _toFocus = _toFocusNode.hasFocus; @@ -79,13 +110,11 @@ class _BusScreenState extends State { _dateFocus = _fromFocusNode.hasFocus; }); }); - _timeFocusNode.addListener(() { setState(() { _timeFocus = _timeFocusNode.hasFocus; }); }); - _commentsFocusNode.addListener(() { setState(() { _commentsFocus = _commentsFocusNode.hasFocus; @@ -93,53 +122,20 @@ class _BusScreenState extends State { }); + _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 { _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 { ), ), - // child: DropdownButtonFormField( - // 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 { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -363,7 +347,7 @@ class _BusScreenState extends State { } // 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 { child: DropdownButtonFormField( 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 { 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 { : 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index 2ad97b2..d6a3676 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -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 formData; - final Map? 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? apiData; + final List? apiCountryData; + + + final Function(bool) onClose; + final Function(Map) onSaveVisa; + final Map? 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 { Map selectedValues = {}; + List 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 _controllers; late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); @@ -39,72 +42,65 @@ class _VisaScreenState extends State { 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 get visaData{ + Map 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 controllers = [ - _tripTypeController, _hotelNameController, _fromController, _toController, _dateController, - _timeController, _commentsController - ]; - - List 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 { _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 { } List> rowBuilders = [ - // _builClassType(isDesktop), _buildSecondRow(isDesktop) ]; @@ -249,7 +256,7 @@ class _VisaScreenState extends State { List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -263,7 +270,7 @@ class _VisaScreenState extends State { } // 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 { print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - widget.updateFormData("Flight", "trip_type", newValue ?? ""); } : null, @@ -304,110 +310,51 @@ class _VisaScreenState extends State { } - List _builClassType(bool isDesktop){ - - List purposeList = widget.apiData?['flight_class'] ?? []; - - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); - - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - 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( - 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 _buildSecondRow(bool isDesktop) { - List purposeList = widget.apiData?['flight_class'] ?? []; + // List countryList = widget.apiCountryData ?? []; - List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); + // + // List> dropdownItems = countryList + // .map((item)=>DropdownMenuItem( + // value: item['country_code'], // Use 'country_code' from API response + // child: Text(item['country_name']), + // )).toList(); + // + // if (dropdownItems.isEmpty) { + // dropdownItems.add( + // DropdownMenuItem( + // value: null, + // child: Text("No options available", style: TextStyle(color: Colors.grey)), + // ), + // ); + // } + // + // // Default selected value + // selectedCountry ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; + // List countryNames = countryList.map((item) => item['country_name'] as String).toList(); + // - if (dropdownItems.isEmpty) { - dropdownItems.add( - DropdownMenuItem( - value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), - ), - ); + late Map countryMap; // Mapping country_code -> country_name + late List 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 { isDesktop: isDesktop, child: SizedBox( height: 40, - - child: DropdownButtonFormField( - 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( + 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( + // 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 { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Start Date", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, @@ -566,7 +543,7 @@ class _VisaScreenState extends State { : 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 { // 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 { // Save Changes Button ElevatedButton( onPressed: () { - // TODO: Implement save logic + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save diff --git a/lib/Screens/itnerary_list/accomodation_list.dart b/lib/Screens/itnerary_list/accomodation_list.dart index e633f43..4673152 100644 --- a/lib/Screens/itnerary_list/accomodation_list.dart +++ b/lib/Screens/itnerary_list/accomodation_list.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; class AccomodationListWidget extends StatelessWidget { - const AccomodationListWidget({super.key}); + + final List> accommodationList; + final Function(bool, Map, String) onOpen; + final Function(Map) 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 _buildDataRows() { - List> 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 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); }, ), ], diff --git a/lib/Screens/itnerary_list/bus_list.dart b/lib/Screens/itnerary_list/bus_list.dart index dd79350..f7c80ac 100644 --- a/lib/Screens/itnerary_list/bus_list.dart +++ b/lib/Screens/itnerary_list/bus_list.dart @@ -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> busList; + final Function(bool, Map, String) onOpen; + final Function(Map) onDeleteBus; + const BusListWidget({super.key, required this.busList, required this.onOpen, required this.onDeleteBus}); - @override - _BusListWidgetState createState() => _BusListWidgetState(); - -} - -class _BusListWidgetState extends State { - // const BusListWidget({super.key}); - - - -late Future> futurePlans; - - -@override -void initState(){ - super.initState(); - futurePlans = fetchPlans(); - -} - -Future getToken() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('auth_token'); -} - - -// Fetch API Data -Future> 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 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> 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> fetchPlans() async { } List _buildDataRows() { - List> 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 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> 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); }, ), ], diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 9a3605b..3860d7b 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -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> forexList; + final Function(bool, Map, String)onOpen; + final Function(Map) onDeleteForex; - @override - _ForexListWidgetState createState() => _ForexListWidgetState(); - -} - -class _ForexListWidgetState extends State { - // const BusListWidget({super.key}); - - - late Future> futurePlans; - - - @override - void initState(){ - super.initState(); - futurePlans = fetchPlans(); - - } - - Future getToken() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('auth_token'); - } - - -// Fetch API Data - Future> 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 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 { 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 { } List _buildDataRows() { - List> 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 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 { 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); }, ), ], diff --git a/lib/Screens/itnerary_list/insurance_list.dart b/lib/Screens/itnerary_list/insurance_list.dart index 3644283..497d0b0 100644 --- a/lib/Screens/itnerary_list/insurance_list.dart +++ b/lib/Screens/itnerary_list/insurance_list.dart @@ -1,7 +1,12 @@ +import 'dart:js_interop'; + import 'package:flutter/material.dart'; class InsuranceListWidget extends StatelessWidget { - const InsuranceListWidget({super.key}); + final List> insuranceList; + final Function(bool, Map, String)onOpen; + final Function(Map)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 _buildDataRows() { - List> 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 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); }, ), ], diff --git a/lib/Screens/itnerary_list/miscellaneous_list.dart b/lib/Screens/itnerary_list/miscellaneous_list.dart index c0f8740..1411ce7 100644 --- a/lib/Screens/itnerary_list/miscellaneous_list.dart +++ b/lib/Screens/itnerary_list/miscellaneous_list.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; class MiscellaneousListWidget extends StatelessWidget { - const MiscellaneousListWidget({super.key}); + + final List> miscellaneousList; + final Function(bool, Map, String) onOpen; + final Function(Map) 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 _buildDataRows() { - List> 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 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 }, ), diff --git a/lib/Screens/itnerary_list/taxi_list.dart b/lib/Screens/itnerary_list/taxi_list.dart index 6a225d9..fe51c09 100644 --- a/lib/Screens/itnerary_list/taxi_list.dart +++ b/lib/Screens/itnerary_list/taxi_list.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; class TaxiListWidget extends StatelessWidget { - const TaxiListWidget({super.key}); + final List> taxiList; + final Function(bool, Map, String) onOpen; + final Function(Map) 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 _buildDataRows() { - List> 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 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); }, ), ], diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index 951992c..b9f7d57 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; class TrainListWidget extends StatelessWidget { - const TrainListWidget({super.key}); + final List> trainList; + final Function(bool, Map, String) onOpen; + final Function(Map) 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 _buildDataRows() { - List> 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 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); }, ), ], diff --git a/lib/Screens/itnerary_list/visa_list.dart b/lib/Screens/itnerary_list/visa_list.dart index 702a6ae..213608b 100644 --- a/lib/Screens/itnerary_list/visa_list.dart +++ b/lib/Screens/itnerary_list/visa_list.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; class VisaListWidget extends StatelessWidget { - const VisaListWidget({super.key}); + final List> visaList; + final Function(bool, Map, String) onOpen; + final Function(Map) 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 _buildDataRows() { - List> 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 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); }, ), ], diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index fecde4c..4985b35 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -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 { - 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? storedData; + // late String? _selectedIsBillable = "Billable"; + + + String? userDetails; + String? userName; + String? selfId; + String? otherUserName; + String? selectedplanUserId; + bool? selectedIstravelUser; Map? apiData; // Store API response here + List? apiCountryData; + List? 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 validationErrors = {}; + List> miscellaneousList = []; + // List> miscellaneousList = [{"special_request": 1, "comments": "posta", "indx": 1}]; + List> trainList = []; + List> busList = []; + List> taxiList = []; + + //Getter Method + Map 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> newMiscellaneousList) { + setState(() { + miscellaneousList = newMiscellaneousList; // Update miscellaneous data + }); + print("Updated Miscellaneous Data in CreateNewPlan: $miscellaneousList"); + } + + + void handleItineraryUpdate(String type, List> 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 getToken() async { final prefs = await SharedPreferences.getInstance(); @@ -132,7 +287,20 @@ class _CreateNewPlansState extends State { return prefs.getString('userId'); } + Future ?> 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 fetchPlans() async { @@ -167,7 +335,7 @@ class _CreateNewPlansState extends State { Map 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 { } } + Future fetchCostCenter() async { + final String apiUrldata = '$apiUrl/api/getCostCenterMaster'; + final token = await getToken(); - Future 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 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 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 plansJson = data['data']; // 'data' is a Map, not a List + + if (data['data'] is List) { + List 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 postPlanData(Map 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 { 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 { ), 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 { 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 { : 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 { _buildNonDescriptionColumn(), SizedBox(height: 15), _buildDescriptionColumn(isDesktop), + + ], ), @@ -349,10 +704,19 @@ class _CreateNewPlansState extends State { 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 { /// Extracted helper function List _buildCostIsBillable() { + + List purposeList = apiData?['plan_is_billable'] ?? []; + return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -380,72 +747,114 @@ class _CreateNewPlansState extends State { child: SizedBox( height: 45, // Set appropriate height child: DropdownButtonFormField( - 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>((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( - value: "Billable", - groupValue: _selectedIsBillable, - activeColor: Colors.blueAccent, - onChanged: (value) { - setState(() { - _selectedIsBillable = value; - }); - }, - ), - Text("Billable"), - ], - ), - SizedBox(width: 20), // Spacing - Row( - children: [ - Radio( - 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((item) { + return Row( + children: [ + Radio( + 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( + // value: "Billable", + // groupValue: _selectedIsBillable, + // activeColor: Colors.blueAccent, + // onChanged: (value) { + // setState(() { + // _selectedIsBillable = value; + // }); + // }, + // ), + // Text("Billable"), + // ], + // ), + // SizedBox(width: 20), // Spacing + // Row( + // children: [ + // Radio( + // value: "Non Billable", + // groupValue: _selectedIsBillable, + // activeColor: Colors.blueAccent, + // onChanged: (value) { + // setState(() { + // _selectedIsBillable = value; + // }); + // }, + // ), + // Text("Non Billable"), + // ], + // ), + // ], + // ), + // ], + // ) ]; } @@ -490,11 +899,12 @@ class _CreateNewPlansState extends State { List _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 { 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 { color: Color(0xFFF4F4FB), width: 150, padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), - isFocused: _selectedOption == "Option 2", + isFocused: _selectedTripType == "2", isDesktop: widget.isDesktop, child: RadioListTile( 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 { // 'plan_purpose_of_travel' Starts ------------------------------------------------------ - List purposeList = apiData?['plan_purpose_of_travel'] ?? []; + List purposeList = apiData?['plan_purpose_of_travel'] ?? []; List> dropdownItems = purposeList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -564,7 +976,7 @@ class _CreateNewPlansState extends State { } // 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 { List> dropdownFuncDeptItems = funcDeptList .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], + value: item['dropdown_key'], child: Text(item['dropdown_value']), )).toList(); @@ -586,7 +998,7 @@ class _CreateNewPlansState extends State { } // 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 { 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 { ), 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 { ); } + List _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 { 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(); } ); } diff --git a/lib/Screens/plans/dynamic_itinerary_stepper.dart b/lib/Screens/plans/dynamic_itinerary_stepper.dart index a4b8e21..65b556f 100644 --- a/lib/Screens/plans/dynamic_itinerary_stepper.dart +++ b/lib/Screens/plans/dynamic_itinerary_stepper.dart @@ -23,7 +23,10 @@ import '../itnerary_list/visa_list.dart'; class DynamicItinerary extends StatefulWidget { final Map? apiData; - const DynamicItinerary({super.key, required this.apiData}); + final List? apiCountryData; + final Function(String, List>) 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 { String selectedListOption = ""; bool isSelected = false; + Map? selectedItem; + int? selectedIndex; + + // List> miscellaneousList = []; + + Map>> 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> formData = { @@ -51,8 +160,24 @@ class _DynamicItineraryState extends State { 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 { } + void handleItineraryUpdate(String type, Map newData) { + setState(() { + if (!itineraryData.containsKey(type)) { + itineraryData[type] = []; // Initialize if null + } + + List> 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 data){ + setState(() { + if(!itineraryData.containsKey(type)){ + return; + } + + List> 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 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 { 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 { ], ), - ], ); @@ -248,6 +515,7 @@ class _DynamicItineraryState extends State { setState(() { selectedOption = title; isSelected = true; + selectedItem = null; }); }, child: Icon( diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index e24e533..a623ac4 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -138,74 +138,78 @@ class _ListPlansState extends State{ 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(), + ), + ), + + ), + ); + + }, ); }, ), diff --git a/lib/data/models/Searchtraveller.dart b/lib/data/models/Searchtraveller.dart new file mode 100644 index 0000000..f0f5ba2 --- /dev/null +++ b/lib/data/models/Searchtraveller.dart @@ -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 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()?? '', + + ); + } + +} \ No newline at end of file diff --git a/lib/data/models/searchUser.dart b/lib/data/models/searchUser.dart index ea3f3b7..20cb817 100644 --- a/lib/data/models/searchUser.dart +++ b/lib/data/models/searchUser.dart @@ -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 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()?? '', diff --git a/lib/widgets/custom_text_forex.dart b/lib/widgets/custom_text_forex.dart new file mode 100644 index 0000000..001dce1 --- /dev/null +++ b/lib/widgets/custom_text_forex.dart @@ -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 { + @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, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index f216996..cc94b90 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index b556a92..d4a2844 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: