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,20 +458,34 @@ class HotelsDataState extends State<HotelsData> {
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
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),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
horizontal: 10.0,
vertical: 8.0,
),
child: Text(
item,
@ -463,8 +503,33 @@ class HotelsDataState extends State<HotelsData> {
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
// 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:
@ -487,6 +552,8 @@ class HotelsDataState extends State<HotelsData> {
});
},
),
)
)
),
),
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,9 +404,23 @@ class ForexDataState extends State<ForexData> {
SizedBox(height: 5),
CustomTextFieldForexWrapper(
isFocused: false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
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(
@ -395,8 +430,8 @@ class ForexDataState extends State<ForexData> {
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
horizontal: 10.0,
vertical: 8.0,
),
child: Text(
item,
@ -414,8 +449,33 @@ class ForexDataState extends State<ForexData> {
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
// 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:
@ -439,6 +499,8 @@ class ForexDataState extends State<ForexData> {
});
},
),
)
)
),
),
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

View File

@ -112,9 +112,12 @@ class _GuestHouseState extends State<GuestHouse>
String getTabName(int index) {
switch (index) {
case 0: return "domestic";
case 1: return "international";
default: return "domestic"; // return "Unknown";
case 0:
return "domestic";
case 1:
return "international";
default:
return "domestic"; // return "Unknown";
}
}
@ -332,35 +335,62 @@ class _GuestHouseState extends State<GuestHouse>
excelName,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
behavior: SnackBarBehavior.floating,
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// backgroundColor: result['status'] ? Colors.green : Colors.red,
// content: Text(result['message']),
// behavior: SnackBarBehavior.floating,
// ),
// );
}
void handleDomesticFilter(String query) {
final lowerQuery = query.toLowerCase();
setState(() {
filteredDomestic =
domesticData.where((object) {
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(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) ||
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) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['functional_department']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['flight_trip_type']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
});
print("handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}");
print(
"handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}",
);
}
void handleInternationalFilter(String query) {
@ -368,23 +398,49 @@ class _GuestHouseState extends State<GuestHouse>
setState(() {
filteredInternational =
internationalData.where((object) {
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(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) ||
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) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['functional_department']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['flight_trip_type']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
});
print("handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}");
print(
"handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}",
);
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
@ -456,7 +512,7 @@ class _GuestHouseState extends State<GuestHouse>
children: [
buildTitle(isDesktop),
Spacer(),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
: Column(
@ -464,9 +520,9 @@ class _GuestHouseState extends State<GuestHouse>
children: [
buildTitle(isDesktop),
SizedBox(height: 8),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
),
),
SizedBox(height: 5),
@ -523,13 +579,34 @@ class _GuestHouseState extends State<GuestHouse>
: domesticData.isEmpty
? 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,
),
),
],
),
),
)
@ -561,7 +638,9 @@ class _GuestHouseState extends State<GuestHouse>
),
),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchDomesticController,
@ -578,24 +657,32 @@ class _GuestHouseState extends State<GuestHouse>
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
),
// SizedBox(width: 16),
@ -703,8 +790,10 @@ class _GuestHouseState extends State<GuestHouse>
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newDomesticData =filteredDomestic.isNotEmpty ? filteredDomestic : domesticData;
List<Map<String, dynamic>> newDomesticData =
filteredDomestic.isNotEmpty
? filteredDomestic
: domesticData;
// Pagination logic
List<Map<String, dynamic>>
@ -714,14 +803,43 @@ class _GuestHouseState extends State<GuestHouse>
.take(itemsPerPage1)
.toList();
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
return ((searchDomesticController
.text
.isNotEmpty &&
filteredDomestic.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -729,17 +847,21 @@ class _GuestHouseState extends State<GuestHouse>
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -877,8 +999,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['plan_id']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -887,8 +1008,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['employee_code']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -897,8 +1017,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['so_number']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -907,8 +1026,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['functional_department']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -917,8 +1035,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['flight_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -927,8 +1044,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -937,8 +1053,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['sector']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -947,8 +1062,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['flight_class']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -957,8 +1071,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
flightTravelDate,
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -977,7 +1090,8 @@ class _GuestHouseState extends State<GuestHouse>
PaginationControls(
currentPage: currentPage1,
itemsPerPage: itemsPerPage1,
totalItems: newDomesticData.length,
totalItems:
newDomesticData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -1003,13 +1117,34 @@ class _GuestHouseState extends State<GuestHouse>
internationalData.isEmpty
? 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,
),
),
],
),
),
)
@ -1140,7 +1275,8 @@ class _GuestHouseState extends State<GuestHouse>
vertical: 8,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Title on the left
@ -1152,17 +1288,21 @@ class _GuestHouseState extends State<GuestHouse>
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow
overflow:
TextOverflow
.ellipsis, // Ensures title doesn't overflow
),
),
SizedBox(width: 16),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchInternationalController,
controller:
searchInternationalController,
onChanged: handleInternationalFilter,
decoration: InputDecoration(
hintText: "Search ...",
@ -1176,27 +1316,36 @@ class _GuestHouseState extends State<GuestHouse>
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
])
),
],
),
),
Container(
height:
@ -1209,7 +1358,11 @@ class _GuestHouseState extends State<GuestHouse>
builder: (context, constraints) {
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newInternationalData =filteredInternational.isNotEmpty ? filteredInternational : internationalData;
List<Map<String, dynamic>>
newInternationalData =
filteredInternational.isNotEmpty
? filteredInternational
: internationalData;
// Pagination logic
List<Map<String, dynamic>>
paginatedInternational =
@ -1218,13 +1371,43 @@ class _GuestHouseState extends State<GuestHouse>
.take(itemsPerPage2)
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
return ((searchInternationalController
.text
.isNotEmpty &&
filteredInternational.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -1232,17 +1415,21 @@ class _GuestHouseState extends State<GuestHouse>
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -1390,8 +1577,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['plan_id']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1400,8 +1586,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['employee_code']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1410,8 +1595,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['so_number']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1420,8 +1604,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['functional_department']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1430,8 +1613,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['flight_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1440,8 +1622,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1450,8 +1631,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['sector']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1460,8 +1640,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
'${entry['flight_class']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1470,8 +1649,7 @@ class _GuestHouseState extends State<GuestHouse>
DataCell(
Text(
flightTravelDate,
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1490,7 +1668,8 @@ class _GuestHouseState extends State<GuestHouse>
PaginationControls(
currentPage: currentPage2,
itemsPerPage: itemsPerPage2,
totalItems: newInternationalData.length,
totalItems:
newInternationalData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -1535,11 +1714,9 @@ class _GuestHouseState extends State<GuestHouse>
onTap: (context) {
// Navigator.pushNamed(context, '/report');
context.go("/report");
}
),
BreadcrumbItem(
title: 'Guest House',
},
),
BreadcrumbItem(title: 'Guest House'),
],
),
);
@ -1555,10 +1732,7 @@ class _GuestHouseState extends State<GuestHouse>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: handleDownload,
child: Row(
@ -1571,11 +1745,7 @@ class _GuestHouseState extends State<GuestHouse>
),
),
SizedBox(width: 8),
Icon(
Icons.file_present_outlined,
size: 15,
color: Colors.white,
),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
@ -1608,14 +1778,14 @@ class _GuestHouseState extends State<GuestHouse>
// ),
],
);
}
Widget buildFromDateField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "From Date *",
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1657,12 +1827,9 @@ class _GuestHouseState extends State<GuestHouse>
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
@ -1689,7 +1856,8 @@ class _GuestHouseState extends State<GuestHouse>
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "To Date *",
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1705,13 +1873,10 @@ class _GuestHouseState extends State<GuestHouse>
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText =
textControllers["from_date"]?.text;
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat(
'dd-MM-yyyy',
).parse(fromDateText);
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
@ -1735,16 +1900,10 @@ class _GuestHouseState extends State<GuestHouse>
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
@ -1763,8 +1922,7 @@ class _GuestHouseState extends State<GuestHouse>
style: const TextStyle(color: Colors.red, fontSize: 12),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow
.ellipsis, // Add ellipsis if it still overflows
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
@ -1772,11 +1930,8 @@ class _GuestHouseState extends State<GuestHouse>
}
Widget buildSearchButton(bool isDesktop) {
return
Padding(
padding: isDesktop
? const EdgeInsets.only(top: 22)
: EdgeInsets.zero,
return Padding(
padding: isDesktop ? const EdgeInsets.only(top: 22) : EdgeInsets.zero,
child: SizedBox(
width: isDesktop ? null : double.infinity,
child: ElevatedButton(
@ -1789,23 +1944,17 @@ class _GuestHouseState extends State<GuestHouse>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
handleSubmit();
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Search",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
style: GoogleFonts.poppins(fontSize: isDesktop ? 13 : 11),
),
SizedBox(width: 8), // spacing between icon and text
Icon(Icons.search, size: 15, color: Colors.white),

View File

@ -111,9 +111,12 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
String getTabName(int index) {
switch (index) {
case 0: return "domestic";
case 1: return "international";
default: return "domestic"; // return "Unknown";
case 0:
return "domestic";
case 1:
return "international";
default:
return "domestic"; // return "Unknown";
}
}
@ -331,13 +334,13 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
excelName,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
behavior: SnackBarBehavior.floating,
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// backgroundColor: result['status'] ? Colors.green : Colors.red,
// content: Text(result['message']),
// behavior: SnackBarBehavior.floating,
// ),
// );
}
void handleDomesticFilter(String query) {
@ -345,20 +348,48 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
setState(() {
filteredDomestic =
domesticData.where((object) {
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(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) ||
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) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['functional_department']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['flight_trip_type']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
});
print("handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}");
print(
"handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}",
);
}
void handleInternationalFilter(String query) {
@ -366,23 +397,50 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
setState(() {
filteredInternational =
internationalData.where((object) {
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['customer_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['functional_department']?.toLowerCase().contains(lowerQuery) ?? false) ||
(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) ||
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) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['functional_department']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['flight_trip_type']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
false) ||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
(object['sector']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
});
print("handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}");
print(
"handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}",
);
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
@ -454,7 +512,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
children: [
buildTitle(isDesktop),
Spacer(),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
: Column(
@ -462,9 +520,9 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
children: [
buildTitle(isDesktop),
SizedBox(height: 8),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
),
),
SizedBox(height: 5),
@ -521,13 +579,34 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
: domesticData.isEmpty
? 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,
),
),
],
),
),
)
@ -559,7 +638,9 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
),
),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchDomesticController,
@ -576,24 +657,32 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
),
// SizedBox(width: 16),
@ -701,8 +790,10 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newDomesticData =filteredDomestic.isNotEmpty ? filteredDomestic : domesticData;
List<Map<String, dynamic>> newDomesticData =
filteredDomestic.isNotEmpty
? filteredDomestic
: domesticData;
// Pagination logic
List<Map<String, dynamic>>
@ -712,14 +803,43 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
.take(itemsPerPage1)
.toList();
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
return ((searchDomesticController
.text
.isNotEmpty &&
filteredDomestic.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -727,17 +847,21 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -875,8 +999,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['plan_id']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -885,8 +1008,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['employee_code']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -895,8 +1017,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['so_number']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -905,8 +1026,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['functional_department']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -915,8 +1035,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['flight_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -925,8 +1044,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -935,8 +1053,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['sector']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -945,8 +1062,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['flight_class']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -955,8 +1071,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
TravelDate,
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -975,7 +1090,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
PaginationControls(
currentPage: currentPage1,
itemsPerPage: itemsPerPage1,
totalItems: newDomesticData.length,
totalItems:
newDomesticData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -1138,7 +1254,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
vertical: 8,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Title on the left
@ -1150,17 +1267,21 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow
overflow:
TextOverflow
.ellipsis, // Ensures title doesn't overflow
),
),
SizedBox(width: 16),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchInternationalController,
controller:
searchInternationalController,
onChanged: handleInternationalFilter,
decoration: InputDecoration(
hintText: "Search ...",
@ -1174,27 +1295,36 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
])
),
],
),
),
Container(
height:
@ -1207,7 +1337,11 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
builder: (context, constraints) {
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newInternationalData =filteredInternational.isNotEmpty ? filteredInternational : internationalData;
List<Map<String, dynamic>>
newInternationalData =
filteredInternational.isNotEmpty
? filteredInternational
: internationalData;
// Pagination logic
List<Map<String, dynamic>>
paginatedInternational =
@ -1216,7 +1350,10 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
.take(itemsPerPage2)
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
return ((searchInternationalController
.text
.isNotEmpty &&
filteredInternational.isEmpty)
? Center(
child: Text(
"No data found",
@ -1230,17 +1367,21 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -1378,8 +1519,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['plan_id']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1388,8 +1528,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['employee_code']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1398,8 +1537,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['so_number']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1408,8 +1546,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['functional_department']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1418,8 +1555,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['flight_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1428,8 +1564,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1438,8 +1573,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['sector']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1448,8 +1582,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
'${entry['flight_class']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1458,8 +1591,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
DataCell(
Text(
TravelDate,
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1478,7 +1610,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
PaginationControls(
currentPage: currentPage2,
itemsPerPage: itemsPerPage2,
totalItems: internationalData.length,
totalItems:
internationalData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -1510,6 +1643,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
),
);
}
Widget buildTitle(bool isDesktop) {
return // Left side: Breadcrumb inside a Container (optional)
Container(
@ -1522,15 +1656,14 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
onTap: (context) {
// Navigator.pushNamed(context, '/report');
context.go("/report");
}
),
BreadcrumbItem(
title: 'MIS Air Report ',
},
),
BreadcrumbItem(title: 'MIS Air Report '),
],
),
);
}
Widget buildExports(bool isDesktop) {
return Row(
children: [
@ -1541,10 +1674,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: handleDownload,
child: Row(
@ -1557,11 +1687,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
),
),
SizedBox(width: 8),
Icon(
Icons.file_present_outlined,
size: 15,
color: Colors.white,
),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
@ -1594,14 +1720,14 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
// ),
],
);
}
Widget buildFromDateField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "From Date *",
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1643,12 +1769,9 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
@ -1675,7 +1798,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "To Date *",
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1691,13 +1815,10 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText =
textControllers["from_date"]?.text;
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat(
'dd-MM-yyyy',
).parse(fromDateText);
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
@ -1722,16 +1843,10 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
@ -1750,8 +1865,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
style: const TextStyle(color: Colors.red, fontSize: 12),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow
.ellipsis, // Add ellipsis if it still overflows
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
@ -1759,11 +1873,8 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
}
Widget buildSearchButton(bool isDesktop) {
return
Padding(
padding: isDesktop
? const EdgeInsets.only(top: 22)
: EdgeInsets.zero,
return Padding(
padding: isDesktop ? const EdgeInsets.only(top: 22) : EdgeInsets.zero,
child: SizedBox(
width: isDesktop ? null : double.infinity,
child: ElevatedButton(
@ -1776,23 +1887,17 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
handleSubmit();
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Search",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
style: GoogleFonts.poppins(fontSize: isDesktop ? 13 : 11),
),
SizedBox(width: 8), // spacing between icon and text
Icon(Icons.search, size: 15, color: Colors.white),

View File

@ -78,7 +78,6 @@ class _MISforexState extends State<MISforex>
focusStates["from_date"] = false;
}
// Add focus listeners
focusNodes.forEach((key, focusNode) {
_addFocusListener(focusNode, (focus) {
@ -92,7 +91,6 @@ class _MISforexState extends State<MISforex>
// fetchMISforexReport();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -243,9 +241,7 @@ class _MISforexState extends State<MISforex>
setState(() {
// domesticData = List<Map<String, dynamic>>.from(result['data']['domestic'] ?? []);
internationalData = List<Map<String, dynamic>>.from(
result['status'] == 'success'
? result['data'] ?? []
: [],
result['status'] == 'success' ? result['data'] ?? [] : [],
);
filteredInternational = internationalData;
@ -299,13 +295,13 @@ class _MISforexState extends State<MISforex>
excelName,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
behavior: SnackBarBehavior.floating,
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// backgroundColor: result['status'] ? Colors.green : Colors.red,
// content: Text(result['message']),
// behavior: SnackBarBehavior.floating,
// ),
// );
}
void handleInternationalFilter(String query) {
@ -313,19 +309,60 @@ class _MISforexState extends State<MISforex>
setState(() {
filteredInternational =
internationalData.where((object) {
return (object['plan_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['employee_code']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['passenger_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ?? false) ||
(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));
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) ||
(object['so_number']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['functional_department']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['visiting_country']?.toLowerCase().contains(
lowerQuery,
) ??
false) ||
(object['amount']?.toLowerCase().contains(lowerQuery) ??
false) ||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
(newToDate.toLowerCase().contains(lowerQuery));
}).toList();
currentPage = 0;
});
print("handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}");
print(
"handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}",
);
}
@override
@ -399,7 +436,7 @@ class _MISforexState extends State<MISforex>
children: [
buildTitle(isDesktop),
Spacer(),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
: Column(
@ -407,9 +444,9 @@ class _MISforexState extends State<MISforex>
children: [
buildTitle(isDesktop),
SizedBox(height: 8),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
),
),
SizedBox(height: 5),
@ -450,13 +487,34 @@ class _MISforexState extends State<MISforex>
internationalData.isEmpty
? 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,
),
),
],
),
),
)
@ -470,7 +528,8 @@ class _MISforexState extends State<MISforex>
vertical: 8,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Title on the left
@ -482,14 +541,16 @@ class _MISforexState extends State<MISforex>
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow
overflow:
TextOverflow
.ellipsis, // Ensures title doesn't overflow
),
),
SizedBox(width: 16),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width * 0.2,
height: 40,
child: TextField(
controller: searchInternationalController,
@ -506,17 +567,23 @@ class _MISforexState extends State<MISforex>
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
@ -526,7 +593,8 @@ class _MISforexState extends State<MISforex>
style: GoogleFonts.poppins(fontSize: 12),
),
),
])
],
),
),
// Table container
Container(
@ -539,7 +607,11 @@ class _MISforexState extends State<MISforex>
builder: (context, constraints) {
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newInternationalData =filteredInternational.isNotEmpty ? filteredInternational : internationalData;
List<Map<String, dynamic>>
newInternationalData =
filteredInternational.isNotEmpty
? filteredInternational
: internationalData;
// Pagination logic
List<Map<String, dynamic>>
paginatedInternational =
@ -548,13 +620,43 @@ class _MISforexState extends State<MISforex>
.take(itemsPerPage)
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
return ((searchInternationalController
.text
.isNotEmpty &&
filteredInternational.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -568,15 +670,20 @@ class _MISforexState extends State<MISforex>
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
border: TableBorder(
horizontalInside: BorderSide(
horizontalInside:
BorderSide(
width: 0.5,
color: Colors.grey.shade200,
color:
Colors
.grey
.shade200,
),
),
columns: [
@ -587,7 +694,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -598,7 +706,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -609,7 +718,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -620,7 +730,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -631,7 +742,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -642,7 +754,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -653,7 +766,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -664,7 +778,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -675,7 +790,8 @@ class _MISforexState extends State<MISforex>
GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w600,
FontWeight
.w600,
),
),
),
@ -803,7 +919,8 @@ class _MISforexState extends State<MISforex>
PaginationControls(
currentPage: currentPage,
itemsPerPage: itemsPerPage,
totalItems: newInternationalData.length,
totalItems:
newInternationalData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -832,6 +949,7 @@ class _MISforexState extends State<MISforex>
),
);
}
Widget buildTitle(bool isDesktop) {
return // Left side: Breadcrumb inside a Container (optional)
Container(
@ -844,15 +962,14 @@ class _MISforexState extends State<MISforex>
onTap: (context) {
// Navigator.pushNamed(context, '/report');
context.go("/report");
}
),
BreadcrumbItem(
title: 'MIS Forex Report ',
},
),
BreadcrumbItem(title: 'MIS Forex Report '),
],
),
);
}
Widget buildExports(bool isDesktop) {
return Row(
children: [
@ -863,10 +980,7 @@ class _MISforexState extends State<MISforex>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: handleDownload,
child: Row(
@ -879,11 +993,7 @@ class _MISforexState extends State<MISforex>
),
),
SizedBox(width: 8),
Icon(
Icons.file_present_outlined,
size: 15,
color: Colors.white,
),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
@ -916,14 +1026,14 @@ class _MISforexState extends State<MISforex>
// ),
],
);
}
Widget buildFromDateField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "From Date *",
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -965,12 +1075,9 @@ class _MISforexState extends State<MISforex>
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
@ -997,7 +1104,8 @@ class _MISforexState extends State<MISforex>
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "To Date *",
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1013,13 +1121,10 @@ class _MISforexState extends State<MISforex>
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText =
textControllers["from_date"]?.text;
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat(
'dd-MM-yyyy',
).parse(fromDateText);
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
@ -1044,16 +1149,10 @@ class _MISforexState extends State<MISforex>
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
@ -1072,8 +1171,7 @@ class _MISforexState extends State<MISforex>
style: const TextStyle(color: Colors.red, fontSize: 12),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow
.ellipsis, // Add ellipsis if it still overflows
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
@ -1081,11 +1179,8 @@ class _MISforexState extends State<MISforex>
}
Widget buildSearchButton(bool isDesktop) {
return
Padding(
padding: isDesktop
? const EdgeInsets.only(top: 22)
: EdgeInsets.zero,
return Padding(
padding: isDesktop ? const EdgeInsets.only(top: 22) : EdgeInsets.zero,
child: SizedBox(
width: isDesktop ? null : double.infinity,
child: ElevatedButton(
@ -1098,23 +1193,17 @@ class _MISforexState extends State<MISforex>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
handleSubmit();
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Search",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
style: GoogleFonts.poppins(fontSize: isDesktop ? 13 : 11),
),
SizedBox(width: 8), // spacing between icon and text
Icon(Icons.search, size: 15, color: Colors.white),

File diff suppressed because it is too large Load Diff

View File

@ -112,9 +112,12 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
String getTabName(int index) {
switch (index) {
case 0: return "domestic";
case 1: return "international";
default: return "domestic"; // return "Unknown";
case 0:
return "domestic";
case 1:
return "international";
default:
return "domestic"; // return "Unknown";
}
}
@ -331,13 +334,13 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
excelName,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text(result as String),
behavior: SnackBarBehavior.floating,
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// backgroundColor: result['status'] ? Colors.green : Colors.red,
// content: Text(result['message']),
// behavior: SnackBarBehavior.floating,
// ),
// );
}
void handleDomesticFilter(String query) {
@ -345,13 +348,17 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
setState(() {
filteredDomestic =
domesticData.where((object) {
return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
return (object['Service']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['service_count']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage1 = 0;
});
print("handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}");
print(
"handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}",
);
}
void handleInternationalFilter(String query) {
@ -359,16 +366,19 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
setState(() {
filteredInternational =
internationalData.where((object) {
return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['flight_service_count']?.toLowerCase().contains(lowerQuery) ?? false) ||
return (object['Service']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['service_count']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['plan_trip_type']?.toLowerCase().contains(lowerQuery));
}).toList();
currentPage2 = 0;
});
print("handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}");
print(
"handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}",
);
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
@ -429,7 +439,6 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ------------------- First Row -------------------
// ------------------- First Row -------------------
Container(
color: Colors.white,
@ -440,7 +449,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
children: [
buildTitle(isDesktop),
Spacer(),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
: Column(
@ -448,9 +457,9 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
children: [
buildTitle(isDesktop),
SizedBox(height: 8),
buildExports(isDesktop)
buildExports(isDesktop),
],
)
),
),
SizedBox(height: 5),
@ -507,13 +516,34 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
: domesticData.isEmpty
? 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,
),
),
],
),
),
)
@ -545,7 +575,9 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchDomesticController,
@ -562,24 +594,32 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
),
// SizedBox(width: 16),
@ -687,8 +727,10 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newDomesticData =filteredDomestic.isNotEmpty ? filteredDomestic : domesticData;
List<Map<String, dynamic>> newDomesticData =
filteredDomestic.isNotEmpty
? filteredDomestic
: domesticData;
// Pagination logic
List<Map<String, dynamic>>
@ -698,14 +740,43 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
.take(itemsPerPage1)
.toList();
return (
(searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty)
return ((searchDomesticController
.text
.isNotEmpty &&
filteredDomestic.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -713,17 +784,21 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -781,8 +856,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
DataCell(
Text(
'${entry['Service']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -790,9 +864,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
DataCell(
Text(
'${entry['flight_service_count']}',
style:
GoogleFonts.poppins(
'${entry['service_count']}',
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -801,8 +874,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -821,7 +893,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
PaginationControls(
currentPage: currentPage1,
itemsPerPage: itemsPerPage1,
totalItems: newDomesticData.length,
totalItems:
newDomesticData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -847,13 +920,34 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
internationalData.isEmpty
? 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,
),
),
],
),
),
)
@ -984,7 +1078,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
vertical: 8,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Title on the left
@ -996,17 +1091,21 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow
overflow:
TextOverflow
.ellipsis, // Ensures title doesn't overflow
),
),
SizedBox(width: 16),
Container(
width: MediaQuery.of(context).size.width * 0.2,
width:
MediaQuery.of(context).size.width *
0.2,
height: 40,
child: TextField(
controller: searchInternationalController,
controller:
searchInternationalController,
onChanged: handleInternationalFilter,
decoration: InputDecoration(
hintText: "Search ...",
@ -1020,27 +1119,36 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(
12,
),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
style: GoogleFonts.poppins(
fontSize: 12,
),
),
])
),
],
),
),
Container(
height:
@ -1053,7 +1161,11 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
builder: (context, constraints) {
final double minWidth =
isDesktop ? constraints.maxWidth : 1300;
List<Map<String, dynamic>> newInternationalData =filteredInternational.isNotEmpty ? filteredInternational : internationalData;
List<Map<String, dynamic>>
newInternationalData =
filteredInternational.isNotEmpty
? filteredInternational
: internationalData;
// Pagination logic
List<Map<String, dynamic>>
paginatedInternational =
@ -1062,13 +1174,43 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
.take(itemsPerPage2)
.toList();
return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty)
return ((searchInternationalController
.text
.isNotEmpty &&
filteredInternational.isEmpty)
? Center(
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,
),
),
],
),
),
)
@ -1076,17 +1218,21 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
scrollDirection:
Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
scrollDirection:
Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing:
isDesktop ? 30.0 : 24.0,
isDesktop
? 30.0
: 24.0,
border: TableBorder(
horizontalInside:
BorderSide(
@ -1144,8 +1290,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
DataCell(
Text(
'${entry['Service']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1153,9 +1298,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
DataCell(
Text(
'${entry['flight_service_count']}',
style:
GoogleFonts.poppins(
'${entry['service_count']}',
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1164,8 +1308,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
DataCell(
Text(
'${entry['plan_trip_type']}',
style:
GoogleFonts.poppins(
style: GoogleFonts.poppins(
fontSize:
12,
),
@ -1184,7 +1327,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
PaginationControls(
currentPage: currentPage2,
itemsPerPage: itemsPerPage2,
totalItems: newInternationalData.length,
totalItems:
newInternationalData.length,
activeColor: layoutColor,
onPageChanged: (page) {
setState(() {
@ -1229,13 +1373,11 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
onTap: (context) {
// Navigator.pushNamed(context, '/report');
context.go("/report");
}
),
BreadcrumbItem(
title: 'Service Analysis Report',
},
),
BreadcrumbItem(title: 'Service Analysis Report'),
],
)
),
);
}
@ -1249,10 +1391,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: handleDownload,
child: Row(
@ -1265,11 +1404,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
),
SizedBox(width: 8),
Icon(
Icons.file_present_outlined,
size: 15,
color: Colors.white,
),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
@ -1302,14 +1437,14 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
// ),
],
);
}
Widget buildFromDateField(bool isDesktop) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "From Date *",
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1350,12 +1485,9 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
@ -1382,7 +1514,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( "To Date *",
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
@ -1398,13 +1531,10 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText =
textControllers["from_date"]?.text;
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat(
'dd-MM-yyyy',
).parse(fromDateText);
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
@ -1429,16 +1559,10 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior:
FloatingLabelBehavior.never,
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
vertical: 16,
),
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
@ -1457,8 +1581,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
style: const TextStyle(color: Colors.red, fontSize: 12),
maxLines: 2, // Allow it to wrap onto two lines
overflow:
TextOverflow
.ellipsis, // Add ellipsis if it still overflows
TextOverflow.ellipsis, // Add ellipsis if it still overflows
),
],
],
@ -1466,11 +1589,8 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
}
Widget buildSearchButton(bool isDesktop) {
return
Padding(
padding: isDesktop
? const EdgeInsets.only(top: 22)
: EdgeInsets.zero,
return Padding(
padding: isDesktop ? const EdgeInsets.only(top: 22) : EdgeInsets.zero,
child: SizedBox(
width: isDesktop ? null : double.infinity,
child: ElevatedButton(
@ -1483,23 +1603,17 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
handleSubmit();
},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
mainAxisSize: MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Search",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
style: GoogleFonts.poppins(fontSize: isDesktop ? 13 : 11),
),
SizedBox(width: 8), // spacing between icon and text
Icon(Icons.search, size: 15, color: Colors.white),
@ -1509,5 +1623,4 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
),
);
}
}

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,
),
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(data['message']);
// For non-200 responses
final errorMsg = 'Failed to upload file: ${response.reasonPhrase}';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(data['message']),
content: Text(errorMsg),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
refreshUserList();
}
} else {
print('Something went wrong');
print('Failed to upload file: ${response.reasonPhrase}');
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,
});
},
),
],
),
),
),
],
);
}
}