userManagement changes ,profile highlighter

This commit is contained in:
venbaittech 2025-05-19 11:21:20 +05:30
parent 53f2c4d5b5
commit b55b908f42
13 changed files with 842 additions and 539 deletions

View File

@ -403,7 +403,7 @@ class _ApprovalListState extends State<ApprovalList> {
),
if (isDesktop)
SizedBox(
width: MediaQuery.of(context).size.width * 0.19,
width: MediaQuery.of(context).size.width * 0.23,
),
if (isDesktop)

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -258,7 +259,8 @@ class _LoginWidgetState extends State<LoginWidget> {
return Container(
// color: Color(0xFF114D8B),
color: Color(0xFFf5f5f5),
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
child: Row(
children: [
if (widget.isDesktop)
@ -266,42 +268,67 @@ class _LoginWidgetState extends State<LoginWidget> {
flex: 2,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/login/login_travel.png',
),
// fit: BoxFit.fill
// image: DecorationImage(
// image: AssetImage(
// 'assets/images/login/login_travel.png',
// ),
// // fit: BoxFit.fill
// fit: BoxFit.contain
// fit: BoxFit.cover, // or BoxFit.contain, BoxFit.fill, etc.
),
color: Colors.white,
// // fit: BoxFit.cover, // or BoxFit.contain, BoxFit.fill, etc.
// ),
color: Color(0xFFE6F0FA),
// color: Color(0xFFF0F7FF),
// color: Colors.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(25), // Rounded top-left corner
topRight: Radius.circular(250), // Rounded top-left corner
bottomRight:
Radius.circular(25), // Rounded bottom-left corner
Radius.circular(250), // Rounded bottom-left corner
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 0,
),
child: Align(
alignment: Alignment.topLeft,
child: Image.asset(
// child: Padding(
// padding: const EdgeInsets.only(
// left: 0,
// ),
// child: Align(
// alignment: Alignment.topLeft,
// child: Image.asset(
// 'assets/images/login/logoNew.jpg',
// width: 200, // Optional: control size
// height: 100,
// fit: BoxFit.contain,
// )),
// ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
'assets/images/login/logoNew.jpg',
width: 200, // Optional: control size
height: 100,
fit: BoxFit.contain,
),
Expanded(
child: Container(
child: Align(
// alignment: Alignment.bottomRight,
child: Image.asset(
'assets/images/login/login_travel.png',
// width: 200, // Optional: control size
// height: 100,
fit: BoxFit.contain,
)),
),
)
],
),
),
),
Expanded(
flex: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
// color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25), // Rounded top-left corner
bottomLeft: Radius.circular(25), // Rounded bottom-left corner
@ -329,20 +356,18 @@ class _LoginWidgetState extends State<LoginWidget> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
Text(
"Sign In",
style: TextStyle(
fontSize: 26,
fontFamily: "Nunito",
fontWeight: FontWeight.w600,
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xFF212121)),
),
const SizedBox(height: 2),
const Text(
Text(
"Welcome To TravelSpends",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 11,
fontFamily: "Nunito",
fontWeight: FontWeight.w400,
color: Color(0xFF212121)),
),
@ -353,10 +378,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Email Address"),
TextFormField(
controller: _emailController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
decoration:
_inputDecoration("Enter your email address").copyWith(
prefixIcon: Icon(
@ -374,10 +397,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Password"),
TextFormField(
controller: _passwordController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
obscureText: _obscureText,
decoration: _inputDecoration("Enter your password").copyWith(
prefixIcon: Icon(
@ -422,10 +443,8 @@ class _LoginWidgetState extends State<LoginWidget> {
children: [
Text(
"Sign In",
style: TextStyle(
fontFamily: "Nunito",
fontWeight: FontWeight.w800,
fontSize: 15),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15),
),
SizedBox(
width: 3,
@ -445,10 +464,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Email Address"),
TextFormField(
controller: _emailController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
decoration:
_inputDecoration("Enter your email address").copyWith(
prefixIcon: Icon(
@ -481,10 +498,8 @@ class _LoginWidgetState extends State<LoginWidget> {
children: [
Text(
"Submit",
style: TextStyle(
fontFamily: "Nunito",
fontWeight: FontWeight.w800,
fontSize: 15),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15),
),
SizedBox(
width: 3,
@ -505,10 +520,8 @@ class _LoginWidgetState extends State<LoginWidget> {
TextFormField(
controller: _emailController,
readOnly: true,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
decoration:
_inputDecoration("Enter your email address").copyWith(
prefixIcon: Icon(
@ -523,10 +536,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("OTP"),
TextFormField(
controller: _otpController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
decoration: _inputDecoration("Enter your OTP").copyWith(
prefixIcon: Icon(
Icons.email_outlined,
@ -540,10 +551,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("New Password"),
TextFormField(
controller: _newPasswordController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
obscureText: _obscureText,
decoration:
_inputDecoration("Enter your new password").copyWith(
@ -569,10 +578,8 @@ class _LoginWidgetState extends State<LoginWidget> {
_buildLabel("Confirm Password"),
TextFormField(
controller: _confirmPasswordController,
style: TextStyle(
fontFamily: "Archivo",
fontWeight: FontWeight.w600,
fontSize: 11),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600, fontSize: 11),
obscureText: _obscureText,
decoration:
_inputDecoration("Enter your confirm password").copyWith(
@ -630,10 +637,8 @@ class _LoginWidgetState extends State<LoginWidget> {
children: [
Text(
"Submit",
style: TextStyle(
fontFamily: "Nunito",
fontWeight: FontWeight.w800,
fontSize: 15),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w800, fontSize: 15),
),
SizedBox(
width: 3,
@ -663,10 +668,10 @@ class _LoginWidgetState extends State<LoginWidget> {
},
child: Text(
"Forgot Password",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w300,
fontFamily: "Nunito",
color: Color(0xFF212121), // Text color
decoration:
TextDecoration.underline, // Underline the text
@ -686,10 +691,10 @@ class _LoginWidgetState extends State<LoginWidget> {
},
child: Text(
"Back to Login",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w300,
fontFamily: "Nunito",
color: Color(0xFF212121), // Text color
decoration:
TextDecoration.underline, // Underline the text
@ -726,10 +731,8 @@ class _LoginWidgetState extends State<LoginWidget> {
children: [
Text(
"Sign In With Microsoft",
style: TextStyle(
fontFamily: "Nunito",
fontWeight: FontWeight.w500,
fontSize: 15),
style: GoogleFonts.poppins(
fontWeight: FontWeight.w500, fontSize: 14),
),
SizedBox(
width: 3,
@ -785,10 +788,9 @@ class _LoginWidgetState extends State<LoginWidget> {
padding: const EdgeInsets.only(bottom: 8),
child: Text(
text,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
fontFamily: "Nunito",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
),
),
@ -803,7 +805,7 @@ class _LoginWidgetState extends State<LoginWidget> {
contentPadding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 0.0),
filled: true,
fillColor: Colors.white,
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
labelStyle: GoogleFonts.poppins(fontSize: 11, color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(18),
borderSide: BorderSide(

View File

@ -8,6 +8,7 @@ import 'package:frontend/Screens/organization/mailSettings.dart';
import 'package:frontend/Screens/organization/themeColor.dart';
import 'package:frontend/utils/auth_utils.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:responsive_builder/responsive_builder.dart';
@ -415,7 +416,7 @@ class _groupState extends State<Group> {
children: [
Text(
"Create Group",
style: TextStyle(fontSize: 18),
style: GoogleFonts.poppins(fontSize: 18),
),
// Container(
// color: Color(0xFFE9EBF6),

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import '../../config/apiUrl.dart';
@ -224,7 +225,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Sender Email",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -254,11 +255,11 @@ class _MailSettingState extends State<MailSetting> {
"Invalid email format";
}
},
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "sender email",
labelStyle:
TextStyle(fontSize: 12, color: Colors.grey),
labelStyle: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -315,7 +316,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Test Mail",
style: TextStyle(
style: GoogleFonts.poppins(
color: Color(0xFF114D8B), fontWeight: FontWeight.w600),
),
],
@ -340,7 +341,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"User Name",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -360,10 +361,11 @@ class _MailSettingState extends State<MailSetting> {
onChanged: (value) {
_clearError("mail_user_name");
},
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "user name",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -385,7 +387,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Password",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -409,7 +411,8 @@ class _MailSettingState extends State<MailSetting> {
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "password",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -449,7 +452,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Host",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -469,10 +472,11 @@ class _MailSettingState extends State<MailSetting> {
onChanged: (value) {
_clearError("mail_host");
},
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "host",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -494,7 +498,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Port",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -514,10 +518,11 @@ class _MailSettingState extends State<MailSetting> {
onChanged: (value) {
_clearError("mail_port");
},
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "port",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -544,7 +549,7 @@ class _MailSettingState extends State<MailSetting> {
children: [
Text(
"Enter Your Mail Id",
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
@ -573,10 +578,11 @@ class _MailSettingState extends State<MailSetting> {
errorMessages["to_mail"] = "Invalid email format";
}
},
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "To mail",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -616,7 +622,7 @@ class _MailSettingState extends State<MailSetting> {
},
child: Text(
"Test Email",
style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(fontSize: 12),
))
],
),

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:frontend/Screens/organization/mailSettings.dart';
import 'package:frontend/Screens/organization/themeColor.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
@ -444,7 +445,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
child: Column(
children: [
Container(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.only(
left: 20, right: 20, bottom: 20, top: 5),
// height: MediaQuery.of(context).size.height * 0.8,
color: Colors.white,
child: Column(
@ -460,8 +462,8 @@ class _OrgSetUpState extends State<OrgSetUp> {
selectedOrg != null && selectedOrg!.isNotEmpty
? "Update Organization"
: "Create Organization",
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w800),
style: GoogleFonts.poppins(
fontSize: 15, fontWeight: FontWeight.w500),
),
],
),
@ -472,12 +474,11 @@ class _OrgSetUpState extends State<OrgSetUp> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(top: 1.0),
Padding(
padding: const EdgeInsets.only(top: 1.0),
child: Text(
"Name:",
style: TextStyle(
fontFamily: "Archivo",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
@ -487,14 +488,15 @@ class _OrgSetUpState extends State<OrgSetUp> {
Expanded(
child: TextFormField(
controller: _orgNameController,
style: TextStyle(
style: GoogleFonts.poppins(
fontSize: 16,
color: Color(0xFF114D8B),
),
decoration: InputDecoration(
hintText: "Enter Organization Name",
hintStyle:
TextStyle(fontSize: 14, color: Colors.grey),
hintStyle: GoogleFonts.poppins(
fontSize: 14, color: Colors.grey),
floatingLabelBehavior:
FloatingLabelBehavior.never,
border: InputBorder.none,
@ -543,8 +545,90 @@ class _OrgSetUpState extends State<OrgSetUp> {
],
),
),
SizedBox(
height: 5,
height: 10,
),
Text(
"Services",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
),
SizedBox(
height: 10,
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1),
// color: bodyColor,
// color: Color(0xFFF5F5F5),
color: Colors.white),
padding:
EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5),
child: isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.min,
children: _buildOptions(),
)
: Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildOptions(),
),
),
),
),
SizedBox(
height: 15,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Choose Theme",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
),
Container(
decoration: BoxDecoration(
// border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1),
// color: Color(0xFFF4F4FB),
),
padding: EdgeInsets.only(
left: 5, right: 5, top: 15, bottom: 5),
child: layoutColor != null && bodyColor != null
? ColorThemePickerWidget(
initialLayoutColor: layoutColor,
initialBodyColor: bodyColor,
onLayoutColorSelected:
(Color selectedLayoutColor) {
setState(() {
layoutColor = selectedLayoutColor;
});
},
onBodyColorSelected: (Color selectedBodyColor) {
setState(() {
bodyColor = selectedBodyColor;
});
},
)
: CircularProgressIndicator(),
),
],
),
SizedBox(
height: 15,
),
Container(
color: Colors.white,
@ -555,8 +639,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
children: [
Text(
"Mail Settings",
style: TextStyle(
fontFamily: "Archivo",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
@ -615,89 +698,6 @@ class _OrgSetUpState extends State<OrgSetUp> {
))
],
)),
SizedBox(
height: 15,
),
Text(
"Services",
style: TextStyle(
fontFamily: "Archivo",
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
),
SizedBox(
height: 10,
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1),
// color: bodyColor,
// color: Color(0xFFF5F5F5),
color: Colors.white),
padding:
EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5),
child: isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.min,
children: _buildOptions(),
)
: Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildOptions(),
),
),
),
),
SizedBox(
height: 15,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Choose Theme",
style: TextStyle(
fontFamily: "Archivo",
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF212121)),
),
Container(
decoration: BoxDecoration(
// border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1),
// color: Color(0xFFF4F4FB),
),
padding: EdgeInsets.only(
left: 5, right: 5, top: 15, bottom: 5),
child: layoutColor != null && bodyColor != null
? ColorThemePickerWidget(
initialLayoutColor: layoutColor,
initialBodyColor: bodyColor,
onLayoutColorSelected:
(Color selectedLayoutColor) {
setState(() {
layoutColor = selectedLayoutColor;
});
},
onBodyColorSelected: (Color selectedBodyColor) {
setState(() {
bodyColor = selectedBodyColor;
});
},
)
: CircularProgressIndicator(),
),
],
),
// isDesktop
// ? Row(
// mainAxisAlignment: MainAxisAlignment.end,
@ -775,21 +775,19 @@ class _OrgSetUpState extends State<OrgSetUp> {
color:
isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569)),
SizedBox(width: 5),
SizedBox(width: 2),
SizedBox(width: 5),
Text(
name,
style: TextStyle(
fontSize: 13,
style: GoogleFonts.poppins(
fontSize: 12,
color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
fontFamily: "Archivo",
fontWeight:
isSelected == name ? FontWeight.bold : FontWeight.w500),
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
),
SizedBox(width: 5),
SizedBox(width: 2),
// if (selectedListOption == title && widget.isViewMode == false)
Container(
height: 15,
@ -849,7 +847,10 @@ class _OrgSetUpState extends State<OrgSetUp> {
onPressed: () {
context.go('/listPlan');
},
child: Text("Cancel")),
child: Text(
"Cancel",
style: GoogleFonts.poppins(fontSize: 12),
)),
SizedBox(
width: 20,
),
@ -871,7 +872,10 @@ class _OrgSetUpState extends State<OrgSetUp> {
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: handleSubmit, // Disable when in view mode
child: Text("Submit"),
child: Text(
"Submit",
style: GoogleFonts.poppins(fontSize: 12),
),
),
)
];

View File

@ -379,7 +379,10 @@ class _ListPlansState extends State<ListPlans> {
],
),
Spacer(),
// Spacer(),
SizedBox(
width: MediaQuery.of(context).size.width * 0.23,
),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -773,7 +776,8 @@ class _ListPlansState extends State<ListPlans> {
context); // Close popup manually
ApiService.viewPlan(
context, plan.planId,
isViewMode: true);
isViewMode: true,
isMyTrips: true);
},
),
IconButton(
@ -785,7 +789,8 @@ class _ListPlansState extends State<ListPlans> {
Navigator.pop(context);
ApiService.viewPlan(
context, plan.planId,
isViewMode: false);
isViewMode: false,
isMyTrips: true);
},
),
IconButton(

View File

@ -70,6 +70,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
late List<dynamic>? apiRoleData;
late List<dynamic>? apiUserData;
Map<String, dynamic>? apiselectedUser;
List<Map<String, dynamic>> selectedServiceIds = [];
late List<dynamic> userList;
late Future<List<dynamic>> futureUsers;
@ -146,7 +147,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
"address": controllers["address"]?.text,
"gender": selectedGender,
// "gender": personalDetailsKey.currentState?.selectedGender,
"agent_supported_service_ids": selectedServiceIds,
"postal_code": controllers["postalCode"]?.text,
"country_code": selectedCountry,
"employee_code": controllers["employeeCode"]?.text,
@ -270,6 +271,27 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedThirdApprover =
apiselectedUser?["third_approver"]?.toString() ?? "";
}
try {
final raw = apiselectedUser!["agent_supported_service_ids"];
// Fix the invalid JSON (dangerous if the format changes)
final fixedJson = raw.replaceAllMapped(
RegExp(r'(\w+):'), // matches `service_id:`
(match) => '"${match.group(1)}":',
);
List<dynamic> decodedList = jsonDecode(fixedJson);
selectedServiceIds = decodedList.map<Map<String, dynamic>>((item) {
final map = Map<String, dynamic>.from(item);
return {
"service_id": map['service_id'].toString(),
};
}).toList();
} catch (e) {
print("❌ Error decoding fixed agent_supported_service_ids: $e");
selectedServiceIds = [];
}
print("Updated selectedGender: $selectedGender"); // Debugging
@ -932,6 +954,13 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedRole = role;
});
},
onServiceIdsChanged: (List<Map<String, dynamic>> updatedList) {
setState(() {
selectedServiceIds = updatedList;
});
},
initialSelectedServices: selectedServiceIds,
);
case "office":
return OfficeDetails(
@ -991,6 +1020,11 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
selectedCountry: selectedCountry,
apiselectedUser: isapiselectedUser,
onUserTypeChanged: handleUserTypeChange,
onServiceIdsChanged: (List<Map<String, dynamic>> updatedList) {
setState(() {
selectedServiceIds = updatedList;
});
},
);
}
}

View File

@ -20,10 +20,13 @@ class PersonalDetails extends StatefulWidget {
final bool isViewMode;
final bool apiselectedUser;
final Function(List<Map<String, dynamic>>)? onServiceIdsChanged;
final ValueChanged<String?>? onGenderChanged;
final ValueChanged<String?>? onCountryChanged;
final ValueChanged<String?>? onRoleChanged;
final void Function(bool)? onUserTypeChanged;
final List<Map<String, dynamic>>? initialSelectedServices;
final String? selectedGender;
final String? selectedCountry;
@ -35,7 +38,9 @@ class PersonalDetails extends StatefulWidget {
this.selectedGender,
this.selectedCountry,
this.selectedRole,
this.onServiceIdsChanged,
required this.personalDetailsKey,
this.initialSelectedServices,
required this.controllers,
required this.errorMessages,
required this.isDesktop,
@ -131,6 +136,10 @@ class PersonalDetailsState extends State<PersonalDetails> {
selectedRole = widget.selectedRole;
fetchRoles();
fetchCountries();
selectedServiceIds =
List<Map<String, dynamic>>.from(widget.initialSelectedServices ?? []);
print("selectedServiceIdsAPI - $selectedServiceIds");
WidgetsBinding.instance.addPostFrameCallback((_) {
loadAllServices();
@ -199,13 +208,22 @@ class PersonalDetailsState extends State<PersonalDetails> {
services = [];
}
if (selectedServiceIds.isEmpty && services.isNotEmpty) {
selectedServiceIds = services.map<Map<String, dynamic>>((item) {
// force cast or copy to a regular map
final map = Map<String, dynamic>.from(item);
return {
"service_id": map['service_id'].toString(),
};
}).toList();
}
// selectedServiceIds = services.map<Map<String, dynamic>>((item) {
// // force cast or copy to a regular map
// final map = Map<String, dynamic>.from(item);
// return {
// "service_id": map['service_id'].toString(),
// };
// }).toList();
});
// orgId = await getOrgId();
@ -351,6 +369,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
} else {
selectedServiceIds.add({"service_id": serviceId});
}
// Call the parent's callback
widget.onServiceIdsChanged!(selectedServiceIds);
});
},
child: Row(children: [
@ -755,7 +776,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
setState(() {
_selectedDateOfBirth = pickedDate;
widget.controllers["dob"]?.text =
DateFormat('yyyy-MM-dd').format(pickedDate);
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}

View File

@ -50,6 +50,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
html.File? passportFile;
late List<bool> isExpandedList;
int? expandedIndex;
bool isCountryLoading = true;
late final userId;
String? selectedFileNames;
@ -79,6 +80,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
Map<String, String> countryMap = {};
late List<dynamic>? apiCountryData;
late List<dynamic>? apiAirlineCountryData;
Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {};
@ -86,7 +88,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
"Passport Details",
// "Local ID Details",
"Visa Details",
"Frequent Flier Information",
"Frequent Flyer Information",
"Hotel Loyalty Membership",
"Preferences Domestic",
"Preferences International",
@ -94,6 +96,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
"Forex Details"
];
//To Create Controllers
List<String> dataHeader = [
"Fname",
"Lname",
@ -111,6 +114,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
"forex_card_num",
"full_name_as_id",
"local_id_num",
"forex_expiry_date"
];
@override
@ -120,11 +124,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
// userId = getUserId();
apiCountryData = null;
apiAirlineCountryData = [];
apiData = null;
for (var field in dataHeader) {
controllers[field] = TextEditingController();
}
fetchCountries();
loadCountryList();
fetchApiData();
// Delay adding the row until after the first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
@ -170,7 +176,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
frequentFlierEntries.add({
"local_id": localId,
"id": null, // to be set when backend responds
"controller_airline": TextEditingController(),
// "controller_airline": null,
"airline": null,
"controller_flier_number": TextEditingController(),
"is_active": "1"
});
@ -196,7 +203,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
"visa_type_id": null,
"controller_valid_from": TextEditingController(),
"controller_valid_upto": TextEditingController(),
"is_active": "1"
});
setState(() {});
@ -240,12 +246,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
"date_of_expiry": controllers["dateOfExpiry"]?.text,
"d_meal_preference": controllers["d_meal_pref"]?.text,
"d_seat_preference": selectedDomesticSeat,
// "d_additonalInfo": controllers["d_additonal_Info"]?.text,
"d_additonalInfo": controllers["d_additonal_Info"]?.text,
"i_meal_preference": controllers["i_meal_pref"]?.text,
"i_seat_preference": selectedIntenationalSeat,
// "i_additonalInfo": controllers["i_additonal_Info"]?.text,
"i_additonalInfo": controllers["i_additonal_Info"]?.text,
"emergency_contact_number": controllers["emergency_contact"]?.text,
"forex_pre_paid_card_number": controllers["forex_card_num"]?.text,
"forex_expiry_date": controllers["forex_expiry_date"]?.text,
// "frequent_flier_information": [],
"frequent_flier_information": frequentFlierList,
"hotel_membership": hotelMembershipList,
@ -270,14 +277,33 @@ class TravellerDetailsState extends State<TravellerDetails> {
}).toList();
}
// List<Map<String, dynamic>> get frequentFlierList {
// return frequentFlierEntries
// // .where((entry) => entry["is_active"] != "0") // Only active entries
// .map((entry) {
// return {
// "id": entry["id"],
// "airline": entry["airline"],
// "frequent_flier_number": entry["controller_flier_number"].text,
// "created_by": null,
// "updated_by": null,
// "is_active": entry["is_active"] ?? "1",
// };
// }).toList();
// }
List<Map<String, dynamic>> get frequentFlierList {
return frequentFlierEntries
// .where((entry) => entry["is_active"] != "0") // Only active entries
.map((entry) {
return frequentFlierEntries.map((entry) {
final frequent_flier_number = entry["controller_flier_number"];
print("frequent_flier_number- $frequent_flier_number");
return {
"id": entry["id"],
"airline": entry["controller_airline"].text,
"frequent_flier_number": entry["controller_flier_number"].text,
"airline": entry["airline"], // string value
"frequent_flier_number": frequent_flier_number is TextEditingController
? frequent_flier_number.text
: "",
"created_by": null,
"updated_by": null,
"is_active": entry["is_active"] ?? "1",
@ -346,10 +372,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
controllers["d_meal_pref"]?.text =
widget.travelDetails?["d_meal_preference"] ?? "";
controllers["i_meal_pref"]?.text =
widget.travelDetails?["i_meal_preference"] ?? "";
controllers["d_additonal_Info"]?.text =
widget.travelDetails?["d_additonalInfo"] ?? "";
controllers["i_additonal_Info"]?.text =
widget.travelDetails?["i_additonalInfo"] ?? "";
selectedIntenationalSeat =
widget.travelDetails?["i_seat_preference"] ?? "";
selectedDomesticSeat = widget.travelDetails?["d_seat_preference"] ?? "";
@ -359,6 +389,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
controllers["forex_card_num"]?.text =
widget.travelDetails?["forex_pre_paid_card_number"] ?? "";
controllers["forex_expiry_date"]?.text =
widget.travelDetails?["forex_expiry_date"] ?? "";
String? apiDocPath = widget.travelDetails?["passport_document"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
@ -402,38 +434,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
}
//----------------------------------------------------
// 🚨 Frequent Flier Logic (pre-fill for edit)
for (var entry in frequentFlierEntries) {
entry["controller_airline"].dispose();
entry["controller_flier_number"].dispose();
}
frequentFlierEntries.clear();
final frequentFlierList =
widget.travelDetails?['frequent_flier_information'];
if (frequentFlierList != null && frequentFlierList is List) {
for (var item in frequentFlierList) {
if (item["is_active"]?.toString() == "0") continue;
final localId = _rowFlierCounter++;
frequentFlierEntries.add({
"local_id": localId,
"id": item["id"],
"controller_airline":
TextEditingController(text: item["airline"] ?? ""),
"controller_flier_number": TextEditingController(
text: item["frequent_flier_number"] ?? ""),
});
}
if (frequentFlierEntries.isEmpty) {
addFrequentFlierEntry();
}
}
//----------------------------------------------------
// 🚨 Visa Logic (pre-fill for edit)
for (var entry in visaEntries) {
entry["controller_valid_from"]?.dispose();
@ -463,6 +463,40 @@ class TravellerDetailsState extends State<TravellerDetails> {
addVisaEntry();
}
}
// ----------------------------------------------------
// 🚨 Frequent Flier Logic (pre-fill for edit)
for (var entry in frequentFlierEntries) {
// entry["controller_airline"].dispose();
entry["controller_flier_number"].dispose();
}
frequentFlierEntries.clear();
final frequentFlierList =
widget.travelDetails?['frequent_flier_information'];
if (frequentFlierList != null && frequentFlierList is List) {
for (var item in frequentFlierList) {
if (item["is_active"]?.toString() == "0") continue;
final localId = _rowFlierCounter++;
final airlineCode = item["airline"];
frequentFlierEntries.add({
"local_id": localId,
"id": item["id"],
"airline": airlineCode ?? "",
"controller_flier_number": TextEditingController(
text: item["frequent_flier_number"] ?? ""),
});
}
if (frequentFlierEntries.isEmpty) {
addFrequentFlierEntry();
}
}
//----------------------------------------------------
setState(() {});
}
@ -479,6 +513,35 @@ class TravellerDetailsState extends State<TravellerDetails> {
}
}
Future<void> loadCountryList() async {
final newTripType = "2";
final result = await apiService.fetchFlightsCountryList(newTripType);
print("resultFlight: $result ");
setState(() {
apiAirlineCountryData = result;
});
// print("ResultCountry : $result");
//
// // Create a map: Country_Code -> "City, Airport"
// Map<String, String> tempCountryMap = {};
//
// for (var country in result) {
// String city = country['City'] ?? '';
// String airport = country['Airport'] ?? '';
// String displayName = '${country['City']} - ${country['Airport']}';
//
// tempCountryMap[country['Code']] = displayName;
// }
//
// setState(() {
// countryMap = tempCountryMap; // Update the map
// isCountryLoading = false;
// });
}
Future<void> fetchApiData() async {
try {
final data = await apiService.fetchMasterDropdown();
@ -605,7 +668,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
case "Visa Details":
return _buildVisaDetails(widget.isDesktop);
case "Frequent Flier Information":
case "Frequent Flyer Information":
return _buildFrequentFlierInfo(widget.isDesktop);
case "Hotel Loyalty Membership":
@ -960,7 +1023,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
setState(() {
_selectedDateOfIssue = pickedDate;
controllers["dateOfIssue"]?.text =
DateFormat('yyyy-MM-dd').format(pickedDate);
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
@ -1038,7 +1101,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
setState(() {
_selectedDateOfExpiry = pickedDate;
controllers["dateOfExpiry"]?.text =
DateFormat('yyyy-MM-dd').format(pickedDate);
DateFormat('dd-MM-yyyy').format(pickedDate);
// controllers["dateOfExpiry"]?.text =
// DateFormat('dd-MM-yyyy').format(pickedDate);
@ -1497,8 +1560,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
buildDomesticSeatType(),
SizedBox(width: 15),
buildDomesticMealPref(),
// SizedBox(width: 15),
// buildDomesticAddtnlPref(),
SizedBox(width: 15),
buildDomesticAddtnlPref(),
],
)
: Column(
@ -1507,9 +1570,9 @@ class TravellerDetailsState extends State<TravellerDetails> {
buildDomesticSeatType(),
SizedBox(height: 8),
buildDomesticMealPref(),
// SizedBox(height: 8), // Vertical space
//
// buildDomesticAddtnlPref(),
SizedBox(height: 8), // Vertical space
buildDomesticAddtnlPref(),
],
),
);
@ -1702,8 +1765,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
buildInternationalSeatType(),
SizedBox(width: 15),
buildInternationalMealPref(),
// SizedBox(width: 15),
// buildInternationalAddtnlPref(),
SizedBox(width: 15),
buildInternationalAddtnlPref(),
],
)
: Column(
@ -1712,9 +1775,9 @@ class TravellerDetailsState extends State<TravellerDetails> {
buildInternationalSeatType(),
SizedBox(height: 8),
buildInternationalMealPref(),
// SizedBox(height: 8), // Vertical space
//
// buildInternationalAddtnlPref(),
SizedBox(height: 8), // Vertical space
buildInternationalAddtnlPref(),
],
),
);
@ -1947,11 +2010,34 @@ class TravellerDetailsState extends State<TravellerDetails> {
SizedBox(
height: 10,
),
buildForexCardNumber(),
_buildForexDetailDataRow1(isDesktop),
],
);
}
Widget _buildForexDetailDataRow1(bool isDesktop) {
return Container(
color: Colors.white,
child: widget.isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildForexCardNumber(),
SizedBox(width: 15),
buildForexExpiryDate(),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildForexCardNumber(),
SizedBox(height: 8),
buildForexExpiryDate()
],
),
);
}
Widget buildForexCardNumber() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -1989,6 +2075,79 @@ class TravellerDetailsState extends State<TravellerDetails> {
);
}
Widget buildForexExpiryDate() {
DateTime? _selectedExpiryDate;
Future<void> _selectExpiryDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
_selectedExpiryDate != null && _selectedExpiryDate!.isAfter(today)
? _selectedExpiryDate!
: today,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedExpiryDate) {
setState(() {
_selectedExpiryDate = pickedDate;
// controllers["forex_expiry_date"]?.text =
// DateFormat('yyyy-MM-dd').format(pickedDate);
controllers["forex_expiry_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex Expiry Date",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: widget.isViewMode
? null
: () async {
await _selectExpiryDate(context);
},
child: AbsorbPointer(
child: TextField(
controller: controllers["forex_expiry_date"],
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)),
),
),
),
),
),
),
],
);
}
//-------------------------Frequent Flier Information -------------------------------
Widget _buildFrequentFlierInfo(bool isDesktop) {
@ -2056,6 +2215,50 @@ class TravellerDetailsState extends State<TravellerDetails> {
}
Widget buildAirline(Map<String, dynamic> entry) {
late Map<String, String> countryMap; // Mapping country_code -> country_name
late List<String> countryCodes; // List of country codes
// countryList = [];
bool hasAirlineCountryData =
apiAirlineCountryData == null || apiAirlineCountryData!.isEmpty;
countryList = apiAirlineCountryData!;
print("TestcountryList2 - $countryList");
countryMap = {
for (var country in countryList)
country['Code'] as String: '${country['City']} - ${country['Airport']}'
};
countryCodes = countryMap.keys.toList();
// setState(() {
// countryMap = tempCountryMap; // Update the map
// isCountryLoading = false;
// });
// Map country codes to country names
// countryMap = {
//
//
// for (var item in countryList)
// item['country_code'] as String: item['country_name'] as String
// };
// Extract only country codes for processing
//--------------------------
// String? selectedCountry = entry['airline'];
String? selectedCode = entry['airline'];
String? selectedText =
selectedCode != null ? countryMap[selectedCode] : null;
print("selectedCode: $selectedCode");
print("selectedText: $selectedText");
print(
"items.contains(selectedText): ${countryMap.values.contains(selectedText)}");
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -2067,30 +2270,105 @@ class TravellerDetailsState extends State<TravellerDetails> {
SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper(
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
? MediaQuery.of(context).size.width * 0.17
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
controller: entry["controller_airline"],
enabled: !widget.isViewMode,
onChanged: (value) {
_clearError("first_name");
},
child: hasAirlineCountryData
? CircularProgressIndicator()
: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// selectedItem: entry['airline'] != null
// ? countryMap[entry['airline']]
// : null,
// selectedItem: countryMap.containsKey(entry['airline'])
// ? countryMap[entry['airline']]
// : null,
// selectedItem: countryMap[selectedCode],
// selectedItem: countryMap.containsKey(selectedCode)
// ? countryMap[selectedCode]
// : null,
selectedItem: (entry["airline"] != null &&
countryMap.containsKey(entry["airline"]))
? countryMap[entry["airline"]]
: null,
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
labelText: "Airline",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
hintText: "Search Country...",
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
),
),
dropdownBuilder: (context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select Country",
style: TextStyle(fontSize: 12),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
// selectedCountry = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
final selectedCode = countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
entry['airline'] = selectedCode;
// final selectedCode = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
// entry['airline'] = selectedCode;
});
},
),
),
),
// CustomTextFieldUserTravellerWrapper(
// width: widget.isDesktop
// ? MediaQuery.of(context).size.width * 0.15
// : null,
// isFocused: false,
// isDesktop: widget.isDesktop,
// child: SizedBox(
// height: 40,
// child: TextField(
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
// controller: entry["controller_airline"],
// enabled: !widget.isViewMode,
// onChanged: (value) {
// _clearError("first_name");
// },
// decoration: InputDecoration(
// labelText: "Airline",
// labelStyle:
// GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
// ),
// ),
],
);
}
@ -2102,7 +2380,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Frequent Flier Information",
"Frequent Flyer Information",
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
),
@ -2120,7 +2398,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
_clearError("first_name");
},
decoration: InputDecoration(
labelText: "Frequent Flier Information",
labelText: "Frequent Flyer Information",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
@ -2145,7 +2423,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
size: 30,
),
onPressed: () => addFrequentFlierEntry(),
tooltip: "Frequent Flier Information",
tooltip: "Frequent Flyer Information",
),
);
}
@ -2397,6 +2675,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
// countryList = [];
countryList = apiCountryData ?? [];
print("TestcountryList1 - $countryList");
// Map country codes to country names
countryMap = {
for (var item in countryList)
@ -2593,7 +2873,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
// _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
entry["controller_valid_from"].text =
DateFormat('yyyy-MM-dd').format(pickedDate);
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
@ -2673,7 +2953,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
// _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
entry["controller_valid_upto"].text =
DateFormat('yyyy-MM-dd').format(pickedDate);
DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}

View File

@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart'; // don't forget
import '../services/apiService.dart';
import '../utils/auth_utils.dart';
enum TabSelection { allTrips, myTrips, myApprovals }
enum TabSelection { allTrips, myTrips, myApprovals, allMenu }
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
final bool isDesktop;
@ -34,7 +34,7 @@ class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
class _CustomAppBarState extends State<CustomAppBar> {
final ApiService apiService = ApiService();
TabSelection selectedTab = TabSelection.allTrips;
late TabSelection selectedTab;
String? token;
Map<String, dynamic>? userData;
@ -43,14 +43,10 @@ class _CustomAppBarState extends State<CustomAppBar> {
Map<String, dynamic> profileUserDetails = {};
Map<String, dynamic>? selectedOrg;
Color? layoutColor;
// Color? layoutColor;
Color? layoutColor = Colors.white10;
Color? bodyColor;
Color _myTravelRequestColor = Color(0xFF475569); // Default color
EdgeInsets _myTravelRequestPadding =
EdgeInsets.symmetric(horizontal: 8, vertical: 4);
Color _myApprovalsColor = Color(0xFF475569); // Default color
void initState() {
super.initState();
// initializeData();
@ -58,6 +54,12 @@ class _CustomAppBarState extends State<CustomAppBar> {
initializeData();
getOrganizationData();
});
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin") {
selectedTab = TabSelection.allTrips;
} else {
selectedTab = TabSelection.myTrips;
}
}
Future<void> getProfileUser() async {
@ -94,17 +96,6 @@ class _CustomAppBarState extends State<CustomAppBar> {
getProfileUser();
}
String getTabLabel(TabSelection tab) {
switch (tab) {
case TabSelection.allTrips:
return "All Trips";
case TabSelection.myTrips:
return "My Trips";
case TabSelection.myApprovals:
return "My Approvals";
}
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString("auth_token");
@ -174,6 +165,43 @@ class _CustomAppBarState extends State<CustomAppBar> {
}
}
void handleTabChange(TabSelection tab, String route) {
final currentUri =
GoRouterState.of(context).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri != route) {
setState(() {
selectedTab = tab;
});
context.go(route);
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
final location = GoRouterState.of(context).uri.toString();
print("location - $location");
setState(() {
if (location.contains('/listAllPlan') ||
location.contains('/allTrips/trips')) {
selectedTab = TabSelection.allTrips;
} else if (location.contains('/listPlan') ||
location.contains('/createPlan') ||
location.contains('/listTravelAgentPlan')) {
selectedTab = TabSelection.myTrips;
} else if (location.contains('/ApprovalList')) {
selectedTab = TabSelection.myApprovals;
} else {
selectedTab = TabSelection.allMenu;
}
});
}
@override
Widget build(BuildContext context) {
return AppBar(
@ -216,7 +244,7 @@ class _CustomAppBarState extends State<CustomAppBar> {
)
: const CircleAvatar(
radius: 20,
backgroundColor: Colors.white,
// backgroundColor: Colors.white,
child: Icon(
Icons.add_a_photo,
size: 10,
@ -225,186 +253,8 @@ class _CustomAppBarState extends State<CustomAppBar> {
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.2,
width: MediaQuery.of(context).size.width * 0.18,
),
// Container(
// width: MediaQuery.of(context).size.width * 0.35,
// // color: Colors.blueGrey,
// child: Row(
// // crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// if (userData?["role"] == "Org Admin" ||
// userData?["role"] == "Travel Admin")
// MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (_) {
// setState(() {
// // _myTravelRequestPadding =
// // EdgeInsets.symmetric(horizontal: 12, vertical: 78);
// // _myTravelRequestColor =
// // Color(0xFF114D8B); // Change color on hover
// });
// },
// onExit: (_) {
// setState(() {
// // _myTravelRequestPadding = EdgeInsets.symmetric(
// // horizontal: 8, vertical: 4); // Normal padding
// // _myTravelRequestColor =
// // Color(0xFF475569); // Revert color when hover ends
// });
// },
// child: InkWell(
// onTap: () {
// context.go('/listAllPlan');
// },
// child: Text(
// "All Trips",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// // color: Color(0xFF475569),
// color: _myTravelRequestColor,
// fontFamily: "Inter",
// ),
// )),
// ),
// const SizedBox(width: 20),
// if (userDetails["role"] == "Travel Agent")
// MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (_) {
// setState(() {
// // _myTravelRequestPadding =
// // EdgeInsets.symmetric(horizontal: 12, vertical: 78);
// // _myTravelRequestColor =
// // Color(0xFF114D8B); // Change color on hover
// });
// },
// onExit: (_) {
// setState(() {
// // _myTravelRequestPadding = EdgeInsets.symmetric(
// // horizontal: 8, vertical: 4); // Normal padding
// // _myTravelRequestColor =
// // Color(0xFF475569); // Revert color when hover ends
// });
// },
// child: InkWell(
// onTap: () {
// context.go('/listTravelAgentPlan');
// },
// child: Text(
// "My Trips",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// // color: Color(0xFF475569),
// color: _myTravelRequestColor,
// fontFamily: "Inter",
// ),
// )),
// ),
// if (userDetails["role"] != "Travel Agent")
// const SizedBox(width: 20),
// if (userDetails["role"] != "Travel Agent")
// MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (_) {
// setState(() {
// // _myTravelRequestPadding =
// // EdgeInsets.symmetric(horizontal: 12, vertical: 78);
// // _myTravelRequestColor =
// // Color(0xFF114D8B); // Change color on hover
// });
// },
// onExit: (_) {
// setState(() {
// // _myTravelRequestPadding = EdgeInsets.symmetric(
// // horizontal: 8, vertical: 4); // Normal padding
// // _myTravelRequestColor =
// // Color(0xFF475569); // Revert color when hover ends
// });
// },
// child: InkWell(
// onTap: () {
// context.go('/listPlan');
// },
// child: Text(
// "My Trips",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// // color: Color(0xFF475569),
// color: _myTravelRequestColor,
// fontFamily: "Inter",
// ),
// )),
// ),
// const SizedBox(width: 20),
// if (userData?["role"] != "Travel Agent")
// MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (_) {
// setState(() {
// _myApprovalsColor =
// Color(0xFF114D8B); // Change color on hover
// });
// },
// onExit: (_) {
// setState(() {
// _myApprovalsColor = Color(
// 0xFF475569); // Revert color when hover ends
// });
// },
// child: InkWell(
// onTap: () {
// context.go('/ApprovalList');
// },
// child: Text(
// "My Approvals",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: _myApprovalsColor,
// // color: Color(0xFF475569),
// fontFamily: "Inter",
// ),
// )),
// ),
// if (userData?["role"] == "Travel Agent")
// MouseRegion(
// cursor: SystemMouseCursors.click,
// onEnter: (_) {
// setState(() {
// _myApprovalsColor =
// Color(0xFF114D8B); // Change color on hover
// });
// },
// onExit: (_) {
// setState(() {
// _myApprovalsColor = Color(
// 0xFF475569); // Revert color when hover ends
// });
// },
// child: InkWell(
// onTap: () {
// context.go('/listTravelAgentPlan');
// },
// child: Text(
// "My Approvals",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: _myApprovalsColor,
// // color: Color(0xFF475569),
// fontFamily: "Inter",
// ),
// )),
// ),
// ],
// ),
// ),
Container(
width: MediaQuery.of(context).size.width * 0.35,
child: Row(
@ -412,9 +262,14 @@ class _CustomAppBarState extends State<CustomAppBar> {
children: [
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
buildNavItem("All Trips", _myTravelRequestColor,
() => context.go('/listAllPlan'),
isSelected: selectedTab == "All Trips"),
buildNavItem(
"All Trips",
() => handleTabChange(
TabSelection.allTrips, '/listAllPlan'),
layoutColor!,
isSelected: selectedTab == TabSelection.allTrips,
icon: Icons.insights_outlined,
),
if (userDetails["role"] != "Travel Agent")
const SizedBox(width: 20),
@ -422,17 +277,23 @@ class _CustomAppBarState extends State<CustomAppBar> {
if (userData?["role"] == "Travel Agent")
buildNavItem(
"My Trips",
_myTravelRequestColor,
() => context.go('/listTravelAgentPlan'),
isSelected: selectedTab == "My Trips",
() => handleTabChange(
TabSelection.myTrips, '/listTravelAgentPlan'),
layoutColor!,
// () => context.go('/listTravelAgentPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.request_page_outlined,
),
if (userData?["role"] != "Travel Agent") // for others
buildNavItem(
"My Trips",
_myTravelRequestColor,
() => context.go('/listPlan'),
isSelected: selectedTab == "My Trips",
() => handleTabChange(
TabSelection.myTrips, '/listPlan'),
layoutColor!,
// () => context.go('/listPlan'),
isSelected: selectedTab == TabSelection.myTrips,
icon: Icons.request_page_outlined,
),
// if (userDetails["role"] == "Travel Agent")
// buildNavItem("My Trips", _myTravelRequestColor,
@ -448,11 +309,23 @@ class _CustomAppBarState extends State<CustomAppBar> {
const SizedBox(width: 20),
if (userData?["role"] != "Travel Agent")
buildNavItem("My Approvals", _myApprovalsColor, () {
isSelected:
selectedTab == "My Approvals";
context.go('/ApprovalList');
})
buildNavItem(
"My Approvals",
() => handleTabChange(
TabSelection.myApprovals, '/ApprovalList'),
layoutColor!,
// () => context.go('/ApprovalList'),
isSelected: selectedTab == TabSelection.myApprovals,
icon: Icons.assessment_outlined,
),
// buildNavItem("My Approvals", _myApprovalsColor, () {
// isSelected:
// selectedTab == "My Approvals",
// context.go('/ApprovalList'),
// icon:Icons.insights_outlined;
// })
// if (userData?["role"] == "Travel Agent") {
// isSelected:
@ -466,7 +339,6 @@ class _CustomAppBarState extends State<CustomAppBar> {
],
),
),
Spacer(),
],
),
@ -664,11 +536,64 @@ PopupMenuItem<String> buildMenuItem(Map<String, dynamic> item) {
);
}
Widget buildNavItem(String label, Color color, VoidCallback onTap,
{bool isSelected = false}) {
Widget buildNavItem(String label, VoidCallback onTap, Color? layoutColor,
{bool isSelected = true, IconData? icon}) {
final effectiveColor =
isSelected ? (layoutColor ?? Colors.blue) : Colors.black;
return MouseRegion(
cursor: SystemMouseCursors.click,
child: InkWell(
child: GestureDetector(
onTap: onTap,
child: Stack(
clipBehavior: Clip.none,
children: [
Row(
children: [
if (icon != null) Icon(icon, size: 16, color: effectiveColor),
if (icon != null) const SizedBox(width: 4),
Text(
label,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: effectiveColor,
),
),
],
),
if (isSelected)
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: -20,
left: 0,
right: 0,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 300),
opacity: 1.0,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: 2,
width: isSelected
? 50
: 0, // Animate width (make sure isSelected changes)
color: effectiveColor,
),
),
),
],
),
),
);
}
Widget buildNavItem1(String label, VoidCallback onTap,
{bool isSelected = true, IconData? icon}) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(bottom: 2),
@ -676,22 +601,43 @@ Widget buildNavItem(String label, Color color, VoidCallback onTap,
? BoxDecoration(
border: Border(
bottom: BorderSide(
color: color, // underline color
width: 2, // underline thickness
),
color: Colors.red),
),
)
: null, // no underline when not selected
child: Text(
child: Row(
children: [
if (icon != null)
Icon(
icon,
size: 16,
color: isSelected ? Colors.red : Colors.black,
// color: Colors.red,
),
if (icon != null) SizedBox(width: 4),
Text(
label,
style: GoogleFonts.poppins(
fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black),
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: color,
// fontFamily: "Inter",
// ),
fontSize: 12,
fontWeight: FontWeight.w500,
color: isSelected ? Colors.red : Colors.black,
// color: Colors.red,
),
),
// Red underline positioned below (e.g. under green line)
if (isSelected)
Positioned(
bottom: -50, // adjust this to push it below green line
left: 0,
right: 0,
child: Container(
height: 2,
color: Colors.red,
),
),
],
),
),
),

View File

@ -173,11 +173,11 @@ class _CustomDrawerState extends State<CustomDrawer> {
if (userData?["role"] == "Org Admin" ||
userData?["role"] == "Travel Admin")
_buildDrawerItem(context, Icons.request_page_outlined,
'All Trips', '/listAllPlan'),
_buildDrawerItem(context, Icons.insights_outlined, 'All Trips',
'/listAllPlan'),
if (userDetails["role"] == "Travel Agent")
_buildDrawerItem(context, Icons.request_page_outlined,
_buildDrawerItem(context, Icons.assessment_outlined,
'My Approvals', '/listTravelAgentPlan'),
if (userDetails["role"] != "Travel Agent")

View File

@ -47,6 +47,10 @@ final GoRouter router = GoRouter(
path: '/createPlan',
builder: (context, state) => CreatePlan(),
),
GoRoute(
path: '/allTrips/trips',
builder: (context, state) => CreatePlan(),
),
GoRoute(
path: '/listUser',
builder: (context, state) => UserListScreen(),

View File

@ -412,12 +412,12 @@ class ApiService {
}
static Future<void> viewPlan(BuildContext context, String planId,
{bool isViewMode = false}) async {
{bool isViewMode = false, bool isMyTrips = false}) async {
try {
Map<String, dynamic> planData = await getViewPlan(planId);
print("ViewAAA - $planData");
context.go('/createPlan',
context.go(isMyTrips ? '/createPlan' : '/allTrips/trips',
extra: {'planData': planData, 'isViewMode': isViewMode});
} catch (e) {
print("Error fetching plan: $e");