Merge branch 'main' of bitbucket.org:venbainformationtechnology/ts-tat

This commit is contained in:
venbaittech 2025-06-18 18:40:55 +05:30
commit e1d1266853
14 changed files with 7004 additions and 2805 deletions

View File

@ -123,6 +123,8 @@ class _PolicyListState extends State<PolicyList> {
futurePolicy?.then((object) {
setState(() {
allPolicy = object;
filteredPolicy = object;
searchController.text = "";
});
});
// Wait for futurePlans to be fetched and update allPlans

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -30,6 +30,7 @@ class _ReportListState extends State<ReportList> {
void initState() {
super.initState();
loadInitialData();
_checkAuthAndLoadData();
}
void loadInitialData() async {
@ -38,13 +39,32 @@ class _ReportListState extends State<ReportList> {
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
void _checkAuthAndLoadData() async {
final String? token = await getToken(); // Your async function to get token
if (token == null || token.isEmpty) {
// Token doesn't exist → redirect to login
context.go(
"/",
); // or use: router.go("/") if you're using `GoRouter` directly
return;
}
if (!mounted) return;
try {
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
} catch (e) {
print("service report : $e");
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
@ -62,16 +82,16 @@ class _ReportListState extends State<ReportList> {
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),
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),
@ -97,38 +117,37 @@ class _ReportListState extends State<ReportList> {
{
'value': '/misServicesAnalysis',
'icon': Icons.miscellaneous_services,
'label': 'Miscellaneous Services Analysis',
'description': 'View Miscellaneous Services Analysis',
'label': 'MIS Services Analysis',
'description': 'View MIS Services Analysis',
},
{
'value': '/misAirReport',
'icon': Icons.flight,
'label': 'Miscellaneous Air Report',
'description': 'View Miscellaneous Air Report',
'label': 'MIS Air Report',
'description': 'View MIS Air Report',
},
{
'value': '/misHotelReport',
'icon': Icons.add_business,
'label': 'Miscellaneous Hotel Report',
'description': 'View Miscellaneous Hotel Report',
'label': 'MIS Hotel Report',
'description': 'View MIS Hotel Report',
},
{
'value': '/misForexReport',
'icon': Icons.credit_card,
'label': 'Miscellaneous Forex Report',
'description': 'View Miscellaneous Forex Report',
'label': 'MIS Forex Report',
'description': 'View MIS Forex Report',
},
{
'value': '/guestHouseReport',
'icon': Icons.add_home_work_sharp,
'icon': Icons.home_work,
'label': 'Guest House Report',
'description': 'View Guest House Report',
}
},
];
return Container(
padding: EdgeInsets.symmetric(vertical: 16, horizontal: 12),
child: Column(
@ -152,73 +171,73 @@ class _ReportListState extends State<ReportList> {
spacing: 16,
runSpacing: 16,
children:
menuItems.map((item) {
return SizedBox(
width: isDesktop ? 325 : double.infinity,
child: Card(
color: Colors.white,
child: InkWell(
onTap: () async {
if (item['value'] as String ==
"/forexTexmplate") {
final data =
await apiService.getForexTemplate();
print("ForexId -- $data");
menuItems.map((item) {
return SizedBox(
width: isDesktop ? 325 : double.infinity,
child: Card(
color: Colors.white,
child: InkWell(
onTap: () async {
if (item['value'] as String ==
"/forexTexmplate") {
final data =
await apiService.getForexTemplate();
print("ForexId -- $data");
context.go(
'/templateForex',
extra: {'templateData': data},
);
} else {
final route = item['value'] as String;
context.go(route);
}
},
child: Padding(
padding: EdgeInsets.all(12),
child: Row(
children: [
Icon(
item['icon'],
size: 40,
color: layoutColor,
// color: Color(0xFF114D8B),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
item['label'],
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
context.go(
'/templateForex',
extra: {'templateData': data},
);
} else {
final route = item['value'] as String;
context.go(route);
}
},
child: Padding(
padding: EdgeInsets.all(12),
child: Row(
children: [
Icon(
item['icon'],
size: 40,
color: layoutColor,
// color: Color(0xFF114D8B),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
item['label'],
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 4),
Text(
item['description'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
],
),
SizedBox(height: 4),
Text(
item['description'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
),
),
],
),
),
],
),
],
),
),
),
),
),
);
}).toList(),
);
}).toList(),
),
),
),

File diff suppressed because it is too large Load Diff

View File

@ -680,11 +680,11 @@ final List<Map<String, dynamic>> menuItems = [
// 'label': 'Template List'
// },
// {'value': '/template', 'icon': Icons.ac_unit_sharp, 'label': 'Template'},
// {
// 'value': '/report',
// 'icon': Icons.auto_graph,
// 'label': 'Reports',
// },
{
'value': '/report',
'icon': Icons.auto_graph,
'label': 'Reports',
},
{
'value': '/CreateUserDetails',
'icon': Icons.account_circle,

View File

@ -35,9 +35,9 @@ import '../Screens/traveller/travellerList.dart';
import '../Screens/reports/reportList.dart';
import '../Screens/reports/advancePurchase.dart';
import '../Screens/reports/servicesAnalysis.dart';
import '../Screens/reports/miscellaneousAir.dart';
import '../Screens/reports/miscellaneousHotel.dart';
import '../Screens/reports/miscellaneousForex.dart';
import '../Screens/reports/misAir.dart';
import '../Screens/reports/misHotel.dart';
import '../Screens/reports/misForex.dart';
import '../Screens/reports/guestHouse.dart';
import 'mainLayout.dart';
@ -164,15 +164,15 @@ final GoRouter router = GoRouter(
),
GoRoute(
path: '/misAirReport',
builder: (context, state) => MiscellaneousAir(),
builder: (context, state) => MISair(),
),
GoRoute(
path: '/misHotelReport',
builder: (context, state) => MiscellaneousHotel(),
builder: (context, state) => MIShotel(),
),
GoRoute(
path: '/misForexReport',
builder: (context, state) => MiscellaneousForex(),
builder: (context, state) => MISforex(),
),
GoRoute(
path: '/guestHouseReport',

View File

@ -1511,11 +1511,9 @@ class ApiService {
// ----------------------- User Management - CheckDuplicate end here ----------------------------------
// ----------------------- Report -------------------------
Future<Map<String, dynamic>> CallReports(String apiRoute,String FromDate,String ToDate) async {
Future<Map<String, dynamic>> CallReports(String apiRoute,String body) async {
String apiUrldata = '$apiUrl/api/$apiRoute';
final token = await getToken();
final body = jsonEncode({"fromDate":'$FromDate',"toDate":'$ToDate'});
if (token == null) {
throw Exception('Token not found. Please log in.');
@ -1536,9 +1534,58 @@ class ApiService {
}
} else {
throw Exception(
'Failed to load checkDuplicate data. Status code: ${response.statusCode}',
'Failed to load data. Status code: ${response.statusCode}',
);
}
}
// ----------------------- Report -------------------------
Future<void> reportExcelDownload(String apiRoute,String body,String name) async {
final String apiUrldata = '$apiUrl/api/$apiRoute';
// Sanitize name for filename (optional)
final safeName = name.replaceAll(RegExp(r'[^\w\s-]'), ''); // remove any special chars if needed
final excelName = '$safeName.xlsx';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.post(
Uri.parse(apiUrldata),
headers: {'Authorization': 'Bearer $token','Content-Type': 'application/json'},
body: body
);
if (response.statusCode == 200) {
try {
print("report XL Dowloaded");
print(excelName);
// Create a blob from the response body
final blob = html.Blob([response.bodyBytes]);
// Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download
final anchor =
html.AnchorElement(href: url)
..setAttribute('download', excelName)
..click();
// Revoke the download URL to free up resources
html.Url.revokeObjectUrl(url);
throw Exception('Sucessfully Downloaded');
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else if (response.statusCode == 404) {
throw Exception('File not found.');
} else {
throw Exception('Failed to Download');
}
}
// ----------------------- Report -------------------------
}