report completed

This commit is contained in:
venba-Inspriron-3558 2025-06-18 16:25:32 +05:30
parent e2b9781cd6
commit 25239c6e62
6 changed files with 3108 additions and 1968 deletions

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

@ -1,651 +0,0 @@
import 'dart:convert';
import 'dart:core';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../routes/mainLayout.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart';
class MiscellaneousAir extends StatefulWidget {
const MiscellaneousAir({super.key});
@override
_MiscellaneousAirState createState() => _MiscellaneousAirState();
}
class _MiscellaneousAirState extends State<MiscellaneousAir> {
final ApiService apiService = ApiService();
String? userId;
String? orgId;
String? roleUser;
String? token;
Color? layoutColor;
Color? bodyColor;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, TextEditingController> textControllers = {};
Map<String, String> errorMessages = {};
// Map<String, dynamic> MiscellaneousAirReport = {};
List<Map<String, dynamic>> domesticData = [];
List<Map<String, dynamic>> internationData = [];
// Map<String, dynamic> domesticData = {};
// Map<String, dynamic> internationData = {};
@override
void initState() {
super.initState();
if (!textControllers.containsKey("from_date")) {
textControllers["from_date"] = TextEditingController();
}
if (!textControllers.containsKey("to_date")) {
textControllers["to_date"] = TextEditingController();
}
if (!focusNodes.containsKey("from_date")) {
focusNodes["from_date"] = FocusNode();
}
if (!focusNodes.containsKey("to_date")) {
focusNodes["to_date"] = FocusNode();
}
if (!focusStates.containsKey("to_date")) {
focusStates["to_date"] = false;
}
if (!focusStates.containsKey("from_date")) {
focusStates["from_date"] = false;
}
// Add focus listeners
focusNodes.forEach((key, focusNode) {
_addFocusListener(focusNode, (focus) {
setState(() {
focusStates[key] = focus;
});
});
});
getToken();
// fetchMiscellaneousAirReport();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null ? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
Future<void> initializeData() async {
token = await getToken();
userId = await getUserId();
orgId = await getOrgId();
roleUser = await getRoleUser();
if (token == null || userId == null) {
print("Token or USerId missing");
return;
}
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["user_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getOrgId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["org_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<void> handleSubmit() async {
errorMessages.remove("from_date");
errorMessages.remove("to_date");
final fromDateText = textControllers["from_date"]?.text;
final toDateText = textControllers["to_date"]?.text;
if (fromDateText == null || fromDateText.isEmpty ||
toDateText == null || toDateText.isEmpty) {
errorMessages["from_date"] = "Required";
errorMessages["to_date"] = "Required";
setState(() {});
return;
}
// Parse the string to DateTime using the correct format
final dateFormat = DateFormat('dd-MM-yyyy');
final DateTime fromDate = dateFormat.parse(fromDateText);
final DateTime toDate = dateFormat.parse(toDateText);
// Check if from date is after to date
if (fromDate.isAfter(toDate)) {
errorMessages["to_date"] = "Invalid Date Range";
setState(() {});
return;
}
// Format for API request
final formattedfromDate = DateFormat('yyyy-MM-dd').format(fromDate);
final formattedtoDate = DateFormat('yyyy-MM-dd').format(toDate);
// Fetch and update
final result = await apiService.CallReports(
'misAirReport',
formattedfromDate,
formattedtoDate,
);
setState(() {
domesticData = List<Map<String, dynamic>>.from(result['data']['domestic'] ?? []);
internationData = List<Map<String, dynamic>>.from(result['data']['international'] ?? []);
});
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
// throw UnimplementedError();
// }
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
// const Expanded(child: Center(child: Text("User Page Content"))),
Expanded(child: buildMiscellaneousAirLayout(isDesktop))
],
),
),
);
},
);
}
Widget buildMiscellaneousAirLayout(bool isDesktop) {
return Container( child: buildMiscellaneousAirFormLayout(isDesktop) );
}
Widget buildMiscellaneousAirFormLayout(bool isDesktop) {
return Container(
margin: isDesktop ? EdgeInsets.all(10.0) : null,
padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20),
height: isDesktop
? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
border: isDesktop
? Border.all(width: 2, color: Colors.white)
: null,
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ------------------- First Row -------------------
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Miscellaneous Air',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your Excel export logic here
},
child: Row(
children: [
Text(
"Excel",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your PDF export logic here
},
child: Row(
children: [
Text(
"PDF",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white),
],
),
),
],
),
],
),
SizedBox(height: 15),
// ------------------- Second Row -------------------
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["from_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["from_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["from_date"],
controller: textControllers["from_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["from_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["from_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["to_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
context: context,
initialDate: mintoDate ?? DateTime.now(),
firstDate: mintoDate ?? DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["to_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["to_date"],
controller: textControllers["to_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["to_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["to_date"]!,
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
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
SizedBox(
child: ElevatedButton(
onPressed: () {
handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
// backgroundColor: layoutColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
'Search',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.white,
),
),
),
),
],
),
SizedBox(height: 15),
// ------------------- Third Row -------------------
DefaultTabController(
length: 2,
child: Column(
children: [
TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorColor: Color(0xFF114D8B),
tabs: [
Tab(text: "Domestic"),
Tab(text: "International"),
],
),
SizedBox(height: 10),
Container(
height: 200, // Or use Expanded for flexible height
child: TabBarView(
children: [
// Domestic Tab
domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"Domestic Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: domesticData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
// International Tab
internationData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"International Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: internationData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
],
),
),
],
),
),
SizedBox(height: 15),
],
),
);
}
}

View File

@ -1,666 +0,0 @@
import 'dart:convert';
import 'dart:core';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../routes/mainLayout.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart';
class MiscellaneousForex extends StatefulWidget {
const MiscellaneousForex({super.key});
@override
_MiscellaneousForexState createState() => _MiscellaneousForexState();
}
class _MiscellaneousForexState extends State<MiscellaneousForex> {
final ApiService apiService = ApiService();
String? userId;
String? orgId;
String? roleUser;
String? token;
Color? layoutColor;
Color? bodyColor;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, TextEditingController> textControllers = {};
Map<String, String> errorMessages = {};
// Map<String, dynamic> MiscellaneousForexReport = {};
List<Map<String, dynamic>> domesticData = [];
List<Map<String, dynamic>> internationData = [];
// Map<String, dynamic> domesticData = {};
// Map<String, dynamic> internationData = {};
@override
void initState() {
super.initState();
if (!textControllers.containsKey("from_date")) {
textControllers["from_date"] = TextEditingController();
}
if (!textControllers.containsKey("to_date")) {
textControllers["to_date"] = TextEditingController();
}
if (!focusNodes.containsKey("from_date")) {
focusNodes["from_date"] = FocusNode();
}
if (!focusNodes.containsKey("to_date")) {
focusNodes["to_date"] = FocusNode();
}
if (!focusStates.containsKey("to_date")) {
focusStates["to_date"] = false;
}
if (!focusStates.containsKey("from_date")) {
focusStates["from_date"] = false;
}
// Add focus listeners
focusNodes.forEach((key, focusNode) {
_addFocusListener(focusNode, (focus) {
setState(() {
focusStates[key] = focus;
});
});
});
getToken();
// fetchMiscellaneousForexReport();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null ? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
Future<void> initializeData() async {
token = await getToken();
userId = await getUserId();
orgId = await getOrgId();
roleUser = await getRoleUser();
if (token == null || userId == null) {
print("Token or USerId missing");
return;
}
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["user_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getOrgId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["org_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<void> handleSubmit() async {
errorMessages.remove("from_date");
errorMessages.remove("to_date");
final fromDateText = textControllers["from_date"]?.text;
final toDateText = textControllers["to_date"]?.text;
if (fromDateText == null || fromDateText.isEmpty ||
toDateText == null || toDateText.isEmpty) {
errorMessages["from_date"] = "Required";
errorMessages["to_date"] = "Required";
setState(() {});
return;
}
// Parse the string to DateTime using the correct format
final dateFormat = DateFormat('dd-MM-yyyy');
final DateTime fromDate = dateFormat.parse(fromDateText);
final DateTime toDate = dateFormat.parse(toDateText);
// Check if from date is after to date
if (fromDate.isAfter(toDate)) {
errorMessages["to_date"] = "Invalid Date Range";
setState(() {});
return;
}
// Format for API request
final formattedfromDate = DateFormat('yyyy-MM-dd').format(fromDate);
final formattedtoDate = DateFormat('yyyy-MM-dd').format(toDate);
// Fetch and update
final result = await apiService.CallReports(
'misForexReport',
formattedfromDate,
formattedtoDate,
);
setState(() {
domesticData = List<Map<String, dynamic>>.from(result['data']['domestic'] ?? []);
internationData = List<Map<String, dynamic>>.from(result['data']['international'] ?? []);
});
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
// throw UnimplementedError();
// }
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
// const Expanded(child: Center(child: Text("User Page Content"))),
Expanded(child: buildMiscellaneousForexLayout(isDesktop))
],
),
),
);
},
);
}
Widget buildMiscellaneousForexLayout(bool isDesktop) {
return Container( child: buildMiscellaneousForexFormLayout(isDesktop) );
}
Widget buildMiscellaneousForexFormLayout(bool isDesktop) {
return Container(
margin: isDesktop ? EdgeInsets.all(10.0) : null,
padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20),
height: isDesktop
? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
border: isDesktop
? Border.all(width: 2, color: Colors.white)
: null,
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ------------------- First Row -------------------
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Miscellaneous Forex',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your Excel export logic here
},
child: Row(
children: [
Text(
"Excel",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your PDF export logic here
},
child: Row(
children: [
Text(
"PDF",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white),
],
),
),
],
),
],
),
SizedBox(height: 15),
// ------------------- Second Row -------------------
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["from_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["from_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["from_date"],
controller: textControllers["from_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["from_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["from_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["to_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
context: context,
initialDate: mintoDate ?? DateTime.now(),
firstDate: mintoDate ?? DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["to_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["to_date"],
controller: textControllers["to_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["to_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["to_date"]!,
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
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
SizedBox(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
foregroundColor: Colors.white,
disabledBackgroundColor: Color(0xFF114D8B),
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
onPressed: () {handleSubmit();},
child: Row(
mainAxisSize:
MainAxisSize.min, // Ensures content fits nicely
children: [
Text(
"Search",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
),
),
SizedBox(width: 8), // spacing between icon and text
Icon(
Icons.search,
size: 15,
color: Colors.white,
),
],
),
),
),
],
),
SizedBox(height: 15),
// ------------------- Third Row -------------------
DefaultTabController(
length: 2,
child: Column(
children: [
TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorColor: Color(0xFF114D8B),
tabs: [
Tab(text: "Domestic"),
Tab(text: "International"),
],
),
SizedBox(height: 10),
Container(
height: 200, // Or use Expanded for flexible height
child: TabBarView(
children: [
// Domestic Tab
domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"Domestic Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: domesticData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
// International Tab
internationData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"International Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: internationData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
],
),
),
],
),
),
SizedBox(height: 15),
],
),
);
}
}

View File

@ -1,651 +0,0 @@
import 'dart:convert';
import 'dart:core';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../routes/mainLayout.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_text_field.dart';
class MiscellaneousHotel extends StatefulWidget {
const MiscellaneousHotel({super.key});
@override
_MiscellaneousHotelState createState() => _MiscellaneousHotelState();
}
class _MiscellaneousHotelState extends State<MiscellaneousHotel> {
final ApiService apiService = ApiService();
String? userId;
String? orgId;
String? roleUser;
String? token;
Color? layoutColor;
Color? bodyColor;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, TextEditingController> textControllers = {};
Map<String, String> errorMessages = {};
// Map<String, dynamic> MiscellaneousHotelReport = {};
List<Map<String, dynamic>> domesticData = [];
List<Map<String, dynamic>> internationData = [];
// Map<String, dynamic> domesticData = {};
// Map<String, dynamic> internationData = {};
@override
void initState() {
super.initState();
if (!textControllers.containsKey("from_date")) {
textControllers["from_date"] = TextEditingController();
}
if (!textControllers.containsKey("to_date")) {
textControllers["to_date"] = TextEditingController();
}
if (!focusNodes.containsKey("from_date")) {
focusNodes["from_date"] = FocusNode();
}
if (!focusNodes.containsKey("to_date")) {
focusNodes["to_date"] = FocusNode();
}
if (!focusStates.containsKey("to_date")) {
focusStates["to_date"] = false;
}
if (!focusStates.containsKey("from_date")) {
focusStates["from_date"] = false;
}
// Add focus listeners
focusNodes.forEach((key, focusNode) {
_addFocusListener(focusNode, (focus) {
setState(() {
focusStates[key] = focus;
});
});
});
getToken();
// fetchMiscellaneousHotelReport();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
String? bodyStringColor = await getBodyColor();
setState(() {
layoutColor = layoutString != null ? Color(int.parse(layoutString))
: Colors.redAccent;
bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor))
: Colors.white;
});
}
Future<void> initializeData() async {
token = await getToken();
userId = await getUserId();
orgId = await getOrgId();
roleUser = await getRoleUser();
if (token == null || userId == null) {
print("Token or USerId missing");
return;
}
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["user_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getOrgId() async {
final prefs = await SharedPreferences.getInstance();
final String? userDataString = prefs.getString('user_data');
if (userDataString != null) {
try {
final Map<String, dynamic> userData = jsonDecode(userDataString);
return userData["org_id"]?.toString();
} catch (e) {
return null;
}
}
return null;
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<void> handleSubmit() async {
errorMessages.remove("from_date");
errorMessages.remove("to_date");
final fromDateText = textControllers["from_date"]?.text;
final toDateText = textControllers["to_date"]?.text;
if (fromDateText == null || fromDateText.isEmpty ||
toDateText == null || toDateText.isEmpty) {
errorMessages["from_date"] = "Required";
errorMessages["to_date"] = "Required";
setState(() {});
return;
}
// Parse the string to DateTime using the correct format
final dateFormat = DateFormat('dd-MM-yyyy');
final DateTime fromDate = dateFormat.parse(fromDateText);
final DateTime toDate = dateFormat.parse(toDateText);
// Check if from date is after to date
if (fromDate.isAfter(toDate)) {
errorMessages["to_date"] = "Invalid Date Range";
setState(() {});
return;
}
// Format for API request
final formattedfromDate = DateFormat('yyyy-MM-dd').format(fromDate);
final formattedtoDate = DateFormat('yyyy-MM-dd').format(toDate);
// Fetch and update
final result = await apiService.CallReports(
'misHotelReport',
formattedfromDate,
formattedtoDate,
);
setState(() {
domesticData = List<Map<String, dynamic>>.from(result['data']['domestic'] ?? []);
internationData = List<Map<String, dynamic>>.from(result['data']['international'] ?? []);
});
}
@override
// Widget build(BuildContext context) {
// // TODO: implement build
// throw UnimplementedError();
// }
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Color(0xFFf5f5f5),
appBar: CustomAppBar(isDesktop: isDesktop),
drawer: CustomDrawer(isDesktop: false),
body: Padding(
padding:
isDesktop
? EdgeInsets.symmetric(
horizontal:
MediaQuery.of(context).size.width *
0.1, // 30% of screen width as horizontal padding
vertical:
MediaQuery.of(context).size.height *
0, // 5% of screen height as vertical padding
)
: EdgeInsets.all(0),
child: Row(
children: [
// if (isDesktop) CustomDrawer(isDesktop: true),
// const Expanded(child: Center(child: Text("User Page Content"))),
Expanded(child: buildMiscellaneousHotelLayout(isDesktop))
],
),
),
);
},
);
}
Widget buildMiscellaneousHotelLayout(bool isDesktop) {
return Container( child: buildMiscellaneousHotelFormLayout(isDesktop) );
}
Widget buildMiscellaneousHotelFormLayout(bool isDesktop) {
return Container(
margin: isDesktop ? EdgeInsets.all(10.0) : null,
padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20),
height: isDesktop
? MediaQuery.of(context).size.height * 0.98
: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
border: isDesktop
? Border.all(width: 2, color: Colors.white)
: null,
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ------------------- First Row -------------------
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Miscellaneous Hotel',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your Excel export logic here
},
child: Row(
children: [
Text(
"Excel",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.file_present_outlined, size: 15, color: Colors.white),
],
),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Color(0xFF114D8B), width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
// Your PDF export logic here
},
child: Row(
children: [
Text(
"PDF",
style: GoogleFonts.poppins(
fontSize: isDesktop ? 13 : 11,
color: Colors.white,
),
),
SizedBox(width: 8),
Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white),
],
),
),
],
),
],
),
SizedBox(height: 15),
// ------------------- Second Row -------------------
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["from_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["from_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["from_date"],
controller: textControllers["from_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Select Date",
labelStyle: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
suffixIcon: const Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["from_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["from_date"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["to_date"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () async {
final fromDateText = textControllers["from_date"]?.text;
DateTime? mintoDate;
if (fromDateText != null && fromDateText.isNotEmpty) {
mintoDate = DateFormat('dd-MM-yyyy').parse(fromDateText);
}
final pickedDate = await showDatePicker(
context: context,
initialDate: mintoDate ?? DateTime.now(),
firstDate: mintoDate ?? DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
textControllers["to_date"]?.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
}
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["to_date"],
controller: textControllers["to_date"],
readOnly: true,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(
Icons.calendar_today,
size: 16,
color: Colors.grey,
),
),
),
),
),
),
),
if (errorMessages["to_date"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["to_date"]!,
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
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
SizedBox(
child: ElevatedButton(
onPressed: () {
handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
// backgroundColor: layoutColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
'Search',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.white,
),
),
),
),
],
),
SizedBox(height: 15),
// ------------------- Third Row -------------------
DefaultTabController(
length: 2,
child: Column(
children: [
TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorColor: Color(0xFF114D8B),
tabs: [
Tab(text: "Domestic"),
Tab(text: "International"),
],
),
SizedBox(height: 10),
Container(
height: 200, // Or use Expanded for flexible height
child: TabBarView(
children: [
// Domestic Tab
domesticData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"Domestic Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: domesticData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
// International Tab
internationData.isEmpty
? Center(
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
"No data found",
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w500),
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Text(
"International Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})",
style: GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700]),
),
),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
dividerThickness: 0.5,
columnSpacing: isDesktop ? 24.0 : 16.0,
border: TableBorder(
horizontalInside: BorderSide(
width: 0.5,
color: Colors.grey.shade200,
),
),
columns: [
DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))),
],
rows: internationData.map((entry) {
return DataRow(cells: [
DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))),
DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))),
]);
}).toList(),
),
),
),
],
),
],
),
),
],
),
),
SizedBox(height: 15),
],
),
);
}
}