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/helpers/services/tenant_service.dart'; import 'package:marco/view/tenant/tenant_selection_screen.dart'; import 'package:marco/controller/tenant/tenant_switch_controller.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(22), 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(22), 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), // --- SWITCH TENANT ROW BELOW AVATAR --- if (!_isLoading && !isCondensed) _switchTenantRow(), 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), ], ), ), ), ), ), ); } /// Row widget to switch tenant with popup menu (button only) /// Row widget to switch tenant with popup menu (button only) Widget _switchTenantRow() { // Use the dedicated switch controller final TenantSwitchController tenantSwitchController = Get.put(TenantSwitchController()); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), child: Obx(() { if (tenantSwitchController.isLoading.value) { return _loadingTenantContainer(); } final tenants = tenantSwitchController.tenants; if (tenants.isEmpty) return _noTenantContainer(); final selectedTenant = TenantService.currentTenant; // Sort tenants: selected tenant first final sortedTenants = List.of(tenants); if (selectedTenant != null) { sortedTenants.sort((a, b) { if (a.id == selectedTenant.id) return -1; if (b.id == selectedTenant.id) return 1; return 0; }); } return PopupMenuButton( onSelected: (tenantId) => tenantSwitchController.switchTenant(tenantId), itemBuilder: (_) => sortedTenants.map((tenant) { return PopupMenuItem( value: tenant.id, child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: Container( width: 20, height: 20, color: Colors.grey.shade200, child: TenantLogo(logoImage: tenant.logoImage), ), ), const SizedBox(width: 10), Expanded( child: Text( tenant.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: tenant.id == selectedTenant?.id ? FontWeight.bold : FontWeight.w600, color: tenant.id == selectedTenant?.id ? Colors.blueAccent : Colors.black87, ), ), ), if (tenant.id == selectedTenant?.id) const Icon(Icons.check_circle, color: Colors.blueAccent, size: 18), ], ), ); }).toList(), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.swap_horiz, color: Colors.blue.shade600), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Text( "Switch Organization", maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.blue, fontWeight: FontWeight.bold), ), ), ), Icon(Icons.arrow_drop_down, color: Colors.blue.shade600), ], ), ), ); }), ); } Widget _loadingTenantContainer() => Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.shade200, width: 1), ), child: const Center(child: CircularProgressIndicator(strokeWidth: 2)), ); Widget _noTenantContainer() => Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.blue.shade200, width: 1), ), child: MyText.bodyMedium( "No tenants available", color: Colors.blueAccent, fontWeight: 600, ), ); 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.settings, label: 'Settings', ), SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.badge_alert, label: 'Support', ), SizedBox(height: spacingHeight), _menuItemRow( icon: LucideIcons.lock, label: hasMpin ? 'Change MPIN' : 'Set MPIN', iconColor: Colors.redAccent, textColor: Colors.redAccent, onTap: _onMpinTap, ), ], ), ); } Widget _menuItemRow({ required IconData icon, required String label, VoidCallback? onTap, Color? iconColor, Color? textColor, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.white.withOpacity(0.9), borderRadius: BorderRadius.circular(12), 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 ? 14 : 18, horizontal: condensed ? 14 : 22, ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ), ); } 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(24)), 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(14)), ), 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()), ); } }