This commit is contained in:
venbaittech 2025-06-21 19:28:19 +05:30
commit 1c77a03067
27 changed files with 12252 additions and 4729 deletions

View File

@ -36,11 +36,12 @@ class CostCenterDataState extends State<CostCenterData> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
Map<String, dynamic>? apiData; Map<String, dynamic>? apiData;
final Map<String, FocusNode> focusNodes = { // final Map<String, FocusNode> focusNodes = {
"name": FocusNode(), // "name": FocusNode(),
"description": FocusNode(), // "description": FocusNode(),
}; // };
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
@ -70,6 +71,15 @@ class CostCenterDataState extends State<CostCenterData> {
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
if (widget.costcenterId != null) { if (widget.costcenterId != null) {
@ -89,8 +99,18 @@ class CostCenterDataState extends State<CostCenterData> {
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateCostCenterDetails() { void updateCostCenterDetails() {
print("Inside Update Function - ${widget.costcenterData}"); print("Inside Update Function - ${widget.costcenterData}");
@ -129,10 +149,10 @@ class CostCenterDataState extends State<CostCenterData> {
if (data[field] == null || data[field]!.trim().isEmpty) { if (data[field] == null || data[field]!.trim().isEmpty) {
errorMessages[field] = "Required"; errorMessages[field] = "Required";
if (!hasFocused) { // if (!hasFocused) {
focusNodes[field]?.requestFocus(); // focusNodes[field]?.requestFocus();
hasFocused = true; // hasFocused = true;
} // }
} }
} }
@ -258,14 +278,14 @@ class CostCenterDataState extends State<CostCenterData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["nameFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["name"], controller: controllers["name"],
focusNode: focusNodes["name"], focusNode: focusNodes["nameFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Name", labelText: "Name",
@ -300,14 +320,14 @@ class CostCenterDataState extends State<CostCenterData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["descriptionFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 100, height: 100,
child: TextField( child: TextField(
controller: controllers["description"], controller: controllers["description"],
focusNode: focusNodes["description"], focusNode: focusNodes["descriptionFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
maxLines: null, maxLines: null,
expands: true, expands: true,

View File

@ -36,26 +36,27 @@ class DepartmentDataState extends State<DepartmentData> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
Map<String, dynamic>? apiData; Map<String, dynamic>? apiData;
final Map<String, FocusNode> focusNodes = { // final Map<String, FocusNode> focusNodes = {
"name": FocusNode(), // "name": FocusNode(),
"description": FocusNode(), // "description": FocusNode(),
}; // };
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
String? selectedName; String? selectedName;
String? selectedDescription; String? selectedDescription;
String? userId; String? userId;
int? departmentDataId; int? departmentDataId;
late String isActive = "1"; late String isActive = "1";
List<String> dataHeader = ["name", "description"]; List<String> dataHeader = ["dropdown_value", "description"];
Map<String, dynamic> departmentDetails() { Map<String, dynamic> departmentDetails() {
final data = { final data = {
// "department_id": int.parse(departmentId), // "department_id": int.parse(departmentId),
"name": controllers["name"]?.text, "dropdown_value": controllers["dropdown_value"]?.text,
"description": controllers["description"]?.text, "description": controllers["description"]?.text,
"created_by": userId, "created_by": userId,
"is_active": isActive, "is_active": isActive,
@ -70,6 +71,15 @@ class DepartmentDataState extends State<DepartmentData> {
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
if (widget.departmentId != null) { if (widget.departmentId != null) {
@ -89,8 +99,18 @@ class DepartmentDataState extends State<DepartmentData> {
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateDepartmentDetails() { void updateDepartmentDetails() {
print("Inside Update Function - ${widget.departmentData}"); print("Inside Update Function - ${widget.departmentData}");
@ -99,10 +119,10 @@ class DepartmentDataState extends State<DepartmentData> {
if (data == null) return; if (data == null) return;
setState(() { setState(() {
controllers['name']?.text = data['name'] ?? ''; controllers['dropdown_value']?.text = data['dropdown_value'] ?? '';
controllers['description']?.text = data['description'].toString(); // controllers['description']?.text = data['description'].toString();
isActive = data["is_active"]; isActive = data["is_active"];
final departmentId = int.tryParse(data['department_id'].toString()); final departmentId = int.tryParse(data['id'].toString());
departmentDataId = departmentId; departmentDataId = departmentId;
}); });
} }
@ -117,11 +137,11 @@ class DepartmentDataState extends State<DepartmentData> {
errorMessages.clear(); errorMessages.clear();
final data = { final data = {
"name": controllers["name"]?.text, "dropdown_value": controllers["dropdown_value"]?.text,
"description": controllers["description"]?.text, // "description": controllers["description"]?.text,
}; };
final requiredFields = ["name", "description"]; final requiredFields = ["dropdown_value"];
bool hasFocused = false; bool hasFocused = false;
// Check validation for each field // Check validation for each field
@ -129,10 +149,10 @@ class DepartmentDataState extends State<DepartmentData> {
if (data[field] == null || data[field]!.trim().isEmpty) { if (data[field] == null || data[field]!.trim().isEmpty) {
errorMessages[field] = "Required"; errorMessages[field] = "Required";
if (!hasFocused) { // if (!hasFocused) {
focusNodes[field]?.requestFocus(); // focusNodes[field]?.requestFocus();
hasFocused = true; // hasFocused = true;
} // }
} }
} }
@ -165,7 +185,7 @@ class DepartmentDataState extends State<DepartmentData> {
if (departmentDataId != null) { if (departmentDataId != null) {
print("for edit department id - $departmentDataId"); print("for edit department id - $departmentDataId");
apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId'; apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId';
departmentData["department_id"] = departmentDataId.toString(); departmentData["id"] = departmentDataId.toString();
departmentData["updated_by"] = userId; departmentData["updated_by"] = userId;
(departmentData.containsKey("created_by")) (departmentData.containsKey("created_by"))
? departmentData.remove("created_by") ? departmentData.remove("created_by")
@ -261,14 +281,14 @@ class DepartmentDataState extends State<DepartmentData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["dropdown_valueFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["name"], controller: controllers["dropdown_value"],
focusNode: focusNodes["name"], focusNode: focusNodes["dropdown_valueFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Name", labelText: "Name",
@ -280,60 +300,60 @@ class DepartmentDataState extends State<DepartmentData> {
), ),
), ),
), ),
if (errorMessages["name"] != null) ...[ if (errorMessages["dropdown_value"] != null) ...[
SizedBox(height: 5), // Space before error message SizedBox(height: 5), // Space before error message
Text( Text(
errorMessages["name"]!, errorMessages["dropdown_value"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
SizedBox(height: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Description *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 100,
child: TextField(
controller: controllers["description"],
focusNode: focusNodes["description"],
style: const TextStyle(fontSize: 12),
maxLines: null,
expands: true,
decoration: const InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["description"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["description"]!,
style: TextStyle(color: Colors.red, fontSize: 12), style: TextStyle(color: Colors.red, fontSize: 12),
), ),
], ],
], ],
), ),
SizedBox(height: 15), SizedBox(height: 15),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Description *",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w600,
// color: Color(0xFF575A74),
// ),
// ),
// SizedBox(height: 5),
// CustomTextFieldForexWrapper(
// isFocused: focusStates["descriptionFocused"] ?? false,
// isDesktop: widget.isDesktop,
// color: Colors.transparent,
// child: SizedBox(
// height: 100,
// child: TextField(
// controller: controllers["description"],
// focusNode: focusNodes["descriptionFocusNode"],
// style: const TextStyle(fontSize: 12),
// maxLines: null,
// expands: true,
// decoration: const InputDecoration(
// labelText: "Description",
// labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
// ),
// ),
// if (errorMessages["description"] != null) ...[
// SizedBox(height: 5), // Space before error message
// Text(
// errorMessages["description"]!,
// style: TextStyle(color: Colors.red, fontSize: 12),
// ),
// ],
// ],
// ),
// SizedBox(height: 15),
if (departmentDataId != null) if (departmentDataId != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -180,13 +180,13 @@ class DepartmentListState extends State<DepartmentList> {
allDepartment.where((object) { allDepartment.where((object) {
final isActiveStatus = final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive"; object['is_active'] == "1" ? "active" : "inactive";
return (object['department_id']?.toLowerCase().contains( return (object['id']?.toLowerCase().contains(
lowerQuery, lowerQuery,
) ?? ) ??
false) || false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ?? false) || (object['dropdown_value']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['description']?.toLowerCase().contains(lowerQuery) ?? // (object['description']?.toLowerCase().contains(lowerQuery) ??
false) || // false) ||
(isActiveStatus.contains(lowerQuery)); (isActiveStatus.contains(lowerQuery));
}).toList(); }).toList();
currentPage = 0; currentPage = 0;
@ -550,15 +550,15 @@ class DepartmentListState extends State<DepartmentList> {
), ),
), ),
), ),
DataColumn( // DataColumn(
label: Text( // label: Text(
'Description', // 'Description',
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: 13, // fontSize: 13,
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
), // ),
), // ),
), // ),
DataColumn( DataColumn(
label: Text( label: Text(
'Status', 'Status',
@ -581,7 +581,7 @@ class DepartmentListState extends State<DepartmentList> {
rows: rows:
paginatedDepartment.map((tableObject) { paginatedDepartment.map((tableObject) {
String departmentId = String departmentId =
tableObject['department_id'] tableObject['id']
.toString(); // Get user ID .toString(); // Get user ID
bool isSelected = bool isSelected =
selectedDepartmentId == departmentId; selectedDepartmentId == departmentId;
@ -590,24 +590,24 @@ class DepartmentListState extends State<DepartmentList> {
cells: [ cells: [
DataCell( DataCell(
Text( Text(
tableObject['name'] ?? '', tableObject['dropdown_value'] ?? '',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: "Inter", fontFamily: "Inter",
), ),
), ),
), ),
DataCell( // DataCell(
Text( // Text(
tableObject['description'] ?? 'N/A', // tableObject['description'] ?? 'N/A',
style: TextStyle( // style: TextStyle(
fontSize: 13, // fontSize: 13,
fontFamily: "Inter", // fontFamily: "Inter",
), // ),
softWrap: true, // softWrap: true,
overflow: TextOverflow.ellipsis, // overflow: TextOverflow.ellipsis,
), // ),
), // ),
DataCell( DataCell(
Text( Text(
tableObject['is_active'] == "1" tableObject['is_active'] == "1"
@ -645,7 +645,7 @@ class DepartmentListState extends State<DepartmentList> {
// final usersData = await getUserDetails(userId); // final usersData = await getUserDetails(userId);
// //
final departmentId = int.tryParse( final departmentId = int.tryParse(
tableObject['department_id'] tableObject['id']
.toString(), .toString(),
); );
@ -716,7 +716,7 @@ class DepartmentListState extends State<DepartmentList> {
MainAxisAlignment.spaceBetween, MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
cardObject['name'] ?? 'N/A', cardObject['dropdown_value'] ?? 'N/A',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 10, fontSize: 10,
color: Colors.black87, color: Colors.black87,
@ -738,7 +738,7 @@ class DepartmentListState extends State<DepartmentList> {
// final usersData = await getUserDetails(userId); // final usersData = await getUserDetails(userId);
// //
final departmentId = int.tryParse( final departmentId = int.tryParse(
cardObject['department_id'] cardObject['id']
.toString(), .toString(),
); );
@ -852,37 +852,37 @@ class DepartmentListState extends State<DepartmentList> {
SizedBox(height: 2), SizedBox(height: 2),
// Trip Id and Trip Name // Trip Id and Trip Name
// Name // Name
Row( // Row(
children: [ // children: [
Column( // Column(
crossAxisAlignment: // crossAxisAlignment:
CrossAxisAlignment.start, // CrossAxisAlignment.start,
children: [ // children: [
Text( // Text(
cardObject['description'] ?? '', // cardObject['description'] ?? '',
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: 10, // fontSize: 10,
color: Colors.black87, // color: Colors.black87,
), // ),
), // ),
], // ],
), // ),
SizedBox(width: 10), // SizedBox(width: 10),
Column( // Column(
crossAxisAlignment: // crossAxisAlignment:
CrossAxisAlignment.start, // CrossAxisAlignment.start,
children: [ // children: [
Text( // Text(
cardObject['description'] ?? '', // cardObject['description'] ?? '',
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: 10, // fontSize: 10,
color: Colors.black87, // color: Colors.black87,
), // ),
), // ),
], // ],
), // ),
], // ],
), // ),
// Actions // Actions
// Actions // Actions
], ],

View File

@ -42,6 +42,8 @@ class HotelsDataState extends State<HotelsData> {
Map<String, dynamic>? apiData; Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<dynamic> countryList = []; List<dynamic> countryList = [];
String? selectedCountry; String? selectedCountry;
@ -83,6 +85,15 @@ class HotelsDataState extends State<HotelsData> {
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
fetchCountries(); fetchCountries();
if (widget.hotelsId != null) { if (widget.hotelsId != null) {
@ -103,8 +114,18 @@ class HotelsDataState extends State<HotelsData> {
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateHotelsDetails() { void updateHotelsDetails() {
print("Update - ${widget.hotelsData}"); print("Update - ${widget.hotelsData}");
@ -312,12 +333,14 @@ class HotelsDataState extends State<HotelsData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, // isFocused: false,
isFocused: focusStates["hotel_nameFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["hotel_nameFocusNode"],
controller: controllers["hotel_name"], controller: controllers["hotel_name"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -353,12 +376,13 @@ class HotelsDataState extends State<HotelsData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["hotel_chainFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["hotel_chainFocusNode"],
controller: controllers["hotel_chain"], controller: controllers["hotel_chain"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -394,12 +418,14 @@ class HotelsDataState extends State<HotelsData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, // isFocused: false,
isFocused: focusStates["categoryFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["categoryFocusNode"],
controller: controllers["category"], controller: controllers["category"],
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 ]')), FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 ]')),
@ -432,61 +458,102 @@ class HotelsDataState extends State<HotelsData> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownSearch<String>( width: double.infinity,
selectedItem: countryMap[selectedCountry], child: Focus(
popupProps: PopupProps.menu( focusNode: focusNodes["country_codeFocusNode"],
showSearchBox: true, // Enables search functionality onFocusChange: (hasFocus) {
menuProps: const MenuProps(backgroundColor: Colors.white), setState(() {
constraints: BoxConstraints(maxHeight: 250), focusStates["country_codeFocused"] = hasFocus;
itemBuilder: });
(context, item, isSelected) => Padding( },
padding: const EdgeInsets.symmetric( child: GestureDetector(
horizontal: 8.0, onTap: () {
vertical: 6.0, // Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
), ),
child: Text( items: countryMap.values.toList(),
item, dropdownDecoratorProps: DropDownDecoratorProps(
style: GoogleFonts.poppins(fontSize: 11.5), dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
),
), ),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
});
},
), ),
searchFieldProps: TextFieldProps( )
decoration: InputDecoration( )
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
});
},
),
), ),
), ),
if (errorMessages["country_code"] != null) ...[ if (errorMessages["country_code"] != null) ...[
@ -512,12 +579,13 @@ class HotelsDataState extends State<HotelsData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["cityFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["cityFocusNode"],
controller: controllers["city"], controller: controllers["city"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(

View File

@ -33,6 +33,7 @@ import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart'; import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
import '../../utils/auth_utils.dart'; import '../../utils/auth_utils.dart';
import '../../widgets/custom_breadcrumb_navigation.dart';
import '../../widgets/custom_user_travel.dart'; import '../../widgets/custom_user_travel.dart';
import 'dialog_placeholders.dart'; import 'dialog_placeholders.dart';
@ -60,6 +61,8 @@ class TemplateState extends State<Template> {
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
List<String> dataHeader = ["subject"]; List<String> dataHeader = ["subject"];
List<String> placeholders = []; List<String> placeholders = [];
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
late QuillController _controller = QuillController.basic(); late QuillController _controller = QuillController.basic();
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
@ -103,6 +106,15 @@ class TemplateState extends State<Template> {
void initState() { void initState() {
super.initState(); super.initState();
_checkAuthAndLoadData(); _checkAuthAndLoadData();
focusNodes["subjectFocusNode"] = FocusNode();
focusStates["subjectFocused"] = false;
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
}
// //
// for (var field in dataHeader) { // for (var field in dataHeader) {
// controllers[field] = TextEditingController(); // controllers[field] = TextEditingController();
@ -144,6 +156,13 @@ class TemplateState extends State<Template> {
// _editorFocusNode.dispose(); // _editorFocusNode.dispose();
// super.dispose(); // super.dispose();
// } // }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void loadinitializeData() async { void loadinitializeData() async {
orgId = await getOrgId(); orgId = await getOrgId();
userId = await getUserId(); userId = await getUserId();
@ -529,15 +548,40 @@ class TemplateState extends State<Template> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( // Text(
formatTemplateName(templateName), // formatTemplateName(templateName),
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: 18, // fontSize: 18,
fontWeight: FontWeight.w400, // fontWeight: FontWeight.w400,
color: Colors.black, // color: Colors.black,
), // ),
// ),
Row(
children: [
BreadcrumbNavigation(
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Organization Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Templates',
tooltip: 'Go To Templates',
onTap: (context) {
context.go("/templateList");
}
),
BreadcrumbItem(
title: formatTemplateName(templateName),
),
],
),
],
), ),
SizedBox(height: 10), SizedBox(height: 10),
buildTempalteSubject(isDesktop), buildTempalteSubject(isDesktop),
@ -565,7 +609,8 @@ class TemplateState extends State<Template> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldUserTravellerWrapper( CustomTextFieldUserTravellerWrapper(
isFocused: false, // isFocused: false,
isFocused: focusStates["subjectFocused"] ?? false,
color: Colors.white, color: Colors.white,
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
isDesktop: isDesktop, isDesktop: isDesktop,
@ -573,6 +618,7 @@ class TemplateState extends State<Template> {
height: 40, height: 40,
child: TextField( child: TextField(
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
focusNode: focusNodes["subjectNode"],
controller: controllers["subject"], controller: controllers["subject"],
onChanged: (value) { onChanged: (value) {
// _clearError("local_id_num"); // _clearError("local_id_num");

View File

@ -14,6 +14,7 @@ import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart'; import '../../services/apiService.dart';
import '../../utils/auth_utils.dart'; import '../../utils/auth_utils.dart';
import '../../utils/pagination.dart'; import '../../utils/pagination.dart';
import '../../widgets/custom_breadcrumb_navigation.dart';
import '../../widgets/popup_userList_action.dart'; import '../../widgets/popup_userList_action.dart';
class TemplatesList extends StatefulWidget { class TemplatesList extends StatefulWidget {
@ -432,12 +433,29 @@ class TemplatesListState extends State<TemplatesList> {
children: [ children: [
Row( Row(
children: [ children: [
Text( // Text(
'Templates', // 'Templates',
style: GoogleFonts.poppins( // style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14, // fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
color: Colors.black, // color: Colors.black,
// ),
// ),
Container(
child: BreadcrumbNavigation(
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Organization Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Templates',
),
],
), ),
), ),
], ],

View File

@ -42,6 +42,8 @@ class ForexDataState extends State<ForexData> {
Map<String, dynamic>? apiData; Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<dynamic> countryList = []; List<dynamic> countryList = [];
String? selectedCountry; String? selectedCountry;
@ -89,6 +91,15 @@ class ForexDataState extends State<ForexData> {
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
fetchCountries(); fetchCountries();
@ -113,8 +124,18 @@ class ForexDataState extends State<ForexData> {
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateForexDetails() { void updateForexDetails() {
print("Updateeee - ${widget.forexData}"); print("Updateeee - ${widget.forexData}");
@ -383,62 +404,103 @@ class ForexDataState extends State<ForexData> {
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: DropdownSearch<String>( width: double.infinity,
selectedItem: countryMap[selectedCountry], child: Focus(
popupProps: PopupProps.menu( focusNode: focusNodes["country_codeFocusNode"],
showSearchBox: true, // Enables search functionality onFocusChange: (hasFocus) {
menuProps: const MenuProps(backgroundColor: Colors.white), setState(() {
constraints: BoxConstraints(maxHeight: 250), focusStates["country_codeFocused"] = hasFocus;
itemBuilder: });
(context, item, isSelected) => Padding( },
padding: const EdgeInsets.symmetric( child: GestureDetector(
horizontal: 8.0, onTap: () {
vertical: 6.0, // Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
), ),
child: Text( items: countryMap.values.toList(),
item, dropdownDecoratorProps: DropDownDecoratorProps(
style: GoogleFonts.poppins(fontSize: 11.5), dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
),
), ),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
setCurrencyFromSelectedCountry(selectedCountry!);
});
},
), ),
searchFieldProps: TextFieldProps( )
decoration: InputDecoration( )
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 4),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
selectedCountryName = newValue;
setCurrencyFromSelectedCountry(selectedCountry!);
});
},
),
), ),
), ),
if (errorMessages["country_code"] != null) ...[ if (errorMessages["country_code"] != null) ...[
@ -464,7 +526,7 @@ class ForexDataState extends State<ForexData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["currencyFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
@ -474,6 +536,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["currencyFocusNode"],
controller: controllers["currency"], controller: controllers["currency"],
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 11, fontSize: 11,
@ -518,7 +581,7 @@ class ForexDataState extends State<ForexData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["cashFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
width: width:
@ -528,6 +591,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["cashFocusNode"],
controller: controllers["cash"], controller: controllers["cash"],
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
@ -570,7 +634,7 @@ class ForexDataState extends State<ForexData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["cardFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
width: width:
@ -580,6 +644,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["cardFocusNode"],
controller: controllers["card"], controller: controllers["card"],
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
@ -624,12 +689,13 @@ class ForexDataState extends State<ForexData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["perdiemAmountFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
focusNode: focusNodes["perdiemAmountFocusNode"],
controller: controllers["perdiemAmount"], controller: controllers["perdiemAmount"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(

View File

@ -57,6 +57,8 @@ class _PolicyState extends State<Policy> {
// ValueNotifier<String> selectedService = ValueNotifier("Train"); // ValueNotifier<String> selectedService = ValueNotifier("Train");
bool isViewMode = false; bool isViewMode = false;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
String? selectedPolicyId; String? selectedPolicyId;
@ -68,7 +70,7 @@ class _PolicyState extends State<Policy> {
String? userId; String? userId;
bool showClass = true; bool showClass = true;
bool showCost = true; bool showCost = false; // bool showCost = true; // on 21st june
List<dynamic>? selectedAllServices; List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = []; List<Map<String, String>> selectedOrgServiceIds = [];
@ -97,6 +99,7 @@ class _PolicyState extends State<Policy> {
}); });
}).toList(); }).toList();
Map<String, dynamic> data = { Map<String, dynamic> data = {
"name": _policyController.text, "name": _policyController.text,
"domestic": SelectedDomestic, "domestic": SelectedDomestic,
@ -115,6 +118,10 @@ class _PolicyState extends State<Policy> {
void initState() { void initState() {
super.initState(); super.initState();
_checkAuthAndLoadData(); _checkAuthAndLoadData();
// for (var field in dataHeader) {
// focusNodes["${field}FocusNode"] = FocusNode();
// focusStates["${field}Focused"] = false;
// }
// WidgetsFlutterBinding.ensureInitialized(); // WidgetsFlutterBinding.ensureInitialized();
// WidgetsBinding.instance.addPostFrameCallback((_) { // WidgetsBinding.instance.addPostFrameCallback((_) {
@ -1104,18 +1111,21 @@ class _PolicyState extends State<Policy> {
// policyCriteriaKey.currentState?.fieldForPolicy(); // policyCriteriaKey.currentState?.fieldForPolicy();
// policyCriteriaKey.currentState // policyCriteriaKey.currentState
// ?.addOrUpdatePolicy(selectedServiceIndex.value); // ?.addOrUpdatePolicy(selectedServiceIndex.value);
print("policyselectedService - $selectedService");
if (selectedService == "Flight" || if (selectedService == "Flight" ||
selectedService == "Train") { selectedService == "Train" || selectedService == "Accomodation") {
showClass = true;
showCost = true;
int serviceCode = selectedService == "Flight" ? 1 : 2;
policyCriteriaKey.currentState
?.fetchTrainFlightClass();
} else if (selectedService == "Accommodation") {
showClass = true; showClass = true;
showCost = false; showCost = false;
} else { int serviceCode = selectedService == "Flight" ? 1 : 2;
print("policyselectedService1 - $selectedService");
policyCriteriaKey.currentState
?.fetchTrainFlightClass();
}
// else if (selectedService == "Accommodation") {
// showClass = true;
// showCost = false;
// }
else {
showClass = false; showClass = false;
showCost = false; showCost = false;
} }
@ -1330,7 +1340,7 @@ class _PolicyState extends State<Policy> {
Text( Text(
"Domestic", "Domestic",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: _selectedTripType == "1" ? Colors.white : Colors.black, color: _selectedTripType == "1" ? Colors.black : Colors.black,
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null, fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
fontSize: 13, fontSize: 13,
), ),
@ -1356,13 +1366,13 @@ class _PolicyState extends State<Policy> {
borderRadius: BorderRadius.circular(4), // Rounded rectangle borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all( border: Border.all(
color: color:
_selectedTripType == "1" ? Colors.white : Colors.black, _selectedTripType == "1" ? Colors.green : Colors.black,
width: _selectedTripType == "1" ? 2 : 1, width: _selectedTripType == "1" ? 2 : 1,
), ),
), ),
child: child:
_selectedTripType == "1" _selectedTripType == "1"
? Icon(Icons.rectangle, size: 8, color: Colors.white) ? Icon(Icons.rectangle, size: 8, color: Colors.green)
: null, // Add checkmark if selected : null, // Add checkmark if selected
), ),
), ),
@ -1387,7 +1397,7 @@ class _PolicyState extends State<Policy> {
"International", "International",
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
color: _selectedTripType == "2" ? Colors.white : Colors.black, color: _selectedTripType == "2" ? Colors.black : Colors.black,
fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null, fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null,
), ),
), ),
@ -1412,13 +1422,13 @@ class _PolicyState extends State<Policy> {
borderRadius: BorderRadius.circular(4), // Rounded rectangle borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all( border: Border.all(
color: color:
_selectedTripType == "2" ? Colors.white : Colors.black, _selectedTripType == "2" ? Colors.green : Colors.black,
width: _selectedTripType == "2" ? 2 : 1, width: _selectedTripType == "2" ? 2 : 1,
), ),
), ),
child: child:
_selectedTripType == "2" _selectedTripType == "2"
? Icon(Icons.rectangle, size: 8, color: Colors.white) ? Icon(Icons.rectangle, size: 8, color: Colors.green)
: null, // Add checkmark if selected : null, // Add checkmark if selected
), ),
), ),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,11 +36,12 @@ class TravellerDataState extends State<TravellerData> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
Map<String, dynamic>? apiData; Map<String, dynamic>? apiData;
final Map<String, FocusNode> focusNodes = { // final Map<String, FocusNode> focusNodes = {
"name": FocusNode(), // "name": FocusNode(),
"description": FocusNode(), // "description": FocusNode(),
}; // };
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
final Map<String, TextEditingController> controllers = {}; final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {}; Map<String, String> errorMessages = {};
@ -71,6 +72,15 @@ class TravellerDataState extends State<TravellerData> {
apiData = null; apiData = null;
for (var field in dataHeader) { for (var field in dataHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
} }
if (widget.travellerId != null) { if (widget.travellerId != null) {
@ -90,8 +100,18 @@ class TravellerDataState extends State<TravellerData> {
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose(); super.dispose();
} }
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateTravellerDetails() { void updateTravellerDetails() {
print("Inside Update Function - ${widget.travellerData}"); print("Inside Update Function - ${widget.travellerData}");
@ -134,10 +154,10 @@ class TravellerDataState extends State<TravellerData> {
if (data[field] == null || data[field]!.trim().isEmpty) { if (data[field] == null || data[field]!.trim().isEmpty) {
errorMessages[field] = "Required"; errorMessages[field] = "Required";
if (!hasFocused) { // if (!hasFocused) {
focusNodes[field]?.requestFocus(); // focusNodes[field]?.requestFocus();
hasFocused = true; // hasFocused = true;
} // }
} }
} }
@ -286,14 +306,14 @@ class TravellerDataState extends State<TravellerData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["first_nameFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["first_name"], controller: controllers["first_name"],
focusNode: focusNodes["first_name"], focusNode: focusNodes["first_nameFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "First Name", labelText: "First Name",
@ -331,14 +351,14 @@ class TravellerDataState extends State<TravellerData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["last_nameFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["last_name"], controller: controllers["last_name"],
focusNode: focusNodes["last_name"], focusNode: focusNodes["last_nameFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Last Name", labelText: "Last Name",
@ -376,14 +396,14 @@ class TravellerDataState extends State<TravellerData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["emailFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["email"], controller: controllers["email"],
focusNode: focusNodes["email"], focusNode: focusNodes["emailFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Email", labelText: "Email",
@ -421,14 +441,14 @@ class TravellerDataState extends State<TravellerData> {
), ),
SizedBox(height: 5), SizedBox(height: 5),
CustomTextFieldForexWrapper( CustomTextFieldForexWrapper(
isFocused: false, isFocused: focusStates["mobileFocused"] ?? false,
isDesktop: widget.isDesktop, isDesktop: widget.isDesktop,
color: Colors.transparent, color: Colors.transparent,
child: SizedBox( child: SizedBox(
height: 40, height: 40,
child: TextField( child: TextField(
controller: controllers["mobile"], controller: controllers["mobile"],
focusNode: focusNodes["mobile"], focusNode: focusNodes["mobileFocusNode"],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Mobile", labelText: "Mobile",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1261,7 +1261,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
} }
Widget buildNationality() { Widget buildNationality() {
List<String> nationalityOptions = ["Indian", "International"]; List<String> nationalityOptions = ["Indian", "International","other nationality"];
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

View File

@ -27,7 +27,7 @@ class UserListScreen extends StatefulWidget {
class _UserListScreenState extends State<UserListScreen> { class _UserListScreenState extends State<UserListScreen> {
final ApiService apiService = ApiService(); final ApiService apiService = ApiService();
late Future<List<dynamic>> futureUsers; late Future<List<dynamic>> futureUsers;
late bool _dialogShown = false;
late Map<String, dynamic> userSingleData; late Map<String, dynamic> userSingleData;
List<dynamic>? apiCountryData; List<dynamic>? apiCountryData;
String? selectedUserId; String? selectedUserId;
@ -219,6 +219,54 @@ class _UserListScreenState extends State<UserListScreen> {
print("handDel - $userId"); print("handDel - $userId");
} }
void _showConfirmationDialog(String subtitle, List<String> successList, List<Map<String, String>> failedList) {
_dialogShown = true;
showDialog(
context: context,
builder: (context) => AlertDialog(
// title: Text("User Details"),
title: Text("User Details - $subtitle", style: TextStyle(fontWeight: FontWeight.bold)),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(subtitle, style: TextStyle(fontWeight: FontWeight.bold)),
//
// SizedBox(height: 12),
Text("Users Created Successfully :", style: TextStyle(fontWeight: FontWeight.bold)),
if (successList.isNotEmpty)
...successList.asMap().entries.map((entry) => Text("${entry.key + 1}) ${entry.value} created."))
else
Text("-- There is no data to create. --"),
SizedBox(height: 12),
Text("Failed to Create Users :", style: TextStyle(fontWeight: FontWeight.bold)),
if (failedList.isNotEmpty)
...failedList.asMap().entries.map((entry) {
final data = entry.value["data"] ?? "-";
final reason = entry.value["reason"] ?? "Unknown error";
return Text("${entry.key + 1}) $data - unable to create due to \"$reason\".");
})
else
Text("-- There is no failed data. --"),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
_dialogShown = false;
},
child: Text("Close"),
),
],
),
);
}
Future<void> handleUpload() async { Future<void> handleUpload() async {
final String apiUrldata = '$apiUrl/api/user/userUpload'; // api final String apiUrldata = '$apiUrl/api/user/userUpload'; // api
final String? token = await getToken(); // 2kn final String? token = await getToken(); // 2kn
@ -303,32 +351,51 @@ class _UserListScreenState extends State<UserListScreen> {
// Check the status code of the response // Check the status code of the response
if (response.statusCode == 200) { if (response.statusCode == 200) {
Map<String, dynamic> data = json.decode(responseString); Map<String, dynamic> responseData = json.decode(responseString);
if (data['status'] == 'success') {
print(data['message']); final message = responseData['message'] ?? '';
ScaffoldMessenger.of(context).showSnackBar( final status = responseData['status'] ?? '';
SnackBar( final data = responseData['data'] ?? {};
content: Text(data['message']),
backgroundColor: Colors.green.shade500, final List<String> successList = List<String>.from(data['successfulUsers'] ?? []);
behavior: SnackBarBehavior.floating, final List<Map<String, String>> failedList = List<Map<String, String>>.from(
), (data['failedUsers'] ?? []).map((item) => Map<String, String>.from(item)),
); );
refreshUserList();
} else { _showConfirmationDialog(message, successList, failedList);
print(data['message']);
ScaffoldMessenger.of(context).showSnackBar( // final successfulUsers = List.from(data['successfulUsers'] ?? []);
SnackBar( // final failedUsers = List.from(data['failedUsers'] ?? []);
content: Text(data['message']),
backgroundColor: Colors.redAccent, // Unified SnackBar logic
behavior: SnackBarBehavior.floating, // ScaffoldMessenger.of(context).showSnackBar(
), // SnackBar(
); // content: Text(
refreshUserList(); // combinedMessage,
} // style: TextStyle(color: Colors.white),
// ),
// backgroundColor: Colors.green,
// behavior: SnackBarBehavior.floating,
// // duration: Duration(seconds: 6),
// ),
// );
// Refresh list if needed
refreshUserList();
} else { } else {
print('Something went wrong'); // For non-200 responses
print('Failed to upload file: ${response.reasonPhrase}'); final errorMsg = 'Failed to upload file: ${response.reasonPhrase}';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMsg),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
print(errorMsg);
} }
} }
} }
} }

View File

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

View File

@ -11,6 +11,7 @@ import 'package:frontend/Screens/plans/create_plans.dart';
import 'package:frontend/Screens/plans/list_plans.dart'; import 'package:frontend/Screens/plans/list_plans.dart';
import 'package:frontend/Screens/policy/policy.dart'; import 'package:frontend/Screens/policy/policy.dart';
import 'package:frontend/Screens/policy/policy_list.dart'; import 'package:frontend/Screens/policy/policy_list.dart';
import 'package:frontend/Screens/userManagement/create_traveller_agent/createTravelAgent.dart';
import 'package:frontend/Screens/userManagement/create_user/create_user1.dart'; import 'package:frontend/Screens/userManagement/create_user/create_user1.dart';
import 'package:frontend/Screens/userManagement/user_List.dart'; import 'package:frontend/Screens/userManagement/user_List.dart';
import 'package:frontend/routes/organizationSetting.dart'; import 'package:frontend/routes/organizationSetting.dart';
@ -39,6 +40,7 @@ import '../Screens/reports/misAir.dart';
import '../Screens/reports/misHotel.dart'; import '../Screens/reports/misHotel.dart';
import '../Screens/reports/misForex.dart'; import '../Screens/reports/misForex.dart';
import '../Screens/reports/guestHouse.dart'; import '../Screens/reports/guestHouse.dart';
import '../Screens/userManagement/create_traveller_agent/listTravelAgent.dart';
import 'mainLayout.dart'; import 'mainLayout.dart';
@ -176,6 +178,14 @@ final GoRouter router = GoRouter(
path: '/guestHouseReport', path: '/guestHouseReport',
builder: (context, state) => GuestHouse(), builder: (context, state) => GuestHouse(),
), ),
GoRoute(
path: '/listTravelAgent',
builder: (context, state) => TravelAgentListScreen(),
),
GoRoute(
path: '/CreateTravelAgent',
builder: (context, state) => CreateTravelAgentFormDetials(),
),
], ],
), ),
], ],

View File

@ -146,6 +146,12 @@ class OrganizationSettingState extends State<OrganizationSetting> {
'label': 'Traveller (Non Employee)', 'label': 'Traveller (Non Employee)',
'description': 'Create and Edit Traveller', 'description': 'Create and Edit Traveller',
}, },
{
'value': '/listTravelAgent',
'icon': Icons.person_pin_outlined,
'label': 'Travel Agents',
'description': 'Create and Edit Travel Agent',
},
]; ];
// List<Widget> rows = []; // List<Widget> rows = [];

View File

@ -643,25 +643,6 @@ class ApiService {
} }
} }
static Future<void> viewPlanTravelAgent(
BuildContext context,
String planId, {
bool isViewMode = false,
bool isMyTrips = false,
}) async {
try {
Map<String, dynamic> planData = await getViewPlanEdit(planId);
print("ViewAAA - $planData");
context.go(
'/travelagent/trips',
extra: {'planData': planData, 'isViewMode': isViewMode},
);
} catch (e) {
print("Error fetching plan: $e");
}
}
Future<Map<String, dynamic>> fetchUserApprovalList() async { Future<Map<String, dynamic>> fetchUserApprovalList() async {
String? orgId = await getOrgId(); String? orgId = await getOrgId();
String? userId = await getUserId(); String? userId = await getUserId();
@ -1301,6 +1282,25 @@ class ApiService {
false; // Default to false if dismissed false; // Default to false if dismissed
} }
static Future<void> viewPlanTravelAgent(
BuildContext context,
String planId, {
bool isViewMode = false,
bool isMyTrips = false,
}) async {
try {
Map<String, dynamic> planData = await getViewPlanEdit(planId);
print("ViewAAA - $planData");
context.go(
'/travelagent/trips',
extra: {'planData': planData, 'isViewMode': isViewMode},
);
} catch (e) {
print("Error fetching plan: $e");
}
}
Future<Map<String, dynamic>> getCostCenterDetailsFind(int id) async { Future<Map<String, dynamic>> getCostCenterDetailsFind(int id) async {
final String apiUrldata = '$apiUrl/api/findCostCenter?cost_center_id=$id'; final String apiUrldata = '$apiUrl/api/findCostCenter?cost_center_id=$id';

View File

@ -63,25 +63,25 @@ class _CustomTextFieldForexWrapperState
decoration: BoxDecoration( decoration: BoxDecoration(
// color: widget.color, // color: widget.color,
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6), color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6), // color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
width: widget.isFocused ? 1.0 : 0.5, width: widget.isFocused ? 1.0 : 0.5,
), ),
boxShadow: // boxShadow:
widget.isFocused // widget.isFocused
? [ // ? [
BoxShadow( // BoxShadow(
color: Colors.white, // color: Colors.white,
// color: Color.fromRGBO(120, 180, 252, 0.3), // // color: Color.fromRGBO(120, 180, 252, 0.3),
// color: Color.fromRGBO(120, 180, 252, 0.3), // // color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 5, // blurRadius: 5,
spreadRadius: 2, // spreadRadius: 2,
offset: Offset(0, 1), // offset: Offset(0, 1),
), // ),
] // ]
: [], // : [],
), ),
// decoration: BoxDecoration( // decoration: BoxDecoration(
// color: widget.color, // color: widget.color,

View File

@ -0,0 +1,88 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../services/apiService.dart';
import 'custom_popup.dart';
class UserActionsMenu extends StatelessWidget {
final ApiService apiService = ApiService();
final Map<String, dynamic> user;
final Future<Map<String, dynamic>> Function(int userId) getUserDetails;
UserActionsMenu({
super.key,
required this.user,
required this.getUserDetails,
});
int getUserId(dynamic value) {
if (value is String) return int.parse(value);
return value;
}
@override
Widget build(BuildContext context) {
return PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder: (context) => [
CustomPopupMenuEntry(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
child: Tooltip(
message: 'View Details',
child: Icon(Icons.remove_red_eye,
color: Color(0xFF475569), size: 18)
),
// child: Icon(Icons.remove_red_eye,
// color: Color(0xFF475569), size: 18,),
onTap: () async {
Navigator.pop(context); // Close the menu
final userId = getUserId(user['user_id']);
final usersData = await getUserDetails(userId);
context.go("/CreateTravelAgent", extra: {
"selectedUser": usersData,
"isViewMode": true,
});
},
),
SizedBox(width: 8),
GestureDetector(
child: Tooltip(
message: 'Edit Details',
child: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
height: 15,
),
),
onTap: () async {
Navigator.pop(context); // Close the menu
final userId = getUserId(user['user_id']);
final usersData = await getUserDetails(userId);
context.go("/CreateTravelAgent", extra: {
"selectedUser": usersData,
"isViewMode": false,
});
},
),
],
),
),
),
],
);
}
}