merge with out code

This commit is contained in:
venba-Inspriron-3558 2025-06-21 13:44:21 +05:30
parent 6169e8b1a2
commit f029c8b9b6
26 changed files with 7896 additions and 846 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,14 +36,15 @@ 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;
@ -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}");
@ -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;
// }
}
}
@ -261,14 +281,14 @@ class DepartmentDataState extends State<DepartmentData> {
),
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",
@ -303,14 +323,14 @@ class DepartmentDataState extends State<DepartmentData> {
),
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

@ -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

@ -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

@ -29,6 +29,8 @@ class _MailSettingState extends State<MailSetting> {
Map<String, String> errorMessages = {};
final Map<String, TextEditingController> controllers = {};
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
bool _obscurePassword = true;
List<String> dataHeader = [
@ -76,11 +78,18 @@ class _MailSettingState extends State<MailSetting> {
void initState() {
super.initState();
// loadAllServices();
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;
});
});
}
_initControllers();
updateData();
@ -89,6 +98,14 @@ class _MailSettingState extends State<MailSetting> {
print(widget.initialMailData['sender_email']);
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void updateData() {
print("Updata 2");
@ -232,7 +249,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["senderEmailFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -241,6 +258,7 @@ class _MailSettingState extends State<MailSetting> {
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
focusNode: focusNodes["senderEmailFocusNode"],
controller: controllers["senderEmail"],
onChanged: (value) {
_clearError("sender_email");
@ -348,7 +366,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["userNameFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -357,6 +375,7 @@ class _MailSettingState extends State<MailSetting> {
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
focusNode: focusNodes["userNameFocusNode"],
controller: controllers["userName"],
onChanged: (value) {
_clearError("mail_user_name");
@ -394,7 +413,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["passwordFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -402,7 +421,7 @@ class _MailSettingState extends State<MailSetting> {
child: SizedBox(
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
focusNode: focusNodes["passwordFocusNode"],
controller: controllers["password"],
onChanged: (value) {
_clearError("mail_password");
@ -459,7 +478,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["hostFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -467,7 +486,7 @@ class _MailSettingState extends State<MailSetting> {
child: SizedBox(
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
focusNode: focusNodes["hostFocusNode"],
controller: controllers["host"],
onChanged: (value) {
_clearError("mail_host");
@ -505,7 +524,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["portFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -513,6 +532,7 @@ class _MailSettingState extends State<MailSetting> {
child: SizedBox(
height: 40,
child: TextField(
focusNode: focusNodes["portFocusNode"],
// focusNode: _destinationFocusNode,
controller: controllers["port"],
onChanged: (value) {
@ -556,7 +576,7 @@ class _MailSettingState extends State<MailSetting> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["toEmailFocused"] ?? false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
@ -565,6 +585,7 @@ class _MailSettingState extends State<MailSetting> {
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
focusNode: focusNodes["toEmailFocusNode"],
controller: controllers["toEmail"],
onChanged: (value) {
_clearError("to_mail");

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;
@ -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") {
selectedService == "Train" || selectedService == "Accomodation") {
showClass = true;
showCost = true;
int serviceCode = selectedService == "Flight" ? 1 : 2;
print("policyselectedService1 - $selectedService");
policyCriteriaKey.currentState
?.fetchTrainFlightClass();
} else if (selectedService == "Accommodation") {
showClass = true;
showCost = false;
} else {
}
// else if (selectedService == "Accommodation") {
// showClass = true;
// showCost = false;
// }
else {
showClass = false;
showCost = false;
}

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,18 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
if (!focusNodes.containsKey("to_date")) { focusNodes["to_date"] = FocusNode(); }
if (!focusStates.containsKey("to_date")) { focusStates["to_date"] = false; }
if (!focusStates.containsKey("from_date")) { focusStates["from_date"] = false; }
// Listen to focus changes
focusNodes["from_date"]!.addListener(() {
setState(() {
focusStates["from_date"] = focusNodes["from_date"]!.hasFocus;
});
});
// Listen to focus changes
focusNodes["to_date"]!.addListener(() {
setState(() {
focusStates["to_date"] = focusNodes["to_date"]!.hasFocus;
});
});
_tabController = TabController(length: 2, vsync: this);
_tabController.addListener(() {
@ -89,7 +100,8 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
focusNodes.forEach((key, focusNode) {
_addFocusListener(focusNode, (focus) {
setState(() {
focusStates[key] = focus;
// focusStates[key] = focus;
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
});
@ -98,6 +110,17 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
// fetchAdvancePurchaseReport();
}
void dispose() {
focusNodes["from_date"]?.dispose();
focusNodes["to_date"]?.dispose();
for (var controller in textControllers.values) {
controller.dispose();
}
super.dispose();
}
String getTabName(int index) {
switch (index) {
case 0: return "domestic";
@ -322,11 +345,13 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
}
void handleDomesticFilter(String query) {
@ -502,17 +527,35 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
? Center(child: CircularProgressIndicator())
: domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -698,14 +741,35 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
)
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
children: [
Expanded(
@ -930,18 +994,36 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
// International Tab
internationalData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1148,15 +1230,36 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
)
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
:Column(
children: [
Expanded(
@ -1573,6 +1676,7 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
height: 40,
child: GestureDetector(
onTap: () async {
focusNodes["to_date"]?.requestFocus();
final fromDateText =
textControllers["from_date"]?.text;
DateTime? mintoDate;

View File

@ -243,6 +243,7 @@ class _GuestHouseState extends State<GuestHouse>
final DateTime fromDate = dateFormat.parse(fromDateText);
final DateTime toDate = dateFormat.parse(toDateText);
// Check if from date is after to date
if (fromDate.isAfter(toDate)) {
errorMessages["to_date"] = "Invalid Date Range";
@ -334,8 +335,8 @@ class _GuestHouseState extends State<GuestHouse>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
@ -347,6 +348,20 @@ class _GuestHouseState extends State<GuestHouse>
setState(() {
filteredDomestic =
domesticData.where((object) {
final travelDateRaw = object['flight_travel_date'];
String formattedDate = '';
if (travelDateRaw != null && travelDateRaw is String) {
try {
final parsedDate = DateTime.parse(travelDateRaw);
formattedDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
formattedDate = travelDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -355,7 +370,7 @@ class _GuestHouseState extends State<GuestHouse>
(object['flight_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
@ -368,6 +383,19 @@ class _GuestHouseState extends State<GuestHouse>
setState(() {
filteredInternational =
internationalData.where((object) {
final travelDateRaw = object['flight_travel_date'];
String formattedDate = '';
if (travelDateRaw != null && travelDateRaw is String) {
try {
final parsedDate = DateTime.parse(travelDateRaw);
formattedDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
formattedDate = travelDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -376,7 +404,7 @@ class _GuestHouseState extends State<GuestHouse>
(object['flight_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
@ -521,18 +549,36 @@ class _GuestHouseState extends State<GuestHouse>
loaderFlag
? Center(child: CircularProgressIndicator())
: domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -716,12 +762,33 @@ class _GuestHouseState extends State<GuestHouse>
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -1001,18 +1068,36 @@ class _GuestHouseState extends State<GuestHouse>
// International Tab
internationalData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1219,12 +1304,33 @@ class _GuestHouseState extends State<GuestHouse>
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)

View File

@ -333,8 +333,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
@ -345,6 +345,20 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
setState(() {
filteredDomestic =
domesticData.where((object) {
final travelDateRaw = object['flight_travel_date'];
String formattedDate = '';
if (travelDateRaw != null && travelDateRaw is String) {
try {
final parsedDate = DateTime.parse(travelDateRaw);
formattedDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
formattedDate = travelDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -353,7 +367,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
(object['flight_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
@ -366,6 +380,20 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
setState(() {
filteredInternational =
internationalData.where((object) {
final travelDateRaw = object['flight_travel_date'];
String formattedDate = '';
if (travelDateRaw != null && travelDateRaw is String) {
try {
final parsedDate = DateTime.parse(travelDateRaw);
formattedDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
formattedDate = travelDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -374,7 +402,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
(object['flight_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
@ -519,18 +547,36 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
loaderFlag
? Center(child: CircularProgressIndicator())
: domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -714,12 +760,33 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)

View File

@ -301,8 +301,8 @@ class _MISforexState extends State<MISforex>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
@ -313,6 +313,34 @@ class _MISforexState extends State<MISforex>
setState(() {
filteredInternational =
internationalData.where((object) {
final fromDateRaw = object['forex_from_date'];
String newFromDate = '';
if (fromDateRaw != null && fromDateRaw is String) {
try {
final parsedDate = DateTime.parse(fromDateRaw);
newFromDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newFromDate = fromDateRaw;
}
}
final toDateRaw = object['flight_travel_date'];
String newToDate = '';
if (toDateRaw != null && toDateRaw is String) {
try {
final parsedDate = DateTime.parse(toDateRaw);
newToDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newToDate = toDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['passenger_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -320,8 +348,8 @@ class _MISforexState extends State<MISforex>
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['visiting_country']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['amount']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['forex_from_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['forex_to_date']?.toLowerCase().contains(lowerQuery));
(newFromDate.toLowerCase().contains(lowerQuery)) ||
(newToDate.toLowerCase().contains(lowerQuery));
}).toList();
currentPage = 0;
});
@ -448,18 +476,36 @@ class _MISforexState extends State<MISforex>
height: MediaQuery.of(context).size.height * 0.6,
child:
internationalData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -549,12 +595,33 @@ class _MISforexState extends State<MISforex>
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)

View File

@ -321,8 +321,8 @@ class _MIShotelState extends State<MIShotel>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
@ -334,6 +334,33 @@ class _MIShotelState extends State<MIShotel>
setState(() {
filteredDomestic =
domesticData.where((object) {
final fromDateRaw = object['check_in_date'];
String newFromDate = '';
if (fromDateRaw != null && fromDateRaw is String) {
try {
final parsedDate = DateTime.parse(fromDateRaw);
newFromDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newFromDate = fromDateRaw;
}
}
final toDateRaw = object['check_out_date'];
String newToDate = '';
if (toDateRaw != null && toDateRaw is String) {
try {
final parsedDate = DateTime.parse(toDateRaw);
newToDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newToDate = toDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -341,8 +368,8 @@ class _MIShotelState extends State<MIShotel>
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['hotel_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['check_in_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['check_out_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
(newToDate.toLowerCase().contains(lowerQuery)) ||
(object['hotel_city']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
@ -355,6 +382,32 @@ class _MIShotelState extends State<MIShotel>
setState(() {
filteredInternational =
internationalData.where((object) {
final fromDateRaw = object['check_in_date'];
String newFromDate = '';
if (fromDateRaw != null && fromDateRaw is String) {
try {
final parsedDate = DateTime.parse(fromDateRaw);
newFromDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newFromDate = fromDateRaw;
}
}
final toDateRaw = object['check_out_date'];
String newToDate = '';
if (toDateRaw != null && toDateRaw is String) {
try {
final parsedDate = DateTime.parse(toDateRaw);
newToDate =
'${parsedDate.day.toString().padLeft(2, '0')}-${parsedDate.month.toString().padLeft(2, '0')}-${parsedDate.year}';
} catch (e) {
// fallback if date is not parsable
newToDate = toDateRaw;
}
}
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
@ -362,8 +415,8 @@ class _MIShotelState extends State<MIShotel>
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['hotel_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['check_in_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['check_out_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
(newToDate.toLowerCase().contains(lowerQuery)) ||
(object['hotel_city']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
@ -511,12 +564,33 @@ class _MIShotelState extends State<MIShotel>
loaderFlag
? Center(child: CircularProgressIndicator())
: domesticData.isEmpty
? Center(
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -696,12 +770,33 @@ class _MIShotelState extends State<MIShotel>
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -792,12 +887,33 @@ class _MIShotelState extends State<MIShotel>
// International Tab
internationalData.isEmpty
? Center(
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -997,12 +1113,33 @@ class _MIShotelState extends State<MIShotel>
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)

View File

@ -333,8 +333,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
backgroundColor: result['status'] ? Colors.green : Colors.red,
content: Text(result['message']),
behavior: SnackBarBehavior.floating,
),
);
@ -346,7 +346,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
filteredDomestic =
domesticData.where((object) {
return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
@ -360,7 +360,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
filteredInternational =
internationalData.where((object) {
return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
@ -429,7 +429,6 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ------------------- First Row -------------------
// ------------------- First Row -------------------
Container(
color: Colors.white,
@ -505,18 +504,36 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
loaderFlag
? Center(child: CircularProgressIndicator())
: domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -700,12 +717,33 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -790,7 +828,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
DataCell(
Text(
'${entry['flight_service_count']}',
'${entry['service_count']}',
style:
GoogleFonts.poppins(
fontSize:
@ -845,18 +883,36 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
// International Tab
internationalData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
),
)
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1063,12 +1119,33 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: MediaQuery.of(context).size.height / 4),
Text(
"No Records Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
const SizedBox(height: 10),
Text(
"Please ensure the selected dates contain report data.",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black54,
),
),
],
),
),
)
@ -1153,7 +1230,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
DataCell(
Text(
'${entry['flight_service_count']}',
'${entry['service_count']}',
style:
GoogleFonts.poppins(
fontSize:

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,30 @@ class _UserListScreenState extends State<UserListScreen> {
print("handDel - $userId");
}
void _showConfirmationDialog(message,reason) {
// if (!mounted) return;
_dialogShown = true;
showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text(message),
content: Text(reason),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context); // Close dialog
_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 +327,68 @@ 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 successfulUsers = List.from(data['successfulUsers'] ?? []);
final failedUsers = List.from(data['failedUsers'] ?? []);
print(successfulUsers);
print(failedUsers);
// Helper function to format user lists
String buildUserList(String title, List users) {
if (users.isEmpty) return '';
final formattedUsers = users.map((user) {
if (user == null || user.toString().trim().isEmpty) {
return '• Email not provided';
}
return '$user';
}).join('\n');
return '\n$title:\n$formattedUsers';
}
final successList = buildUserList("✅ Successfully Processed", successfulUsers);
final failedList = buildUserList("❌ Failed to Process", failedUsers);
final combinedMessage = [message, successList, failedList]
.where((part) => part.isNotEmpty)
.join('\n\n');
_showConfirmationDialog(message,combinedMessage);
// 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

@ -12,6 +12,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';
@ -178,6 +180,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

@ -1539,53 +1539,104 @@ class ApiService {
}
}
Future<void> reportExcelDownload(String apiRoute,String body,String name) async {
// Future<void> reportExcelDownload(String apiRoute,String body,String name) async {
// final String apiUrldata = '$apiUrl/api/$apiRoute';
//
// // Sanitize name for filename (optional)
// final safeName = name.replaceAll(RegExp(r'[^\w\s-]'), ''); // remove any special chars if needed
// final excelName = '$safeName.xlsx';
//
//
// final token = await getToken();
//
// if (token == null) {
// throw Exception('Token not found. Please log in.');
// }
//
// final response = await http.post(
// Uri.parse(apiUrldata),
// headers: {'Authorization': 'Bearer $token','Content-Type': 'application/json'},
// body: body
// );
//
// if (response.statusCode == 200) {
// try {
// print("report XL Dowloaded");
// print(excelName);
// // Create a blob from the response body
// final blob = html.Blob([response.bodyBytes]);
//
// // Generate a download URL for the blob
// final url = html.Url.createObjectUrlFromBlob(blob);
//
// // Create a link element to trigger the download
// final anchor =
// html.AnchorElement(href: url)
// ..setAttribute('download', excelName)
// ..click();
//
// // Revoke the download URL to free up resources
// html.Url.revokeObjectUrl(url);
// throw Exception('Sucessfully Downloaded');
// } catch (e) {
// throw Exception('Error parsing response: $e');
// }
// } else if (response.statusCode == 404) {
// throw Exception('File not found.');
// } else {
// throw Exception('Failed to Download');
// }
// }
Future<Map<String, dynamic>> reportExcelDownload(String apiRoute, String body, String name) async {
final String apiUrldata = '$apiUrl/api/$apiRoute';
// Sanitize name for filename (optional)
final safeName = name.replaceAll(RegExp(r'[^\w\s-]'), ''); // remove any special chars if needed
final safeName = name.replaceAll(RegExp(r'[^\w\s-]'), '');
final excelName = '$safeName.xlsx';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
return {'status': false, 'message': 'Token not found. Please log in.'};
}
final response = await http.post(
Uri.parse(apiUrldata),
headers: {'Authorization': 'Bearer $token','Content-Type': 'application/json'},
body: body
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json'
},
body: body,
);
if (response.statusCode == 200) {
try {
print("report XL Dowloaded");
print(excelName);
// Create a blob from the response body
final blob = html.Blob([response.bodyBytes]);
// Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download
final anchor =
html.AnchorElement(href: url)
final anchor = html.AnchorElement(href: url)
..setAttribute('download', excelName)
..click();
// Revoke the download URL to free up resources
html.Url.revokeObjectUrl(url);
throw Exception('Sucessfully Downloaded');
return {'status': true, 'message': 'Successfully downloaded Excel file.'};
} catch (e) {
throw Exception('Error parsing response: $e');
return {'status': false, 'message': 'Error during download: $e'};
}
} else if (response.statusCode == 404) {
throw Exception('File not found.');
return {'status': false, 'message': 'File not found.'};
} else {
throw Exception('Failed to Download');
try {
final decoded = jsonDecode(response.body);
return {
'status': false,
'message': decoded['message'] ?? 'Download failed.'
};
} catch (_) {
return {'status': false, 'message': 'Download failed.'};
}
}
}
// ----------------------- Report -------------------------
}

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,
});
},
),
],
),
),
),
],
);
}
}