import 'dart:math' as math show sqrt; import 'package:flutter/material.dart'; import 'package:taxglide/consts/app_style.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:get/get.dart'; import 'package:taxglide/consts/app_asstes.dart'; import 'package:taxglide/consts/app_colors.dart'; import 'package:taxglide/consts/local_store.dart'; import 'package:taxglide/controller/api_repository.dart'; import 'package:taxglide/router/consts_routers.dart'; import 'package:taxglide/controller/api_contoller.dart'; import 'package:taxglide/main.dart'; class ProfileScreen extends ConsumerStatefulWidget { const ProfileScreen({super.key}); @override ConsumerState createState() => _ProfileScreenState(); } class _ProfileScreenState extends ConsumerState { String? selectedItem; bool _isLoggingOut = false; String? userRole; // <-- role variable @override void initState() { super.initState(); selectedItem = 'profile'; // Set default selection _loadRole(); // <-- load role on start } Future _loadRole() async { userRole = await LocalStore().getRole(); setState(() {}); } @override Widget build(BuildContext context) { final profileAsync = ref.watch(profileProvider); return Scaffold( backgroundColor: Colors.white, body: profileAsync.when( data: (profile) { final data = profile.data; return SingleChildScrollView( child: Column( children: [ Stack( clipBehavior: Clip.none, children: [ Container( width: double.infinity, decoration: const BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(50), bottomRight: Radius.circular(50), ), ), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(140), bottomRight: Radius.circular(0), ), child: Align( alignment: Alignment.centerRight, child: Image.asset( AppAssets.profilebanner, fit: BoxFit.cover, height: 200, width: MediaQuery.of(context).size.width * 1.3, ), ), ), ), Positioned( bottom: -50, left: 0, right: 0, child: Center( child: Container( decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 4), ), child: data?.logo != null && data!.logo!.isNotEmpty ? CircleAvatar( radius: 50, backgroundImage: NetworkImage(data.logo!), ) : Container( width: 100, height: 100, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.black87, ), child: Center( child: Text( (data?.companyName != null && data!.companyName!.isNotEmpty) ? data.companyName![0].toUpperCase() : "M", style: AppTextStyles.bold.copyWith( color: Colors.white, fontSize: 40, ), ), ), ), ), ), ), ], ), const SizedBox(height: 60), Text( data?.companyName ?? 'No Company Name', style: AppTextStyles.semiBold.copyWith( fontSize: 20, color: Colors.black, ), textAlign: TextAlign.center, ), const SizedBox(height: 30), ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 20), children: [ _buildMenuItem( id: 'profile', icon: Icons.person_outline, title: 'Profile', onTap: () { setState(() { selectedItem = 'profile'; }); final pan = profile.data?.panNumber ?? ''; ref.invalidate(profileProvider); if (pan.isNotEmpty) { Get.toNamed(ConstRouters.kycdetailslist); } else { Get.toNamed(ConstRouters.kycdetails); } }, ), const SizedBox(height: 16), if (userRole != "employee") _buildMenuItem( id: 'Staff', icon: Icons.list_alt_outlined, title: 'Sub - Staff List', onTap: () { setState(() { selectedItem = 'staff'; }); ref.invalidate(staffListProvider); Get.toNamed(ConstRouters.staff); }, ), const SizedBox(height: 16), _buildMenuItem( id: 'terms', icon: Icons.description_outlined, title: 'Terms and Conditions', onTap: () { setState(() { selectedItem = 'terms'; }); ref.invalidate(termsProvider); Get.toNamed(ConstRouters.terms); }, ), const SizedBox(height: 16), _buildMenuItem( id: 'policy', icon: Icons.policy_outlined, title: 'Policy', onTap: () { setState(() { selectedItem = 'policy'; }); ref.invalidate(termsProvider); Get.toNamed(ConstRouters.policy); }, ), const SizedBox(height: 16), _buildMenuItem( id: 'logout', icon: Icons.logout, title: 'Log out', onTap: () { setState(() { selectedItem = 'logout'; }); _showLogoutPopup(context); }, ), ], ), const SizedBox(height: 50), ], ), ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center(child: Text('Error: $err')), ), ); } /// ----------------------- LOGOUT POPUP --------------------- void _showLogoutPopup(BuildContext context) { showGeneralDialog( context: context, barrierDismissible: true, barrierLabel: 'Logout', barrierColor: Colors.black.withOpacity(0.2), transitionDuration: const Duration(milliseconds: 300), pageBuilder: (context, animation, secondaryAnimation) { return const SizedBox.shrink(); }, transitionBuilder: (context, animation, secondaryAnimation, child) { final offsetAnimation = Tween(begin: const Offset(0, -1), end: Offset.zero).animate( CurvedAnimation(parent: animation, curve: Curves.easeOutBack), ); return SlideTransition( position: offsetAnimation, child: Align( alignment: Alignment.topCenter, child: Padding( padding: const EdgeInsets.only(top: 60.0), child: Material( color: Colors.transparent, child: Container( width: MediaQuery.of(context).size.width * 0.9, height: 160, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(7.75), border: Border.all(color: Colors.white, width: 3.87), boxShadow: const [ BoxShadow( color: Color(0x7A5E5E5E), offset: Offset(0, 2.32), blurRadius: 9.14, spreadRadius: 3.1, ), ], ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16, ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Note", style: AppTextStyles.semiBold.copyWith( fontSize: 20, color: Colors.black, ), ), Text( "Are you sure you want to log out?", textAlign: TextAlign.center, style: AppTextStyles.medium.copyWith( fontSize: 15, color: Colors.black87, ), ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( "Cancel", style: AppTextStyles.semiBold.copyWith( color: const Color(0xFF535A5B), fontSize: 16, ), ), ), const SizedBox(width: 8), TextButton( onPressed: _isLoggingOut ? null : () async { Navigator.pop(context); setState(() { _isLoggingOut = true; }); try { await ApiRepository().logout(); _showSnackBar("Logout successful"); await Future.delayed( const Duration(milliseconds: 800), ); Get.offAllNamed(ConstRouters.login); } catch (e) { _showSnackBar( e.toString(), isError: true, ); } finally { setState(() { _isLoggingOut = false; }); } }, child: _isLoggingOut ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFF5F297B), ), ) : Text( "Log Out", style: AppTextStyles.semiBold.copyWith( color: const Color(0xFF5F297B), fontSize: 16, ), ), ), ], ), ], ), ), ), ), ), ), ); }, ); } /// ----------------------- MENU ITEM --------------------- Widget _buildMenuItem({ required String id, required IconData icon, required String title, required VoidCallback onTap, }) { final isSelected = selectedItem == id; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Row( children: [ SizedBox( width: 24, height: 48, child: isSelected ? CustomPaint( size: const Size(24, 48), painter: BeveledTrianglePainter(), ) : const SizedBox.shrink(), ), Expanded( child: Container( height: 58, decoration: BoxDecoration( color: isSelected ? const Color(0xFFEEFAFF) : Colors.transparent, borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Icon(icon, size: 24, color: Colors.black87), const SizedBox(width: 16), Expanded( child: Text( title, style: AppTextStyles.medium.copyWith( fontSize: 16, color: Colors.black87, ), ), ), ], ), ), ), ], ), ); } /// ----------------------- SNACK BAR --------------------- void _showSnackBar(String msg, {bool isError = false}) { rootScaffoldMessengerKey.currentState?.showSnackBar( SnackBar( duration: const Duration(seconds: 2), backgroundColor: isError ? Colors.red : AppColors.commanbutton, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.symmetric(horizontal: 70, vertical: 25), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), content: Row( children: [ Image.asset(AppAssets.taxgildelogoauth, height: 18, width: 18), const SizedBox(width: 8), Expanded( child: Text( msg, style: AppTextStyles.regular.copyWith(color: Colors.white, fontSize: 12), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ), ); } } class BeveledTrianglePainter extends CustomPainter { final double cornerRadius; BeveledTrianglePainter({ this.cornerRadius = 4.0, // Adjust this value for more/less rounding }); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = const Color(0xFF5E2976) ..style = PaintingStyle.fill; // Triangle vertices final vertices = [ Offset(0, cornerRadius), // Top left (adjusted for bevel) Offset(size.width - 6, size.height / 2), // Right point Offset(0, size.height - cornerRadius), // Bottom left (adjusted for bevel) ]; final path = Path(); for (int i = 0; i < vertices.length; i++) { final current = vertices[i]; final next = vertices[(i + 1) % vertices.length]; final prev = vertices[(i - 1 + vertices.length) % vertices.length]; // Calculate direction vectors final toCurrent = Offset(current.dx - prev.dx, current.dy - prev.dy); final toNext = Offset(next.dx - current.dx, next.dy - current.dy); // Normalize final lengthToCurrent = math.sqrt( toCurrent.dx * toCurrent.dx + toCurrent.dy * toCurrent.dy, ); final lengthToNext = math.sqrt( toNext.dx * toNext.dx + toNext.dy * toNext.dy, ); final normalizedToCurrent = Offset( toCurrent.dx / lengthToCurrent, toCurrent.dy / lengthToCurrent, ); final normalizedToNext = Offset( toNext.dx / lengthToNext, toNext.dy / lengthToNext, ); // Points before and after the corner final beforeCorner = Offset( current.dx - normalizedToCurrent.dx * cornerRadius, current.dy - normalizedToCurrent.dy * cornerRadius, ); final afterCorner = Offset( current.dx + normalizedToNext.dx * cornerRadius, current.dy + normalizedToNext.dy * cornerRadius, ); if (i == 0) { path.moveTo(beforeCorner.dx, beforeCorner.dy); } else { path.lineTo(beforeCorner.dx, beforeCorner.dy); } // Draw rounded corner path.quadraticBezierTo( current.dx, current.dy, afterCorner.dx, afterCorner.dy, ); } path.close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }