merge with old code

This commit is contained in:
venba-Inspriron-3558 2025-06-21 13:52:46 +05:30
commit 428dff9ca8
15 changed files with 2130 additions and 1267 deletions

View File

@ -216,8 +216,8 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final data = json.decode(response.body); final data = json.decode(response.body);
List<dynamic> plansJson = []; // List<dynamic> plansJson = [];
// List<dynamic> plansJson = data['data']; List<dynamic> plansJson = data['data'];
return plansJson.map((json) => Plan.fromJson(json)).toList(); return plansJson.map((json) => Plan.fromJson(json)).toList();
} else { } else {
throw Exception('Failed to load plans'); throw Exception('Failed to load plans');

View File

@ -30,6 +30,8 @@ class _LoginWidgetState extends State<LoginWidget> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
bool _moved = false; bool _moved = false;
bool _isLoading = false;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController(); final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController(); final TextEditingController _passwordController = TextEditingController();
@ -103,6 +105,10 @@ class _LoginWidgetState extends State<LoginWidget> {
Future<void> _login(BuildContext context) async { Future<void> _login(BuildContext context) async {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});
const String url = '$apiUrl/auth/login'; const String url = '$apiUrl/auth/login';
try { try {
@ -193,6 +199,10 @@ class _LoginWidgetState extends State<LoginWidget> {
fontSize: 16.0, fontSize: 16.0,
webBgColor: "linear-gradient(to right, #dc1c13, #dc1c13)", webBgColor: "linear-gradient(to right, #dc1c13, #dc1c13)",
); );
} finally {
setState(() {
_isLoading = false;
});
} }
} }
} }
@ -415,8 +425,9 @@ class _LoginWidgetState extends State<LoginWidget> {
// fit: BoxFit.contain, // fit: BoxFit.contain,
// ), // ),
Container( Container(
width: 200, // Optional: control size // color: Colors.yellow,
height: 100, width: 120, // Optional: control size
height: 200,
), ),
Expanded( Expanded(
child: Container( child: Container(
@ -618,7 +629,13 @@ class _LoginWidgetState extends State<LoginWidget> {
children: [ children: [
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () => _login(context), // onPressed: () => _login(context),
onPressed:
_isLoading
? null
: () => _login(
context,
), // disable when loading
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color( backgroundColor: Color(
0xFF12B24B, 0xFF12B24B,
@ -638,13 +655,25 @@ class _LoginWidgetState extends State<LoginWidget> {
horizontal: 24, horizontal: 24,
vertical: 5, vertical: 5,
), ),
child: Row( child:
_isLoading
? SizedBox(
height: 20,
width: 20,
child:
CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Sign In", "Sign In",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontWeight:
FontWeight.w800,
fontSize: 13.5, fontSize: 13.5,
), ),
), ),

View File

@ -763,7 +763,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckInDate(context), onTap: () {
_checkInFocusNode.requestFocus();
_selectCheckInDate(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _checkInFocusNode, focusNode: _checkInFocusNode,
@ -816,6 +819,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
_checkInTimeFocusNode.requestFocus();
_checkInTimeController.text = ""; _checkInTimeController.text = "";
_selectCheckInTime(context); _selectCheckInTime(context);
}, },
@ -870,7 +874,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () {
_checkOutFocusNode.requestFocus();
_selectCheckOutDate(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _checkOutFocusNode, focusNode: _checkOutFocusNode,
@ -922,7 +929,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutTime(context), onTap: () {
_checkOutTimeFocusNode.requestFocus();
_selectCheckOutTime(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _checkOutTimeFocusNode, focusNode: _checkOutTimeFocusNode,

View File

@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_radio_button.dart'; import '../../widgets/custom_radio_button.dart';
import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
@ -64,6 +65,9 @@ class FlightScreenState extends State<FlightScreen> {
String? selectedvisa_available; String? selectedvisa_available;
int multiTripRowCount = 1; int multiTripRowCount = 1;
// Color? layoutColor;
Color layoutColor = Colors.grey;
List<String> dataHeader = [ List<String> dataHeader = [
"_tripType", "_tripType",
"_class", "_class",
@ -89,7 +93,9 @@ class FlightScreenState extends State<FlightScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
print("TripType - ${widget.tripType}"); print("TripType - ${widget.tripType}");
// final newTripType = widget.tripTypeNotifier.value; // final newTripType = widget.tripTypeNotifier.value;
@ -280,6 +286,24 @@ class FlightScreenState extends State<FlightScreen> {
return 1; // Default for Oneway return 1; // Default for Oneway
} }
void loadInitialData() async {
String? layoutString = await getLayoutColor();
// setState(() {
// layoutColor =
// layoutString != null
// ? Color(int.parse(layoutString))
// : Colors.redAccent;
// });
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
void _initializeFields() { void _initializeFields() {
print("_initializeFields-----------------"); print("_initializeFields-----------------");
@ -982,12 +1006,27 @@ class FlightScreenState extends State<FlightScreen> {
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
// isFocused: _tripTypeFocused, // isFocused: _tripTypeFocused,
isFocused: focusStates["_tripType1Focused"] ?? false, // isFocused: focusStates["_tripType1Focused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["_tripType1FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_tripType1Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_tripType1FocusNode"]?.requestFocus();
},
child: DropdownSearch<String>( child: DropdownSearch<String>(
items: items:
purposeList purposeList
@ -995,8 +1034,35 @@ class FlightScreenState extends State<FlightScreen> {
.toList(), .toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1), border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["_tripType1Focused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["_tripType1Focused"] ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
), ),
), ),
onChanged: (newValue) { onChanged: (newValue) {
@ -1012,7 +1078,8 @@ class FlightScreenState extends State<FlightScreen> {
textControllers["_time${i}Controller"]?.clear(); textControllers["_time${i}Controller"]?.clear();
// Also clear the actual selected data (not just the text in controllers) // Also clear the actual selected data (not just the text in controllers)
selectedFrom[i] = null; // Assuming null means no selection selectedFrom[i] =
null; // Assuming null means no selection
selectedTo[i] = null; selectedTo[i] = null;
} }
}); });
@ -1048,6 +1115,8 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
), ),
),
),
// DropdownButtonFormField<String>( // DropdownButtonFormField<String>(
// isExpanded: true, // isExpanded: true,
@ -1411,7 +1480,9 @@ class FlightScreenState extends State<FlightScreen> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
// isFocused: _fromFocus, // isFocused: _fromFocus,
isFocused: focusStates["_from${index}Focused"] ?? false, // isFocused: focusStates["_from${index}Focused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
// isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
@ -1419,9 +1490,24 @@ class FlightScreenState extends State<FlightScreen> {
child: child:
isCountryLoading isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : Focus(
focusNode: focusNodes["_from${index}FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_from${index}Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_from${index}FocusNode"]
?.requestFocus();
},
child: DropdownSearch<String>(
enabled: enabled:
!(selectedTripType == "Roundtrip" && index == 2), !(selectedTripType == "Roundtrip" &&
index == 2),
selectedItem: selectedItem:
selectedFrom[index] != null selectedFrom[index] != null
? countryMap[selectedFrom[index]] ? countryMap[selectedFrom[index]]
@ -1429,7 +1515,8 @@ class FlightScreenState extends State<FlightScreen> {
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
fit: FlexFit.loose, fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 220), constraints: BoxConstraints(maxHeight: 220),
showSearchBox: true, // Enables search functionality showSearchBox:
true, // Enables search functionality
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
@ -1440,11 +1527,14 @@ class FlightScreenState extends State<FlightScreen> {
), ),
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
), ),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: (context, item, isSelected) { itemBuilder: (context, item, isSelected) {
final parts = item.split('\n'); final parts = item.split('\n');
final cityAndCode = parts[0]; final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : ''; final airport =
parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)" // Extract city and code from "City (CODE)"
final cityMatch = RegExp( final cityMatch = RegExp(
@ -1459,7 +1549,8 @@ class FlightScreenState extends State<FlightScreen> {
vertical: 6.0, vertical: 6.0,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: mainAxisAlignment:
@ -1512,8 +1603,39 @@ class FlightScreenState extends State<FlightScreen> {
items: countryMap.values.toList(), items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: OutlineInputBorder(
contentPadding: EdgeInsets.symmetric(horizontal: 1), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["_from${index}Focused"] ??
false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["_from${index}Focused"] ??
false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: layoutColor,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
), ),
), ),
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
@ -1571,6 +1693,8 @@ class FlightScreenState extends State<FlightScreen> {
}, },
), ),
), ),
),
),
// child: SizedBox( // child: SizedBox(
// height: 40, // height: 40,
@ -1612,16 +1736,32 @@ class FlightScreenState extends State<FlightScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_to${index}Focused"] ?? false, // isFocused: focusStates["_to${index}Focused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: child:
isCountryLoading isCountryLoading
? Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: DropdownSearch<String>( : Focus(
focusNode: focusNodes["_to${index}FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_to${index}Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_to${index}FocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
enabled: enabled:
!(selectedTripType == "Roundtrip" && index == 2), !(selectedTripType == "Roundtrip" &&
index == 2),
selectedItem: selectedItem:
selectedTo[index] != null selectedTo[index] != null
? countryMap[selectedTo[index]] ? countryMap[selectedTo[index]]
@ -1629,7 +1769,8 @@ class FlightScreenState extends State<FlightScreen> {
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
fit: FlexFit.loose, fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 220), constraints: BoxConstraints(maxHeight: 220),
showSearchBox: true, // Enables search functionality showSearchBox:
true, // Enables search functionality
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search...", hintText: "Search...",
@ -1640,11 +1781,14 @@ class FlightScreenState extends State<FlightScreen> {
), ),
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
), ),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: (context, item, isSelected) { itemBuilder: (context, item, isSelected) {
final parts = item.split('\n'); final parts = item.split('\n');
final cityAndCode = parts[0]; final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : ''; final airport =
parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)" // Extract city and code from "City (CODE)"
final cityMatch = RegExp( final cityMatch = RegExp(
@ -1659,7 +1803,8 @@ class FlightScreenState extends State<FlightScreen> {
vertical: 6.0, vertical: 6.0,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: mainAxisAlignment:
@ -1711,8 +1856,39 @@ class FlightScreenState extends State<FlightScreen> {
items: countryMap.values.toList(), items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: OutlineInputBorder(
contentPadding: EdgeInsets.symmetric(horizontal: 1), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["_to${index}Focused"] ??
false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["_to${index}Focused"] ??
false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: layoutColor,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
), ),
), ),
@ -1769,6 +1945,8 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
), ),
),
),
if (errorMessages["to_place_$index"] != null) ...[ if (errorMessages["to_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
@ -1845,11 +2023,27 @@ class FlightScreenState extends State<FlightScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_class${index}Focused"] ?? false, // isFocused: focusStates["_class${index}Focused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 40,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["_class${index}FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_class${index}Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_class${index}FocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>( child: DropdownSearch<Map<String, dynamic>>(
items: purposeList.cast<Map<String, dynamic>>(), items: purposeList.cast<Map<String, dynamic>>(),
selectedItem: purposeList.firstWhere( selectedItem: purposeList.firstWhere(
@ -1877,10 +2071,34 @@ class FlightScreenState extends State<FlightScreen> {
); );
}, },
), ),
dropdownDecoratorProps: const DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["_class${index}Focused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["_class${index}Focused"] ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
@ -1908,14 +2126,19 @@ class FlightScreenState extends State<FlightScreen> {
purposeList.isNotEmpty purposeList.isNotEmpty
? (Map<String, dynamic>? newValue) { ? (Map<String, dynamic>? newValue) {
setState(() { setState(() {
selectedClasses[index] = newValue?['dropdown_key']; selectedClasses[index] =
print("Selected Class: ${selectedClasses[index]}"); newValue?['dropdown_key'];
print(
"Selected Class: ${selectedClasses[index]}",
);
}); });
} }
: null, : null,
), ),
), ),
), ),
),
),
if (errorMessages["class_$index"] != null) if (errorMessages["class_$index"] != null)
Padding( Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
@ -1941,12 +2164,16 @@ class FlightScreenState extends State<FlightScreen> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_date${index}Focused"] ?? false, isFocused: focusStates["_date${index}Focused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () => _selectCheckOutDate(context), onTap: () {
focusNodes["_date${index}FocusNode"]?.requestFocus();
_selectCheckOutDate(context);
},
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: focusNodes["_date${index}FocusNode"], focusNode: focusNodes["_date${index}FocusNode"],
@ -1954,13 +2181,44 @@ class FlightScreenState extends State<FlightScreen> {
controller: textControllers["_date${index}Controller"], controller: textControllers["_date${index}Controller"],
readOnly: true, readOnly: true,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: InputDecoration(
labelText: "Select Date", labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey), labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon( // border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide: BorderSide(
// color:
// (focusStates["_date${index}Focused"] ?? false)
// ? layoutColor!
// : Colors.white,
// width: 0.5,
// ),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(
// color:
// (focusStates["_date${index}Focused"] ?? false)
// ? layoutColor
// : Colors.white,
// // : const Color(0xFFD6D5E6),
// width: 0.5,
// // const Color(0xFFD6D5E6),
// ),
// ),
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: layoutColor, width: 1),
// ),
// contentPadding: const EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 5,
// ),
suffixIcon: const Icon(
Icons.calendar_today, Icons.calendar_today,
size: 16, size: 16,
color: Colors.grey, color: Colors.grey,
@ -1994,13 +2252,14 @@ class FlightScreenState extends State<FlightScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_timeFocused"] ?? false, isFocused: focusStates["_time${index}Focused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
focusNodes["_time${index}FocusNode"]?.requestFocus();
textControllers["_time${index}Controller"]?.text = ""; textControllers["_time${index}Controller"]?.text = "";
_clearError("time_$index"); _clearError("time_$index");
// validateTimeDifference(index); // validateTimeDifference(index);
@ -2013,6 +2272,7 @@ class FlightScreenState extends State<FlightScreen> {
); // Force rebuild to show the error immediately ); // Force rebuild to show the error immediately
}); });
}, },
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: focusNodes["_time${index}FocusNode"], focusNode: focusNodes["_time${index}FocusNode"],
@ -2034,6 +2294,19 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
), ),
// Focus(
// focusNode: focusNodes["_time${index}FocusNode"],
// onFocusChange: (hasFocus) {
// setState(() {
// focusStates["_time${index}Focused"] = hasFocus;
// });
// },
// child: GestureDetector(
// //
// onTap: () {
// // Request focus when user taps
// focusNodes["_time${index}FocusNode"]?.requestFocus();
// },
), ),
), ),
), ),
@ -2159,11 +2432,26 @@ class FlightScreenState extends State<FlightScreen> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper( CustomTextFieldItnerarySubWrapper(
// use same wrapper as class // use same wrapper as class
isFocused: focusStates["_visa1Focused"] ?? false, // isFocused: focusStates["_visa1Focused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["_visa1FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_visa1Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_visa1FocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>( child: DropdownSearch<Map<String, dynamic>>(
items: visa_available.cast<Map<String, dynamic>>(), items: visa_available.cast<Map<String, dynamic>>(),
selectedItem: visa_available.firstWhere( selectedItem: visa_available.firstWhere(
@ -2191,10 +2479,34 @@ class FlightScreenState extends State<FlightScreen> {
); );
}, },
), ),
dropdownDecoratorProps: const DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // border: InputBorder.none,
contentPadding: EdgeInsets.symmetric( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["_visa1Focused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["_visa1Focused"] ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
@ -2222,7 +2534,8 @@ class FlightScreenState extends State<FlightScreen> {
visa_available.isNotEmpty visa_available.isNotEmpty
? (Map<String, dynamic>? newValue) { ? (Map<String, dynamic>? newValue) {
setState(() { setState(() {
selectedvisa_available = newValue?['dropdown_key']; selectedvisa_available =
newValue?['dropdown_key'];
print("Selected Visa: $selectedvisa_available"); print("Selected Visa: $selectedvisa_available");
}); });
} }
@ -2230,6 +2543,8 @@ class FlightScreenState extends State<FlightScreen> {
), ),
), ),
), ),
),
),
], ],
), ),
if (isDesktop) SizedBox(width: 20), if (isDesktop) SizedBox(width: 20),

View File

@ -135,7 +135,6 @@ class _ForexScreenState extends State<ForexScreen> {
String? selectedQuotedAmount; String? selectedQuotedAmount;
int? selectedCashPercent; int? selectedCashPercent;
int? selectedCardPercent; int? selectedCardPercent;
bool userEdited = false; bool userEdited = false;
Map<String, dynamic> get forexData { Map<String, dynamic> get forexData {
@ -399,7 +398,10 @@ class _ForexScreenState extends State<ForexScreen> {
focusStates[field] = false; focusStates[field] = false;
} }
} }
print("textControllers - $textControllers");
print("Focus States KeysII: ${focusStates.keys.toList()}");
print("Focus Nodes KeysII: ${focusNodes.keys.toList()}");
print("Text Controllers KeysII: ${textControllers.keys.toList()}");
// Add focus listeners // Add focus listeners
focusNodes.forEach((key, focusNode) { focusNodes.forEach((key, focusNode) {
@ -1347,17 +1349,32 @@ class _ForexScreenState extends State<ForexScreen> {
// ), // ),
// ), // ),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: focusStates["_countries"] ?? false, isFocused: focusStates["_countriesFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: Focus(
focusNode: focusNodes["_countriesFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_countriesFocused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_countriesFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>( child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry], selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: true, showSearchBox: true,
fit: FlexFit.loose, fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250), constraints: BoxConstraints(maxHeight: 250),
menuProps: const MenuProps(backgroundColor: Colors.white), menuProps: const MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: itemBuilder:
(context, item, isSelected) => Container( (context, item, isSelected) => Container(
color: Colors.white, color: Colors.white,
@ -1373,7 +1390,9 @@ class _ForexScreenState extends State<ForexScreen> {
searchFieldProps: TextFieldProps( searchFieldProps: TextFieldProps(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search ...", hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(horizontal: 10), contentPadding: EdgeInsets.symmetric(
horizontal: 10,
),
), ),
), ),
), ),
@ -1400,7 +1419,9 @@ class _ForexScreenState extends State<ForexScreen> {
// Find the country_code based on selected country_name // Find the country_code based on selected country_name
selectedCountry = selectedCountry =
countryMap.entries countryMap.entries
.firstWhere((entry) => entry.value == newValue) .firstWhere(
(entry) => entry.value == newValue,
)
.key; .key;
_onCountryChanged(selectedCountry); _onCountryChanged(selectedCountry);
}); });
@ -1408,6 +1429,8 @@ class _ForexScreenState extends State<ForexScreen> {
), ),
), ),
), ),
),
),
if (errorMessages["country_code"] != null) ...[ if (errorMessages["country_code"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(

View File

@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart'; import 'package:responsive_builder/responsive_builder.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart'; import '../../widgets/custom_text_itnerary_sub.dart';
@ -53,6 +54,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
late TextEditingController _nomineerelationController = late TextEditingController _nomineerelationController =
TextEditingController(); TextEditingController();
Color layoutColor = Colors.grey;
bool _isHotelNameFocused = false; bool _isHotelNameFocused = false;
bool _startDateFocus = false; bool _startDateFocus = false;
bool _endDateFocus = false; bool _endDateFocus = false;
@ -63,6 +66,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
String? selectedInsuranceType; String? selectedInsuranceType;
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<String> dataHeader = [
"type_of_insurance",
"start_date",
"end_date",
"comments",
"nominee_name",
"nominee_dob",
"nominee_relationship",
];
Map<String, dynamic> get InsuranceData { Map<String, dynamic> get InsuranceData {
Map<String, dynamic> data = { Map<String, dynamic> data = {
@ -94,33 +109,27 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
focusNodes.clear();
focusStates.clear();
_hotelNameFocusNode.addListener(() { // Initialize fields dynamically
setState(() { for (var field in dataHeader) {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus; focusNodes["${field}FocusNode"] = FocusNode();
}); focusStates["${field}Focused"] = false;
}); }
_endDateFocusNode.addListener(() {
setState(() {
_endDateFocus = _endDateFocusNode.hasFocus;
});
});
_startDateFocusNode.addListener(() {
setState(() {
_startDateFocus = _startDateFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_nomineeFocusNode.addListener(() { for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() { setState(() {
_nomineeFocus = _nomineeFocusNode.hasFocus; focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
}); });
}); });
}
print("Focus Nodes Keys: ${focusNodes.keys.toList()}");
print("Focus States Keys: ${focusStates.keys.toList()}");
_insuranceCommentsController = TextEditingController( _insuranceCommentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "", text: widget.selectedItem?["comments"] ?? "",
@ -181,6 +190,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}); });
} }
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
Map<String, String?> getFlightTripDateRange( Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData, List<Map<String, dynamic>> flightData,
) { ) {
@ -285,6 +304,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
@override @override
void dispose() { void dispose() {
// Dispose all dynamically created FocusNodes
for (var node in focusNodes.values) {
node.dispose();
}
_tripTypeFocusNode.dispose(); _tripTypeFocusNode.dispose();
_tripTypeController.dispose(); _tripTypeController.dispose();
_startDateController.dispose(); _startDateController.dispose();
@ -769,7 +793,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// ), // ),
// Dropdown Wrapper // Dropdown Wrapper
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isHotelNameFocused, // isFocused: _isHotelNameFocused,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
child: SizedBox( child: SizedBox(
@ -802,14 +828,45 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
); );
}, },
), ),
dropdownDecoratorProps: const DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // border: InputBorder.none, // No underline
contentPadding: EdgeInsets.symmetric( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["type_of_insuranceFocusNode"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["type_of_insuranceFocusNode"] ?? false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor!, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 10, vertical: 5,
), ),
), ),
// dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 10,
// ),
// ),
), ),
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) { if (selectedItem == null || selectedItem.isEmpty) {
@ -897,13 +954,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _startDateFocus, // isFocused: _startDateFocus,
isFocused: focusStates["start_dateFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () async { onTap: () async {
focusNodes["start_dateFocusNode"]?.requestFocus();
await _selectCheckOutDate(context); await _selectCheckOutDate(context);
if (_startDateController.text.isNotEmpty) { if (_startDateController.text.isNotEmpty) {
setState(() { setState(() {
@ -915,7 +974,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _startDateFocusNode, // focusNode: _startDateFocusNode,
focusNode: focusNodes["start_dateFocusNode"],
controller: _startDateController, controller: _startDateController,
readOnly: true, readOnly: true,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -956,13 +1016,14 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _endDateFocus, isFocused: focusStates["end_dateFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () async { onTap: () async {
focusNodes["end_dateFocusNode"]?.requestFocus();
await _selectEndCheckOutDate(context); await _selectEndCheckOutDate(context);
if (_endDateController.text.isNotEmpty) { if (_endDateController.text.isNotEmpty) {
@ -985,7 +1046,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _endDateFocusNode, // focusNode: _endDateFocusNode,
focusNode: focusNodes["end_dateFocusNode"],
controller: _endDateController, controller: _endDateController,
readOnly: true, readOnly: true,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -1108,7 +1170,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _nomineeFocus, // isFocused: _nomineeFocus,
isFocused: focusStates["nominee_nameFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
@ -1116,7 +1179,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _nomineeFocusNode, focusNode: focusNodes["nominee_nameFocusNode"],
// focusNode: _nomineeFocusNode,
controller: _nomineeController, controller: _nomineeController,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
@ -1150,7 +1215,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _startDateFocus, isFocused: focusStates["nominee_dobFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
@ -1158,6 +1223,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
height: 40, height: 40,
child: GestureDetector( child: GestureDetector(
onTap: () async { onTap: () async {
focusNodes["nominee_dobFocusNode"]?.requestFocus();
await _selectCheckDOBOutDate(context); await _selectCheckDOBOutDate(context);
// await _selectDateOfBirth(context); // await _selectDateOfBirth(context);
if (_dateOfBirthController.text.isNotEmpty) { if (_dateOfBirthController.text.isNotEmpty) {
@ -1170,7 +1236,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}, },
child: AbsorbPointer( child: AbsorbPointer(
child: TextField( child: TextField(
focusNode: _startDateFocusNode, focusNode: focusNodes["nominee_dobFocusNode"],
controller: _dateOfBirthController, controller: _dateOfBirthController,
readOnly: true, readOnly: true,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
@ -1213,7 +1279,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// //
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: focusStates["nominee_relationshipFocused"] ?? false,
isDesktop: isDesktop, isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null,
// width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
@ -1221,7 +1287,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
// focusNode: _nomineeFocusNode, focusNode: focusNodes["nominee_relationshipFocusNode"],
controller: _nomineerelationController, controller: _nomineerelationController,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
@ -1260,7 +1326,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus isFocused:
focusStates["commentsFocused"] ??
false, // Dropdown doesn't use focus
isDesktop: isDesktop, isDesktop: isDesktop,
width: width:
isDesktop isDesktop
@ -1269,7 +1337,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: _commentsFocusNode, // focusNode: _commentsFocusNode,
focusNode: focusNodes["commentsFocusNode"],
controller: _insuranceCommentsController, controller: _insuranceCommentsController,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -29,17 +29,15 @@ class _MailSettingState extends State<MailSetting> {
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
bool _obscurePassword = true; bool _obscurePassword = true;
List<String> dataHeader = [ List<String> dataHeader = [
"host", "host",
"userName", "userName",
"password", "mail_password",
"port", "port",
"senderEmail", "senderEmail",
"toEmail" "toEmail",
]; ];
Map<String, dynamic> getMailData() => mailData; Map<String, dynamic> getMailData() => mailData;
@ -48,7 +46,7 @@ class _MailSettingState extends State<MailSetting> {
final data = { final data = {
"mail_host": controllers["host"]?.text, "mail_host": controllers["host"]?.text,
"mail_user_name": controllers["userName"]?.text, "mail_user_name": controllers["userName"]?.text,
"mail_password": controllers["password"]?.text, "mail_password": controllers["mail_password"]?.text,
"mail_port": controllers["port"]?.text, "mail_port": controllers["port"]?.text,
"sender_email": controllers["senderEmail"]?.text, "sender_email": controllers["senderEmail"]?.text,
"to_mail": controllers["toEmail"]?.text, "to_mail": controllers["toEmail"]?.text,
@ -78,18 +76,11 @@ class _MailSettingState extends State<MailSetting> {
void initState() { void initState() {
super.initState(); super.initState();
// loadAllServices(); // loadAllServices();
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
_initControllers(); _initControllers();
updateData(); updateData();
@ -98,14 +89,6 @@ class _MailSettingState extends State<MailSetting> {
print(widget.initialMailData['sender_email']); print(widget.initialMailData['sender_email']);
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateData() { void updateData() {
print("Updata 2"); print("Updata 2");
@ -114,7 +97,7 @@ class _MailSettingState extends State<MailSetting> {
widget.initialMailData['sender_email'] ?? ''; widget.initialMailData['sender_email'] ?? '';
controllers["userName"]?.text = controllers["userName"]?.text =
widget.initialMailData['mail_user_name'] ?? ''; widget.initialMailData['mail_user_name'] ?? '';
controllers["password"]?.text = controllers["mail_password"]?.text =
widget.initialMailData['mail_password'] ?? ''; widget.initialMailData['mail_password'] ?? '';
controllers["host"]?.text = widget.initialMailData['mail_host'] ?? ''; controllers["host"]?.text = widget.initialMailData['mail_host'] ?? '';
controllers["port"]?.text = controllers["port"]?.text =
@ -198,8 +181,9 @@ class _MailSettingState extends State<MailSetting> {
// Email validation // Email validation
if (data["to_mail"] != null && data["to_mail"].toString().isNotEmpty) { if (data["to_mail"] != null && data["to_mail"].toString().isNotEmpty) {
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") if (!RegExp(
.hasMatch(data["to_mail"].toString())) { r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(data["to_mail"].toString())) {
errorMessages["to_mail"] = errorMessages["to_mail"] =
"Invalid email format"; // Invalid email format "Invalid email format"; // Invalid email format
} }
@ -208,8 +192,9 @@ class _MailSettingState extends State<MailSetting> {
// Email validation // Email validation
if (data["sender_email"] != null && if (data["sender_email"] != null &&
data["sender_email"].toString().isNotEmpty) { data["sender_email"].toString().isNotEmpty) {
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") if (!RegExp(
.hasMatch(data["sender_email"].toString())) { r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(data["sender_email"].toString())) {
errorMessages["sender_email"] = errorMessages["sender_email"] =
"Invalid email format"; // Invalid email format "Invalid email format"; // Invalid email format
} }
@ -245,20 +230,21 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["senderEmailFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
// focusNode: _destinationFocusNode, // focusNode: _destinationFocusNode,
focusNode: focusNodes["senderEmailFocusNode"],
controller: controllers["senderEmail"], controller: controllers["senderEmail"],
onChanged: (value) { onChanged: (value) {
_clearError("sender_email"); _clearError("sender_email");
@ -267,8 +253,8 @@ class _MailSettingState extends State<MailSetting> {
if (value.trim().isEmpty) { if (value.trim().isEmpty) {
errorMessages["sender_email"] = "Required"; errorMessages["sender_email"] = "Required";
} else if (!RegExp( } else if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
.hasMatch(value)) { ).hasMatch(value)) {
errorMessages["sender_email"] = errorMessages["sender_email"] =
"Invalid email format"; "Invalid email format";
} }
@ -277,7 +263,9 @@ class _MailSettingState extends State<MailSetting> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: "sender email", labelText: "sender email",
labelStyle: GoogleFonts.poppins( labelStyle: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -296,9 +284,7 @@ class _MailSettingState extends State<MailSetting> {
), ),
], ],
), ),
SizedBox( SizedBox(height: 10),
height: 10,
),
widget.isDesktop widget.isDesktop
? Row( ? Row(
@ -310,22 +296,16 @@ class _MailSettingState extends State<MailSetting> {
children: _buildMailFirstRow(widget.isDesktop), children: _buildMailFirstRow(widget.isDesktop),
), ),
SizedBox( SizedBox(height: 10),
height: 10,
),
widget.isDesktop widget.isDesktop
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildMailSecondRow(widget.isDesktop), children: _buildMailSecondRow(widget.isDesktop),
) )
: Column( : Column(children: _buildMailSecondRow(widget.isDesktop)),
children: _buildMailSecondRow(widget.isDesktop),
),
SizedBox( SizedBox(height: 10),
height: 10,
),
Container( Container(
// color: Color(0xFFF7F7FB), // color: Color(0xFFF7F7FB),
@ -335,7 +315,9 @@ class _MailSettingState extends State<MailSetting> {
Text( Text(
"Test Mail", "Test Mail",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: Color(0xFF114D8B), fontWeight: FontWeight.w600), color: Color(0xFF114D8B),
fontWeight: FontWeight.w600,
),
), ),
], ],
), ),
@ -345,7 +327,7 @@ class _MailSettingState extends State<MailSetting> {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: _buildTestMail(widget.isDesktop), children: _buildTestMail(widget.isDesktop),
) ),
], ],
), ),
), ),
@ -362,20 +344,21 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["userNameFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
// focusNode: _destinationFocusNode, // focusNode: _destinationFocusNode,
focusNode: focusNodes["userNameFocusNode"],
controller: controllers["userName"], controller: controllers["userName"],
onChanged: (value) { onChanged: (value) {
_clearError("mail_user_name"); _clearError("mail_user_name");
@ -383,8 +366,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "user name", labelText: "user name",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -409,45 +394,54 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["passwordFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["passwordFocusNode"], // focusNode: _destinationFocusNode,
controller: controllers["password"], controller: controllers["mail_password"],
onChanged: (value) { onChanged: (value) {
_clearError("mail_password"); _clearError("mail_password");
}, },
obscureText: _obscurePassword, // obscureText: _obscurePassword,
autofillHints: const [
// AutofillHints.newPassword,
], // <--- This is key
enableSuggestions: false, // <--- Disable suggestions
autocorrect: false,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "password", labelText: "Access Key",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: IconButton( // suffixIcon: IconButton(
icon: Icon( // icon: Icon(
_obscurePassword // _obscurePassword
? Icons.visibility_off // ? Icons.visibility_off
: Icons.visibility, // : Icons.visibility,
size: 16, // size: 16,
), // ),
onPressed: () { // onPressed: () {
setState(() { // setState(() {
_obscurePassword = !_obscurePassword; // _obscurePassword = !_obscurePassword;
}); // });
}, // },
), // ),
), ),
), ),
), ),
@ -474,19 +468,21 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["hostFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["hostFocusNode"], // focusNode: _destinationFocusNode,
controller: controllers["host"], controller: controllers["host"],
onChanged: (value) { onChanged: (value) {
_clearError("mail_host"); _clearError("mail_host");
@ -494,8 +490,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "host", labelText: "host",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -520,19 +518,20 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["portFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["portFocusNode"],
// focusNode: _destinationFocusNode, // focusNode: _destinationFocusNode,
controller: controllers["port"], controller: controllers["port"],
onChanged: (value) { onChanged: (value) {
@ -541,8 +540,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "port", labelText: "port",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -572,20 +573,21 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF575A74)), color: Color(0xFF575A74),
),
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: focusStates["toEmailFocused"] ?? false, isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: widget.isDesktop width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34 ? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.85,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
// focusNode: _destinationFocusNode, // focusNode: _destinationFocusNode,
focusNode: focusNodes["toEmailFocusNode"],
controller: controllers["toEmail"], controller: controllers["toEmail"],
onChanged: (value) { onChanged: (value) {
_clearError("to_mail"); _clearError("to_mail");
@ -594,16 +596,18 @@ class _MailSettingState extends State<MailSetting> {
if (value.trim().isEmpty) { if (value.trim().isEmpty) {
errorMessages["to_mail"] = "Required"; errorMessages["to_mail"] = "Required";
} else if (!RegExp( } else if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
.hasMatch(value)) { ).hasMatch(value)) {
errorMessages["to_mail"] = "Invalid email format"; errorMessages["to_mail"] = "Invalid email format";
} }
}, },
style: GoogleFonts.poppins(fontSize: 12), style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
labelText: "To mail", labelText: "To mail",
labelStyle: labelStyle: GoogleFonts.poppins(
GoogleFonts.poppins(fontSize: 12, color: Colors.grey), fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never, floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -620,17 +624,16 @@ class _MailSettingState extends State<MailSetting> {
], ],
], ],
), ),
SizedBox( SizedBox(width: 10),
width: 10,
),
Column( Column(
children: [ children: [
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Keep original color backgroundColor: Color(0xFF114D8B), // Keep original color
foregroundColor: Colors.white, // Keep original color foregroundColor: Colors.white, // Keep original color
disabledBackgroundColor: disabledBackgroundColor: Color(
Color(0xFF114D8B), // Ensure color remains when disabled 0xFF114D8B,
), // Ensure color remains when disabled
disabledForegroundColor: Colors.white, disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@ -641,10 +644,8 @@ class _MailSettingState extends State<MailSetting> {
onPressed: () { onPressed: () {
handleTestMailSubmit(); handleTestMailSubmit();
}, },
child: Text( child: Text("Test Email", style: GoogleFonts.poppins(fontSize: 12)),
"Test Email", ),
style: GoogleFonts.poppins(fontSize: 12),
))
], ],
), ),
]; ];

View File

@ -383,11 +383,13 @@ class CreateNewPlansState extends State<CreateNewPlan> {
final TextEditingController _descriptionController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController();
final TextEditingController _remarksController = TextEditingController(); final TextEditingController _remarksController = TextEditingController();
final FocusNode _tripTitleFocusNode = FocusNode(); // final FocusNode _tripTitleFocusNode = FocusNode();
final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode // final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode
bool _isdescriptionFocused = false; // bool _isdescriptionFocused = false;
bool _isTripTitleFocused = false; // bool _isTripTitleFocused = false;
Color? layoutColor;
late String _selectedOption = "Option 1"; late String _selectedOption = "Option 1";
// late String? _selectedIsBillable = "Billable"; // late String? _selectedIsBillable = "Billable";
@ -450,6 +452,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<Map<String, dynamic>> planStatusList = []; List<Map<String, dynamic>> planStatusList = [];
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
late bool isApproverApproved = false; late bool isApproverApproved = false;
late bool isApproverRejected = false; late bool isApproverRejected = false;
@ -483,6 +488,16 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// "status_value": statusValue, // "status_value": statusValue,
}; };
List<String> dataHeader = [
"trip_planned",
"trip_type",
"cost_center_id",
"is_billable",
"purpose_of_travel",
"description",
"functional_department",
];
void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) { void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) {
setState(() { setState(() {
switch (type) { switch (type) {
@ -524,40 +539,82 @@ class CreateNewPlansState extends State<CreateNewPlan> {
void initState() { void initState() {
super.initState(); super.initState();
print("approverStatus - ${widget.approverStatus}"); print("approverStatus - ${widget.approverStatus}");
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
if (widget.approverStatus == "Approval pending") { if (widget.approverStatus == "Approval pending") {
print("Status approver - Approval Pending"); print("Status approver - Approval Pending");
} }
flightTripTypeNotifier = ValueNotifier(null); flightTripTypeNotifier = ValueNotifier(null);
for (var field in dataHeader) {
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
print("Focus Nodes KeysII: ${focusNodes.keys.toList()}");
print("Focus States KeysII: ${focusStates.keys.toList()}");
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
}
fetchUserDetails(); fetchUserDetails();
fetchPlans(); fetchPlans();
fetchCostCenter(); fetchCostCenter();
fetchCountryList(); fetchCountryList();
_tripTitleFocusNode.addListener(() { // _tripTitleFocusNode.addListener(() {
setState(() { // setState(() {
_isTripTitleFocused = _tripTitleFocusNode.hasFocus; // _isTripTitleFocused = _tripTitleFocusNode.hasFocus;
}); // });
}); // });
//
_descriptionFocusNode.addListener(() { // _descriptionFocusNode.addListener(() {
setState(() { // setState(() {
_isdescriptionFocused = _descriptionFocusNode.hasFocus; // _isdescriptionFocused = _descriptionFocusNode.hasFocus;
}); // });
}); // });
handleUpdateData(); handleUpdateData();
} }
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
@override @override
void dispose() { void dispose() {
_tripTitleFocusNode.dispose(); for (var node in focusNodes.values) {
_descriptionFocusNode.dispose(); node.dispose();
}
// _tripTitleFocusNode.dispose();
// _descriptionFocusNode.dispose();
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void handleSelectedUser() { void handleSelectedUser() {
if (widget.selectedPlanData != null) { if (widget.selectedPlanData != null) {
setState(() { setState(() {
@ -1236,7 +1293,29 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// await postPlanData(planData); // await postPlanData(planData);
print("Hide loader at: ${DateTime.now()}"); print("Hide loader at: ${DateTime.now()}");
// Close loading dialog (ONLY if still mounted) // Close loading dialog (ONLY if still mounted)
if (mounted) Navigator.of(context, rootNavigator: true).pop(); Future.delayed(Duration(seconds: 12), () {
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
final currentUri =
GoRouterState.of(
context,
).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri == "/allTrips/trips") {
context.go('/listAllPlan');
} else if (currentUri == "/createPlan") {
context.go('/listPlan');
} else {
widget.isApprover
? context.go('/approvallist')
: context.go('/listPlan');
}
}
});
// if (mounted) Navigator.of(context, rootNavigator: true).pop();
} }
}); });
} }
@ -1269,22 +1348,22 @@ class CreateNewPlansState extends State<CreateNewPlan> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
print("Plan submitted successfully!"); print("Plan submitted successfully!");
print("Response: ${response.body}"); print("Response: ${response.body}");
//
final currentUri = // final currentUri =
GoRouterState.of( // GoRouterState.of(
context, // context,
).uri.toString(); // safer than `.location` // ).uri.toString(); // safer than `.location`
print("currentUri - $currentUri"); // print("currentUri - $currentUri");
//
if (currentUri == "/allTrips/trips") { // if (currentUri == "/allTrips/trips") {
context.go('/listAllPlan'); // context.go('/listAllPlan');
} else if (currentUri == "/createPlan") { // } else if (currentUri == "/createPlan") {
context.go('/listPlan'); // context.go('/listPlan');
} else { // } else {
widget.isApprover // widget.isApprover
? context.go('/approvallist') // ? context.go('/approvallist')
: context.go('/listPlan'); // : context.go('/listPlan');
} // }
} else { } else {
print("Failed to submit plan. Status: ${response.statusCode}"); print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}"); print("Error: ${response.body}");
@ -1742,15 +1821,32 @@ class CreateNewPlansState extends State<CreateNewPlan> {
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
// isFocused: focusStates["trip_typeFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
// width: double.infinity,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["trip_typeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["trip_typeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["trip_typeFocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>( child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: false, // Optionally enable search showSearchBox: false, // Optionally enable search
fit: FlexFit.loose, fit: FlexFit.loose,
menuProps: const MenuProps(backgroundColor: Colors.white), menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder: (context, item, isSelected) { itemBuilder: (context, item, isSelected) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -1767,16 +1863,42 @@ class CreateNewPlansState extends State<CreateNewPlan> {
); );
}, },
), ),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, // No underline
contentPadding: EdgeInsets.symmetric( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none, // No underline
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["trip_typeFocused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["trip_typeFocused"] ?? false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor!, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
), ),
), ),
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) { if (selectedItem == null || selectedItem.isEmpty) {
return Text( return Text(
@ -1818,7 +1940,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
_selectedTripType = _selectedTripType =
newItem['dropdown_key'].toString(); newItem['dropdown_key'].toString();
// Notify FlightScreen // Notify FlightScreen
flightTripTypeNotifier.value = _selectedTripType; flightTripTypeNotifier.value =
_selectedTripType;
print( print(
"flightTripTypeNotifier- ${flightTripTypeNotifier.value}", "flightTripTypeNotifier- ${flightTripTypeNotifier.value}",
); );
@ -1833,6 +1956,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
),
),
// isMobile // isMobile
// ? SingleChildScrollView( // ? SingleChildScrollView(
// scrollDirection: Axis.horizontal, // scrollDirection: Axis.horizontal,
@ -1873,15 +1998,30 @@ class CreateNewPlansState extends State<CreateNewPlan> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
// isFocused: focusStates["is_billableFocused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
width: width:
widget.isDesktop widget.isDesktop
? MediaQuery.of(context).size.width * 0.2 ? MediaQuery.of(context).size.width * 0.2
: null, : null,
isFocused: false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["is_billableFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["is_billableFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["is_billableFocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>( child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: false, showSearchBox: false,
@ -1903,10 +2043,33 @@ class CreateNewPlansState extends State<CreateNewPlan> {
); );
}, },
), ),
dropdownDecoratorProps: const DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: OutlineInputBorder(
contentPadding: EdgeInsets.symmetric( borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["is_billableFocused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["is_billableFocused"] ?? false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor!, width: 1),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
@ -1962,6 +2125,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
),
),
// Row( // Row(
// children: purposeList.map<Widget>((item) { // children: purposeList.map<Widget>((item) {
@ -2132,7 +2297,10 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus // isFocused: focusStates["cost_center_idFocused"] ??
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
@ -2145,7 +2313,21 @@ class CreateNewPlansState extends State<CreateNewPlan> {
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
) )
: DropdownSearch<String>( : Focus(
focusNode: focusNodes["cost_center_idFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["cost_center_idFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["cost_center_idFocusNode"]
?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: costCenterMap[selectedCostCenterId], selectedItem: costCenterMap[selectedCostCenterId],
enabled: !widget.isViewMode, enabled: !widget.isViewMode,
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
@ -2166,7 +2348,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
style: GoogleFonts.poppins(fontSize: 13), style: GoogleFonts.poppins(fontSize: 13),
), ),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: itemBuilder:
(context, item, isSelected) => Padding( (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -2185,7 +2369,35 @@ class CreateNewPlansState extends State<CreateNewPlan> {
items: costCenterMap.values.toList(), // just names items: costCenterMap.values.toList(), // just names
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["cost_center_idFocused"] ??
false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["cost_center_idFocused"] ??
false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: layoutColor!,
width: 1,
),
),
// contentPadding: EdgeInsets.symmetric(horizontal: 1), // contentPadding: EdgeInsets.symmetric(horizontal: 1),
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
@ -2195,7 +2407,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) { if (selectedItem == null ||
selectedItem.isEmpty) {
return Text( return Text(
"Select Purpose", // fallback text "Select Purpose", // fallback text
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
@ -2231,6 +2444,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
}, },
), ),
), ),
),
),
// child: SizedBox( // child: SizedBox(
// height: 45, // Set appropriate height // height: 45, // Set appropriate height
@ -2286,8 +2501,11 @@ class CreateNewPlansState extends State<CreateNewPlan> {
width: width:
isDesktop isDesktop
? MediaQuery.of(context).size.width * 0.2 ? MediaQuery.of(context).size.width * 0.2
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.88,
isFocused: false, // Dropdown doesn't use focus // isFocused: focusStates["purpose_of_travelFocused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 35, // Set appropriate height height: 35, // Set appropriate height
@ -2297,12 +2515,26 @@ class CreateNewPlansState extends State<CreateNewPlan> {
? Center( ? Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
) // Show loading inside dropdown ) // Show loading inside dropdown
: DropdownSearch<Map<String, dynamic>>( : Focus(
focusNode: focusNodes["purpose_of_travelFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["purpose_of_travelFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["purpose_of_travelFocusNode"]
?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: purposeList.firstWhere( selectedItem: purposeList.firstWhere(
(item) => (item) =>
item['dropdown_key'].toString() == item['dropdown_key'].toString() ==
selectedPurpose, selectedPurpose,
orElse: () => <String, dynamic>{}, // Safe fallback orElse:
() => <String, dynamic>{}, // Safe fallback
), ),
items: purposeList, items: purposeList,
itemAsString: itemAsString:
@ -2325,7 +2557,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
style: GoogleFonts.poppins(fontSize: 13), style: GoogleFonts.poppins(fontSize: 13),
), ),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: itemBuilder:
(context, item, isSelected) => Padding( (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -2342,11 +2576,39 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["purpose_of_travelFocused"] ??
false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["purpose_of_travelFocused"] ??
false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: layoutColor!,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
border: InputBorder.none,
), ),
), ),
onChanged: onChanged:
@ -2355,13 +2617,17 @@ class CreateNewPlansState extends State<CreateNewPlan> {
: (Map<String, dynamic>? newValue) { : (Map<String, dynamic>? newValue) {
setState(() { setState(() {
selectedPurpose = selectedPurpose =
newValue?['dropdown_key'].toString(); newValue?['dropdown_key']
.toString();
}); });
print("selectedPurpose - $selectedPurpose"); print(
"selectedPurpose - $selectedPurpose",
);
}, },
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) { if (selectedItem == null ||
selectedItem.isEmpty) {
return Text( return Text(
"Select Purpose", // fallback text "Select Purpose", // fallback text
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
@ -2389,6 +2655,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
),
),
if (validationErrors["purpose_of_travel"] != null) if (validationErrors["purpose_of_travel"] != null)
Padding( Padding(
padding: EdgeInsets.only(top: 4), padding: EdgeInsets.only(top: 4),
@ -2446,12 +2714,14 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus // isFocused: focusStates["functional_departmentFocused"] ?? false,
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
width: width:
isDesktop isDesktop
? MediaQuery.of(context).size.width * 0.2 ? MediaQuery.of(context).size.width * 0.2
: MediaQuery.of(context).size.width * 0.85, : MediaQuery.of(context).size.width * 0.88,
// width: MediaQuery.of(context).size.width * 0.2, // width: MediaQuery.of(context).size.width * 0.2,
child: SizedBox( child: SizedBox(
height: 35, // Set appropriate height height: 35, // Set appropriate height
@ -2460,12 +2730,27 @@ class CreateNewPlansState extends State<CreateNewPlan> {
? Center( ? Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
) // Show loading inside dropdown ) // Show loading inside dropdown
: DropdownSearch<Map<String, dynamic>>( : Focus(
focusNode: focusNodes["functional_departmentFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["functional_departmentFocused"] =
hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["functional_departmentFocusNode"]
?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: funcDeptList.firstWhere( selectedItem: funcDeptList.firstWhere(
(item) => (item) =>
item['dropdown_key'].toString() == item['dropdown_key'].toString() ==
selectedFuncDept, selectedFuncDept,
orElse: () => <String, dynamic>{}, // Safe fallback orElse:
() => <String, dynamic>{}, // Safe fallback
), ),
items: funcDeptList, items: funcDeptList,
itemAsString: itemAsString:
@ -2491,7 +2776,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
13, // 👈 Small font size for selected item 13, // 👈 Small font size for selected item
), ),
), ),
menuProps: MenuProps(backgroundColor: Colors.white), menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: itemBuilder:
(context, item, isSelected) => Padding( (context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -2513,7 +2800,35 @@ class CreateNewPlansState extends State<CreateNewPlan> {
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
), ),
border: InputBorder.none, border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["functional_departmentFocused"] ??
false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["functional_departmentFocused"] ??
false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: layoutColor!,
width: 1,
),
),
), ),
), ),
onChanged: onChanged:
@ -2522,13 +2837,17 @@ class CreateNewPlansState extends State<CreateNewPlan> {
: (Map<String, dynamic>? newValue) { : (Map<String, dynamic>? newValue) {
setState(() { setState(() {
selectedFuncDept = selectedFuncDept =
newValue?['dropdown_key'].toString(); newValue?['dropdown_key']
.toString();
}); });
print("selectedFuncDept - $selectedFuncDept"); print(
"selectedFuncDept - $selectedFuncDept",
);
}, },
dropdownBuilder: (context, selectedItem) { dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) { if (selectedItem == null ||
selectedItem.isEmpty) {
return const Text( return const Text(
"Select Purpose", // fallback text "Select Purpose", // fallback text
style: TextStyle( style: TextStyle(
@ -2556,6 +2875,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
),
),
if (validationErrors["functional_department"] != null) if (validationErrors["functional_department"] != null)
Padding( Padding(
padding: EdgeInsets.only(top: 4), padding: EdgeInsets.only(top: 4),
@ -2578,12 +2899,26 @@ class CreateNewPlansState extends State<CreateNewPlan> {
return [ return [
CustomTextFieldWrapper( CustomTextFieldWrapper(
// isFocused: focusStates["trip_plannedFocused"] ?? false,
isFocused: false, isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop, isDesktop: isDesktop,
layoutColor: widget.layoutColor, layoutColor: widget.layoutColor,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
width: double.infinity, width: double.infinity,
child: Focus(
focusNode: focusNodes["trip_plannedFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["trip_plannedFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["trip_plannedFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>( child: DropdownSearch<String>(
selectedItem: selectedItem:
options.firstWhere( options.firstWhere(
@ -2594,6 +2929,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
popupProps: PopupProps.menu( popupProps: PopupProps.menu(
showSearchBox: false, showSearchBox: false,
menuProps: const MenuProps(backgroundColor: Colors.white), menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 100), constraints: BoxConstraints(maxHeight: 100),
itemBuilder: itemBuilder:
(context, item, isSelected) => Padding( (context, item, isSelected) => Padding(
@ -2601,12 +2937,39 @@ class CreateNewPlansState extends State<CreateNewPlan> {
horizontal: 8.0, horizontal: 8.0,
vertical: 6.0, vertical: 6.0,
), ),
child: Text(item, style: GoogleFonts.poppins(fontSize: 13)), child: Text(
item,
style: GoogleFonts.poppins(fontSize: 13),
),
), ),
), ),
dropdownDecoratorProps: DropDownDecoratorProps( dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration( dropdownSearchDecoration: InputDecoration(
border: InputBorder.none, border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["trip_typeFocused"] ?? false)
? layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["trip_plannedFocused"] ?? false)
? layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor!, width: 1),
),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 5, vertical: 5,
@ -2640,6 +3003,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
), ),
), ),
),
),
]; ];
} }
@ -2892,12 +3257,13 @@ class CreateNewPlansState extends State<CreateNewPlan> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldWrapper( CustomTextFieldWrapper(
isFocused: _isTripTitleFocused, isFocused: false,
// isFocused: _isTripTitleFocused,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: TextField( child: TextField(
focusNode: _tripTitleFocusNode, // focusNode: _tripTitleFocusNode,
controller: _tripTitleController, controller: _tripTitleController,
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
enabled: !widget.isViewMode, enabled: !widget.isViewMode,
@ -2963,8 +3329,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// ), // ),
// ), // ),
CustomTextFieldWrapper( CustomTextFieldWrapper(
// isFocused: false, isFocused: focusStates["descriptionFocused"] ?? false,
isFocused: _isdescriptionFocused, // isFocused: _isdescriptionFocused,
// width: isDesktop // width: isDesktop
// ? MediaQuery.of(context).size.width * 0.38 // ? MediaQuery.of(context).size.width * 0.38
// : MediaQuery.of(context).size.width * 0.85, // : MediaQuery.of(context).size.width * 0.85,
@ -2972,7 +3338,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
child: SizedBox( child: SizedBox(
height: 55, height: 55,
child: TextField( child: TextField(
focusNode: _descriptionFocusNode, focusNode: focusNodes["descriptionFocusNode"],
controller: _descriptionController, controller: _descriptionController,
maxLines: 2, maxLines: 2,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
@ -3059,7 +3425,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : 180, width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : 180,
height: 35, height: 35,
child: TextField( child: TextField(
focusNode: _tripTitleFocusNode, // focusNode: _tripTitleFocusNode,
controller: _tripTitleController, controller: _tripTitleController,
// style: TextStyle(fontSize: 12), // style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins( style: GoogleFonts.poppins(

View File

@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; // import 'package:flutter/rendering.dart';
import 'dart:html' as html; import 'dart:html' as html;
import 'package:frontend/config/apiUrl.dart'; // 1 newly added import 'package:frontend/config/apiUrl.dart'; // 1 newly added
import 'package:frontend/services/apiService.dart'; import 'package:frontend/services/apiService.dart';
@ -31,7 +31,7 @@ class _MyAppState extends State<MyApp> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
SemanticsBinding.instance.ensureSemantics(); // Safe here // SemanticsBinding.instance.ensureSemantics(); // Safe here
if (kIsWeb) { if (kIsWeb) {
final uri = Uri.parse(html.window.location.href); final uri = Uri.parse(html.window.location.href);
print("URI - $uri"); print("URI - $uri");

View File

@ -1,3 +1,3 @@
//api url //api url
const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be'; // const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be';
// const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be'; const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be';

View File

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:frontend/Screens/authentication/login/login_page.dart'; import 'package:frontend/Screens/authentication/login/login_page.dart';
import 'package:frontend/Screens/authentication/loginPage1.dart'; import 'package:frontend/Screens/authentication/loginPage1.dart';
import 'package:frontend/Screens/dashboard/home_page.dart'; import 'package:frontend/Screens/dashboard/home_page.dart';
import 'package:frontend/Screens/forex/forex_list.dart';
import 'package:frontend/Screens/myTemplates/templatesList.dart'; import 'package:frontend/Screens/myTemplates/templatesList.dart';
import 'package:frontend/Screens/organization/orgSetup.dart'; import 'package:frontend/Screens/organization/orgSetup.dart';
import 'package:frontend/Screens/organization/org_List.dart'; import 'package:frontend/Screens/organization/org_List.dart';
@ -27,6 +26,7 @@ import '../Screens/group/groupList.dart';
import '../Screens/myTemplates/template.dart'; import '../Screens/myTemplates/template.dart';
import '../Screens/myTemplates/templateForex.dart'; import '../Screens/myTemplates/templateForex.dart';
import '../Screens/myTemplates/templateTest.dart'; import '../Screens/myTemplates/templateTest.dart';
import '../Screens/perdiem_amount/forex_list.dart';
import '../Screens/userManagement/create_user/create_user.dart'; import '../Screens/userManagement/create_user/create_user.dart';
import '../Screens/department/department_list.dart'; import '../Screens/department/department_list.dart';
import '../Screens/costCenter/costCenter_list.dart'; import '../Screens/costCenter/costCenter_list.dart';
@ -152,10 +152,7 @@ final GoRouter router = GoRouter(
pageBuilder: pageBuilder:
(context, state) => MaterialPage(child: Group.fromState(state)), (context, state) => MaterialPage(child: Group.fromState(state)),
), ),
GoRoute( GoRoute(path: '/report', builder: (context, state) => ReportList()),
path: '/report',
builder: (context, state) => ReportList(),
),
GoRoute( GoRoute(
path: '/advancePurchase', path: '/advancePurchase',
builder: (context, state) => AdvancePurchase(), builder: (context, state) => AdvancePurchase(),
@ -164,10 +161,7 @@ final GoRouter router = GoRouter(
path: '/misservicesAnalysis', path: '/misservicesAnalysis',
builder: (context, state) => ServicesAnalysis(), builder: (context, state) => ServicesAnalysis(),
), ),
GoRoute( GoRoute(path: '/misAirReport', builder: (context, state) => MISair()),
path: '/misAirReport',
builder: (context, state) => MISair(),
),
GoRoute( GoRoute(
path: '/misHotelReport', path: '/misHotelReport',
builder: (context, state) => MIShotel(), builder: (context, state) => MIShotel(),

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:frontend/utils/colorOpcity.dart'; import 'package:frontend/utils/colorOpcity.dart';
import '../utils/auth_utils.dart';
class CustomTextFieldWrapper extends StatefulWidget { class CustomTextFieldWrapper extends StatefulWidget {
final Widget child; final Widget child;
final bool isFocused; final bool isFocused;
@ -30,6 +32,28 @@ class CustomTextFieldWrapper extends StatefulWidget {
} }
class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> { class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
Color? layoutColor;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
Color getColorWithOpacity(Color color, double opacity) { Color getColorWithOpacity(Color color, double opacity) {
final int alpha = (opacity * 255).round().clamp(0, 255); final int alpha = (opacity * 255).round().clamp(0, 255);
return Color.fromARGB( return Color.fromARGB(
@ -43,27 +67,29 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: widget.width ?? // Use custom width if provided, else default width:
widget.width ?? // Use custom width if provided, else default
(widget.isDesktop (widget.isDesktop
? MediaQuery.of(context).size.width * 0.3 ? MediaQuery.of(context).size.width * 0.3
: MediaQuery.of(context).size.width * 0.85), : MediaQuery.of(context).size.width * 0.88),
padding: widget.padding, padding: widget.padding,
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.isFocused // color: widget.isFocused
? (widget.layoutColor ?? widget.color) // ? (widget.layoutColor ?? widget.color)
: widget.color, // : widget.color,
color: Colors.white,
borderRadius: widget.borderRadius ?? BorderRadius.circular(8), borderRadius: widget.borderRadius ?? BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: widget.isFocused color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
? (widget.layoutColor ?? Colors.blueAccent) // color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
: Color(0xFFF5F5F5), width: widget.isFocused ? 1.0 : 0.5,
width: widget.isFocused ? 1.5 : 1.5,
), ),
// boxShadow: widget.isFocused // boxShadow:
// widget.isFocused
// ? [ // ? [
// BoxShadow( // BoxShadow(
// // color: Color.fromRGBO(120, 180, 252, 0.3), // color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
// // color: widget.isFocused // // color: widget.isFocused
// // ? (widget.layoutColor ?? Colors.red).withAlpha(204) // // ? (widget.layoutColor ?? Colors.red).withAlpha(204)
// // : Color(0xFFF5F5F5), // // : Color(0xFFF5F5F5),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils/auth_utils.dart';
class CustomTextFieldItnerarySubWrapper extends StatefulWidget { class CustomTextFieldItnerarySubWrapper extends StatefulWidget {
final Widget child; final Widget child;
final bool isFocused; final bool isFocused;
@ -21,33 +23,61 @@ class CustomTextFieldItnerarySubWrapper extends StatefulWidget {
}); });
@override @override
_CustomTextFieldItnerarySubWrapperState createState() => _CustomTextFieldItnerarySubWrapperState(); _CustomTextFieldItnerarySubWrapperState createState() =>
_CustomTextFieldItnerarySubWrapperState();
}
class _CustomTextFieldItnerarySubWrapperState
extends State<CustomTextFieldItnerarySubWrapper> {
Color? layoutColor;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
} }
class _CustomTextFieldItnerarySubWrapperState extends State<CustomTextFieldItnerarySubWrapper> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: widget.width ?? // Use custom width if provided, else default width:
widget.width ?? // Use custom width if provided, else default
(widget.isDesktop (widget.isDesktop
? MediaQuery.of(context).size.width * 0.15 ? MediaQuery.of(context).size.width * 0.15
: MediaQuery.of(context).size.width * 0.8), : MediaQuery.of(context).size.width * 0.8),
padding: widget.padding, padding: widget.padding,
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all( border: Border.all(
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6), color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
width: widget.isFocused ? 2.0 : 0.5, // color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
width: widget.isFocused ? 1.0 : 0.5,
), ),
boxShadow: widget.isFocused boxShadow:
widget.isFocused
? [ ? [
BoxShadow( BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3), color: Colors.white,
// color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10, blurRadius: 10,
spreadRadius: 2, spreadRadius: 2,
offset: Offset(0, 4), offset: Offset(0, 4),
), ),
] ]
: [], : [],