diff --git a/lib/Screens/allTrips/list_all_plans.dart b/lib/Screens/allTrips/list_all_plans.dart index 04b11e1..5d55f45 100644 --- a/lib/Screens/allTrips/list_all_plans.dart +++ b/lib/Screens/allTrips/list_all_plans.dart @@ -231,6 +231,11 @@ class _ListAllPlansState extends State { // List plansJson = []; List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load plans'); } @@ -264,6 +269,11 @@ class _ListAllPlansState extends State { print("Plan Deleted successfully!"); print("Response: ${response.body}"); initializeData(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -281,7 +291,10 @@ class _ListAllPlansState extends State { if (confirmed) { try { - Map planData = await ApiService.getViewPlan(planId); + Map planData = await ApiService().getViewPlan( + planId, + context, + ); print("ViewAAA - $planData"); refresh(); // postPlanData(planData, planId); @@ -321,6 +334,11 @@ class _ListAllPlansState extends State { approverData = List>.from( data['data']['approver_data'] ?? [], ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print('Failed to load'); label = "Error - Failed to load data"; @@ -1071,7 +1089,7 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); // Close popup manually - ApiService.viewPlan( + ApiService().viewPlan( context, plan.planId, isViewMode: @@ -1095,7 +1113,7 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); - ApiService.viewPlan( + ApiService().viewPlan( context, plan.planId, isViewMode: @@ -1137,6 +1155,7 @@ class _ListAllPlansState extends State { ); apiService .getPdfDownload( + context, plan.planId, ); }, @@ -1162,10 +1181,10 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); - apiService - .getForexPdfDownload( - plan.forexId, - ); + apiService.getForexPdfDownload( + context, + plan.forexId, + ); }, ), IconButton( @@ -1389,7 +1408,7 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); // Close popup manually - ApiService.viewPlan( + ApiService().viewPlan( context, plan.planId, isViewMode: @@ -1414,7 +1433,7 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); - ApiService.viewPlan( + ApiService().viewPlan( context, plan.planId, isViewMode: @@ -1454,10 +1473,10 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); - apiService - .getPdfDownload( - plan.planId, - ); + apiService.getPdfDownload( + context, + plan.planId, + ); }, ), @@ -1482,10 +1501,10 @@ class _ListAllPlansState extends State { Navigator.pop( context, ); - apiService - .getForexPdfDownload( - plan.forexId, - ); + apiService.getForexPdfDownload( + context, + plan.forexId, + ); }, ), diff --git a/lib/Screens/allTrips/list_all_plans_24july2025.dart b/lib/Screens/allTrips/list_all_plans_24july2025.dart deleted file mode 100644 index f3415df..0000000 --- a/lib/Screens/allTrips/list_all_plans_24july2025.dart +++ /dev/null @@ -1,1928 +0,0 @@ -import 'dart:convert'; -import 'dart:core'; -import 'package:frontend/Screens/allTrips/remarks_list.dart'; -import 'package:frontend/data/models/plan.dart'; -import 'package:go_router/go_router.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter/material.dart'; -import 'package:frontend/config/apiUrl.dart'; -import 'package:intl/intl.dart'; -import 'package:responsive_builder/responsive_builder.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../../routes/custom_appBar.dart'; -import '../../routes/custom_drawer.dart'; -import '../../routes/mainLayout.dart'; -import '../../services/apiService.dart'; -import '../../utils/auth_utils.dart'; -import '../../utils/pagination.dart'; -import '../../utils/travelAgent_remarks.dart'; -import '../../widgets/custom_popup.dart'; -import '../../widgets/popup_listPlan_action.dart'; - -class ListAllPlans extends StatefulWidget { - const ListAllPlans({super.key}); - - @override - _ListAllPlansState createState() => _ListAllPlansState(); -} - -class _ListAllPlansState extends State { - final ApiService apiService = ApiService(); - - int currentPage = 0; - int itemsPerPage = 10; - - String? userId; - String? orgId; - String? roleUser; - String? token; - String? TripPlanAction; - - Color? layoutColor; - Color? bodyColor; - late Future> futurePlans; - List allPlans = []; - List filteredPlans = []; - TextEditingController searchController = TextEditingController(); - late bool _listDialogShown = false; - - @override - void initState() { - super.initState(); - _checkAuthAndLoadData(); - - // // If token exists, load the dashboard data - // WidgetsBinding.instance.addPostFrameCallback((_) { - // initializeData(); - // loadInitialData(); - // - // // futurePlans.then((plans) { - // // setState(() { - // // allPlans = plans; - // // filteredPlans = plans; - // // }); - // // }); - // }); - // // futurePlans = fetchPlans(); - } - - void _checkAuthAndLoadData() async { - final String? token = await getToken(); // Your async function to get token - - if (token == null || token.isEmpty) { - // Token doesn't exist → redirect to login - context.go( - "/", - ); // or use: router.go("/") if you're using `GoRouter` directly - return; - } else { - getToken(); - initializeData(); - loadInitialData(); - } - } - - void filterPlans(String query) { - print("allPlans before filtering: $allPlans"); - final lowerQuery = query.toLowerCase(); - setState(() { - filteredPlans = - allPlans.where((plan) { - return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? - false) || - (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? - false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); - currentPage = 0; - }); - print("filteredPlans: $filteredPlans"); - } - - void loadInitialData() async { - String? layoutString = await getLayoutColor(); - String? bodyStringColor = await getBodyColor(); - - setState(() { - layoutColor = - layoutString != null - ? Color(int.parse(layoutString)) - : Colors.redAccent; - - bodyColor = - bodyStringColor != null - ? Color(int.parse(bodyStringColor)) - : Colors.white; - }); - } - - Future initializeData() async { - token = await getToken(); - userId = await getUserId(); - orgId = await getOrgId(); - roleUser = await getRoleUser(); - TripPlanAction = await getTripPlanAction(); - - if (token == null || userId == null) { - print("Token or USerId missing"); - return; - } else { - setState(() { - futurePlans = fetchPlans(); - }); - - // Wait for futurePlans to be fetched and update allPlans - futurePlans.then((plans) { - setState(() { - allPlans = plans; // Set allPlans after the fetch is complete - filteredPlans = - allPlans; // You can update filteredPlans too if needed - }); - }); - } - } - - void refresh() { - print("Refresj"); - setState(() { - futurePlans = fetchPlans(); - }); - - // Wait for futurePlans to be fetched and update allPlans - futurePlans.then((plans) { - setState(() { - allPlans = plans; // Set allPlans after the fetch is complete - filteredPlans = allPlans; // You can update filteredPlans too if needed - }); - }); - } - - Future getUserId() async { - final prefs = await SharedPreferences.getInstance(); - final String? userDataString = prefs.getString('user_data'); - - if (userDataString != null) { - try { - final Map userData = jsonDecode(userDataString); - return userData["user_id"]?.toString(); - } catch (e) { - return null; - } - } - return null; - } - - Future getOrgId() async { - final prefs = await SharedPreferences.getInstance(); - final String? userDataString = prefs.getString('user_data'); - - if (userDataString != null) { - try { - final Map userData = jsonDecode(userDataString); - return userData["org_id"]?.toString(); - } catch (e) { - return null; - } - } - return null; - } - - Future getToken() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('auth_token'); - } - - // Fetch API Data - Future> fetchPlans() async { - late String apiUrldata; - - apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; - - // if (roleUser == "Org Admin") { - // apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; - // } else { - // apiUrldata = - // '$apiUrl/api/plans/findTravelAgentPlanList?org_id=$orgId&user_id=$userId'; - // } - - if (token == null) { - throw Exception('Token not found. Please log in.'); - } - - final response = await http.get( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', // Add token here - 'Content-Type': 'application/json', - 'app-signature': 'ts-traveltool-2025-signature-123456', - }, - ); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - // List plansJson = []; - List plansJson = data['data']; - return plansJson.map((json) => Plan.fromJson(json)).toList(); - } else { - throw Exception('Failed to load plans'); - } - } - - Future postPlanData(planData, planId) async { - final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; - - final token = await getToken(); // Fetch token - - if (token == null) { - throw Exception('Token not found. Please log in.'); - } - - planData['is_active'] = "0"; - - print("POSTPlanTesting------- $planData"); // Add plan_id for update - - try { - final response = await http.post( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - 'app-signature': 'ts-traveltool-2025-signature-123456', - }, - body: jsonEncode(planData), // Convert map to JSON - ); - - if (response.statusCode == 200) { - print("Plan Deleted successfully!"); - print("Response: ${response.body}"); - initializeData(); - } else { - print("Failed to submit plan. Status: ${response.statusCode}"); - print("Error: ${response.body}"); - } - } catch (e) { - print(" Error submitting plan: $e"); - } - } - - void deletePlan(String planId) async { - bool confirmed = await apiService.showCancelConfirmationDialog( - context, - layoutColor, - ); - - if (confirmed) { - try { - Map planData = await ApiService.getViewPlan(planId); - print("ViewAAA - $planData"); - refresh(); - // postPlanData(planData, planId); - } catch (e) { - print("Error fetching plan: $e"); - } - } else { - print("User cancelled"); - } - } - - Future _showDetails(id) async { - _listDialogShown = true; - final String apiUrldata = - '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; - final String? token = await getToken(); - String label = ""; - List> approverData = []; - - if (token == null) { - print('Token not found. Please log in.'); - label = "Error - Token not found"; - } else { - try { - final response = await http.get( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - 'app-signature': 'ts-traveltool-2025-signature-123456', - }, - ); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - label = data['data']['model_heading'] ?? ""; - approverData = List>.from( - data['data']['approver_data'] ?? [], - ); - } else { - print('Failed to load'); - label = "Error - Failed to load data"; - } - } catch (e) { - print('Exception occurred: $e'); - label = "Error - Something went wrong"; - } - } - - showDialog( - context: context, - builder: - (context) => AlertDialog( - title: Text( - label, - style: GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (approverData.isNotEmpty) - SizedBox( - height: 250, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: DataTable( - columns: [ - // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - rows: - approverData.asMap().entries.map((entry) { - final index = entry.key + 1; - final item = entry.value; - - final approverIdKey = item.keys.firstWhere( - (k) => k.endsWith('_id'), - orElse: () => '', - ); - final statusKey = item.keys.firstWhere( - (k) => k.endsWith('_status'), - orElse: () => '', - ); - - final approverId = - item[approverIdKey]?.toString() ?? '-'; - final statusText = - item[statusKey]?.toString() ?? 'Unknown'; - - return DataRow( - cells: [ - // DataCell(Text('$index')), - // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), - DataCell( - SizedBox( - child: Text( - statusText, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ], - ); - }).toList(), - ), - ), - ), - ) - else - Text( - "-- no data. --", - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w300, - ), - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - _listDialogShown = false; - }, - child: Text("Close"), - ), - ], - ), - ); - } - - Widget build(BuildContext context) { - return ResponsiveBuilder( - builder: (context, sizingInfo) { - bool isDesktop = - sizingInfo.deviceScreenType == DeviceScreenType.desktop; - - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // backgroundColor: Color(0xFFFCFCFC), - - // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), - // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), - appBar: CustomAppBar(isDesktop: isDesktop), - drawer: CustomDrawer(isDesktop: false), - body: Padding( - padding: - isDesktop - ? EdgeInsets.symmetric( - horizontal: - MediaQuery.of(context).size.width * - 0.1, // 30% of screen width as horizontal padding - vertical: - MediaQuery.of(context).size.height * - 0, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(0), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupListLayout(isDesktop)), - ], - ), - ), - ); - }, - ); - } - - Widget buildGroupListLayout(bool isDesktop) { - return Container( - // decoration: BoxDecoration( - // color: Colors.amber, - // // color: bodyColor, - // // color: Color(0xFFE1F5FE), - // // border: Border.all( - // // // color: Color(0xFFF7F7FB), - // // color: Colors.white, - // // width: 3.5) - // ), - child: buildTableLayout(isDesktop), - ); - } - - Widget buildTableLayout(isDesktop) { - // String _formatDate(String rawDate) { - // try { - // final dateTime = DateTime.parse(rawDate); - // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); - // } catch (e) { - // return rawDate; // fallback if parsing fails - // } - // } - - String _formatDate(String rawDate) { - try { - final dateTime = DateTime.parse(rawDate); - return DateFormat( - 'dd, MMM yyyy HH:mm', - ).format(dateTime); // 24-hour format - } catch (e) { - return rawDate; // fallback if parsing fails - } - } - - return Container( - margin: isDesktop ? EdgeInsets.all(10.0) : null, - padding: - isDesktop - ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) - : EdgeInsets.all(3.0), - height: - isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, - decoration: BoxDecoration( - border: - isDesktop - ? Border.all( - width: 2, - color: Colors.white, - // color: Color(0xFFF7F7FB), - ) - : null, - color: isDesktop ? Colors.white : Color(0xFFFCFCFC), - // color: Color(0xFFF7F7FB), - - // color: Colors.amber, - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - // padding: const EdgeInsets.all(10.0), - color: isDesktop ? Colors.white : Color(0xFFFCFCFC), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Text( - 'All Trips', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - - // style: TextStyle( - // fontFamily: "Inter", - // fontSize: isDesktop ? 16 : 14, - // fontWeight: FontWeight.w600, - // color: const Color(0xFF212121) ) - ), - ], - ), - - SizedBox(width: 1), - Spacer(), - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - hintStyle: TextStyle( - fontSize: 12, - color: Color(0xFF9E9DBD), - ), - prefixIcon: Icon( - Icons.search, - color: Color(0xFF9E9DBD), - size: 18, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - style: GoogleFonts.poppins(fontSize: 12), - ), - ), - - // SizedBox(width: 16), - Spacer(), - // ElevatedButton( - // style: ElevatedButton.styleFrom( - // foregroundColor: Colors.white, - // backgroundColor: Colors.blueAccent, - // ), - // onPressed: () { - // context.go('/createPlan', extra: { - // 'orgId': orgId, - // }); - // if (!isDesktop) Navigator.pop(context); - // }, - // child: Row( - // children: [ - // Icon(Icons.add_circle, color: Colors.white), - // SizedBox(width: 5), - // Text('NewPlan'), - // - // ], - // ), - // ), - - // ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: Color(0xFF114D8B), - // foregroundColor: Colors.white, - // disabledBackgroundColor: Color(0xFF114D8B), - // disabledForegroundColor: Colors.white, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8), - // side: BorderSide(color: Color(0xFF114D8B), width: 2), - // ), - // padding: - // EdgeInsets.symmetric(horizontal: 20, vertical: 12), - // ), - // onPressed: () { - // if (TripPlanAction == "Plan Creation Not Allowed") { - // showDialog( - // context: context, - // builder: (context) => AlertDialog( - // title: Text( - // "Action Not Allowed", - // style: TextStyle( - // fontSize: 18, fontWeight: FontWeight.bold), - // ), - // content: Text( - // "Plan Creation Not Allowed For This User."), - // actions: [ - // TextButton( - // onPressed: () => Navigator.pop(context), - // child: Text( - // "OK", - // style: TextStyle(color: layoutColor), - // ), - // ), - // ], - // ), - // ); - // } else { - // context.go('/createPlan', extra: { - // 'orgId': orgId, - // }); - // if (!isDesktop) Navigator.pop(context); - // } - // }, - // child: Row( - // mainAxisSize: - // MainAxisSize.min, // Ensures content fits nicely - // children: [ - // Text( - // "Add New Plan", - // style: TextStyle(fontSize: 13), - // ), - // SizedBox(width: 8), // spacing between icon and text - // Icon( - // Icons.add_circle_outline_rounded, - // size: 15, - // color: Colors.white, - // ), - // ], - // ), - // ), - ], - ), - const SizedBox(height: 10), - - if (!isDesktop) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: MediaQuery.of(context).size.width * 0.88, - height: 35, - child: TextField( - controller: searchController, - onChanged: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - hintStyle: TextStyle( - fontSize: 12, - color: Color(0xFF9E9DBD), - ), - prefixIcon: Icon( - Icons.search, - color: Color(0xFF9E9DBD), - size: 18, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - style: GoogleFonts.poppins(fontSize: 12), - ), - ), - ], - ), - FutureBuilder>( - future: futurePlans, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - final adjHgt = MediaQuery.of(context).size.height; - - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // const Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // const SizedBox(height: 1), - // Text( - // "Oops!", - // style: GoogleFonts.poppins( - // fontSize: 12, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent), - // - // ), - SizedBox(height: adjHgt / 4), - Text( - " No Data Found", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey, - ), - ), - ], - ), - ), - ); - } - - // List plans = snapshot.data!; - - // List plans = - // filteredPlans.isNotEmpty ? filteredPlans : allPlans; - - List plans = - searchController.text.isEmpty ? allPlans : filteredPlans; - - plans.sort( - (a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId)), - ); - - List 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: [ - DataColumn( - label: Expanded( - child: Text( - 'S.NO', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - - DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - rows: - // paginatedPlans.map((plan) - paginatedPlans.asMap().entries.map((entry) { - int index = entry.key; - var plan = entry.value; - return DataRow( - cells: [ - DataCell( - Text( - // "${index + 1}", - "${(currentPage * itemsPerPage) + index + 1}", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, // Allows wrapping - overflow: - TextOverflow - .visible, // Ensures full display - maxLines: null, - ), - ), - DataCell( - Text( - plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.employeeCode ?? " - ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.userName.isNotEmpty - ? plan.userName - : plan.travellerName, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - - DataCell( - Text( - _formatDate(plan.createdOn), - // plan.createdOn, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - GestureDetector( - onTap: () => _showDetails(plan.planId), - child: Container( - width: - double - .infinity, // Set your desired fixed size (equal width and height) - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor( - plan.statusValue, - ), - borderRadius: BorderRadius.circular( - 10, - ), - ), - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: getStatusTextColor( - plan.statusValue, - ), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ), - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: - (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Row( - mainAxisSize: - MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, - ), - tooltip: - 'View The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - true, - ); - }, - ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - false, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - deletePlan( - plan.planId, - ); - }, - ), - IconButton( - icon: Icon( - Icons.download, - color: Color( - 0xFF114D8B, - ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop( - context, - ); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Trip Comments', - onPressed: () { - showDialog( - context: - context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Admin", - ), - ); - }, - ), - ], - ), - ), - ), - ], - ), - ], - ), - ), - ], - ); - }).toList(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedPlans) { - return ListView.builder( - itemCount: paginatedPlans.length, - itemBuilder: (context, index) { - final plan = paginatedPlans[index]; - return Card( - color: Colors.white, - margin: EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 3, // Control the strength of the shadow - // shadowColor: Colors.black54, // Softer shadow - // clipBehavior: Clip.antiAlias, - child: Padding( - padding: const EdgeInsets.all(10.0), - - child: Row( - children: [ - // Container( - // // color: Colors.red.shade50, - // child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - // crossAxisAlignment: - // CrossAxisAlignment.center, - // children: [ - // Image.asset( - // plan.tripType == 'Domestic' - // ? 'assets/images/IconsImg/Domestic_new.png' - // : 'assets/images/IconsImg/International_new.png', - // // width: 65, - // height: - // plan.tripType == 'Domestic' - // ? 37 - // : 40, - // ), - // ], - // ), - // ), - // SizedBox(width: 10), - Expanded( - child: Container( - // color: Colors.yellow.shade50, - child: Column( - children: [ - Row( - // mainAxisAlignment: - // MainAxisAlignment.spaceBetween, - children: [ - Container( - // color: Colors.red.shade50, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Image.asset( - plan.tripType == 'Domestic' - ? 'assets/images/IconsImg/Domestic_new.png' - : 'assets/images/IconsImg/International_new.png', - // width: 65, - height: - plan.tripType == - 'Domestic' - ? 28 - : 30, - ), - ], - ), - ), - SizedBox(width: 10), - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '${plan.tripTitle}', - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: - FontWeight.w500, - ), - softWrap: - true, // Allows wrapping - overflow: - TextOverflow - .visible, // Ensures full display - maxLines: null, - ), - ], - ), - ), - - SizedBox(width: 10), - Column( - children: [ - // PlanPopupMenu( - // plan: plan, - // deletePlan: deletePlan, - // apiService: apiService, - // layoutColor: layoutColor, - // ), - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: - (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 1, - // horizontal: 8, - // vertical: 8, - ), - child: Row( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, - ), - tooltip: - 'View The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - true, - ); - }, - ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: - 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - ApiService.viewPlan( - context, - plan.planId, - isViewMode: - false, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - deletePlan( - plan.planId, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .download, - color: Color( - 0xFF114D8B, - ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop( - context, - ); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons - .comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Trip Comments', - onPressed: () { - showDialog( - context: - context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId.toString(), - layoutColorForUser: - layoutColor!, - role: - "Admin", - ), - ); - }, - ), - ], - ), - ), - ), - ], - ), - ], - ), - ], - ), - - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - crossAxisAlignment: - CrossAxisAlignment.end, - - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - // plan.userName.isNotEmpty - // ? plan.userName - // : '${plan.travellerName}', - '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} (${plan.employeeCode ?? 'No Data'})', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - ), - ), - // Text( - // // plan.userName.isNotEmpty - // // ? plan.userName - // // : '${plan.travellerName}', - // // '(${plan.employeeCode ?? 'No Data'})', - // plan.employeeCode ?? - // 'No Data', - // style: GoogleFonts.poppins( - // fontSize: 9, - // color: Colors.black87, - // ), - // ), - Text( - '${_formatDate(plan.createdOn)}', - style: GoogleFonts.poppins( - fontSize: 9, - color: Colors.grey, - ), - ), - ], - ), - Column( - mainAxisAlignment: - MainAxisAlignment.end, - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - GestureDetector( - onTap: - () => _showDetails( - plan.planId, - ), - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - decoration: BoxDecoration( - color: getStatusColor( - plan.statusValue, - ), - borderRadius: - BorderRadius.circular( - 8, - ), - ), - child: Text( - plan.statusValue, - style: GoogleFonts.poppins( - color: - getStatusTextColor( - plan.statusValue, - ), - fontSize: 9, - fontWeight: - FontWeight.w600, - ), - ), - ), - ), - ], - ), - ], - ), - ], - ), - ), - ), - ], - ), - - // padding: EdgeInsets.symmetric(vertical: 12), - // child: Container( - // // color: Colors.yellow, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // // Status and Employee Code - // Row( - // mainAxisAlignment: - // MainAxisAlignment.spaceBetween, - // children: [ - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // GestureDetector( - // onTap: - // () => _showDetails(plan.planId), - // child: Container( - // padding: EdgeInsets.symmetric( - // horizontal: 8, - // vertical: 4, - // ), - // decoration: BoxDecoration( - // color: getStatusColor( - // plan.statusValue, - // ), - // borderRadius: - // BorderRadius.circular(8), - // ), - // child: Text( - // plan.statusValue, - // style: TextStyle( - // color: getStatusTextColor( - // plan.statusValue, - // ), - // fontSize: 10, - // fontWeight: FontWeight.w600, - // ), - // ), - // ), - // ), - // ], - // ), - // Column( - // children: [ - // // PlanPopupMenu( - // // plan: plan, - // // deletePlan: deletePlan, - // // apiService: apiService, - // // layoutColor: layoutColor, - // // ), - // PopupMenuButton( - // color: Colors.white, - // padding: EdgeInsets.zero, - // offset: Offset(0, 30), - // icon: Icon( - // Icons.more_vert, - // color: Color(0xFF475569), - // size: 14, - // ), - // itemBuilder: - // (context) => [ - // CustomPopupMenuEntry( - // child: Container( - // padding: - // EdgeInsets.symmetric( - // horizontal: 1, - // vertical: 1, - // // horizontal: 8, - // // vertical: 8, - // ), - // child: Row( - // mainAxisSize: - // MainAxisSize.min, - // mainAxisAlignment: - // MainAxisAlignment - // .center, - // children: [ - // IconButton( - // icon: Icon( - // Icons - // .remove_red_eye, - // color: Color( - // 0xFF475569, - // ), - // size: 18, - // ), - // tooltip: - // 'View The Trip Details', - // onPressed: () { - // Navigator.pop( - // context, - // ); // Close popup manually - // ApiService.viewPlan( - // context, - // plan.planId, - // isViewMode: - // true, - // ); - // }, - // ), - // if (plan.statusValue == - // "Pending Approval" || - // plan.statusValue == - // "Partially Approved") - // IconButton( - // icon: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15, - // ), - // tooltip: - // 'Edit The Trip Details', - // onPressed: () { - // Navigator.pop( - // context, - // ); - // ApiService.viewPlan( - // context, - // plan.planId, - // isViewMode: - // false, - // ); - // }, - // ), - // IconButton( - // icon: Icon( - // Icons - // .cancel_rounded, - // size: 18, - // ), - // tooltip: - // 'Cancellation The Trip Details', - // onPressed: () { - // Navigator.pop( - // context, - // ); - // deletePlan( - // plan.planId, - // ); - // }, - // ), - // IconButton( - // icon: Icon( - // Icons.download, - // color: Color( - // 0xFF114D8B, - // ), - // size: 18, - // ), - // tooltip: - // 'Download The PDF', - // onPressed: () { - // Navigator.pop( - // context, - // ); - // apiService - // .getPdfDownload( - // plan.planId, - // ); - // }, - // ), - // IconButton( - // icon: const Icon( - // Icons.comment, - // color: Color( - // 0xFF475569, - // ), - // size: 11, - // ), - // tooltip: - // 'Trip Comments', - // onPressed: () { - // showDialog( - // context: - // context, - // builder: - // ( - // context, - // ) => CommentModalList( - // // planId: plan.planId, - // planId: - // plan.planId - // .toString(), - // layoutColorForUser: - // layoutColor!, - // role: - // "Admin", - // ), - // ); - // }, - // ), - // ], - // ), - // ), - // ), - // ], - // ), - // ], - // ), - // ], - // ), - // - // SizedBox(height: 2), - // // Trip Id and Trip Name - // Row( - // children: [ - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Text( - // ' ${plan.tripTitle}', - // style: TextStyle( - // fontSize: 12, - // fontFamily: "Inter", - // fontWeight: FontWeight.bold, - // ), - // ), - // ], - // ), - // ], - // ), - // SizedBox(height: 2), - // // Type and Created On - // Row( - // mainAxisAlignment: - // MainAxisAlignment.spaceBetween, - // children: [ - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Text( - // ' ${plan.tripType}', - // style: TextStyle( - // fontSize: 9, - // color: Colors.black87, - // fontFamily: "Inter", - // ), - // ), - // ], - // ), - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Text( - // '${_formatDate(plan.createdOn)}', - // style: TextStyle( - // fontSize: 9, - // color: Colors.black87, - // fontFamily: "Inter", - // ), - // ), - // ], - // ), - // ], - // ), - // SizedBox(height: 3), - // // Name - // Row( - // children: [ - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Text( - // plan.userName.isNotEmpty - // ? '${plan.userName}' - // : '${plan.travellerName}', - // style: TextStyle( - // fontSize: 9, - // fontFamily: "Inter", - // color: Colors.black87, - // ), - // ), - // ], - // ), - // Spacer(), - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Text( - // plan.employeeCode ?? 'No Data', - // style: TextStyle( - // fontSize: 9, - // fontFamily: "Inter", - // color: Colors.black87, - // ), - // ), - // ], - // ), - // ], - // ), - // // Actions - // ], - // ), - // ), - ), - ); - }, - ); - } - - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: - isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : buildMobileCardView(paginatedPlans)), - - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedPlans), - ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: plans.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], - ), - ); - }, - ), - ], - ), - ), - ), - ); - } - - // Helper functions for colors - Color getStatusColor(String status) { - if (status == "Partially Approved") return Colors.yellow.shade100; - if (status == "Approved") return Colors.green.shade100; - if (status == "Completed") return Colors.green.shade500; - if (status == "Rejected") return Colors.red.shade100; - return Colors.grey.shade100; - } - - Color getStatusTextColor(String status) { - if (status == "Partially Approved" || - status == "Approved" || - status == "Rejected") { - return Colors.black; - } else if (status == "Completed") { - return Colors.white; - } - return Colors.grey; - } -} diff --git a/lib/Screens/allTrips/plan_info_mdl.dart b/lib/Screens/allTrips/plan_info_mdl.dart index b960e10..7b05dc6 100644 --- a/lib/Screens/allTrips/plan_info_mdl.dart +++ b/lib/Screens/allTrips/plan_info_mdl.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:frontend/services/apiService.dart'; import 'package:google_fonts/google_fonts.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; @@ -22,6 +23,7 @@ class TripInformation extends StatefulWidget { } class _TripInformationState extends State { + final ApiService apiService = ApiService(); late Future> _tripInfoFuture; Future> fetchComments() async { @@ -48,6 +50,11 @@ class _TripInformationState extends State { final Map dataMap = jsonData['data'] as Map; return dataMap; + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return {}; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load comments'); } diff --git a/lib/Screens/allTrips/remarks_list.dart b/lib/Screens/allTrips/remarks_list.dart index e50c097..8771635 100644 --- a/lib/Screens/allTrips/remarks_list.dart +++ b/lib/Screens/allTrips/remarks_list.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import '../../config/apiUrl.dart'; +import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; class CommentModalList extends StatefulWidget { @@ -24,6 +25,7 @@ class CommentModalList extends StatefulWidget { } class _CommentModalListState extends State { + final ApiService apiService = ApiService(); late Future>> _commentsFuture; // Future>> fetchComments1() async { @@ -68,6 +70,11 @@ class _CommentModalListState extends State { final jsonData = json.decode(response.body); final List dataList = jsonData['data']; return dataList.cast>(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load comments'); } diff --git a/lib/Screens/allTrips/travel_agent_list.dart b/lib/Screens/allTrips/travel_agent_list.dart index d4903e7..3ddc47d 100644 --- a/lib/Screens/allTrips/travel_agent_list.dart +++ b/lib/Screens/allTrips/travel_agent_list.dart @@ -1,5 +1,7 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:core'; +import 'dart:html' as html; import 'package:frontend/Screens/allTrips/plan_info_mdl.dart'; import 'package:frontend/data/models/plan.dart'; import 'package:frontend/utils/travelAgent_remarks.dart'; @@ -29,6 +31,7 @@ class TravelAgentListPlans extends StatefulWidget { class _TravelAgentListPlansState extends State { final ApiService apiService = ApiService(); + late StreamSubscription _popStateListener; int currentPage = 0; int itemsPerPage = 10; @@ -39,6 +42,8 @@ class _TravelAgentListPlansState extends State { String? token; String? TripPlanAction; + late bool _dialogShown = false; + Color? layoutColor; Color? bodyColor; late Future> futurePlans; @@ -50,6 +55,7 @@ class _TravelAgentListPlansState extends State { void initState() { super.initState(); _checkAuthAndLoadData(); + checkbackbutton(); // getToken(); // // WidgetsBinding.instance.addPostFrameCallback((_) { @@ -82,6 +88,81 @@ class _TravelAgentListPlansState extends State { loadInitialData(); } + void checkbackbutton() async { + roleUser = await getRoleUser(); + print(roleUser); + if (roleUser == "Travel Agent") { + print("user"); + final prefs = await SharedPreferences.getInstance(); + final String? msUserStr = prefs.getString('is_microsoft_user'); + final bool msUser = msUserStr?.toLowerCase() == 'true'; + print("msUser - $msUser"); + html.window.history.pushState(null, '', html.window.location.href); + + if (!msUser) { + _popStateListener = html.window.onPopState.listen((event) { + if (!_dialogShown && mounted) { + _showBackConfirmationDialog(); + } + + // Re-push to prevent leaving + html.window.history.pushState(null, '', html.window.location.href); + }); + } + } + } + + @override + void dispose() { + _popStateListener.cancel(); // ✅ Remove the browser popstate listener + super.dispose(); + } + + Future _logoutAndRedirect(BuildContext context) async { + print("logue 0"); + await apiService.logout(context); + + // // Example: clear session or shared preferences + // final prefs = await SharedPreferences.getInstance(); + // await prefs.clear(); + + // context.go("/"); + + print("logue 1"); + } + + void _showBackConfirmationDialog() { + if (!mounted) return; + + _dialogShown = true; + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text("Confirm"), + content: Text("Do you want to logout?"), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); // Close dialog + _dialogShown = false; + }, + child: Text("Cancel"), + ), + TextButton( + onPressed: () async { + // Navigator.pop(context); // Close dialog + _dialogShown = false; + await _logoutAndRedirect(context); + }, + child: Text("Logout"), + ), + ], + ), + ); + } + void filterPlans(String query) { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); @@ -221,6 +302,11 @@ class _TravelAgentListPlansState extends State { // List plansJson = []; List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load plans'); } @@ -254,6 +340,11 @@ class _TravelAgentListPlansState extends State { print("Plan Deleted successfully!"); print("Response: ${response.body}"); initializeData(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -271,7 +362,10 @@ class _TravelAgentListPlansState extends State { if (confirmed) { try { - Map planData = await ApiService.getViewPlan(planId); + Map planData = await ApiService().getViewPlan( + planId, + context, + ); print("ViewAAA - $planData"); refresh(); // postPlanData(planData, planId); @@ -839,12 +933,13 @@ class _TravelAgentListPlansState extends State { Navigator.pop( context, ); // Close popup manually - ApiService.viewPlanTravelAgent( - context, - plan.planId, - isViewMode: - true, - ); + ApiService() + .viewPlanTravelAgent( + context, + plan.planId, + isViewMode: + true, + ); }, ), // IconButton( @@ -888,6 +983,7 @@ class _TravelAgentListPlansState extends State { ); apiService .getPdfDownload( + context, plan.planId, ); }, @@ -915,6 +1011,7 @@ class _TravelAgentListPlansState extends State { ); apiService .getForexPdfDownload( + context, plan.forexId, ); }, diff --git a/lib/Screens/approvals/approval_list.dart b/lib/Screens/approvals/approval_list.dart index 7b7d3b1..bba41eb 100644 --- a/lib/Screens/approvals/approval_list.dart +++ b/lib/Screens/approvals/approval_list.dart @@ -204,6 +204,11 @@ class _ApprovalListState extends State { List plansJson = data['data']; print('plansJson $plansJson'); return plansJson.map((json) => Plan.fromJson(json)).toList(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load plans'); } @@ -237,6 +242,11 @@ class _ApprovalListState extends State { print("Plan Deleted successfully!"); print("Response: ${response.body}"); initializeData(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -263,7 +273,10 @@ class _ApprovalListState extends State { if (confirmed) { try { - Map planData = await ApiService.getViewPlan(planId); + Map planData = await ApiService().getViewPlan( + planId, + context, + ); print("ViewAAA - $planData"); refresh(); // postPlanData(planData, planId); @@ -297,6 +310,11 @@ class _ApprovalListState extends State { final Map? resData = json.decode(response.body); return resData?["data"]; + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return {}; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load plans'); } @@ -402,6 +420,11 @@ class _ApprovalListState extends State { approverData = List>.from( data['data']['approver_data'] ?? [], ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print('Failed to load'); label = "Error - Failed to load data"; @@ -1157,7 +1180,7 @@ class _ApprovalListState extends State { Navigator.pop( context, ); - ApiService.viewPlanForApprover( + ApiService().viewPlanForApprover( context, plan.planId, plan.approverId, @@ -1204,6 +1227,7 @@ class _ApprovalListState extends State { ); apiService .getPdfDownload( + context, plan.planId, ); }, @@ -1229,10 +1253,10 @@ class _ApprovalListState extends State { Navigator.pop( context, ); - apiService - .getForexPdfDownload( - plan.forexId, - ); + apiService.getForexPdfDownload( + context, + plan.forexId, + ); }, ), @@ -1511,10 +1535,10 @@ class _ApprovalListState extends State { Navigator.pop( context, ); - apiService - .getPdfDownload( - plan.planId, - ); + apiService.getPdfDownload( + context, + plan.planId, + ); }, ), if (plan.statusValue == @@ -1538,10 +1562,10 @@ class _ApprovalListState extends State { Navigator.pop( context, ); - apiService - .getForexPdfDownload( - plan.forexId, - ); + apiService.getForexPdfDownload( + context, + plan.forexId, + ); }, ), IconButton( diff --git a/lib/Screens/approvals/approval_list_backup24july2025.dart b/lib/Screens/approvals/approval_list_backup24july2025.dart deleted file mode 100644 index 59e1a8d..0000000 --- a/lib/Screens/approvals/approval_list_backup24july2025.dart +++ /dev/null @@ -1,1816 +0,0 @@ -import 'dart:convert'; -import 'dart:core'; -import 'package:frontend/data/models/plan.dart'; -import 'package:go_router/go_router.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter/material.dart'; -import 'package:frontend/config/apiUrl.dart'; -import 'package:intl/intl.dart'; -import 'package:responsive_builder/responsive_builder.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../../routes/custom_appBar.dart'; -import '../../routes/custom_drawer.dart'; -import '../../services/apiService.dart'; -import '../../utils/auth_utils.dart'; -import '../../utils/pagination.dart'; -import '../../utils/travelAgent_remarks.dart'; -import '../../widgets/custom_popup.dart'; -import '../../widgets/popup_listPlan_action.dart'; -import '../allTrips/remarks_list.dart'; - -class ApprovalList extends StatefulWidget { - const ApprovalList({super.key}); - - @override - _ApprovalListState createState() => _ApprovalListState(); -} - -class _ApprovalListState extends State { - final ApiService apiService = ApiService(); - late Future> futurePlans; - String? userId; - String? orgId; - String? token; - - Color? layoutColor; - Color? bodyColor; - - int currentPage = 0; - int itemsPerPage = 10; - List allPlans = []; - List filteredPlans = []; - TextEditingController searchController = TextEditingController(); - - late bool _dialogShown = false; - Map successList = {}; - - @override - void initState() { - super.initState(); - _checkAuthAndLoadData(); - // getToken(); - // - // WidgetsBinding.instance.addPostFrameCallback((_) { - // initializeData(); - // loadInitialData(); - // }); - - // futurePlans = fetchPlans(); - } - - void _checkAuthAndLoadData() async { - final String? token = await getToken(); // Your async function to get token - - if (token == null || token.isEmpty) { - // Token doesn't exist → redirect to login - context.go( - "/", - ); // or use: router.go("/") if you're using `GoRouter` directly - return; - } - getToken(); - initializeData(); - loadInitialData(); - } - - 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 loadAllGroups() async { - // try { - // final result = await apiService.fetchUserApprovalList(); - // // setState(() { - // // apiAllGroups = result; - // // }); - // print("Fetched services: $result"); - // } catch (e) { - // print('Error fetching role list: $e'); - // } - // } - - Future initializeData() async { - token = await getToken(); - userId = await getUserId(); - orgId = await getOrgId(); - - if (token == null || userId == null) { - print("Token or USerId missing"); - return; - } else { - setState(() { - futurePlans = fetchPlans(); - }); - print("Token availa"); - // Wait for futurePlans to be fetched and update allPlans - futurePlans.then((plans) { - setState(() { - allPlans = plans; // Set allPlans after the fetch is complete - filteredPlans = - allPlans; // You can update filteredPlans too if needed - }); - }); - } - } - - void refresh() { - print("Refresj"); - setState(() { - futurePlans = fetchPlans(); - }); - - // Wait for futurePlans to be fetched and update allPlans - futurePlans.then((plans) { - setState(() { - allPlans = plans; // Set allPlans after the fetch is complete - filteredPlans = allPlans; // You can update filteredPlans too if needed - }); - }); - } - - Future getUserId() async { - final prefs = await SharedPreferences.getInstance(); - final String? userDataString = prefs.getString('user_data'); - - if (userDataString != null) { - try { - final Map userData = jsonDecode(userDataString); - return userData["user_id"]?.toString(); - } catch (e) { - return null; - } - } - return null; - } - - Future getOrgId() async { - final prefs = await SharedPreferences.getInstance(); - final String? userDataString = prefs.getString('user_data'); - - if (userDataString != null) { - try { - final Map userData = jsonDecode(userDataString); - return userData["org_id"]?.toString(); - } catch (e) { - return null; - } - } - return null; - } - - Future getToken() async { - final prefs = await SharedPreferences.getInstance(); - return prefs.getString('auth_token'); - } - - // Fetch API Data - Future> fetchPlans() async { - // final String apiUrldata = '$apiUrl/api/plans'; - // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; - // final String apiUrldata = '$apiUrl/api/plans?user_id=$userId'; - // final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId'; - - final String apiUrldata = - '$apiUrl/api/plans/findApprovalList?user_id=$userId&org_id=$orgId'; - - // api/plans?org_id=1&user_id=1 - // 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', // Add token here - 'Content-Type': 'application/json', - }, - ); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - print('plansJson1'); - List plansJson = data['data']; - print('plansJson $plansJson'); - return plansJson.map((json) => Plan.fromJson(json)).toList(); - } else { - throw Exception('Failed to load plans'); - } - } - - Future 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 planData = await getViewPlan(planId); - // print("ViewAAA - $planData"); - // - // postPlanData(planData, planId); - // } catch (e) { - // print("Error fetching plan: $e"); - // } - - bool confirmed = await apiService.showCancelConfirmationDialog( - context, - layoutColor, - ); - - if (confirmed) { - try { - Map planData = await ApiService.getViewPlan(planId); - print("ViewAAA - $planData"); - refresh(); - // postPlanData(planData, planId); - } catch (e) { - print("Error fetching plan: $e"); - } - } else { - print("User cancelled"); - } - } - - Future> 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? resData = json.decode(response.body); - - return resData?["data"]; - } else { - throw Exception('Failed to load plans'); - } - } - - void viewPlanforApprover( - String planId, - String? approverId, - String? delegaterId, { - bool isViewMode = false, - bool isApprover = true, - }) async { - try { - Map planData = await getViewPlan(planId); - - print("ViewAAA - $planData"); - - context.replace( - '/approver/plans', - extra: { - 'planData': planData, - 'approverId': approverId, - 'delegaterId': delegaterId, - 'isViewMode': isViewMode, - 'isApprover': isApprover, - }, - ); - - // context.go( - // '/createPlan', - // extra: { - // 'planData': planData, - // 'isViewMode': isViewMode, - // 'isApprover': isApprover, - // }, - // ); - } catch (e) { - print("Error fetching plan: $e"); - } - } - - // void viewPlan(String planId, {bool isViewMode = false}) async { - // try { - // Map planData = await getViewPlan(planId); - // print("ViewAAA - $planData"); - // - // context.go('/createPlan', - // extra: {'planData': planData, 'isViewMode': isViewMode}); - // } catch (e) { - // print("Error fetching plan: $e"); - // } - // } - - 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.approverName?.toLowerCase().contains(lowerQuery) ?? - false) || - (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || - (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); - }).toList(); - currentPage = 0; - }); - print("filteredPlans: $filteredPlans"); - } - - // Future _showDetails(Id) async { - // _dialogShown = true; - // final String apiUrldata = '$apiUrl/api/plans/get_plan_approval_status?plan_id=$Id'; - // final String? token = await getToken(); - // - // if (token == null) { - // print('Token not found. Please log in.'); - // successList = { - // "model_heading": "Error - Token not found", - // "approver_data": [], - // }; - // } else { - // try { - // 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); - // print(data['data']); - // successList = data['data']; - // } else { - // print('Failed to load'); - // successList = { - // "model_heading": "Error - Failed to load data", - // "approver_data": [], - // }; - // } - // } catch (e) { - // print('Exception occurred: $e'); - // successList = { - // "model_heading": "Error - Something went wrong", - // "approver_data": [], - // }; - // } - // } - // - // showDialog( - // context: context, - // builder: (context) => AlertDialog( - // title: Text( - // successList["model_heading"] ?? '', - // style: GoogleFonts.poppins( - // fontSize: 18, - // fontWeight: FontWeight.w600, - // color: Colors.black, - // ), - // overflow: TextOverflow.ellipsis, - // ), - // content: SingleChildScrollView( - // child: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // // Text( - // // successList["Trip Title"] ?? '', - // // style: TextStyle(fontWeight: FontWeight.bold), - // // ), - // const SizedBox(height: 8), - // (successList["approver_data"] != null && successList["approver_data"].isNotEmpty) - // ? SizedBox( - // height: 250, - // child: SingleChildScrollView( - // scrollDirection: Axis.horizontal, - // child: SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: DataTable( - // columns: [ - // DataColumn(label: Text('S.No', style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, - // ))), - // DataColumn(label: Text('ID', style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, - // ))), - // DataColumn(label: Text('Reason', style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black, - // ))), - // ], - // rows: successList["approver_data"].asMap().entries.map((entry) { - // final index = entry.key + 1; - // final rawData = entry.value['a1_id']; - // final data = (rawData == null || rawData.toString().trim().isEmpty) ? '-' : rawData.toString(); - // final reason = entry.value['a1_status'] ?? 'Unknown'; - // - // return DataRow( - // cells: [ - // DataCell(Text('$index', style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, - // ))), - // DataCell(SizedBox( - // width: 180, - // child: Text(data, style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, - // ), overflow: TextOverflow.ellipsis), - // )), - // DataCell(SizedBox( - // width: 280, - // child: Text(reason, style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, - // ), overflow: TextOverflow.ellipsis), - // )), - // ], - // ); - // }).toList(), - // ), - // ), - // ), - // ) - // : Text("-- no data. --", style: GoogleFonts.poppins( - // fontSize: 14, fontWeight: FontWeight.w200, color: Colors.black, - // )), - // ], - // ), - // ), - // actions: [ - // TextButton( - // onPressed: () { - // Navigator.pop(context); - // _dialogShown = false; - // }, - // child: Text("Close"), - // ), - // ], - // ), - // ); - // } - - Future _showDetails(id) async { - _dialogShown = true; - final String apiUrldata = - '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; - final String? token = await getToken(); - String label = ""; - List> approverData = []; - - if (token == null) { - print('Token not found. Please log in.'); - label = "Error - Token not found"; - } else { - try { - final response = await http.get( - Uri.parse(apiUrldata), - headers: { - 'Authorization': 'Bearer $token', - 'Content-Type': 'application/json', - }, - ); - - if (response.statusCode == 200) { - final data = json.decode(response.body); - label = data['data']['model_heading'] ?? ""; - approverData = List>.from( - data['data']['approver_data'] ?? [], - ); - } else { - print('Failed to load'); - label = "Error - Failed to load data"; - } - } catch (e) { - print('Exception occurred: $e'); - label = "Error - Something went wrong"; - } - } - - showDialog( - context: context, - builder: - (context) => AlertDialog( - title: Text( - label, - style: GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (approverData.isNotEmpty) - SizedBox( - height: 250, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: DataTable( - columns: [ - // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - rows: - approverData.asMap().entries.map((entry) { - final index = entry.key + 1; - final item = entry.value; - - final approverIdKey = item.keys.firstWhere( - (k) => k.endsWith('_id'), - orElse: () => '', - ); - final statusKey = item.keys.firstWhere( - (k) => k.endsWith('_status'), - orElse: () => '', - ); - - final approverId = - item[approverIdKey]?.toString() ?? '-'; - final statusText = - item[statusKey]?.toString() ?? 'Unknown'; - - return DataRow( - cells: [ - // DataCell(Text('$index')), - // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), - DataCell( - SizedBox( - child: Text( - statusText, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ], - ); - }).toList(), - ), - ), - ), - ) - else - Text( - "-- no data. --", - style: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w300, - ), - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - _dialogShown = false; - }, - child: Text("Close"), - ), - ], - ), - ); - } - - Widget build(BuildContext context) { - return ResponsiveBuilder( - builder: (context, sizingInfo) { - bool isDesktop = - sizingInfo.deviceScreenType == DeviceScreenType.desktop; - - return Scaffold( - backgroundColor: Color(0xFFf5f5f5), - // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), - // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), - appBar: CustomAppBar(isDesktop: isDesktop), - drawer: CustomDrawer(isDesktop: false), - body: Padding( - padding: - isDesktop - ? EdgeInsets.symmetric( - horizontal: - MediaQuery.of(context).size.width * - 0.1, // 30% of screen width as horizontal padding - vertical: - MediaQuery.of(context).size.height * - 0, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(0), - child: Row( - children: [ - // if (isDesktop) CustomDrawer(isDesktop: true), - Expanded(child: buildGroupListLayout(isDesktop)), - ], - ), - ), - ); - }, - ); - } - - Widget buildGroupListLayout(bool isDesktop) { - return Container( - // decoration: BoxDecoration( - // // color: Colors.amber, - // // color: bodyColor, - // color: Color(0xFFE1F5FE), - // border: Border.all( - // // color: Color(0xFFF7F7FB), - // color: Colors.white, - // width: 3.5)), - child: buildTableLayout(isDesktop), - ); - } - - Widget buildTableLayout(isDesktop) { - // String _formatDate(String rawDate) { - // try { - // final dateTime = DateTime.parse(rawDate); - // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); - // } catch (e) { - // return rawDate; // fallback if parsing fails - // } - // } - - final adjHgt = MediaQuery.of(context).size.height; - - String _formatDate(String rawDate) { - try { - final dateTime = DateTime.parse(rawDate); - return DateFormat( - 'dd, MMM yyyy HH:mm', - ).format(dateTime); // 24-hour format - } catch (e) { - return rawDate; // fallback if parsing fails - } - } - - return Container( - margin: isDesktop ? EdgeInsets.all(10.0) : null, - padding: - isDesktop - ? const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20) - : EdgeInsets.all(3.0), - height: - isDesktop - ? MediaQuery.of(context).size.height * 0.98 - : MediaQuery.of(context).size.height, - decoration: BoxDecoration( - border: - isDesktop - ? Border.all( - width: 2, - color: Colors.white, - // color: Color(0xFFF7F7FB), - ) - : null, - color: isDesktop ? Colors.white : Color(0xFFFCFCFC), - // color: Color(0xFFF7F7FB), - - // color: Colors.amber, - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - // padding: const EdgeInsets.all(10.0), - color: isDesktop ? Colors.white : Color(0xFFFCFCFC), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Divider( - // thickness: 0.2, // how "thick" the line is - // color: Colors.grey, // optional - // ), - Row( - children: [ - Row( - children: [ - Text( - 'My Approvals', - style: GoogleFonts.poppins( - fontSize: isDesktop ? 16 : 14, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - // style: TextStyle( - // fontFamily: "Inter", - // fontSize: isDesktop ? 16 : 14, - // fontWeight: FontWeight.w600, - // ) - ), - ], - ), - if (isDesktop) - SizedBox(width: MediaQuery.of(context).size.width * 0.23), - - if (isDesktop) - Container( - width: MediaQuery.of(context).size.width * 0.2, - height: 40, - child: TextField( - controller: searchController, - onChanged: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - hintStyle: TextStyle( - fontSize: 12, - color: Color(0xFF9E9DBD), - ), - prefixIcon: Icon( - Icons.search, - color: Color(0xFF9E9DBD), - size: 18, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - style: GoogleFonts.poppins(fontSize: 12), - ), - ), - // SizedBox(width: 16), - ], - ), - - if (!isDesktop) SizedBox(height: 5), - isDesktop - ? SizedBox.shrink() - : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: MediaQuery.of(context).size.width * 0.82, - height: 35, - child: TextField( - controller: searchController, - onChanged: filterPlans, - decoration: InputDecoration( - hintText: "Search...", - hintStyle: TextStyle( - fontSize: 12, - color: Color(0xFF9E9DBD), - ), - prefixIcon: Icon( - Icons.search, - color: Color(0xFF9E9DBD), - size: 18, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade200, - width: 0.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - style: GoogleFonts.poppins(fontSize: 12), - ), - ), - // SizedBox(width: 16), - ], - ), - - const SizedBox(height: 10), - FutureBuilder>( - 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, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // Icon(Icons.error_outline, - // color: Colors.redAccent, size: 60), - // SizedBox(height: 1), - // Text("Oops!", - // style: TextStyle( - // fontSize: 22, - // fontWeight: FontWeight.bold, - // color: Colors.redAccent)), - SizedBox(height: adjHgt / 4), - Text( - "No Data Found", - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.grey, - ), - ), - ], - ), - ), - ); - } - - // List plans = - // filteredPlans.isNotEmpty ? filteredPlans : allPlans; - - // List plans = - // searchController.text.isEmpty ? allPlans : filteredPlans; - - List plans = - searchController.text.isEmpty ? allPlans : filteredPlans; - - plans.sort( - (a, b) => - int.parse(b.planId).compareTo(int.parse(a.planId)), - ); - - List 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: [ - DataColumn( - label: Text( - 'Trip ID', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Trip Name', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Emp Code', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Traveller', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Approver', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Trip Type', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Created On', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Status', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - DataColumn( - label: Text( - 'Actions', - style: GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - rows: - paginatedPlans.map((plan) { - return DataRow( - cells: [ - DataCell( - Text( - plan.planId, - style: TextStyle( - fontSize: 13, - fontFamily: "Archivo", - ), - ), - ), - - DataCell( - Text( - plan.tripTitle, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - softWrap: true, - overflow: TextOverflow.ellipsis, - ), - ), - DataCell( - Text( - plan.employeeCode ?? " - ", - 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", - ), - softWrap: true, - overflow: TextOverflow.ellipsis, - ), - ), - // DataCell( - // Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // mainAxisAlignment: - // MainAxisAlignment.center, - // - // children: [ - // SizedBox(height: 1), - // Text( - // plan.userName.isNotEmpty - // ? plan.userName - // : plan.travellerName, - // style: TextStyle( - // fontSize: 12, - // fontFamily: "Inter", - // ), - // ), - // SizedBox(height: 3), - // Expanded( - // child: Text( - // "(${plan.employeeCode})" ?? - // " - ", - // style: GoogleFonts.poppins( - // fontSize: 10, - // // fontFamily: "Inter", - // ), - // ), - // ), - // ], - // ), - // ), - DataCell( - Text( - plan.approverName ?? " ", - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - DataCell( - Text( - plan.tripType, - style: TextStyle( - fontSize: 13, - fontFamily: "Inter", - ), - ), - ), - - DataCell( - Text( - // plan.createdOn, - _formatDate(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, - // ), - // borderRadius: BorderRadius.circular( - // 8, - // ), - // ), - // - // child: Text( - // plan.statusValue, - // textAlign: TextAlign.center, - // style: TextStyle( - // color: getStatusTextColor( - // plan.statusValue, - // ), - // fontSize: 12, - // fontFamily: "Inter", - // fontWeight: FontWeight.w400, - // ), - // ), - // ), - // ), - DataCell( - GestureDetector( - onTap: () => _showDetails(plan.planId), - child: Container( - width: double.infinity, - height: 25, - alignment: Alignment.center, - decoration: BoxDecoration( - color: getStatusColor( - plan.statusValue, - ), - borderRadius: BorderRadius.circular( - 8, - ), - ), - child: Text( - plan.statusValue, - textAlign: TextAlign.center, - style: TextStyle( - color: getStatusTextColor( - plan.statusValue, - ), - fontSize: 12, - fontFamily: "Inter", - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ), - DataCell( - Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: - (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Row( - mainAxisSize: - MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, - ), - tooltip: - 'View The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - viewPlanforApprover( - plan.planId, - plan.approverId, - plan.delegaterId, - isViewMode: - true, - isApprover: - true, - ); - }, - ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - print( - "Approver Edit : ${plan.approverId}", - ); - print( - "Approver Edit2 : ${plan.delegaterId}", - ); - print( - "Approver Edit3 : ${plan.approver_status}", - ); - Navigator.pop( - context, - ); - ApiService.viewPlanForApprover( - context, - plan.planId, - plan.approverId, - plan.delegaterId, - plan.approver_status, - isViewMode: - false, - isApprover: - true, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); - deletePlan( - plan.planId, - ); - }, - ), - IconButton( - icon: Icon( - Icons.download, - color: Color( - 0xFF114D8B, - ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop( - context, - ); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons.comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Approver Comment', - onPressed: () { - showDialog( - context: - context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Approver", - ), - ); - }, - ), - ], - ), - ), - ), - ], - ), - ], - ), - ), - // DataCell(Row(children: [ - // IconButton( - // icon: const Icon( - // Icons.remove_red_eye, - // color: Color(0xFF475569), - // size: 18, - // ), - // onPressed: () => viewPlanforApprover( - // plan.planId, - // isViewMode: true, - // isApprover: true)), - // - // GestureDetector( - // onTap: () => ApiService.viewPlanForApprover( - // context, plan.planId, - // isViewMode: false, isApprover: true), - // child: Image.asset( - // 'assets/images/IconsImg/edit.png', - // width: 20, - // height: 15), - // ), - // - // // IconButton( - // // icon: const Icon(Icons.edit, - // // color: Colors.green), - // // onPressed: () => viewPlanforApprover(plan.planId, - // // isViewMode: false) ), - // - // 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(), - ), - ); - }, - ); - - Widget buildMobileCardView(List paginatedPlans) { - return ListView.builder( - itemCount: paginatedPlans.length, - itemBuilder: (context, index) { - final plan = paginatedPlans[index]; - return Card( - color: Colors.white, - margin: EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 3, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Container( - // color: Colors.yellow.shade50, - child: Column( - children: [ - Row( - // mainAxisAlignment: - // MainAxisAlignment.spaceBetween, - children: [ - Container( - // color: Colors.red.shade50, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Image.asset( - plan.tripType == 'Domestic' - ? 'assets/images/IconsImg/Domestic_new.png' - : 'assets/images/IconsImg/International_new.png', - // width: 65, - height: plan.tripType == 'Domestic' ? 28 : 30, - ), - ], - ), - ), - SizedBox(width: 10), - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - ' ${plan.tripTitle}', - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: - FontWeight.w500, - ), - ), - ], - ), - ), - - SizedBox(width: 10), - Column( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: - (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 1, - vertical: 1, - // horizontal: 8, - // vertical: 8, - ), - child: Row( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - IconButton( - icon: Icon( - Icons - .remove_red_eye, - color: Color( - 0xFF475569, - ), - size: 18, - ), - tooltip: - 'View The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - viewPlanforApprover( - plan.planId, - plan.approverId, - plan.delegaterId, - isViewMode: true, - isApprover: true, - ); - }, - ), - if (plan.statusValue == - "Pending Approval" || - plan.statusValue == - "Partially Approved") - IconButton( - icon: Image.asset( - 'assets/images/IconsImg/edit.png', - width: 20, - height: - 15, - ), - tooltip: - 'Edit The Trip Details', - onPressed: () { - Navigator.pop( - context, - ); // Close popup manually - viewPlanforApprover( - plan.planId, - plan.approverId, - plan.delegaterId, - isViewMode: true, - isApprover: true, - ); - }, - ), - IconButton( - icon: Icon( - Icons - .cancel_rounded, - size: 18, - ), - tooltip: - 'Cancellation The Trip Details', - onPressed: () { - Navigator.pop(context); - deletePlan(plan.planId); - }, - ), - IconButton( - icon: Icon( - Icons - .download, - color: Color( - 0xFF114D8B, - ), - size: 18, - ), - tooltip: - 'Download The PDF', - onPressed: () { - Navigator.pop(context); - apiService - .getPdfDownload( - plan.planId, - ); - }, - ), - IconButton( - icon: const Icon( - Icons - .comment, - color: Color( - 0xFF475569, - ), - size: 11, - ), - tooltip: - 'Trip Comments', - onPressed: () { - showDialog( - context: context, - builder: - ( - context, - ) => CommentModalList( - // planId: plan.planId, - planId: - plan.planId - .toString(), - layoutColorForUser: - layoutColor!, - role: - "Approver", - ), - ); - }, - ), - ], - ), - ), - ), - ], - ), - ], - ), - ], - ), - - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - crossAxisAlignment: - CrossAxisAlignment.end, - - children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} ${(plan.employeeCode) ?? ''}', - style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - ), - ), - Text( - '${_formatDate(plan.createdOn)}', - style: GoogleFonts.poppins( - fontSize: 9, - color: Colors.grey, - ), - ), - ], - ), - Column( - mainAxisAlignment: - MainAxisAlignment.end, - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - GestureDetector( - onTap: - () => _showDetails( - plan.planId, - ), - child: Container( - padding: - EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - decoration: BoxDecoration( - color: getStatusColor( - plan.statusValue, - ), - borderRadius: - BorderRadius.circular( - 8, - ), - ), - child: Text( - plan.statusValue, - style: GoogleFonts.poppins( - color: - getStatusTextColor( - plan.statusValue, - ), - fontSize: 9, - fontWeight: - FontWeight.w600, - ), - ), - ), - ), - ], - ), - ], - ), - ], - ), - ), - ), - ], - ), - ), - ); - }, - ); - } - - return Expanded( - child: Column( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: - isDesktop - ? (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.vertical, - child: table, - )) - : (searchController.text.isNotEmpty && - filteredPlans.isEmpty - ? Center( - child: Text( - "No Matches Found", - style: GoogleFonts.poppins( - fontSize: 14, - color: Colors.grey, - ), - ), - ) - : buildMobileCardView(paginatedPlans)), - - // child: isDesktop - // ? SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: table, // <-- your existing table - // ) - // : buildMobileCardView(paginatedPlans), - ), - PaginationControls( - currentPage: currentPage, - itemsPerPage: itemsPerPage, - totalItems: plans.length, - activeColor: layoutColor, // your theme color - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, - onItemsPerPageChanged: (items) { - setState(() { - itemsPerPage = items; - currentPage = 0; - }); - }, - ), - ], - ), - ); - }, - ), - ], - ), - ), - ), - ); - } -} - -// Helper functions for colors -Color getStatusColor(String status) { - if (status == "Partially Approved") return Colors.yellow.shade100; - if (status == "Approved") return Colors.green.shade100; - if (status == "Completed") return Colors.green.shade500; - if (status == "Rejected") return Colors.red.shade100; - return Colors.grey.shade100; -} - -Color getStatusTextColor(String status) { - if (status == "Partially Approved" || - status == "Approved" || - status == "Rejected") { - return Colors.black; - } else if (status == "Completed") { - return Colors.white; - } - return Colors.grey; -} diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index 31ec96b..d7666e3 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:html' as html; // import 'dart:ui' as html; +import 'package:encrypt/encrypt.dart' as encrypt; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -103,12 +104,24 @@ class _LoginWidgetState extends State { print("userData12 - $userRole"); } - await apiService.getOrganizationData(); + await apiService.getOrganizationData(context); } catch (e) { print('Error decoding token: $e'); } } + String encryptLoginPayload(Map credentials) { + final key = encrypt.Key.fromUtf8('1234567890123456'); // 16 chars key + final iv = encrypt.IV.fromUtf8('abcdefghijklmnop'); // 16 chars IV + final encrypter = encrypt.Encrypter( + encrypt.AES(key, mode: encrypt.AESMode.cbc), + ); + + final jsonString = jsonEncode(credentials); + final encrypted = encrypter.encrypt(jsonString, iv: iv); + return encrypted.base64; + } + Future _login(BuildContext context) async { if (_formKey.currentState!.validate()) { setState(() { @@ -117,6 +130,11 @@ class _LoginWidgetState extends State { const String url = '$apiUrl/api/auth/login'; + final encryptedData = encryptLoginPayload({ + 'email': _emailController.text.trim(), + 'password': _passwordController.text.trim(), + }); + try { final response = await http.post( Uri.parse(url), @@ -125,10 +143,11 @@ class _LoginWidgetState extends State { 'Accept': 'application/json', 'app-signature': 'ts-traveltool-2025-signature-123456', }, - body: jsonEncode({ - 'email': _emailController.text.trim(), - 'password': _passwordController.text.trim(), - }), + body: jsonEncode({'encrypted': true, 'payload': encryptedData}), + // body: jsonEncode({ + // 'email': _emailController.text.trim(), + // 'password': _passwordController.text.trim(), + // }), ); if (response.statusCode == 200) { @@ -145,7 +164,7 @@ class _LoginWidgetState extends State { msg: "You're in!", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, - timeInSecForIosWeb: 2, + timeInSecForIosWeb: 3, backgroundColor: Colors.green, textColor: Colors.white, fontSize: 18.0, @@ -159,6 +178,11 @@ class _LoginWidgetState extends State { } else { context.go('/listPlan'); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { final body = jsonDecode(response.body); final messages = body['messages']; @@ -173,7 +197,7 @@ class _LoginWidgetState extends State { msg: "Login Failed: $errorMessage", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, - timeInSecForIosWeb: 2, + timeInSecForIosWeb: 6, backgroundColor: Colors.red, textColor: Colors.white, fontSize: 16.0, @@ -200,8 +224,8 @@ class _LoginWidgetState extends State { msg: "Login Failed: $e", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, - timeInSecForIosWeb: 2, - backgroundColor: Colors.red, + timeInSecForIosWeb: 5, + backgroundColor: Colors.green, textColor: Colors.white, fontSize: 16.0, webBgColor: "linear-gradient(to right, #dc1c13, #dc1c13)", @@ -250,6 +274,11 @@ class _LoginWidgetState extends State { fontSize: 16.0, webBgColor: "linear-gradient(to right, #28a745, #28a745)", ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print(response); @@ -318,6 +347,11 @@ class _LoginWidgetState extends State { fontSize: 16.0, webBgColor: "linear-gradient(to right, #28a745, #28a745)", ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print('23'); print('otp wrong'); @@ -1166,6 +1200,11 @@ class _LoginWidgetState extends State { print('auth URL not Founded'); throw Exception('auth URL not Founded'); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { final errorMessage = json.decode(response.body)['message']; print(errorMessage); diff --git a/lib/Screens/costCenter/costCenterDetails.dart b/lib/Screens/costCenter/costCenterDetails.dart index 327e279..d31040d 100644 --- a/lib/Screens/costCenter/costCenterDetails.dart +++ b/lib/Screens/costCenter/costCenterDetails.dart @@ -247,6 +247,12 @@ class CostCenterDataState extends State { }); break; + case 403: + print("403-FORB"); + await apiService.logout(context); + break; + // throw Exception('Failed to load users'); + default: print("Failed to submit costcenter. Status: ${response.statusCode}"); print("Error: ${response.body}"); diff --git a/lib/Screens/costCenter/costCenter_list.dart b/lib/Screens/costCenter/costCenter_list.dart index 5e47127..486a28c 100644 --- a/lib/Screens/costCenter/costCenter_list.dart +++ b/lib/Screens/costCenter/costCenter_list.dart @@ -157,6 +157,11 @@ class CostCenterListState extends State { final data = json.decode(response.body); print(data['data']); return data['data']; // Returning raw JSON list + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load users'); } @@ -658,6 +663,7 @@ class CostCenterListState extends State { ); final data = await apiService .getCostCenterDetailsFind( + context, costcenterId, ); print("CostCenterId -- $data"); @@ -751,6 +757,7 @@ class CostCenterListState extends State { ); final data = await apiService .getCostCenterDetailsFind( + context, costcenterId, ); print("CostCenterId -- $data"); diff --git a/lib/Screens/dashboard/status_dashboard.dart b/lib/Screens/dashboard/status_dashboard.dart index 507a56d..4eeed20 100644 --- a/lib/Screens/dashboard/status_dashboard.dart +++ b/lib/Screens/dashboard/status_dashboard.dart @@ -40,6 +40,7 @@ class StatusDashboardState extends State { List flightData = []; List accommodationData = []; List forexData = []; + final ApiService apiService = ApiService(); final categoryIcons = { "flight": Icons.flight, @@ -96,8 +97,10 @@ class StatusDashboardState extends State { _dialogShown = true; + final parentContext = context; // <-- save parent context + showDialog( - context: context, + context: parentContext, builder: (context) => AlertDialog( title: Text("Confirm"), @@ -105,16 +108,20 @@ class StatusDashboardState extends State { actions: [ TextButton( onPressed: () { - Navigator.pop(context); // Close dialog + Navigator.pop(parentContext); // Close dialog _dialogShown = false; }, child: Text("Cancel"), ), TextButton( onPressed: () async { - Navigator.pop(context); // Close dialog + // Navigator.pop(parentContext); // Close dialog _dialogShown = false; - await _logoutAndRedirect(context); + print("logue 1s"); + // Use parentContext for navigation, not dialog context + if (!mounted) return; + await _logoutAndRedirect(parentContext); + print("logue 2s"); }, child: Text("Logout"), ), @@ -123,14 +130,52 @@ class StatusDashboardState extends State { ); } + // void _showBackConfirmationDialog() { + // if (!mounted) return; + // + // _dialogShown = true; + // + // showDialog( + // context: context, + // builder: + // (context) => AlertDialog( + // title: Text("Confirm"), + // content: Text("Do you want to logout?"), + // actions: [ + // TextButton( + // onPressed: () { + // Navigator.pop(context); // Close dialog + // _dialogShown = false; + // }, + // child: Text("Cancel"), + // ), + // TextButton( + // onPressed: () async { + // Navigator.pop(context); // Close dialog + // _dialogShown = false; + // + // // Use the parent widget's context + // if (!mounted) return; + // await _logoutAndRedirect(context); + // }, + // child: Text("Logout"), + // ), + // ], + // ), + // ); + // } + Future _logoutAndRedirect(BuildContext context) async { + print("logue _logoutAndRedirect"); + // Navigator.pop(context); // closes dialog + _dialogShown = false; print("logue 0"); - + await apiService.logout(context); // Example: clear session or shared preferences - final prefs = await SharedPreferences.getInstance(); - await prefs.clear(); + // final prefs = await SharedPreferences.getInstance(); + // await prefs.clear(); - context.go("/"); + // context.go("/"); print("logue 1"); } @@ -214,6 +259,11 @@ class StatusDashboardState extends State { final apiData = json.decode(response.body); print("Fetch StatusDashboard -- Reponse Here : $apiData"); return apiData; // Returning raw JSON list + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return {}; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load users'); } diff --git a/lib/Screens/dashboard/status_dashboard_web.dart b/lib/Screens/dashboard/status_dashboard_web.dart index 61aa6ae..27a729b 100644 --- a/lib/Screens/dashboard/status_dashboard_web.dart +++ b/lib/Screens/dashboard/status_dashboard_web.dart @@ -41,6 +41,7 @@ class StatusDashboardState extends State { List flightData = []; List accommodationData = []; List forexData = []; + final ApiService apiService = ApiService(); final categoryIcons = { "flight": Icons.flight, @@ -53,14 +54,14 @@ class StatusDashboardState extends State { "Partially Approved", "Approved", "Rejected", - "Cancelled" + "Cancelled", ]; - // Extract counts for "Both" section - List getStatusValues(Map serviceData) { - final both = serviceData['Both'] ?? {}; - return statusLabels.map((label) => (both[label] ?? 0) as int).toList(); - } + // Extract counts for "Both" section + List getStatusValues(Map serviceData) { + final both = serviceData['Both'] ?? {}; + return statusLabels.map((label) => (both[label] ?? 0) as int).toList(); + } @override void initState() { @@ -113,7 +114,7 @@ class StatusDashboardState extends State { ), TextButton( onPressed: () async { - Navigator.pop(context); // Close dialog + // Navigator.pop(context); // Close dialog _dialogShown = false; await _logoutAndRedirect(context); }, @@ -126,12 +127,14 @@ class StatusDashboardState extends State { Future _logoutAndRedirect(BuildContext context) async { print("logue 0"); - + Navigator.pop(context); // closes dialog + _dialogShown = false; + await apiService.logout(context); // Example: clear session or shared preferences - final prefs = await SharedPreferences.getInstance(); - await prefs.clear(); + // final prefs = await SharedPreferences.getInstance(); + // await prefs.clear(); - context.go("/"); + // context.go("/"); print("logue 1"); } @@ -210,6 +213,10 @@ class StatusDashboardState extends State { final apiData = json.decode(response.body); print("Fetch StatusDashboard -- Reponse Here : $apiData"); return apiData; // Returning raw JSON list + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return {}; } else { throw Exception('Failed to load users'); } @@ -316,25 +323,27 @@ class StatusDashboardState extends State { setState(() { // currentData = selectedView == "Today" ? todayBasedCount : weekBasedCount; - typeData = selectedView == "Today" - ? typeBasedTodayCount - : typeBasedWeeklyCount ; - statusData = selectedView == "Today" - ? statusBasedTodayCount - : statusBasedWeeklyCount ; + typeData = + selectedView == "Today" ? typeBasedTodayCount : typeBasedWeeklyCount; + statusData = + selectedView == "Today" + ? statusBasedTodayCount + : statusBasedWeeklyCount; - flightData = selectedView == "Today" - ? getStatusValues(todayBasedCount['flight'] ?? {}) - : getStatusValues(weekBasedCount['flight'] ?? {}); - accommodationData = selectedView == "Today" - ? getStatusValues(todayBasedCount['acomodation'] ?? {}) - : getStatusValues(weekBasedCount['acomodation'] ?? {}); - forexData = selectedView == "Today" - ? getStatusValues(todayBasedCount['forex'] ?? {}) - : getStatusValues(weekBasedCount['forex'] ?? {}); + flightData = + selectedView == "Today" + ? getStatusValues(todayBasedCount['flight'] ?? {}) + : getStatusValues(weekBasedCount['flight'] ?? {}); + accommodationData = + selectedView == "Today" + ? getStatusValues(todayBasedCount['acomodation'] ?? {}) + : getStatusValues(weekBasedCount['acomodation'] ?? {}); + forexData = + selectedView == "Today" + ? getStatusValues(todayBasedCount['forex'] ?? {}) + : getStatusValues(weekBasedCount['forex'] ?? {}); }); - print("statusBasedCount - => $statusBasedCount"); print("Current Data - => $currentData"); // 👇 Local function to create the card widget @@ -399,6 +408,7 @@ class StatusDashboardState extends State { print("dashboard .."); } } + String capitalize(String s) => s[0].toUpperCase() + s.substring(1); Widget toggleChip(String label) { @@ -446,7 +456,7 @@ class StatusDashboardState extends State { child: Column( children: [ Tooltip( - message: label , + message: label, child: Image.asset( label == 'Domestic' ? 'assets/images/IconsImg/Domestic_new.png' @@ -520,8 +530,12 @@ class StatusDashboardState extends State { ); } - Widget buildCategoryCard(String title, IconData icon, List statusLabels,List values) { - + Widget buildCategoryCard( + String title, + IconData icon, + List statusLabels, + List values, + ) { final statusIcons = [ Icons.calendar_today, Icons.assignment, @@ -532,9 +546,9 @@ class StatusDashboardState extends State { final statusColors = [ Color(0xFFFBFFCA), - Color(0xFFFFF1CD),// Colors.yellow.shade100, // Color(0xFFCAE77B), - Color(0xFFDAFFE8),// Colors.green.shade100, // Color(0xFF72D480), - Color(0xFFFFD6D3),// Colors.red.shade100, // Color(0xFFF88C8C), + Color(0xFFFFF1CD), // Colors.yellow.shade100, // Color(0xFFCAE77B), + Color(0xFFDAFFE8), // Colors.green.shade100, // Color(0xFF72D480), + Color(0xFFFFD6D3), // Colors.red.shade100, // Color(0xFFF88C8C), Color(0xFFFFA8A8), // Colors.red.shade200, // Color(0xFFE94B4B), ]; @@ -575,17 +589,16 @@ class StatusDashboardState extends State { color: Color(0xFF004A8E), shape: BoxShape.circle, ), - child: Icon( - icon, - color: Colors.white, - size: 20, - ), + child: Icon(icon, color: Colors.white, size: 20), ), const SizedBox(width: 10), - Text(title, style: GoogleFonts.poppins( + Text( + title, + style: GoogleFonts.poppins( color: Colors.black87, - fontWeight: FontWeight.bold - ),), + fontWeight: FontWeight.bold, + ), + ), ], ), const SizedBox(height: 12), @@ -603,7 +616,9 @@ class StatusDashboardState extends State { height: 28, decoration: BoxDecoration( color: statusColors[index], // background color - borderRadius: BorderRadius.circular(4), // square with slight rounding + borderRadius: BorderRadius.circular( + 4, + ), // square with slight rounding ), alignment: Alignment.center, child: Icon( @@ -613,16 +628,20 @@ class StatusDashboardState extends State { ), ), const SizedBox(width: 6), - Text(statusLabels[index], style: GoogleFonts.poppins( - color: Colors.black87, - ),), + Text( + statusLabels[index], + style: GoogleFonts.poppins(color: Colors.black87), + ), ], ), - Text(values[index].toString(), style: GoogleFonts.poppins( - fontSize: 10, - color: Colors.black87, - fontWeight: FontWeight.bold, - ),), + Text( + values[index].toString(), + style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), + ), ], ), ); @@ -789,31 +808,33 @@ class StatusDashboardState extends State { ], ), padding: const EdgeInsets.all(15), - child: Row( - children: [ - ...typeData.map((item) { - return Expanded( - child: Column( - children: [ - buildTopCard( - // icon: item['value'] == "Domestic" - // ? Icons.home - // : Icons.travel_explore, - color: item['value'] == "Domestic" - ? Color(0xFF0DB04B) - : Color(0xFF004A8E), - label: item['value'], - count: item['count'], - bgColor: item['value'] == "Domestic" - ? const Color(0xFFD6FBE4) - : const Color(0xFFD9E8FF), - ), - ], - ), - ); - }).toList(), - ], - ), + child: Row( + children: [ + ...typeData.map((item) { + return Expanded( + child: Column( + children: [ + buildTopCard( + // icon: item['value'] == "Domestic" + // ? Icons.home + // : Icons.travel_explore, + color: + item['value'] == "Domestic" + ? Color(0xFF0DB04B) + : Color(0xFF004A8E), + label: item['value'], + count: item['count'], + bgColor: + item['value'] == "Domestic" + ? const Color(0xFFD6FBE4) + : const Color(0xFFD9E8FF), + ), + ], + ), + ); + }).toList(), + ], + ), ), ), const SizedBox(width: 20), @@ -836,18 +857,27 @@ class StatusDashboardState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Status", - style: GoogleFonts.poppins( - fontSize: 19, - color: Colors.black87, - fontWeight: FontWeight.bold, - ), + Text( + "Status", + style: GoogleFonts.poppins( + fontSize: 19, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: 23), Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisAlignment: + MainAxisAlignment.spaceAround, children: [ - ...statusData.map((item) => statusCard(item['value'], item['count'])).toList(), + ...statusData + .map( + (item) => statusCard( + item['value'], + item['count'], + ), + ) + .toList(), ], ), ], @@ -862,11 +892,32 @@ class StatusDashboardState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(child: buildCategoryCard("Flight", Icons.flight,statusLabels,flightData)), + Expanded( + child: buildCategoryCard( + "Flight", + Icons.flight, + statusLabels, + flightData, + ), + ), const SizedBox(width: 10), - Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined,statusLabels,accommodationData)), + Expanded( + child: buildCategoryCard( + "Accomodation", + Icons.hotel_outlined, + statusLabels, + accommodationData, + ), + ), const SizedBox(width: 10), - Expanded(child: buildCategoryCard("Forex", Icons.attach_money,statusLabels,forexData)), + Expanded( + child: buildCategoryCard( + "Forex", + Icons.attach_money, + statusLabels, + forexData, + ), + ), ], ), ], diff --git a/lib/Screens/department/departmentDetails.dart b/lib/Screens/department/departmentDetails.dart index 940020c..fa99d0e 100644 --- a/lib/Screens/department/departmentDetails.dart +++ b/lib/Screens/department/departmentDetails.dart @@ -249,6 +249,11 @@ class DepartmentDataState extends State { behavior: SnackBarBehavior.floating, ), ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit. Status: ${response.statusCode}"); print("Error: ${response.body}"); diff --git a/lib/Screens/department/department_list.dart b/lib/Screens/department/department_list.dart index 802fdb0..0efd1eb 100644 --- a/lib/Screens/department/department_list.dart +++ b/lib/Screens/department/department_list.dart @@ -156,6 +156,11 @@ class DepartmentListState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); return data['data']; // Returning raw JSON list + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load users'); } @@ -654,6 +659,7 @@ class DepartmentListState extends State { ); final data = await apiService .getDepartmentDetailsFind( + context, departmentId, ); print("DepartmentId -- $data"); @@ -746,6 +752,7 @@ class DepartmentListState extends State { ); final data = await apiService .getDepartmentDetailsFind( + context, departmentId, ); print("DepartmentId -- $data"); diff --git a/lib/Screens/dialog/user_selection_dialog.dart b/lib/Screens/dialog/user_selection_dialog.dart index 90542bb..116905d 100644 --- a/lib/Screens/dialog/user_selection_dialog.dart +++ b/lib/Screens/dialog/user_selection_dialog.dart @@ -8,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../config/apiUrl.dart'; import '../../data/models/Searchtraveller.dart'; import '../../data/models/searchUser.dart'; +import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_traveller.dart'; @@ -32,6 +33,7 @@ class UserSelectionDialog extends StatefulWidget { } class _UserSelectionDialogState extends State { + final ApiService apiService = ApiService(); TextEditingController _controller = TextEditingController(); TextEditingController _searchController = TextEditingController(); // List _filteredUsers = []; @@ -107,6 +109,11 @@ class _UserSelectionDialogState extends State { "Unexpected response format: Expected a List but got ${responseBody.runtimeType}", ); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { throw Exception( 'Failed to load users. Status Code: ${response.statusCode}', @@ -164,6 +171,16 @@ class _UserSelectionDialogState extends State { "Unexpected response format: Expected a List but got ${responseBody.runtimeType}", ); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { throw Exception( 'Failed to load users. Status Code: ${response.statusCode}', @@ -707,6 +724,7 @@ class TravelerForm extends StatefulWidget { } class _TravelerFormState extends State { + final ApiService apiService = ApiService(); Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); @@ -819,6 +837,11 @@ class _TravelerFormState extends State { backgroundColor: Colors.green, ), ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { ScaffoldMessenger.of( context, diff --git a/lib/Screens/group/group.dart b/lib/Screens/group/group.dart index dc2fd83..145726a 100644 --- a/lib/Screens/group/group.dart +++ b/lib/Screens/group/group.dart @@ -141,7 +141,7 @@ class _groupState extends State { Future loadAllServices() async { try { - final result = await apiService.fetchAllPolicy(); + final result = await apiService.fetchAllPolicy(context); orgId = await getOrgId(); userId = await getUserId(); @@ -263,10 +263,15 @@ class _groupState extends State { if (response.statusCode == 200 || response.statusCode == 201) { print("GRPDATA"); - await apiService.handleTokenRefresh(userId!); + await apiService.handleTokenRefresh(context, userId!); print("Group submitted successfully!"); print("Response: ${response.body}"); context.go('/group'); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit group. Status: ${response.statusCode}"); print("Error: ${response.body}"); diff --git a/lib/Screens/group/groupDetails.dart b/lib/Screens/group/groupDetails.dart index 7358dae..43b0670 100644 --- a/lib/Screens/group/groupDetails.dart +++ b/lib/Screens/group/groupDetails.dart @@ -177,7 +177,7 @@ class GroupDataState extends State { Future loadAllServices() async { try { - final result = await apiService.fetchAllPolicy(); + final result = await apiService.fetchAllPolicy(context); userId = await getUserId(); @@ -286,7 +286,7 @@ class GroupDataState extends State { _clearError(); await widget.fetchGetGroup(); - await apiService.handleTokenRefresh(userId!); + await apiService.handleTokenRefresh(context, userId!); if (context.mounted) { Navigator.of(context).pop(); // Close modal only if mounted } @@ -295,6 +295,11 @@ class GroupDataState extends State { }); // dispose(); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else if (response.statusCode == 404) { if (context.mounted) { Navigator.of(context).pop(); @@ -311,6 +316,11 @@ class GroupDataState extends State { behavior: SnackBarBehavior.floating, ), ); + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); diff --git a/lib/Screens/group/groupList.dart b/lib/Screens/group/groupList.dart index ac164c5..1f5b17a 100644 --- a/lib/Screens/group/groupList.dart +++ b/lib/Screens/group/groupList.dart @@ -118,13 +118,13 @@ class _GroupListState extends State { Future> fetchGroups() async { // return []; - final result = await apiService.fetchAllGroup(); + final result = await apiService.fetchAllGroup(context); return result; // Returning raw JSON list } Future loadAllGroups() async { try { - final result = await apiService.fetchAllGroup(); + final result = await apiService.fetchAllGroup(context); setState(() { allGroups = result; filteredGroups = result; @@ -200,6 +200,11 @@ class _GroupListState extends State { if (response.statusCode == 200 || response.statusCode == 201) { print("Group status updated successfully to $newStatus!"); loadAllGroups(); // Refresh groups list after update + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to update group status. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -713,6 +718,7 @@ class _GroupListState extends State { if (newGroupID != null) { final data = await apiService .getGroupDetailsFind( + context, newGroupID, ); // ✅ Always an int showDialog( @@ -850,6 +856,7 @@ class _GroupListState extends State { if (newGroupID != null) { final data = await apiService .getGroupDetailsFind( + context, newGroupID, ); // ✅ Always an int showDialog( diff --git a/lib/Screens/group/groupListBackUp.dart b/lib/Screens/group/groupListBackUp.dart index 5036d8b..6a1b791 100644 --- a/lib/Screens/group/groupListBackUp.dart +++ b/lib/Screens/group/groupListBackUp.dart @@ -55,7 +55,7 @@ class _GroupListBackUpState extends State { Future loadAllGroups() async { try { - final result = await apiService.fetchAllGroup(); + final result = await apiService.fetchAllGroup(context); setState(() { apiAllGroups = result; }); @@ -410,6 +410,7 @@ class _GroupListBackUpState extends State { ); if (newGroupID != null) { final data = await apiService.getGroupDetailsFind( + context, newGroupID, ); // ✅ Always an int showDialog( diff --git a/lib/Screens/hotels/hotelsDetails.dart b/lib/Screens/hotels/hotelsDetails.dart index b4f1602..fbdcb1f 100644 --- a/lib/Screens/hotels/hotelsDetails.dart +++ b/lib/Screens/hotels/hotelsDetails.dart @@ -151,7 +151,7 @@ class HotelsDataState extends State { Future fetchCountries() async { try { - List countries = await apiService.fetchCountryList(); + List countries = await apiService.fetchCountryList(context); setState(() { apiCountryData = countries; }); @@ -269,6 +269,11 @@ class HotelsDataState extends State { isDisable = false; }); // Do NOT re-enable here if success + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else if (response.statusCode == 404) { if (context.mounted) { Navigator.of(context).pop(); diff --git a/lib/Screens/hotels/hotels_list.dart b/lib/Screens/hotels/hotels_list.dart index 43f0601..39cae10 100644 --- a/lib/Screens/hotels/hotels_list.dart +++ b/lib/Screens/hotels/hotels_list.dart @@ -159,6 +159,11 @@ class HotelsDataListState extends State { if (response.statusCode == 200) { final data = json.decode(response.body); return data['data']; // Returning raw JSON list + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return []; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load users'); } @@ -209,6 +214,11 @@ class HotelsDataListState extends State { } catch (e) { throw Exception('Error parsing response: $e'); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { throw Exception('Failed to load plans'); } @@ -755,7 +765,10 @@ class HotelsDataListState extends State { if (hotelsId != null) { print("HotelsId -- $hotelsId"); final data = await apiService - .getHotelsDetailsFind(hotelsId); + .getHotelsDetailsFind( + context, + hotelsId, + ); print("HotelsId -- $data"); showDialog( @@ -841,7 +854,10 @@ class HotelsDataListState extends State { if (hotelsId != null) { print("HotelsId -- $hotelsId"); final data = await apiService - .getHotelsDetailsFind(hotelsId); + .getHotelsDetailsFind( + context, + hotelsId, + ); print("HotelsId -- $data"); showDialog( diff --git a/lib/Screens/itnerary/accomodations.dart b/lib/Screens/itnerary/accomodations.dart index a080093..f97538f 100644 --- a/lib/Screens/itnerary/accomodations.dart +++ b/lib/Screens/itnerary/accomodations.dart @@ -182,7 +182,7 @@ class _AccomodationScreenState extends State { // } WidgetsBinding.instance.addPostFrameCallback((_) { - loadCountryList(); + loadCountryList(context); final result = getFlightTripDateRange(widget.flightData); @@ -288,7 +288,7 @@ class _AccomodationScreenState extends State { // : Colors.redAccent; // }); - futureHotels = await apiService.fetchGetHotels(); + futureHotels = await apiService.fetchGetHotels(context); print("futureHotels - $futureHotels"); @@ -489,8 +489,11 @@ class _AccomodationScreenState extends State { return errorMessages.isEmpty; // Valid if there are no errors } - Future loadCountryList() async { - final result = await apiService.fetchFlightsCountryList(widget.tripType); + Future loadCountryList(BuildContext context) async { + final result = await apiService.fetchFlightsCountryList( + context, + widget.tripType, + ); print("ResultCountry : $result"); diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 3c94eb4..faf766a 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -191,7 +191,7 @@ class FlightScreenState extends State { if (tripTypeValue != null && tripTypeValue.isNotEmpty) { print("🚀 Initial loadCountryList for tripType: $tripTypeValue"); - loadCountryList(tripTypeValue); + loadCountryList(context, tripTypeValue); } else { print("⚠️ tripType is null or empty, skipping loadCountryList"); } @@ -253,14 +253,17 @@ class FlightScreenState extends State { // isCountryLoading = false; // }); // } - Future loadCountryList(newTripType) async { + Future loadCountryList(BuildContext context, newTripType) async { print("🔄 loadCountryList called with tripType: ${newTripType}"); setState(() { isCountryLoading = true; }); - final result = await apiService.fetchFlightsCountryList(newTripType); + final result = await apiService.fetchFlightsCountryList( + context, + newTripType, + ); print("ResultCountry : $result"); diff --git a/lib/Screens/itnerary/forex.dart b/lib/Screens/itnerary/forex.dart index 27ecf49..7c1f3ff 100644 --- a/lib/Screens/itnerary/forex.dart +++ b/lib/Screens/itnerary/forex.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:frontend/services/apiService.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; @@ -39,6 +40,7 @@ class ForexScreen extends StatefulWidget { } class _ForexScreenState extends State { + final ApiService apiService = ApiService(); final GlobalKey _formKey = GlobalKey(); late ValueNotifier flightFirstTripDateNotifier; @@ -245,6 +247,11 @@ class _ForexScreenState extends State { } else { print("Warning: Response does not contain expected fields."); } + } else if (response.statusCode == 403) { + print("403-FORB"); + await apiService.logout(context); + return null; + // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); diff --git a/lib/Screens/itnerary/train.dart b/lib/Screens/itnerary/train.dart index d47bf24..3b0ca4d 100644 --- a/lib/Screens/itnerary/train.dart +++ b/lib/Screens/itnerary/train.dart @@ -287,7 +287,7 @@ class _TrainScreenState extends State { isCountryLoading = true; }); - final result = await apiService.fetchTrainCountryList(); + final result = await apiService.fetchTrainCountryList(context); print("ResultCountry : $result"); diff --git a/lib/Screens/itnerary_list/flight_list.dart b/lib/Screens/itnerary_list/flight_list.dart index 807b04d..ba35702 100644 --- a/lib/Screens/itnerary_list/flight_list.dart +++ b/lib/Screens/itnerary_list/flight_list.dart @@ -96,7 +96,10 @@ class _FlightListWidgetState extends State { } Future loadCountryList() async { - final result = await apiService.fetchFlightsCountryList(widget.tripType); + final result = await apiService.fetchFlightsCountryList( + context, + widget.tripType, + ); print("ResultCountry : $result"); diff --git a/lib/Screens/itnerary_list/forex_list.dart b/lib/Screens/itnerary_list/forex_list.dart index 2083813..0d90489 100644 --- a/lib/Screens/itnerary_list/forex_list.dart +++ b/lib/Screens/itnerary_list/forex_list.dart @@ -395,7 +395,7 @@ class _ForexListWidgetState extends State { final id = int.tryParse(forexIdString ?? ''); if (id != null) { - apiService.getForexPdfDownload(id); + apiService.getForexPdfDownload(context, id); } else { print("Invalid forex_id: $forexIdString"); } diff --git a/lib/Screens/itnerary_list/train_list.dart b/lib/Screens/itnerary_list/train_list.dart index 9f3e351..4d70a09 100644 --- a/lib/Screens/itnerary_list/train_list.dart +++ b/lib/Screens/itnerary_list/train_list.dart @@ -127,7 +127,7 @@ class _TrainListWidgetState extends State { Future loadCountryList() async { // final result = await apiService.fetchFlightsCountryList(); - final result = await apiService.fetchTrainCountryList(); + final result = await apiService.fetchTrainCountryList(context); print("ResultCountry : $result"); diff --git a/lib/Screens/myTemplates/template.dart b/lib/Screens/myTemplates/template.dart index cae70e0..dd85dac 100644 --- a/lib/Screens/myTemplates/template.dart +++ b/lib/Screens/myTemplates/template.dart @@ -487,6 +487,11 @@ class TemplateState extends State