ts-tat/lib/Screens/allTrips/list_all_plans.dart
2025-10-08 11:37:26 +05:30

2076 lines
108 KiB
Dart

import 'dart:convert';
import 'dart:core';
import 'package:frontend/Screens/allTrips/plan_info_mdl.dart';
import 'package:frontend/Screens/allTrips/remarks_list.dart';
import 'package:frontend/data/models/plan.dart';
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 '../../utils/pagination.dart';
import '../../utils/travelAgent_remarks.dart';
import '../../widgets/custom_popup.dart';
import '../../widgets/popup_listPlan_action.dart';
class ListAllPlans extends StatefulWidget {
const ListAllPlans({super.key});
@override
_ListAllPlansState createState() => _ListAllPlansState();
}
class _ListAllPlansState extends State<ListAllPlans> {
final ApiService apiService = ApiService();
int currentPage = 0;
int itemsPerPage = 10;
String? userId;
String? orgId;
String? roleUser;
String? token;
String? TripPlanAction;
Color? layoutColor;
Color? bodyColor;
late Future<List<Plan>> futurePlans;
List<Plan> allPlans = [];
List<Plan> filteredPlans = [];
TextEditingController searchController = TextEditingController();
late bool _listDialogShown = false;
@override
void initState() {
super.initState();
_checkAuthAndLoadData();
// // If token exists, load the dashboard data
// WidgetsBinding.instance.addPostFrameCallback((_) {
// initializeData();
// loadInitialData();
//
// // futurePlans.then((plans) {
// // setState(() {
// // allPlans = plans;
// // filteredPlans = plans;
// // });
// // });
// });
// // futurePlans = fetchPlans();
}
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;
} else {
getToken();
initializeData();
loadInitialData();
}
}
void filterPlans(String query) {
print("allPlans before filtering: $allPlans");
final lowerQuery = query.toLowerCase();
setState(() {
filteredPlans =
allPlans.where((plan) {
return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.employeeCode?.toLowerCase().contains(lowerQuery) ??
false) ||
(plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.userName?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.travellerName?.toLowerCase().contains(lowerQuery) ??
false) ||
(plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) ||
(plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false);
}).toList();
currentPage = 0;
});
print("filteredPlans: $filteredPlans");
}
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();
TripPlanAction = await getTripPlanAction();
if (token == null || userId == null) {
print("Token or USerId missing");
return;
} else {
setState(() {
futurePlans = fetchPlans();
});
// Wait for futurePlans to be fetched and update allPlans
futurePlans.then((plans) {
setState(() {
allPlans = plans; // Set allPlans after the fetch is complete
filteredPlans =
allPlans; // You can update filteredPlans too if needed
});
});
}
}
void refresh() {
print("Refresj");
setState(() {
futurePlans = fetchPlans();
});
// Wait for futurePlans to be fetched and update allPlans
futurePlans.then((plans) {
setState(() {
allPlans = plans; // Set allPlans after the fetch is complete
filteredPlans = allPlans; // You can update filteredPlans too if needed
});
});
}
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');
}
// Fetch API Data
Future<List<Plan>> fetchPlans() async {
late String apiUrldata;
apiUrldata = '$apiUrl/api/plans?org_id=$orgId';
// if (roleUser == "Org Admin") {
// apiUrldata = '$apiUrl/api/plans?org_id=$orgId';
// } else {
// apiUrldata =
// '$apiUrl/api/plans/findTravelAgentPlanList?org_id=$orgId&user_id=$userId';
// }
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
// List<dynamic> plansJson = [];
List<dynamic> plansJson = data['data'];
return plansJson.map((json) => Plan.fromJson(json)).toList();
} else if (response.statusCode == 403) {
print("403-FORB");
await apiService.logout(context);
return [];
// throw Exception('Failed to load users');
} else {
throw Exception('Failed to load plans');
}
}
Future<void> postPlanData(planData, planId) async {
final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan';
final token = await getToken(); // Fetch token
if (token == null) {
throw Exception('Token not found. Please log in.');
}
planData['is_active'] = "0";
print("POSTPlanTesting------- $planData"); // Add plan_id for update
try {
final response = await http.post(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);
if (response.statusCode == 200) {
print("Plan Deleted successfully!");
print("Response: ${response.body}");
initializeData();
} else if (response.statusCode == 403) {
print("403-FORB");
await apiService.logout(context);
return null;
// throw Exception('Failed to load users');
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting plan: $e");
}
}
void deletePlan(String planId) async {
bool confirmed = await apiService.showCancelConfirmationDialog(
context,
layoutColor,
);
if (confirmed) {
try {
Map<String, dynamic> planData = await ApiService().getViewPlan(
planId,
context,
);
print("ViewAAA - $planData");
refresh();
// postPlanData(planData, planId);
} catch (e) {
print("Error fetching plan: $e");
}
} else {
print("User cancelled");
}
}
Future<void> _showDetails(id) async {
_listDialogShown = true;
final String apiUrldata =
'$apiUrl/api/plans/get_plan_approval_status?plan_id=$id';
final String? token = await getToken();
String label = "";
List<Map<String, dynamic>> approverData = [];
if (token == null) {
print('Token not found. Please log in.');
label = "Error - Token not found";
} else {
try {
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
label = data['data']['model_heading'] ?? "";
approverData = List<Map<String, dynamic>>.from(
data['data']['approver_data'] ?? [],
);
} else if (response.statusCode == 403) {
print("403-FORB");
await apiService.logout(context);
return null;
// throw Exception('Failed to load users');
} else {
print('Failed to load');
label = "Error - Failed to load data";
}
} catch (e) {
print('Exception occurred: $e');
label = "Error - Something went wrong";
}
}
showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text(
label,
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
overflow: TextOverflow.ellipsis,
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (approverData.isNotEmpty)
SizedBox(
height: 250,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
columns: [
// DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))),
// DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))),
DataColumn(
label: Text(
'Status',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
rows:
approverData.asMap().entries.map((entry) {
final index = entry.key + 1;
final item = entry.value;
final approverIdKey = item.keys.firstWhere(
(k) => k.endsWith('_id'),
orElse: () => '',
);
final statusKey = item.keys.firstWhere(
(k) => k.endsWith('_status'),
orElse: () => '',
);
final approverId =
item[approverIdKey]?.toString() ?? '-';
final statusText =
item[statusKey]?.toString() ?? 'Unknown';
return DataRow(
cells: [
// DataCell(Text('$index')),
// DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))),
DataCell(
SizedBox(
child: Text(
statusText,
overflow: TextOverflow.ellipsis,
),
),
),
],
);
}).toList(),
),
),
),
)
else
Text(
"-- no data. --",
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w300,
),
),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
_listDialogShown = false;
},
child: Text("Close"),
),
],
),
);
}
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop =
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Color(0xFFf5f5f5),
// backgroundColor: Color(0xFFFCFCFC),
// appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
// drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
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),
Expanded(child: buildGroupListLayout(isDesktop)),
],
),
),
);
},
);
}
Widget buildGroupListLayout(bool isDesktop) {
return Container(
// decoration: BoxDecoration(
// color: Colors.amber,
// // color: bodyColor,
// // color: Color(0xFFE1F5FE),
// // border: Border.all(
// // // color: Color(0xFFF7F7FB),
// // color: Colors.white,
// // width: 3.5)
// ),
child: buildTableLayout(isDesktop),
);
}
Widget buildTableLayout(isDesktop) {
// String _formatDate(String rawDate) {
// try {
// final dateTime = DateTime.parse(rawDate);
// return DateFormat('dd, MMM yyyy hh:m a').format(dateTime);
// } catch (e) {
// return rawDate; // fallback if parsing fails
// }
// }
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat(
'dd, MMM yyyy HH:mm',
).format(dateTime); // 24-hour format
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
return Container(
margin: isDesktop ? EdgeInsets.all(10.0) : null,
padding:
isDesktop
? const EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20)
: EdgeInsets.all(3.0),
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,
// color: Color(0xFFF7F7FB),
)
: null,
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
// color: Color(0xFFF7F7FB),
// color: Colors.amber,
),
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15, left: 5, right: 5),
child: Container(
// padding: const EdgeInsets.all(10.0),
color: isDesktop ? Colors.white : Color(0xFFFCFCFC),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Divider(
// thickness: 0.2, // how "thick" the line is
// color: Colors.grey, // optional
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding:
isDesktop
? const EdgeInsets.all(0)
: const EdgeInsets.only(left: 14.0),
child: Row(
children: [
Text(
'All Trips',
style: GoogleFonts.poppins(
fontSize: isDesktop ? 16 : 14,
fontWeight: FontWeight.w600,
color: Colors.black,
),
// style: TextStyle(
// fontFamily: "Inter",
// fontSize: isDesktop ? 16 : 14,
// fontWeight: FontWeight.w600,
// color: const Color(0xFF212121) )
),
],
),
),
SizedBox(width: 1),
Spacer(),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
height: 40,
child: TextField(
controller: searchController,
onChanged: filterPlans,
decoration: InputDecoration(
hintText: "Search...",
hintStyle: TextStyle(
fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon(
Icons.search,
color: Color(0xFF9E9DBD),
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
),
),
// SizedBox(width: 16),
Spacer(),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// foregroundColor: Colors.white,
// backgroundColor: Colors.blueAccent,
// ),
// onPressed: () {
// context.go('/createPlan', extra: {
// 'orgId': orgId,
// });
// if (!isDesktop) Navigator.pop(context);
// },
// child: Row(
// children: [
// Icon(Icons.add_circle, color: Colors.white),
// SizedBox(width: 5),
// Text('NewPlan'),
//
// ],
// ),
// ),
// 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: () {
// if (TripPlanAction == "Plan Creation Not Allowed") {
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// title: Text(
// "Action Not Allowed",
// style: TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold),
// ),
// content: Text(
// "Plan Creation Not Allowed For This User."),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: Text(
// "OK",
// style: TextStyle(color: layoutColor),
// ),
// ),
// ],
// ),
// );
// } else {
// context.go('/createPlan', extra: {
// 'orgId': orgId,
// });
// if (!isDesktop) Navigator.pop(context);
// }
// },
// child: Row(
// mainAxisSize:
// MainAxisSize.min, // Ensures content fits nicely
// children: [
// Text(
// "Add New Plan",
// style: TextStyle(fontSize: 13),
// ),
// SizedBox(width: 8), // spacing between icon and text
// Icon(
// Icons.add_circle_outline_rounded,
// size: 15,
// color: Colors.white,
// ),
// ],
// ),
// ),
],
),
const SizedBox(height: 10),
if (!isDesktop)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.88,
height: 35,
child: TextField(
controller: searchController,
onChanged: filterPlans,
decoration: InputDecoration(
hintText: "Search...",
hintStyle: TextStyle(
fontSize: 12,
color: Color(0xFF9E9DBD),
),
prefixIcon: Icon(
Icons.search,
color: Color(0xFF9E9DBD),
size: 18,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade200,
width: 0.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
style: GoogleFonts.poppins(fontSize: 12),
),
),
],
),
FutureBuilder<List<Plan>>(
future: futurePlans,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError ||
!snapshot.hasData ||
snapshot.data!.isEmpty) {
final adjHgt = MediaQuery.of(context).size.height;
return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// const Icon(Icons.error_outline,
// color: Colors.redAccent, size: 60),
// const SizedBox(height: 1),
// Text(
// "Oops!",
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.bold,
// color: Colors.redAccent),
//
// ),
SizedBox(height: adjHgt / 4),
Text(
" No Data Found",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
],
),
),
);
}
// List<Plan> plans = snapshot.data!;
// List<Plan> plans =
// filteredPlans.isNotEmpty ? filteredPlans : allPlans;
List<Plan> plans =
searchController.text.isEmpty ? allPlans : filteredPlans;
plans.sort(
(a, b) =>
int.parse(b.planId).compareTo(int.parse(a.planId)),
);
List<Plan> paginatedPlans =
plans
.skip(currentPage * itemsPerPage)
.take(itemsPerPage)
.toList();
Widget table = LayoutBuilder(
builder: (context, constraints) {
double minWidth = isDesktop ? constraints.maxWidth : 1300;
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: minWidth),
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(
'S.NO',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Trip ID',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Expanded(
flex: 1,
child: Text(
'Trip Name',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
),
DataColumn(
label: Text(
'Trip Type',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Emp Code',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Traveller',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Created On',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Status',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
DataColumn(
label: Text(
'Actions',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
],
rows:
// paginatedPlans.map((plan)
paginatedPlans.asMap().entries.map((entry) {
int index = entry.key;
var plan = entry.value;
print("fordIf - ${plan.forexId}");
return DataRow(
cells: [
DataCell(
Text(
// "${index + 1}",
"${(currentPage * itemsPerPage) + index + 1}",
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
Text(
plan.planId,
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
SizedBox(
width: 130,
child: Text(
plan.tripTitle,
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
softWrap: true,
maxLines: 2,
// overflow: TextOverflow.ellipsis,
),
),
),
DataCell(
Text(
plan.tripType,
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
Text(
plan.employeeCode ?? " - ",
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
Text(
plan.userName.isNotEmpty
? plan.userName
: plan.travellerName,
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
Text(
_formatDate(plan.createdOn),
// plan.createdOn,
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
),
),
),
DataCell(
GestureDetector(
onTap:
() => _showDetails(plan.planId),
child: Container(
width:
double
.infinity, // Set your desired fixed size (equal width and height)
height: 25,
alignment: Alignment.center,
decoration: BoxDecoration(
color: getStatusColor(
plan.statusValue,
),
borderRadius:
BorderRadius.circular(10),
),
child: Text(
plan.statusValue,
textAlign: TextAlign.center,
style: TextStyle(
color: getStatusTextColor(
plan.statusValue,
),
fontSize: 12,
fontFamily: "Inter",
fontWeight: FontWeight.w400,
),
),
),
),
),
DataCell(
Row(
children: [
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder:
(context) => [
CustomPopupMenuEntry(
child: Container(
padding:
EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
child: Row(
mainAxisSize:
MainAxisSize
.min,
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
IconButton(
icon: Icon(
Icons
.remove_red_eye,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'View The Trip Details',
onPressed: () {
Navigator.pop(
context,
); // Close popup manually
ApiService().viewPlan(
context,
plan.planId,
isViewMode:
true,
);
},
),
if (plan.statusValue ==
"Pending Approval" ||
plan.statusValue ==
"Partially Approved")
IconButton(
icon: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
height: 15,
),
tooltip:
'Edit The Trip Details',
onPressed: () {
Navigator.pop(
context,
);
ApiService().viewPlan(
context,
plan.planId,
isViewMode:
false,
);
},
),
IconButton(
icon: Icon(
Icons
.cancel_rounded,
size: 18,
),
tooltip:
'Cancellation The Trip Details',
onPressed: () {
Navigator.pop(
context,
);
deletePlan(
plan.planId,
);
},
),
IconButton(
icon: Icon(
Icons
.download,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'Download The PDF',
onPressed: () {
Navigator.pop(
context,
);
apiService
.getPdfDownload(
context,
plan.planId,
);
},
),
if (plan.statusValue ==
"Approved" &&
plan.forexId !=
null &&
plan.forexId !=
'')
IconButton(
icon: Icon(
Icons
.monetization_on_outlined,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'Download Forex PDF',
onPressed: () {
Navigator.pop(
context,
);
apiService.getForexPdfDownload(
context,
plan.forexId,
);
},
),
IconButton(
icon: const Icon(
Icons.comment,
color: Color(
0xFF475569,
),
size: 11,
),
tooltip:
'Trip Comments',
onPressed: () {
showDialog(
context:
context,
builder:
(
context,
) => CommentModalList(
// planId: plan.planId,
planId:
plan.planId.toString(),
layoutColorForUser:
layoutColor!,
role:
"Admin",
),
);
},
),
IconButton(
icon: const Icon(
Icons
.info_outlined,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'Trip Info',
onPressed: () {
showDialog(
context:
context,
builder:
(
context,
) => TripInformation(
// planId: plan.planId,
planId:
plan.planId.toString(),
layoutColorForUser:
layoutColor!,
),
);
},
),
],
),
),
),
],
),
],
),
),
],
);
}).toList(),
),
),
),
);
},
);
Widget buildMobileCardView(List<Plan> paginatedPlans) {
return ListView.builder(
itemCount: paginatedPlans.length,
itemBuilder: (context, index) {
final plan = paginatedPlans[index];
return Card(
color: Colors.white,
margin: EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 3, // Control the strength of the shadow
// shadowColor: Colors.black54, // Softer shadow
// clipBehavior: Clip.antiAlias,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: [
// Container(
// // color: Colors.red.shade50,
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment:
// CrossAxisAlignment.center,
// children: [
// Image.asset(
// plan.tripType == 'Domestic'
// ? 'assets/images/IconsImg/Domestic_new.png'
// : 'assets/images/IconsImg/International_new.png',
// // width: 65,
// height:
// plan.tripType == 'Domestic'
// ? 37
// : 40,
// ),
// ],
// ),
// ),
// SizedBox(width: 10),
Expanded(
child: Container(
// color: Colors.yellow.shade50,
child: Column(
children: [
Row(
// mainAxisAlignment:
// MainAxisAlignment.spaceBetween,
children: [
Container(
// color: Colors.red.shade50,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Image.asset(
plan.tripType == 'Domestic'
? 'assets/images/IconsImg/Domestic_new.png'
: 'assets/images/IconsImg/International_new.png',
// width: 65,
height:
plan.tripType ==
'Domestic'
? 28
: 30,
),
],
),
),
SizedBox(width: 10),
Expanded(
flex: 1,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'${plan.tripTitle}',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight:
FontWeight.w500,
),
),
],
),
),
SizedBox(width: 10),
Column(
children: [
// PlanPopupMenu(
// plan: plan,
// deletePlan: deletePlan,
// apiService: apiService,
// layoutColor: layoutColor,
// ),
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder:
(context) => [
CustomPopupMenuEntry(
child: Container(
padding:
EdgeInsets.symmetric(
horizontal: 1,
vertical: 1,
// horizontal: 8,
// vertical: 8,
),
child: Row(
mainAxisSize:
MainAxisSize
.min,
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
IconButton(
icon: Icon(
Icons
.remove_red_eye,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'View The Trip Details',
onPressed: () {
Navigator.pop(
context,
); // Close popup manually
ApiService().viewPlan(
context,
plan.planId,
isViewMode:
true,
);
},
),
if (plan.statusValue ==
"Pending Approval" ||
plan.statusValue ==
"Partially Approved")
IconButton(
icon: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
height:
15,
),
tooltip:
'Edit The Trip Details',
onPressed: () {
Navigator.pop(
context,
);
ApiService().viewPlan(
context,
plan.planId,
isViewMode:
false,
);
},
),
IconButton(
icon: Icon(
Icons
.cancel_rounded,
size: 18,
),
tooltip:
'Cancellation The Trip Details',
onPressed: () {
Navigator.pop(
context,
);
deletePlan(
plan.planId,
);
},
),
IconButton(
icon: Icon(
Icons
.download,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'Download The PDF',
onPressed: () {
Navigator.pop(
context,
);
apiService.getPdfDownload(
context,
plan.planId,
);
},
),
if (plan.statusValue ==
"Approved" &&
plan.forexId !=
null &&
plan.forexId !=
'')
IconButton(
icon: Icon(
Icons
.monetization_on_outlined,
color: Color(
0xFF475569,
),
size: 18,
),
tooltip:
'Download Forex PDF',
onPressed: () {
Navigator.pop(
context,
);
apiService.getForexPdfDownload(
context,
plan.forexId,
);
},
),
IconButton(
icon: const Icon(
Icons
.comment,
color: Color(
0xFF475569,
),
size: 11,
),
tooltip:
'Trip Comments',
onPressed: () {
showDialog(
context:
context,
builder:
(
context,
) => CommentModalList(
// planId: plan.planId,
planId:
plan.planId.toString(),
layoutColorForUser:
layoutColor!,
role:
"Admin",
),
);
},
),
IconButton(
icon: const Icon(
Icons
.info_outline_rounded,
color: Color(
0xFF475569,
),
size: 20,
),
tooltip:
'Trip Info',
onPressed: () {
showDialog(
context:
context,
builder:
(
context,
) => TripInformation(
// planId: plan.planId,
planId:
plan.planId.toString(),
layoutColorForUser:
layoutColor!,
),
);
},
),
],
),
),
),
],
),
],
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
// plan.userName.isNotEmpty
// ? plan.userName
// : '${plan.travellerName}',
'${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} (${plan.employeeCode ?? 'No Data'})',
style: GoogleFonts.poppins(
fontSize: 10,
color: Colors.black87,
),
// overflow: TextOverflow.ellipsis,
// maxLines: 2,
),
// Text(
// // plan.userName.isNotEmpty
// // ? plan.userName
// // : '${plan.travellerName}',
// // '(${plan.employeeCode ?? 'No Data'})',
// plan.employeeCode ??
// 'No Data',
// style: GoogleFonts.poppins(
// fontSize: 9,
// color: Colors.black87,
// ),
// ),
Text(
'${_formatDate(plan.createdOn)}',
style: GoogleFonts.poppins(
fontSize: 9,
color: Colors.grey,
),
),
],
),
),
Column(
mainAxisAlignment:
MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
GestureDetector(
onTap:
() => _showDetails(
plan.planId,
),
child: Container(
padding:
EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: getStatusColor(
plan.statusValue,
),
borderRadius:
BorderRadius.circular(
8,
),
),
child: Text(
plan.statusValue,
style: GoogleFonts.poppins(
color:
getStatusTextColor(
plan.statusValue,
),
fontSize: 9,
fontWeight:
FontWeight.w600,
),
),
),
),
],
),
],
),
],
),
),
),
],
),
// padding: EdgeInsets.symmetric(vertical: 12),
// child: Container(
// // color: Colors.yellow,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// // Status and Employee Code
// Row(
// mainAxisAlignment:
// MainAxisAlignment.spaceBetween,
// children: [
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// GestureDetector(
// onTap:
// () => _showDetails(plan.planId),
// child: Container(
// padding: EdgeInsets.symmetric(
// horizontal: 8,
// vertical: 4,
// ),
// decoration: BoxDecoration(
// color: getStatusColor(
// plan.statusValue,
// ),
// borderRadius:
// BorderRadius.circular(8),
// ),
// child: Text(
// plan.statusValue,
// style: TextStyle(
// color: getStatusTextColor(
// plan.statusValue,
// ),
// fontSize: 10,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
// ),
// ],
// ),
// Column(
// children: [
// // PlanPopupMenu(
// // plan: plan,
// // deletePlan: deletePlan,
// // apiService: apiService,
// // layoutColor: layoutColor,
// // ),
// PopupMenuButton<int>(
// color: Colors.white,
// padding: EdgeInsets.zero,
// offset: Offset(0, 30),
// icon: Icon(
// Icons.more_vert,
// color: Color(0xFF475569),
// size: 14,
// ),
// itemBuilder:
// (context) => [
// CustomPopupMenuEntry(
// child: Container(
// padding:
// EdgeInsets.symmetric(
// horizontal: 1,
// vertical: 1,
// // horizontal: 8,
// // vertical: 8,
// ),
// child: Row(
// mainAxisSize:
// MainAxisSize.min,
// mainAxisAlignment:
// MainAxisAlignment
// .center,
// children: [
// IconButton(
// icon: Icon(
// Icons
// .remove_red_eye,
// color: Color(
// 0xFF475569,
// ),
// size: 18,
// ),
// tooltip:
// 'View The Trip Details',
// onPressed: () {
// Navigator.pop(
// context,
// ); // Close popup manually
// ApiService.viewPlan(
// context,
// plan.planId,
// isViewMode:
// true,
// );
// },
// ),
// if (plan.statusValue ==
// "Pending Approval" ||
// plan.statusValue ==
// "Partially Approved")
// IconButton(
// icon: Image.asset(
// 'assets/images/IconsImg/edit.png',
// width: 20,
// height: 15,
// ),
// tooltip:
// 'Edit The Trip Details',
// onPressed: () {
// Navigator.pop(
// context,
// );
// ApiService.viewPlan(
// context,
// plan.planId,
// isViewMode:
// false,
// );
// },
// ),
// IconButton(
// icon: Icon(
// Icons
// .cancel_rounded,
// size: 18,
// ),
// tooltip:
// 'Cancellation The Trip Details',
// onPressed: () {
// Navigator.pop(
// context,
// );
// deletePlan(
// plan.planId,
// );
// },
// ),
// IconButton(
// icon: Icon(
// Icons.download,
// color: Color(
// 0xFF114D8B,
// ),
// size: 18,
// ),
// tooltip:
// 'Download The PDF',
// onPressed: () {
// Navigator.pop(
// context,
// );
// apiService
// .getPdfDownload(
// plan.planId,
// );
// },
// ),
// IconButton(
// icon: const Icon(
// Icons.comment,
// color: Color(
// 0xFF475569,
// ),
// size: 11,
// ),
// tooltip:
// 'Trip Comments',
// onPressed: () {
// showDialog(
// context:
// context,
// builder:
// (
// context,
// ) => CommentModalList(
// // planId: plan.planId,
// planId:
// plan.planId
// .toString(),
// layoutColorForUser:
// layoutColor!,
// role:
// "Admin",
// ),
// );
// },
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// ],
// ),
// ],
// ),
//
// SizedBox(height: 2),
// // Trip Id and Trip Name
// Row(
// children: [
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// ' ${plan.tripTitle}',
// style: TextStyle(
// fontSize: 12,
// fontFamily: "Inter",
// fontWeight: FontWeight.bold,
// ),
// ),
// ],
// ),
// ],
// ),
// SizedBox(height: 2),
// // Type and Created On
// Row(
// mainAxisAlignment:
// MainAxisAlignment.spaceBetween,
// children: [
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// ' ${plan.tripType}',
// style: TextStyle(
// fontSize: 9,
// color: Colors.black87,
// fontFamily: "Inter",
// ),
// ),
// ],
// ),
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// '${_formatDate(plan.createdOn)}',
// style: TextStyle(
// fontSize: 9,
// color: Colors.black87,
// fontFamily: "Inter",
// ),
// ),
// ],
// ),
// ],
// ),
// SizedBox(height: 3),
// // Name
// Row(
// children: [
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// plan.userName.isNotEmpty
// ? '${plan.userName}'
// : '${plan.travellerName}',
// style: TextStyle(
// fontSize: 9,
// fontFamily: "Inter",
// color: Colors.black87,
// ),
// ),
// ],
// ),
// Spacer(),
// Column(
// crossAxisAlignment:
// CrossAxisAlignment.start,
// children: [
// Text(
// plan.employeeCode ?? 'No Data',
// style: TextStyle(
// fontSize: 9,
// fontFamily: "Inter",
// color: Colors.black87,
// ),
// ),
// ],
// ),
// ],
// ),
// // Actions
// ],
// ),
// ),
),
);
},
);
}
return Expanded(
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child:
isDesktop
? (searchController.text.isNotEmpty &&
filteredPlans.isEmpty
? Center(
child: Text(
"No Matches Found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
filteredPlans.isEmpty
? Center(
child: Text(
"No Matches Found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: buildMobileCardView(paginatedPlans)),
// child: isDesktop
// ? SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: table, // <-- your existing table
// )
// : buildMobileCardView(paginatedPlans),
),
PaginationControls(
currentPage: currentPage,
itemsPerPage: itemsPerPage,
totalItems: plans.length,
activeColor: layoutColor, // your theme color
onPageChanged: (page) {
setState(() {
currentPage = page;
});
},
onItemsPerPageChanged: (items) {
setState(() {
itemsPerPage = items;
currentPage = 0;
});
},
),
],
),
);
},
),
],
),
),
),
);
}
// Helper functions for colors
Color getStatusColor(String status) {
if (status == "Partially Approved") return Colors.yellow.shade100;
if (status == "Approved") return Colors.green.shade100;
if (status == "Completed") return Colors.green.shade500;
if (status == "Rejected") return Colors.red.shade100;
return Colors.grey.shade100;
}
Color getStatusTextColor(String status) {
if (status == "Partially Approved" ||
status == "Approved" ||
status == "Rejected") {
return Colors.black;
} else if (status == "Completed") {
return Colors.white;
}
return Colors.grey;
}
}