search for all pages
This commit is contained in:
parent
36975e716f
commit
a76464bbbc
BIN
assets/images/IconsImg/planPdf_icon.png
Normal file
BIN
assets/images/IconsImg/planPdf_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 900 B |
798
lib/Screens/allTrips/list_all_plans.dart
Normal file
798
lib/Screens/allTrips/list_all_plans.dart
Normal file
@ -0,0 +1,798 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:core';
|
||||||
|
import 'package:frontend/data/models/plan.dart';
|
||||||
|
import 'package:go_router/go_router.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 '../../services/apiService.dart';
|
||||||
|
import '../../utils/auth_utils.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 = 8;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getToken();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
initializeData();
|
||||||
|
loadInitialData();
|
||||||
|
|
||||||
|
// futurePlans.then((plans) {
|
||||||
|
// setState(() {
|
||||||
|
// allPlans = plans;
|
||||||
|
// filteredPlans = plans;
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
// futurePlans = fetchPlans();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
List<dynamic> plansJson = data['data'];
|
||||||
|
return plansJson.map((json) => Plan.fromJson(json)).toList();
|
||||||
|
} 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',
|
||||||
|
},
|
||||||
|
body: jsonEncode(planData), // Convert map to JSON
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print("Plan Deleted successfully!");
|
||||||
|
print("Response: ${response.body}");
|
||||||
|
initializeData();
|
||||||
|
} 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 {
|
||||||
|
try {
|
||||||
|
Map<String, dynamic> planData = await ApiService.getViewPlan(planId);
|
||||||
|
print("ViewAAA - $planData");
|
||||||
|
|
||||||
|
postPlanData(planData, planId);
|
||||||
|
} catch (e) {
|
||||||
|
print("Error fetching plan: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||||
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
// appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
||||||
|
// drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||||
|
appBar: const CustomAppBar(),
|
||||||
|
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(8),
|
||||||
|
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 yy hh:m a').format(dateTime);
|
||||||
|
} catch (e) {
|
||||||
|
return rawDate; // fallback if parsing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: isDesktop
|
||||||
|
? EdgeInsets.all(10.0)
|
||||||
|
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||||
|
// padding: const EdgeInsets.all(10),
|
||||||
|
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: Colors.white,
|
||||||
|
// color: Color(0xFFF7F7FB),
|
||||||
|
|
||||||
|
// color: Colors.amber,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(1.0),
|
||||||
|
child: Container(
|
||||||
|
// padding: const EdgeInsets.all(10.0),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// Divider(
|
||||||
|
// thickness: 0.2, // how "thick" the line is
|
||||||
|
// color: Colors.grey, // optional
|
||||||
|
// ),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text('All Trip List',
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF212121))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
Spacer(),
|
||||||
|
|
||||||
|
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: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
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) {
|
||||||
|
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),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text("Oops!",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.redAccent)),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text("No Plans Available For This User",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey)),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text("Please Create Plan",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, color: Colors.grey)),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List<Plan> plans = snapshot.data!;
|
||||||
|
|
||||||
|
List<Plan> plans =
|
||||||
|
filteredPlans.isNotEmpty ? filteredPlans : allPlans;
|
||||||
|
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 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: const [
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Trip Id',
|
||||||
|
style: TextStyle(
|
||||||
|
// color: Colors.grey,
|
||||||
|
// color: Color(0xFF9E9DBD),
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Employee Code',
|
||||||
|
style: TextStyle(
|
||||||
|
// color: Color(0xFF9E9DBD),
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Trip Name',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Traveller',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Trip Type',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Created On',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Status',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Actions',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
],
|
||||||
|
rows: paginatedPlans.map((plan) {
|
||||||
|
return DataRow(cells: [
|
||||||
|
DataCell(Text(plan.planId,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.employeeCode ?? "no data",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.tripTitle,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
softWrap: true,
|
||||||
|
overflow: TextOverflow.ellipsis)),
|
||||||
|
DataCell(Text(
|
||||||
|
plan.userName.isNotEmpty
|
||||||
|
? plan.userName
|
||||||
|
: plan.travellerName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.tripType,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(_formatDate(plan.createdOn),
|
||||||
|
// plan.createdOn,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(
|
||||||
|
Container(
|
||||||
|
width: double
|
||||||
|
.infinity, // Set your desired fixed size (equal width and height)
|
||||||
|
height: 25,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: getStatusColor(plan.statusValue),
|
||||||
|
// color: plan.statusValue ==
|
||||||
|
// "Partially Approved"
|
||||||
|
// ? Colors.yellow.shade100
|
||||||
|
// : plan.statusValue == "Approved"
|
||||||
|
// ? Colors.green.shade100
|
||||||
|
// : plan.statusValue == "Completed"
|
||||||
|
// ? Colors.green.shade500
|
||||||
|
// : plan.statusValue == "Rejected"
|
||||||
|
// ? Colors.red.shade100
|
||||||
|
// : Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
plan.statusValue,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
getStatusTextColor(plan.statusValue),
|
||||||
|
// color: (plan.statusValue ==
|
||||||
|
// "Partially Approved" ||
|
||||||
|
// plan.statusValue == "Approved" ||
|
||||||
|
// plan.statusValue == "Rejected")
|
||||||
|
// ? Colors.black
|
||||||
|
// : plan.statusValue == "Completed"
|
||||||
|
// ? Colors.white
|
||||||
|
// : Colors.grey,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DataCell(Row(children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.remove_red_eye,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () => ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: true)),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: false),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/IconsImg/edit.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => deletePlan(plan.planId),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/IconsImg/delete.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.download,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
apiService.getPdfDownload(plan.planId);
|
||||||
|
}),
|
||||||
|
])),
|
||||||
|
]);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
isDesktop
|
||||||
|
? table
|
||||||
|
: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: table,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
// Previous button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back_ios_new,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: currentPage > 0
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage--;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Page number text
|
||||||
|
Text(
|
||||||
|
'Page ${currentPage + 1}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Next button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: (currentPage + 1) * itemsPerPage <
|
||||||
|
plans.length
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,6 +31,12 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
Color? layoutColor;
|
Color? layoutColor;
|
||||||
Color? bodyColor;
|
Color? bodyColor;
|
||||||
|
|
||||||
|
int currentPage = 0;
|
||||||
|
int itemsPerPage = 8;
|
||||||
|
List<Plan> allPlans = [];
|
||||||
|
List<Plan> filteredPlans = [];
|
||||||
|
TextEditingController searchController = TextEditingController();
|
||||||
|
|
||||||
late List<dynamic> plansJson;
|
late List<dynamic> plansJson;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -85,6 +91,15 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
futurePlans = fetchPlans();
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,49 +208,6 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<Map<String, dynamic>> getViewPlan(String planId) async {
|
|
||||||
// final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
|
||||||
// print("API URL: $apiUrldata");
|
|
||||||
// // final token = await getToken();
|
|
||||||
//
|
|
||||||
// if (token == null) {
|
|
||||||
// throw Exception('Token not found. Please log in.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final response = await http.put(
|
|
||||||
// Uri.parse(apiUrldata),
|
|
||||||
// headers: {
|
|
||||||
// 'Authorization': 'Bearer $token', // Add token here
|
|
||||||
// 'Content-Type': 'application/json',
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// if (response.statusCode == 200) {
|
|
||||||
// final Map<String, dynamic>? resData = json.decode(response.body);
|
|
||||||
//
|
|
||||||
// return resData?["data"];
|
|
||||||
// } else {
|
|
||||||
// throw Exception('Failed to load plans');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<Map<String, dynamic>> getViewPlan(String planId, List plansJson) async {
|
|
||||||
// try {
|
|
||||||
// final plan = plansJson.firstWhere(
|
|
||||||
// (item) => item["plan_id"].toString() == planId,
|
|
||||||
// orElse: () => null,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// if (plan == null) {
|
|
||||||
// throw Exception("Plan with ID $planId not found.");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Map<String, dynamic>.from(plan);
|
|
||||||
// } catch (e) {
|
|
||||||
// throw Exception("Error finding plan: $e");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
void deletePlan(String planId) async {
|
void deletePlan(String planId) async {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> planData = await getViewPlan(planId);
|
Map<String, dynamic> planData = await getViewPlan(planId);
|
||||||
@ -301,6 +273,24 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
print("filteredPlans: $filteredPlans");
|
||||||
|
}
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||||
@ -356,30 +346,18 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
// margin: isDesktop
|
margin: isDesktop
|
||||||
// ? EdgeInsets.all(10.0)
|
? EdgeInsets.all(10.0)
|
||||||
// : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||||
// padding: const EdgeInsets.all(10),
|
// padding: const EdgeInsets.all(10),
|
||||||
height: isDesktop
|
height: isDesktop
|
||||||
? MediaQuery.of(context).size.height * 0.98
|
? MediaQuery.of(context).size.height * 0.98
|
||||||
: MediaQuery.of(context).size.height,
|
: MediaQuery.of(context).size.height,
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// border: isDesktop
|
|
||||||
// ? Border.all(
|
|
||||||
// width: 2,
|
|
||||||
// color: Colors.white,
|
|
||||||
// // color: Color(0xFFF7F7FB),
|
|
||||||
// )
|
|
||||||
// : null,
|
|
||||||
// color: Colors.white,
|
|
||||||
// // color: Color(0xFFF7F7FB),
|
|
||||||
//
|
|
||||||
// // color: Colors.amber,
|
|
||||||
// ),
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(1.0),
|
padding: const EdgeInsets.all(1.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(1.0),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@ -389,47 +367,110 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
// color: Colors.grey, // optional
|
// color: Colors.grey, // optional
|
||||||
// ),
|
// ),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Text('List For Approval',
|
const Text('List For Approval',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF212121))),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Container(
|
if (isDesktop)
|
||||||
width: MediaQuery.of(context).size.width * 0.2,
|
SizedBox(
|
||||||
// or use Flexible
|
width: MediaQuery.of(context).size.width * 0.19,
|
||||||
child: TextField(
|
),
|
||||||
onChanged: (query) {},
|
|
||||||
decoration: InputDecoration(
|
if (isDesktop)
|
||||||
hintText: "Search for a plan",
|
Container(
|
||||||
hintStyle:
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
TextStyle(fontSize: 14, color: Color(0xFF9E9DBD)),
|
height: 40,
|
||||||
prefixIcon:
|
child: TextField(
|
||||||
Icon(Icons.search, color: Color(0xFF9E9DBD)),
|
controller: searchController,
|
||||||
border: OutlineInputBorder(
|
onChanged: filterPlans,
|
||||||
borderRadius: BorderRadius.circular(8),
|
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),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
style: TextStyle(
|
||||||
borderSide: BorderSide(
|
fontSize: 12,
|
||||||
color: Colors.grey.shade300, width: 0.5),
|
fontFamily: "Inter",
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
// borderRadius: BorderRadius.circular(8),
|
|
||||||
borderSide:
|
|
||||||
BorderSide(color: Colors.blueAccent, width: 1),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
// SizedBox(width: 16),
|
// SizedBox(width: 16),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (!isDesktop)
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
isDesktop
|
||||||
|
? SizedBox.shrink()
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
height: 40,
|
||||||
|
child: TextField(
|
||||||
|
controller: searchController,
|
||||||
|
onChanged: filterPlans,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "Search for a plan",
|
||||||
|
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: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// SizedBox(width: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
FutureBuilder<List<Plan>>(
|
FutureBuilder<List<Plan>>(
|
||||||
future: futurePlans,
|
future: futurePlans,
|
||||||
@ -472,10 +513,16 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Plan> plans = snapshot.data!;
|
List<Plan> plans =
|
||||||
|
filteredPlans.isNotEmpty ? filteredPlans : allPlans;
|
||||||
plans.sort((a, b) =>
|
plans.sort((a, b) =>
|
||||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||||
|
|
||||||
|
List<Plan> paginatedPlans = plans
|
||||||
|
.skip(currentPage * itemsPerPage)
|
||||||
|
.take(itemsPerPage)
|
||||||
|
.toList();
|
||||||
|
|
||||||
Widget table = LayoutBuilder(
|
Widget table = LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||||
@ -493,50 +540,57 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
),
|
),
|
||||||
columns: const [
|
columns: const [
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Plan Id',
|
label: Text('Trip Id',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontSize: 14,
|
fontFamily: "Inter",
|
||||||
fontFamily: "Archivo",
|
fontWeight: FontWeight.w600))),
|
||||||
fontWeight: FontWeight.bold))),
|
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Planned User',
|
label: Text('Employee Code',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
// color: Color(0xFF9E9DBD),
|
||||||
fontFamily: "Archivo",
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.bold))),
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Title',
|
label: Text('Trip Name',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Traveller',
|
||||||
|
style: TextStyle(
|
||||||
|
// color: Color(0xFF9E9DBD),
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Type',
|
label: Text('Trip Type',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Created On',
|
label: Text('Created On',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Status',
|
label: Text('Status',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Actions',
|
label: Text('Actions',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
],
|
],
|
||||||
rows: plans.map((plan) {
|
rows: paginatedPlans.map((plan) {
|
||||||
return DataRow(cells: [
|
return DataRow(cells: [
|
||||||
DataCell(Text(plan.planId,
|
DataCell(Text(plan.planId,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -549,27 +603,32 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
: plan.travellerName,
|
: plan.travellerName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.employeeCode ?? "no data",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.tripType,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(plan.tripTitle,
|
DataCell(Text(plan.tripTitle,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
),
|
),
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis)),
|
overflow: TextOverflow.ellipsis)),
|
||||||
DataCell(Text(plan.tripType,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
fontFamily: "Archivo",
|
|
||||||
))),
|
|
||||||
DataCell(Text(
|
DataCell(Text(
|
||||||
|
|
||||||
// plan.createdOn,
|
// plan.createdOn,
|
||||||
_formatDate(plan.createdOn),
|
_formatDate(plan.createdOn),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(
|
DataCell(
|
||||||
Container(
|
Container(
|
||||||
@ -603,6 +662,7 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
? Colors.white
|
? Colors.white
|
||||||
: Colors.grey,
|
: Colors.grey,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -646,6 +706,16 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
width: 20,
|
width: 20,
|
||||||
height: 15),
|
height: 15),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.download,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
apiService.getPdfDownload(plan.planId);
|
||||||
|
}),
|
||||||
])),
|
])),
|
||||||
]);
|
]);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@ -655,15 +725,68 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: isDesktop
|
child: Column(
|
||||||
? table
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
: SingleChildScrollView(
|
children: [
|
||||||
scrollDirection: Axis.horizontal,
|
isDesktop
|
||||||
child: SingleChildScrollView(
|
? table
|
||||||
scrollDirection: Axis.vertical,
|
: SingleChildScrollView(
|
||||||
child: table,
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: table,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
// Previous button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back_ios_new,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: currentPage > 0
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage--;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Page number text
|
||||||
|
Text(
|
||||||
|
'Page ${currentPage + 1}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Next button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: (currentPage + 1) * itemsPerPage <
|
||||||
|
plans.length
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
|
||||||
|
import '../../services/apiService.dart';
|
||||||
import '../../widgets/custom_text_field.dart';
|
import '../../widgets/custom_text_field.dart';
|
||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
@ -24,8 +25,14 @@ class AccomodationScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AccomodationScreenState extends State<AccomodationScreen> {
|
class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||||
|
ApiService apiService = ApiService();
|
||||||
|
|
||||||
|
// late Map<String, String> countryMap;
|
||||||
|
Map<String, String> countryMap = {};
|
||||||
|
|
||||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightFirstToDestinationNotifier;
|
||||||
|
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@ -95,6 +102,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_addFocusListener(
|
_addFocusListener(
|
||||||
_destinationFocusNode, (focus) => _destinationFocused = focus);
|
_destinationFocusNode, (focus) => _destinationFocused = focus);
|
||||||
_addFocusListener(
|
_addFocusListener(
|
||||||
@ -124,11 +132,21 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
|
|
||||||
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
flightFirstTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||||
|
flightFirstToDestinationNotifier = ValueNotifier<String?>(null);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
loadCountryList();
|
||||||
|
|
||||||
final result = getFlightTripDateRange(widget.flightData);
|
final result = getFlightTripDateRange(widget.flightData);
|
||||||
|
|
||||||
|
print("Rs: $result");
|
||||||
|
|
||||||
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
flightFirstTripDateNotifier.value = result['firstTripDate'];
|
||||||
flightLastTripDateNotifier.value = result['lastTripDate'];
|
flightLastTripDateNotifier.value = result['lastTripDate'];
|
||||||
|
flightFirstToDestinationNotifier.value = result['firstToDestination'];
|
||||||
|
|
||||||
|
print(
|
||||||
|
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ");
|
||||||
|
|
||||||
// ✅ Only set controller after value is updated
|
// ✅ Only set controller after value is updated
|
||||||
final parsedDate =
|
final parsedDate =
|
||||||
@ -190,10 +208,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
|
|
||||||
final firstTrip = allTrips.first;
|
final firstTrip = allTrips.first;
|
||||||
final lastTrip = allTrips.last;
|
final lastTrip = allTrips.last;
|
||||||
|
final firstTripToDestination = allTrips.first;
|
||||||
|
|
||||||
|
print("allTrips - $allTrips");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'firstTripDate': firstTrip['date'],
|
'firstTripDate': firstTrip['date'],
|
||||||
'lastTripDate': lastTrip['date'],
|
'lastTripDate': lastTrip['date'],
|
||||||
|
'firstToDestination': firstTripToDestination['to_place']
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +291,37 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
|||||||
return errorMessages.isEmpty; // Valid if there are no errors
|
return errorMessages.isEmpty; // Valid if there are no errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadCountryList() async {
|
||||||
|
final result = await apiService.fetchFlightsCountryList();
|
||||||
|
|
||||||
|
print("ResultCountry : $result");
|
||||||
|
|
||||||
|
// Create a map: Country_Code -> "City, Airport"
|
||||||
|
Map<String, String> tempCountryMap = {};
|
||||||
|
|
||||||
|
for (var country in result) {
|
||||||
|
String city = country['City'] ?? '';
|
||||||
|
String airport = country['Airport'] ?? '';
|
||||||
|
String displayName = '${country['City']}';
|
||||||
|
// String displayName = '${country['City']} | ${country['Airport']}';
|
||||||
|
|
||||||
|
tempCountryMap[country['Code']] = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
countryMap = tempCountryMap; // Update the map
|
||||||
|
_updateDestinationCity();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateDestinationCity() {
|
||||||
|
final toDestination = flightFirstToDestinationNotifier.value ?? '';
|
||||||
|
final toDestinationCity = countryMap[toDestination] ?? "";
|
||||||
|
if (toDestinationCity != '') {
|
||||||
|
_destinationController.text = toDestinationCity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void handleSave() {
|
void handleSave() {
|
||||||
print("Handle Save accomadationData $accomadationData");
|
print("Handle Save accomadationData $accomadationData");
|
||||||
|
|
||||||
|
|||||||
@ -603,7 +603,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(28.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(children: _buildAccomadtionForm(isDesktop)),
|
child: Column(children: _buildAccomadtionForm(isDesktop)),
|
||||||
),
|
),
|
||||||
@ -1126,7 +1126,7 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Class $index *",
|
selectedTripType == "Oneway" ? "Class *" : "Class $index *",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -1203,12 +1203,27 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
? countryMap[selectedFrom[index]]
|
? countryMap[selectedFrom[index]]
|
||||||
: null,
|
: null,
|
||||||
popupProps: PopupProps.menu(
|
popupProps: PopupProps.menu(
|
||||||
showSearchBox: true,
|
fit: FlexFit.loose,
|
||||||
|
constraints: BoxConstraints(maxHeight: 220),
|
||||||
|
showSearchBox: true, // Enables search functionality
|
||||||
searchFieldProps: TextFieldProps(
|
searchFieldProps: TextFieldProps(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Search ...",
|
hintText: "Search...",
|
||||||
contentPadding:
|
contentPadding: EdgeInsets.symmetric(
|
||||||
EdgeInsets.symmetric(horizontal: 10),
|
horizontal: 10, vertical: 1),
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 12)),
|
||||||
|
menuProps: MenuProps(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, item, isSelected) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8.0, vertical: 6.0),
|
||||||
|
child: Text(
|
||||||
|
item,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize:
|
||||||
|
13), // 👈 Set your desired text size here
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1297,12 +1312,27 @@ class _FlightScreenState extends State<FlightScreen> {
|
|||||||
? countryMap[selectedTo[index]]
|
? countryMap[selectedTo[index]]
|
||||||
: null,
|
: null,
|
||||||
popupProps: PopupProps.menu(
|
popupProps: PopupProps.menu(
|
||||||
showSearchBox: true,
|
fit: FlexFit.loose,
|
||||||
|
constraints: BoxConstraints(maxHeight: 220),
|
||||||
|
showSearchBox: true, // Enables search functionality
|
||||||
searchFieldProps: TextFieldProps(
|
searchFieldProps: TextFieldProps(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Search ...",
|
hintText: "Search...",
|
||||||
contentPadding:
|
contentPadding: EdgeInsets.symmetric(
|
||||||
EdgeInsets.symmetric(horizontal: 10),
|
horizontal: 10, vertical: 1),
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 12)),
|
||||||
|
menuProps: MenuProps(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
itemBuilder: (context, item, isSelected) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8.0, vertical: 6.0),
|
||||||
|
child: Text(
|
||||||
|
item,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize:
|
||||||
|
13), // 👈 Set your desired text size here
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import 'package:responsive_builder/responsive_builder.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import '../../config/apiUrl.dart';
|
import '../../config/apiUrl.dart';
|
||||||
|
import '../../utils/auth_utils.dart';
|
||||||
import '../../widgets/custom_text_field.dart';
|
import '../../widgets/custom_text_field.dart';
|
||||||
import '../../widgets/custom_text_forex.dart';
|
import '../../widgets/custom_text_forex.dart';
|
||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
@ -41,6 +42,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
|
||||||
|
late String? userCardNumber;
|
||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
bool isChecked = false; // State variable for checkbox
|
bool isChecked = false; // State variable for checkbox
|
||||||
|
|
||||||
@ -214,6 +217,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
bool isCardChecked = data["have_card"] == "1";
|
bool isCardChecked = data["have_card"] == "1";
|
||||||
if (isCardChecked) {
|
if (isCardChecked) {
|
||||||
requiredFields.add("delivery_location");
|
requiredFields.add("delivery_location");
|
||||||
|
} else {
|
||||||
|
requiredFields.add("card_number");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check validation for each field
|
// Check validation for each field
|
||||||
@ -341,8 +346,18 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUpdatedField() {
|
void handleUpdatedField() async {
|
||||||
// Set the selected value if available
|
// Set the selected value if available
|
||||||
|
userCardNumber = await getForexCardNumber();
|
||||||
|
// userCardNumber = "CD7909043";
|
||||||
|
print("userCardNumber - $userCardNumber");
|
||||||
|
|
||||||
|
if (widget.selectedItem == null &&
|
||||||
|
textControllers["_cardNumber"]?.text == "") {
|
||||||
|
print("userCardNumber11 - $userCardNumber");
|
||||||
|
textControllers["_cardNumber"]?.text = userCardNumber ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
if (widget.selectedItem != null) {
|
if (widget.selectedItem != null) {
|
||||||
print("UPDATAED SELECTION");
|
print("UPDATAED SELECTION");
|
||||||
|
|
||||||
@ -365,6 +380,12 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
|
selectedPerdiemAmount = widget.selectedItem!["perdiem_amount"] as String?;
|
||||||
isChecked =
|
isChecked =
|
||||||
widget.selectedItem!["have_card"] == "1"; // Convert string to bool
|
widget.selectedItem!["have_card"] == "1"; // Convert string to bool
|
||||||
|
|
||||||
|
// if (textControllers["_cardNumber"] != null) {
|
||||||
|
// print("userCardNumber11 - $userCardNumber");
|
||||||
|
// textControllers["_cardNumber"]!.text = userCardNumber ?? '';
|
||||||
|
// }
|
||||||
|
|
||||||
_onFieldChangedForOthers();
|
_onFieldChangedForOthers();
|
||||||
setState(() {}); // Update the UI
|
setState(() {}); // Update the UI
|
||||||
|
|
||||||
@ -604,8 +625,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
),
|
),
|
||||||
...buildResponsiveRow(_buildSecondRow(isDesktop)),
|
...buildResponsiveRow(_buildSecondRow(isDesktop)),
|
||||||
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
|
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
|
||||||
...buildResponsiveRow(_buildFprexCard(isDesktop)),
|
...buildResponsiveRow(_buildForexCard(isDesktop)),
|
||||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
|
||||||
...buildResponsiveRow(_buildCommetsRow(isDesktop)),
|
...buildResponsiveRow(_buildCommetsRow(isDesktop)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -1361,7 +1382,9 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
if (isDesktop)
|
if (isDesktop)
|
||||||
Spacer()
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.035,
|
||||||
|
)
|
||||||
else
|
else
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
@ -1413,7 +1436,9 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
if (isDesktop)
|
if (isDesktop)
|
||||||
Spacer()
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.035,
|
||||||
|
)
|
||||||
else
|
else
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8,
|
height: 8,
|
||||||
@ -1453,12 +1478,6 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
if (isDesktop)
|
|
||||||
Spacer()
|
|
||||||
else
|
|
||||||
SizedBox(
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
// Column(
|
// Column(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// children: [
|
// children: [
|
||||||
@ -1499,96 +1518,6 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
// ),
|
// ),
|
||||||
// ],
|
// ],
|
||||||
// ),
|
// ),
|
||||||
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Card Number",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF575A74)),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
CustomTextFieldItnerarySubWrapper(
|
|
||||||
isFocused: focusStates["_cardNumber"] ?? false,
|
|
||||||
isDesktop: isDesktop,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 40,
|
|
||||||
child: TextField(
|
|
||||||
enabled: !isChecked,
|
|
||||||
focusNode: focusNodes["_cardNumber"],
|
|
||||||
controller: textControllers["_cardNumber"],
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: "Card Number",
|
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (errorMessages["card_number"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
"Required",
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildThirdRow(bool isDesktop) {
|
|
||||||
return [
|
|
||||||
isChecked
|
|
||||||
? Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Delivery Location",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF575A74)),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
CustomTextFieldWrapper(
|
|
||||||
isFocused: focusStates["_deliveryLocation"] ??
|
|
||||||
false, // Dropdown doesn't use focus
|
|
||||||
isDesktop: isDesktop,
|
|
||||||
width: isDesktop
|
|
||||||
? MediaQuery.of(context).size.width * 0.330
|
|
||||||
: MediaQuery.of(context).size.width * 0.66,
|
|
||||||
child: TextField(
|
|
||||||
focusNode: focusNodes["_deliveryLocation"],
|
|
||||||
controller: textControllers["_deliveryLocation"],
|
|
||||||
maxLines: 3,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Location",
|
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 4),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (errorMessages["delivery_location"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
"Required",
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: SizedBox.shrink()
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1644,36 +1573,141 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildFprexCard(bool isDesktop) {
|
List<Widget> _buildForexCard(bool isDesktop) {
|
||||||
return [
|
return [
|
||||||
Row(
|
!isChecked
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
? Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Checkbox(
|
children: [
|
||||||
value: isChecked,
|
Text(
|
||||||
side: BorderSide(
|
"Card Number",
|
||||||
color: Colors.grey, // Change border color
|
style: TextStyle(
|
||||||
width: 1, // Adjust thickness
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldItnerarySubWrapper(
|
||||||
|
isFocused: focusStates["_cardNumber"] ?? false,
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: TextField(
|
||||||
|
enabled: !isChecked,
|
||||||
|
focusNode: focusNodes["_cardNumber"],
|
||||||
|
controller: textControllers["_cardNumber"],
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: "Card Number",
|
||||||
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessages["card_number"] != null) ...[
|
||||||
|
SizedBox(height: 5), // Space before error message
|
||||||
|
Text(
|
||||||
|
"Required",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Delivery Location",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldWrapper(
|
||||||
|
isFocused: focusStates["_deliveryLocation"] ??
|
||||||
|
false, // Dropdown doesn't use focus
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
width: isDesktop
|
||||||
|
? MediaQuery.of(context).size.width * 0.34
|
||||||
|
: null,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 35,
|
||||||
|
child: TextField(
|
||||||
|
focusNode: focusNodes["_deliveryLocation"],
|
||||||
|
controller: textControllers["_deliveryLocation"],
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Location",
|
||||||
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessages["delivery_location"] != null) ...[
|
||||||
|
SizedBox(height: 5), // Space before error message
|
||||||
|
Text(
|
||||||
|
"Required",
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
onChanged: (bool? value) {
|
if (isDesktop)
|
||||||
setState(() {
|
SizedBox(
|
||||||
isChecked = value!;
|
width: MediaQuery.of(context).size.width * 0.035,
|
||||||
if (isChecked) {
|
)
|
||||||
textControllers["_cardNumber"]
|
else
|
||||||
?.clear(); // Clear the value when isChecked is true
|
SizedBox(
|
||||||
}
|
height: 8,
|
||||||
});
|
),
|
||||||
},
|
Column(
|
||||||
),
|
children: [
|
||||||
Text(
|
Row(
|
||||||
"Check If You Don't Have a forex Account",
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
style: TextStyle(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
fontSize: 12,
|
children: [
|
||||||
fontWeight: FontWeight.w600,
|
Container(
|
||||||
color: Color(0xFF575A74)),
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: 0.8,
|
||||||
|
child: Checkbox(
|
||||||
|
value: isChecked,
|
||||||
|
activeColor: Color(0xFF114D8B),
|
||||||
|
// checkColor: Color(0xFF114D8B),
|
||||||
|
side: BorderSide(
|
||||||
|
color: Colors.grey, // Change border color
|
||||||
|
width: 1, // Adjust thickness
|
||||||
|
),
|
||||||
|
onChanged: (bool? value) {
|
||||||
|
setState(() {
|
||||||
|
isChecked = value!;
|
||||||
|
if (isChecked) {
|
||||||
|
textControllers["_cardNumber"]
|
||||||
|
?.clear(); // Clear the value when isChecked is true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Check If You Don't Have a forex Account",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFF575A74)),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -506,7 +506,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Destination",
|
"City",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@ -247,7 +247,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
isCountryLoading = true;
|
isCountryLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final result = await apiService.fetchFlightsCountryList();
|
final result = await apiService.fetchTrainCountryList();
|
||||||
|
|
||||||
print("ResultCountry : $result");
|
print("ResultCountry : $result");
|
||||||
|
|
||||||
@ -255,11 +255,9 @@ class _TrainScreenState extends State<TrainScreen> {
|
|||||||
Map<String, String> tempCountryMap = {};
|
Map<String, String> tempCountryMap = {};
|
||||||
|
|
||||||
for (var country in result) {
|
for (var country in result) {
|
||||||
String city = country['City'] ?? '';
|
String displayName = '${country['Station_Name']} ';
|
||||||
String airport = country['Airport'] ?? '';
|
|
||||||
String displayName = '${country['City']} - ${country['Airport']}';
|
|
||||||
|
|
||||||
tempCountryMap[country['Code']] = displayName;
|
tempCountryMap[country['Station_Code']] = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
|
||||||
|
import '../../services/apiService.dart';
|
||||||
import '../../widgets/custom_text_field.dart';
|
import '../../widgets/custom_text_field.dart';
|
||||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||||
|
|
||||||
@ -32,8 +33,14 @@ class VisaScreen extends StatefulWidget {
|
|||||||
class _VisaScreenState extends State<VisaScreen> {
|
class _VisaScreenState extends State<VisaScreen> {
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
ApiService apiService = ApiService();
|
||||||
|
|
||||||
|
// late Map<String, String> countryMap;
|
||||||
|
Map<String, String> countryMap = {};
|
||||||
|
|
||||||
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
late ValueNotifier<String?> flightFirstTripDateNotifier;
|
||||||
late ValueNotifier<String?> flightLastTripDateNotifier;
|
late ValueNotifier<String?> flightLastTripDateNotifier;
|
||||||
|
late ValueNotifier<String?> flightFirstToDestinationNotifier;
|
||||||
|
|
||||||
Map<String, String?> selectedValues = {};
|
Map<String, String?> selectedValues = {};
|
||||||
|
|
||||||
@ -136,6 +143,40 @@ class _VisaScreenState extends State<VisaScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Future<void> loadCountryList() async {
|
||||||
|
// final result = await apiService.fetchFlightsCountryList();
|
||||||
|
//
|
||||||
|
// print("ResultCountry : $result");
|
||||||
|
//
|
||||||
|
// // Create a map: Country_Code -> "City, Airport"
|
||||||
|
// Map<String, String> tempCountryMap = {};
|
||||||
|
//
|
||||||
|
// for (var country in result) {
|
||||||
|
// String city = country['City'] ?? '';
|
||||||
|
// String airport = country['Airport'] ?? '';
|
||||||
|
// String displayName = '${country['City']}';
|
||||||
|
// // String displayName = '${country['City']} | ${country['Airport']}';
|
||||||
|
//
|
||||||
|
// tempCountryMap[country['Code']] = displayName;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// setState(() {
|
||||||
|
// countryMap = tempCountryMap; // Update the map
|
||||||
|
// _updateDestinationCity();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
void _updateDestinationCity() {
|
||||||
|
final toDestination = flightFirstToDestinationNotifier.value ?? '';
|
||||||
|
|
||||||
|
// final toDestinationCity = countryMap[toDestination] ?? "";
|
||||||
|
if (toDestination != '') {
|
||||||
|
// _destinationController.text = toDestinationCity;
|
||||||
|
|
||||||
|
selectedCountry = widget.selectedItem!["toDestination"] as String?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, String?> getFlightTripDateRange(
|
Map<String, String?> getFlightTripDateRange(
|
||||||
List<Map<String, dynamic>> flightData) {
|
List<Map<String, dynamic>> flightData) {
|
||||||
final allTrips = flightData
|
final allTrips = flightData
|
||||||
|
|||||||
@ -279,7 +279,7 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Planned Trips",
|
"Sector",
|
||||||
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
|
style: TextStyle(fontSize: 11, fontFamily: "Archivo"),
|
||||||
)),
|
)),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@ -231,7 +231,7 @@ class TaxiListWidget extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Destination",
|
"City",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11, fontFamily: "Archivo"),
|
fontSize: 11, fontFamily: "Archivo"),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -71,7 +71,8 @@ class _TrainListWidgetState extends State<TrainListWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadCountryList() async {
|
Future<void> loadCountryList() async {
|
||||||
final result = await apiService.fetchFlightsCountryList();
|
// final result = await apiService.fetchFlightsCountryList();
|
||||||
|
final result = await apiService.fetchTrainCountryList();
|
||||||
|
|
||||||
print("ResultCountry : $result");
|
print("ResultCountry : $result");
|
||||||
|
|
||||||
@ -79,12 +80,11 @@ class _TrainListWidgetState extends State<TrainListWidget> {
|
|||||||
Map<String, String> tempCountryMap = {};
|
Map<String, String> tempCountryMap = {};
|
||||||
|
|
||||||
for (var country in result) {
|
for (var country in result) {
|
||||||
String city = country['City'] ?? '';
|
String displayName = '${country['Station_Name']}';
|
||||||
String airport = country['Airport'] ?? '';
|
|
||||||
String displayName = '${country['City']} - ${country['Airport']}';
|
|
||||||
// String displayName = '${country['City']} | ${country['Airport']}';
|
// String displayName = '${country['City']} | ${country['Airport']}';
|
||||||
|
|
||||||
tempCountryMap[country['Code']] = displayName;
|
// tempCountryMap[country['Code']] = displayName;
|
||||||
|
tempCountryMap[country['Station_Code']] = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -361,8 +361,7 @@ class _TrainListWidgetState extends State<TrainListWidget> {
|
|||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(
|
child: Text("$fromPlaceCountry - $toPlaceCountry",
|
||||||
"$fromPlaceCountry (from) - (to) $toPlaceCountry",
|
|
||||||
|
|
||||||
// "${item["from_station"]!} - ${(item["to_station"])}",
|
// "${item["from_station"]!} - ${(item["to_station"])}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:ui' as html;
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:html' as html;
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
import 'package:web/web.dart' as web;
|
import 'package:web/web.dart' as web;
|
||||||
@ -14,6 +12,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
import '../../config/apiUrl.dart';
|
import '../../config/apiUrl.dart';
|
||||||
import '../../data/models/plan.dart';
|
import '../../data/models/plan.dart';
|
||||||
@ -556,6 +555,69 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> getPdfDownload() async {
|
||||||
|
final String apiUrldata =
|
||||||
|
'$apiUrl/api/plans/download?plan_id=$selectedPlanId';
|
||||||
|
|
||||||
|
// final String apiUrldata = '$apiUrl/auth/googlelogin';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
try {
|
||||||
|
print("PDf Dowloaded");
|
||||||
|
|
||||||
|
// 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', 'trip_plan_$selectedPlanId.pdf')
|
||||||
|
..click();
|
||||||
|
|
||||||
|
// Revoke the download URL to free up resources
|
||||||
|
html.Url.revokeObjectUrl(url);
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else if (response.statusCode == 404) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('File not found.'),
|
||||||
|
// content: Text('File not found.'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(); // Close the dialog
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load plans');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void getSelectedPlanFor() {
|
void getSelectedPlanFor() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
@ -1031,80 +1093,105 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
DeviceScreenType.desktop;
|
DeviceScreenType.desktop;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(5),
|
||||||
child: isDesktop
|
child: isDesktop
|
||||||
? Row(
|
? Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildApproverControls(),
|
children: [
|
||||||
|
..._buildApproverControls(isDesktop),
|
||||||
|
Spacer(),
|
||||||
|
Text(
|
||||||
|
widget.isViewMode
|
||||||
|
? "View Plan"
|
||||||
|
: (selectedPlanId != null &&
|
||||||
|
selectedPlanId!.isNotEmpty
|
||||||
|
? "Update Plan"
|
||||||
|
: "New Plan"),
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
..._buildPlanPdf()
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: Column(
|
: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildApproverControls(),
|
children: [
|
||||||
|
..._buildApproverControls(isDesktop),
|
||||||
|
Text(
|
||||||
|
widget.isViewMode
|
||||||
|
? "View Plan"
|
||||||
|
: (planData.isNotEmpty
|
||||||
|
? "Update Plan"
|
||||||
|
: "New Plan"),
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
..._buildPlanPdf()
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
if (isStatusExpanded)
|
// if (isStatusExpanded)
|
||||||
Container(
|
// Container(
|
||||||
margin: isDesktop
|
// margin: isDesktop
|
||||||
? const EdgeInsets.only(left: 60, top: 0)
|
// ? const EdgeInsets.only(left: 0, top: 0)
|
||||||
: const EdgeInsets.only(left: 5, top: 2),
|
// : const EdgeInsets.only(left: 5, top: 2),
|
||||||
padding: const EdgeInsets.all(12),
|
// padding: const EdgeInsets.all(12),
|
||||||
width: isDesktop
|
// width: isDesktop
|
||||||
? MediaQuery.of(context).size.width * 0.2
|
// ? MediaQuery.of(context).size.width * 0.2
|
||||||
: MediaQuery.of(context).size.width,
|
// : MediaQuery.of(context).size.width,
|
||||||
decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
color: Color(0xFFF5F5F5),
|
// color: Color(0xFFF5F5F5),
|
||||||
border: Border.all(
|
// border: Border.all(
|
||||||
// color: Colors.grey.shade300,
|
// // color: Colors.grey.shade300,
|
||||||
color: Colors.white,
|
// color: Colors.white,
|
||||||
width: 0.2),
|
// width: 0.2),
|
||||||
borderRadius: BorderRadius.circular(8),
|
// borderRadius: BorderRadius.circular(8),
|
||||||
// boxShadow: [
|
// // boxShadow: [
|
||||||
// BoxShadow(
|
// // BoxShadow(
|
||||||
// // color: Colors.grey.withAlpha(20),
|
// // // color: Colors.grey.withAlpha(20),
|
||||||
// color: Colors.grey.withAlpha(20),
|
// // color: Colors.grey.withAlpha(20),
|
||||||
// spreadRadius: 1.5,
|
// // spreadRadius: 1.5,
|
||||||
// blurRadius: 7,
|
// // blurRadius: 7,
|
||||||
// offset: Offset(0, 4), // shadow direction: bottom
|
// // offset: Offset(0, 4), // shadow direction: bottom
|
||||||
// ),
|
// // ),
|
||||||
// ],
|
// // ],
|
||||||
),
|
// ),
|
||||||
child: Column(
|
// child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
// children: [
|
||||||
if (!hasApprovals)
|
// if (!hasApprovals)
|
||||||
Center(
|
// Center(
|
||||||
child: Text(
|
// child: Text(
|
||||||
"--- No Approvals ---",
|
// "--- No Approvals ---",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
// fontFamily: "Archivo",
|
||||||
fontSize: 11,
|
// fontSize: 11,
|
||||||
fontWeight: FontWeight.w500,
|
// fontWeight: FontWeight.w500,
|
||||||
color: Colors.black87,
|
// color: Colors.black87,
|
||||||
),
|
// ),
|
||||||
)),
|
// )),
|
||||||
for (int i = 0; i < planStatusList.length; i++) ...[
|
// for (int i = 0; i < planStatusList.length; i++) ...[
|
||||||
if (planStatusList[i].entries.any((entry) =>
|
// if (planStatusList[i].entries.any((entry) =>
|
||||||
entry.key.contains('status') &&
|
// entry.key.contains('status') &&
|
||||||
entry.value != null &&
|
// entry.value != null &&
|
||||||
entry.value.toString().isNotEmpty)) ...[
|
// entry.value.toString().isNotEmpty)) ...[
|
||||||
_buildApprovalItem(
|
// _buildApprovalItem(
|
||||||
"Approver ${i + 1}",
|
// "Approver ${i + 1}",
|
||||||
planStatusList[i]
|
// planStatusList[i]
|
||||||
.entries
|
// .entries
|
||||||
.firstWhere(
|
// .firstWhere(
|
||||||
(entry) => entry.key.contains('status'),
|
// (entry) => entry.key.contains('status'),
|
||||||
orElse: () => MapEntry('', ''),
|
// orElse: () => MapEntry('', ''),
|
||||||
)
|
// )
|
||||||
.value
|
// .value
|
||||||
.toString(),
|
// .toString(),
|
||||||
),
|
// ),
|
||||||
SizedBox(height: 6),
|
// SizedBox(height: 6),
|
||||||
],
|
// ],
|
||||||
],
|
// ],
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
if (isApproverRejected)
|
if (isApproverRejected)
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -1140,11 +1227,13 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (widget.isApprover || isStatusExpanded)
|
|
||||||
Divider(
|
// if (widget.isApprover || isStatusExpanded)
|
||||||
thickness: 0.1,
|
|
||||||
color: Colors.blueGrey,
|
Divider(
|
||||||
),
|
thickness: 0.1,
|
||||||
|
color: Colors.blueGrey,
|
||||||
|
),
|
||||||
if (widget.isApprover)
|
if (widget.isApprover)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 5,
|
height: 5,
|
||||||
@ -2283,55 +2372,217 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildApproverControls() {
|
Color getStatusColor(String status) {
|
||||||
|
if (status == "Partially Approved") return Colors.yellow;
|
||||||
|
if (status == "Approved") return Colors.green;
|
||||||
|
if (status == "Completed") return Colors.green;
|
||||||
|
if (status == "Rejected") return Colors.red;
|
||||||
|
return Colors.grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildApproverControls(bool isDesktop) {
|
||||||
|
final statusText = isApproverApproved
|
||||||
|
? "Approved"
|
||||||
|
: isApproverRejected
|
||||||
|
? "Rejected"
|
||||||
|
: (statusValue ?? "");
|
||||||
|
|
||||||
|
bool hasApprovals = planStatusList.any(
|
||||||
|
(item) => item.entries.any(
|
||||||
|
(entry) =>
|
||||||
|
entry.key.contains('status') &&
|
||||||
|
entry.value != null &&
|
||||||
|
entry.value.toString().isNotEmpty,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Row(
|
Stack(
|
||||||
mainAxisSize: MainAxisSize.min,
|
clipBehavior: Clip.none, // allow tooltip to overflow
|
||||||
children: [
|
children: [
|
||||||
Text("Status : "),
|
if (statusValue != "")
|
||||||
if (!isApproverApproved && !isApproverRejected)
|
MouseRegion(
|
||||||
Text(statusValue ?? "",
|
onEnter: (_) => setState(() => isStatusExpanded = true),
|
||||||
style: TextStyle(
|
onExit: (_) => setState(() => isStatusExpanded = false),
|
||||||
fontFamily: "Archivo",
|
child: Container(
|
||||||
fontWeight: FontWeight.bold,
|
decoration: BoxDecoration(
|
||||||
color: widget.layoutColor ?? Colors.grey,
|
border: Border.all(color: getStatusColor(statusText)),
|
||||||
)),
|
borderRadius: BorderRadius.circular(8),
|
||||||
if (isApproverApproved)
|
),
|
||||||
Text("Approved",
|
child: Row(
|
||||||
style: TextStyle(
|
mainAxisSize: MainAxisSize.min,
|
||||||
fontFamily: "Archivo",
|
children: [
|
||||||
fontWeight: FontWeight.bold,
|
Padding(
|
||||||
color: widget.layoutColor ?? Colors.grey,
|
padding: const EdgeInsets.only(
|
||||||
)),
|
top: 8.0, bottom: 8.0, left: 15, right: 15),
|
||||||
if (isApproverRejected)
|
child: Text(
|
||||||
Text("Rejected",
|
statusText,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Roboto",
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.w400,
|
||||||
color: widget.layoutColor ?? Colors.grey,
|
fontSize: 12,
|
||||||
)),
|
color: getStatusColor(
|
||||||
SizedBox(
|
isApproverApproved
|
||||||
width: 5,
|
? "Approved"
|
||||||
),
|
: isApproverRejected
|
||||||
MouseRegion(
|
? "Rejected"
|
||||||
onEnter: (_) {
|
: (statusValue ?? ""),
|
||||||
setState(() {
|
),
|
||||||
isStatusExpanded = true;
|
),
|
||||||
});
|
),
|
||||||
},
|
),
|
||||||
onExit: (_) {
|
],
|
||||||
setState(() {
|
),
|
||||||
isStatusExpanded = false;
|
),
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
Icons.approval_outlined,
|
|
||||||
size: 18,
|
|
||||||
color: isStatusExpanded ? Colors.green : Colors.grey,
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
// Tooltip
|
||||||
|
if (isStatusExpanded)
|
||||||
|
Positioned(
|
||||||
|
top: -20, // Ensure the tooltip is above the container
|
||||||
|
left:
|
||||||
|
MediaQuery.of(context).size.width * 0.06, // align with label
|
||||||
|
child: Material(
|
||||||
|
// important: avoid clipping, give elevation
|
||||||
|
elevation: 4,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: SizedBox(
|
||||||
|
child: Container(
|
||||||
|
width: isDesktop
|
||||||
|
? MediaQuery.of(context).size.width * 0.25
|
||||||
|
: MediaQuery.of(context).size.width,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
// color: Colors.transparent,
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (!hasApprovals)
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
"--- No Approvals ---",
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: "Archivo",
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
for (int i = 0; i < planStatusList.length; i++) ...[
|
||||||
|
if (planStatusList[i].entries.any((entry) =>
|
||||||
|
entry.key.contains('status') &&
|
||||||
|
entry.value != null &&
|
||||||
|
entry.value.toString().isNotEmpty)) ...[
|
||||||
|
_buildApprovalItem(
|
||||||
|
"Approver ${i + 1}",
|
||||||
|
planStatusList[i]
|
||||||
|
.entries
|
||||||
|
.firstWhere(
|
||||||
|
(entry) => entry.key.contains('status'),
|
||||||
|
orElse: () => MapEntry('', ''),
|
||||||
|
)
|
||||||
|
.value
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
SizedBox(height: 6),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// if (isStatusExpanded)
|
||||||
|
// Positioned(
|
||||||
|
// top: 20, // adjust how much above you want
|
||||||
|
// left: 100,
|
||||||
|
// child: Container(
|
||||||
|
// margin: isDesktop
|
||||||
|
// ? const EdgeInsets.only(left: 0, top: 0)
|
||||||
|
// : const EdgeInsets.only(left: 5, top: 2),
|
||||||
|
// padding: const EdgeInsets.all(12),
|
||||||
|
// width: isDesktop
|
||||||
|
// ? MediaQuery.of(context).size.width * 0.2
|
||||||
|
// : MediaQuery.of(context).size.width,
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// // color: Color(0xFFF5F5F5),
|
||||||
|
// color: Colors.white,
|
||||||
|
// border: Border.all(color: Colors.white, width: 0.2),
|
||||||
|
// borderRadius: BorderRadius.circular(8),
|
||||||
|
// ),
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
// children: [
|
||||||
|
// if (!hasApprovals)
|
||||||
|
// Center(
|
||||||
|
// child: Text(
|
||||||
|
// "--- No Approvals ---",
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontFamily: "Archivo",
|
||||||
|
// fontSize: 11,
|
||||||
|
// fontWeight: FontWeight.w500,
|
||||||
|
// color: Colors.black87,
|
||||||
|
// ),
|
||||||
|
// )),
|
||||||
|
// for (int i = 0; i < planStatusList.length; i++) ...[
|
||||||
|
// if (planStatusList[i].entries.any((entry) =>
|
||||||
|
// entry.key.contains('status') &&
|
||||||
|
// entry.value != null &&
|
||||||
|
// entry.value.toString().isNotEmpty)) ...[
|
||||||
|
// _buildApprovalItem(
|
||||||
|
// "Approver ${i + 1}",
|
||||||
|
// planStatusList[i]
|
||||||
|
// .entries
|
||||||
|
// .firstWhere(
|
||||||
|
// (entry) => entry.key.contains('status'),
|
||||||
|
// orElse: () => MapEntry('', ''),
|
||||||
|
// )
|
||||||
|
// .value
|
||||||
|
// .toString(),
|
||||||
|
// ),
|
||||||
|
// SizedBox(height: 6),
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// isApproverApproved
|
||||||
|
// ? "Approved"
|
||||||
|
// : isApproverRejected
|
||||||
|
// ? "Rejected"
|
||||||
|
// : (statusValue ?? ""),
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontFamily: "Archivo",
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
// color: widget.layoutColor ?? Colors.grey,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// SizedBox(width: 5),
|
||||||
|
// MouseRegion(
|
||||||
|
// onEnter: (_) => setState(() => isStatusExpanded = true),
|
||||||
|
// onExit: (_) => setState(() => isStatusExpanded = false),
|
||||||
|
// child: Icon(
|
||||||
|
// Icons.approval_outlined,
|
||||||
|
// size: 18,
|
||||||
|
// color: isStatusExpanded ? Colors.green : Colors.grey,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
const SizedBox(height: 10, width: 10),
|
const SizedBox(height: 10, width: 10),
|
||||||
if (widget.isApprover)
|
if (widget.isApprover)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@ -2461,6 +2712,38 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildPlanPdf() {
|
||||||
|
return [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
hoverColor: Colors.white,
|
||||||
|
onTap: () {
|
||||||
|
print('📄 PDF icon clicked!');
|
||||||
|
getPdfDownload();
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text("Download PDF"),
|
||||||
|
SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Transform.scale(
|
||||||
|
scale: 1.5, // 1.0 = normal, 1.5 = 50% bigger
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/IconsImg/planPdf_icon.png',
|
||||||
|
width: 25,
|
||||||
|
height: 25, // keep the real height small
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
// SizedBox(height: 10), // small spacing
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
void _showInputDialog(String title) {
|
void _showInputDialog(String title) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@ -154,7 +154,7 @@ class DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
|
|
||||||
List<String> getAllowedServiceNames() {
|
List<String> getAllowedServiceNames() {
|
||||||
if (widget.tripType == "1") {
|
if (widget.tripType == "1") {
|
||||||
return ["flight", "accomodation", "train", "bus"];
|
return ["flight", "accomodation", "train", "bus", "taxi"];
|
||||||
} else if (widget.tripType == "2") {
|
} else if (widget.tripType == "2") {
|
||||||
return [
|
return [
|
||||||
"flight",
|
"flight",
|
||||||
@ -162,7 +162,8 @@ class DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
"forex",
|
"forex",
|
||||||
"insurance",
|
"insurance",
|
||||||
"visa",
|
"visa",
|
||||||
"miscellaneous"
|
"miscellaneous",
|
||||||
|
"taxi"
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
// tripType is null or not 1/2, allow everything
|
// tripType is null or not 1/2, allow everything
|
||||||
|
|||||||
@ -27,7 +27,6 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
int itemsPerPage = 8;
|
int itemsPerPage = 8;
|
||||||
|
|
||||||
late Future<List<Plan>> futurePlans;
|
|
||||||
String? userId;
|
String? userId;
|
||||||
String? orgId;
|
String? orgId;
|
||||||
String? token;
|
String? token;
|
||||||
@ -35,7 +34,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
|
|
||||||
Color? layoutColor;
|
Color? layoutColor;
|
||||||
Color? bodyColor;
|
Color? bodyColor;
|
||||||
|
late Future<List<Plan>> futurePlans;
|
||||||
List<Plan> allPlans = [];
|
List<Plan> allPlans = [];
|
||||||
List<Plan> filteredPlans = [];
|
List<Plan> filteredPlans = [];
|
||||||
TextEditingController searchController = TextEditingController();
|
TextEditingController searchController = TextEditingController();
|
||||||
@ -49,22 +48,24 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
initializeData();
|
initializeData();
|
||||||
loadInitialData();
|
loadInitialData();
|
||||||
|
|
||||||
futurePlans.then((plans) {
|
// futurePlans.then((plans) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
allPlans = plans;
|
// allPlans = plans;
|
||||||
filteredPlans = plans;
|
// filteredPlans = plans;
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
|
|
||||||
// futurePlans = fetchPlans();
|
// futurePlans = fetchPlans();
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterPlans(String query) {
|
void filterPlans(String query) {
|
||||||
|
print("allPlans before filtering: $allPlans");
|
||||||
final lowerQuery = query.toLowerCase();
|
final lowerQuery = query.toLowerCase();
|
||||||
setState(() {
|
setState(() {
|
||||||
filteredPlans = allPlans.where((plan) {
|
filteredPlans = allPlans.where((plan) {
|
||||||
return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) ||
|
return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
|
(plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
(plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) ||
|
(plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
(plan.userName?.toLowerCase().contains(lowerQuery) ?? false) ||
|
(plan.userName?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
(plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) ||
|
(plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
@ -73,6 +74,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
(plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false);
|
(plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false);
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
|
print("filteredPlans: $filteredPlans");
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadInitialData() async {
|
void loadInitialData() async {
|
||||||
@ -103,6 +105,15 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
futurePlans = fetchPlans();
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +361,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
children: [
|
children: [
|
||||||
const Text('Trip List',
|
const Text('Trip List',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF212121))),
|
color: Color(0xFF212121))),
|
||||||
@ -358,31 +369,43 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
Spacer(),
|
Spacer(),
|
||||||
|
|
||||||
Container(
|
Container(
|
||||||
width: MediaQuery.of(context).size.width * 0.2,
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: filterPlans,
|
onChanged: filterPlans,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Search for a plan",
|
hintText: "Search...",
|
||||||
hintStyle:
|
hintStyle:
|
||||||
TextStyle(fontSize: 14, color: Color(0xFF9E9DBD)),
|
TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)),
|
||||||
prefixIcon:
|
prefixIcon: Icon(
|
||||||
Icon(Icons.search, color: Color(0xFF9E9DBD)),
|
Icons.search,
|
||||||
|
color: Color(0xFF9E9DBD),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.grey.shade300, width: 0.5),
|
color: Colors.grey.shade200, width: 0.5),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide:
|
borderSide:
|
||||||
BorderSide(color: Colors.blueAccent, width: 1),
|
BorderSide(color: Colors.grey.shade300, width: 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// SizedBox(width: 16),
|
// SizedBox(width: 16),
|
||||||
|
|
||||||
Spacer(),
|
Spacer(),
|
||||||
@ -511,7 +534,10 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Plan> plans = snapshot.data!;
|
// List<Plan> plans = snapshot.data!;
|
||||||
|
|
||||||
|
List<Plan> plans =
|
||||||
|
filteredPlans.isNotEmpty ? filteredPlans : allPlans;
|
||||||
plans.sort((a, b) =>
|
plans.sort((a, b) =>
|
||||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||||
|
|
||||||
@ -537,58 +563,71 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Id',
|
label: Text('Trip Id',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
// color: Colors.grey,
|
||||||
fontSize: 14,
|
// color: Color(0xFF9E9DBD),
|
||||||
fontFamily: "Archivo",
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.bold))),
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Employee Code',
|
||||||
|
style: TextStyle(
|
||||||
|
// color: Color(0xFF9E9DBD),
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Name',
|
label: Text('Trip Name',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Traveller',
|
label: Text('Traveller',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Trip Type',
|
label: Text('Trip Type',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Created On',
|
label: Text('Created On',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Status',
|
label: Text('Status',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Actions',
|
label: Text('Actions',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
],
|
],
|
||||||
rows: paginatedPlans.map((plan) {
|
rows: paginatedPlans.map((plan) {
|
||||||
return DataRow(cells: [
|
return DataRow(cells: [
|
||||||
DataCell(Text(plan.planId,
|
DataCell(Text(plan.planId,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
|
))),
|
||||||
|
DataCell(Text(plan.employeeCode ?? "no data",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(plan.tripTitle,
|
DataCell(Text(plan.tripTitle,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
),
|
),
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis)),
|
overflow: TextOverflow.ellipsis)),
|
||||||
@ -598,18 +637,18 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
: plan.travellerName,
|
: plan.travellerName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(plan.tripType,
|
DataCell(Text(plan.tripType,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(_formatDate(plan.createdOn),
|
DataCell(Text(_formatDate(plan.createdOn),
|
||||||
// plan.createdOn,
|
// plan.createdOn,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(
|
DataCell(
|
||||||
Container(
|
Container(
|
||||||
@ -646,6 +685,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
// ? Colors.white
|
// ? Colors.white
|
||||||
// : Colors.grey,
|
// : Colors.grey,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -668,7 +708,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
isViewMode: false),
|
isViewMode: false),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/IconsImg/edit.png',
|
'assets/images/IconsImg/edit.png',
|
||||||
width: 20,
|
width: 15,
|
||||||
height: 15),
|
height: 15),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -688,6 +728,15 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
width: 20,
|
width: 20,
|
||||||
height: 15),
|
height: 15),
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.download,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
apiService.getPdfDownload(plan.planId);
|
||||||
|
}),
|
||||||
])),
|
])),
|
||||||
]);
|
]);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@ -698,6 +747,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
isDesktop
|
isDesktop
|
||||||
? table
|
? table
|
||||||
@ -734,6 +784,7 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
|
fontFamily: "Inter",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 2),
|
SizedBox(width: 2),
|
||||||
|
|||||||
@ -111,15 +111,18 @@ class _PolicyState extends State<Policy> {
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
loadinitializeData();
|
loadinitializeData();
|
||||||
|
|
||||||
if (widget.policy != null) {
|
|
||||||
final details =
|
|
||||||
List<Map<String, dynamic>>.from(widget.policy!['policy_details']);
|
|
||||||
policyCriteriaKey.currentState?.loadPolicyDetails(details);
|
|
||||||
}
|
|
||||||
updateSelectedServices();
|
updateSelectedServices();
|
||||||
updateData();
|
updateData();
|
||||||
|
|
||||||
loadInitialData();
|
loadInitialData();
|
||||||
|
|
||||||
|
if (widget.policy != null) {
|
||||||
|
final details =
|
||||||
|
List<Map<String, dynamic>>.from(widget.policy!['policy_details']);
|
||||||
|
policyCriteriaKey.currentState?.loadPolicyDetails(details);
|
||||||
|
|
||||||
|
policyCriteriaKey.currentState?.fetchTrainFlightClass();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +236,7 @@ class _PolicyState extends State<Policy> {
|
|||||||
String firstServiceName = ServicesChoosed?.first['name'];
|
String firstServiceName = ServicesChoosed?.first['name'];
|
||||||
print("✅ First service name selected for filter: $firstServiceName");
|
print("✅ First service name selected for filter: $firstServiceName");
|
||||||
selectedService = firstServiceName;
|
selectedService = firstServiceName;
|
||||||
|
policyCriteriaKey.currentState?.fetchTrainFlightClass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,89 +542,25 @@ class _PolicyState extends State<Policy> {
|
|||||||
),
|
),
|
||||||
isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
|
isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
|
||||||
Container(
|
Container(
|
||||||
// color: Colors.amber,
|
padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
|
||||||
// color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
child: isDesktop
|
||||||
padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
|
? Row(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Expanded(child: _buildPolicyNameField(isDesktop)),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Spacer(),
|
||||||
children: [
|
Expanded(child: _buildPolicyTypeField(isDesktop)),
|
||||||
Text("Policy Name",
|
],
|
||||||
style: TextStyle(
|
)
|
||||||
fontSize: 12,
|
: Column(
|
||||||
fontWeight: FontWeight.w200,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: Colors.black)),
|
children: [
|
||||||
SizedBox(height: 5),
|
_buildPolicyNameField(isDesktop),
|
||||||
CustomTextFieldUserWrapper(
|
SizedBox(height: 20),
|
||||||
isFocused: false,
|
_buildPolicyTypeField(isDesktop),
|
||||||
isDesktop: isDesktop,
|
|
||||||
child: SizedBox(
|
|
||||||
height: 40,
|
|
||||||
child: TextField(
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
controller: _policyController,
|
|
||||||
// enabled: !isViewMode,
|
|
||||||
onChanged: (value) {
|
|
||||||
_clearError("name");
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Policy Name",
|
|
||||||
labelStyle: TextStyle(
|
|
||||||
fontSize: 12, color: Colors.grey),
|
|
||||||
floatingLabelBehavior:
|
|
||||||
FloatingLabelBehavior.never,
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (errorMessages["name"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
errorMessages["name"]!,
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(
|
),
|
||||||
height: 5,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text("Policy Type",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w200,
|
|
||||||
color: Colors.black)),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildTripType(isDesktop),
|
|
||||||
),
|
|
||||||
if (errorMessages["trip_type"] != null) ...[
|
|
||||||
SizedBox(height: 5), // Space before error message
|
|
||||||
Text(
|
|
||||||
errorMessages["trip_type"]!,
|
|
||||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
@ -657,6 +597,67 @@ class _PolicyState extends State<Policy> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildPolicyNameField(bool isDesktop) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Policy Name",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w200,
|
||||||
|
color: Colors.black)),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
CustomTextFieldUserWrapper(
|
||||||
|
isFocused: false,
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: TextField(
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
controller: _policyController,
|
||||||
|
onChanged: (value) => _clearError("name"),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Policy Name",
|
||||||
|
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessages["name"] != null) ...[
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text(errorMessages["name"]!,
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPolicyTypeField(bool isDesktop) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Policy Type",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w200,
|
||||||
|
color: Colors.black)),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: _buildTripType(isDesktop),
|
||||||
|
),
|
||||||
|
if (errorMessages["trip_type"] != null) ...[
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text(errorMessages["trip_type"]!,
|
||||||
|
style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildPolicyCategoryList(bool isDesktop) {
|
Widget _buildPolicyCategoryList(bool isDesktop) {
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -727,6 +728,8 @@ class _PolicyState extends State<Policy> {
|
|||||||
selectedService == "Train") {
|
selectedService == "Train") {
|
||||||
showClass = true;
|
showClass = true;
|
||||||
showCost = true;
|
showCost = true;
|
||||||
|
int serviceCode = selectedService == "Flight" ? 1 : 2;
|
||||||
|
policyCriteriaKey.currentState?.fetchTrainFlightClass();
|
||||||
} else if (selectedService == "Accommodation") {
|
} else if (selectedService == "Accommodation") {
|
||||||
showClass = true;
|
showClass = true;
|
||||||
showCost = false;
|
showCost = false;
|
||||||
@ -825,23 +828,6 @@ class _PolicyState extends State<Policy> {
|
|||||||
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
|
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
|
||||||
fontSize: 13),
|
fontSize: 13),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Radio<String>(
|
|
||||||
// activeColor: Colors.blueAccent,
|
|
||||||
// // contentPadding: EdgeInsets.zero,
|
|
||||||
// visualDensity: VisualDensity.compact,
|
|
||||||
// // dense: true,
|
|
||||||
// value: "1",
|
|
||||||
// groupValue: _selectedTripType,
|
|
||||||
// onChanged: widget.isViewMode
|
|
||||||
// ? null
|
|
||||||
// : (value) {
|
|
||||||
// setState(() {
|
|
||||||
// _selectedTripType = value!;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../../config/apiUrl.dart';
|
||||||
|
import '../../services/apiService.dart';
|
||||||
|
import '../../utils/auth_utils.dart';
|
||||||
import '../../widgets/custom_text_field.dart';
|
import '../../widgets/custom_text_field.dart';
|
||||||
import '../../widgets/custom_user_form.dart';
|
import '../../widgets/custom_user_form.dart';
|
||||||
|
|
||||||
@ -30,6 +36,12 @@ class PolicyCriteria extends StatefulWidget {
|
|||||||
class PolicyCriteriaState extends State<PolicyCriteria> {
|
class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||||
String? ServiceId = "1";
|
String? ServiceId = "1";
|
||||||
|
|
||||||
|
String? orgId;
|
||||||
|
String? userId;
|
||||||
|
|
||||||
|
Map<String, dynamic>? apiDataForClass;
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
// final TextEditingController _costController = TextEditingController();
|
// final TextEditingController _costController = TextEditingController();
|
||||||
// final TextEditingController _classController = TextEditingController();
|
// final TextEditingController _classController = TextEditingController();
|
||||||
// String? FirstApproverAction;
|
// String? FirstApproverAction;
|
||||||
@ -37,12 +49,17 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
// String? ThirdApproverAction;
|
// String? ThirdApproverAction;
|
||||||
// String? SelectedParallelProcess = "3";
|
// String? SelectedParallelProcess = "3";
|
||||||
|
|
||||||
|
final ApiService apiService = ApiService();
|
||||||
|
|
||||||
Map<String, TextEditingController> costController = {};
|
Map<String, TextEditingController> costController = {};
|
||||||
Map<String, TextEditingController> classController = {};
|
// Map<String, TextEditingController> classController = {};
|
||||||
|
Map<String, String?> classAction = {};
|
||||||
Map<String, String?> FirstApproverAction = {};
|
Map<String, String?> FirstApproverAction = {};
|
||||||
Map<String, String?> SecondApproverAction = {};
|
Map<String, String?> SecondApproverAction = {};
|
||||||
Map<String, String?> ThirdApproverAction = {};
|
Map<String, String?> ThirdApproverAction = {};
|
||||||
Map<String, String?> SelectedParallelProcess = {};
|
Map<String, String?> SelectedParallelProcess = {};
|
||||||
|
Map<String, String> policyDetailsIdMap = {}; // new
|
||||||
|
Map<String, String> policyIdMap = {}; // new
|
||||||
|
|
||||||
Map<String, String> validationErrors = {};
|
Map<String, String> validationErrors = {};
|
||||||
|
|
||||||
@ -72,41 +89,72 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
widget.selectedTabNotifier.addListener(() {
|
widget.selectedTabNotifier.addListener(() {
|
||||||
print("selectedTab changed: ${widget.selectedTabNotifier.value}");
|
print("selectedTab changed: ${widget.selectedTabNotifier.value}");
|
||||||
fieldForPolicy();
|
fieldForPolicy();
|
||||||
|
fetchTrainFlightClass();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadinitializeData() async {
|
||||||
|
orgId = await getOrgId();
|
||||||
|
userId = await getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
void saveCurrentPolicy() {
|
void saveCurrentPolicy() {
|
||||||
if (ServiceId != null) {
|
if (ServiceId != null) {
|
||||||
addOrUpdatePolicy(ServiceId!);
|
addOrUpdatePolicy(ServiceId!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To set the data (update)
|
||||||
void loadPolicyDetails(List<Map<String, dynamic>> details) {
|
void loadPolicyDetails(List<Map<String, dynamic>> details) {
|
||||||
for (var item in details) {
|
for (var item in details) {
|
||||||
final serviceId = item['service_id'].toString();
|
final serviceId = item['service_id'].toString();
|
||||||
|
|
||||||
costController[serviceId] =
|
costController[serviceId] =
|
||||||
TextEditingController(text: item['cost'] ?? '');
|
TextEditingController(text: item['cost'] ?? '');
|
||||||
classController[serviceId] =
|
|
||||||
TextEditingController(text: item['class'] ?? '');
|
// classAction[serviceId] = classAction[serviceId]?.toString();
|
||||||
|
|
||||||
|
classAction[serviceId] = item['class']?.toString() ?? '';
|
||||||
|
|
||||||
FirstApproverAction[serviceId] = item['a1_action']?.toString();
|
FirstApproverAction[serviceId] = item['a1_action']?.toString();
|
||||||
SecondApproverAction[serviceId] = item['a2_action']?.toString();
|
SecondApproverAction[serviceId] = item['a2_action']?.toString();
|
||||||
ThirdApproverAction[serviceId] = item['a3_action']?.toString();
|
ThirdApproverAction[serviceId] = item['a3_action']?.toString();
|
||||||
SelectedParallelProcess[serviceId] =
|
SelectedParallelProcess[serviceId] =
|
||||||
item['parallel_process_from']?.toString() ?? "3";
|
item['parallel_process_from']?.toString() ?? "3";
|
||||||
|
|
||||||
|
if (item['policy_details_id'] != null) {
|
||||||
|
policyDetailsIdMap[serviceId] = item['policy_details_id'].toString();
|
||||||
|
}
|
||||||
|
if (item['policy_id'] != null) {
|
||||||
|
policyIdMap[serviceId] = item['policy_id'].toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
policyData = details;
|
policyData = details;
|
||||||
widget.onPolicyDataChanged(policyData);
|
widget.onPolicyDataChanged(policyData);
|
||||||
|
|
||||||
|
if (details.isNotEmpty) {
|
||||||
|
final firstServiceId = details.first['service_id'].toString();
|
||||||
|
print('First Service ID is: $firstServiceId');
|
||||||
|
// firstServiceId == "1"
|
||||||
|
// ? fetchTrainFlightClass(1)
|
||||||
|
// : fetchTrainFlightClass(2);
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
|
print("detailsdetails- $details");
|
||||||
}
|
}
|
||||||
|
|
||||||
void addOrUpdatePolicy(String serviceId) {
|
void addOrUpdatePolicy(String serviceId) {
|
||||||
|
// 1. First, find existing item if any
|
||||||
|
final existingIndex =
|
||||||
|
policyData!.indexWhere((item) => item["service_id"] == serviceId);
|
||||||
|
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
"service_id": serviceId,
|
"service_id": serviceId,
|
||||||
"cost": costController[serviceId]?.text,
|
"cost": costController[serviceId]?.text,
|
||||||
"class": classController[serviceId]?.text,
|
// "class": classController[serviceId]?.text,
|
||||||
|
"class": classAction[serviceId],
|
||||||
"a1_action": FirstApproverAction[serviceId],
|
"a1_action": FirstApproverAction[serviceId],
|
||||||
"a2_action": SecondApproverAction[serviceId],
|
"a2_action": SecondApproverAction[serviceId],
|
||||||
"a3_action": ThirdApproverAction[serviceId],
|
"a3_action": ThirdApproverAction[serviceId],
|
||||||
@ -115,12 +163,15 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
final cost = costController[serviceId]?.text ?? "";
|
final cost = costController[serviceId]?.text ?? "";
|
||||||
final travelClass = classController[serviceId]?.text ?? "";
|
|
||||||
|
// final travelClass = classController[serviceId]?.text ?? "";
|
||||||
|
final travelClass = classAction[serviceId];
|
||||||
final a1 = FirstApproverAction[serviceId];
|
final a1 = FirstApproverAction[serviceId];
|
||||||
final a2 = SecondApproverAction[serviceId];
|
final a2 = SecondApproverAction[serviceId];
|
||||||
final a3 = ThirdApproverAction[serviceId];
|
final a3 = ThirdApproverAction[serviceId];
|
||||||
|
|
||||||
bool hasValue = cost.trim().isNotEmpty || travelClass.trim().isNotEmpty;
|
bool hasValue = cost.trim().isNotEmpty;
|
||||||
|
// bool hasValue = cost.trim().isNotEmpty || travelClass.trim().isNotEmpty;
|
||||||
bool allActionsNull = a1 == null && a2 == null && a3 == null;
|
bool allActionsNull = a1 == null && a2 == null && a3 == null;
|
||||||
bool someActionsMissing = [a1, a2, a3].where((a) => a != null).length > 0 &&
|
bool someActionsMissing = [a1, a2, a3].where((a) => a != null).length > 0 &&
|
||||||
[a1, a2, a3].where((a) => a == null).length > 0;
|
[a1, a2, a3].where((a) => a == null).length > 0;
|
||||||
@ -137,19 +188,103 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
|
|
||||||
validationErrors.remove(serviceId);
|
validationErrors.remove(serviceId);
|
||||||
|
|
||||||
int index =
|
// 2. ⚡ If updating an existing item, also **keep its IDs**
|
||||||
policyData!.indexWhere((item) => item["service_id"] == serviceId);
|
if (existingIndex != -1) {
|
||||||
if (index != -1) {
|
final existingItem = policyData![existingIndex];
|
||||||
policyData![index] = data; // Replace existing entry
|
|
||||||
|
if (existingItem.containsKey("policy_details_id")) {
|
||||||
|
data["policy_details_id"] = existingItem["policy_details_id"];
|
||||||
|
}
|
||||||
|
if (existingItem.containsKey("policy_id")) {
|
||||||
|
data["policy_id"] = existingItem["policy_id"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Now update it
|
||||||
|
policyData![existingIndex] = data;
|
||||||
print("🔁 Updated policy for ServiceId: $serviceId");
|
print("🔁 Updated policy for ServiceId: $serviceId");
|
||||||
} else {
|
} else {
|
||||||
policyData!.add(data); // Add new entry
|
// 4. Else add as new
|
||||||
|
policyData!.add(data);
|
||||||
print("➕ Added policy for ServiceId: $serviceId");
|
print("➕ Added policy for ServiceId: $serviceId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// int index =
|
||||||
|
// policyData!.indexWhere((item) => item["service_id"] == serviceId);
|
||||||
|
// if (index != -1) {
|
||||||
|
// policyData![index] = data; // Replace existing entry
|
||||||
|
// print("🔁 Updated policy for ServiceId: $serviceId");
|
||||||
|
// } else {
|
||||||
|
// policyData!.add(data); // Add new entry
|
||||||
|
// print("➕ Added policy for ServiceId: $serviceId");
|
||||||
|
// }
|
||||||
|
|
||||||
print("📋 policyData - $policyData");
|
print("📋 policyData - $policyData");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Future<void> fetchTrainFlightClass(int tripId) async {
|
||||||
|
// // final String apiUrldata = '$apiUrl/api/getDropdownMaster';
|
||||||
|
//
|
||||||
|
// // final String apiUrldata =
|
||||||
|
// // '$apiUrl/api/getFlightAndTrainClass?user_id=${widget.userId}&trip_type=tripId';
|
||||||
|
// final String apiUrldata = await apiService.fetchMasterDropdown();
|
||||||
|
//
|
||||||
|
// final token = await getToken();
|
||||||
|
//
|
||||||
|
// if (token == null) {
|
||||||
|
// throw Exception('Token not found. Please log in.');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final response = await http.get(
|
||||||
|
// Uri.parse(apiUrldata),
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': 'Bearer $token',
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.statusCode == 200) {
|
||||||
|
// try {
|
||||||
|
// final data = json.decode(response.body);
|
||||||
|
// print(data);
|
||||||
|
//
|
||||||
|
// if (!data.containsKey('data') || data['data'] is! Map) {
|
||||||
|
// throw Exception(
|
||||||
|
// "Invalid response format: 'data' field is missing or not a Map");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Map<String, dynamic> plansJson =
|
||||||
|
// data['data']; // 'data' is a Map, not a List
|
||||||
|
// setState(() {
|
||||||
|
// apiDataForClass = plansJson; // Store API response in state
|
||||||
|
// isLoading = false;
|
||||||
|
// });
|
||||||
|
// } catch (e) {
|
||||||
|
// throw Exception('Error parsing response: $e');
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// throw Exception('Failed to load plans');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Future<void> fetchTrainFlightClass() async {
|
||||||
|
try {
|
||||||
|
final data = await apiService.fetchMasterDropdown();
|
||||||
|
|
||||||
|
print("fetchTrainFlightClass - $data");
|
||||||
|
|
||||||
|
if (data is! Map) {
|
||||||
|
throw Exception("Invalid response format: expected a Map");
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
apiDataForClass = data; // Store API response in state
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching role list: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void fieldForPolicy() {
|
void fieldForPolicy() {
|
||||||
print("fieldForPolicy - ${widget.selectedTabNotifier.value}");
|
print("fieldForPolicy - ${widget.selectedTabNotifier.value}");
|
||||||
|
|
||||||
@ -162,7 +297,8 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
ServiceId = widget.selectedTabNotifier.value ?? "1";
|
ServiceId = widget.selectedTabNotifier.value ?? "1";
|
||||||
// Initialize controllers and variables if not present
|
// Initialize controllers and variables if not present
|
||||||
costController.putIfAbsent(ServiceId!, () => TextEditingController());
|
costController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||||
classController.putIfAbsent(ServiceId!, () => TextEditingController());
|
classAction.putIfAbsent(ServiceId!, () => null);
|
||||||
|
// classController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||||
|
|
||||||
FirstApproverAction.putIfAbsent(ServiceId!, () => null);
|
FirstApproverAction.putIfAbsent(ServiceId!, () => null);
|
||||||
SecondApproverAction.putIfAbsent(ServiceId!, () => null);
|
SecondApproverAction.putIfAbsent(ServiceId!, () => null);
|
||||||
@ -210,7 +346,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
? Row(
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
if (widget.isClass!)
|
if (widget.isClass)
|
||||||
buildClassWidget(widget.isDesktop),
|
buildClassWidget(widget.isDesktop),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
if (widget.isCost!)
|
if (widget.isCost!)
|
||||||
@ -372,6 +508,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
items: [
|
items: [
|
||||||
"Approval",
|
"Approval",
|
||||||
"Notification",
|
"Notification",
|
||||||
|
"None"
|
||||||
],
|
],
|
||||||
dropdownDecoratorProps:
|
dropdownDecoratorProps:
|
||||||
DropDownDecoratorProps(
|
DropDownDecoratorProps(
|
||||||
@ -500,6 +637,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
items: [
|
items: [
|
||||||
"Approval",
|
"Approval",
|
||||||
"Notification",
|
"Notification",
|
||||||
|
"None"
|
||||||
],
|
],
|
||||||
dropdownDecoratorProps:
|
dropdownDecoratorProps:
|
||||||
DropDownDecoratorProps(
|
DropDownDecoratorProps(
|
||||||
@ -633,6 +771,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
items: [
|
items: [
|
||||||
"Approval",
|
"Approval",
|
||||||
"Notification",
|
"Notification",
|
||||||
|
"None"
|
||||||
],
|
],
|
||||||
dropdownDecoratorProps:
|
dropdownDecoratorProps:
|
||||||
DropDownDecoratorProps(
|
DropDownDecoratorProps(
|
||||||
@ -740,6 +879,58 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget buildClassWidget(isDesktop) {
|
Widget buildClassWidget(isDesktop) {
|
||||||
|
List<dynamic> purposeList = [];
|
||||||
|
if (ServiceId != null) {
|
||||||
|
purposeList = (ServiceId == "1")
|
||||||
|
? (apiDataForClass?['flight_class'] ?? [])
|
||||||
|
: (apiDataForClass?['train_class'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("CLASS1 - ");
|
||||||
|
|
||||||
|
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||||
|
.map((item) => DropdownMenuItem<String>(
|
||||||
|
value: item['dropdown_key'],
|
||||||
|
child: Text(item['dropdown_value']),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
print("CLASS 2 - ");
|
||||||
|
|
||||||
|
if (dropdownItems.isEmpty) {
|
||||||
|
dropdownItems.add(
|
||||||
|
DropdownMenuItem<String>(
|
||||||
|
value: null,
|
||||||
|
child: Text("No options available",
|
||||||
|
style: TextStyle(color: Colors.grey)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (ServiceId != null) {
|
||||||
|
// if (classAction[ServiceId!] == null && dropdownItems.isNotEmpty) {
|
||||||
|
// classAction[ServiceId!] = dropdownItems.first.value!;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (ServiceId != null &&
|
||||||
|
// classAction[ServiceId!] == null &&
|
||||||
|
// dropdownItems.isNotEmpty) {
|
||||||
|
// classAction[ServiceId!] = dropdownItems.first.value!;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 1. First, when dropdownItems are ready, assign default value:
|
||||||
|
if (ServiceId != null &&
|
||||||
|
classAction[ServiceId!] == null &&
|
||||||
|
dropdownItems.isNotEmpty) {
|
||||||
|
// Important to wrap inside setState if async
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
classAction[ServiceId!] = dropdownItems.first.value!;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
@ -754,21 +945,48 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
|||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
isDesktop: widget.isDesktop,
|
isDesktop: widget.isDesktop,
|
||||||
|
// child: SizedBox(
|
||||||
|
// height: 40,
|
||||||
|
// child: TextField(
|
||||||
|
// style: TextStyle(fontSize: 12),
|
||||||
|
// // controller: classController[ServiceId],
|
||||||
|
// // enabled: !isViewMode,
|
||||||
|
// onChanged: (value) {},
|
||||||
|
// decoration: InputDecoration(
|
||||||
|
// labelText: "Class",
|
||||||
|
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
|
// border: InputBorder.none,
|
||||||
|
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: ServiceId != null && (isLoading || dropdownItems.isEmpty)
|
||||||
style: TextStyle(fontSize: 12),
|
? const Center(child: CircularProgressIndicator())
|
||||||
controller: classController[ServiceId],
|
: DropdownButtonFormField<String>(
|
||||||
// enabled: !isViewMode,
|
// Assign the correct focus node
|
||||||
onChanged: (value) {},
|
// controller: _hotelNameController,
|
||||||
decoration: InputDecoration(
|
value: classAction[ServiceId],
|
||||||
labelText: "Class",
|
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
style: TextStyle(fontSize: 12),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 10), // Proper padding
|
||||||
),
|
),
|
||||||
|
onChanged: (newValue) {
|
||||||
|
if (ServiceId != null && newValue != null) {
|
||||||
|
setState(() {
|
||||||
|
classAction[ServiceId!] = newValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
items: dropdownItems,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -52,6 +52,14 @@ class _PolicyListState extends State<PolicyList> {
|
|||||||
Future<void> loadAllGroups() async {
|
Future<void> loadAllGroups() async {
|
||||||
try {
|
try {
|
||||||
final result = await apiService.fetchAllPolicy();
|
final result = await apiService.fetchAllPolicy();
|
||||||
|
|
||||||
|
// Sort by policy_id descending (latest first)
|
||||||
|
result.sort((a, b) {
|
||||||
|
int idA = int.tryParse(a['policy_id'].toString()) ?? 0;
|
||||||
|
int idB = int.tryParse(b['policy_id'].toString()) ?? 0;
|
||||||
|
return idB.compareTo(idA); // latest first
|
||||||
|
});
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
apiAllGroups = result;
|
apiAllGroups = result;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -24,11 +24,24 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
Color? layoutColor;
|
Color? layoutColor;
|
||||||
Color? bodyColor;
|
Color? bodyColor;
|
||||||
|
|
||||||
|
List allUsers = [];
|
||||||
|
List filteredUsers = [];
|
||||||
|
TextEditingController searchController = TextEditingController();
|
||||||
|
|
||||||
|
int currentPage = 0;
|
||||||
|
int itemsPerPage = 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
futureUsers = fetchUsers();
|
futureUsers = fetchUsers();
|
||||||
|
|
||||||
|
futureUsers.then((users) {
|
||||||
|
setState(() {
|
||||||
|
allUsers = users;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
fetchCountryList();
|
fetchCountryList();
|
||||||
loadInitialData();
|
loadInitialData();
|
||||||
@ -57,6 +70,35 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
return prefs.getString('auth_token');
|
return prefs.getString('auth_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Future<List<dynamic>> fetchSingleUsers(userId) async {
|
||||||
|
// orgId = await getOrgId();
|
||||||
|
// final String apiUrlData = '$apiUrl/api/users/find/$userId';
|
||||||
|
//
|
||||||
|
// final String? token = await getToken();
|
||||||
|
//
|
||||||
|
// print("Fetch Users");
|
||||||
|
// print("TOEKRWE: $token");
|
||||||
|
//
|
||||||
|
// if (token == null) {
|
||||||
|
// throw Exception('Token not found. Please log in.');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final response = await http.get(
|
||||||
|
// Uri.parse(apiUrlData),
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': 'Bearer $token',
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if (response.statusCode == 200) {
|
||||||
|
// final data = json.decode(response.body);
|
||||||
|
// return data['data']; // Returning raw JSON list
|
||||||
|
// } else {
|
||||||
|
// throw Exception('Failed to load users');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
Future<List<dynamic>> fetchUsers() async {
|
Future<List<dynamic>> fetchUsers() async {
|
||||||
orgId = await getOrgId();
|
orgId = await getOrgId();
|
||||||
final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
|
final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
|
||||||
@ -236,9 +278,25 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
void refreshUserList() {
|
void refreshUserList() {
|
||||||
setState(() {
|
setState(() {
|
||||||
futureUsers = fetchUsers(); // Re-fetch users after status update
|
futureUsers = fetchUsers(); // Re-fetch users after status update
|
||||||
|
// Wait for futurePlans to be fetched and update allPlans
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void filterUsers(String query) {
|
||||||
|
print("allUsers before filtering: $query");
|
||||||
|
final lowerQuery = query.toLowerCase();
|
||||||
|
setState(() {
|
||||||
|
filteredUsers = allUsers.where((user) {
|
||||||
|
return (user['first_name']?.toLowerCase().contains(lowerQuery) ??
|
||||||
|
false) ||
|
||||||
|
(user['last_name']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
|
(user['email']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||||
|
(user['role_value']?.toLowerCase().contains(lowerQuery) ?? false);
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
print("filteredPlans: $filteredUsers");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||||
@ -294,19 +352,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
height: isDesktop
|
height: isDesktop
|
||||||
? MediaQuery.of(context).size.height * 0.98
|
? MediaQuery.of(context).size.height * 0.98
|
||||||
: MediaQuery.of(context).size.height,
|
: MediaQuery.of(context).size.height,
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// border: isDesktop
|
|
||||||
// ? Border.all(
|
|
||||||
// width: 2,
|
|
||||||
// color: Colors.white,
|
|
||||||
// // color: Color(0xFFF7F7FB),
|
|
||||||
// )
|
|
||||||
// : null,
|
|
||||||
// color: Colors.white,
|
|
||||||
// // color: Color(0xFFF7F7FB),
|
|
||||||
//
|
|
||||||
// // color: Colors.amber,
|
|
||||||
// ),
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -326,7 +372,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Text('User List',
|
const Text('User List',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF212121))),
|
color: Color(0xFF212121))),
|
||||||
@ -334,29 +380,38 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Container(
|
Container(
|
||||||
width: MediaQuery.of(context).size.width * 0.3,
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
// or use Flexible
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
onChanged: (query) {},
|
controller: searchController,
|
||||||
|
onChanged: filterUsers,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Search for a plan",
|
hintText: "Search for a User",
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
fontSize: 14, color: Color(0xFF9E9DBD)),
|
fontSize: 12, color: Color(0xFF9E9DBD)),
|
||||||
prefixIcon:
|
prefixIcon: Icon(
|
||||||
Icon(Icons.search, color: Color(0xFF9E9DBD)),
|
Icons.search,
|
||||||
|
color: Color(0xFF9E9DBD),
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.grey.shade300, width: 0.5),
|
color: Colors.grey.shade200, width: 0.5),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
// borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: Colors.blueAccent, width: 1),
|
color: Colors.grey.shade300, width: 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// SizedBox(width: 16),
|
// SizedBox(width: 16),
|
||||||
@ -451,9 +506,21 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<dynamic> users = snapshot.data!;
|
List<dynamic> users =
|
||||||
// users.sort((a, b) =>
|
filteredUsers.isNotEmpty ? filteredUsers : allUsers;
|
||||||
// int.parse(b.planId).compareTo(int.parse(a.planId)));
|
|
||||||
|
users.sort((a, b) {
|
||||||
|
DateTime dateA = DateTime.parse(a['created_on']);
|
||||||
|
DateTime dateB = DateTime.parse(b['created_on']);
|
||||||
|
|
||||||
|
return dateB
|
||||||
|
.compareTo(dateA); // Descending: newest first
|
||||||
|
});
|
||||||
|
|
||||||
|
List paginatedUser = users
|
||||||
|
.skip(currentPage * itemsPerPage)
|
||||||
|
.take(itemsPerPage)
|
||||||
|
.toList();
|
||||||
|
|
||||||
Widget table = LayoutBuilder(
|
Widget table = LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
@ -474,36 +541,36 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
label: Text('UserName',
|
label: Text('UserName',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
// color: Color(0xFF9E9DBD),
|
// color: Color(0xFF9E9DBD),
|
||||||
color: Color(0xFF9E9DBD),
|
// color: Color(0xFF9E9DBD),
|
||||||
fontSize: 14,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.w500))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Email Id',
|
label: Text('Email Id',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Role',
|
label: Text('Role',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Status',
|
label: Text('Status',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
label: Text('Actions',
|
label: Text('Actions',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9E9DBD),
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.bold))),
|
fontWeight: FontWeight.w600))),
|
||||||
],
|
],
|
||||||
rows: users.map((user) {
|
rows: paginatedUser.map((user) {
|
||||||
String userId =
|
String userId =
|
||||||
user['user_id'].toString(); // Get user ID
|
user['user_id'].toString(); // Get user ID
|
||||||
bool isSelected = selectedUserId == userId;
|
bool isSelected = selectedUserId == userId;
|
||||||
@ -513,17 +580,17 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
"${user['first_name'] ?? ''} ${user['last_name'] ?? ''}",
|
"${user['first_name'] ?? ''} ${user['last_name'] ?? ''}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(user['email'] ?? '',
|
DataCell(Text(user['email'] ?? '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
))),
|
))),
|
||||||
DataCell(Text(user['role_id'] ?? 'N/A',
|
DataCell(Text(user['role_value'] ?? 'N/A',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontFamily: "Archivo",
|
fontFamily: "Inter",
|
||||||
),
|
),
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis)),
|
overflow: TextOverflow.ellipsis)),
|
||||||
@ -540,6 +607,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
color: user['is_active'] == "1"
|
color: user['is_active'] == "1"
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: Colors.grey,
|
: Colors.grey,
|
||||||
|
fontFamily: "Inter",
|
||||||
fontWeight: FontWeight.w400),
|
fontWeight: FontWeight.w400),
|
||||||
))),
|
))),
|
||||||
DataCell(
|
DataCell(
|
||||||
@ -640,463 +708,70 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: isDesktop
|
child: Column(
|
||||||
? table
|
children: [
|
||||||
: SingleChildScrollView(
|
isDesktop
|
||||||
scrollDirection: Axis.horizontal,
|
? table
|
||||||
child: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.horizontal,
|
||||||
child: table,
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: table,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
// Previous button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_back_ios_new,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: currentPage > 0
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage--;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Page number text
|
||||||
|
Text(
|
||||||
|
'Page ${currentPage + 1}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 2),
|
||||||
|
|
||||||
|
// Next button with arrow icon
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 10,
|
||||||
|
),
|
||||||
|
onPressed: (currentPage + 1) * itemsPerPage <
|
||||||
|
users.length
|
||||||
|
? () {
|
||||||
|
setState(() {
|
||||||
|
currentPage++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// FutureBuilder<List<dynamic>>(
|
|
||||||
// future: futureUsers,
|
|
||||||
// builder: (context, snapshot) {
|
|
||||||
// if (snapshot.connectionState == ConnectionState.waiting) {
|
|
||||||
// return Center(child: CircularProgressIndicator());
|
|
||||||
// } else if (snapshot.hasError) {
|
|
||||||
// return Center(
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsets.all(16.0),
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
// children: [
|
|
||||||
// Icon(
|
|
||||||
// Icons.error_outline,
|
|
||||||
// color: Colors.redAccent,
|
|
||||||
// size: 60,
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 16),
|
|
||||||
// Text(
|
|
||||||
// "Oops!",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 22,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: Colors.redAccent,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 8),
|
|
||||||
// Text(
|
|
||||||
// "No User Available",
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 20,
|
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: Colors.grey,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 20),
|
|
||||||
// Text(
|
|
||||||
// " Please Create NewUser",
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 16,
|
|
||||||
// color: Colors.grey[700],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 20),
|
|
||||||
// // ElevatedButton.icon(
|
|
||||||
// // onPressed: () {
|
|
||||||
// // // Optional: retry logic or navigation
|
|
||||||
// // },
|
|
||||||
// // icon: Icon(Icons.refresh),
|
|
||||||
// // label: Text("Try Again"),
|
|
||||||
// // style: ElevatedButton.styleFrom(
|
|
||||||
// // backgroundColor: Colors.blueAccent,
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
|
||||||
// return Center(child: Text("No users found"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// List<dynamic> users = snapshot.data!;
|
|
||||||
// Color borderColor = Color(0xFF9E9DBD);
|
|
||||||
//
|
|
||||||
// return Expanded(
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// scrollDirection: Axis.vertical,
|
|
||||||
// child: SizedBox(
|
|
||||||
// width: MediaQuery.of(context).size.width * 1.5,
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// scrollDirection: Axis
|
|
||||||
// .horizontal, // Inner wrapper for vertical scrolling
|
|
||||||
//
|
|
||||||
// child: ConstrainedBox(
|
|
||||||
// constraints: BoxConstraints(
|
|
||||||
// minWidth:
|
|
||||||
// MediaQuery.of(context).size.width *
|
|
||||||
// 0.8),
|
|
||||||
// // constraints: BoxConstraints(minWidth: 1300),
|
|
||||||
// // width: MediaQuery.of(context).size.width ,
|
|
||||||
//
|
|
||||||
// child: Container(
|
|
||||||
// // color: Colors.grey,
|
|
||||||
// // color: Colors.amber,
|
|
||||||
// child: DataTable(
|
|
||||||
// columnSpacing:
|
|
||||||
// 20.0, // Adjust spacing between columns
|
|
||||||
// dividerThickness: 0.5,
|
|
||||||
// dataRowMinHeight:
|
|
||||||
// 60.0, // Minimum row height
|
|
||||||
// dataRowMaxHeight: 100.0,
|
|
||||||
// border: TableBorder(
|
|
||||||
// horizontalInside: BorderSide(
|
|
||||||
// width: 0.5,
|
|
||||||
// color: Colors.grey.shade200),
|
|
||||||
// ),
|
|
||||||
// columns: const [
|
|
||||||
// DataColumn(
|
|
||||||
// label: Text(
|
|
||||||
// 'User Details',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(0xFF9E9DBD),
|
|
||||||
// fontSize: 15,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataColumn(
|
|
||||||
// label: Text(
|
|
||||||
// 'Role',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(0xFF9E9DBD),
|
|
||||||
// fontSize: 15,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataColumn(
|
|
||||||
// label: Text(
|
|
||||||
// 'Level',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(0xFF9E9DBD),
|
|
||||||
// fontSize: 15,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataColumn(
|
|
||||||
// label: Text(
|
|
||||||
// 'Status',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(0xFF9E9DBD),
|
|
||||||
// fontSize: 15,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataColumn(
|
|
||||||
// label: Text(
|
|
||||||
// 'Actions',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: Color(0xFF9E9DBD),
|
|
||||||
// fontSize: 15,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// rows: users.map((user) {
|
|
||||||
// String userId = user['user_id']
|
|
||||||
// .toString(); // Get user ID
|
|
||||||
// bool isSelected =
|
|
||||||
// selectedUserId == userId;
|
|
||||||
//
|
|
||||||
// return DataRow(cells: [
|
|
||||||
// // DataCell(Text(user['user_id'].toString())),
|
|
||||||
// DataCell(Row(
|
|
||||||
// mainAxisAlignment:
|
|
||||||
// MainAxisAlignment.start,
|
|
||||||
// crossAxisAlignment:
|
|
||||||
// CrossAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// Align(
|
|
||||||
// alignment: Alignment.center,
|
|
||||||
// child: GestureDetector(
|
|
||||||
// onTap: () {
|
|
||||||
// setState(() {
|
|
||||||
// selectedUserId =
|
|
||||||
// userId; // Store clicked user ID
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// child: Container(
|
|
||||||
// width: 15, // Adjust size
|
|
||||||
// height: 15,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// // Background color
|
|
||||||
// shape: BoxShape.rectangle,
|
|
||||||
// border: Border.all(
|
|
||||||
// color: isSelected
|
|
||||||
// ? Colors.blueAccent
|
|
||||||
// : Color(0xFF9E9DBD),
|
|
||||||
// // color: Color(0xFF9E9DBD),
|
|
||||||
// // color: Color.fromRGBO(128, 128, 128, 0.6),
|
|
||||||
// width: isSelected
|
|
||||||
// ? 2
|
|
||||||
// : 1), // Grey outline
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(
|
|
||||||
// width: 50,
|
|
||||||
// ),
|
|
||||||
// Align(
|
|
||||||
// alignment: Alignment.center,
|
|
||||||
// child: Container(
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// shape: BoxShape.circle,
|
|
||||||
// border: Border.all(
|
|
||||||
// color:
|
|
||||||
// Color(0xFF9E9DBD),
|
|
||||||
// width:
|
|
||||||
// 1), // Grey outline
|
|
||||||
// ),
|
|
||||||
// child: Padding(
|
|
||||||
// padding:
|
|
||||||
// const EdgeInsets.all(
|
|
||||||
// 2.0),
|
|
||||||
// child: Container(
|
|
||||||
// width: 40, // Adjust size
|
|
||||||
// height: 40,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: Colors
|
|
||||||
// .amber, // Inner circle background
|
|
||||||
// shape: BoxShape.circle,
|
|
||||||
// ),
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisAlignment:
|
|
||||||
// MainAxisAlignment
|
|
||||||
// .center,
|
|
||||||
// crossAxisAlignment:
|
|
||||||
// CrossAxisAlignment
|
|
||||||
// .center,
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// (user['first_name'] !=
|
|
||||||
// null &&
|
|
||||||
// user['first_name']!
|
|
||||||
// .isNotEmpty)
|
|
||||||
// ? user['first_name']![
|
|
||||||
// 0]
|
|
||||||
// .toUpperCase()
|
|
||||||
// : "?",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: 18,
|
|
||||||
// fontWeight:
|
|
||||||
// FontWeight
|
|
||||||
// .bold,
|
|
||||||
// color:
|
|
||||||
// Colors.white,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )),
|
|
||||||
// ),
|
|
||||||
// SizedBox(
|
|
||||||
// width: 20,
|
|
||||||
// ),
|
|
||||||
// Column(
|
|
||||||
// mainAxisAlignment:
|
|
||||||
// MainAxisAlignment.center,
|
|
||||||
// crossAxisAlignment:
|
|
||||||
// CrossAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Text(
|
|
||||||
// "${user['first_name'] ?? ''} ${user['last_name'] ?? ''}",
|
|
||||||
// style: TextStyle(
|
|
||||||
// color:
|
|
||||||
// Colors.blueAccent,
|
|
||||||
// fontSize: 16),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 5),
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Icon(Icons.mail_outline,
|
|
||||||
// size: 15,
|
|
||||||
// color:
|
|
||||||
// Color(0xFF9E9EBE)),
|
|
||||||
// SizedBox(
|
|
||||||
// width: 10,
|
|
||||||
// ),
|
|
||||||
// Text(user['email'] ?? '')
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 5),
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// Icon(
|
|
||||||
// Icons
|
|
||||||
// .account_tree_outlined,
|
|
||||||
// size: 15,
|
|
||||||
// color:
|
|
||||||
// Color(0xFF9E9EBE)),
|
|
||||||
// SizedBox(
|
|
||||||
// width: 10,
|
|
||||||
// ),
|
|
||||||
// Text(
|
|
||||||
// user['user_type'] ?? '')
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// )),
|
|
||||||
//
|
|
||||||
// DataCell(Text(
|
|
||||||
// user['role_id'] ?? 'N/A',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: user['is_active'] == "1"
|
|
||||||
// ? Colors.black
|
|
||||||
// : Colors.grey,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataCell(Text(
|
|
||||||
// user['level_id'] ?? 'N/A',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: user['is_active'] == "1"
|
|
||||||
// ? Colors.black
|
|
||||||
// : Colors.grey,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// )),
|
|
||||||
// DataCell(GestureDetector(
|
|
||||||
// onTap: () {
|
|
||||||
// handleToggleUserStatus(
|
|
||||||
// user['user_id'],
|
|
||||||
// user['is_active'],
|
|
||||||
// user);
|
|
||||||
// },
|
|
||||||
// child: Text(
|
|
||||||
// user['is_active'] == "1"
|
|
||||||
// ? "Active"
|
|
||||||
// : "Inactive",
|
|
||||||
// style: TextStyle(
|
|
||||||
// color:
|
|
||||||
// user['is_active'] == "1"
|
|
||||||
// ? Colors.lightGreen
|
|
||||||
// : Colors.grey,
|
|
||||||
// fontWeight: FontWeight.bold),
|
|
||||||
// ))),
|
|
||||||
//
|
|
||||||
// DataCell(
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// MouseRegion(
|
|
||||||
// cursor: user['is_active'] == "0"
|
|
||||||
// ? SystemMouseCursors
|
|
||||||
// .forbidden
|
|
||||||
// : SystemMouseCursors.click,
|
|
||||||
// child: IconButton(
|
|
||||||
// icon: Icon(
|
|
||||||
// Icons.remove_red_eye,
|
|
||||||
// size: 18,
|
|
||||||
// color:
|
|
||||||
// user['is_active'] ==
|
|
||||||
// "0"
|
|
||||||
// ? Colors.grey
|
|
||||||
// : Color(
|
|
||||||
// 0xFF475569)),
|
|
||||||
// onPressed:
|
|
||||||
// user['is_active'] == "0"
|
|
||||||
// ? null
|
|
||||||
// : () {
|
|
||||||
// context.go(
|
|
||||||
// "/CreateUserDetails",
|
|
||||||
// extra: {
|
|
||||||
// "selectedUser":
|
|
||||||
// user,
|
|
||||||
// "isViewMode":
|
|
||||||
// true
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// MouseRegion(
|
|
||||||
// cursor: user['is_active'] == "0"
|
|
||||||
// ? SystemMouseCursors
|
|
||||||
// .forbidden
|
|
||||||
// : SystemMouseCursors.click,
|
|
||||||
// child: GestureDetector(
|
|
||||||
// onTap:
|
|
||||||
// user['is_active'] == "0"
|
|
||||||
// ? null
|
|
||||||
// : () {
|
|
||||||
// context.go(
|
|
||||||
// "/CreateUserDetails",
|
|
||||||
// extra: {
|
|
||||||
// "selectedUser":
|
|
||||||
// user,
|
|
||||||
// "isViewMode":
|
|
||||||
// false
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// child: Image.asset(
|
|
||||||
// 'assets/images/IconsImg/edit.png',
|
|
||||||
// width: 20,
|
|
||||||
// height: 15),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
// // MouseRegion(
|
|
||||||
// // cursor: user['is_active'] == "0"
|
|
||||||
// // ? SystemMouseCursors
|
|
||||||
// // .forbidden
|
|
||||||
// // : SystemMouseCursors.click,
|
|
||||||
// // child: IconButton(
|
|
||||||
// // icon: Icon(Icons.edit,
|
|
||||||
// // color:
|
|
||||||
// // user['is_active'] ==
|
|
||||||
// // "0"
|
|
||||||
// // ? Colors.grey
|
|
||||||
// // : Colors.green),
|
|
||||||
// // onPressed:
|
|
||||||
// // user['is_active'] == "0"
|
|
||||||
// // ? null
|
|
||||||
// // : () {
|
|
||||||
// // print(
|
|
||||||
// // "USER: $user");
|
|
||||||
// //
|
|
||||||
// // // final userJson = jsonEncode(
|
|
||||||
// // // user); // Convert user map to string
|
|
||||||
// // // final encodedUser =
|
|
||||||
// // // Uri.encodeComponent(
|
|
||||||
// // // userJson);
|
|
||||||
// //
|
|
||||||
// // context.go(
|
|
||||||
// // "/CreateUserDetails",
|
|
||||||
// // extra: {
|
|
||||||
// // "selectedUser":
|
|
||||||
// // user,
|
|
||||||
// // "isViewMode":
|
|
||||||
// // false
|
|
||||||
// // },
|
|
||||||
// // );
|
|
||||||
// // },
|
|
||||||
// // ),
|
|
||||||
// // ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ]);
|
|
||||||
// }).toList(),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
//
|
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
class Plan {
|
class Plan {
|
||||||
final String planId;
|
final String planId;
|
||||||
|
final String? employeeCode;
|
||||||
final String tripTitle;
|
final String tripTitle;
|
||||||
final String tripType;
|
final String tripType;
|
||||||
final String status;
|
final String status;
|
||||||
@ -17,6 +18,7 @@ class Plan {
|
|||||||
|
|
||||||
Plan({
|
Plan({
|
||||||
required this.planId,
|
required this.planId,
|
||||||
|
this.employeeCode,
|
||||||
required this.tripTitle,
|
required this.tripTitle,
|
||||||
required this.tripType,
|
required this.tripType,
|
||||||
required this.status,
|
required this.status,
|
||||||
@ -33,8 +35,10 @@ class Plan {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Plan.fromJson(Map<String, dynamic> json) {
|
factory Plan.fromJson(Map<String, dynamic> json) {
|
||||||
|
print('Parsing Plan from JSON: $json');
|
||||||
return Plan(
|
return Plan(
|
||||||
planId: json['plan_id'],
|
planId: json['plan_id'],
|
||||||
|
employeeCode: json['employee_code'],
|
||||||
tripTitle: json['trip_title'],
|
tripTitle: json['trip_title'],
|
||||||
tripType: json['trip_type_value'],
|
tripType: json['trip_type_value'],
|
||||||
status: json['status'] == "0" ? "Inactive" : "Active",
|
status: json['status'] == "0" ? "Inactive" : "Active",
|
||||||
|
|||||||
@ -38,6 +38,8 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
Color? bodyColor;
|
Color? bodyColor;
|
||||||
|
|
||||||
Color _myTravelRequestColor = Color(0xFF475569); // Default color
|
Color _myTravelRequestColor = Color(0xFF475569); // Default color
|
||||||
|
EdgeInsets _myTravelRequestPadding =
|
||||||
|
EdgeInsets.symmetric(horizontal: 8, vertical: 4);
|
||||||
Color _myApprovalsColor = Color(0xFF475569); // Default color
|
Color _myApprovalsColor = Color(0xFF475569); // Default color
|
||||||
|
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -183,45 +185,88 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.25,
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 15),
|
if (userData?["role"] == "Org Admin" ||
|
||||||
|
userData?["role"] == "Travel Admin")
|
||||||
|
MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
onEnter: (_) {
|
||||||
|
setState(() {
|
||||||
|
// _myTravelRequestPadding =
|
||||||
|
// EdgeInsets.symmetric(horizontal: 12, vertical: 78);
|
||||||
|
// _myTravelRequestColor =
|
||||||
|
// Color(0xFF114D8B); // Change color on hover
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (_) {
|
||||||
|
setState(() {
|
||||||
|
// _myTravelRequestPadding = EdgeInsets.symmetric(
|
||||||
|
// horizontal: 8, vertical: 4); // Normal padding
|
||||||
|
// _myTravelRequestColor =
|
||||||
|
// Color(0xFF475569); // Revert color when hover ends
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
context.go('/listAllPlan');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"All Trips",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
// color: Color(0xFF475569),
|
||||||
|
color: _myTravelRequestColor,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
onEnter: (_) {
|
onEnter: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_myTravelRequestColor =
|
// _myTravelRequestPadding =
|
||||||
Colors.blue; // Change color on hover
|
// EdgeInsets.symmetric(horizontal: 12, vertical: 78);
|
||||||
|
// _myTravelRequestColor =
|
||||||
|
// Color(0xFF114D8B); // Change color on hover
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onExit: (_) {
|
onExit: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_myTravelRequestColor =
|
// _myTravelRequestPadding = EdgeInsets.symmetric(
|
||||||
Color(0xFF475569); // Revert color when hover ends
|
// horizontal: 8, vertical: 4); // Normal padding
|
||||||
|
// _myTravelRequestColor =
|
||||||
|
// Color(0xFF475569); // Revert color when hover ends
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.go('/listPlan');
|
context.go('/listPlan');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"My Travel Request",
|
"My Travel Request",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
// color: Color(0xFF475569),
|
// color: Color(0xFF475569),
|
||||||
color: _myTravelRequestColor,
|
color: _myTravelRequestColor,
|
||||||
fontFamily: "Archivo"),
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 25),
|
const SizedBox(width: 20),
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
onEnter: (_) {
|
onEnter: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_myApprovalsColor = Colors.blue; // Change color on hover
|
_myApprovalsColor =
|
||||||
|
Color(0xFF114D8B); // Change color on hover
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onExit: (_) {
|
onExit: (_) {
|
||||||
@ -230,18 +275,19 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
Color(0xFF475569); // Revert color when hover ends
|
Color(0xFF475569); // Revert color when hover ends
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.go('/ApprovalList');
|
context.go('/ApprovalList');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"My Approvals",
|
"My Approvals",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: _myApprovalsColor,
|
color: _myApprovalsColor,
|
||||||
// color: Color(0xFF475569),
|
// color: Color(0xFF475569),
|
||||||
fontFamily: "Archivo"),
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import 'package:frontend/Screens/userManagement/create_user/create_user.dart';
|
|||||||
import 'package:frontend/Screens/userManagement/user_List.dart';
|
import 'package:frontend/Screens/userManagement/user_List.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../Screens/allTrips/list_all_plans.dart';
|
||||||
import '../Screens/approvals/approval_list.dart';
|
import '../Screens/approvals/approval_list.dart';
|
||||||
import '../Screens/group/group.dart';
|
import '../Screens/group/group.dart';
|
||||||
import '../Screens/group/groupList.dart';
|
import '../Screens/group/groupList.dart';
|
||||||
@ -28,6 +29,10 @@ final GoRouter router = GoRouter(
|
|||||||
path: '/home',
|
path: '/home',
|
||||||
builder: (context, state) => HomePage(),
|
builder: (context, state) => HomePage(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/listAllPlan',
|
||||||
|
builder: (context, state) => ListAllPlans(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/listPlan',
|
path: '/listPlan',
|
||||||
builder: (context, state) => ListPlans(),
|
builder: (context, state) => ListPlans(),
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:frontend/utils/auth_utils.dart';
|
import 'package:frontend/utils/auth_utils.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
import 'package:universal_html/js.dart';
|
||||||
import '../../config/apiUrl.dart';
|
import '../../config/apiUrl.dart';
|
||||||
|
|
||||||
class ApiService {
|
class ApiService {
|
||||||
@ -486,4 +489,102 @@ class ApiService {
|
|||||||
throw Exception('Failed to load country list');
|
throw Exception('Failed to load country list');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<dynamic>> fetchTrainCountryList() async {
|
||||||
|
final String apiUrldata = '$apiUrl/api/getTrainCodeMaster';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
try {
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
print("Country - $data");
|
||||||
|
|
||||||
|
if (!data.containsKey('data') || data['data'] is! List) {
|
||||||
|
throw Exception(
|
||||||
|
"Invalid response format: 'data' field is missing or not a List");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data['data'];
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load country list');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getPdfDownload(planId) async {
|
||||||
|
final String apiUrldata = '$apiUrl/api/plans/download?plan_id=$planId';
|
||||||
|
|
||||||
|
// final String apiUrldata = '$apiUrl/auth/googlelogin';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
try {
|
||||||
|
print("PDf Dowloaded");
|
||||||
|
|
||||||
|
// 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', 'trip_plan_$planId.pdf')
|
||||||
|
..click();
|
||||||
|
|
||||||
|
// Revoke the download URL to free up resources
|
||||||
|
html.Url.revokeObjectUrl(url);
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else if (response.statusCode == 404) {
|
||||||
|
// showDialog(
|
||||||
|
// context: context,
|
||||||
|
// builder: (BuildContext context) {
|
||||||
|
// return AlertDialog(
|
||||||
|
// title: Text('File not found.'),
|
||||||
|
// // content: Text('File not found.'),
|
||||||
|
// actions: [
|
||||||
|
// TextButton(
|
||||||
|
// child: Text('OK'),
|
||||||
|
// onPressed: () {
|
||||||
|
// Navigator.of(context).pop(); // Close the dialog
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load plans');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,36 @@ Future<String?> getOrgId() async {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> getRoleUser() 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["role"]?.toString();
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getForexCardNumber() 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["forex_pre_paid_card_number"]?.toString();
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<String?> getTripPlanAction() async {
|
Future<String?> getTripPlanAction() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final String? userDataString = prefs.getString('user_data');
|
final String? userDataString = prefs.getString('user_data');
|
||||||
|
|||||||
40
pubspec.lock
40
pubspec.lock
@ -41,6 +41,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -73,6 +81,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.6"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -200,6 +216,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.8.1"
|
version: "14.8.1"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.6"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -565,6 +589,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
universal_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: universal_html
|
||||||
|
sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -46,6 +46,7 @@ dependencies:
|
|||||||
http_parser: ^4.1.2
|
http_parser: ^4.1.2
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
web: ^1.1.0
|
web: ^1.1.0
|
||||||
|
universal_html: ^2.2.4
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
@ -76,6 +77,7 @@ flutter:
|
|||||||
- assets/images/login/VectorG.png
|
- assets/images/login/VectorG.png
|
||||||
- assets/images/IconsImg/delete.png
|
- assets/images/IconsImg/delete.png
|
||||||
- assets/images/IconsImg/edit.png
|
- assets/images/IconsImg/edit.png
|
||||||
|
- assets/images/IconsImg/planPdf_icon.png
|
||||||
|
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user