import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/features/accounts/screens/account_info_screen.dart'; import 'package:kmobile/features/accounts/screens/account_statement_screen.dart'; import 'package:kmobile/features/auth/controllers/auth_cubit.dart'; import 'package:kmobile/features/auth/controllers/auth_state.dart'; import 'package:kmobile/features/customer_info/screens/customer_info_screen.dart'; import 'package:kmobile/features/beneficiaries/screens/manage_beneficiaries_screen.dart'; import 'package:kmobile/features/dashboard/widgets/transaction_list_placeholder.dart'; import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart'; import 'package:kmobile/features/fund_transfer/screens/fund_transfer_beneficiary_screen.dart'; import 'package:kmobile/features/quick_pay/screens/quick_pay_screen.dart'; import 'package:kmobile/security/secure_storage.dart'; import 'package:local_auth/local_auth.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shimmer/shimmer.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { int selectedAccountIndex = 0; bool isVisible = false; bool isRefreshing = false; bool isBalanceLoading = false; bool _biometricPromptShown = false; Future _refreshAccountData(BuildContext context) async { setState(() { isRefreshing = true; }); try { // Call your AuthCubit or repository to refresh user/accounts data await context.read().refreshUserData(); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Failed to refresh data')), ); } setState(() { isRefreshing = false; }); } Widget _buildBalanceShimmer() { return Shimmer.fromColors( baseColor: Colors.white.withOpacity(0.7), highlightColor: Colors.white.withOpacity(0.3), child: Container( width: 100, height: 32, color: Colors.white, ), ); } String getProcessedFirstName(String? name) { if (name == null || name.trim().isEmpty) return ''; // Remove common titles final titles = [ 'mr.', 'mrs.', 'ms.', 'miss', 'dr.', 'shri', 'smt.', 'kumari' ]; String processed = name.trim().toLowerCase(); for (final title in titles) { if (processed.startsWith(title)) { processed = processed.replaceFirst(title, '').trim(); break; } } // Take the first word (first name) final firstName = processed.split(' ').first; // Convert to title case if (firstName.isEmpty) return ''; return firstName[0].toUpperCase() + firstName.substring(1); } String getFullAccountType(String? accountType) { if (accountType == null || accountType.isEmpty) return 'N/A'; // Convert to title case switch (accountType.toLowerCase()) { case 'sa': return 'Savings Account'; case 'ln': return 'Loan Account'; case 'td': return 'Term Deposit Account'; case 'rd': return 'Recurring Deposit Account'; default: return 'Unknown Account Type'; } } Future _showBiometricOptInDialog() async { final storage = SecureStorage(); showDialog( context: context, barrierDismissible: false, builder: (_) => AlertDialog( title: const Text('Enable Biometric Authentication'), content: const Text('Use fingerprint/face ID for faster login?'), actions: [ TextButton( onPressed: () async { await storage.write('biometric_prompt_shown', 'true'); Navigator.of(context).pop(); }, child: const Text('Later'), ), TextButton( onPressed: () async { final auth = LocalAuthentication(); final canCheck = await auth.canCheckBiometrics; bool ok = false; if (canCheck) { ok = await auth.authenticate( localizedReason: 'Scan to enable biometric login', ); } if (ok) { await storage.write('biometric_enabled', 'true'); } await storage.write('biometric_prompt_shown', 'true'); Navigator.of(context).pop(); }, child: const Text('Enable'), ), ], ), ); } @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) async { if (state is Authenticated && !_biometricPromptShown) { _biometricPromptShown = true; final storage = getIt(); final already = await storage.read('biometric_prompt_shown'); if (already == null) { _showBiometricOptInDialog(); } } }, child: Scaffold( backgroundColor: const Color(0xfff5f9fc), appBar: AppBar( backgroundColor: const Color(0xfff5f9fc), automaticallyImplyLeading: false, title: Text( 'kMobile', style: TextStyle( color: Theme.of(context).primaryColor, fontWeight: FontWeight.w500), ), actions: [ Padding( padding: const EdgeInsets.only(right: 10.0), child: InkWell( borderRadius: BorderRadius.circular(20), onTap: () { // Navigator.push( // context, // MaterialPageRoute( // builder: (context) => const Preference())); }, child: CircleAvatar( backgroundColor: Colors.grey[200], radius: 20, child: SvgPicture.asset( 'assets/images/avatar_male.svg', width: 40, height: 40, fit: BoxFit.cover, ), ), ), ), ], ), body: BlocBuilder(builder: (context, state) { if (state is AuthLoading || state is AuthInitial) { return const Center(child: CircularProgressIndicator()); } if (state is Authenticated) { final users = state.users; final currAccount = users[selectedAccountIndex]; final firstName = getProcessedFirstName(currAccount.name); return SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( "Hi! $firstName", style: GoogleFonts.montserrat().copyWith( fontSize: 25, color: Theme.of(context).primaryColorDark, fontWeight: FontWeight.w700), ), ), const SizedBox(height: 16), // Account Info Card Container( padding: const EdgeInsets.symmetric( horizontal: 18, vertical: 10), decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Text("Account Number: ", style: TextStyle( color: Colors.white, fontSize: 12)), DropdownButton( value: selectedAccountIndex, dropdownColor: Theme.of(context).primaryColor, underline: const SizedBox(), icon: const Icon(Icons.keyboard_arrow_down), iconEnabledColor: Colors.white, style: const TextStyle( color: Colors.white, fontSize: 14), items: List.generate(users.length, (index) { return DropdownMenuItem( value: index, child: Text( users[index].accountNo ?? 'N/A', style: const TextStyle( color: Colors.white, fontSize: 14), ), ); }), onChanged: (int? newIndex) async { if (newIndex == null || newIndex == selectedAccountIndex) { return; } if (isBalanceLoading) return; if (isVisible) { setState(() { isBalanceLoading = true; selectedAccountIndex = newIndex; }); await Future.delayed( const Duration(seconds: 1)); setState(() { isBalanceLoading = false; }); } else { setState(() { selectedAccountIndex = newIndex; }); } }, ), const Spacer(), IconButton( icon: isRefreshing ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Icon(Icons.refresh, color: Colors.white), onPressed: isRefreshing ? null : () => _refreshAccountData(context), tooltip: 'Refresh', ), ], ), Text( getFullAccountType(currAccount.accountType), style: const TextStyle( color: Colors.white, fontSize: 16), ), const SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ const Text("₹ ", style: TextStyle( color: Colors.white, fontSize: 40, fontWeight: FontWeight.w700)), isRefreshing || isBalanceLoading ? _buildBalanceShimmer() : Text( isVisible ? currAccount.currentBalance ?? '0.00' : '********', style: const TextStyle( color: Colors.white, fontSize: 40, fontWeight: FontWeight.w700)), const Spacer(), InkWell( onTap: () async { if (isBalanceLoading) return; if (!isVisible) { setState(() { isBalanceLoading = true; }); await Future.delayed( const Duration(seconds: 1)); setState(() { isVisible = true; isBalanceLoading = false; }); } else { setState(() { isVisible = false; }); } }, child: Icon( isVisible ? Symbols.visibility_lock : Symbols.visibility, color: Colors.white), ), ], ), const SizedBox(height: 15), ], ), ), const SizedBox(height: 18), const Text( 'Quick Links', style: TextStyle(fontSize: 17), ), const SizedBox(height: 16), // Quick Links GridView.count( crossAxisCount: 4, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ _buildQuickLink(Symbols.id_card, "Customer \n Info", () { Navigator.push( context, MaterialPageRoute( builder: (context) => CustomerInfoScreen( user: users[selectedAccountIndex], ))); }), _buildQuickLink(Symbols.currency_rupee, "Quick \n Pay", () { Navigator.push( context, MaterialPageRoute( builder: (context) => const QuickPayScreen())); }), _buildQuickLink(Symbols.send_money, "Fund \n Transfer", () { Navigator.push( context, MaterialPageRoute( builder: (context) => const FundTransferBeneficiaryScreen())); }), _buildQuickLink( Symbols.server_person, "Account \n Info", () { Navigator.push( context, MaterialPageRoute( builder: (context) => AccountInfoScreen( user: users[selectedAccountIndex]))); }), _buildQuickLink( Symbols.receipt_long, "Account \n Statement", () { Navigator.push( context, MaterialPageRoute( builder: (context) => const AccountStatementScreen())); }), _buildQuickLink( Symbols.checkbook, "Handle \n Cheque", () {}, disable: true), _buildQuickLink(Icons.group, "Manage \n Beneficiary", () { Navigator.push( context, MaterialPageRoute( builder: (context) => const ManageBeneficiariesScreen())); }), _buildQuickLink(Symbols.support_agent, "Contact \n Us", () { Navigator.push( context, MaterialPageRoute( builder: (context) => const EnquiryScreen())); }), ], ), const SizedBox(height: 5), // Recent Transactions const Text( 'Recent Transactions', style: TextStyle(fontSize: 17), ), const SizedBox(height: 16), if (currAccount.transactions != null && currAccount.transactions!.isNotEmpty) ...currAccount.transactions!.map((tx) => ListTile( leading: Icon( tx.type == 'CR' ? Symbols.call_received : Symbols.call_made, color: tx.type == 'CR' ? Colors.green : Colors.red), title: Text(tx.name ?? ''), subtitle: Text(tx.date ?? ''), trailing: Text("₹${tx.amount}", style: const TextStyle( fontSize: 16, )), )) else const EmptyTransactionsPlaceholder(), ], ), ), ); } return const Center(child: Text("Something went wrong")); }), ), ); } Widget _buildQuickLink(IconData icon, String label, VoidCallback onTap, {bool disable = false}) { return InkWell( onTap: disable ? null : onTap, child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 30, color: disable ? Colors.grey : Theme.of(context).primaryColor), const SizedBox(height: 4), Text(label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 13)), ], ), ); } }