dashboard mobile

This commit is contained in:
venba-Inspriron-3558 2025-07-17 12:02:27 +05:30
parent 35229fa755
commit 0ec324bf03
5 changed files with 647 additions and 339 deletions

View File

@ -30,6 +30,7 @@ class StatusDashboardState extends State<StatusDashboard> {
Map<String, dynamic>? apiData;
String? organizationId;
late bool _dialogShown = false;
bool isLoading = false;
late StreamSubscription<html.PopStateEvent> _popStateListener;
String selectedView = 'Today';
String AccessKeyFlag = "Both";
@ -148,6 +149,10 @@ class StatusDashboardState extends State<StatusDashboard> {
WidgetsBinding.instance.addPostFrameCallback((_) {
checkbackbutton();
});
setState(() {
isLoading = true;
});
// If token exists, load the dashboard data
loadDashboardData();
}
@ -157,6 +162,7 @@ class StatusDashboardState extends State<StatusDashboard> {
final data = await fetchStatusDashboard();
setState(() {
apiData = data;
isLoading = false;
});
} catch (e) {
print("Error loading dashboard: $e");
@ -484,37 +490,35 @@ class StatusDashboardState extends State<StatusDashboard> {
);
}
Widget statusCard(String label, int count) {
return Column(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
decoration: BoxDecoration(
color: const Color(0xFFEAF3FB),
borderRadius: BorderRadius.circular(8),
Widget statusCard(String label, int count, bool isDesktop) {
return Container(
width: isDesktop ? 120 : double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
decoration: BoxDecoration(
color: const Color(0xFFEAF3FB),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
count.toString(),
style: GoogleFonts.poppins(
fontSize: isDesktop ? 18 : 16,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
child: Column(
children: [
Text(
count.toString(),
style: GoogleFonts.poppins(
fontSize: 18,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 5),
Text(
label,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black87,
),
),
],
const SizedBox(height: 5),
Text(
label,
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: isDesktop ? 12 : 11,
color: Colors.black87,
),
),
),
],
],
),
);
}
@ -725,152 +729,256 @@ class StatusDashboardState extends State<StatusDashboard> {
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical: 10, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
// child: ConstrainedBox(
// constraints: BoxConstraints(
// minHeight: constraints.maxHeight,
// ),
// ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// First Row: Toggle Buttons Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: 36,
decoration: BoxDecoration(
color: const Color(0xFFEAEAEA),
borderRadius: BorderRadius.circular(20),
),
child: Row(
padding: isDesktop
? const EdgeInsets.symmetric(vertical: 5)
: EdgeInsets.zero,
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 1200,
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0), // Scroll content padding
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
isLoading ? Center(child: CircularProgressIndicator()) :
LayoutBuilder(
builder: (context, constraints) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
toggleChip("Today"),
toggleChip("This Week"),
],
),
),
],
),
const SizedBox(height: 20),
// Second Row: Domestic & International and Status
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// LEFT SIDE
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
padding: const EdgeInsets.all(15),
child: Row(
// First Row: Toggle Buttons Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
...typeData.map((item) {
return Expanded(
child: Column(
children: [
buildTopCard(
// icon: item['value'] == "Domestic"
// ? Icons.home
// : Icons.travel_explore,
color: item['value'] == "Domestic"
? Color(0xFF0DB04B)
: Color(0xFF004A8E),
label: item['value'],
count: item['count'],
bgColor: item['value'] == "Domestic"
? const Color(0xFFD6FBE4)
: const Color(0xFFD9E8FF),
Container(
height: 36,
decoration: BoxDecoration(
color: const Color(0xFFEAEAEA),
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
toggleChip("Today"),
toggleChip("This Week"),
],
),
),
],
),
const SizedBox(height: 20),
// Second Row: Domestic & International and Status
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
);
}).toList(),
padding: const EdgeInsets.all(15),
child: Row(
children: [
...typeData.map((item) {
return Expanded(
child: Column(
children: [
buildTopCard(
color: item['value'] == "Domestic"
? Color(0xFF0DB04B)
: Color(0xFF004A8E),
label: item['value'],
count: item['count'],
bgColor: item['value'] == "Domestic"
? const Color(0xFFD6FBE4)
: const Color(0xFFD9E8FF),
),
],
),
);
}).toList(),
],
),
),
),
const SizedBox(width: 20),
Expanded(
flex: 3,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Status",
style: GoogleFonts.poppins(
fontSize: 19,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
...statusData.map((item) => statusCard(item['value'], item['count'], isDesktop)).toList(),
],
),
],
),
),
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
padding: const EdgeInsets.all(15),
child: Row(
children: [
...typeData.map((item) {
return Expanded(
child: Column(
children: [
buildTopCard(
color: item['value'] == "Domestic"
? const Color(0xFF0DB04B)
: const Color(0xFF004A8E),
label: item['value'],
count: item['count'],
bgColor: item['value'] == "Domestic"
? const Color(0xFFD6FBE4)
: const Color(0xFFD9E8FF),
),
],
),
);
}).toList(),
],
),
),
const SizedBox(height: 20),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Status",
style: GoogleFonts.poppins(
fontSize: 19,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 23),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
...statusData.map((item) => statusCard(item['value'], item['count'], isDesktop)).toList(),
],
),
],
),
),
],
),
),
),
const SizedBox(width: 20),
// RIGHT SIDE
Expanded(
flex: 3,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.blueGrey,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Status",
style: GoogleFonts.poppins(
fontSize: 19,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 23),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
...statusData.map((item) => statusCard(item['value'], item['count'])).toList(),
],
),
],
),
),
),
],
),
const SizedBox(height: 20),
// Third Row: Flight, Accommodation, Forex
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: buildCategoryCard("Flight", Icons.flight,statusLabels,flightData)),
const SizedBox(width: 10),
Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined,statusLabels,accommodationData)),
const SizedBox(width: 10),
Expanded(child: buildCategoryCard("Forex", Icons.attach_money,statusLabels,forexData)),
],
),
],
const SizedBox(height: 20),
// Third Row: Flight, Accommodation, Forex
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: buildCategoryCard("Flight", Icons.flight, statusLabels, flightData)),
const SizedBox(width: 10),
Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined, statusLabels, accommodationData)),
const SizedBox(width: 10),
Expanded(child: buildCategoryCard("Forex", Icons.attach_money, statusLabels, forexData)),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(child: buildCategoryCard("Flight", Icons.flight, statusLabels, flightData)),
],
),
const SizedBox(height: 20),
Row(
children: [
Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined, statusLabels, accommodationData)),
],
),
const SizedBox(height: 20),
Row(
children: [
Expanded(child: buildCategoryCard("Forex", Icons.attach_money, statusLabels, forexData)),
],
),
const SizedBox(height: 20),
],
),
],
);
},
) ,
],
),
),
);
},
),
),
),
),
),

View File

@ -11,6 +11,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:frontend/Screens/userManagement/create_user/personal_details.dart';
import 'package:frontend/Screens/userManagement/create_user/traveller_details.dart';
import 'package:frontend/Screens/userManagement/create_user/office_details.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
@ -30,7 +31,6 @@ import '../../../widgets/custom_text_field.dart';
import '../../../widgets/custom_text_forex.dart';
import '../../../widgets/custom_user_form.dart';
import '../../../widgets/saving_loader.dart';
import 'office_details.dart';
import 'package:universal_html/html.dart' as html;
@ -46,6 +46,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
GlobalKey<PersonalDetailsState>();
final GlobalKey<TravellerDetailsState> travellerDetailsKey =
GlobalKey<TravellerDetailsState>();
final GlobalKey<OfficeDetailsState> officeDetailsKey =
GlobalKey<OfficeDetailsState>();
// late List<Map<String, dynamic>?> travelDetailsData;
Map<String, dynamic>? travelDetailsData;
@ -60,6 +62,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
bool isCheckingToken = false;
bool isDisable = false;
final FocusNode submitButtonFocusNode = FocusNode();
String? token;
late bool isapiselectedUser = false;
@ -1448,7 +1453,6 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedRole = role;
});
},
onServiceIdsChanged: (List<Map<String, dynamic>> updatedList) {
setState(() {
selectedServiceIds = updatedList;
@ -1755,26 +1759,29 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
isViewMode
? SystemMouseCursors.forbidden
: SystemMouseCursors.click,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color
foregroundColor:
isViewMode
? Colors.white
: Colors.white, // Keep original color
disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: layoutColor, width: 2),
child: Focus( // 👈 Wrap with Focus here
focusNode: submitButtonFocusNode,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
isViewMode ? layoutColor : layoutColor, // Keep original color
foregroundColor:
isViewMode
? Colors.white
: Colors.white, // Keep original color
disabledBackgroundColor:
layoutColor, // Ensure color remains when disabled
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: layoutColor, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
onPressed:
isViewMode ? null : handleSubmit, // Disable when in view mode
child: Text("Submit"),
),
onPressed:
isViewMode ? null : handleSubmit, // Disable when in view mode
child: Text("Submit"),
),
),
];

View File

@ -58,10 +58,10 @@ class OfficeDetails extends StatefulWidget {
}) : super(key: key);
@override
_OfficeDetailsState createState() => _OfficeDetailsState();
OfficeDetailsState createState() => OfficeDetailsState();
}
class _OfficeDetailsState extends State<OfficeDetails> {
class OfficeDetailsState extends State<OfficeDetails> {
final ApiService apiService = ApiService();
late Map<String, String> countryMap; // Mapping country_code -> country_name
@ -169,6 +169,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
_employeeCodeFocusNode.requestFocus();
});
apiCountryData = null;
apiUserData = null;
@ -702,8 +703,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(_departmentFocus ?? false)
? (layoutColor ?? Colors.white)
(_departmentFocus)
? (layoutColor)
: Colors.white,
width: 0.5,
),
@ -711,8 +712,8 @@ class _OfficeDetailsState extends State<OfficeDetails> {
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_departmentFocus ?? false)
? (layoutColor ?? Colors.white)
(_departmentFocus)
? (layoutColor)
: Colors.white,
width: 0.5,
// const Color(0xFFD6D5E6),
@ -1868,39 +1869,83 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
// onTap: () async{
// _selectCheckOutDate(context);
//
// },
onTap: () async {
_startDateFocusNode?.requestFocus();
await _selectCheckOutDate(context);
child:Focus(
focusNode: _startDateFocusNode,
onKeyEvent: (FocusNode node, KeyEvent event) {
// Only act on KeyDown and when Enter is pressed
if (event is KeyDownEvent &&
(event.logicalKey == LogicalKeyboardKey.enter ||
event.logicalKey == LogicalKeyboardKey.numpadEnter)) {
_startDateFocusNode?.requestFocus();
_selectCheckOutDate(context);
return KeyEventResult.handled; // Stop default behavior
}
return KeyEventResult.ignored; // Allow normal handling
},
child: AbsorbPointer(
child: TextField(
focusNode: _startDateFocusNode,
controller: widget.controllers["delegationStartDate"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (details) {
_startDateFocusNode?.requestFocus();
_selectCheckOutDate(context);
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["delegationStartDate"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
// child: GestureDetector(
// // onTap: () async{
// // _selectCheckOutDate(context);
// //
// // },
// onTap: () async {
// _startDateFocusNode?.requestFocus();
// await _selectCheckOutDate(context);
// },
// child: AbsorbPointer(
// child: TextField(
// focusNode: _startDateFocusNode,
// controller: widget.controllers["delegationStartDate"],
// readOnly: true,
// style: const TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Date",
// labelStyle: const TextStyle(
// fontSize: 12,
// color: Colors.grey,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
// suffixIcon: const Icon(
// Icons.calendar_today,
// size: 16,
// color: Colors.grey,
// ),
// ),
// ),
// ),
// ),
),
),
// if (errorMessages["start_date"] != null) ...[
@ -1975,39 +2020,89 @@ class _OfficeDetailsState extends State<OfficeDetails> {
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
// onTap: () async{
// _selectCheckOutDate(context);
//
// },
onTap: () async {
_endDateFocusNode?.requestFocus();
await _selectForexEndDate(context);
child:Focus(
focusNode: _endDateFocusNode,
onKeyEvent: (FocusNode node, KeyEvent event) {
// Only act on KeyDown and when Enter is pressed
if (event is KeyDownEvent &&
(event.logicalKey == LogicalKeyboardKey.enter ||
event.logicalKey == LogicalKeyboardKey.numpadEnter)) {
if (!widget.isViewMode) {
_selectForexEndDate(context).then((_) {
_endDateFocusNode?.requestFocus();
});
}
return KeyEventResult.handled; // Stop default behavior
}
return KeyEventResult.ignored; // Allow normal handling
},
child: AbsorbPointer(
child: TextField(
focusNode: _endDateFocusNode,
controller: widget.controllers["delegationEndDate"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (details) {
if (!widget.isViewMode) {
_selectForexEndDate(context).then((_) {
_endDateFocusNode?.requestFocus();
});
}
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["delegationEndDate"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
// child: GestureDetector(
// // onTap: () async{
// // _selectCheckOutDate(context);
// //
// // },
// onTap: () async {
// _endDateFocusNode?.requestFocus();
// await _selectForexEndDate(context);
// },
// child: AbsorbPointer(
// child: TextField(
// focusNode: _endDateFocusNode,
// controller: widget.controllers["delegationEndDate"],
// readOnly: true,
// style: const TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Date",
// labelStyle: const TextStyle(
// fontSize: 12,
// color: Colors.grey,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
// suffixIcon: const Icon(
// Icons.calendar_today,
// size: 16,
// color: Colors.grey,
// ),
// ),
// ),
// ),
// ),
),
),

View File

@ -35,6 +35,7 @@ class PersonalDetails extends StatefulWidget {
final String? selectedCountry;
final String? selectedRole;
final String? userIdApi;
final dynamic focusNodes;
// const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},);
const PersonalDetails({
@ -50,6 +51,7 @@ class PersonalDetails extends StatefulWidget {
required this.isDesktop,
required this.isViewMode,
required this.apiselectedUser,
this.focusNodes,
this.onGenderChanged,
this.onCountryChanged,
this.onRoleChanged,
@ -75,8 +77,32 @@ class PersonalDetailsState extends State<PersonalDetails> {
String? get selectedGenderValue => selectedGender;
String? get selectedCountryValue => selectedCountry;
String? get selectedRoleValue => selectedRole;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
final FocusNode _fnameFocusNode = FocusNode();
final FocusNode _lnameFocusNode = FocusNode();
final FocusNode _emailFocusNode = FocusNode();
final FocusNode _passwordFocusNode = FocusNode();
final FocusNode _dobFocusNode = FocusNode();
final FocusNode _genderFocusNode = FocusNode();
final FocusNode _mobileNumberFocusNode = FocusNode();
final FocusNode _alternateMobileFocusNode = FocusNode();
final FocusNode _addressFocusNode = FocusNode();
final FocusNode _countryFocusNode = FocusNode();
final FocusNode _postalCodeFocusNode = FocusNode();
final FocusNode _roleIdFocusNode = FocusNode();
bool _fnameFocus = false;
bool _lnameFocus = false;
bool _emailFocus = false;
bool _passwordFocus = false;
bool _dobFocus = false;
bool _genderFocus = false;
bool _mobileNumberFocus = false;
bool _alternateMobileFocus = false;
bool _addressFocus = false;
bool _countryFocus = false;
bool _postalCodeFocus = false;
bool _roleIdFocus = false;
late bool isTravelAgent = false;
@ -154,23 +180,26 @@ class PersonalDetailsState extends State<PersonalDetails> {
print("selectedServiceIdsAPI - $selectedServiceIds");
for (var field in dataHeader) {
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;
});
});
}
_addFocusListener(_fnameFocusNode, (focus) => _fnameFocus = focus);
_addFocusListener(_lnameFocusNode, (focus) => _lnameFocus = focus);
_addFocusListener(_emailFocusNode, (focus) => _emailFocus = focus);
_addFocusListener(_passwordFocusNode, (focus) => _passwordFocus = focus);
_addFocusListener(_dobFocusNode, (focus) => _dobFocus = focus);
_addFocusListener(_genderFocusNode, (focus) => _genderFocus = focus);
_addFocusListener(_mobileNumberFocusNode, (focus) => _mobileNumberFocus = focus);
_addFocusListener(_alternateMobileFocusNode, (focus) => _alternateMobileFocus = focus);
_addFocusListener(_addressFocusNode, (focus) => _addressFocus = focus);
_addFocusListener(_countryFocusNode, (focus) => _countryFocus = focus);
_addFocusListener(_postalCodeFocusNode, (focus) => _postalCodeFocus = focus);
_addFocusListener(_roleIdFocusNode, (focus) => _roleIdFocus = focus);
WidgetsBinding.instance.addPostFrameCallback((_) {
loadAllServices();
getOrganizationData();
loadInitialData();
_fnameFocusNode.requestFocus();
});
}
@ -802,13 +831,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["FnameFocused"] ?? false,
isFocused: _fnameFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["FnameFocusNode"],
// focusNode: focusNodes["FnameFocusNode"],
focusNode: _fnameFocusNode, // 👈 Use it here
controller: widget.controllers["Fname"],
autofocus: true,
enabled: !widget.isViewMode,
@ -854,13 +884,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["LnameFocused"] ?? false,
isFocused: _lnameFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: TextStyle(fontSize: 12, color: Colors.black),
focusNode: focusNodes["LnameFocusNode"],
focusNode: _lnameFocusNode,
autofocus: true,
controller: widget.controllers["Lname"],
enabled: !widget.isViewMode,
@ -1006,21 +1036,21 @@ class PersonalDetailsState extends State<PersonalDetails> {
widget.isDesktop
? MediaQuery.of(context).size.width * 0.12
: null,
isFocused: focusStates["genderFocused"] ?? false,
isFocused: _genderFocus,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: Focus(
focusNode: focusNodes["genderFocusNode"],
focusNode: _genderFocusNode,
onFocusChange: (hasFocus) {
setState(() {
focusStates["genderFocused"] = hasFocus;
_genderFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
focusNodes["genderFocusNode"]?.requestFocus();
_genderFocusNode?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: selectedGender,
@ -1051,7 +1081,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
color:
(focusStates["genderFocused"] ?? false)
(_genderFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
width: 0.5,
@ -1060,7 +1090,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["genderFocused"] ?? false)
(_genderFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
// : const Color(0xFFD6D5E6),
@ -1069,7 +1099,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color : (layoutColor ?? Colors.grey), width: 0.5),
borderSide: BorderSide(
color : (layoutColor ?? Colors.grey), width: 0.5),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
@ -1152,51 +1183,110 @@ class PersonalDetailsState extends State<PersonalDetails> {
widget.isDesktop
? MediaQuery.of(context).size.width * 0.12
: null,
isFocused: focusStates["dobFocused"] ?? false,
isFocused: _dobFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap:
widget.isViewMode
? null
: () async {
await _selectCheckDateOfBirth(context);
if (widget.controllers["dob"]!.text.isNotEmpty) {
setState(() {
// errorMessages.remove("start_date");
});
// Delay re-focusing the DOB field slightly
Future.delayed(Duration(milliseconds: 100), () {
focusNodes["dobFocusNode"]?.requestFocus();
});
}
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["dob"],
focusNode: focusNodes["dobFocusNode"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
child:
Focus(
focusNode: _dobFocusNode,
onKeyEvent: (FocusNode node, KeyEvent event) {
// Only act on KeyDown and when Enter is pressed
if (event is KeyDownEvent &&
(event.logicalKey == LogicalKeyboardKey.enter ||
event.logicalKey == LogicalKeyboardKey.numpadEnter)) {
if (!widget.isViewMode) {
_selectCheckDateOfBirth(context).then((_) {
if (widget.controllers["dob"]!.text.isNotEmpty) {
Future.delayed(Duration(milliseconds: 100), () {
_dobFocusNode?.requestFocus();
});
}
});
}
return KeyEventResult.handled; // Stop default behavior
}
return KeyEventResult.ignored; // Allow normal handling
},
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (details) {
if (!widget.isViewMode) {
_selectCheckDateOfBirth(context).then((_) {
if (widget.controllers["dob"]!.text.isNotEmpty) {
Future.delayed(Duration(milliseconds: 100), () {
_dobFocusNode?.requestFocus();
});
}
});
}
},
child: AbsorbPointer(
child: TextField(
controller: widget.controllers["dob"],
readOnly: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Color(0xFF8B8FB2),
),
),
),
),
),
),
// GestureDetector(
// onTap:
// widget.isViewMode
// ? null
// : () async {
// await _selectCheckDateOfBirth(context);
// if (widget.controllers["dob"]!.text.isNotEmpty) {
// setState(() {
// // errorMessages.remove("start_date");
// });
// // Delay re-focusing the DOB field slightly
// Future.delayed(Duration(milliseconds: 100), () {
// _dobFocusNode?.requestFocus();
// });
// }
// },
// child: AbsorbPointer(
// child: TextField(
// controller: widget.controllers["dob"],
// focusNode:_dobFocusNode,
// readOnly: true,
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// decoration: InputDecoration(
// labelText: "Select Date",
// labelStyle: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w400,
// color: Colors.grey,
// ),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// suffixIcon: Icon(
// Icons.calendar_today,
// size: 16,
// color: Color(0xFF8B8FB2),
// ),
// ),
// ),
// ),
// ),
),
),
],
@ -1217,13 +1307,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["emailFocused"] ?? false,
isFocused: _emailFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["emailFocusNode"],
focusNode: _emailFocusNode,
autofocus: true,
controller: widget.controllers["email"],
onChanged: (value) {
@ -1269,13 +1359,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["mobileNumberFocused"] ?? false,
isFocused: _mobileNumberFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["mobileNumberFocusNode"],
focusNode: _mobileNumberFocusNode,
autofocus: true,
controller: widget.controllers["mobileNumber"],
enabled: !widget.isViewMode,
@ -1332,13 +1422,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["alternateMobileFocused"] ?? false,
isFocused: _alternateMobileFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["alternateMobileFocusNode"],
focusNode: _alternateMobileFocusNode,
autofocus: true,
controller: widget.controllers["alternateMobile"],
enabled: !widget.isViewMode,
@ -1408,23 +1498,23 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["countryFocused"] ?? false,
isFocused: _countryFocus,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: focusNodes["countryFocusNode"],
focusNode: _countryFocusNode,
onFocusChange: (hasFocus) {
setState(() {
focusStates["countryFocused"] = hasFocus;
_countryFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["countryFocusNode"]?.requestFocus();
_countryFocusNode?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
@ -1461,7 +1551,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["countryFocused"] ?? false)
(_countryFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
width: 0.5,
@ -1470,7 +1560,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["countryFocused"] ?? false)
(_countryFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
width: 0.5,
@ -1552,14 +1642,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["postalCodeFocused"] ?? false,
isFocused: _postalCodeFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
autofocus: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["postalCodeFocusNode"],
focusNode: _postalCodeFocusNode,
controller: widget.controllers["postalCode"],
enabled: !widget.isViewMode,
keyboardType: TextInputType.numberWithOptions(decimal: true),
@ -1673,22 +1763,22 @@ class PersonalDetailsState extends State<PersonalDetails> {
SizedBox(height: 5),
CustomTextFieldUserWrapper(
// isFocused: false,
isFocused: focusStates["roleIdFocusNode"] ?? false,
isFocused: _roleIdFocus,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: Focus(
focusNode: focusNodes["roleIdFocusNode"],
focusNode: _roleIdFocusNode,
onFocusChange: (hasFocus) {
setState(() {
focusStates["roleIdFocused"] = hasFocus;
_roleIdFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["roleIdFocusNode"]?.requestFocus();
_roleIdFocusNode?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: selectedRole != null ? roleMap[selectedRole] : null,
@ -1726,7 +1816,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
color:
(focusStates["roleIdFocused"] ?? false)
(_roleIdFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
width: 0.5,
@ -1735,7 +1825,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["roleIdFocused"] ?? false)
(_roleIdFocus)
? (layoutColor ?? Colors.blue)
: Colors.white,
// : const Color(0xFFD6D5E6),
@ -1810,14 +1900,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["addressFocused"] ?? false,
isFocused: _addressFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 80,
child: TextField(
autofocus: true,
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["addressFocusNode"],
focusNode: _addressFocusNode,
controller: widget.controllers["address"],
enabled: !widget.isViewMode,
maxLines: 2,
@ -1854,13 +1944,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
isFocused: focusStates["passwordFocused"] ?? false,
isFocused: _passwordFocus,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["passwordFocusNode"],
focusNode: _passwordFocusNode,
autofocus: true,
controller: widget.controllers["password"],
enabled: !widget.isViewMode,

View File

@ -169,6 +169,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
fetchApiData();
// Delay adding the row until after the first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
// focusNodes["FnameFocusNode"]?.requestFocus();
setState(() {
addHotelLoyaltyEntry();
addFrequentFlierEntry();
@ -176,6 +177,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
loadInitialData();
});
updateTravel();
print("Trying to focus Fname...");
final node = focusNodes["FnameFocusNode"];
if (node != null) {
print("Node exists, requesting focus");
node.requestFocus();
}
});
_addFocusListener(_iSeatPrefFocusNode, (focus) => _iSeatPrefFocus = focus);
@ -1200,8 +1208,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
"First Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
@ -1244,8 +1252,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
"Middle Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
@ -1287,8 +1295,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
"Last Name",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
@ -1339,8 +1347,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
"Nationality",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Colors.black,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),