import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:html' as html; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:frontend/routes/custom_router.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../config/apiUrl.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../utils/pagination.dart'; class StatusDashboard extends StatefulWidget { const StatusDashboard({super.key}); @override StatusDashboardState createState() => StatusDashboardState(); } class StatusDashboardState extends State { Map? apiData; String? organizationId; late bool _dialogShown = false; late StreamSubscription _popStateListener; @override void initState() { super.initState(); _checkAuthAndLoadData(); checkbackbutton(); // loadDashboardData(); } void checkbackbutton() async { // Push a dummy state so back button triggers popstate instead of navigating html.window.history.pushState(null, 'home', html.window.location.href); _popStateListener = html.window.onPopState.listen((event) { if (!_dialogShown && mounted) { _showBackConfirmationDialog(); } // Re-push to prevent leaving html.window.history.pushState(null, 'home', html.window.location.href); }); } @override void dispose() { _popStateListener.cancel(); // ✅ Remove the browser popstate listener super.dispose(); } 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"), ), ], ), ); } Future _logoutAndRedirect(BuildContext context) async { print("logue 0"); // Example: clear session or shared preferences final prefs = await SharedPreferences.getInstance(); await prefs.clear(); context.go("/"); print("logue 1"); } 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; } WidgetsBinding.instance.addPostFrameCallback((_) { checkbackbutton(); }); // If token exists, load the dashboard data loadDashboardData(); } Future loadDashboardData() async { try { final data = await fetchStatusDashboard(); setState(() { apiData = data; }); } catch (e) { print("Error loading dashboard: $e"); } } Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } 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> fetchStatusDashboard() async { organizationId = await getOrgId(); final String apiUrlData = '$apiUrl/api/plans/statusDashboard?org_id=$organizationId'; final String? token = await getToken(); print("Fetch StatusDashboard -- 2KN Here : $token"); if (token == null) { throw Exception('Token not found. Please log in.'); } final response = await http.get( Uri.parse(apiUrlData), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, ); print("called api : $apiUrlData"); if (response.statusCode == 200) { final apiData = json.decode(response.body); print("Fetch StatusDashboard -- Reponse Here : $apiData"); return apiData; // Returning raw JSON list } else { throw Exception('Failed to load users'); } } Color getStatusColor(String status) { if (status == "Domestic") return Colors.white; if (status == "International") return Colors.white; if (status == "Partially Approved") return Colors.yellow.shade100; if (status == "Pending Approval") return Colors.greenAccent.shade100; if (status == "Approved") return Colors.green.shade100; if (status == "Completed") return Colors.green.shade500; if (status == "Rejected") return Colors.red.shade100; if (status == "Cancelled") return Colors.red.shade200; return Colors.grey.shade100; } IconData getStatusIcon(String title) { switch (title) { case "Domestic": return Icons.directions_bus_filled_outlined; case "International": return Icons.airplanemode_active; case "Partially Approved": return Icons.pending; case "Approved": return Icons.check_circle_outline; case "Completed": return Icons.done_all; case "Rejected": return Icons.cancel_outlined; case "Cancelled": return Icons.cancel_schedule_send_outlined; default: return Icons.info_outline; } } @override Widget build(BuildContext context) { final typeBasedCount = List>.from( (apiData?['data']?['typeBasedCount']) ?? [], ); print("apiData - => $apiData"); print("typeBasedCount => $typeBasedCount"); final statusBasedCount = List>.from( (apiData?['data']?['statusBasedCount']) ?? [], ); print("statusBasedCount - => $statusBasedCount"); // 👇 Local function to create the card widget Widget buildInfoCard(String title, int count, double width) { print('title $title'); final color = getStatusColor(title); final icon = getStatusIcon(title); return Card( elevation: 3, color: color, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Container( width: width, padding: const EdgeInsets.all(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 32, color: (title == 'Domestic' || title == 'International') ? Colors.green : Colors.black54, ), // 👈 Add Icon here const SizedBox(height: 8), Text( title, textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.w600, fontSize: (title == 'Domestic' || title == 'International') ? 16 : 14, ), ), const SizedBox(height: 8), Text( count.toString(), style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, // color: Colors.blue, ), ), ], ), ), ); } void _handleBackButton() { final location = GoRouterState.of(context).uri.toString(); print("location - $location"); if (location.contains('/StatusDashboard')) { // Do nothing or show "Press again to exit" toast print("Blocked back on dashboard"); } else { print("dashboard .."); } } return ResponsiveBuilder( builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return PopScope( canPop: false, // Allow back navigation only if not login screen onPopInvokedWithResult: (didPop, result) { if (didPop) return; _handleBackButton(); // Show exit confirmation dialog }, child: Scaffold( backgroundColor: const Color(0xFFf5f5f5), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( padding: isDesktop ? EdgeInsets.symmetric( horizontal: MediaQuery.of(context).size.width * 0.1, // 30% of screen width as horizontal padding vertical: 10, // 5% of screen height as vertical padding ) : EdgeInsets.all(0), child: LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints( minHeight: constraints.maxHeight, ), child: IntrinsicHeight( // Only needed if child layout depends on height child: Container( decoration: BoxDecoration( color: isDesktop ? Colors.white : const Color(0xFFFCFCFC), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.all(30.0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Wrap( spacing: 10, runSpacing: 10, children: typeBasedCount.map((item) { double cardWidth = isDesktop ? (MediaQuery.of( context, ).size.width * 0.75 - 10) / 2 // 80% width padding adjusted : MediaQuery.of( context, ).size.width - 24; // full width with padding return SizedBox( width: cardWidth, child: buildInfoCard( item['value'], item['count'], cardWidth, ), ); }).toList(), ), const SizedBox(height: 20), Wrap( spacing: 10, runSpacing: 10, children: statusBasedCount.map((item) { double cardWidth = isDesktop ? (MediaQuery.of( context, ).size.width * 0.90 - 50) / 6 // desktop layout: 6 cards per row : MediaQuery.of( context, ).size.width - 24; // mobile: full width return SizedBox( width: cardWidth, child: buildInfoCard( item['value'], item['count'], cardWidth, ), ); }).toList(), ), ], ), ), ), ], ), ), ), ), ); }, ), ), ), ); }, ); } }