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

View File

@ -36,26 +36,27 @@ class DepartmentDataState extends State<DepartmentData> {
final ApiService apiService = ApiService();
Map<String, dynamic>? apiData;
final Map<String, FocusNode> focusNodes = {
"name": FocusNode(),
"description": FocusNode(),
};
// final Map<String, FocusNode> focusNodes = {
// "name": FocusNode(),
// "description": FocusNode(),
// };
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
String? selectedName;
String? selectedDescription;
String? userId;
int? departmentDataId;
late String isActive = "1";
List<String> dataHeader = ["name", "description"];
List<String> dataHeader = ["dropdown_value", "description"];
Map<String, dynamic> departmentDetails() {
final data = {
// "department_id": int.parse(departmentId),
"name": controllers["name"]?.text,
"dropdown_value": controllers["dropdown_value"]?.text,
"description": controllers["description"]?.text,
"created_by": userId,
"is_active": isActive,
@ -70,6 +71,15 @@ class DepartmentDataState extends State<DepartmentData> {
apiData = null;
for (var field in dataHeader) {
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) {
@ -89,8 +99,18 @@ class DepartmentDataState extends State<DepartmentData> {
for (var controller in controllers.values) {
controller.dispose();
}
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateDepartmentDetails() {
print("Inside Update Function - ${widget.departmentData}");
@ -99,10 +119,10 @@ class DepartmentDataState extends State<DepartmentData> {
if (data == null) return;
setState(() {
controllers['name']?.text = data['name'] ?? '';
controllers['description']?.text = data['description'].toString();
controllers['dropdown_value']?.text = data['dropdown_value'] ?? '';
// controllers['description']?.text = data['description'].toString();
isActive = data["is_active"];
final departmentId = int.tryParse(data['department_id'].toString());
final departmentId = int.tryParse(data['id'].toString());
departmentDataId = departmentId;
});
}
@ -117,11 +137,11 @@ class DepartmentDataState extends State<DepartmentData> {
errorMessages.clear();
final data = {
"name": controllers["name"]?.text,
"description": controllers["description"]?.text,
"dropdown_value": controllers["dropdown_value"]?.text,
// "description": controllers["description"]?.text,
};
final requiredFields = ["name", "description"];
final requiredFields = ["dropdown_value"];
bool hasFocused = false;
// Check validation for each field
@ -129,10 +149,10 @@ class DepartmentDataState extends State<DepartmentData> {
if (data[field] == null || data[field]!.trim().isEmpty) {
errorMessages[field] = "Required";
if (!hasFocused) {
focusNodes[field]?.requestFocus();
hasFocused = true;
}
// if (!hasFocused) {
// focusNodes[field]?.requestFocus();
// hasFocused = true;
// }
}
}
@ -165,7 +185,7 @@ class DepartmentDataState extends State<DepartmentData> {
if (departmentDataId != null) {
print("for edit department id - $departmentDataId");
apiUrldata = '$apiUrl/api/updateDepartment/$departmentDataId';
departmentData["department_id"] = departmentDataId.toString();
departmentData["id"] = departmentDataId.toString();
departmentData["updated_by"] = userId;
(departmentData.containsKey("created_by"))
? departmentData.remove("created_by")
@ -261,14 +281,14 @@ class DepartmentDataState extends State<DepartmentData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["dropdown_valueFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["name"],
focusNode: focusNodes["name"],
controller: controllers["dropdown_value"],
focusNode: focusNodes["dropdown_valueFocusNode"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
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
Text(
errorMessages["name"]!,
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"]!,
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: 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)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

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

View File

@ -42,6 +42,8 @@ class HotelsDataState extends State<HotelsData> {
Map<String, dynamic>? apiData;
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<dynamic> countryList = [];
String? selectedCountry;
@ -83,6 +85,15 @@ class HotelsDataState extends State<HotelsData> {
apiData = null;
for (var field in dataHeader) {
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();
if (widget.hotelsId != null) {
@ -103,8 +114,18 @@ class HotelsDataState extends State<HotelsData> {
for (var controller in controllers.values) {
controller.dispose();
}
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateHotelsDetails() {
print("Update - ${widget.hotelsData}");
@ -312,12 +333,14 @@ class HotelsDataState extends State<HotelsData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
// isFocused: false,
isFocused: focusStates["hotel_nameFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["hotel_nameFocusNode"],
controller: controllers["hotel_name"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
@ -353,12 +376,13 @@ class HotelsDataState extends State<HotelsData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["hotel_chainFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["hotel_chainFocusNode"],
controller: controllers["hotel_chain"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
@ -394,12 +418,14 @@ class HotelsDataState extends State<HotelsData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
// isFocused: false,
isFocused: focusStates["categoryFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["categoryFocusNode"],
controller: controllers["category"],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 ]')),
@ -432,61 +458,102 @@ class HotelsDataState extends State<HotelsData> {
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
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: 8.0,
vertical: 6.0,
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// 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(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
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) ...[
@ -512,12 +579,13 @@ class HotelsDataState extends State<HotelsData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["cityFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["cityFocusNode"],
controller: controllers["city"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(

View File

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

View File

@ -14,6 +14,7 @@ import '../../routes/custom_drawer.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../utils/pagination.dart';
import '../../widgets/custom_breadcrumb_navigation.dart';
import '../../widgets/popup_userList_action.dart';
class TemplatesList extends StatefulWidget {
@ -432,12 +433,29 @@ class TemplatesListState extends State<TemplatesList> {
children: [
Row(
children: [
Text(
'Templates',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
// Text(
// 'Templates',
// style: GoogleFonts.poppins(
// fontSize: isDesktop ? 16 : 14,
// fontWeight: FontWeight.w600,
// 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;
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<dynamic> countryList = [];
String? selectedCountry;
@ -89,6 +91,15 @@ class ForexDataState extends State<ForexData> {
apiData = null;
for (var field in dataHeader) {
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();
@ -113,8 +124,18 @@ class ForexDataState extends State<ForexData> {
for (var controller in controllers.values) {
controller.dispose();
}
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateForexDetails() {
print("Updateeee - ${widget.forexData}");
@ -383,62 +404,103 @@ class ForexDataState extends State<ForexData> {
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
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: 8.0,
vertical: 6.0,
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// 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(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
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) ...[
@ -464,7 +526,7 @@ class ForexDataState extends State<ForexData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["currencyFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
@ -474,6 +536,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["currencyFocusNode"],
controller: controllers["currency"],
style: GoogleFonts.poppins(
fontSize: 11,
@ -518,7 +581,7 @@ class ForexDataState extends State<ForexData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["cashFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
width:
@ -528,6 +591,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["cashFocusNode"],
controller: controllers["cash"],
keyboardType: TextInputType.number,
inputFormatters: [
@ -570,7 +634,7 @@ class ForexDataState extends State<ForexData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["cardFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
width:
@ -580,6 +644,7 @@ class ForexDataState extends State<ForexData> {
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["cardFocusNode"],
controller: controllers["card"],
keyboardType: TextInputType.number,
inputFormatters: [
@ -624,12 +689,13 @@ class ForexDataState extends State<ForexData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["perdiemAmountFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["perdiemAmountFocusNode"],
controller: controllers["perdiemAmount"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(

View File

@ -57,6 +57,8 @@ class _PolicyState extends State<Policy> {
// ValueNotifier<String> selectedService = ValueNotifier("Train");
bool isViewMode = false;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, String> errorMessages = {};
String? selectedPolicyId;
@ -68,7 +70,7 @@ class _PolicyState extends State<Policy> {
String? userId;
bool showClass = true;
bool showCost = true;
bool showCost = false; // bool showCost = true; // on 21st june
List<dynamic>? selectedAllServices;
List<Map<String, String>> selectedOrgServiceIds = [];
@ -97,6 +99,7 @@ class _PolicyState extends State<Policy> {
});
}).toList();
Map<String, dynamic> data = {
"name": _policyController.text,
"domestic": SelectedDomestic,
@ -115,6 +118,10 @@ class _PolicyState extends State<Policy> {
void initState() {
super.initState();
_checkAuthAndLoadData();
// for (var field in dataHeader) {
// focusNodes["${field}FocusNode"] = FocusNode();
// focusStates["${field}Focused"] = false;
// }
// WidgetsFlutterBinding.ensureInitialized();
// WidgetsBinding.instance.addPostFrameCallback((_) {
@ -1104,18 +1111,21 @@ class _PolicyState extends State<Policy> {
// policyCriteriaKey.currentState?.fieldForPolicy();
// policyCriteriaKey.currentState
// ?.addOrUpdatePolicy(selectedServiceIndex.value);
print("policyselectedService - $selectedService");
if (selectedService == "Flight" ||
selectedService == "Train") {
showClass = true;
showCost = true;
int serviceCode = selectedService == "Flight" ? 1 : 2;
policyCriteriaKey.currentState
?.fetchTrainFlightClass();
} else if (selectedService == "Accommodation") {
selectedService == "Train" || selectedService == "Accomodation") {
showClass = true;
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;
showCost = false;
}
@ -1330,7 +1340,7 @@ class _PolicyState extends State<Policy> {
Text(
"Domestic",
style: GoogleFonts.poppins(
color: _selectedTripType == "1" ? Colors.white : Colors.black,
color: _selectedTripType == "1" ? Colors.black : Colors.black,
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
fontSize: 13,
),
@ -1356,13 +1366,13 @@ class _PolicyState extends State<Policy> {
borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all(
color:
_selectedTripType == "1" ? Colors.white : Colors.black,
_selectedTripType == "1" ? Colors.green : Colors.black,
width: _selectedTripType == "1" ? 2 : 1,
),
),
child:
_selectedTripType == "1"
? Icon(Icons.rectangle, size: 8, color: Colors.white)
? Icon(Icons.rectangle, size: 8, color: Colors.green)
: null, // Add checkmark if selected
),
),
@ -1387,7 +1397,7 @@ class _PolicyState extends State<Policy> {
"International",
style: GoogleFonts.poppins(
fontSize: 13,
color: _selectedTripType == "2" ? Colors.white : Colors.black,
color: _selectedTripType == "2" ? Colors.black : Colors.black,
fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null,
),
),
@ -1412,13 +1422,13 @@ class _PolicyState extends State<Policy> {
borderRadius: BorderRadius.circular(4), // Rounded rectangle
border: Border.all(
color:
_selectedTripType == "2" ? Colors.white : Colors.black,
_selectedTripType == "2" ? Colors.green : Colors.black,
width: _selectedTripType == "2" ? 2 : 1,
),
),
child:
_selectedTripType == "2"
? Icon(Icons.rectangle, size: 8, color: Colors.white)
? Icon(Icons.rectangle, size: 8, color: Colors.green)
: 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();
Map<String, dynamic>? apiData;
final Map<String, FocusNode> focusNodes = {
"name": FocusNode(),
"description": FocusNode(),
};
// final Map<String, FocusNode> focusNodes = {
// "name": FocusNode(),
// "description": FocusNode(),
// };
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
@ -71,6 +72,15 @@ class TravellerDataState extends State<TravellerData> {
apiData = null;
for (var field in dataHeader) {
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) {
@ -90,8 +100,18 @@ class TravellerDataState extends State<TravellerData> {
for (var controller in controllers.values) {
controller.dispose();
}
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateTravellerDetails() {
print("Inside Update Function - ${widget.travellerData}");
@ -134,10 +154,10 @@ class TravellerDataState extends State<TravellerData> {
if (data[field] == null || data[field]!.trim().isEmpty) {
errorMessages[field] = "Required";
if (!hasFocused) {
focusNodes[field]?.requestFocus();
hasFocused = true;
}
// if (!hasFocused) {
// focusNodes[field]?.requestFocus();
// hasFocused = true;
// }
}
}
@ -286,14 +306,14 @@ class TravellerDataState extends State<TravellerData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["first_nameFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["first_name"],
focusNode: focusNodes["first_name"],
focusNode: focusNodes["first_nameFocusNode"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "First Name",
@ -331,14 +351,14 @@ class TravellerDataState extends State<TravellerData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["last_nameFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["last_name"],
focusNode: focusNodes["last_name"],
focusNode: focusNodes["last_nameFocusNode"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Last Name",
@ -376,14 +396,14 @@ class TravellerDataState extends State<TravellerData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["emailFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["email"],
focusNode: focusNodes["email"],
focusNode: focusNodes["emailFocusNode"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Email",
@ -421,14 +441,14 @@ class TravellerDataState extends State<TravellerData> {
),
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
isFocused: focusStates["mobileFocused"] ?? false,
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["mobile"],
focusNode: focusNodes["mobile"],
focusNode: focusNodes["mobileFocusNode"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
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() {
List<String> nationalityOptions = ["Indian", "International"];
List<String> nationalityOptions = ["Indian", "International","other nationality"];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

View File

@ -27,7 +27,7 @@ class UserListScreen extends StatefulWidget {
class _UserListScreenState extends State<UserListScreen> {
final ApiService apiService = ApiService();
late Future<List<dynamic>> futureUsers;
late bool _dialogShown = false;
late Map<String, dynamic> userSingleData;
List<dynamic>? apiCountryData;
String? selectedUserId;
@ -219,6 +219,54 @@ class _UserListScreenState extends State<UserListScreen> {
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 {
final String apiUrldata = '$apiUrl/api/user/userUpload'; // api
final String? token = await getToken(); // 2kn
@ -303,32 +351,51 @@ class _UserListScreenState extends State<UserListScreen> {
// Check the status code of the response
if (response.statusCode == 200) {
Map<String, dynamic> data = json.decode(responseString);
if (data['status'] == 'success') {
print(data['message']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(data['message']),
backgroundColor: Colors.green.shade500,
behavior: SnackBarBehavior.floating,
),
);
refreshUserList();
} else {
print(data['message']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(data['message']),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
refreshUserList();
}
Map<String, dynamic> responseData = json.decode(responseString);
final message = responseData['message'] ?? '';
final status = responseData['status'] ?? '';
final data = responseData['data'] ?? {};
final List<String> successList = List<String>.from(data['successfulUsers'] ?? []);
final List<Map<String, String>> failedList = List<Map<String, String>>.from(
(data['failedUsers'] ?? []).map((item) => Map<String, String>.from(item)),
);
_showConfirmationDialog(message, successList, failedList);
// final successfulUsers = List.from(data['successfulUsers'] ?? []);
// final failedUsers = List.from(data['failedUsers'] ?? []);
// Unified SnackBar logic
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// combinedMessage,
// style: TextStyle(color: Colors.white),
// ),
// backgroundColor: Colors.green,
// behavior: SnackBarBehavior.floating,
// // duration: Duration(seconds: 6),
// ),
// );
// Refresh list if needed
refreshUserList();
} else {
print('Something went wrong');
print('Failed to upload file: ${response.reasonPhrase}');
// For non-200 responses
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
const String apiUrl = 'http://apitest.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/policy/policy.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/user_List.dart';
import 'package:frontend/routes/organizationSetting.dart';
@ -39,6 +40,7 @@ import '../Screens/reports/misAir.dart';
import '../Screens/reports/misHotel.dart';
import '../Screens/reports/misForex.dart';
import '../Screens/reports/guestHouse.dart';
import '../Screens/userManagement/create_traveller_agent/listTravelAgent.dart';
import 'mainLayout.dart';
@ -176,6 +178,14 @@ final GoRouter router = GoRouter(
path: '/guestHouseReport',
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)',
'description': 'Create and Edit Traveller',
},
{
'value': '/listTravelAgent',
'icon': Icons.person_pin_outlined,
'label': 'Travel Agents',
'description': 'Create and Edit Travel Agent',
},
];
// 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 {
String? orgId = await getOrgId();
String? userId = await getUserId();
@ -1301,6 +1282,25 @@ class ApiService {
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 {
final String apiUrldata = '$apiUrl/api/findCostCenter?cost_center_id=$id';

View File

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