From 886bef2baa7198e9cacff3ab1d7bde86fd6c9116 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Tue, 8 Apr 2025 16:14:01 +0530 Subject: [PATCH] OrgLevel Data, UserDetails --- lib/Screens/dialog/user_selection_dialog.dart | 323 ++--- lib/Screens/itnerary/accomodations.dart | 191 +-- lib/Screens/itnerary/bus.dart | 213 ++-- lib/Screens/itnerary/flights.dart | 759 ++++++------ lib/Screens/itnerary/forex.dart | 578 ++++----- lib/Screens/itnerary/insurance.dart | 227 ++-- lib/Screens/itnerary/miscellaneous.dart | 128 +- lib/Screens/itnerary/taxi.dart | 268 ++--- lib/Screens/itnerary/train.dart | 297 +++-- lib/Screens/itnerary/visa.dart | 237 ++-- lib/Screens/plans/create_plans.dart | 1042 +++++++++-------- lib/Screens/plans/list_plans.dart | 557 +++++---- lib/Screens/policy/policy.dart | 389 ++++-- lib/Screens/policy/policyCriteria.dart | 80 +- .../create_user/create_user.dart | 341 +++--- lib/Screens/userManagement/user_List.dart | 200 +++- lib/routes/custom_drawer.dart | 272 +++-- lib/routes/custom_router.dart | 26 +- lib/services/apiService.dart | 44 +- lib/utils/auth_utils.dart | 17 + lib/widgets/custom_text_field.dart | 17 +- lib/widgets/custom_text_forex.dart | 23 +- pubspec.lock | 44 +- pubspec.yaml | 1 + 24 files changed, 3302 insertions(+), 2972 deletions(-) diff --git a/lib/Screens/dialog/user_selection_dialog.dart b/lib/Screens/dialog/user_selection_dialog.dart index be3b13d..d7b556e 100644 --- a/lib/Screens/dialog/user_selection_dialog.dart +++ b/lib/Screens/dialog/user_selection_dialog.dart @@ -4,23 +4,27 @@ import 'package:flutter/material.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; - import '../../config/apiUrl.dart'; import '../../data/models/Searchtraveller.dart'; import '../../data/models/searchUser.dart'; +import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_traveller.dart'; -class UserSelectionDialog extends StatefulWidget{ +class UserSelectionDialog extends StatefulWidget { final String title; - final void Function(String,String, bool) onSubmit; + final void Function(String, String, bool) onSubmit; - UserSelectionDialog({Key? key, required this.title, required this.onSubmit,}) : super(key: key); + UserSelectionDialog({ + Key? key, + required this.title, + required this.onSubmit, + }) : super(key: key); @override _UserSelectionDialogState createState() => _UserSelectionDialogState(); } -class _UserSelectionDialogState extends State{ +class _UserSelectionDialogState extends State { TextEditingController _controller = TextEditingController(); TextEditingController _searchController = TextEditingController(); // List _filteredUsers = []; @@ -31,21 +35,23 @@ class _UserSelectionDialogState extends State{ List> _filteredList = []; List _filteredTraveller = []; String userIdSelected = " "; - bool isTraveller = false; + bool isTraveller = false; bool _showTravellerForm = false; - final _formKey = GlobalKey(); + String? orgId; + final _formKey = GlobalKey(); Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } - Future fetchUsers() async { - final String apiUrldata = '$apiUrl/api/users'; + orgId = await getOrgId(); + // final String apiUrlData = '$apiUrl/api/users?org_id=$orgId'; + final String apiUrldata = '$apiUrl/api/users?org_id=$orgId'; try { final token = await getToken(); @@ -62,8 +68,7 @@ class _UserSelectionDialogState extends State{ ); if (response.statusCode == 200) { - final Map responseBody = json.decode(response.body); - + final Map responseBody = json.decode(response.body); print("API Response: $responseBody"); // Debugging @@ -88,18 +93,69 @@ class _UserSelectionDialogState extends State{ for (var user in _users) { print("${user.firstName} ${user.lastName}"); } - } else { - throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); + throw Exception( + "Unexpected response format: Expected a List but got ${responseBody.runtimeType}"); } } else { - throw Exception('Failed to load users. Status Code: ${response.statusCode}'); + throw Exception( + 'Failed to load users. Status Code: ${response.statusCode}'); } } catch (e) { print("Error fetching users: $e"); } } + Future fetchTraveller() async { + orgId = await getOrgId(); + final String apiUrldata = '$apiUrl/api/travellers?org_id=$orgId'; + + try { + final token = await getToken(); + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + + if (response.statusCode == 200) { + final Map 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"); + } + } + void _filterUsers1(String query) { print("Filtering users..."); setState(() { @@ -115,7 +171,8 @@ class _UserSelectionDialogState extends State{ user.alternateMobileNo ?? "" ]; - return searchFields.any((field) => field.contains(query.toLowerCase())); + return searchFields + .any((field) => field.contains(query.toLowerCase())); }).toList(); } }); @@ -126,7 +183,6 @@ class _UserSelectionDialogState extends State{ } } - void _filterUsers(String query) { print("Filtering _filterUsersTravellers..."); setState(() { @@ -146,9 +202,9 @@ class _UserSelectionDialogState extends State{ user.mobileNo ?? "", user.alternateMobileNo ?? "" ]; - return searchFields.any((field) => field.contains(query.toLowerCase())); + return searchFields + .any((field) => field.contains(query.toLowerCase())); }).map((user) => {"type": "user", "data": user}), - ]; } }); @@ -156,7 +212,8 @@ class _UserSelectionDialogState extends State{ print("Filtered List:"); for (var item in _filteredList) { var user = item["data"]; - print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); + print( + "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); } } @@ -168,7 +225,8 @@ class _UserSelectionDialogState extends State{ if (query.isEmpty) { _filteredList = [ ..._users.map((user) => {"type": "user", "data": user}), - ..._traveller.map((traveller) => {"type": "traveller", "data": traveller}), + ..._traveller + .map((traveller) => {"type": "traveller", "data": traveller}), ]; } else { _filteredList = [ @@ -180,9 +238,9 @@ class _UserSelectionDialogState extends State{ user.mobileNo ?? "", user.alternateMobileNo ?? "" ]; - return searchFields.any((field) => field.contains(query.toLowerCase())); + return searchFields + .any((field) => field.contains(query.toLowerCase())); }).map((user) => {"type": "user", "data": user}), - ..._traveller.where((traveller) { List searchFields = [ "${traveller.firstName} ${traveller.lastName}".toLowerCase(), @@ -190,7 +248,8 @@ class _UserSelectionDialogState extends State{ traveller.travellerId.toLowerCase() ?? "", traveller.mobileNo ?? "", ]; - return searchFields.any((field) => field.contains(query.toLowerCase())); + return searchFields + .any((field) => field.contains(query.toLowerCase())); }).map((traveller) => {"type": "traveller", "data": traveller}), ]; } @@ -199,58 +258,10 @@ class _UserSelectionDialogState extends State{ print("Filtered List:"); for (var item in _filteredList) { var user = item["data"]; - print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); + print( + "${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}"); } } - Future 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() { @@ -268,7 +279,8 @@ class _UserSelectionDialogState extends State{ width: 400, // Adjust width as needed padding: EdgeInsets.all(16), child: Column( - mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily + mainAxisSize: + MainAxisSize.min, // Ensures content doesn't expand unnecessarily children: [ Text("Please Select User", style: TextStyle(fontSize: 14)), SizedBox(height: 10), @@ -280,15 +292,16 @@ class _UserSelectionDialogState extends State{ setState(() { _showTravellerForm = false; }); - widget.title == "Others"? _filterUsersTravellers(query): - _filterUsers(query); - + widget.title == "Others" + ? _filterUsersTravellers(query) + : _filterUsers(query); }, decoration: InputDecoration( hintText: "Search for a user", hintStyle: TextStyle(fontSize: 14), prefixIcon: Icon(Icons.search), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(8)), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.blueAccent, width: 2), @@ -301,7 +314,8 @@ class _UserSelectionDialogState extends State{ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("or create a new traveler", style: TextStyle(fontSize: 14, color: Color(0xFF575A74))), + Text("or create a new traveler", + style: TextStyle(fontSize: 14, color: Color(0xFF575A74))), TextButton( onPressed: () { setState(() { @@ -309,7 +323,9 @@ class _UserSelectionDialogState extends State{ _searchController.clear(); }); }, - child: Text("Create", style: TextStyle(fontSize: 14, color: Colors.blueAccent)), + child: Text("Create", + style: + TextStyle(fontSize: 14, color: Colors.blueAccent)), ), ], ), @@ -320,42 +336,51 @@ class _UserSelectionDialogState extends State{ // User List or Message _searchController.text.isNotEmpty ? SizedBox( - height: 300, // Limit height to avoid overflow - // child: _filteredUsers.isEmpty - child: _filteredList.isEmpty - ? Center( - child: Text( - "No users found", - style: TextStyle(fontSize: 14, color: Colors.grey), - ), - ) - : ListView.builder( - // itemCount: _filteredUsers.length, - itemCount: _filteredList.length, - itemBuilder: (context, index) { - // final user = _filteredUsers[index]; + height: 300, // Limit height to avoid overflow + // child: _filteredUsers.isEmpty + child: _filteredList.isEmpty + ? Center( + child: Text( + "No users found", + style: + TextStyle(fontSize: 14, color: Colors.grey), + ), + ) + : ListView.builder( + // itemCount: _filteredUsers.length, + itemCount: _filteredList.length, + itemBuilder: (context, index) { + // final user = _filteredUsers[index]; - final item = _filteredList[index]; - final user = item["data"]; // Extract user object - final userType = item["type"]; // "user" or "traveller" + final item = _filteredList[index]; + final user = item["data"]; // Extract user object + final userType = + item["type"]; // "user" or "traveller" - return ListTile( - title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"), - subtitle: Text("ID: ${userType == "user" ? user.userId : user.travellerId}"), - onTap: () { - String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"; - setState(() { - _searchController.text = selectedUser; - userIdSelected = userType == "user" ? user.userId : user.travellerId; - isTraveller = userType == "traveller"; - }); - print("Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," - " isTraveller: $userIdSelected"); - }, - ); - }, - ), - ) : SizedBox.shrink(), + return ListTile( + title: Text( + "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"), + subtitle: Text( + "ID: ${userType == "user" ? user.userId : user.travellerId}"), + onTap: () { + String selectedUser = + "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"; + setState(() { + _searchController.text = selectedUser; + userIdSelected = userType == "user" + ? user.userId + : user.travellerId; + isTraveller = userType == "traveller"; + }); + print( + "Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId}," + " isTraveller: $userIdSelected"); + }, + ); + }, + ), + ) + : SizedBox.shrink(), // Traveler Form if (_showTravellerForm) @@ -366,13 +391,16 @@ class _UserSelectionDialogState extends State{ padding: const EdgeInsets.all(16.0), child: TravelerForm( formKey: _formKey, - onSubmit: (String fullName, String travellerId, bool isTraveller) { - widget.onSubmit(fullName, travellerId,isTraveller); // Pass the data up + onSubmit: (String fullName, String travellerId, + bool isTraveller) { + widget.onSubmit(fullName, travellerId, + isTraveller); // Pass the data up }, firstNameController: TextEditingController(), lastNameController: TextEditingController(), emailController: TextEditingController(), mobileController: TextEditingController(), + orgId: orgId, ), ), ), @@ -392,7 +420,9 @@ class _UserSelectionDialogState extends State{ padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () => Navigator.pop(context), - child: Text("Cancel",), + child: Text( + "Cancel", + ), ), SizedBox(width: 10), ElevatedButton( @@ -406,8 +436,10 @@ class _UserSelectionDialogState extends State{ padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { - print("Submitting: ${_searchController.text}, ID: $userIdSelected"); - widget.onSubmit(_searchController.text,userIdSelected,isTraveller); + print( + "Submitting: ${_searchController.text}, ID: $userIdSelected"); + widget.onSubmit( + _searchController.text, userIdSelected, isTraveller); Navigator.pop(context); }, child: Text("Submit"), @@ -419,7 +451,6 @@ class _UserSelectionDialogState extends State{ ), ); } - } class TravelerForm extends StatefulWidget { @@ -427,25 +458,24 @@ class TravelerForm extends StatefulWidget { final TextEditingController lastNameController; final TextEditingController emailController; final TextEditingController mobileController; + final String? orgId; final GlobalKey formKey; final void Function(String, String, bool) onSubmit; - TravelerForm({ - required this.formKey, - required this.firstNameController, - required this.lastNameController, - required this.emailController, - required this.mobileController, - required this.onSubmit - }); + TravelerForm( + {required this.formKey, + required this.orgId, + required this.firstNameController, + required this.lastNameController, + required this.emailController, + required this.mobileController, + required this.onSubmit}); @override _TravelerFormState createState() => _TravelerFormState(); } class _TravelerFormState extends State { - - Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); @@ -472,27 +502,29 @@ class _TravelerFormState extends State { if (value == null || value.isEmpty) { return 'Email is required'; } - if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(value)) { + if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') + .hasMatch(value)) { return 'Enter a valid email address'; } return null; } - void _onSubmit(BuildContext context) { + Future _onSubmit(BuildContext context) async { bool isValid = _validateForm(); print("Form Validation Result: $isValid"); if (isValid) { print("Validation Success"); + _submitForm(context); } else { print("Validation Failed"); // This should now print if validation fails } } - Future _submitForm(BuildContext context) async { Map requestBody = { + "org_id": widget.orgId!, "first_name": widget.firstNameController.text, "last_name": widget.lastNameController.text, "email": widget.emailController.text, @@ -516,24 +548,24 @@ class _TravelerFormState extends State { body: jsonEncode(requestBody), ); - 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 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"); + print( + "Traveller Added: ID: $travellerId, Name: $firstName $lastName"); // Pass data to callback widget.onSubmit("$firstName $lastName", travellerId, true); - // Close the dialog Navigator.pop(context); } @@ -542,10 +574,11 @@ class _TravelerFormState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( - "Traveller added successfully!", - style: TextStyle(color: Colors.white), // ✅ Set text color + "Traveller added successfully!", + style: TextStyle(color: Colors.white), // ✅ Set text color + ), + backgroundColor: Colors.green, ), - backgroundColor: Colors.green,), ); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -564,7 +597,8 @@ class _TravelerFormState extends State { return ResponsiveBuilder( builder: (context, sizingInfo) { double widthFactor; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) { widthFactor = 0.23; @@ -599,7 +633,8 @@ class _TravelerFormState extends State { ), TextButton( onPressed: () => _onSubmit(context), - child: Text("Add", style: TextStyle(color: Colors.blueAccent)), + child: + Text("Add", style: TextStyle(color: Colors.blueAccent)), ), ], ), diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index 9d24738..460aa88 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -6,15 +6,16 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class AccomodationScreen extends StatefulWidget { - final Function(bool) onClose; // Callback function - final Function(Map) onSaveAccomadation; + final Function(Map) onSaveAccomadation; final Map? selectedItem; final String? loginUser; - - AccomodationScreen({ - required this.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser}); + AccomodationScreen( + {required this.onClose, + required this.onSaveAccomadation, + required this.selectedItem, + required this.loginUser}); @override _AccomodationScreenState createState() => _AccomodationScreenState(); @@ -31,7 +32,6 @@ class _AccomodationScreenState extends State { final FocusNode _checkOutTimeFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late TextEditingController _destinationController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _checkInController = TextEditingController(); @@ -59,11 +59,10 @@ class _AccomodationScreenState extends State { Map errorMessages = {}; Map get accomadationData { - - Map data ={ + Map data = { "destination_city": _destinationController.text, "hotel_name": _hotelNameController.text, - "checkin_date": _checkInController.text , + "checkin_date": _checkInController.text, "checkin_time": _checkInTimeController.text, "checkout_date": _checkOutController.text, "checkout_time": _checkOutTimeController.text, @@ -73,9 +72,11 @@ class _AccomodationScreenState extends State { }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["accomodation_id"] != null && widget.selectedItem?["accomodation_id"] != 0) { + } else if (widget.selectedItem?["accomodation_id"] != null && + widget.selectedItem?["accomodation_id"] != 0) { data["accomodation_id"] = widget.selectedItem!["accomodation_id"]; } } @@ -86,16 +87,19 @@ class _AccomodationScreenState extends State { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } - @override void initState() { super.initState(); - _addFocusListener(_destinationFocusNode, (focus) => _destinationFocused = focus); - _addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); + _addFocusListener( + _destinationFocusNode, (focus) => _destinationFocused = focus); + _addFocusListener( + _hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); _addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus); - _addFocusListener(_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus); + _addFocusListener( + _checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus); _addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus); - _addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus); + _addFocusListener( + _checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); _destinationController = initController("destination_city"); @@ -106,7 +110,6 @@ class _AccomodationScreenState extends State { _checkOutTimeController = initController("checkout_time"); _commentsController = initController("comments"); - _destinationController.addListener(() => _clearError("destination_city")); _hotelNameController.addListener(() => _clearError("hotel_name")); _checkInController.addListener(() => _clearError("checkin_date")); @@ -115,8 +118,6 @@ class _AccomodationScreenState extends State { _checkOutTimeController.addListener(() => _clearError("checkout_time")); } - - @override void dispose() { _destinationFocusNode.dispose(); @@ -137,12 +138,19 @@ class _AccomodationScreenState extends State { }); } } + bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["destination_city", "hotel_name","checkin_date","checkin_time","checkout_date", - "checkout_time"]; + List requiredFields = [ + "destination_city", + "hotel_name", + "checkin_date", + "checkin_time", + "checkout_date", + "checkout_time" + ]; // Check validation for each field for (String field in requiredFields) { @@ -154,25 +162,22 @@ class _AccomodationScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save accomadationData $accomadationData"); - void handleSave(){ - - print( "Handle Save accomadationData $accomadationData"); - - Map data = accomadationData; + Map data = accomadationData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveAccomadation(accomadationData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -201,10 +206,11 @@ class _AccomodationScreenState extends State { ), ), ), - Text("Accomodation Booking", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -245,11 +251,11 @@ class _AccomodationScreenState extends State { isDesktop ? Row( - children: _buildThirdRow(isDesktop), - ) + children: _buildThirdRow(isDesktop), + ) : Column( - children: _buildThirdRow(isDesktop), - ), + children: _buildThirdRow(isDesktop), + ), SizedBox(height: 10), Row( @@ -275,6 +281,9 @@ class _AccomodationScreenState extends State { CustomTextFieldWrapper( isFocused: _destinationFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -291,14 +300,14 @@ class _AccomodationScreenState extends State { ), ), ), - if (errorMessages["destination_city"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["destination_city"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["destination_city"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["destination_city"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -320,6 +329,9 @@ class _AccomodationScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -336,15 +348,14 @@ class _AccomodationScreenState extends State { ), ), ), - - if (errorMessages["hotel_name"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["hotel_name"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["hotel_name"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["hotel_name"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), ]; } @@ -359,9 +370,10 @@ class _AccomodationScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - initialDate: _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today) - ? _selectedCheckInDate! - : today, + initialDate: + _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today) + ? _selectedCheckInDate! + : today, firstDate: today, lastDate: DateTime(2100), ); @@ -395,7 +407,6 @@ class _AccomodationScreenState extends State { } //-------------------------------Check-In End - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -405,9 +416,8 @@ class _AccomodationScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -417,7 +427,8 @@ class _AccomodationScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - _checkOutController.text = DateFormat('yyyy-MM-dd').format(pickedDate); + _checkOutController.text = + DateFormat('yyyy-MM-dd').format(pickedDate); }); } } @@ -442,9 +453,6 @@ class _AccomodationScreenState extends State { } } - - - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -483,14 +491,14 @@ class _AccomodationScreenState extends State { ), ), ), - if (errorMessages["checkin_date"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["checkin_date"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["checkin_date"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["checkin_date"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -535,14 +543,14 @@ class _AccomodationScreenState extends State { ), ), ), - if (errorMessages["checkin_time"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["checkin_time"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["checkin_time"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["checkin_time"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -566,7 +574,6 @@ class _AccomodationScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( @@ -586,17 +593,16 @@ class _AccomodationScreenState extends State { ), ), ), - ), ), - if (errorMessages["checkout_date"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - errorMessages["checkout_date"]!, - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["checkout_date"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + errorMessages["checkout_date"]!, + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -634,12 +640,11 @@ class _AccomodationScreenState extends State { border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + Icon(Icons.access_time, size: 16, color: Colors.grey), ), ), ), ), - ), ), if (errorMessages["checkout_time"] != null) ...[ @@ -671,7 +676,7 @@ class _AccomodationScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, diff --git a/lib/Screens/itnerary/bus.dart b/lib/Screens/itnerary/bus.dart index bd5e2b7..404a1d8 100644 --- a/lib/Screens/itnerary/bus.dart +++ b/lib/Screens/itnerary/bus.dart @@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class BusScreen extends StatefulWidget { - final Map? apiData; final Function(bool) onClose; - final Function(Map)onSaveBus; - final Map? selectedItem; - final String? loginUser; + final Function(Map) onSaveBus; + final Map? selectedItem; + final String? loginUser; - BusScreen({ - required this.onClose, this.apiData, required this.onSaveBus, required this.selectedItem, required this.loginUser}); + BusScreen( + {required this.onClose, + this.apiData, + required this.onSaveBus, + required this.selectedItem, + required this.loginUser}); @override _BusScreenState createState() => _BusScreenState(); @@ -52,27 +55,26 @@ class _BusScreenState extends State { 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, - "created_by": widget.loginUser, - "updated_by": widget.loginUser, + Map get busData { + Map data = { + "from": _fromController.text, + "to": _toController.text, + "date": _dateController.text, + "time": _timeController.text, + "comments": _buscommentsController.text, + "created_by": widget.loginUser, + "updated_by": widget.loginUser, }; - - if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { - data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["bus_id"] != null && widget.selectedItem?["bus_id"] != 0) { - data["bus_id"] = widget.selectedItem!["bus_id"]; - } - } - + if (widget.selectedItem != null) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { + data["indx"] = widget.selectedItem!["indx"]; + } else if (widget.selectedItem?["bus_id"] != null && + widget.selectedItem?["bus_id"] != 0) { + data["bus_id"] = widget.selectedItem!["bus_id"]; + } + } return data; } @@ -91,41 +93,39 @@ class _BusScreenState extends State { }); }); - _hotelNameFocusNode.addListener(() { - setState(() { - _isHotelNameFocused = _hotelNameFocusNode.hasFocus; - }); + _hotelNameFocusNode.addListener(() { + setState(() { + _isHotelNameFocused = _hotelNameFocusNode.hasFocus; }); - _fromFocusNode.addListener(() { - setState(() { - _fromFocus = _fromFocusNode.hasFocus; - }); + }); + _fromFocusNode.addListener(() { + setState(() { + _fromFocus = _fromFocusNode.hasFocus; }); + }); - _toFocusNode.addListener(() { - setState(() { - _toFocus = _toFocusNode.hasFocus; - }); + _toFocusNode.addListener(() { + setState(() { + _toFocus = _toFocusNode.hasFocus; }); - _dateFocusNode.addListener(() { - setState(() { - _dateFocus = _fromFocusNode.hasFocus; - }); + }); + _dateFocusNode.addListener(() { + setState(() { + _dateFocus = _fromFocusNode.hasFocus; }); + }); - _timeFocusNode.addListener(() { - setState(() { - _timeFocus = _timeFocusNode.hasFocus; - }); + _timeFocusNode.addListener(() { + setState(() { + _timeFocus = _timeFocusNode.hasFocus; }); + }); - _commentsFocusNode.addListener(() { - setState(() { - _commentsFocus = _commentsFocusNode.hasFocus; - }); + _commentsFocusNode.addListener(() { + setState(() { + _commentsFocus = _commentsFocusNode.hasFocus; }); - - + }); _buscommentsController = initController("comments"); _fromController = initController("from"); @@ -137,7 +137,6 @@ class _BusScreenState extends State { _toController.addListener(() => _clearError("to")); _dateController.addListener(() => _clearError("date")); _timeController.addListener(() => _clearError("time")); - } @override @@ -153,7 +152,6 @@ class _BusScreenState extends State { super.dispose(); } - void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { @@ -161,11 +159,12 @@ class _BusScreenState extends State { }); } } + bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["from", "to","date","time"]; + List requiredFields = ["from", "to", "date", "time"]; // Check validation for each field for (String field in requiredFields) { @@ -177,26 +176,22 @@ class _BusScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save accomadationData $busData"); - void handleSave(){ - - print( "Handle Save accomadationData $busData"); - - Map data = busData; + Map data = busData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveBus(busData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } - - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -225,8 +220,10 @@ class _BusScreenState extends State { ), ), Text("Bus Booking List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -244,7 +241,7 @@ class _BusScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -258,7 +255,6 @@ class _BusScreenState extends State { ]; return [ - // ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container @@ -274,10 +270,7 @@ class _BusScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -290,14 +283,9 @@ class _BusScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ) - - + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)) ], ), if (isDesktop) @@ -306,34 +294,32 @@ class _BusScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - + List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['flight_trip_type'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + String? selectedPurpose = + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( @@ -341,37 +327,33 @@ class _BusScreenState extends State { 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 + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } : null, + setState(() { + selectedPurpose = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } + : null, items: dropdownItems, ), - - ), ), ]; } - List _buildSecondRow(bool isDesktop) { - - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -381,9 +363,8 @@ class _BusScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -419,7 +400,6 @@ class _BusScreenState extends State { } return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -436,7 +416,7 @@ class _BusScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: _fromFocusNode, controller: _fromController, style: const TextStyle(fontSize: 12), @@ -446,7 +426,6 @@ class _BusScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -482,7 +461,6 @@ class _BusScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: _toFocusNode, controller: _toController, @@ -528,7 +506,6 @@ class _BusScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( @@ -548,7 +525,6 @@ class _BusScreenState extends State { ), ), ), - ), ), if (errorMessages["date"] != null) ...[ @@ -596,12 +572,11 @@ class _BusScreenState extends State { border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + Icon(Icons.access_time, size: 16, color: Colors.grey), ), ), ), ), - ), ), if (errorMessages["time"] != null) ...[ @@ -613,8 +588,6 @@ class _BusScreenState extends State { ], ], ), - - ]; } @@ -635,7 +608,7 @@ class _BusScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, @@ -644,7 +617,7 @@ class _BusScreenState extends State { keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( - labelText: "Description", + labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -662,7 +635,7 @@ class _BusScreenState extends State { // Close Button ElevatedButton( onPressed: () { - widget.onClose(false);// Close the dialog or screen + widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color @@ -697,4 +670,4 @@ class _BusScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 587716e..029623e 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -7,23 +7,23 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class FlightScreen extends StatefulWidget { - final Map? apiData; final String? loginUser; final Function(bool) onClose; - final Function(Map) onSaveFlight; - final Map? selectedItem; + final Function(Map) onSaveFlight; + final Map? selectedItem; - FlightScreen({ required this.apiData,required this.loginUser, - required this.onClose, required this.onSaveFlight,required this.selectedItem}); + FlightScreen( + {required this.apiData, + required this.loginUser, + required this.onClose, + required this.onSaveFlight, + required this.selectedItem}); @override _FlightScreenState createState() => _FlightScreenState(); - - } - class _FlightScreenState extends State { final GlobalKey _formKey = GlobalKey(); @@ -32,10 +32,18 @@ class _FlightScreenState extends State { String? selectedTripType; Map selectedClasses = {}; // Store class selection for each trip String? selectedvisa_available; - int multiTripRowCount = 1; + int multiTripRowCount = 1; - - List dataHeader = ["_tripType", "_class", "_from", "_to", "_date","_visa", "_time", "_comments"]; + List dataHeader = [ + "_tripType", + "_class", + "_from", + "_to", + "_date", + "_visa", + "_time", + "_comments" + ]; Map focusNodes = {}; Map focusStates = {}; @@ -55,7 +63,6 @@ class _FlightScreenState extends State { // List purposeList = widget.apiData?['flight_trip_type'] ?? []; // selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; - // Get trip type from widget.selectedItem selectedTripType = widget.selectedItem?["trip_type"] as String?; @@ -66,9 +73,6 @@ class _FlightScreenState extends State { selectedTripType = purposeList.first['dropdown_value'] as String?; } - - - _initializeFields(); getRowCount(); @@ -76,7 +80,6 @@ class _FlightScreenState extends State { print("Focus States Keys: ${focusStates.keys.toList()}"); print("Text Controllers Keys: ${textControllers.keys.toList()}"); - for (var key in focusNodes.keys) { _addFocusListener(focusNodes[key]!, (focus) { setState(() { @@ -87,7 +90,6 @@ class _FlightScreenState extends State { handleUpdateField(); - int rowCount = 1; // Default row count for One-way if (selectedTripType == "Roundtrip") { rowCount = 2; // Fixed for Roundtrip @@ -97,25 +99,27 @@ class _FlightScreenState extends State { // Loop through each row and add listeners to clear errors for (int i = 1; i <= rowCount; i++) { - textControllers["_from${i}Controller"]?.addListener(() => _clearError("from_place_$i")); - textControllers["_to${i}Controller"]?.addListener(() => _clearError("to_place_$i")); - textControllers["_date${i}Controller"]?.addListener(() => _clearError("date_$i")); - textControllers["_time${i}Controller"]?.addListener(() => _clearError("time_$i")); + textControllers["_from${i}Controller"] + ?.addListener(() => _clearError("from_place_$i")); + textControllers["_to${i}Controller"] + ?.addListener(() => _clearError("to_place_$i")); + textControllers["_date${i}Controller"] + ?.addListener(() => _clearError("date_$i")); + textControllers["_time${i}Controller"] + ?.addListener(() => _clearError("time_$i")); } - } - int getRowCount() { - if (selectedTripType == "RoundTrip") { - return 2; - } else if (selectedTripType == "Multitrip") { - return multiTripRowCount; - } - return 1; // Default for Oneway + int getRowCount() { + if (selectedTripType == "RoundTrip") { + return 2; + } else if (selectedTripType == "Multitrip") { + return multiTripRowCount; } + return 1; // Default for Oneway + } void _initializeFields() { - print("_initializeFields-----------------"); // Dispose and clear previous controllers and focus nodes @@ -141,14 +145,13 @@ class _FlightScreenState extends State { int rowCount = selectedTripType == "Roundtrip" ? 2 : selectedTripType == "Multitrip" - ? multiTripRowCount - : 1; + ? multiTripRowCount + : 1; // Initialize fields dynamically for (var field in dataHeader) { for (int i = 1; i <= rowCount; i++) { - textControllers["${field}${i}Controller"] = - TextEditingController(); + textControllers["${field}${i}Controller"] = TextEditingController(); focusNodes["${field}${i}FocusNode"] = FocusNode(); focusStates["${field}${i}Focused"] = false; @@ -165,8 +168,6 @@ class _FlightScreenState extends State { } setState(() {}); // Ensure UI updates - - } void addMultiTripRow() { @@ -192,14 +193,10 @@ class _FlightScreenState extends State { } } - - - @override void dispose() { // _tripTypeFocusNode.dispose(); - // Dispose all dynamically created FocusNodes for (var node in focusNodes.values) { node.dispose(); @@ -212,10 +209,7 @@ class _FlightScreenState extends State { super.dispose(); } - - - Map get flightsData{ - + Map get flightsData { List> trips = []; int rowCount = 1; // Default for One-way @@ -226,10 +220,9 @@ class _FlightScreenState extends State { rowCount = multiTripRowCount; // Use dynamic count for Multitrip } - for (int i = 1; i <= rowCount; i++) { trips.add({ - "class": selectedClasses[i] , + "class": selectedClasses[i], "from_place": textControllers["_from${i}Controller"]?.text ?? "", "to_place": textControllers["_to${i}Controller"]?.text ?? "", "date": textControllers["_date${i}Controller"]?.text ?? "", @@ -239,26 +232,26 @@ class _FlightScreenState extends State { }); } - - Map data ={ + Map data = { "trip_type": selectedTripType, - "comments": textControllers["_comments1Controller"]?.text ?? "", - "visa_available": selectedvisa_available, - "created_by": widget.loginUser, - "updated_by": widget.loginUser, - "trips": trips, - }; - + "comments": textControllers["_comments1Controller"]?.text ?? "", + "visa_available": selectedvisa_available, + "created_by": widget.loginUser, + "updated_by": widget.loginUser, + "trips": trips, + }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["flight_id"] != null && widget.selectedItem?["flight_id"] != 0) { + } else if (widget.selectedItem?["flight_id"] != null && + widget.selectedItem?["flight_id"] != 0) { data["flight_id"] = widget.selectedItem!["flight_id"]; } } - return data; + return data; } TextEditingController initController(String key) { @@ -267,17 +260,17 @@ class _FlightScreenState extends State { void handleUpdateField() { if (widget.selectedItem != null) { - textControllers["_comments1Controller"] = initController("comments"); // selectedTripType = widget.selectedItem!["trip_type"] as String?; // selectedvisa_available = widget.selectedItem!["visa_available"].toString(); - if ( widget.selectedItem!["trip_type"] != null) { + if (widget.selectedItem!["trip_type"] != null) { selectedTripType = widget.selectedItem!["trip_type"].toString(); } - if ( widget.selectedItem!["visa_available"] != null) { - selectedvisa_available = widget.selectedItem!["visa_available"].toString(); + if (widget.selectedItem!["visa_available"] != null) { + selectedvisa_available = + widget.selectedItem!["visa_available"].toString(); } // Extract trips from selectedItem @@ -298,10 +291,14 @@ class _FlightScreenState extends State { int index = i + 1; // Use 1-based indexing to match the form selectedClasses[index] = trip["class"].toString(); - textControllers["_from${index}Controller"] = TextEditingController(text: trip["from_place"]); - textControllers["_to${index}Controller"] = TextEditingController(text: trip["to_place"]); - textControllers["_date${index}Controller"] = TextEditingController(text: trip["date"]); - textControllers["_time${index}Controller"] = TextEditingController(text: trip["time"]); + textControllers["_from${index}Controller"] = + TextEditingController(text: trip["from_place"]); + textControllers["_to${index}Controller"] = + TextEditingController(text: trip["to_place"]); + textControllers["_date${index}Controller"] = + TextEditingController(text: trip["date"]); + textControllers["_time${index}Controller"] = + TextEditingController(text: trip["time"]); } print("Selected ITEM - ${widget.selectedItem}"); @@ -352,7 +349,8 @@ class _FlightScreenState extends State { setState(() {}); // Update UI to show error messages - return errorMessages.isEmpty; // Returns true if all required fields are filled + return errorMessages + .isEmpty; // Returns true if all required fields are filled } void removeTrip(int index) { @@ -368,25 +366,26 @@ class _FlightScreenState extends State { textControllers.remove("_date${index}Controller"); textControllers.remove("_time${index}Controller"); - - // Step 2: Shift remaining textControllers keys Map updatedTextControllers = {}; int newIndex = 1; for (int i = 1; i <= multiTripRowCount + 1; i++) { if (i == index) continue; // Skip the deleted one - updatedTextControllers["_from${newIndex}Controller"] = textControllers["_from${i}Controller"]!; - updatedTextControllers["_to${newIndex}Controller"] = textControllers["_to${i}Controller"]!; - updatedTextControllers["_date${newIndex}Controller"] = textControllers["_date${i}Controller"]!; - updatedTextControllers["_time${newIndex}Controller"] = textControllers["_time${i}Controller"]!; + updatedTextControllers["_from${newIndex}Controller"] = + textControllers["_from${i}Controller"]!; + updatedTextControllers["_to${newIndex}Controller"] = + textControllers["_to${i}Controller"]!; + updatedTextControllers["_date${newIndex}Controller"] = + textControllers["_date${i}Controller"]!; + updatedTextControllers["_time${newIndex}Controller"] = + textControllers["_time${i}Controller"]!; newIndex++; } textControllers = updatedTextControllers; - // Shift the selectedClasses map BEFORE removing the index Map updatedClasses = {}; - newIndex = 1; + newIndex = 1; for (int i = 1; i <= selectedClasses.length; i++) { if (i == index) continue; // Skip the one being deleted @@ -402,7 +401,6 @@ class _FlightScreenState extends State { } } - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -431,8 +429,10 @@ class _FlightScreenState extends State { ), ), Text("Flight Booking", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -457,8 +457,7 @@ class _FlightScreenState extends State { // }); // } - - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -467,107 +466,99 @@ class _FlightScreenState extends State { } List> rowBuilders = [ - _builClassType(isDesktop,1), - _buildSecondRow(isDesktop,1) + _builClassType(isDesktop, 1), + _buildSecondRow(isDesktop, 1) ]; List> rowRoundBuilders = [ - _builClassType(isDesktop,1), - _buildSecondRow(isDesktop,1), - _builClassType(isDesktop,2), - _buildSecondRow(isDesktop,2) + _builClassType(isDesktop, 1), + _buildSecondRow(isDesktop, 1), + _builClassType(isDesktop, 2), + _buildSecondRow(isDesktop, 2) ]; - print("Trip Type Selected: $selectedTripType"); return [ - ...buildResponsiveRow(_buildFirstRow(isDesktop)), - // Iterate over rowBuilders based on selectedTripType - if (selectedTripType == "Oneway") + if (selectedTripType == "Oneway") ...rowBuilders.expand((row) => buildResponsiveRow(row)), - if (selectedTripType == "Roundtrip") ...rowRoundBuilders.expand((row) => buildResponsiveRow(row)), - if (selectedTripType == "Multitrip") ...List.generate(multiTripRowCount, (index) { List firstRow = _builClassType(isDesktop, index + 1); List secondRow = _buildSecondRow(isDesktop, index + 1); return [ - ...buildResponsiveRow(firstRow), // Row 1 + ...buildResponsiveRow(firstRow), // Row 1 ...buildResponsiveRow(secondRow), // Row 2 ]; }).expand((row) => row), - - // if (selectedTripType == "Multitrip") // ...List.generate(multiTripRowCount, (index) => // buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1)) // ).expand((row) => row), - - if (selectedTripType == "Multitrip") - Align( - alignment: Alignment.centerRight, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blueAccent, - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.blueAccent, width: 2), + Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueAccent, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Colors.blueAccent, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), ), - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), - ) , - onPressed: () { - setState(() { - multiTripRowCount++; // Increase the row count - }); + onPressed: () { + setState(() { + multiTripRowCount++; // Increase the row count + }); - // Dynamically create controllers and focus nodes for new trip fields - for (var field in dataHeader) { - String keyController = "${field}${multiTripRowCount}Controller"; - String keyFocusNode = "${field}${multiTripRowCount}FocusNode"; - String keyFocusState = "${field}${multiTripRowCount}Focused"; + // Dynamically create controllers and focus nodes for new trip fields + for (var field in dataHeader) { + String keyController = "${field}${multiTripRowCount}Controller"; + String keyFocusNode = "${field}${multiTripRowCount}FocusNode"; + String keyFocusState = "${field}${multiTripRowCount}Focused"; - // Create TextEditingController if it doesn't exist - if (!textControllers.containsKey(keyController)) { - textControllers[keyController] = TextEditingController( + // Create TextEditingController if it doesn't exist + if (!textControllers.containsKey(keyController)) { + textControllers[keyController] = TextEditingController(); + } - ); - } + // Create FocusNode if it doesn't exist + if (!focusNodes.containsKey(keyFocusNode)) { + focusNodes[keyFocusNode] = FocusNode(); - // Create FocusNode if it doesn't exist - if (!focusNodes.containsKey(keyFocusNode)) { - focusNodes[keyFocusNode] = FocusNode(); - - // Attach focus listener for dynamic fields - focusNodes[keyFocusNode]!.addListener(() { - setState(() { - focusStates[keyFocusState] = focusNodes[keyFocusNode]!.hasFocus; + // Attach focus listener for dynamic fields + focusNodes[keyFocusNode]!.addListener(() { + setState(() { + focusStates[keyFocusState] = + focusNodes[keyFocusNode]!.hasFocus; + }); }); - }); + } + + // Initialize focus state + focusStates[keyFocusState] = false; } - // Initialize focus state - focusStates[keyFocusState] = false; - } - - // _initializeFields(); - }, - child: Text("Add Trip",style: TextStyle(fontSize: 10,fontWeight:FontWeight.bold),), + // _initializeFields(); + }, + child: Text( + "Add Trip", + style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold), + ), + ), ), - ), - ...buildResponsiveRow(_buildvisa(isDesktop)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), @@ -579,10 +570,7 @@ class _FlightScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -595,14 +583,9 @@ class _FlightScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ) - - + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)) ], ), if (isDesktop) @@ -611,154 +594,267 @@ class _FlightScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - + 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(); + .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)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } - - return [ - CustomTextFieldWrapper( - // isFocused: _tripTypeFocused, - isFocused: focusStates["_tripType1Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - - child: DropdownButtonFormField( - // focusNode: _tripTypeFocusNode, // Assign the correct focus node - focusNode: focusNodes["_tripType1FocusNode"], - value: selectedTripType, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding - ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedTripType = newValue; - // selectedTripType = "Oneway"; - // Reset `multiTripRowCount` when switching away from Multitrip - if (selectedTripType != "Multitrip") { - multiTripRowCount = 1; - } - errorMessages.clear(); - }); - print("Updating form data: Flight -> trip_type -> $selectedTripType"); - _initializeFields(); - - - // _initializeRows(); - } - : null, - - - items: dropdownItems, - ), - - + CustomTextFieldWrapper( + // isFocused: _tripTypeFocused, + isFocused: focusStates["_tripType1Focused"] ?? false, + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, + child: SizedBox( + height: 40, + child: DropdownButtonFormField( + // focusNode: _tripTypeFocusNode, // Assign the correct focus node + focusNode: focusNodes["_tripType1FocusNode"], + value: selectedTripType, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), + onChanged: purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedTripType = newValue; + // selectedTripType = "Oneway"; + // Reset `multiTripRowCount` when switching away from Multitrip + if (selectedTripType != "Multitrip") { + multiTripRowCount = 1; + } + errorMessages.clear(); + }); + print( + "Updating form data: Flight -> trip_type -> $selectedTripType"); + _initializeFields(); + + // _initializeRows(); + } + : null, + + items: dropdownItems, ), - ]; + ), + ), + ]; } - List _buildDelete(bool isDesktop, int index){ - return [ + // Widget _buildDelete(bool isDesktop, int index) { + // return Container( + // color: Colors.blueAccent, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // "Trip ${index}", + // style: TextStyle( + // fontSize: 14, + // fontWeight: FontWeight.w600, + // color: Color(0xFF575A74), + // ), + // ), + // SizedBox( + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.38 + // : MediaQuery.of(context).size.width * 0.3, + // child: Stack( + // alignment: Alignment.center, // Centers the icon + // children: [ + // Divider( + // color: Color(0xFF8B8FB2), + // thickness: 0.5, + // height: 20, + // ), + // Container( + // // padding: EdgeInsets.all(4), + // color: Colors.white, // Background to avoid overlapping + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Prevents row from taking full width + // children: [ + // Icon(Icons.add_circle_sharp, + // color: Colors.blue, size: 28), + // ], + // ), + // ), + // ], + // ), + // ), + // IconButton( + // onPressed: () { + // removeTrip(index); + // }, + // icon: Icon(Icons.delete), + // color: Colors.red, + // iconSize: 20, + // ) + // ], + // ), + // ); + // } - Align( - alignment: Alignment.center, - child: Text( - "Trip ${index}", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74), - ), - ), - ), - SizedBox( + List _buildDelete(bool isDesktop, int index) { + return [ + Container( + padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.only(left: 10, right: 10), + // color: Colors.white, - width: isDesktop? 1000 : 80, // Ensure full width - child: Divider( + child: Text( + "Trip ${index}", + style: TextStyle( + fontSize: 14, + color: Colors.blueAccent, + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + width: isDesktop + ? MediaQuery.of(context).size.width * 0.58 + : 80, // Ensure full width + child: Stack( + alignment: Alignment.center, // Centers the icon + children: [ + Divider( + color: Color(0xFF8B8FB2), + thickness: 0.5, + height: 20, + ), + Container( + // padding: EdgeInsets.all(4), + color: Colors.white, // Background to avoid overlapping + child: Row( + mainAxisSize: + MainAxisSize.min, // Prevents row from taking full width + children: [ + Icon(Icons.add_circle_sharp, color: Colors.blue, size: 28), + ], + ), + ), + ], + ), + ), - thickness: 1, // Make it more visible - ), - ), - IconButton(onPressed: (){ - removeTrip(index); - }, - icon: Icon(Icons.delete),color: Colors.red,iconSize: 20,) + // SizedBox( + // width: isDesktop + // ? MediaQuery.of(context).size.width * 0.29 + // : MediaQuery.of(context).size.width * 0.3, + // child: Stack( + // alignment: Alignment.center, // Centers the icon + // children: [ + // Divider( + // color: Color(0xFF8B8FB2), + // thickness: 0.5, + // height: 20, + // ), + // Container( + // // padding: EdgeInsets.all(4), + // color: Colors.white, // Background to avoid overlapping + // child: Row( + // mainAxisSize: + // MainAxisSize.min, // Prevents row from taking full width + // children: [ + // Icon(Icons.add_circle_sharp, color: Colors.blue, size: 28), + // ], + // ), + // ), + // ], + // ), + // ), - - ]; + Container( + // color: Colors.white, + // padding: const EdgeInsets.only(left: 10, right: 10), + child: IconButton( + onPressed: () { + removeTrip(index); + }, + icon: Icon(Icons.delete), + color: Colors.blueAccent, + iconSize: 20, + ), + ) + ]; } - List _builClassType(bool isDesktop, int index){ - + List _builClassType(bool isDesktop, int index) { List purposeList = widget.apiData?['flight_class'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedClasses[index] ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + selectedClasses[index] ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ - - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (selectedTripType == "Multitrip") + isDesktop + ? + // SizedBox( + // width: MediaQuery.of(context).size.width * 0.89, + // child: - if (selectedTripType == "Multitrip" ) - isDesktop? - SizedBox( - width: MediaQuery.of(context).size.width * 0.89, - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [..._buildDelete(isDesktop, index)]), - ) - : Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [..._buildDelete(isDesktop, index)]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Spacer(flex: 2), - SizedBox(height: 20), + // _buildDelete(isDesktop, index) + // Spacer(flex: 2), + ..._buildDelete(isDesktop, index) + ]) + // ) + : Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + // _buildDelete(isDesktop, index) + ..._buildDelete(isDesktop, index) + ]), + SizedBox(height: 20), Text( "Class $index *", style: TextStyle( @@ -767,50 +863,44 @@ class _FlightScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - CustomTextFieldWrapper( isFocused: focusStates["_class${index}Focused"] ?? false, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: focusNodes["_class${index}FocusNode"], // focusNode: _tripTypeFocusNode, // Assign the correct focus node // controller: _hotelNameController, - value: selectedClasses[index] , + value: selectedClasses[index], style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedClasses[index] = newValue; - }); + setState(() { + selectedClasses[index] = newValue; + }); - print( selectedClasses[index] ); - - } + print(selectedClasses[index]); + } : null, items: dropdownItems, ), - ), ), - - ], ), ]; } - List _buildSecondRow(bool isDesktop, int index) { - - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -820,9 +910,8 @@ class _FlightScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -833,7 +922,8 @@ class _FlightScreenState extends State { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); - textControllers["_date${index}Controller"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate); + textControllers["_date${index}Controller"]?.text = + DateFormat('yyyy-MM-dd').format(pickedDate); }); } } @@ -860,7 +950,6 @@ class _FlightScreenState extends State { } return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -878,26 +967,22 @@ class _FlightScreenState extends State { // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, isDesktop: isDesktop, child: SizedBox( - height: 40, - child: TextField( - // focusNode: _fromFocusNode, - focusNode: focusNodes["_from${index}FocusNode"], + height: 40, + child: TextField( + // focusNode: _fromFocusNode, + focusNode: focusNodes["_from${index}FocusNode"], controller: textControllers["_from${index}Controller"], - 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), - - ), - ), + 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 (errorMessages["from_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( @@ -905,8 +990,6 @@ class _FlightScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - - ], ), if (isDesktop) @@ -931,19 +1014,18 @@ class _FlightScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: focusNodes["_to${index}FocusNode"], controller: textControllers["_to${index}Controller"], - 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), - ), - ), + 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 (errorMessages["to_place_$index"] != null) ...[ @@ -977,7 +1059,6 @@ class _FlightScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( @@ -998,10 +1079,8 @@ class _FlightScreenState extends State { ), ), ), - ), ), - if (errorMessages["date_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( @@ -1009,10 +1088,6 @@ class _FlightScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - - - - ], ), if (isDesktop) @@ -1052,12 +1127,11 @@ class _FlightScreenState extends State { border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + Icon(Icons.access_time, size: 16, color: Colors.grey), ), ), ), ), - ), ), if (errorMessages["time_$index"] != null) ...[ @@ -1067,14 +1141,11 @@ class _FlightScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - ], ), - ]; } - List _buildThirdRow(bool isDesktop) { return [ Column( @@ -1092,7 +1163,7 @@ class _FlightScreenState extends State { isFocused: focusStates["_comments1Focused"] ?? false, isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_comments1FocusNode"], @@ -1115,33 +1186,31 @@ class _FlightScreenState extends State { ]; } - - - List _buildvisa(bool isDesktop){ - - List visa_available = widget.apiData?['flight_visa_available'] ?? []; + List _buildvisa(bool isDesktop) { + List visa_available = + widget.apiData?['flight_visa_available'] ?? []; // Default selected value - List> dropdownItems = visa_available - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); - selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; + selectedvisa_available ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } - - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1158,9 +1227,11 @@ class _FlightScreenState extends State { // isFocused: _tripTypeFocused, isFocused: focusStates["_visa1Focused"] ?? false, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( // focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: focusNodes["_visa1FocusNode"], @@ -1168,28 +1239,25 @@ class _FlightScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: visa_available.isNotEmpty ? (newValue) { - setState(() { - selectedvisa_available = newValue; - // selectedTripType = "Oneway"; - // Reset `multiTripRowCount` when switching away from Multitrip - }); - print("Updating form data: Flight -> trip_type -> $selectedvisa_available"); + setState(() { + selectedvisa_available = newValue; + // selectedTripType = "Oneway"; + // Reset `multiTripRowCount` when switching away from Multitrip + }); + print( + "Updating form data: Flight -> trip_type -> $selectedvisa_available"); - - // _initializeRows(); - } + // _initializeRows(); + } : null, - items: dropdownItems, ), - - ), ), ], @@ -1197,13 +1265,12 @@ class _FlightScreenState extends State { ]; } - List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { - widget.onClose(false);// Close the dialog or screen + widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 089f515..bf57266 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -14,19 +14,20 @@ import '../../widgets/custom_text_itnerary_sub.dart'; import 'package:http/http.dart' as http; class ForexScreen extends StatefulWidget { - final Map? apiData; final Function(bool) onClose; final Map? selectedItem; final List? apiCountryData; - final Function(Map)onSaveForex; + final Function(Map) onSaveForex; final String? loginUser; - - - ForexScreen({ - required this.onClose, this.apiData, required this.selectedItem, required this.apiCountryData, - required this.onSaveForex, required this.loginUser }); + ForexScreen( + {required this.onClose, + this.apiData, + required this.selectedItem, + required this.apiCountryData, + required this.onSaveForex, + required this.loginUser}); @override _ForexScreenState createState() => _ForexScreenState(); @@ -46,21 +47,39 @@ class _ForexScreenState extends State { Map textControllers = {}; List countryList = []; - - List dataHeader = ["_forexStartDate", "_forexEndDate", "_countries", "_duration", - "_currency","_perdiemAmount", "_transport", "_accomodation", "_telephone", "_otherExpenses", - "_cardNumber", "_currency","_card", "_cash","_checkForex", "_deliveryLocation","_comments"]; + 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 + 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 } } + Map errorMessages = {}; String? selectedCountry; @@ -70,9 +89,8 @@ class _ForexScreenState extends State { String? CalculatedOtherExpenses; String? selectedQuotedAmount; - Map get forexData { - Map data ={ + Map data = { "start_date": textControllers["_forexStartDate"]?.text, "end_date": textControllers["_forexEndDate"]?.text, "country_code": selectedCountry, @@ -84,7 +102,7 @@ class _ForexScreenState extends State { "telephone": textControllers["_telephone"]?.text, "have_card": isChecked ? "1" : "0", "card_number": textControllers["_cardNumber"]?.text, - "deposit_on_card":textControllers["_card"]?.text, + "deposit_on_card": textControllers["_card"]?.text, "deposit_on_cash": textControllers["_cash"]?.text, "delivery_location": textControllers["_deliveryLocation"]?.text, "comments": textControllers["_comments"]?.text, @@ -93,9 +111,11 @@ class _ForexScreenState extends State { }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["forex_id"] != null && widget.selectedItem?["forex_id"] != 0) { + } else if (widget.selectedItem?["forex_id"] != null && + widget.selectedItem?["forex_id"] != 0) { data["forex_id"] = widget.selectedItem!["forex_id"]; } } @@ -112,8 +132,6 @@ class _ForexScreenState extends State { }; } - - Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); @@ -150,19 +168,20 @@ class _ForexScreenState extends State { if (responseData.containsKey("currency") && responseData.containsKey("perdiem_amount") && responseData.containsKey("duration")) { - - setState(() { // Update only if data is valid + setState(() { + // Update only if data is valid selectedCurrency = responseData["currency"] ?? selectedCurrency; - selectedPerdiemAmount = responseData["perdiem_amount"]?.toString() ?? ""; + selectedPerdiemAmount = + responseData["perdiem_amount"]?.toString() ?? ""; selectedDuration = responseData["duration"]?.toString() ?? ""; - selectedQuotedAmount = responseData["perdiem_amount"]?.toString() ?? ""; + selectedQuotedAmount = + responseData["perdiem_amount"]?.toString() ?? ""; }); _onFieldChangedForOthers(); _divideQuotedAmount(); } else { print("Warning: Response does not contain expected fields."); } - } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -176,7 +195,14 @@ class _ForexScreenState extends State { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["start_date", "end_date", "country_code", "deposit_on_card", "deposit_on_cash", "card_number"]; + List requiredFields = [ + "start_date", + "end_date", + "country_code", + "deposit_on_card", + "deposit_on_cash", + "card_number" + ]; // If have_card is "1", then delivery_location is required bool isCardChecked = data["have_card"] == "1"; @@ -194,27 +220,22 @@ class _ForexScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } - void handleSave(){ - - print( "Handle Save forexData $forexData"); - + void handleSave() { + print("Handle Save forexData $forexData"); Map data = forexData; - if (!isValidForexData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveForex(forexData); // Send object to parent } - widget.onClose(false);// Close screen after saving - + widget.onClose(false); // Close screen after saving } - DateTime? _parseDate(String date) { try { return DateFormat("yyyy-MM-dd").parse(date); // Change format if needed @@ -227,9 +248,6 @@ class _ForexScreenState extends State { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } - - - @override void initState() { super.initState(); @@ -257,8 +275,6 @@ class _ForexScreenState extends State { }); }); - - // Add listeners to text fields textControllers["_forexStartDate"]?.addListener(_onFieldChanged); textControllers["_forexEndDate"]?.addListener(_onFieldChanged); @@ -266,36 +282,35 @@ class _ForexScreenState extends State { handleUpdatedField(); } - void handleUpdatedField(){ - + void handleUpdatedField() { // Set the selected value if available if (widget.selectedItem != null) { - print("UPDATAED SELECTION"); + print("UPDATAED SELECTION"); - textControllers["_forexStartDate"] = initController("start_date"); - textControllers["_forexEndDate"] = initController("end_date"); - textControllers["_transport"] = initController("transport"); - textControllers["_accomodation"] = initController("accommodation"); - textControllers["_telephone"] = initController("telephone"); - textControllers["_cardNumber"] = initController("card_number"); - textControllers["_card"] = initController("deposit_on_card"); - textControllers["_cash"]= initController("deposit_on_cash"); - textControllers["_deliveryLocation"]= initController("delivery_location"); - textControllers["_comments"]= initController("comments"); + textControllers["_forexStartDate"] = initController("start_date"); + textControllers["_forexEndDate"] = initController("end_date"); + textControllers["_transport"] = initController("transport"); + textControllers["_accomodation"] = initController("accommodation"); + textControllers["_telephone"] = initController("telephone"); + textControllers["_cardNumber"] = initController("card_number"); + textControllers["_card"] = initController("deposit_on_card"); + textControllers["_cash"] = initController("deposit_on_cash"); + textControllers["_deliveryLocation"] = + initController("delivery_location"); + textControllers["_comments"] = initController("comments"); + // Set dropdown values + selectedCountry = widget.selectedItem!["country_code"] as String?; + selectedCurrency = widget.selectedItem!["currency"] as String?; + selectedDuration = widget.selectedItem!["duration"] as String?; + selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?; + isChecked = + widget.selectedItem!["have_card"] == "1"; // Convert string to bool + _onFieldChangedForOthers(); + setState(() {}); // Update the UI - // Set dropdown values - selectedCountry = widget.selectedItem!["country_code"] as String?; - selectedCurrency = widget.selectedItem!["currency"] as String?; - selectedDuration = widget.selectedItem!["duration"] as String?; - selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?; - isChecked = widget.selectedItem!["have_card"] == "1"; // Convert string to bool - _onFieldChangedForOthers(); - setState(() {}); // Update the UI - - // // Calculate other expenses (if applicable) - // CalculatedOtherExpenses = calculateOtherExpenses(); - + // // Calculate other expenses (if applicable) + // CalculatedOtherExpenses = calculateOtherExpenses(); } } @@ -323,6 +338,7 @@ class _ForexScreenState extends State { postgetForexData(getForexData); } } + // Handle dropdown change void _onCountryChanged(String? newCountry) { setState(() { @@ -334,14 +350,17 @@ class _ForexScreenState extends State { } } - // _____________ End Forex Details ______________- + // _____________ 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; - double calclateVal = (transport + accommodation + telephone) ; + double transport = + double.tryParse(textControllers["_transport"]?.text ?? "0") ?? 0; + double accommodation = + double.tryParse(textControllers["_accomodation"]?.text ?? "0") ?? 0; + double telephone = + double.tryParse(textControllers["_telephone"]?.text ?? "0") ?? 0; + double calclateVal = (transport + accommodation + telephone); CalculatedOtherExpenses = (calclateVal).toStringAsFixed(2); @@ -349,19 +368,20 @@ class _ForexScreenState extends State { double perdiemAmount = double.tryParse(selectedPerdiemAmount ?? "0") ?? 0; print("calclateVal - $calclateVal"); - selectedQuotedAmount = ((perdiemAmount + calclateVal).toString() ?? 0) as String?; + selectedQuotedAmount = + ((perdiemAmount + calclateVal).toString() ?? 0) as String?; }); _divideQuotedAmount(); errorMessages.clear(); } - void _divideQuotedAmount(){ - - int? quotedAmount = int.tryParse(selectedQuotedAmount!); + void _divideQuotedAmount() { + int? quotedAmount = int.tryParse(selectedQuotedAmount!); print("DIVIDREFD - $selectedQuotedAmount -$quotedAmount"); if (quotedAmount != null) { - fifteenPercent = (quotedAmount * 15) ~/ 100; // Calculate 15% (integer division) - remainingAmount = quotedAmount - fifteenPercent; // Subtract from total + fifteenPercent = + (quotedAmount * 15) ~/ 100; // Calculate 15% (integer division) + remainingAmount = quotedAmount - fifteenPercent; // Subtract from total textControllers["_cash"]?.text = fifteenPercent.toString(); textControllers["_card"]?.text = remainingAmount.toString(); @@ -370,21 +390,18 @@ class _ForexScreenState extends State { } else { print("Invalid number format in selectedQuotedAmount"); } - } void _validateCardAmount(String value) { - print("_validateCardAmount - $value - $remainingAmount"); - - int? enteredAmount = int.tryParse(value) ; - int? cashAmount = int.tryParse(textControllers["_cash"]!.text ?? "0"); - int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0"); + int? enteredAmount = int.tryParse(value); + int? cashAmount = int.tryParse(textControllers["_cash"]!.text ?? "0"); + int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0"); int? calculateAmnt = cashAmount! + enteredAmount!; - print("CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount"); - + print( + "CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount"); if (enteredAmount == null || calculateAmnt > qouteAmount!) { errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount"; @@ -396,9 +413,7 @@ class _ForexScreenState extends State { setState(() {}); } - void _validateCashAmount(String value) { - print("_validateCashAmount - $value - $fifteenPercent"); int? enteredAmount = int.tryParse(value); @@ -413,7 +428,6 @@ class _ForexScreenState extends State { setState(() {}); } - @override void dispose() { for (var node in focusNodes.values) { @@ -427,17 +441,18 @@ class _ForexScreenState extends State { super.dispose(); } - void _validateDates() { - print("VALiDATING DATES"); - DateTime? startDate = _parseDate(textControllers["_forexStartDate"]?.text ?? ""); - DateTime? endDate = _parseDate(textControllers["_forexEndDate"]?.text ?? ""); + DateTime? startDate = + _parseDate(textControllers["_forexStartDate"]?.text ?? ""); + DateTime? endDate = + _parseDate(textControllers["_forexEndDate"]?.text ?? ""); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { - errorMessages["end_date"] = "End date cannot be earlier than start date"; + errorMessages["end_date"] = + "End date cannot be earlier than start date"; }); } else { setState(() { @@ -446,7 +461,6 @@ class _ForexScreenState extends State { } } - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -475,8 +489,10 @@ class _ForexScreenState extends State { ), ), Text("Forex List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -494,11 +510,9 @@ class _ForexScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ - - isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; @@ -510,34 +524,41 @@ class _ForexScreenState extends State { // _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,), + Text( + "Forex Details", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74)), + ), + SizedBox( + height: 8, + ), Divider(), - SizedBox(height: 8,), + SizedBox( + height: 8, + ), ...buildResponsiveRow(_builClassType(isDesktop)), - SizedBox(height: 8,), + SizedBox( + height: 8, + ), Divider(), - SizedBox(height: 28,), + SizedBox( + height: 28, + ), ...buildResponsiveRow(_buildSecondRow(isDesktop)), ...buildResponsiveRow(_buildCardDetailsRow(isDesktop)), @@ -556,21 +577,18 @@ class _ForexScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - DateTime? _selectedCheckOutDate; DateTime? _selectedEndDate; - 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) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -580,22 +598,24 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - textControllers["_forexStartDate"]?.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); DateTime? pickedDate = await showDatePicker( context: context, - initialDate: _selectedEndDate != null && _selectedEndDate!.isAfter(today) - ? _selectedEndDate! - : today, + initialDate: + _selectedEndDate != null && _selectedEndDate!.isAfter(today) + ? _selectedEndDate! + : today, firstDate: today, lastDate: DateTime(2100), ); @@ -603,21 +623,21 @@ class _ForexScreenState extends State { if (pickedDate != null && pickedDate != _selectedEndDate) { setState(() { _selectedEndDate = pickedDate; - textControllers["_forexEndDate"]?.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 + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String }; // Extract only country codes for processing @@ -625,7 +645,6 @@ class _ForexScreenState extends State { selectedCountry ??= null; - // // Set default selected value // if (selectedCountry == null && countryCodes.isNotEmpty) { // selectedCountry = countryCodes.first; @@ -650,7 +669,6 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( // onTap: () async{ // _selectCheckOutDate(context); @@ -660,12 +678,17 @@ class _ForexScreenState extends State { await _selectCheckOutDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { - DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text); - DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text); + DateTime? startDate = + _parseDate(textControllers["_forexStartDate"]!.text); + DateTime? endDate = + _parseDate(textControllers["_forexEndDate"]!.text); - if (startDate != null && endDate != null && endDate.isBefore(startDate)) { + if (startDate != null && + endDate != null && + endDate.isBefore(startDate)) { setState(() { - errorMessages["end_date"] = "End date cannot be earlier than start date"; + errorMessages["end_date"] = + "End date cannot be earlier than start date"; }); } else { setState(() { @@ -681,7 +704,8 @@ class _ForexScreenState extends State { style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", - labelStyle: const TextStyle(fontSize: 12, color: Colors.grey), + labelStyle: + const TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16), @@ -691,7 +715,6 @@ class _ForexScreenState extends State { ), ), ), - ), ), if (errorMessages["start_date"] != null) ...[ @@ -721,25 +744,27 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: focusStates["_forexEndDate"] ?? false, - isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( // onTap: () => _selectForexEndDate(context), onTap: () async { await _selectForexEndDate(context); if (textControllers["_forexEndDate"]!.text.isNotEmpty) { - DateTime? startDate = _parseDate(textControllers["_forexStartDate"]!.text); - DateTime? endDate = _parseDate(textControllers["_forexEndDate"]!.text); + DateTime? startDate = + _parseDate(textControllers["_forexStartDate"]!.text); + DateTime? endDate = + _parseDate(textControllers["_forexEndDate"]!.text); - if (startDate != null && endDate != null && endDate.isBefore(startDate)) { + if (startDate != null && + endDate != null && + endDate.isBefore(startDate)) { setState(() { - errorMessages["end_date"] = "End date cannot be earlier than start date"; + errorMessages["end_date"] = + "End date cannot be earlier than start date"; }); } else { setState(() { @@ -766,7 +791,6 @@ class _ForexScreenState extends State { ), ), ), - ), ), if (errorMessages["end_date"] != null) ...[ @@ -779,9 +803,12 @@ class _ForexScreenState extends State { ], ], ), - - - if (isDesktop)Spacer() else SizedBox(height: 8,), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -796,13 +823,12 @@ class _ForexScreenState extends State { CustomTextFieldForexWrapper( isFocused: focusStates["_countries"] ?? false, isDesktop: isDesktop, - child: SizedBox( height: 40, child: DropdownSearch( selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality + showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", @@ -810,15 +836,17 @@ class _ForexScreenState extends State { ), ), ), - items: countryMap.values.toList(), - + items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1,), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + ), ), ), - dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item + dropdownBuilder: (context, selectedItem) => Align( + // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", @@ -833,7 +861,6 @@ class _ForexScreenState extends State { .key; _onCountryChanged(selectedCountry); }); - }, ), ), @@ -847,19 +874,11 @@ class _ForexScreenState extends State { ], ], ), - ]; } - - - - List _builClassType(bool isDesktop){ - - + List _builClassType(bool isDesktop) { return [ - - // if (isDesktop) Spacer() else SizedBox( // height: 8, // ), @@ -877,17 +896,19 @@ class _ForexScreenState extends State { CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_durationFocused"] ?? false, isDesktop: isDesktop, - color:Colors.transparent, + color: Colors.transparent, child: SizedBox( height: 40, - child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "dur", // selectedDuration?.isNotEmpty == true ? selectedDuration! : "Duration", selectedDuration ?? "Duration", - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -904,7 +925,12 @@ class _ForexScreenState extends State { // if (isDesktop)SizedBox(width: 8,) else SizedBox( // height: 8, // ), - if (isDesktop)Spacer() else SizedBox(height: 8,), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -925,21 +951,27 @@ class _ForexScreenState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - // "cur", + // "cur", // "${selectedCurrency}", selectedCurrency ?? "Currency", // selectedCurrency?.isNotEmpty == true ? selectedCurrency! : "Currency", - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), - + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), ), ), ), ), - ], ), // if (isDesktop)SizedBox(width: 8,) else SizedBox(height: 8,), - if (isDesktop)Spacer() else SizedBox(height: 8,), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -954,17 +986,19 @@ class _ForexScreenState extends State { CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, - color:Colors.transparent, + color: Colors.transparent, child: SizedBox( height: 40, - child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "amo", selectedPerdiemAmount ?? "Amount", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -978,7 +1012,12 @@ class _ForexScreenState extends State { ), ], ), - if (isDesktop)Spacer() else SizedBox(height: 8,), + if (isDesktop) + Spacer() + else + SizedBox( + height: 8, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -994,18 +1033,19 @@ class _ForexScreenState extends State { CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_perdiemAmount"] ?? false, isDesktop: isDesktop, - color:Colors.transparent, + color: Colors.transparent, child: SizedBox( height: 40, - child: Padding( padding: const EdgeInsets.all(8.0), child: Text( // "amo", selectedQuotedAmount ?? "0", // selectedPerdiemAmount?.isNotEmpty == true ? selectedPerdiemAmount! : "Amount", - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), - + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), ), ), ), @@ -1016,12 +1056,8 @@ class _ForexScreenState extends State { ]; } - List _buildSecondRow(bool isDesktop) { - - return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1038,13 +1074,14 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: focusNodes["_transport"], controller: textControllers["_transport"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -1081,14 +1118,14 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: focusNodes["_accomodation"], controller: textControllers["_accomodation"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -1125,13 +1162,14 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: focusNodes["_telephone"], controller: textControllers["_telephone"], onChanged: (value) => _onFieldChangedForOthers(), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -1140,7 +1178,6 @@ class _ForexScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -1167,17 +1204,19 @@ class _ForexScreenState extends State { CustomTextFieldItnerarySubWrapper( isFocused: false, isDesktop: isDesktop, - color:Colors.transparent, + color: Colors.transparent, child: SizedBox( height: 40, - child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - CalculatedOtherExpenses?? "0", + CalculatedOtherExpenses ?? "0", // focusNode: _toFocusNode, // controller: _toController, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), // decoration: const InputDecoration( // labelText: "To", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), @@ -1191,35 +1230,33 @@ class _ForexScreenState extends State { ), ], ), - - ]; } List _buildCardDetailsRow(bool isDesktop) { - List purposeList = widget.apiData?['flight_class'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_value'], - child: Text(item['dropdown_value']), - )).toList(); + .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)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; + String? selectedPurpose = + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ - - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1236,22 +1273,21 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: focusNodes["_cash"], controller: textControllers["_cash"], - style: const TextStyle(fontSize: 12), - keyboardType: TextInputType.number, - onChanged: (value) { - _validateCashAmount(value); // Call validation when text changes - }, + keyboardType: TextInputType.number, + onChanged: (value) { + _validateCashAmount( + value); // Call validation when text changes + }, decoration: const InputDecoration( labelText: "Cash", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -1274,7 +1310,6 @@ class _ForexScreenState extends State { height: 8, ), - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1291,14 +1326,14 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: focusNodes["_card"], controller: textControllers["_card"], style: const TextStyle(fontSize: 12), keyboardType: TextInputType.number, onChanged: (value) { - _validateCardAmount(value); // Call validation when text changes + _validateCardAmount( + value); // Call validation when text changes }, decoration: const InputDecoration( labelText: "Card", @@ -1313,19 +1348,13 @@ class _ForexScreenState extends State { if (errorMessages["deposit_on_card"] != null) ...[ SizedBox(height: 5), // Space before error message Text( - errorMessages["deposit_on_card"] !, + errorMessages["deposit_on_card"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], - - - ], ), - - - if (isDesktop) Spacer() else @@ -1389,7 +1418,7 @@ class _ForexScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: focusNodes["_cardNumber"], controller: textControllers["_cardNumber"], style: const TextStyle(fontSize: 12), @@ -1399,7 +1428,6 @@ class _ForexScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -1413,59 +1441,58 @@ class _ForexScreenState extends State { ], ], ), - ]; } List _buildThirdRow(bool isDesktop) { return [ - isChecked? - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Delivery Location", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - 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: focusNodes["_deliveryLocation"], - controller: textControllers["_deliveryLocation"], - maxLines: 3, - keyboardType: TextInputType.multiline, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - labelText: "Location", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(vertical: 4), - ), - ), - ), - if (errorMessages["delivery_location"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], - ) - :SizedBox.shrink() + isChecked + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Delivery Location", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: focusStates["_deliveryLocation"] ?? + false, // Dropdown doesn't use focus + isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.464 + : MediaQuery.of(context).size.width * 0.66, + child: TextField( + focusNode: focusNodes["_deliveryLocation"], + controller: textControllers["_deliveryLocation"], + maxLines: 3, + keyboardType: TextInputType.multiline, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + labelText: "Location", + labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(vertical: 4), + ), + ), + ), + if (errorMessages["delivery_location"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], + ) + : SizedBox.shrink() ]; } - List _buildCommetsRow(bool isDesktop) { return [ Column( @@ -1480,10 +1507,11 @@ class _ForexScreenState extends State { ), SizedBox(height: 5), CustomTextFieldWrapper( - isFocused: focusStates["_comments"] ?? false, // Dropdown doesn't use focus + 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.464 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: focusNodes["_comments"], @@ -1505,7 +1533,6 @@ class _ForexScreenState extends State { ]; } - List _buildFprexCard(bool isDesktop) { return [ Row( @@ -1525,7 +1552,10 @@ class _ForexScreenState extends State { ), Text( "Check If You Don't Have a forex Account", - style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), ), ], ) @@ -1556,7 +1586,7 @@ class _ForexScreenState extends State { // Save Changes Button ElevatedButton( onPressed: () { - handleSave(); + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save @@ -1572,4 +1602,4 @@ class _ForexScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/insurance.dart b/lib/Screens/itnerary/insurance.dart index 20aa8dc..9d0e7a1 100644 --- a/lib/Screens/itnerary/insurance.dart +++ b/lib/Screens/itnerary/insurance.dart @@ -6,17 +6,18 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class InsuranceScreen extends StatefulWidget { - final Map? apiData; final Function(bool) onClose; final Function(Map) onSaveInsurance; - final Map? selectedItem; + final Map? selectedItem; final String? loginUser; - - InsuranceScreen({ - required this.onClose, required this.apiData, required this.onSaveInsurance, - required this.selectedItem,required this.loginUser}); + InsuranceScreen( + {required this.onClose, + required this.apiData, + required this.onSaveInsurance, + required this.selectedItem, + required this.loginUser}); @override _InsuranceScreenState createState() => _InsuranceScreenState(); @@ -33,72 +34,79 @@ class _InsuranceScreenState extends State { final FocusNode _dateFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _startdateController = TextEditingController(); late TextEditingController _endDateController = TextEditingController(); - late TextEditingController _insuranceCommentsController = TextEditingController(); + late TextEditingController _insuranceCommentsController = + TextEditingController(); bool _isHotelNameFocused = false; bool _dateFocus = false; bool _commentsFocus = false; - String? selectedTripType; String? selectedInsuranceType; Map errorMessages = {}; - Map get InsuranceData{ + Map get InsuranceData { Map data = { - "type_of_insurance": selectedInsuranceType, "start_date": _startdateController.text, "end_date": _endDateController.text, "comments": _insuranceCommentsController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, - }; + }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) { + } else if (widget.selectedItem?["insurance_id"] != null && + widget.selectedItem?["insurance_id"] != 0) { data["insurance_id"] = widget.selectedItem!["insurance_id"]; } } - return data; - } - + return data; + } @override void initState() { super.initState(); - _hotelNameFocusNode.addListener(() { - setState(() {_isHotelNameFocused = _hotelNameFocusNode.hasFocus;});}); - _dateFocusNode.addListener(() { - setState(() {_dateFocus = _fromFocusNode.hasFocus;});}); - _commentsFocusNode.addListener(() { - setState(() {_commentsFocus = _commentsFocusNode.hasFocus;});}); - + _hotelNameFocusNode.addListener(() { + setState(() { + _isHotelNameFocused = _hotelNameFocusNode.hasFocus; + }); + }); + _dateFocusNode.addListener(() { + setState(() { + _dateFocus = _fromFocusNode.hasFocus; + }); + }); + _commentsFocusNode.addListener(() { + setState(() { + _commentsFocus = _commentsFocusNode.hasFocus; + }); + }); _insuranceCommentsController = TextEditingController(text: widget.selectedItem?["comments"] ?? ""); _startdateController = TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); _endDateController = - TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); + TextEditingController(text: widget.selectedItem?['end_date'] ?? ""); // Set the selected value if available - if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) { - selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString(); + if (widget.selectedItem != null && + widget.selectedItem!["type_of_insurance"] != null) { + selectedInsuranceType = + widget.selectedItem!["type_of_insurance"].toString(); } - } - void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { @@ -107,15 +115,15 @@ class _InsuranceScreenState extends State { }); } - - - - bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["type_of_insurance", "start_date","end_date"]; + List requiredFields = [ + "type_of_insurance", + "start_date", + "end_date" + ]; // Check validation for each field for (String field in requiredFields) { @@ -127,22 +135,20 @@ class _InsuranceScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save InsuranceData $InsuranceData"); - void handleSave(){ - - print( "Handle Save InsuranceData $InsuranceData"); - - Map data = InsuranceData; + Map data = InsuranceData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveInsurance(InsuranceData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } DateTime? _parseDate(String date) { @@ -153,9 +159,6 @@ class _InsuranceScreenState extends State { } } - - - @override void dispose() { _tripTypeFocusNode.dispose(); @@ -165,8 +168,6 @@ class _InsuranceScreenState extends State { super.dispose(); } - - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -195,8 +196,10 @@ class _InsuranceScreenState extends State { ), ), Text("Insurance Booking List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -214,7 +217,7 @@ class _InsuranceScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -228,7 +231,6 @@ class _InsuranceScreenState extends State { ]; return [ - ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container @@ -244,10 +246,7 @@ class _InsuranceScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -260,14 +259,9 @@ class _InsuranceScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ) - - + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)) ], ), if (isDesktop) @@ -276,81 +270,74 @@ class _InsuranceScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - - List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; + List _buildTripType(bool isDesktop) { + List purposeList = + widget.apiData?['insurance_type_of_insurance'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + selectedInsuranceType ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: _tripTypeFocusNode, // Assign the correct focus node value: selectedInsuranceType, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedInsuranceType = newValue; - if (selectedInsuranceType!.isNotEmpty) { - errorMessages.remove("type_of_insurance"); - } + setState(() { + selectedInsuranceType = newValue; + if (selectedInsuranceType!.isNotEmpty) { + errorMessages.remove("type_of_insurance"); + } + }); - }); - - print(selectedInsuranceType); - - } + print(selectedInsuranceType); + } : null, items: dropdownItems, ), - - ), ), ]; } - - List _buildSecondRow(bool isDesktop) { - - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -360,9 +347,8 @@ class _InsuranceScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -372,21 +358,20 @@ class _InsuranceScreenState extends State { if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; - _startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); + _startdateController.text = + DateFormat('yyyy-MM-dd').format(pickedDate); }); } } - Future _selectEndCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -402,7 +387,6 @@ class _InsuranceScreenState extends State { } return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -417,15 +401,18 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: GestureDetector( - onTap: () async{ + onTap: () async { await _selectCheckOutDate(context); - if(_startdateController.text.isNotEmpty){ + if (_startdateController.text.isNotEmpty) { setState(() { - errorMessages.remove("start_date"); // Removes the key completely + errorMessages + .remove("start_date"); // Removes the key completely }); } }, @@ -446,11 +433,8 @@ class _InsuranceScreenState extends State { ), ), ), - ), ), - - if (errorMessages["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( @@ -458,7 +442,7 @@ class _InsuranceScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - ], + ], ), if (isDesktop) Spacer() @@ -466,7 +450,6 @@ class _InsuranceScreenState extends State { SizedBox( height: 8, ), - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -481,9 +464,11 @@ class _InsuranceScreenState extends State { CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: GestureDetector( onTap: () async { await _selectEndCheckOutDate(context); @@ -492,9 +477,12 @@ class _InsuranceScreenState extends State { DateTime? startDate = _parseDate(_startdateController.text); DateTime? endDate = _parseDate(_endDateController.text); - if (startDate != null && endDate != null && endDate.isBefore(startDate)) { + if (startDate != null && + endDate != null && + endDate.isBefore(startDate)) { setState(() { - errorMessages["end_date"] = "End date cannot be earlier than start date"; + errorMessages["end_date"] = + "End date cannot be earlier than start date"; }); } else { setState(() { @@ -503,7 +491,6 @@ class _InsuranceScreenState extends State { } } }, - child: AbsorbPointer( child: TextField( focusNode: _dateFocusNode, @@ -521,7 +508,6 @@ class _InsuranceScreenState extends State { ), ), ), - ), ), if (errorMessages["end_date"] != null) ...[ @@ -532,7 +518,6 @@ class _InsuranceScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - ], ), if (isDesktop) @@ -541,8 +526,6 @@ class _InsuranceScreenState extends State { SizedBox( height: 8, ), - - ]; } @@ -563,7 +546,7 @@ class _InsuranceScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, @@ -609,7 +592,7 @@ class _InsuranceScreenState extends State { // Save Changes Button ElevatedButton( onPressed: () { - handleSave(); + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save @@ -625,4 +608,4 @@ class _InsuranceScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/miscellaneous.dart b/lib/Screens/itnerary/miscellaneous.dart index 0d13e8d..c41beff 100644 --- a/lib/Screens/itnerary/miscellaneous.dart +++ b/lib/Screens/itnerary/miscellaneous.dart @@ -6,7 +6,6 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class MiscellaneousScreen extends StatefulWidget { - final Map? apiData; final Function(bool) onClose; final Function(Map) onSaveMiscellaneous; @@ -14,9 +13,13 @@ class MiscellaneousScreen extends StatefulWidget { final int? selectedIndex; final String? loginUser; - MiscellaneousScreen({ - required this.onClose, required this.apiData, required this.onSaveMiscellaneous, - this.selectedItem, this.selectedIndex,required this.loginUser}); + MiscellaneousScreen( + {required this.onClose, + required this.apiData, + required this.onSaveMiscellaneous, + this.selectedItem, + this.selectedIndex, + required this.loginUser}); @override _MiscellaneousScreenState createState() => _MiscellaneousScreenState(); @@ -49,14 +52,14 @@ class _MiscellaneousScreenState extends State { "comments": _commentsController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, - }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - - } else if (widget.selectedItem?["miscellaneous_id"] != null && widget.selectedItem?["miscellaneous_id"] != 0) { + } else if (widget.selectedItem?["miscellaneous_id"] != null && + widget.selectedItem?["miscellaneous_id"] != 0) { data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"]; } } @@ -64,9 +67,6 @@ class _MiscellaneousScreenState extends State { return data; } - - - @override void initState() { super.initState(); @@ -90,13 +90,12 @@ class _MiscellaneousScreenState extends State { TextEditingController(text: widget.selectedItem?["comments"] ?? ""); // Set the selected value if available - if (widget.selectedItem != null && widget.selectedItem!["special_request"] != null) { + if (widget.selectedItem != null && + widget.selectedItem!["special_request"] != null) { selectedSpecialType = widget.selectedItem!["special_request"].toString(); } - } - @override void dispose() { _tripTypeFocusNode.dispose(); @@ -105,16 +104,12 @@ class _MiscellaneousScreenState extends State { super.dispose(); } - - - bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = ["special_request", "comments"]; - // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { @@ -125,30 +120,26 @@ class _MiscellaneousScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save miscellaneousData $miscellaneousData"); - - void handleSave(){ - - print( "Handle Save miscellaneousData $miscellaneousData"); - - Map data = miscellaneousData; + Map data = miscellaneousData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving // Clear only if this is a new entry // if (widget.selectedItem == null) { // _commentsController.clear(); // } } - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -178,8 +169,10 @@ class _MiscellaneousScreenState extends State { ), ), Text("Miscellaneous Booking List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -197,7 +190,7 @@ class _MiscellaneousScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -205,9 +198,7 @@ class _MiscellaneousScreenState extends State { ]; } - return [ - ...buildResponsiveRow(_buildFirstRow(isDesktop)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), @@ -220,10 +211,7 @@ class _MiscellaneousScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -236,14 +224,9 @@ class _MiscellaneousScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ) - - + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)) ], ), if (isDesktop) @@ -252,66 +235,64 @@ class _MiscellaneousScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - - List purposeList = widget.apiData?['miscellaneous_special_request'] ?? []; + List _buildTripType(bool isDesktop) { + List purposeList = + widget.apiData?['miscellaneous_special_request'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedSpecialType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options"; - + selectedSpecialType ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options"; return [ CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: _tripTypeFocusNode, // Assign the correct focus node value: selectedSpecialType, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedSpecialType = newValue; - }); + setState(() { + selectedSpecialType = newValue; + }); - print(selectedSpecialType); - - } + print(selectedSpecialType); + } : null, items: dropdownItems, ), - - ), ), if (errorMessages["special_request"] != null) ...[ @@ -324,8 +305,6 @@ class _MiscellaneousScreenState extends State { ]; } - - List _buildThirdRow(bool isDesktop) { return [ Column( @@ -343,7 +322,7 @@ class _MiscellaneousScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, @@ -352,7 +331,7 @@ class _MiscellaneousScreenState extends State { keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( - labelText: "Description", + labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -377,9 +356,8 @@ class _MiscellaneousScreenState extends State { // Close Button ElevatedButton( onPressed: () { - _commentsController.clear(); - widget.onClose(false);// Close the dialog or screen + widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color @@ -398,7 +376,7 @@ class _MiscellaneousScreenState extends State { // Save Changes Button ElevatedButton( onPressed: () { - handleSave(); + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save @@ -414,4 +392,4 @@ class _MiscellaneousScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/taxi.dart b/lib/Screens/itnerary/taxi.dart index 023ffd7..66d3714 100644 --- a/lib/Screens/itnerary/taxi.dart +++ b/lib/Screens/itnerary/taxi.dart @@ -9,13 +9,16 @@ import '../../widgets/custom_text_itnerary_sub.dart'; class TaxiScreen extends StatefulWidget { final Map? apiData; final Function(bool) onClose; - final Function(Map) onSavetaxi; - final Map? selectedItem; - final String? loginUser; + final Function(Map) onSavetaxi; + final Map? selectedItem; + final String? loginUser; - TaxiScreen({ - required this.onClose, this.apiData, required this.onSavetaxi, - required this.selectedItem,required this.loginUser}); + TaxiScreen( + {required this.onClose, + this.apiData, + required this.onSavetaxi, + required this.selectedItem, + required this.loginUser}); @override _TaxiScreenState createState() => _TaxiScreenState(); @@ -55,11 +58,8 @@ class _TaxiScreenState extends State { String? selectedReqTaxi; String? selectedCarType; - - Map get taxiData { - Map data ={ - - + Map get taxiData { + Map data = { "destination_city": _destinationController.text, "date": _dateController.text, "time": _timeController.text, @@ -72,13 +72,14 @@ class _TaxiScreenState extends State { "updated_by": widget.loginUser, // "updated_on": , // "updated_by": , - }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["taxi_id"] != null && widget.selectedItem?["taxi_id"] != 0) { + } else if (widget.selectedItem?["taxi_id"] != null && + widget.selectedItem?["taxi_id"] != 0) { data["taxi_id"] = widget.selectedItem!["taxi_id"]; } } @@ -94,17 +95,16 @@ class _TaxiScreenState extends State { void initState() { super.initState(); - - _addFocusListener(_destinationFocusNode, (focus) => _destinationFocus = focus); + _addFocusListener( + _destinationFocusNode, (focus) => _destinationFocus = focus); _addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus); _addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus); - _addFocusListener(_numPassengerFocusNode, (focus) => _numPassengerFocus = focus); + _addFocusListener( + _numPassengerFocusNode, (focus) => _numPassengerFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); - - _destinationController = initController("destination_city"); _dateController = initController("date"); _timeController = initController("time"); @@ -112,14 +112,15 @@ class _TaxiScreenState extends State { _numPassengerController = initController("no_of_passengers"); _taxiCommentsController = initController("comments"); - // Set the selected value if available - if (widget.selectedItem != null && widget.selectedItem!["car_required_for"] != null) { + if (widget.selectedItem != null && + widget.selectedItem!["car_required_for"] != null) { selectedReqTaxi = widget.selectedItem!["car_required_for"].toString(); } // Set the selected value if available - if (widget.selectedItem != null && widget.selectedItem!["car_type"] != null) { + if (widget.selectedItem != null && + widget.selectedItem!["car_type"] != null) { selectedCarType = widget.selectedItem!["car_type"].toString(); } @@ -128,8 +129,6 @@ class _TaxiScreenState extends State { _dateController.addListener(() => _clearError("date")); _timeController.addListener(() => _clearError("time")); _numPassengerController.addListener(() => _clearError("no_of_passengers")); - - } void _addFocusListener(FocusNode node, Function(bool) updateState) { @@ -140,8 +139,6 @@ class _TaxiScreenState extends State { }); } - - @override void dispose() { _destinationFocusNode.dispose(); @@ -153,7 +150,6 @@ class _TaxiScreenState extends State { super.dispose(); } - void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { @@ -162,12 +158,17 @@ class _TaxiScreenState extends State { } } - bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["destination_city", "location_of_pickup","no_of_passengers","date","time"]; + List requiredFields = [ + "destination_city", + "location_of_pickup", + "no_of_passengers", + "date", + "time" + ]; // Check validation for each field for (String field in requiredFields) { @@ -179,27 +180,22 @@ class _TaxiScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save taxiData $taxiData"); - void handleSave(){ - - print( "Handle Save taxiData $taxiData"); - - Map data = taxiData; + Map data = taxiData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSavetaxi(taxiData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } - - - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -228,8 +224,10 @@ class _TaxiScreenState extends State { ), ), Text("Taxi Booking List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -247,7 +245,7 @@ class _TaxiScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -261,7 +259,6 @@ class _TaxiScreenState extends State { ]; return [ - // Iterate over rowBuilders and wrap each in a responsive container ...rowBuilders.expand((row) => buildResponsiveRow(row)), @@ -277,30 +274,29 @@ class _TaxiScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - List purposeList = widget.apiData?['taxt_car_type'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedCarType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + selectedCarType ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ Column( @@ -314,12 +310,9 @@ class _TaxiScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ) + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)) ], ), if (isDesktop) @@ -328,7 +321,6 @@ class _TaxiScreenState extends State { SizedBox( height: 8, ), - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -345,13 +337,14 @@ class _TaxiScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: _numPassengerFocusNode, controller: _numPassengerController, style: const TextStyle(fontSize: 12), keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal + FilteringTextInputFormatter.allow(RegExp( + r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal ], decoration: const InputDecoration( labelText: "Number of Passenger", @@ -359,7 +352,6 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), @@ -371,7 +363,6 @@ class _TaxiScreenState extends State { style: TextStyle(color: Colors.red, fontSize: 12), ), ], - ], ), if (isDesktop) @@ -396,24 +387,23 @@ class _TaxiScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: _toFocusNode, // Assign the correct focus node value: selectedCarType, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedCarType = newValue; - }); - print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - - } + setState(() { + selectedCarType = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } : null, items: dropdownItems, @@ -428,73 +418,69 @@ class _TaxiScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - + List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['taxi_car_required_for'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedReqTaxi ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + selectedReqTaxi ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _taxiReqFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - - child: DropdownButtonFormField( + child: DropdownButtonFormField( focusNode: _taxiReqFocusNode, // Assign the correct focus node value: selectedReqTaxi, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedReqTaxi = newValue; - }); - print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - } + setState(() { + selectedReqTaxi = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } : null, items: dropdownItems, ), - - ), ), ]; } - List _buildSecondRow(bool isDesktop) { - - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -504,9 +490,8 @@ class _TaxiScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -542,7 +527,6 @@ class _TaxiScreenState extends State { } return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -559,7 +543,7 @@ class _TaxiScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: _destinationFocusNode, controller: _destinationController, style: const TextStyle(fontSize: 12), @@ -569,19 +553,18 @@ class _TaxiScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), ), - if (errorMessages["destination_city"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["destination_city"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -605,7 +588,6 @@ class _TaxiScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: _locationFocusNode, controller: _locationController, @@ -620,13 +602,13 @@ class _TaxiScreenState extends State { ), ), ), - if (errorMessages["location_of_pickup"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], + if (errorMessages["location_of_pickup"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], ], ), if (isDesktop) @@ -651,7 +633,6 @@ class _TaxiScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( @@ -671,17 +652,16 @@ class _TaxiScreenState extends State { ), ), ), - ), ), - if (errorMessages["date"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["date"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -719,22 +699,21 @@ class _TaxiScreenState extends State { border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + Icon(Icons.access_time, size: 16, color: Colors.grey), ), ), ), ), - ), ), - if (errorMessages["time"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["time"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), ]; } @@ -756,8 +735,9 @@ class _TaxiScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, + child: TextField( focusNode: _commentsFocusNode, controller: _taxiCommentsController, @@ -783,7 +763,7 @@ class _TaxiScreenState extends State { // Close Button ElevatedButton( onPressed: () { - widget.onClose(false);// Close the dialog or screen + widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color @@ -818,4 +798,4 @@ class _TaxiScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index a9331d5..5602e2d 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class TrainScreen extends StatefulWidget { - final Map? apiData; - final Function(Map)onSavetrain; + final Function(Map) onSavetrain; final Function(bool) onClose; final Map? selectedItem; final String? loginUser; - TrainScreen({ - required this.onClose, this.apiData, required this.onSavetrain, required this.selectedItem, required this.loginUser}); + TrainScreen( + {required this.onClose, + this.apiData, + required this.onSavetrain, + required this.selectedItem, + required this.loginUser}); @override _TrainScreenState createState() => _TrainScreenState(); @@ -33,7 +36,6 @@ class _TrainScreenState extends State { final FocusNode _timeFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late TextEditingController _trainNoController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _fromController = TextEditingController(); @@ -54,11 +56,10 @@ class _TrainScreenState extends State { Map errorMessages = {}; - Map get trainData { - Map data ={ - - "train_no": _trainNoController.text, - "class": selectedClass, + Map get trainData { + Map data = { + "train_no": _trainNoController.text, + "class": selectedClass, "from_station": _fromController.text, "to_station": _toController.text, "date": _dateController.text, @@ -69,9 +70,11 @@ class _TrainScreenState extends State { }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["train_id"] != null && widget.selectedItem?["train_id"] != 0) { + } else if (widget.selectedItem?["train_id"] != null && + widget.selectedItem?["train_id"] != 0) { data["train_id"] = widget.selectedItem!["train_id"]; } } @@ -83,47 +86,45 @@ class _TrainScreenState extends State { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } - @override void initState() { super.initState(); - _trainNoFocusNode.addListener(() { + _trainNoFocusNode.addListener(() { setState(() { _trainNoFocused = _trainNoFocusNode.hasFocus; }); }); - _hotelNameFocusNode.addListener(() { - setState(() { - _isHotelNameFocused = _hotelNameFocusNode.hasFocus; - }); + _hotelNameFocusNode.addListener(() { + setState(() { + _isHotelNameFocused = _hotelNameFocusNode.hasFocus; }); - _fromFocusNode.addListener(() { - setState(() { - _fromFocus = _fromFocusNode.hasFocus; - }); + }); + _fromFocusNode.addListener(() { + setState(() { + _fromFocus = _fromFocusNode.hasFocus; }); - _toFocusNode.addListener(() { - setState(() { - _toFocus = _toFocusNode.hasFocus; - }); + }); + _toFocusNode.addListener(() { + setState(() { + _toFocus = _toFocusNode.hasFocus; }); - _dateFocusNode.addListener(() { - setState(() { - _dateFocus = _fromFocusNode.hasFocus; - }); + }); + _dateFocusNode.addListener(() { + setState(() { + _dateFocus = _fromFocusNode.hasFocus; }); - _timeFocusNode.addListener(() { - setState(() { - _timeFocus = _timeFocusNode.hasFocus; - }); + }); + _timeFocusNode.addListener(() { + setState(() { + _timeFocus = _timeFocusNode.hasFocus; }); - _commentsFocusNode.addListener(() { - setState(() { - _commentsFocus = _commentsFocusNode.hasFocus; - }); + }); + _commentsFocusNode.addListener(() { + setState(() { + _commentsFocus = _commentsFocusNode.hasFocus; }); - + }); _trainCommentsController = initController("comments"); _trainNoController = initController("train_no"); @@ -134,7 +135,7 @@ class _TrainScreenState extends State { // Set the selected value if available if (widget.selectedItem != null && widget.selectedItem!["class"] != null) { - selectedClass = widget.selectedItem!["class"].toString(); + selectedClass = widget.selectedItem!["class"].toString(); } _trainNoController.addListener(() => _clearError("train_no")); @@ -142,10 +143,8 @@ class _TrainScreenState extends State { _toController.addListener(() => _clearError("to_station")); _dateController.addListener(() => _clearError("date")); _timeController.addListener(() => _clearError("time")); - } - @override void dispose() { _trainNoFocusNode.dispose(); @@ -159,7 +158,6 @@ class _TrainScreenState extends State { super.dispose(); } - void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { @@ -168,12 +166,18 @@ class _TrainScreenState extends State { } } - bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["train_no", "class","from_station", "to_station","date","time"]; + List requiredFields = [ + "train_no", + "class", + "from_station", + "to_station", + "date", + "time" + ]; // Check validation for each field for (String field in requiredFields) { @@ -185,28 +189,22 @@ class _TrainScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save accomadationData $trainData"); - void handleSave(){ - - print( "Handle Save accomadationData $trainData"); - - Map data = trainData; + Map data = trainData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSavetrain(trainData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } - - - - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -235,8 +233,10 @@ class _TrainScreenState extends State { ), ), Text("Train Booking List", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -254,7 +254,7 @@ class _TrainScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -268,7 +268,6 @@ class _TrainScreenState extends State { ]; return [ - ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container @@ -284,10 +283,7 @@ class _TrainScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -300,20 +296,16 @@ class _TrainScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ), + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), if (errorMessages["train_no"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], ], ), if (isDesktop) @@ -322,39 +314,40 @@ class _TrainScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - + 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(); + .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)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + String? selectedPurpose = + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _trainNoFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( @@ -369,36 +362,34 @@ class _TrainScreenState extends State { contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), - - - ), ), ]; } - - List _builClassType(bool isDesktop){ - + List _builClassType(bool isDesktop) { List purposeList = widget.apiData?['train_class'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedClass ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; + selectedClass ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ Column( @@ -415,9 +406,11 @@ class _TrainScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: _hotelNameFocusNode, // Assign the correct focus node // controller: _hotelNameController, @@ -425,38 +418,33 @@ class _TrainScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedClass = newValue; - }); - } + setState(() { + selectedClass = newValue; + }); + } : null, items: dropdownItems, ), - - ), ), - if (errorMessages["class"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["class"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), ]; } - List _buildSecondRow(bool isDesktop) { - - DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; @@ -466,9 +454,8 @@ class _TrainScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -504,7 +491,6 @@ class _TrainScreenState extends State { } return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -521,7 +507,7 @@ class _TrainScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( + child: TextField( focusNode: _fromFocusNode, controller: _fromController, style: const TextStyle(fontSize: 12), @@ -531,19 +517,18 @@ class _TrainScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - ), ), ), ), - if (errorMessages["from_station"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["from_station"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -567,7 +552,6 @@ class _TrainScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: TextField( focusNode: _toFocusNode, controller: _toController, @@ -582,14 +566,14 @@ class _TrainScreenState extends State { ), ), ), - if (errorMessages["to_station"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["to_station"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -613,7 +597,6 @@ class _TrainScreenState extends State { isDesktop: isDesktop, child: SizedBox( height: 40, - child: GestureDetector( onTap: () => _selectCheckOutDate(context), child: AbsorbPointer( @@ -633,17 +616,16 @@ class _TrainScreenState extends State { ), ), ), - ), ), - if (errorMessages["date"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - ], + if (errorMessages["date"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], + ], ), if (isDesktop) Spacer() @@ -681,12 +663,11 @@ class _TrainScreenState extends State { border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + Icon(Icons.access_time, size: 16, color: Colors.grey), ), ), ), ), - ), ), if (errorMessages["time"] != null) ...[ @@ -698,8 +679,6 @@ class _TrainScreenState extends State { ], ], ), - - ]; } @@ -720,7 +699,7 @@ class _TrainScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, @@ -782,4 +761,4 @@ class _TrainScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/itnerary/visa.dart b/lib/Screens/itnerary/visa.dart index e78df4c..14b8f62 100644 --- a/lib/Screens/itnerary/visa.dart +++ b/lib/Screens/itnerary/visa.dart @@ -7,20 +7,21 @@ import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class VisaScreen extends StatefulWidget { - final Map? apiData; final List? apiCountryData; - final Function(bool) onClose; - final Function(Map) onSaveVisa; - final Map? selectedItem; - final String? loginUser; + final Function(Map) onSaveVisa; + final Map? selectedItem; + final String? loginUser; - - VisaScreen({ - required this.onClose,required this.onSaveVisa, this.apiData, required this.selectedItem, - required this.apiCountryData, required this.loginUser}); + VisaScreen( + {required this.onClose, + required this.onSaveVisa, + this.apiData, + required this.selectedItem, + required this.apiCountryData, + required this.loginUser}); @override _VisaScreenState createState() => _VisaScreenState(); @@ -38,7 +39,6 @@ class _VisaScreenState extends State { final FocusNode _dateFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); - late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _hotelNameController = TextEditingController(); late TextEditingController _fromController = TextEditingController(); @@ -57,36 +57,36 @@ class _VisaScreenState extends State { Map errorMessages = {}; - - Map get visaData{ - Map data ={ - "type_of_visa" :selectedPurpose, - // "country": selectedCountry, + Map get visaData { + Map data = { + "type_of_visa": selectedPurpose, + // "country": selectedCountry, "country_code": selectedCountry, - "start_date": _dateController.text, - "comments":_visaCommentsController.text, + "start_date": _dateController.text, + "comments": _visaCommentsController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; if (widget.selectedItem != null) { - if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { + if (widget.selectedItem?["indx"] != null && + widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; - } else if (widget.selectedItem?["visa_id"] != null && widget.selectedItem?["visa_id"] != 0) { + } else if (widget.selectedItem?["visa_id"] != null && + widget.selectedItem?["visa_id"] != 0) { data["visa_id"] = widget.selectedItem!["visa_id"]; } } return data; } - - @override void initState() { super.initState(); _addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus); - _addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); + _addFocusListener( + _hotelNameFocusNode, (focus) => _isHotelNameFocused = focus); _addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus); _addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus); @@ -96,20 +96,17 @@ class _VisaScreenState extends State { TextEditingController(text: widget.selectedItem?["start_date"] ?? ""); // Set the selected value if available - if (widget.selectedItem != null && widget.selectedItem!["type_of_visa"] != null) { + if (widget.selectedItem != null && + widget.selectedItem!["type_of_visa"] != null) { selectedPurpose = widget.selectedItem!["type_of_visa"].toString(); } - if (widget.selectedItem != null && widget.selectedItem!["country_code"] != null) { + if (widget.selectedItem != null && + widget.selectedItem!["country_code"] != null) { // selectedPurpose = widget.selectedItem!["selectedCountry"].toString(); selectedCountry = widget.selectedItem!["country_code"] as String?; } - - - } - - void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { @@ -131,12 +128,15 @@ class _VisaScreenState extends State { super.dispose(); } - bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty - List requiredFields = ["type_of_visa", "country_code","start_date"]; + List requiredFields = [ + "type_of_visa", + "country_code", + "start_date" + ]; // Check validation for each field for (String field in requiredFields) { @@ -148,29 +148,22 @@ class _VisaScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } + void handleSave() { + print("Handle Save visaData $visaData"); - - void handleSave(){ - - print( "Handle Save visaData $visaData"); - - Map data = visaData; + Map data = visaData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails - }else { + } else { widget.onSaveVisa(visaData); } - widget.onClose(false);// Close screen after saving + widget.onClose(false); // Close screen after saving } - - - - @override Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -199,8 +192,10 @@ class _VisaScreenState extends State { ), ), Text("Visa Registration", - style: - TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))), + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFF575A74))), SizedBox( height: 6, ), @@ -218,7 +213,7 @@ class _VisaScreenState extends State { }); } - List _buildAccomadtionForm (bool isDesktop) { + List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), @@ -226,12 +221,9 @@ class _VisaScreenState extends State { ]; } - List> rowBuilders = [ - _buildSecondRow(isDesktop) - ]; + List> rowBuilders = [_buildSecondRow(isDesktop)]; return [ - ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container @@ -247,10 +239,7 @@ class _VisaScreenState extends State { ]; } - - List _buildFirstRow(isDesktop) { - return [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -263,20 +252,16 @@ class _VisaScreenState extends State { color: Color(0xFF575A74)), ), SizedBox(height: 5), - - isDesktop ? Row(children: _buildTripType(isDesktop) - ) : - Column( - children: _buildTripType(isDesktop) - ), + isDesktop + ? Row(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), if (errorMessages["type_of_visa"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], - + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], ], ), if (isDesktop) @@ -285,75 +270,69 @@ class _VisaScreenState extends State { SizedBox( height: 8, ), - ]; } - - - List _buildTripType(bool isDesktop){ - + List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['visa_type_of_visa'] ?? []; List> dropdownItems = purposeList - .map((item)=>DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + value: item['dropdown_key'], + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } // Default selected value - selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; - + selectedPurpose ??= + dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _tripTypeFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: DropdownButtonFormField( focusNode: _tripTypeFocusNode, // Assign the correct focus node value: selectedPurpose, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + contentPadding: + EdgeInsets.symmetric(horizontal: 10), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); - - - } + setState(() { + selectedPurpose = newValue; + }); + print( + "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); + } : null, items: dropdownItems, ), - - ), ), ]; } - - List _buildSecondRow(bool isDesktop) { - // List countryList = widget.apiCountryData ?? []; // @@ -380,12 +359,12 @@ class _VisaScreenState extends State { 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 + for (var item in countryList) + item['country_code'] as String: item['country_name'] as String }; // Extract only country codes for processing @@ -403,9 +382,8 @@ class _VisaScreenState extends State { DateTime? pickedDate = await showDatePicker( context: context, - - - initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today) + initialDate: _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(today) ? _selectedCheckOutDate! : today, firstDate: today, @@ -420,9 +398,7 @@ class _VisaScreenState extends State { } } - return [ - Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -437,12 +413,15 @@ class _VisaScreenState extends State { CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownSearch( selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality + showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", @@ -450,14 +429,17 @@ class _VisaScreenState extends State { ), ), ), - items: countryMap.values.toList(), + items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1,), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + ), ), ), - dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item + dropdownBuilder: (context, selectedItem) => Align( + // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", @@ -474,22 +456,20 @@ class _VisaScreenState extends State { if (selectedCountry!.isNotEmpty) { errorMessages.remove("country_code"); } - }); }, ), ), ), - if (errorMessages["country_code"] != null) ...[ - SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), - ], + if (errorMessages["country_code"] != null) ...[ + SizedBox(height: 5), // Space before error message + Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ], ], ), - if (isDesktop) Spacer() else @@ -510,19 +490,20 @@ class _VisaScreenState extends State { CustomTextFieldWrapper( isFocused: _dateFocus, isDesktop: isDesktop, + width: isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, - child: GestureDetector( - onTap: ()async{ + onTap: () async { await _selectCheckOutDate(context); - if (_dateController.text.isNotEmpty) { - setState(() { - errorMessages.remove("start_date"); - }); - } - - }, + if (_dateController.text.isNotEmpty) { + setState(() { + errorMessages.remove("start_date"); + }); + } + }, child: AbsorbPointer( child: TextField( focusNode: _dateFocusNode, @@ -540,7 +521,6 @@ class _VisaScreenState extends State { ), ), ), - ), ), if (errorMessages["start_date"] != null) ...[ @@ -552,9 +532,6 @@ class _VisaScreenState extends State { ], ], ), - - - ]; } @@ -575,7 +552,7 @@ class _VisaScreenState extends State { isFocused: _commentsFocus, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: TextField( focusNode: _commentsFocusNode, @@ -584,7 +561,7 @@ class _VisaScreenState extends State { keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( - labelText: "Description", + labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, @@ -621,7 +598,7 @@ class _VisaScreenState extends State { // Save Changes Button ElevatedButton( onPressed: () { - handleSave(); + handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Primary color for save @@ -637,4 +614,4 @@ class _VisaScreenState extends State { ), ]; } -} \ No newline at end of file +} diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index 8488744..fd4f2ed 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:frontend/Screens/plans/dynamic_itinerary_stepper.dart'; +import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -28,73 +29,12 @@ class _CreatePlansState extends State { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; - - final args = GoRouterState.of(context).extra as Map? ?? {}; - // final planData = args?['planData']; - final bool isViewMode = args?['isViewMode'] ?? false; - - final Map planData = args['planData'] as Map? ?? {}; - - - // print("isViewMode: $isViewMode"); - - // final bool isViewMode = true; - // final planData = GoRouterState.of(context).extra as Map? ?? {}; - - - print("RECived palndata"); - // print("RECived palndata - ${planData}"); - return Scaffold( backgroundColor: Colors.white, - body: Column( + body: Row( children: [ - Container( - color: Color(0xFFF4F4FB), - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), - child: Row(children: [ - Row( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Icon( - Icons.create_new_folder_outlined, - color: Color(0xFF84869A), - size: 23, - ), - ), - Text( - isViewMode - ? "View Plan" - : (planData.isNotEmpty ? "Update Plan" : "New Plan"), - - style: TextStyle(fontSize: 18), - ), - ], - ), - Spacer(), - Container( - color: Color(0xFFE9EBF6), - child: IconButton( - icon: Icon(Icons.close), - onPressed: () { - context.go('/listPlan'); - }, - ), - ) - ]), - ), - Expanded( - child: Container( - color: Colors.white, - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(26.0), - child: CreateNewPlan(isDesktop: isDesktop, selectedPlanData: planData, isViewMode : isViewMode), - ), - ), - ), - ) + if (isDesktop) CustomDrawer(isDesktop: true), + Expanded(child: buildUserTable(isDesktop, context)), ], ), ); @@ -102,18 +42,97 @@ class _CreatePlansState extends State { } } +Widget buildUserTable(bool isDesktop, context) { + final args = GoRouterState.of(context).extra as Map? ?? {}; + // final planData = args?['planData']; + final bool isViewMode = args?['isViewMode'] ?? false; + + final Map planData = + args['planData'] as Map? ?? {}; + + // print("isViewMode: $isViewMode"); + + // final bool isViewMode = true; + // final planData = GoRouterState.of(context).extra as Map? ?? {}; + + print("RECived palndata"); + // print("RECived palndata - ${planData}"); + + return Container( + margin: + const EdgeInsets.only(left: 10.0, right: 15.0, top: 10.0, bottom: 10.0), + decoration: + BoxDecoration(border: Border.all(color: Color(0xFFF7F7FB), width: 3.5)), + child: Column( + children: [ + Container( + color: Color(0xFFF4F4FB), + padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), + child: Row(children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.create_new_folder_outlined, + color: Color(0xFF84869A), + size: 23, + ), + ), + Text( + isViewMode + ? "View Plan" + : (planData.isNotEmpty ? "Update Plan" : "New Plan"), + style: TextStyle(fontSize: 18), + ), + ], + ), + Spacer(), + // Container( + // color: Color(0xFFE9EBF6), + // child: IconButton( + // icon: Icon(Icons.close), + // onPressed: () { + // context.go('/listPlan'); + // }, + // ), + // ) + ]), + ), + Expanded( + child: Container( + color: Colors.white, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(26.0), + child: CreateNewPlan( + isDesktop: isDesktop, + selectedPlanData: planData, + isViewMode: isViewMode), + ), + ), + ), + ) + ], + ), + ); +} + class CreateNewPlan extends StatefulWidget { final bool isDesktop; final bool isViewMode; final Map selectedPlanData; - const CreateNewPlan({super.key, required this.isDesktop, required this.selectedPlanData, required this.isViewMode}); + const CreateNewPlan( + {super.key, + required this.isDesktop, + required this.selectedPlanData, + required this.isViewMode}); @override _CreateNewPlansState createState() => _CreateNewPlansState(); } class _CreateNewPlansState extends State { - final TextEditingController _tripTitleController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController(); @@ -134,18 +153,17 @@ class _CreateNewPlansState extends State { 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? orgId; String? planUsrId; String? planTravlrId; String? _selectedTripType; String? selectedCostCenterId; - String? _selectedIsBillable ; + String? _selectedIsBillable; String? selectedFuncDept; String? selectedPurpose; @@ -163,32 +181,31 @@ class _CreateNewPlansState extends State { //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_by": selfId, - "updated_by": selfId, - "is_active": "1", - "flight":flightList, - "accomodation": accommodationList, - "bus": busList, - "taxi": taxiList, - "train": trainList, - "visa": visaList, - "forex": forexList, - "insurance": insuranceList, - "miscellaneous": miscellaneousList, - }; - - + "org_id": orgId, + "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_by": selfId, + "updated_by": selfId, + "is_active": "1", + "flight": flightList, + "accomodation": accommodationList, + "bus": busList, + "taxi": taxiList, + "train": trainList, + "visa": visaList, + "forex": forexList, + "insurance": insuranceList, + "miscellaneous": miscellaneousList, + }; // // Function to update miscellaneous list // void updateMiscellaneousData(List> newMiscellaneousList) { @@ -198,7 +215,6 @@ class _CreateNewPlansState extends State { // print("Updated Miscellaneous Data in CreateNewPlan: $miscellaneousList"); // } - void handleItineraryUpdate(String type, List> newList) { setState(() { switch (type) { @@ -211,7 +227,7 @@ class _CreateNewPlansState extends State { case "Insurance": insuranceList = newList; break; - case "Train": + case "Train": trainList = newList; break; case "Bus": @@ -223,13 +239,13 @@ class _CreateNewPlansState extends State { case "Forex": forexList = newList; break; - case "Flight": - flightList = newList; + case "Flight": + flightList = newList; break; case "Accomodation": accommodationList = newList; break; - default: + default: print("Unknown itinerary type: $type"); } }); @@ -237,11 +253,10 @@ class _CreateNewPlansState extends State { } @override - void initState(){ + void initState() { super.initState(); fetchUserDetails(); - fetchPlans(); fetchCostCenter(); fetchCountryList(); @@ -263,7 +278,6 @@ class _CreateNewPlansState extends State { }); handleUpdateData(); - } @override @@ -273,99 +287,97 @@ class _CreateNewPlansState extends State { super.dispose(); } - - void handleUpdateData() { if (widget.selectedPlanData != null) { - setState(() { + setState(() { + planUsrId = widget.selectedPlanData['user_id'] ?? ''; + _tripTitleController.text = widget.selectedPlanData['trip_title'] ?? ''; + _descriptionController.text = + widget.selectedPlanData['description'] ?? ''; + _selectedTripType = widget.selectedPlanData['trip_type']; + _selectedIsBillable = + widget.selectedPlanData['is_billable'] == "1" ? "1" : "2"; - planUsrId = widget.selectedPlanData['user_id'] ?? ''; - _tripTitleController.text = widget.selectedPlanData['trip_title'] ?? ''; - _descriptionController.text = widget.selectedPlanData['description'] ?? ''; + // selectedCostCenterId = widget.selectedPlanData['cost_center_id']?.toString() ; + // selectedPurpose = widget.selectedPlanData['purpose_of_travel']?.toString(); + // selectedFuncDept =widget.selectedPlanData['functional_department']?.toString(); - _selectedTripType = widget.selectedPlanData['trip_type']; - _selectedIsBillable = widget.selectedPlanData['is_billable'] == "1" ? "1" : "2"; + if (widget.selectedPlanData!["cost_center_id"] != null) { + selectedCostCenterId = + widget.selectedPlanData!["cost_center_id"].toString(); + } - // selectedCostCenterId = widget.selectedPlanData['cost_center_id']?.toString() ; - // selectedPurpose = widget.selectedPlanData['purpose_of_travel']?.toString(); - // selectedFuncDept =widget.selectedPlanData['functional_department']?.toString(); + // + if (widget.selectedPlanData!["purpose_of_travel"] != null) { + selectedPurpose = + widget.selectedPlanData!["purpose_of_travel"].toString(); + } - if (widget.selectedPlanData!["cost_center_id"] != null) { - selectedCostCenterId = widget.selectedPlanData!["cost_center_id"].toString(); + if (widget.selectedPlanData!["functional_department"] != null) { + // selectedFuncDept = widget.selectedPlanData!["functional_department"].toString(); + selectedFuncDept = widget.selectedPlanData!["functional_department"]; + } + + // Assign lists from selectedPlanData, ensuring they are properly formatted + flightList = List>.from( + widget.selectedPlanData['flight'] ?? []); + accommodationList = List>.from( + widget.selectedPlanData['accomodation'] ?? []); + busList = List>.from( + widget.selectedPlanData['bus'] ?? []); + taxiList = List>.from( + widget.selectedPlanData['taxi'] ?? []); + trainList = List>.from( + widget.selectedPlanData['train'] ?? []); + visaList = List>.from( + widget.selectedPlanData['visa'] ?? []); + forexList = List>.from( + widget.selectedPlanData['forex'] ?? []); + insuranceList = List>.from( + widget.selectedPlanData['insurance'] ?? []); + miscellaneousList = List>.from( + widget.selectedPlanData['miscellaneous'] ?? []); + }); + + if (widget.selectedPlanData.containsKey('plan_id') && + widget.selectedPlanData['plan_id'] != null) { + print("Plan ID exists: ${widget.selectedPlanData['plan_id']}"); + selectedPlanId = widget.selectedPlanData['plan_id']?.toString(); + } else { + print("Plan ID is missing or null"); } - // - if (widget.selectedPlanData!["purpose_of_travel"] != null) { - selectedPurpose = widget.selectedPlanData!["purpose_of_travel"].toString(); - } - - - - if (widget.selectedPlanData!["functional_department"] != null) { - // selectedFuncDept = widget.selectedPlanData!["functional_department"].toString(); - selectedFuncDept = widget.selectedPlanData!["functional_department"]; - } - - - - // Assign lists from selectedPlanData, ensuring they are properly formatted - flightList = List>.from(widget.selectedPlanData['flight'] ?? []); - accommodationList = List>.from(widget.selectedPlanData['accomodation'] ?? []); - busList = List>.from(widget.selectedPlanData['bus'] ?? []); - taxiList = List>.from(widget.selectedPlanData['taxi'] ?? []); - trainList = List>.from(widget.selectedPlanData['train'] ?? []); - visaList = List>.from(widget.selectedPlanData['visa'] ?? []); - forexList = List>.from(widget.selectedPlanData['forex'] ?? []); - insuranceList = List>.from(widget.selectedPlanData['insurance'] ?? []); - miscellaneousList = List>.from(widget.selectedPlanData['miscellaneous'] ?? []); - - }); - - if (widget.selectedPlanData.containsKey('plan_id') && widget.selectedPlanData['plan_id'] != null) { - print("Plan ID exists: ${widget.selectedPlanData['plan_id']}"); - selectedPlanId = widget.selectedPlanData['plan_id']?.toString(); - } else { - print("Plan ID is missing or null"); - } - - - print("updatedPlanDAta - $planData"); + print("updatedPlanDAta - $planData"); } } - - void getSelectedPlanFor(){ + void getSelectedPlanFor() { if (!mounted) return; setState(() { - if(selectedplanUserId != null){ - - if(selectedIstravelUser!){ + if (selectedplanUserId != null) { + if (selectedIstravelUser!) { planUsrId = ""; planTravlrId = selectedplanUserId; - }else { + } else { planUsrId = selectedplanUserId; planTravlrId = ""; } - - } - else { - planUsrId = selfId; - 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 @@ -373,7 +385,7 @@ class _CreateNewPlansState extends State { selfId = details['user_id']; }); } - + orgId = await getOrgId(); print("userDetails - $selfId"); getSelectedPlanFor(); } @@ -383,17 +395,16 @@ class _CreateNewPlansState extends State { return prefs.getString('auth_token'); } - Future getUserId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('userId'); } - Future ?> getUserDetails() async{ + Future?> getUserDetails() async { final prefs = await SharedPreferences.getInstance(); final userData = prefs.getString('user_data'); - if(userData!= null){ + if (userData != null) { final decodedData = jsonDecode(userData); return { @@ -404,7 +415,6 @@ class _CreateNewPlansState extends State { return null; } - Future fetchPlans() async { final String apiUrldata = '$apiUrl/api/getDropdownMaster'; @@ -428,16 +438,16 @@ class _CreateNewPlansState extends State { print(data); if (!data.containsKey('data') || data['data'] is! Map) { - throw Exception("Invalid response format: 'data' field is missing or not a Map"); + throw Exception( + "Invalid response format: 'data' field is missing or not a Map"); } - Map plansJson = data['data']; // 'data' is a Map, not a List + Map plansJson = + data['data']; // 'data' is a Map, not a List setState(() { apiData = plansJson; // Store API response in state isLoading = false; }); - - } catch (e) { throw Exception('Error parsing response: $e'); } @@ -472,23 +482,24 @@ class _CreateNewPlansState extends State { final data = json.decode(response.body); print(data); - if (!data.containsKey('data') || data['data'] is!List) { - throw Exception("Invalid response format: 'data' field is missing or not a List"); + if (!data.containsKey('data') || data['data'] is! List) { + throw Exception( + "Invalid response format: 'data' field is missing or not a List"); } - List plansJson = data['data']; // 'data' is a Map, 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']; - // } + // if(apiCostData!.isNotEmpty){ + // selectedCostCenterId =apiCostData?.first['department_id']; + // } if (apiCostData != null && apiCostData!.isNotEmpty) { - selectedCostCenterId ??= apiCostData!.first['department_id']?.toString(); + selectedCostCenterId ??= + apiCostData!.first['department_id']?.toString(); } }); print('plansJSON'); - } catch (e) { throw Exception('Error parsing response: $e'); } @@ -497,7 +508,6 @@ class _CreateNewPlansState extends State { } } - Future fetchCountryList() async { final String apiUrldata = '$apiUrl/api/getcountryMaster'; @@ -524,12 +534,12 @@ class _CreateNewPlansState extends State { 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"); + 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 + List plansJson = data['data']; // 'data' is a Map, not a List if (data['data'] is List) { List plansJson = data['data']; @@ -540,10 +550,8 @@ class _CreateNewPlansState extends State { setState(() { apiCountryData = plansJson; // Store API response in state - }); print('plansJSONContry - $plansJson'); - } catch (e) { throw Exception('Error parsing response: $e'); } @@ -554,13 +562,16 @@ class _CreateNewPlansState extends State { // Handle Submit - bool validateForm(){ + 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"; + 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 = { @@ -572,7 +583,8 @@ class _CreateNewPlansState extends State { for (var entry in requiredFields.entries) { if (entry.value == null || entry.value!.isEmpty) { - validationErrors[entry.key] = "${entry.key.replaceAll('_', ' ').toUpperCase()} is required"; + validationErrors[entry.key] = + "${entry.key.replaceAll('_', ' ').toUpperCase()} is required"; } } @@ -581,49 +593,47 @@ class _CreateNewPlansState extends State { void handleSubmit() { setState(() { - if(validateForm()){ - print("Form submitted successfully: $planData"); - postPlanData(planData); - context.go('/listPlan'); - } + if (validateForm()) { + print("Form submitted successfully: $planData"); + postPlanData(planData); + context.go('/listPlan'); + } }); } Future postPlanData(Map planData) async { - final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; - final token = await getToken(); // Fetch token + final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; + final token = await getToken(); // Fetch token - if (token == null) { - throw Exception('Token not found. Please log in.'); - } - - if (selectedPlanId != null && selectedPlanId!.isNotEmpty) { - planData['plan_id'] = selectedPlanId; // Add plan_id for update - } - - try { - final response = await http.post( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }, - body: jsonEncode(planData), // Convert map to JSON - ); - - if (response.statusCode == 200) { - print("Plan submitted successfully!"); - print("Response: ${response.body}"); - } else { - print("Failed to submit plan. Status: ${response.statusCode}"); - print("Error: ${response.body}"); - } - } catch (e) { - print(" Error submitting plan: $e"); - } + if (token == null) { + throw Exception('Token not found. Please log in.'); } + if (selectedPlanId != null && selectedPlanId!.isNotEmpty) { + planData['plan_id'] = selectedPlanId; // Add plan_id for update + } + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + body: jsonEncode(planData), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Plan submitted successfully!"); + print("Response: ${response.body}"); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print(" Error submitting plan: $e"); + } + } Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { @@ -657,11 +667,14 @@ class _CreateNewPlansState extends State { ), children: [ TextSpan( - text: otherUserName ?? userName ?? " ", // Dynamic username + text: otherUserName ?? + userName ?? + " ", // Dynamic username style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, - color: Colors.blueAccent, // Change this to any color + color: + Colors.blueAccent, // Change this to any color ), ), ], @@ -716,7 +729,8 @@ class _CreateNewPlansState extends State { enabled: !widget.isViewMode, decoration: InputDecoration( labelText: "Trip Title", - labelStyle: TextStyle(fontSize: 12, color: Colors.grey), + labelStyle: + TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), @@ -781,27 +795,23 @@ class _CreateNewPlansState extends State { SizedBox( height: 8, ), - - isDesktop - ? Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildNonDescriptionColumn(), - SizedBox(width: 25), - _buildDescriptionColumn(isDesktop), - ], - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildNonDescriptionColumn(), - SizedBox(height: 15), - _buildDescriptionColumn(isDesktop), - - - ], - ), - + isDesktop + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildNonDescriptionColumn(), + SizedBox(width: 25), + _buildDescriptionColumn(isDesktop), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildNonDescriptionColumn(), + SizedBox(height: 15), + _buildDescriptionColumn(isDesktop), + ], + ), Padding( padding: const EdgeInsets.all(8.0), child: Divider( @@ -809,32 +819,36 @@ class _CreateNewPlansState extends State { thickness: 0.5, ), ), - Row( children: [ - Expanded(child: DynamicItinerary(apiData: apiData, apiCountryData: apiCountryData, - onItineraryUpdate: handleItineraryUpdate,loginUser: selfId,selectedPlanData: planData, isViewMode:widget.isViewMode , - )), // Wrap with Expanded if needed + Expanded( + child: DynamicItinerary( + apiData: apiData, + apiCountryData: apiCountryData, + onItineraryUpdate: handleItineraryUpdate, + loginUser: selfId, + selectedPlanData: planData, + isViewMode: widget.isViewMode, + )), // Wrap with Expanded if needed ], ), SizedBox(height: 15), isDesktop ? Row( - mainAxisAlignment: MainAxisAlignment.end, - children: _buildSubmit(isDesktop),) - :Row( - mainAxisAlignment: MainAxisAlignment.center, - children: _buildSubmit(isDesktop),) - + mainAxisAlignment: MainAxisAlignment.end, + children: _buildSubmit(isDesktop), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildSubmit(isDesktop), + ) ], ); }); } - /// Extracted helper function List _buildCostIsBillable() { - List purposeList = apiData?['plan_is_billable'] ?? []; return [ @@ -862,63 +876,66 @@ class _CreateNewPlansState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 10), // Proper padding ), - onChanged: widget.isViewMode ? null : (newValue) { - setState(() { - selectedCostCenterId = newValue; - }); - }, - items:apiCostData?.map>((item){ + onChanged: widget.isViewMode + ? null + : (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,height: 5,), + 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: purposeList.map((item) { - return Row( - children: [ - Radio( - // value: item['dropdown_key'], // Use dropdown_value as value + 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: purposeList.map((item) { + return Row( + children: [ + Radio( + // value: item['dropdown_key'], // Use dropdown_value as value - value: item['dropdown_key'].toString(),// Convert to String - groupValue: _selectedIsBillable, - activeColor: Colors.blueAccent, - onChanged: widget.isViewMode ? null :(value) { - setState(() { - _selectedIsBillable = value; - }); - print("SELECBILL - $_selectedIsBillable"); - }, - ), + value: item['dropdown_key'].toString(), // Convert to String + groupValue: _selectedIsBillable, + activeColor: Colors.blueAccent, + onChanged: widget.isViewMode + ? null + : (value) { + setState(() { + _selectedIsBillable = value; + }); + print("SELECBILL - $_selectedIsBillable"); + }, + ), - Text(item['dropdown_value'] ?? ''), // Display dropdown_value - SizedBox(width: 20), // Spacing - ], - ); - }).toList(), - ), - - ]) + Text(item['dropdown_value'] ?? ''), // Display dropdown_value + SizedBox(width: 20), // Spacing + ], + ); + }).toList(), + ), + ]) // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ @@ -970,7 +987,6 @@ class _CreateNewPlansState extends State { ]; } - List _buildPlanTrip(bool isDesktop) { List> options = [ {"title": "Self", "value": "Option 1"}, @@ -978,13 +994,14 @@ class _CreateNewPlansState extends State { {"title": "Others", "value": "Option 3"}, ]; - return options.map((option) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: CustomTextFieldWrapper( color: Color(0xFFF4F4FB), - width: option["value"] == "Option 2" ? 175 : 120, // Adjust width conditionally + width: option["value"] == "Option 2" + ? 185 + : 125, // Adjust width conditionally isFocused: _selectedOption == option["value"], isDesktop: isDesktop, child: RadioListTile( @@ -994,32 +1011,32 @@ class _CreateNewPlansState extends State { title: Text(option["title"]!), value: option["value"]!, groupValue: _selectedOption, - onChanged: widget.isViewMode ? null : (value) { - setState(() { - _selectedOption = value!; - if(value == 'Option 2' || value == 'Option 3' ){ - _showInputDialog(option["title"]!); - } - }); - }, + onChanged: widget.isViewMode + ? null + : (value) { + setState(() { + _selectedOption = value!; + if (value == 'Option 2' || value == 'Option 3') { + _showInputDialog(option["title"]!); + } + }); + }, ), ), ); }).toList(); } - List _buildTripType(bool isMobile) { return [ - CustomTextFieldWrapper( color: Color(0xFFF4F4FB), padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), - width: 120, + width: 130, isFocused: _selectedTripType == "1", isDesktop: widget.isDesktop, child: SizedBox( - height: 35, + height: 45, child: Material( color: Colors.transparent, child: RadioListTile( @@ -1030,11 +1047,13 @@ class _CreateNewPlansState extends State { title: Text("Domestic"), value: "1", groupValue: _selectedTripType, - onChanged: widget.isViewMode ? null : (value) { - setState(() { - _selectedTripType = value!; - }); - }, + onChanged: widget.isViewMode + ? null + : (value) { + setState(() { + _selectedTripType = value!; + }); + }, ), ), ), @@ -1053,192 +1072,199 @@ class _CreateNewPlansState extends State { title: Text("International"), value: "2", groupValue: _selectedTripType, - onChanged: widget.isViewMode ? null :(value) { - setState(() { - _selectedTripType = value!; - }); - }, + onChanged: widget.isViewMode + ? null + : (value) { + setState(() { + _selectedTripType = value!; + }); + }, ), ), - - ]; } - - Widget _buildNonDescriptionColumn (){ + Widget _buildNonDescriptionColumn() { // if (apiData == null) { // return Center(child: CircularProgressIndicator()); // Show loading indicator // } // '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_key'], - value: item['dropdown_key']?.toString(), - // value: item['dropdown_key'].toString(), - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + // value: item['dropdown_key'], + value: item['dropdown_key']?.toString(), + // value: item['dropdown_key'].toString(), + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( // value: null, value: "1", - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } - // Ensure Selected Value Exists in the Dropdown List - List dropdownKeys = dropdownItems.map((e) => e.value ?? "").toList(); + List dropdownKeys = + dropdownItems.map((e) => e.value ?? "").toList(); + selectedPurpose ??= dropdownItems.isNotEmpty + ? dropdownItems.first.value.toString() + : "No options"; + print( + "Dropdown Purpose List: ${dropdownItems.map((e) => e.value).toList()}"); + print("Selected Purpose: $selectedPurpose"); - selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value.toString() : "No options"; - - print("Dropdown Purpose List: ${dropdownItems.map((e) => e.value).toList()}"); - print("Selected Purpose: $selectedPurpose"); - - - - - // 'plan_functional_department' Starts --------------------------------------------- + // 'plan_functional_department' Starts --------------------------------------------- List funcDeptList = apiData?['plan_functional_department'] ?? []; List> dropdownFuncDeptItems = funcDeptList - .map((item)=>DropdownMenuItem( - // value: item['dropdown_key'], - value: item['dropdown_key']?.toString(), - child: Text(item['dropdown_value']), - )).toList(); + .map((item) => DropdownMenuItem( + // value: item['dropdown_key'], + value: item['dropdown_key']?.toString(), + child: Text(item['dropdown_value']), + )) + .toList(); if (dropdownFuncDeptItems.isEmpty) { dropdownFuncDeptItems.add( DropdownMenuItem( // value: null, value: "1", - child: Text("No options available", style: TextStyle(color: Colors.grey)), + child: Text("No options available", + style: TextStyle(color: Colors.grey)), ), ); } - // selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : null; - selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : "No options"; + // selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value.toString() : null; + selectedFuncDept ??= dropdownFuncDeptItems.isNotEmpty + ? dropdownFuncDeptItems.first.value.toString() + : "No options"; - print("Dropdown Functional Department List: ${dropdownFuncDeptItems.map((e) => e.value).toList()}"); - print("Selected Functional Department: $selectedFuncDept"); + print( + "Dropdown Functional Department List: ${dropdownFuncDeptItems.map((e) => e.value).toList()}"); + print("Selected Functional Department: $selectedFuncDept"); - - return Column( + return Column(children: [ + Row( children: [ - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Purpose of Travel *", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: false, // Dropdown doesn't use focus - isDesktop: widget.isDesktop, - child: SizedBox( - height: 45, // Set appropriate height - child: apiData == null - ? Center(child: CircularProgressIndicator()) // Show loading inside dropdown - : - DropdownButtonFormField( - value: selectedPurpose, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + Text( + "Purpose of Travel *", // Your label + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: widget.isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: apiData == null + ? Center( + child: + CircularProgressIndicator()) // Show loading inside dropdown + : DropdownButtonFormField( + value: selectedPurpose, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding + ), + onChanged: widget.isViewMode + ? null + : purposeList.isNotEmpty + ? (newValue) { + setState(() { + selectedPurpose = newValue; + }); + print( + "selectedPurpose - $selectedPurpose"); + } + : null, + items: dropdownItems, ), - onChanged: widget.isViewMode ? null : purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedPurpose = newValue; - }); - print("selectedPurpose - $selectedPurpose"); - } - : null, - items: dropdownItems, - ), - ), - ), - ], + ), ), ], ), - SizedBox( - height: 8, - ), - Row( + ], + ), + SizedBox( + height: 8, + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Functional Department", // Your label - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), - ), - SizedBox(height: 5), - CustomTextFieldWrapper( - isFocused: false, // Dropdown doesn't use focus - isDesktop: widget.isDesktop, - child: SizedBox( - height: 45, // Set appropriate height - child: - apiData == null - ? Center(child: CircularProgressIndicator()) // Show loading inside dropdown - :DropdownButtonFormField( - value: selectedFuncDept, - style: TextStyle(fontSize: 12), - decoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10), // Proper padding + Text( + "Functional Department", // Your label + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74)), + ), + SizedBox(height: 5), + CustomTextFieldWrapper( + isFocused: false, // Dropdown doesn't use focus + isDesktop: widget.isDesktop, + child: SizedBox( + height: 45, // Set appropriate height + child: apiData == null + ? Center( + child: + CircularProgressIndicator()) // Show loading inside dropdown + : DropdownButtonFormField( + value: selectedFuncDept, + style: TextStyle(fontSize: 12), + decoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10), // Proper padding + ), + onChanged: widget.isViewMode + ? null + : funcDeptList.isNotEmpty + ? (newValue) { + setState(() { + selectedFuncDept = newValue; + }); + } + : null, + items: dropdownFuncDeptItems, ), - onChanged: widget.isViewMode ? null : funcDeptList.isNotEmpty - ? (newValue) { - setState(() { - selectedFuncDept = newValue; - }); - } - : null, - items: dropdownFuncDeptItems, - ), - ), - ), - ], + ), ), ], ), - SizedBox( - height: 15, - ), - ] - ); + ], + ), + SizedBox( + height: 15, + ), + ]); } - - Widget _buildDescriptionColumn(isDesktop){ - return Column( + Widget _buildDescriptionColumn(isDesktop) { + return Column( children: [ Row( children: [ @@ -1255,8 +1281,9 @@ class _CreateNewPlansState extends State { SizedBox(height: 5), CustomTextFieldWrapper( isFocused: _isdescriptionFocused, - width: isDesktop? MediaQuery.of(context).size.width * 0.5 : - MediaQuery.of(context).size.width * 0.85 , + width: isDesktop + ? MediaQuery.of(context).size.width * 0.4 + : MediaQuery.of(context).size.width * 0.85, isDesktop: widget.isDesktop, child: TextField( focusNode: _descriptionFocusNode, @@ -1282,8 +1309,8 @@ class _CreateNewPlansState extends State { ); } - List _buildSubmit(isDesktop){ - return[ + List _buildSubmit(isDesktop) { + return [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, @@ -1294,53 +1321,58 @@ class _CreateNewPlansState extends State { ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), - onPressed: (){ + onPressed: () { context.go('/listPlan'); }, - child: Text("Cancel") - + child: Text("Cancel")), + SizedBox( + width: 20, ), - SizedBox(width: 20,), - MouseRegion( - cursor: widget.isViewMode ? SystemMouseCursors.forbidden : SystemMouseCursors.click, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: widget.isViewMode ? Colors.blueAccent : Colors.blueAccent, // Keep original color - foregroundColor: widget.isViewMode ? Colors.white : Colors.white, // Keep original color - disabledBackgroundColor: Colors.blueAccent, // Ensure color remains when disabled - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Colors.blueAccent, width: 2), - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - ), - onPressed: widget.isViewMode ? null : handleSubmit, // Disable when in view mode - child: Text("Submit"), - ), - ) - - - ]; + MouseRegion( + cursor: widget.isViewMode + ? SystemMouseCursors.forbidden + : SystemMouseCursors.click, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: widget.isViewMode + ? Colors.blueAccent + : Colors.blueAccent, // Keep original color + foregroundColor: widget.isViewMode + ? Colors.white + : Colors.white, // Keep original color + disabledBackgroundColor: + Colors.blueAccent, // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Colors.blueAccent, width: 2), + ), + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + ), + onPressed: widget.isViewMode + ? null + : handleSubmit, // Disable when in view mode + child: Text("Submit"), + ), + ) + ]; } - void _showInputDialog(String title){ + void _showInputDialog(String title) { showDialog( - context: context, - builder: (BuildContext context){ - return UserSelectionDialog( - title:title, - onSubmit: (input,userId,isTraveller){ - setState(() { - otherUserName = input; - selectedplanUserId = userId; - selectedIstravelUser = isTraveller; - }); - print("USer entered : $otherUserName $userId $isTraveller"); - getSelectedPlanFor(); - } - ); - } - ); + context: context, + builder: (BuildContext context) { + return UserSelectionDialog( + title: title, + onSubmit: (input, userId, isTraveller) { + setState(() { + otherUserName = input; + selectedplanUserId = userId; + selectedIstravelUser = isTraveller; + }); + print("USer entered : $otherUserName $userId $isTraveller"); + getSelectedPlanFor(); + }); + }); } } diff --git a/lib/Screens/plans/list_plans.dart b/lib/Screens/plans/list_plans.dart index 61908db..ed8f40b 100644 --- a/lib/Screens/plans/list_plans.dart +++ b/lib/Screens/plans/list_plans.dart @@ -11,80 +11,87 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; - -class ListPlans extends StatefulWidget{ +class ListPlans extends StatefulWidget { const ListPlans({super.key}); @override _ListPlansState createState() => _ListPlansState(); } - -class _ListPlansState extends State{ - +class _ListPlansState extends State { late Future> futurePlans; String? userId; + String? orgId; String? token; @override - void initState(){ + void initState() { super.initState(); getToken(); initializeData(); - // futurePlans = fetchPlans(); - } - Future initializeData ()async{ + Future initializeData() async { token = await getToken(); userId = await getUserId(); + orgId = await getOrgId(); - if(token == null || userId == null){ + if (token == null || userId == null) { print("Token or USerId missing"); return; - } - else{ + } else { setState(() { futurePlans = fetchPlans(); }); } } - - Future getUserId() async { final prefs = await SharedPreferences.getInstance(); final String? userDataString = prefs.getString('user_data'); - if(userDataString != null){ - try{ - final Map userData = jsonDecode(userDataString); + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); return userData["user_id"]?.toString(); - }catch(e){ + } catch (e) { return null; } } - return null; + return null; } + Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } 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 String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; - + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; + final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId'; + // api/plans?org_id=1&user_id=1 // final token = await getToken(); - if (token == null) { throw Exception('Token not found. Please log in.'); } @@ -92,7 +99,7 @@ class _ListPlansState extends State{ final response = await http.get( Uri.parse(apiUrldata), headers: { - 'Authorization': 'Bearer $token', // Add token here + 'Authorization': 'Bearer $token', // Add token here 'Content-Type': 'application/json', }, ); @@ -106,8 +113,7 @@ class _ListPlansState extends State{ } } - - Future > getViewPlan(String planId) async{ + Future> getViewPlan(String planId) async { final String apiUrldata = '$apiUrl/api/plans/find/$planId'; print("API URL: $apiUrldata"); // final token = await getToken(); @@ -119,36 +125,32 @@ class _ListPlansState extends State{ final response = await http.put( Uri.parse(apiUrldata), headers: { - 'Authorization': 'Bearer $token', // Add token here + 'Authorization': 'Bearer $token', // Add token here 'Content-Type': 'application/json', }, ); if (response.statusCode == 200) { - final Map? resData = json.decode(response.body); + final Map? resData = json.decode(response.body); return resData?["data"]; - } else { throw Exception('Failed to load plans'); } - - } - - void viewPlan(String planId, {bool isViewMode = false}) async{ + void viewPlan(String planId, {bool isViewMode = false}) async { try { Map planData = await getViewPlan(planId); print("ViewAAA - $planData"); - context.go('/createPlan',extra: {'planData': planData, 'isViewMode': isViewMode} ); + context.go('/createPlan', + extra: {'planData': planData, 'isViewMode': isViewMode}); } catch (e) { print("Error fetching plan: $e"); } } - Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; @@ -192,13 +194,22 @@ class _ListPlansState extends State{ foregroundColor: Colors.white, backgroundColor: Colors.blueAccent), onPressed: () { - context.go('/createPlan'); + context.go('/createPlan', extra: { + // 'apiCountryData': apiCountryData, + 'orgId': orgId, + }); + if (!isDesktop) Navigator.pop(context); }, child: Row( children: [ - Icon(Icons.add_circle,color: Colors.white,), - SizedBox(width: 5,), + Icon( + Icons.add_circle, + color: Colors.white, + ), + SizedBox( + width: 5, + ), Text('NewPlan'), ], ), @@ -206,220 +217,290 @@ class _ListPlansState extends State{ ], ), const SizedBox(height: 10), - FutureBuilder>( - future: futurePlans, // Use the futurePlans variable - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text("Error: ${snapshot.error}")); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text("No plans available")); - } + FutureBuilder>( + future: futurePlans, // Use the futurePlans variable + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.error_outline, + color: Colors.redAccent, + size: 60, + ), + SizedBox(height: 16), + Text( + "Oops!", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.redAccent, + ), + ), + SizedBox(height: 8), + Text( + "No Plans Available For This User", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + SizedBox(height: 20), + Text( + " Please Create Plan", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey[700], + ), + ), + SizedBox(height: 20), + // ElevatedButton.icon( + // onPressed: () { + // // Optional: retry logic or navigation + // }, + // icon: Icon(Icons.refresh), + // label: Text("Try Again"), + // style: ElevatedButton.styleFrom( + // backgroundColor: Colors.blueAccent, + // ), + // ), + ], + ), + ), + ); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return const Center(child: Text("No plans available")); + } - List plans = snapshot.data!; // Extract the list of plans + List plans = snapshot.data!; // Extract the list of plans + // Ensure planId is sorted in descending order + plans.sort((a, b) => int.parse(b.planId.toString()) + .compareTo(int.parse(a.planId.toString()))); - // Ensure planId is sorted in descending order - plans.sort((a, b) => int.parse(b.planId.toString()).compareTo(int.parse(a.planId.toString()))); + // return ResponsiveBuilder( + // builder: (context, sizingInfo) { + // bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop; + // + // return SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // child: Container( + // color: Colors.grey, + // child: SizedBox( + // width: MediaQuery.of(context).size.width , + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + // // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal, + // + // // constraints: isTabletOrDesktop + // // ? const BoxConstraints(maxWidth: double.infinity) + // // : BoxConstraints.tightFor(width: 600), + // + // + // child: DataTable( + // // columnSpacing: 50.0, + // dividerThickness: 0.5, // Reduce the thickness of row dividers + // border: TableBorder( + // horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness + // ), + // columns: const [ + // DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // // + // DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), + // ], + // rows: plans.map((plan) { + // return DataRow(cells: [ + // DataCell(Text(plan.planId)), + // // DataCell(Text(plan.tripTitle)), + // DataCell(Row( + // children: [ + // Flexible( + // child: Text( + // plan.tripTitle, + // softWrap: true, + // overflow: TextOverflow.ellipsis, // Adds "..." if text is too long + // ), + // ), + // ], + // )), + // + // + // DataCell(Text(plan.tripType)), + // DataCell(Text(plan.costCenter)), + // // DataCell(Text(plan.functionalDepartment)), + // // DataCell(Text(plan.purposeOfTravel)), + // // DataCell(Text(plan.description)), + // // + // DataCell(Text(plan.isBillable)), + // DataCell(Text(plan.status)), + // DataCell( + // TextButton( + // onPressed: () { + // viewPlan(plan.planId); + // print("View button clicked for ${plan.planId}"); + // }, + // child: const Text('View', + // style: TextStyle(color: Colors.blueAccent)), + // ), + // ), + // ]); + // }).toList(), + // ), + // + // + // ), + // ), + // ), + // ); + // + // }, + // ); + return Expanded( + child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling + scrollDirection: Axis.vertical, + child: SizedBox( + width: MediaQuery.of(context).size.width * 1.5, + // width: MediaQuery.of(context).size.width , // Ensure table is wider than screen + // width: double.infinity , // Ensure table is wider than screen + child: SingleChildScrollView( + // scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling + scrollDirection: Axis + .horizontal, // Inner wrapper for vertical scrolling - // return ResponsiveBuilder( - // builder: (context, sizingInfo) { - // bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop; - // - // return SingleChildScrollView( - // scrollDirection: Axis.horizontal, - // child: Container( - // color: Colors.grey, - // child: SizedBox( - // width: MediaQuery.of(context).size.width , - // child: SingleChildScrollView( - // scrollDirection: Axis.vertical, - // // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal, - // - // // constraints: isTabletOrDesktop - // // ? const BoxConstraints(maxWidth: double.infinity) - // // : BoxConstraints.tightFor(width: 600), - // - // - // child: DataTable( - // // columnSpacing: 50.0, - // dividerThickness: 0.5, // Reduce the thickness of row dividers - // border: TableBorder( - // horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness - // ), - // columns: const [ - // DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // // - // DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))), - // ], - // rows: plans.map((plan) { - // return DataRow(cells: [ - // DataCell(Text(plan.planId)), - // // DataCell(Text(plan.tripTitle)), - // DataCell(Row( - // children: [ - // Flexible( - // child: Text( - // plan.tripTitle, - // softWrap: true, - // overflow: TextOverflow.ellipsis, // Adds "..." if text is too long - // ), - // ), - // ], - // )), - // - // - // DataCell(Text(plan.tripType)), - // DataCell(Text(plan.costCenter)), - // // DataCell(Text(plan.functionalDepartment)), - // // DataCell(Text(plan.purposeOfTravel)), - // // DataCell(Text(plan.description)), - // // - // DataCell(Text(plan.isBillable)), - // DataCell(Text(plan.status)), - // DataCell( - // TextButton( - // onPressed: () { - // viewPlan(plan.planId); - // print("View button clicked for ${plan.planId}"); - // }, - // child: const Text('View', - // style: TextStyle(color: Colors.blueAccent)), - // ), - // ), - // ]); - // }).toList(), - // ), - // - // - // ), - // ), - // ), - // ); - // - // }, - // ); + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: 1300), + // width: MediaQuery.of(context).size.width , - return Expanded( - child: SingleChildScrollView( - // scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling - scrollDirection: Axis.vertical, - child: SizedBox( - width: MediaQuery.of(context).size.width * 1.5, - // width: MediaQuery.of(context).size.width , // Ensure table is wider than screen - // width: double.infinity , // Ensure table is wider than screen - - child: SingleChildScrollView( - // scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling - scrollDirection: Axis.horizontal, // Inner wrapper for vertical scrolling - - - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: 1300), - // width: MediaQuery.of(context).size.width , - - child: Container( - // color: Colors.amber, - child: DataTable( - columnSpacing: 50.0, // Adjust spacing between columns - dividerThickness: 0.5, - border: TableBorder( - horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), - ), - columns: const [ - DataColumn(label: Text('Plan ID', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Trip Title', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Trip Type', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Cost Center', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Is Billable', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Status', style: TextStyle(fontWeight: FontWeight.bold))), - DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.bold))), - ], - - - - rows: plans.map((plan) { - return DataRow(cells: [ - DataCell(Text(plan.planId)), - DataCell(Text(plan.tripTitle, softWrap: true, overflow: TextOverflow.ellipsis)), - DataCell(Text(plan.tripType)), - DataCell(Text(plan.costCenter)), - DataCell(Text(plan.isBillable)), - DataCell( - Container( - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), // Padding for better look - decoration: BoxDecoration( - color: plan.status == "Active" ? Colors.green.shade50 : Colors.grey.shade50, // Background color - borderRadius: BorderRadius.circular(10), // Rounded corners - ), - child: Text( - plan.status, - style: TextStyle( - color: plan.status == "Active" ? Colors.green : Colors.grey, // Text color - fontWeight: FontWeight.bold, // Optional: Make text bold - ), - ), - ), - ), - DataCell( - Row( - children:[ - - IconButton( - icon: Icon(Icons.remove_red_eye, color: Colors.blue), - onPressed: () { - viewPlan(plan.planId, isViewMode: true); - - }, - ), - IconButton( - icon: Icon(Icons.edit, color: Colors.green), - onPressed: () { - viewPlan(plan.planId, isViewMode: false); - }, - ), - // IconButton( - // icon: Icon(Icons.delete, color: Colors.red), - // onPressed: () { - // deletePlan(plan.planId); - // }, - // ), - - - ] - ) - - ), - ]); - }).toList(), + child: Container( + // color: Colors.amber, + child: DataTable( + columnSpacing: + 50.0, // Adjust spacing between columns + dividerThickness: 0.5, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, color: Colors.grey.shade200), ), + columns: const [ + DataColumn( + label: Text('Plan ID', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Trip Title', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Trip Type', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Cost Center', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Is Billable', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Status', + style: TextStyle( + fontWeight: FontWeight.bold))), + DataColumn( + label: Text('Actions', + style: TextStyle( + fontWeight: FontWeight.bold))), + ], + + rows: plans.map((plan) { + return DataRow(cells: [ + DataCell(Text(plan.planId)), + DataCell(Text(plan.tripTitle, + softWrap: true, + overflow: TextOverflow.ellipsis)), + DataCell(Text(plan.tripType)), + DataCell(Text(plan.costCenter)), + DataCell(Text(plan.isBillable)), + DataCell( + Container( + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: + 10), // Padding for better look + decoration: BoxDecoration( + color: plan.status == "Active" + ? Colors.green.shade50 + : Colors + .grey.shade50, // Background color + borderRadius: BorderRadius.circular( + 10), // Rounded corners + ), + child: Text( + plan.status, + style: TextStyle( + color: plan.status == "Active" + ? Colors.green + : Colors.grey, // Text color + fontWeight: FontWeight + .bold, // Optional: Make text bold + ), + ), + ), + ), + DataCell(Row(children: [ + IconButton( + icon: Icon(Icons.remove_red_eye, + color: Colors.blue), + onPressed: () { + viewPlan(plan.planId, isViewMode: true); + }, + ), + IconButton( + icon: Icon(Icons.edit, color: Colors.green), + onPressed: () { + viewPlan(plan.planId, isViewMode: false); + }, + ), + // IconButton( + // icon: Icon(Icons.delete, color: Colors.red), + // onPressed: () { + // deletePlan(plan.planId); + // }, + // ), + ])), + ]); + }).toList(), ), ), ), ), ), - ); - - - - - }, - ), - + ), + ); + }, + ), ], ), ); } } - diff --git a/lib/Screens/policy/policy.dart b/lib/Screens/policy/policy.dart index 4fe056a..bad4648 100644 --- a/lib/Screens/policy/policy.dart +++ b/lib/Screens/policy/policy.dart @@ -6,6 +6,8 @@ import 'package:responsive_builder/responsive_builder.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; +import '../../widgets/custom_text_field.dart'; +import '../../widgets/custom_user_form.dart'; class Policy extends StatefulWidget { const Policy({super.key}); @@ -18,6 +20,7 @@ class _PolicyState extends State { late String policyType = "domestic"; int? selectedServiceIndex = 1; late String selectedService = "Train"; + String? _selectedTripType; bool showClass = true; bool showCost = true; @@ -42,136 +45,206 @@ class _PolicyState extends State { } Widget buildPolicyLayout(bool isDesktop) { - return Container( - margin: isDesktop - ? EdgeInsets.all(20.0) - : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - border: isDesktop - ? Border.all( - width: 3, - color: Color(0xFFF7F7FB), - ) - : null, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - color: Color(0xFFF7F7FB), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), - // color: Colors.white, // Background to avoid overlapping - color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - "Choose Policy Type", - style: TextStyle( - fontSize: 18, - color: Colors.black, + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + margin: isDesktop + ? EdgeInsets.all(20.0) + : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + border: isDesktop + ? Border.all( + width: 2, + color: Color(0xFFF7F7FB), + ) + : null, + color: Color(0xFFF7F7FB), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // color: Color(0xFFF7F7FB), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3), + // color: Colors.white, // Background to avoid overlapping + color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "Choose Policy Type", + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), ), - ), - ], + ], + ), ), - ), - // Container( - // child: Row( - // children: [ - // Expanded( - // child: GestureDetector( - // onTap: () { - // setState(() { - // policyType = "domestic"; - // }); - // }, - // child: Container( - // padding: EdgeInsets.all(10), - // color: policyType == "domestic" - // ? Colors.blueAccent.shade100 - // : Color(0xFFEBEBF7), - // // color: Colors.blue.shade300, - // child: Column( - // children: [ - // Text( - // "Domestic", - // style: TextStyle( - // color: policyType == "domestic" - // ? Colors.white - // : Colors.black87, - // fontSize: 15, - // fontWeight: FontWeight.bold), - // ), - // ], - // ), - // ), - // ), - // ), - // Expanded( - // child: GestureDetector( - // onTap: () { - // setState(() { - // policyType = "international"; - // }); - // }, - // child: Container( - // padding: EdgeInsets.all(10), - // // color: Color(0xFFEBEBF7), - // color: policyType == "international" - // ? Colors.blueAccent.shade100 - // : Color(0xFFEBEBF7), - // // color: Color(0xFFE3F2FD), - // - // child: Column( - // children: [ - // Text( - // "International", - // style: TextStyle( - // color: policyType == "international" - // ? Colors.white - // : Colors.black87, - // fontSize: 15, - // fontWeight: FontWeight.bold), - // ), - // ], - // ), - // ), - // )), - // ], - // ), - // ), - // - ], + // Container( + // child: Row( + // children: [ + // Expanded( + // child: GestureDetector( + // onTap: () { + // setState(() { + // policyType = "domestic"; + // }); + // }, + // child: Container( + // padding: EdgeInsets.all(10), + // color: policyType == "domestic" + // ? Colors.blueAccent.shade100 + // : Color(0xFFEBEBF7), + // // color: Colors.blue.shade300, + // child: Column( + // children: [ + // Text( + // "Domestic", + // style: TextStyle( + // color: policyType == "domestic" + // ? Colors.white + // : Colors.black87, + // fontSize: 15, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // ), + // ), + // Expanded( + // child: GestureDetector( + // onTap: () { + // setState(() { + // policyType = "international"; + // }); + // }, + // child: Container( + // padding: EdgeInsets.all(10), + // // color: Color(0xFFEBEBF7), + // color: policyType == "international" + // ? Colors.blueAccent.shade100 + // : Color(0xFFEBEBF7), + // // color: Color(0xFFE3F2FD), + // + // child: Column( + // children: [ + // Text( + // "International", + // style: TextStyle( + // color: policyType == "international" + // ? Colors.white + // : Colors.black87, + // fontSize: 15, + // fontWeight: FontWeight.bold), + // ), + // ], + // ), + // ), + // )), + // ], + // ), + // ), + // + ], + ), ), - ), - SizedBox( - height: 10, - ), - isDesktop - ? Expanded( - child: Row( - children: [ - _buildPolicyCategoryList(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), - ) - : Expanded( - child: Column( - children: [ - _buildPolicyCategoryList(isDesktop), - _buildPolicyCategory(isDesktop), - ], - ), - ) - ], + isDesktop ? SizedBox(height: 0) : SizedBox(height: 5), + Container( + // color: Colors.amber, + // color: isDesktop ? Color(0xFFF7F7FB) : Colors.white, + padding: isDesktop ? const EdgeInsets.only(left: 35) : null, + child: Column( + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Policy Name", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + CustomTextFieldUserWrapper( + isFocused: false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: TextField( + style: TextStyle(fontSize: 12), + // controller: controllers["Fname"], + // enabled: !isViewMode, + onChanged: (value) {}, + decoration: InputDecoration( + labelText: "Policy Name", + labelStyle: TextStyle( + fontSize: 12, color: Colors.grey), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ), + ], + ), + ], + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Policy Type", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.black)), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildTripType(isDesktop), + ) + ], + ) + ], + ), + ], + )), + SizedBox( + height: 10, + ), + isDesktop + ? Expanded( + child: Row( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + ) + : Expanded( + child: Column( + children: [ + _buildPolicyCategoryList(isDesktop), + _buildPolicyCategory(isDesktop), + ], + ), + ) + ], + ), ), ); } @@ -224,8 +297,10 @@ class _PolicyState extends State { return SizedBox( width: isDesktop ? 180 : null, height: isDesktop - ? max((MediaQuery.of(context).size.height * 0.09), 10) + ? max((MediaQuery.of(context).size.height * 0.075), 10) : 45, + + // max((MediaQuery.of(context).size.height * 0.09), 10) child: GestureDetector( onTap: () { print("Selected Services - $service - $index"); @@ -288,4 +363,64 @@ class _PolicyState extends State { selectedTab: selectedService)), ); } + + List _buildTripType(bool isDesktop) { + return [ + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + width: isDesktop ? 170 : 140, + isFocused: _selectedTripType == "1", + isDesktop: isDesktop, + child: SizedBox( + height: 35, + child: Material( + color: Colors.transparent, + child: RadioListTile( + activeColor: Colors.blueAccent, + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + dense: true, + title: Text("Domestic"), + value: "1", + groupValue: _selectedTripType, + onChanged: (value) { + setState(() { + _selectedTripType = value!; + }); + }, + ), + ), + ), + ), + isDesktop ? SizedBox(width: 28) : SizedBox(width: 15), + CustomTextFieldWrapper( + color: Color(0xFFF4F4FB), + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + width: isDesktop ? 180 : 180, + isFocused: _selectedTripType == "2", + isDesktop: isDesktop, + child: SizedBox( + height: 35, + child: Material( + color: Colors.transparent, + child: RadioListTile( + activeColor: Colors.blueAccent, + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + dense: true, + title: Text("International"), + value: "2", + groupValue: _selectedTripType, + onChanged: (value) { + setState(() { + _selectedTripType = value!; + }); + }, + ), + ), + ), + ), + ]; + } } diff --git a/lib/Screens/policy/policyCriteria.dart b/lib/Screens/policy/policyCriteria.dart index 85d759c..d045a1f 100644 --- a/lib/Screens/policy/policyCriteria.dart +++ b/lib/Screens/policy/policyCriteria.dart @@ -42,6 +42,7 @@ class _PolicyCriteriaState extends State { Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5), Row( @@ -56,22 +57,13 @@ class _PolicyCriteriaState extends State { ) ], ), - widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5), - Padding( - padding: widget.isDesktop - ? const EdgeInsets.all(8.0) - : const EdgeInsets.all(1.0), - child: Row( - children: _buildTripType(widget.isDesktop), - ), - ), if (widget.isClass!) widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5), Row( children: [ Expanded( child: Container( - // color:Colors.grey, + // color: Colors.grey, padding: const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5), child: Column( @@ -181,19 +173,20 @@ class _PolicyCriteriaState extends State { children: [ Expanded( child: Container( + color: Colors.grey.shade100, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.63 : 600, child: Column( children: [ Container( - margin: const EdgeInsets.only(right: 20), + margin: const EdgeInsets.only(right: 0), decoration: BoxDecoration( - color: Colors.grey.shade50, - border: Border.all( - color: Colors.grey.shade50, - ), - borderRadius: BorderRadius.circular(8)), + color: Colors.grey.shade100, + border: Border.all( + color: Colors.grey.shade100, + ), + ), padding: const EdgeInsets.only( top: 10, bottom: 10, left: 35, right: 35), child: Row( @@ -232,7 +225,7 @@ class _PolicyCriteriaState extends State { child: Container( // color: Colors.grey, margin: const EdgeInsets.only(right: 20), - color: Colors.grey.shade50, + color: Colors.grey.shade100, child: Column(children: [ Container( padding: const EdgeInsets.all(10), @@ -511,57 +504,4 @@ class _PolicyCriteriaState extends State { ], ); } - - List _buildTripType(bool isMobile) { - return [ - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), - width: 120, - isFocused: _selectedTripType == "1", - isDesktop: widget.isDesktop, - child: SizedBox( - height: 35, - child: Material( - color: Colors.transparent, - child: RadioListTile( - activeColor: Colors.blueAccent, - contentPadding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, - dense: true, - title: Text("Domestic"), - value: "1", - groupValue: _selectedTripType, - onChanged: (value) { - setState(() { - _selectedTripType = value!; - }); - }, - ), - ), - ), - ), - SizedBox(width: 20), - CustomTextFieldWrapper( - color: Color(0xFFF4F4FB), - width: 150, - padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2), - isFocused: _selectedTripType == "2", - isDesktop: widget.isDesktop, - child: RadioListTile( - activeColor: Colors.blueAccent, - contentPadding: EdgeInsets.zero, - dense: true, - title: Text("International"), - value: "2", - groupValue: _selectedTripType, - onChanged: (value) { - setState(() { - _selectedTripType = value!; - }); - }, - ), - ), - ]; - } } diff --git a/lib/Screens/userManagement/create_user/create_user.dart b/lib/Screens/userManagement/create_user/create_user.dart index 77c7df8..5d289a9 100644 --- a/lib/Screens/userManagement/create_user/create_user.dart +++ b/lib/Screens/userManagement/create_user/create_user.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; import 'dart:typed_data'; // Import for Uint8List @@ -8,11 +9,14 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; +import 'package:http_parser/http_parser.dart' as http_parser; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http_parser/http_parser.dart'; import '../../../config/apiUrl.dart'; import '../../../routes/custom_appBar.dart'; @@ -31,6 +35,7 @@ class _CreateUserFormState extends State { final ApiService apiService = ApiService(); String? userId; + String? orgId; String? token; @@ -67,8 +72,11 @@ class _CreateUserFormState extends State { String? selectedFileNames; Uint8List? passportDocumentBytes; + String? passportFileUrlFromApi; String? base64PDF; + html.File? passportFile; + List dataHeader = [ "Fname", "Lname", @@ -109,12 +117,13 @@ class _CreateUserFormState extends State { "address": controllers["address"]?.text, "gender": selectedGender, "postal_code": controllers["postalCode"]?.text, - "country": selectedCountry, + "country_code": selectedCountry, "employee_code": controllers["employeeCode"]?.text, "user_type": selectedUserType, "role_id": selectedRole, "department_id": selectedDepartment, + "group_id": selectedLevel, "first_approver": selectedFirstApprover, @@ -122,21 +131,22 @@ class _CreateUserFormState extends State { "third_approver": selectedThirdApprover, "passport_number": controllers["passportNumber"]?.text, "place_of_issue": controllers["placeOfIssue"]?.text, - "passport_document": base64PDF, + "passport_document": passportFile, "date_of_issue": controllers["dateOfIssue"]?.text, "date_of_expiry": controllers["dateOfExpiry"]?.text, "created_by": userId, "is_active": "1", - // "passport_fileData": base64PDF, + "org_id": orgId, + // "passport_fileData": passportFile, }; return data; } - void updateData() { + Future updateData() async { // Ensure apiselectedUser is not null before printing if (apiselectedUser != null) { - print("API Selected User Has Data - $apiselectedUser"); + print("API Selected User Has Data - $widget.apiselectedUser"); setState(() { // ✅ Wrap in setState to update the UI @@ -165,15 +175,22 @@ class _CreateUserFormState extends State { apiselectedUser?["date_of_expiry"] ?? ""; selectedCountry = apiselectedUser?["country_code"]?.toString() ?? ""; + selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? ""; - base64PDF = - apiselectedUser?["passport_document"]?.toString().trim() ?? ""; - selectedUserType = + selectedUserType = selectedUserType = apiselectedUser?["user_type"]?.toString().trim() ?? ""; + selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? ""; - selectedDepartment = - apiselectedUser?["department_id"]?.toString().trim() ?? ""; + // selectedDepartment = + // apiselectedUser?["department_id"]?.toString().trim() ?? ""; + + if (apiselectedUser?["department_id"] != null) { + selectedDepartment = apiselectedUser!["department_id"].toString(); + } + // print( + // "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} "); + selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? ""; selectedFirstApprover = @@ -184,6 +201,19 @@ class _CreateUserFormState extends State { apiselectedUser?["third_approver"]?.toString() ?? ""; print("Updated selectedGender: $selectedGender"); // Debugging + + // ✅ Load passport document from API + String? apiDocPath = apiselectedUser?["passport_document"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + passportFileUrlFromApi = apiDocPath; + selectedFileNames = + apiDocPath.split('/').last; // Extract filename from path + passportFile = null; // No local file selected yet + } else { + passportFileUrlFromApi = null; + selectedFileNames = null; + passportFile = null; + } }); } else { print("API Selected User Has Data - No data available yet"); @@ -195,37 +225,29 @@ class _CreateUserFormState extends State { super.initState(); WidgetsFlutterBinding.ensureInitialized(); + // Step 1: Set 'reloaded' flag before page unload + html.window.onBeforeUnload.listen((event) { + html.window.localStorage['reloaded'] = 'true'; + }); + // apiCountryData = extraData['apiCountryData']; // Extract apiCountryData // futureUsers = extraData['apiUserData']; // Extract futureUsers (Future>) apiCountryData = null; apiUserData = null; - apiselectedUser = null; + // apiselectedUser = null; apiCostData = null; apiRoleData = null; - // Delay accessing context until the widget is fully initialized - // WidgetsBinding.instance.addPostFrameCallback((_) { - // setState(() { - // apiCountryData = (GoRouterState.of(context).extra as Map)['apiCountryData']; - // apiUserData = (GoRouterState.of(context).extra as Map)['apiUserData']; - // apiselectedUser = (GoRouterState.of(context).extra as Map)['selectedUser']; - // - // - // userList = apiUserData ?? []; - // userMap = { - // for (var user in userList) - // user['user_id'] as String: "${user['first_name']} ${user['last_name']}" - // }; - // - // userIdsApi = userMap.keys.toList(); - // - // - // }); - // }); - // Initialize controllers for each field WidgetsBinding.instance.addPostFrameCallback((_) async { + // final wasReloaded = html.window.localStorage['reloaded'] == 'true'; + // + // if (wasReloaded) { + // html.window.localStorage.remove('reloaded'); // Clear it + // context.go('/listUser'); // Navigate using go_router + // } + final extraData = GoRouterState.of(context).extra as Map?; @@ -250,6 +272,8 @@ class _CreateUserFormState extends State { isEditProfile = extraData['isEditProfile'] ?? false; }); + print("selectedUser: $apiselectedUser"); + // Add another post-frame callback to check after setState await Future.delayed(Duration( milliseconds: 100)); // Optional delay to ensure UI has updated @@ -296,7 +320,7 @@ class _CreateUserFormState extends State { setState(() { // apiUserData = users; - apiUserData = users.where((user) => user["role_id"] == "3").toList(); + apiUserData = users.where((user) => user["role_id"] == "4").toList(); print("APIUSerDATa - $apiUserData"); @@ -383,7 +407,7 @@ class _CreateUserFormState extends State { super.dispose(); } - void handleSubmit() { + void handleSubmit() async { print("USR Detail Submit"); printFormData(); @@ -396,6 +420,8 @@ class _CreateUserFormState extends State { return; // Stop execution if validation fails } else { print("USERDETAILS : $userDetials"); + orgId = await getOrgId(); + createUserData(userDetials); } } @@ -464,133 +490,102 @@ class _CreateUserFormState extends State { uploadInput.onChange.listen((e) { final file = uploadInput.files!.first; - final reader = html.FileReader(); - reader.readAsArrayBuffer(file); - reader.onLoadEnd.listen((event) { - print('File picked: ${file.name}'); - print('File size: ${file.size} bytes'); + // Ensure the file is a PDF + if (!file.type.contains("pdf")) { + print("Error: Not a PDF file"); + return; + } - // Ensure the file is a PDF - if (!file.type.contains("pdf")) { - print("Error: Not a PDF file"); - return; - } + // 🔹 File size check: Ensure it does not exceed 3MB + int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes + if (file.size > maxFileSize) { + print('Error: File size exceeds 3MB'); + return; + } - setState(() { - selectedFileNames = file.name; // Store file name - passportDocumentBytes = reader.result as Uint8List; // Store file data - - // 🔹 Convert to Base64 properly - base64PDF = base64Encode(passportDocumentBytes!); - - print('Base64 Length: ${base64PDF!.length}'); - print('Base64 (first 50 chars): ${base64PDF!.substring(0, 50)}'); - - // Ensure Base64 starts with "JVBERi0x" - if (!base64PDF!.startsWith("JVBERi0x")) { - print("Error: Base64 does not start with 'JVBERi0x'"); - return; - } - - // 🔹 File size check: Ensure it does not exceed 3MB - int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes - if (file.size > maxFileSize) { - print('Error: File size exceeds 3MB'); - return; - } - }); + setState(() { + selectedFileNames = file.name; + passportFile = file; + passportFileUrlFromApi = null; }); + + print('PDF File selected: ${file.name}'); }); } - // void pickPDFWeb() { - // html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); - // uploadInput.accept = '.pdf'; - // uploadInput.click(); - // - // uploadInput.onChange.listen((e) { - // final file = uploadInput.files!.first; - // final reader = html.FileReader(); - // - // reader.readAsArrayBuffer(file); - // reader.onLoadEnd.listen((event) { - // print('File picked: ${file.name}'); - // print('File size: ${file.size} bytes'); - // - // - // // Update the state with the selected file name - // setState(() { - // selectedFileNames = file.name; // Store only one file name - // passportDocumentBytes = reader.result as Uint8List; // Store file data - // - // // 🔹 Convert file to Base64 - // base64PDF = base64Encode(passportDocumentBytes! as List); - // // File size check: Ensure the file size does not exceed 3MB - // int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes - // - // if (file.size > maxFileSize) { - // print('Error: File size exceeds 3MB'); - // // You can show an error message here if necessary - // // For example: - // // showError('File size cannot exceed 3MB'); - // return; - // } - // - // print('File size: ${file.size} bytes'); - // print('Base64 Data: $base64PDF'); // Debugging - // }); - // - // - // - // }); - // }); - // } - Future createUserData(Map userData) async { - bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty; - final String apiUrldata = isUpdating - ? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}' - : '$apiUrl/api/users/create'; + final bool isUpdating = + apiselectedUser != null && apiselectedUser!.isNotEmpty; + final uri = Uri.parse( + isUpdating + ? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}' + : '$apiUrl/api/users/create', + ); if (token == null) { throw Exception('Token not found. Please log in.'); } - // Add user_id only if updating + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + + // If updating, spoof the method Laravel-style if (isUpdating) { - userData['user_id'] = apiselectedUser?["user_id"]; + request.fields['_method'] = 'PUT'; + request.fields['user_id'] = apiselectedUser!["user_id"].toString(); } + // Add all non-null and non-empty user data fields + userData.forEach((key, value) { + if (value != null && value.toString().trim().isNotEmpty) { + request.fields[key] = value.toString(); + } + }); + + // Attach file if selected + if (passportFile != null) { + try { + final reader = html.FileReader(); + reader.readAsArrayBuffer(passportFile!); + await reader.onLoad.first; + + final data = reader.result as Uint8List; + + final multipartFile = http.MultipartFile.fromBytes( + 'passport_document', + data, + filename: passportFile!.name, + ); + + request.files.add(multipartFile); + print("📎 File attached: ${passportFile!.name}"); + } catch (e) { + print("❌ Failed to read file: $e"); + } + } else { + print("⚠️ No passport file selected."); + } + + print("🚀 Sending request with fields: ${request.fields}"); + try { - final response = isUpdating - ? await http.put( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }, - body: jsonEncode(userData), - ) - : await http.post( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }, - body: jsonEncode(userData), - ); + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); if (response.statusCode == 200 || response.statusCode == 201) { - print("Plan submitted successfully!"); - print("Response: ${response.body}"); + print("✅ User submitted successfully!"); + print("📨 Response: ${response.body}"); context.go('/listUser'); } else { - print("Failed to submit plan. Status: ${response.statusCode}"); - print("Error: ${response.body}"); + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); } } catch (e) { - print(" Error submitting plan: $e"); + print("🔥 Error submitting user: $e"); } } @@ -755,6 +750,7 @@ class _CreateUserFormState extends State { child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ // Expanded(child: _buildFirstRowLeftColumn(isDesktop)), // SizedBox(width: 20), @@ -1136,7 +1132,7 @@ class _CreateUserFormState extends State { _selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today) ? _selectedDateOfBirth! : today, - firstDate: today, + firstDate: DateTime(1900), lastDate: DateTime(2100), ); @@ -1436,21 +1432,7 @@ class _CreateUserFormState extends State { ), SizedBox(height: 3), apiselectedUser != null - ? Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Change Password", - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w200, - color: Colors.black)), - SizedBox(height: 5), - ], - ), - ], - ) + ? SizedBox() : Row( children: [ Column( @@ -1876,7 +1858,7 @@ class _CreateUserFormState extends State { ), ), SizedBox(height: 10), - if (base64PDF != null) + if (passportFile != null || passportFileUrlFromApi != null) // Centers the text Container( @@ -1890,57 +1872,46 @@ class _CreateUserFormState extends State { children: [ GestureDetector( onTap: () { - print('DOWLOAS- $base64PDF '); + print('DOWNLOAD - $passportFile'); - if (base64PDF != null && base64PDF!.isNotEmpty) { + if (passportFile != null) { try { - // ✅ Step 1: Clean the Base64 string - String cleanedBase64 = base64PDF! - .replaceAll("\n", "") // Remove newlines - .replaceAll( - "\r", "") // Remove carriage returns - .replaceAll(" ", "") // Remove spaces - .trim(); // Trim any whitespace + // ✅ Step 1: Create a Blob directly from the file + final blob = html.Blob( + [passportFile!], 'application/pdf'); - // ✅ Step 2: Ensure valid Base64 length (multiple of 4) - while (cleanedBase64.length % 4 != 0) { - cleanedBase64 += "_"; // Add '=' padding - } - - // ✅ Step 3: Decode the cleaned Base64 - Uint8List bytes; - try { - bytes = base64Decode(cleanedBase64); - } catch (e) { - print("Base64 decoding failed: $e"); - return; - } - - // ✅ Step 4: Create a Blob for download - final blob = - html.Blob([bytes], 'application/pdf'); + // ✅ Step 2: Generate a download URL from the Blob final url = html.Url.createObjectUrlFromBlob(blob); - // ✅ Step 5: Trigger the file download + // ✅ Step 3: Create an invisible anchor to trigger download final anchor = html.AnchorElement(href: url) ..setAttribute("download", selectedFileNames ?? "document.pdf") ..style.display = "none"; + // ✅ Step 4: Add anchor to DOM and click it html.document.body!.append(anchor); anchor.click(); - // ✅ Step 6: Clean up + // ✅ Step 5: Clean up anchor.remove(); html.Url.revokeObjectUrl(url); - print("Download successful!"); + print("Download triggered successfully!"); } catch (e) { - print("Error downloading file: $e"); + print("Error during download: $e"); } + } else if (passportFileUrlFromApi != null) { + // Trigger file download from the server path + final anchor = html.AnchorElement( + href: passportFileUrlFromApi!) + ..target = 'blank' + ..download = + selectedFileNames ?? "document.pdf" + ..click(); } else { - print("No file to download."); + print("No file available to download."); } }, child: Container( diff --git a/lib/Screens/userManagement/user_List.dart b/lib/Screens/userManagement/user_List.dart index c6657df..11299d7 100644 --- a/lib/Screens/userManagement/user_List.dart +++ b/lib/Screens/userManagement/user_List.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:frontend/utils/auth_utils.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:responsive_builder/responsive_builder.dart'; @@ -18,6 +19,7 @@ class _UserListScreenState extends State { late Future> futureUsers; List? apiCountryData; String? selectedUserId; + String? orgId; Future getToken() async { final prefs = await SharedPreferences.getInstance(); @@ -25,7 +27,8 @@ class _UserListScreenState extends State { } Future> fetchUsers() async { - final String apiUrlData = '$apiUrl/api/users'; + orgId = await getOrgId(); + final String apiUrlData = '$apiUrl/api/users?org_id=$orgId'; final String? token = await getToken(); print("Fetch Users"); @@ -110,7 +113,59 @@ class _UserListScreenState extends State { print("handDel - $userId"); } - void handleToggleUserStatus(String userId, String currentStatus) async { + Future createUserData( + Map userData, String userId, String newStatus) async { + final uri = Uri.parse('$apiUrl/api/users/update/$userId'); + + final String? token = await getToken(); + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + + // If updating, spoof the method Laravel-style + + request.fields['_method'] = 'PUT'; + request.fields['user_id'] = userId; + + print("STatus 2 - $newStatus"); + + // Add all non-null and non-empty user data fields + userData.forEach((key, value) { + if (value != null && value.toString().trim().isNotEmpty) { + request.fields[key] = value.toString(); + } + }); + + request.fields['is_active'] = newStatus; + + print("🚀 Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + print("✅ User Status submitted successfully! "); + print("📨 Response: ${response.body}"); + + refreshUserList(); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + void handleToggleUserStatus(String userId, String currentStatus, + Map userData) async { print("Toggling user status - $userId (Current: $currentStatus)"); final String apiUrlData = @@ -125,28 +180,32 @@ class _UserListScreenState extends State { // Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1") String newStatus = (currentStatus == "1") ? "0" : "1"; - try { - final response = await http.put( - Uri.parse(apiUrlData), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }, - body: jsonEncode({ - "is_active": newStatus // Set new status dynamically - }), - ); + print("STatus 1 - $newStatus"); - if (response.statusCode == 200) { - print("User status updated successfully to $newStatus!"); - refreshUserList(); // Refresh users list after update - } else { - print("Failed to update user status. Status: ${response.statusCode}"); - print("Error: ${response.body}"); - } - } catch (e) { - print("Error updating user status: $e"); - } + createUserData(userData, userId, newStatus); + + // try { + // final response = await http.put( + // Uri.parse(apiUrlData), + // headers: { + // 'Authorization': 'Bearer $token', + // 'Content-Type': 'application/json', + // }, + // body: jsonEncode({ + // "is_active": newStatus // Set new status dynamically + // }), + // ); + // + // if (response.statusCode == 200) { + // print("User status updated successfully to $newStatus!"); + // refreshUserList(); // Refresh users list after update + // } else { + // print("Failed to update user status. Status: ${response.statusCode}"); + // print("Error: ${response.body}"); + // } + // } catch (e) { + // print("Error updating user status: $e"); + // } } // Refresh user list after update @@ -207,10 +266,12 @@ class _UserListScreenState extends State { // Print the resolved value print("CREATELIAS - $users"); - context.go("/CreateUserDetails", extra: { - // 'apiCountryData': apiCountryData, - 'apiUserData': users, - }); + context.go("/CreateUserDetails" + // extra: { + // // 'apiCountryData': apiCountryData, + // 'apiUserData': users, + // } + ); if (!isDesktop) Navigator.pop(context); }, child: Row( @@ -235,7 +296,60 @@ class _UserListScreenState extends State { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { - return Center(child: Text("Error: ${snapshot.error}")); + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.error_outline, + color: Colors.redAccent, + size: 60, + ), + SizedBox(height: 16), + Text( + "Oops!", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.redAccent, + ), + ), + SizedBox(height: 8), + Text( + "No User Available", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey, + ), + ), + SizedBox(height: 20), + Text( + " Please Create NewUser", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey[700], + ), + ), + SizedBox(height: 20), + // ElevatedButton.icon( + // onPressed: () { + // // Optional: retry logic or navigation + // }, + // icon: Icon(Icons.refresh), + // label: Text("Try Again"), + // style: ElevatedButton.styleFrom( + // backgroundColor: Colors.blueAccent, + // ), + // ), + ], + ), + ), + ); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return Center(child: Text("No users found")); } @@ -467,8 +581,8 @@ class _UserListScreenState extends State { )), DataCell(GestureDetector( onTap: () { - handleToggleUserStatus( - user['user_id'], user['is_active']); + handleToggleUserStatus(user['user_id'], + user['is_active'], user); }, child: Text( user['is_active'] == "1" @@ -519,6 +633,13 @@ class _UserListScreenState extends State { ? null : () { print("USER: $user"); + + // final userJson = jsonEncode( + // user); // Convert user map to string + // final encodedUser = + // Uri.encodeComponent( + // userJson); + context.go( "/CreateUserDetails", extra: { @@ -529,25 +650,6 @@ class _UserListScreenState extends State { }, ), ), - MouseRegion( - cursor: user['is_active'] == "0" - ? SystemMouseCursors.forbidden - : SystemMouseCursors.click, - child: IconButton( - icon: Icon(Icons.delete, - color: user['is_active'] == "0" - ? Colors.grey - : Colors.redAccent), - onPressed: user['is_active'] == "0" - ? null - : () { - print( - "USER ID: ${user['user_id']}"); - var userId = user['user_id']; - handleDelete(userId); - }, - ), - ), ], ), ), diff --git a/lib/routes/custom_drawer.dart b/lib/routes/custom_drawer.dart index fa5487f..903a15c 100644 --- a/lib/routes/custom_drawer.dart +++ b/lib/routes/custom_drawer.dart @@ -5,86 +5,77 @@ import 'package:go_router/go_router.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; - - class CustomDrawer extends StatefulWidget{ - - final bool isDesktop; - const CustomDrawer({super.key, required this.isDesktop}); - - @override - _CustomDrawerState createState() => _CustomDrawerState(); - - } - -class _CustomDrawerState extends State{ - - String? token; - Map? userData; - Map? fetchedUserData; - Map userDetails = {}; - - @override - void initState() { - super.initState(); - initializeData(); - } - - Future initializeData() async{ - - print("initializeDatainitializeData"); - token = await getToken(); - fetchedUserData = await getUserData(); - - if(token == null || fetchedUserData == null) - { - print("Token or USerId missing"); - return; - } - - setState(() { - userData = fetchedUserData; - }); - - } - - - Future getToken() async{ - final prefs = await SharedPreferences.getInstance(); - return prefs.getString("auth_token"); - } - - Future ?> getUserData() async { - final prefs = await SharedPreferences.getInstance(); - final String? userDataString = prefs.getString('user_data'); - - if(userDataString != null){ - try{ - userDetails = jsonDecode(userDataString); - - return{ - "user_id" : userDetails["user_id"].toString(), - "name" : "${userDetails["first_name"]} ${userDetails["last_name"]}", - "email" : userDetails["email"] ?? "", - }; - }catch (e) { - print("Error decoding user data: $e"); - return null; - } - } - return null; - } - - +class CustomDrawer extends StatefulWidget { + final bool isDesktop; + const CustomDrawer({super.key, required this.isDesktop}); @override - Widget build(BuildContext context){ + _CustomDrawerState createState() => _CustomDrawerState(); +} + +class _CustomDrawerState extends State { + String? token; + Map? userData; + Map? fetchedUserData; + Map userDetails = {}; + + @override + void initState() { + super.initState(); + initializeData(); + } + + Future initializeData() async { + print("initializeDatainitializeData"); + token = await getToken(); + fetchedUserData = await getUserData(); + + if (token == null || fetchedUserData == null) { + print("Token or USerId missing"); + return; + } + + setState(() { + userData = fetchedUserData; + }); + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString("auth_token"); + } + + Future?> getUserData() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + userDetails = jsonDecode(userDataString); + + return { + "user_id": userDetails["user_id"].toString(), + "name": "${userDetails["first_name"]} ${userDetails["last_name"]}", + "email": userDetails["email"] ?? "", + }; + } catch (e) { + print("Error decoding user data: $e"); + return null; + } + } + return null; + } + + @override + Widget build(BuildContext context) { Widget drawerContent = Container( color: Color(0xFFF3F3FA), child: Column( children: [ GestureDetector( - onTap:(){ + onTap: () { print("ONTAP Custom"); + print("ONTAP Custom- $userDetails "); context.go( "/CreateUserDetails", extra: { @@ -94,69 +85,71 @@ class _CustomDrawerState extends State{ }, ); }, - child :SizedBox( - height: 80, - child: Container( - color: Color(0xFFF3F3FA), - padding: EdgeInsets.all(16), - width: double.infinity, - child: Row( - - children: [ - Padding( - padding: const EdgeInsets.all(2.0), - child: Container( - height: 50, - width: 50, - decoration:BoxDecoration( - color: Colors.blueAccent, - shape: BoxShape.circle - ) , - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ Text( - userData?["name"]?.isNotEmpty == true - ? userData!["name"]![0].toUpperCase() - : "N/A", - style: TextStyle(color: Colors.white, fontSize: 25), - ),],), - ), - ), - - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - userData?["name"] ?? "N/A", - style: TextStyle(color: Colors.black87, fontSize: 11), - ), - Text( - userData?["email"] ?? "N/A", - style: TextStyle(color: Colors.black45, fontSize: 10), - ), - - ],) - ],) + child: SizedBox( + height: 80, + child: Container( + color: Color(0xFFF3F3FA), + padding: EdgeInsets.all(16), + width: double.infinity, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(2.0), + child: Container( + height: 50, + width: 50, + decoration: BoxDecoration( + color: Colors.blueAccent, shape: BoxShape.circle), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + userData?["name"]?.isNotEmpty == true + ? userData!["name"]![0].toUpperCase() + : "N/A", + style: TextStyle( + color: Colors.white, fontSize: 25), + ), + ], + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + userData?["name"] ?? "N/A", + style: + TextStyle(color: Colors.black87, fontSize: 11), + ), + Text( + userData?["email"] ?? "N/A", + style: + TextStyle(color: Colors.black45, fontSize: 10), + ), + ], + ) + ], + )), ), ), - ), - _buildDrawerItem(context, Icons.home,'Home', '/home'), - _buildExpandableItem(context,Icons.assessment,'Plans',[ - _buildSubDrawerItem(context,'My Travel Request','/listPlan'), + _buildDrawerItem(context, Icons.home, 'Home', '/home'), + _buildExpandableItem(context, Icons.assessment, 'Plans', [ + _buildSubDrawerItem(context, 'My Travel Request', '/listPlan'), // _buildSubDrawerItem(context,'PlanB','/PlanB') ]), - _buildExpandableItem(context,Icons.account_circle_outlined,'User ',[ - _buildSubDrawerItem(context,'User List','/listUser'), + _buildExpandableItem( + context, Icons.account_circle_outlined, 'User ', [ + _buildSubDrawerItem(context, 'User List', '/listUser'), // _buildSubDrawerItem(context,'PlanB','/PlanB') ]), - - _buildExpandableItem(context,Icons.policy,'Policy ',[ - _buildSubDrawerItem(context,'Policy','/Policy'), + _buildExpandableItem(context, Icons.policy, 'Policy ', [ + _buildSubDrawerItem(context, 'Policy', '/Policy'), // _buildSubDrawerItem(context,'PlanB','/PlanB') ]), - _buildDrawerItem(context,Icons.logout,'Logout','/') - ], + _buildDrawerItem(context, Icons.logout, 'Logout', '/') + ], ), ); @@ -169,17 +162,17 @@ class _CustomDrawerState extends State{ ); } else { // Drawer for Mobile & Tablet** - return Drawer(child: ListView(padding: EdgeInsets.zero, children: [drawerContent])); + return Drawer( + child: ListView(padding: EdgeInsets.zero, children: [drawerContent])); } - } /// **Reusable Drawer Item** - Widget _buildDrawerItem(BuildContext context, IconData icon, String title, String route) - { + Widget _buildDrawerItem( + BuildContext context, IconData icon, String title, String route) { return ListTile( - leading: Icon(icon), - title: Text(title), + leading: Icon(icon), + title: Text(title), onTap: () async { if (route == '/') { // Handle logout separately @@ -189,11 +182,11 @@ class _CustomDrawerState extends State{ } else { context.go(route); } - } - ); + }); } - Widget _buildExpandableItem(BuildContext context, IconData icon, String title, Listchildren){ + Widget _buildExpandableItem(BuildContext context, IconData icon, String title, + List children) { return ExpansionTile( leading: Icon(icon), title: Text(title), @@ -203,16 +196,13 @@ class _CustomDrawerState extends State{ ); } - Widget _buildSubDrawerItem(BuildContext context, String title, String route) - { + Widget _buildSubDrawerItem(BuildContext context, String title, String route) { return ListTile( title: Text(title), - onTap: (){ + onTap: () { context.go(route); if (!widget.isDesktop) Navigator.pop(context); }, ); } - } - diff --git a/lib/routes/custom_router.dart b/lib/routes/custom_router.dart index 4b27bc1..91175a8 100644 --- a/lib/routes/custom_router.dart +++ b/lib/routes/custom_router.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'package:frontend/Screens/authentication/login/login_page.dart'; import 'package:frontend/Screens/authentication/loginPage1.dart'; @@ -11,7 +12,6 @@ import 'package:go_router/go_router.dart'; final GoRouter router = GoRouter( routes: [ - GoRoute( path: '/', builder: (context, state) => LoginPage(), @@ -35,11 +35,27 @@ final GoRouter router = GoRouter( GoRoute( path: '/CreateUserDetails', builder: (context, state) => CreateUserForm(), + // builder: (context, state) { + // final userParam = state.uri.queryParameters['user']; + // + // final isEditProfile = + // state.uri.queryParameters['isEditProfile'] == 'true'; + // final isViewMode = state.uri.queryParameters['isViewMode'] == 'true'; + // + // final user = userParam != null + // ? jsonDecode(Uri.decodeComponent(userParam)) + // : null; + // + // return CreateUserForm( + // apiselectedUser: user, + // isEditProfile: isEditProfile, + // isViewMode: isViewMode, + // ); + // } ), GoRoute( - path: '/Policy', - builder: (context,state) => Policy(), + path: '/Policy', + builder: (context, state) => Policy(), ), - ], -); \ No newline at end of file +); diff --git a/lib/services/apiService.dart b/lib/services/apiService.dart index 91768b4..1d12d96 100644 --- a/lib/services/apiService.dart +++ b/lib/services/apiService.dart @@ -3,11 +3,8 @@ import 'package:frontend/utils/auth_utils.dart'; import 'package:http/http.dart' as http; import '../../config/apiUrl.dart'; - class ApiService { - Future> fetchCountryList() async { - final String apiUrldata = '$apiUrl/api/getcountryMaster'; final token = await getToken(); @@ -29,7 +26,8 @@ class ApiService { print("Country - $data"); if (!data.containsKey('data') || data['data'] is! List) { - throw Exception("Invalid response format: 'data' field is missing or not a List"); + throw Exception( + "Invalid response format: 'data' field is missing or not a List"); } return data['data']; @@ -42,7 +40,8 @@ class ApiService { } Future> fetchUsers() async { - final String apiUrlData = '$apiUrl/api/users'; + String? ordId = await getOrgId(); + final String apiUrlData = '$apiUrl/api/users?org_id=$ordId'; final String? token = await getToken(); print("Fetch Users"); @@ -68,7 +67,6 @@ class ApiService { } } - Future fetchCostCenter() async { final String apiUrldata = '$apiUrl/api/getCostCenterMaster'; @@ -95,27 +93,26 @@ class ApiService { final data = json.decode(response.body); print(data); - if (!data.containsKey('data') || data['data'] is!List) { - throw Exception("Invalid response format: 'data' field is missing or not a List"); + if (!data.containsKey('data') || data['data'] is! List) { + throw Exception( + "Invalid response format: 'data' field is missing or not a List"); } - List plansJson = data['data']; // 'data' is a Map, 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']; - // } + // if(apiCostData!.isNotEmpty){ + // selectedCostCenterId =apiCostData?.first['department_id']; + // } - // if (apiCostData != null && apiCostData!.isNotEmpty) { - // selectedCostCenterId ??= apiCostData!.first['department_id']?.toString(); - // } + // if (apiCostData != null && apiCostData!.isNotEmpty) { + // selectedCostCenterId ??= apiCostData!.first['department_id']?.toString(); + // } // }); print('plansJSON'); return plansJson; - - } catch (e) { throw Exception('Error parsing response: $e'); } @@ -137,20 +134,23 @@ class ApiService { Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json',},); + 'Content-Type': 'application/json', + }, + ); if (response.statusCode == 200) { try { final data = json.decode(response.body); print(data); if (!data.containsKey('data') || data['data'] is! Map) { - throw Exception("Invalid response format: 'data' field is missing or not a Map"); + throw Exception( + "Invalid response format: 'data' field is missing or not a Map"); } - Map plansJson = data['data']; // 'data' is a Map, not a List + Map plansJson = + data['data']; // 'data' is a Map, not a List return plansJson; - } catch (e) { throw Exception('Error parsing response: $e'); } @@ -158,6 +158,4 @@ class ApiService { throw Exception('Failed to load plans'); } } - - } diff --git a/lib/utils/auth_utils.dart b/lib/utils/auth_utils.dart index 8510d02..297b6ac 100644 --- a/lib/utils/auth_utils.dart +++ b/lib/utils/auth_utils.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:shared_preferences/shared_preferences.dart'; Future getToken() async { @@ -9,3 +11,18 @@ Future getUserId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('userId'); } + +Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; +} diff --git a/lib/widgets/custom_text_field.dart b/lib/widgets/custom_text_field.dart index f836a92..1397b85 100644 --- a/lib/widgets/custom_text_field.dart +++ b/lib/widgets/custom_text_field.dart @@ -30,7 +30,7 @@ class _CustomTextFieldWrapperState extends State { return Container( width: widget.width ?? // Use custom width if provided, else default (widget.isDesktop - ? MediaQuery.of(context).size.width * 0.4 + ? MediaQuery.of(context).size.width * 0.3 : MediaQuery.of(context).size.width * 0.85), padding: widget.padding, decoration: BoxDecoration( @@ -42,14 +42,13 @@ class _CustomTextFieldWrapperState extends State { ), boxShadow: widget.isFocused ? [ - BoxShadow( - color: Color.fromRGBO(120, 180, 252, 0.3), - blurRadius: 10, - spreadRadius: 2, - offset: Offset(0, 4), - - ), - ] + BoxShadow( + color: Color.fromRGBO(120, 180, 252, 0.3), + blurRadius: 10, + spreadRadius: 2, + offset: Offset(0, 4), + ), + ] : [], ), child: widget.child, diff --git a/lib/widgets/custom_text_forex.dart b/lib/widgets/custom_text_forex.dart index 38bdecc..289217d 100644 --- a/lib/widgets/custom_text_forex.dart +++ b/lib/widgets/custom_text_forex.dart @@ -21,16 +21,18 @@ class CustomTextFieldForexWrapper extends StatefulWidget { }); @override - _CustomTextFieldForexWrapperState createState() => _CustomTextFieldForexWrapperState(); + _CustomTextFieldForexWrapperState createState() => + _CustomTextFieldForexWrapperState(); } -class _CustomTextFieldForexWrapperState extends State { +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.2 : MediaQuery.of(context).size.width * 0.8), padding: widget.padding, decoration: BoxDecoration( @@ -43,14 +45,13 @@ class _CustomTextFieldForexWrapperState extends State=3.7.0-0 <4.0.0" + dart: ">=3.7.0 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 58fc8b0..08f2523 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: dropdown_search: ^5.0.6 file_picker: ^10.0.0 bcrypt: ^1.1.3 + http_parser: ^4.1.2 dev_dependencies: