import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/model/employees/employee_info.dart'; import 'package:marco/controller/auth/mpin_controller.dart'; import 'package:marco/view/employees/employee_profile_screen.dart'; import 'package:marco/view/support/support_screen.dart'; import 'package:marco/view/faq/faq_screen.dart'; class UserProfileBar extends StatefulWidget { final bool isCondensed; const UserProfileBar({Key? key, this.isCondensed = false}) : super(key: key); @override State createState() => _UserProfileBarState(); } class _UserProfileBarState extends State with SingleTickerProviderStateMixin, UIMixin { late EmployeeInfo employeeInfo; bool _isLoading = true; bool hasMpin = true; @override void initState() { super.initState(); _initData(); } Future _initData() async { employeeInfo = LocalStorage.getEmployeeInfo()!; hasMpin = await LocalStorage.getIsMpin(); setState(() => _isLoading = false); } @override Widget build(BuildContext context) { final bool isCondensed = widget.isCondensed; return Padding( padding: const EdgeInsets.only(left: 14), child: ClipRRect( borderRadius: BorderRadius.circular(5), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18), child: AnimatedContainer( duration: const Duration(milliseconds: 350), curve: Curves.easeInOut, width: isCondensed ? 84 : 260, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.white.withValues(alpha: 0.95), Colors.white.withValues(alpha: 0.85), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(5), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.06), blurRadius: 18, offset: const Offset(0, 8), ) ], border: Border.all( color: Colors.grey.withValues(alpha: 0.25), width: 1, ), ), child: SafeArea( bottom: true, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _isLoading ? const _LoadingSection() : _userProfileSection(isCondensed), MySpacing.height(12), Divider( indent: 18, endIndent: 18, thickness: 0.7, color: Colors.grey.withValues(alpha: 0.25), ), MySpacing.height(12), _supportAndSettingsMenu(isCondensed), const Spacer(), Divider( indent: 18, endIndent: 18, thickness: 0.35, color: Colors.grey.withValues(alpha: 0.18), ), _logoutButton(isCondensed), ], ), ), ), ), ), ); } Widget _userProfileSection(bool condensed) { final padding = MySpacing.fromLTRB( condensed ? 16 : 26, condensed ? 20 : 28, condensed ? 14 : 28, condensed ? 10 : 18, ); final avatarSize = condensed ? 48.0 : 64.0; return Padding( padding: padding, child: Row( children: [ Container( decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Theme.of(context).primaryColor.withValues(alpha: 0.15), blurRadius: 10, spreadRadius: 1, ), ], border: Border.all( color: Theme.of(context).primaryColor, width: 2, ), ), child: Avatar( firstName: employeeInfo.firstName, lastName: employeeInfo.lastName, size: avatarSize, ), ), if (!condensed) ...[ MySpacing.width(16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyLarge( '${employeeInfo.firstName} ${employeeInfo.lastName}', fontWeight: 700, color: Colors.indigo, ), MySpacing.height(4), MyText.bodySmall( "You're on track this month!", color: Colors.black54, ), ], ), ), ], ], ), ); } Widget _supportAndSettingsMenu(bool condensed) { final spacingHeight = condensed ? 8.0 : 14.0; return Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: Column( children: [ _menuItemRow( icon: LucideIcons.user, label: 'My Profile', onTap: _onProfileTap, ), SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.badge_help, label: 'Support', onTap: _onSupportTap, ), SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.info, label: 'FAQ', // <-- New FAQ menu item onTap: _onFaqTap, // <-- Handle tap ), SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.lock, label: hasMpin ? 'Change MPIN' : 'Set MPIN', iconColor: contentTheme.primary, textColor: contentTheme.primary, onTap: _onMpinTap, ), ], ), ); } void _onFaqTap() { Get.to(() => const FAQScreen()); } void _onSupportTap() { Get.to(() => const SupportScreen()); } Widget _menuItemRow({ required IconData icon, required String label, VoidCallback? onTap, Color? iconColor, Color? textColor, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(5), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey.withOpacity(0.2), width: 1), ), child: Row( children: [ Icon(icon, size: 22, color: iconColor ?? Colors.black87), const SizedBox(width: 16), Expanded( child: Text( label, style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: textColor ?? Colors.black87, ), ), ), const Icon(Icons.chevron_right, size: 20, color: Colors.black54), ], ), ), ); } void _onProfileTap() { Get.to(() => EmployeeProfilePage( employeeId: employeeInfo.id, )); } void _onMpinTap() { final controller = Get.put(MPINController()); if (hasMpin) controller.setChangeMpinMode(); Navigator.pushNamed(context, "/auth/mpin-auth"); } Widget _logoutButton(bool condensed) { return Padding( padding: MySpacing.all(14), child: SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _showLogoutConfirmation, icon: const Icon(LucideIcons.log_out, size: 22, color: Colors.white), label: condensed ? const SizedBox.shrink() : MyText.bodyMedium( "Logout", color: Colors.white, fontWeight: 700, ), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, foregroundColor: Colors.white, shadowColor: Colors.red.shade200, padding: EdgeInsets.symmetric( vertical: condensed ? 9 : 12, horizontal: condensed ? 6 : 16, ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), ), ), ), ); } Future _showLogoutConfirmation() async { final bool? confirm = await showDialog( context: context, builder: _buildLogoutDialog, ); if (confirm == true) await LocalStorage.logout(); } Widget _buildLogoutDialog(BuildContext context) { return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), elevation: 10, backgroundColor: Colors.white, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 34), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(LucideIcons.log_out, size: 56, color: Colors.red.shade700), const SizedBox(height: 18), const Text( "Logout Confirmation", style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, color: Colors.black87), ), const SizedBox(height: 14), const Text( "Are you sure you want to logout?\nYou will need to login again to continue.", textAlign: TextAlign.center, style: TextStyle(fontSize: 16, color: Colors.black54), ), const SizedBox(height: 30), Row( children: [ Expanded( child: TextButton( onPressed: () => Navigator.pop(context, false), style: TextButton.styleFrom( foregroundColor: Colors.grey.shade700, padding: const EdgeInsets.symmetric(vertical: 12)), child: const Text("Cancel"), ), ), const SizedBox(width: 18), Expanded( child: ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5)), ), child: const Text("Logout"), ), ), ], ), ], ), ), ); } } class _LoadingSection extends StatelessWidget { const _LoadingSection(); @override Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.symmetric(vertical: 44, horizontal: 8), child: Center(child: CircularProgressIndicator()), ); } }