import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/images.dart'; import 'package:marco/controller/tenant/tenant_selection_controller.dart'; import 'package:marco/view/splash_screen.dart'; class TenantSelectionScreen extends StatefulWidget { const TenantSelectionScreen({super.key}); @override State createState() => _TenantSelectionScreenState(); } class _TenantSelectionScreenState extends State with UIMixin, SingleTickerProviderStateMixin { late final TenantSelectionController _controller; late final AnimationController _logoAnimController; late final Animation _logoAnimation; final bool _isBetaEnvironment = ApiEndpoints.baseUrl.contains("stage"); @override void initState() { super.initState(); _controller = Get.put(TenantSelectionController()); _logoAnimController = AnimationController( vsync: this, duration: const Duration(milliseconds: 800), ); _logoAnimation = CurvedAnimation( parent: _logoAnimController, curve: Curves.easeOutBack, ); _logoAnimController.forward(); } @override void dispose() { _logoAnimController.dispose(); Get.delete(); super.dispose(); } Future _onTenantSelected(String tenantId) async { await _controller.onTenantSelected(tenantId); } @override Widget build(BuildContext context) { return Obx(() { // Splash screen for auto-selection if (_controller.isAutoSelecting.value) { return const SplashScreen(); } return Scaffold( body: Stack( children: [ _RedWaveBackground(brandRed: contentTheme.brandRed), SafeArea( child: Center( child: Column( children: [ const SizedBox(height: 24), _AnimatedLogo(animation: _logoAnimation), const SizedBox(height: 8), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: Column( children: [ const SizedBox(height: 12), const _WelcomeTexts(), if (_isBetaEnvironment) ...[ const SizedBox(height: 12), const _BetaBadge(), ], const SizedBox(height: 36), TenantCardList( controller: _controller, isLoading: _controller.isLoading.value, onTenantSelected: _onTenantSelected, ), ], ), ), ), ), ), ], ), ), ), ], ), ); }); } } /// Animated Logo Widget class _AnimatedLogo extends StatelessWidget { final Animation animation; const _AnimatedLogo({required this.animation}); @override Widget build(BuildContext context) { return ScaleTransition( scale: animation, child: Container( width: 100, height: 100, padding: const EdgeInsets.all(20), decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Image.asset(Images.logoDark), ), ); } } /// Welcome Texts class _WelcomeTexts extends StatelessWidget { const _WelcomeTexts(); @override Widget build(BuildContext context) { return Column( children: [ MyText( "Welcome", fontSize: 24, fontWeight: 600, color: Colors.black87, textAlign: TextAlign.center, ), const SizedBox(height: 10), MyText( "Please select which dashboard you want to explore!", fontSize: 14, color: Colors.black54, textAlign: TextAlign.center, ), ], ); } } /// Beta Badge class _BetaBadge extends StatelessWidget { const _BetaBadge(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: Colors.orangeAccent, borderRadius: BorderRadius.circular(5), ), child: MyText( 'BETA', color: Colors.white, fontWeight: 600, fontSize: 12, ), ); } } /// Tenant Card List class TenantCardList extends StatelessWidget { final TenantSelectionController controller; final bool isLoading; final Function(String tenantId) onTenantSelected; const TenantCardList({ required this.controller, required this.isLoading, required this.onTenantSelected, }); @override Widget build(BuildContext context) { return Obx(() { if (controller.isLoading.value || isLoading) { return const Center(child: CircularProgressIndicator(strokeWidth: 2)); } if (controller.tenants.isEmpty) { return Center( child: MyText( "No dashboards available for your account.", fontSize: 14, color: Colors.black54, textAlign: TextAlign.center, ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ ...controller.tenants.map( (tenant) => _TenantCard( tenant: tenant, onTap: () => onTenantSelected(tenant.id), ), ), const SizedBox(height: 16), TextButton.icon( onPressed: () => Get.back(), icon: const Icon(Icons.arrow_back, size: 20, color: Colors.redAccent), label: MyText( 'Back to Login', color: Colors.red, fontWeight: 600, fontSize: 14, ), ), ], ); }); } } /// Single Tenant Card class _TenantCard extends StatelessWidget { final dynamic tenant; final VoidCallback onTap; const _TenantCard({required this.tenant, required this.onTap}); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(5), child: Card( elevation: 3, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), margin: const EdgeInsets.only(bottom: 20), child: Padding( padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(5), child: Container( width: 60, height: 60, color: Colors.grey.shade200, child: TenantLogo(logoImage: tenant.logoImage), ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText( tenant.name, fontSize: 18, fontWeight: 700, color: Colors.black87, ), const SizedBox(height: 6), MyText( "Industry: ${tenant.industry?.name ?? "-"}", fontSize: 13, color: Colors.black54, ), ], ), ), const Icon(Icons.arrow_forward_ios, size: 24, color: Colors.red), ], ), ), ), ); } } /// Tenant Logo (supports base64 and URL) class TenantLogo extends StatelessWidget { final String? logoImage; const TenantLogo({required this.logoImage}); @override Widget build(BuildContext context) { if (logoImage == null || logoImage!.isEmpty) { return Center(child: Icon(Icons.business, color: Colors.grey.shade600)); } if (logoImage!.startsWith("data:image")) { try { final base64Str = logoImage!.split(',').last; final bytes = base64Decode(base64Str); return Image.memory(bytes, fit: BoxFit.cover); } catch (_) { return Center(child: Icon(Icons.business, color: Colors.grey.shade600)); } } else { return Image.network( logoImage!, fit: BoxFit.cover, errorBuilder: (_, __, ___) => Center( child: Icon(Icons.business, color: Colors.grey.shade600), ), ); } } } /// Red Wave Background class _RedWaveBackground extends StatelessWidget { final Color brandRed; const _RedWaveBackground({required this.brandRed}); @override Widget build(BuildContext context) { return CustomPaint( painter: _WavePainter(brandRed), size: Size.infinite, ); } } class _WavePainter extends CustomPainter { final Color brandRed; _WavePainter(this.brandRed); @override void paint(Canvas canvas, Size size) { final paint1 = Paint() ..shader = LinearGradient( colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)], begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); final path1 = Path() ..moveTo(0, size.height * 0.2) ..quadraticBezierTo(size.width * 0.25, size.height * 0.05, size.width * 0.5, size.height * 0.15) ..quadraticBezierTo( size.width * 0.75, size.height * 0.25, size.width, size.height * 0.1) ..lineTo(size.width, 0) ..lineTo(0, 0) ..close(); canvas.drawPath(path1, paint1); final paint2 = Paint()..color = Colors.redAccent.withOpacity(0.15); final path2 = Path() ..moveTo(0, size.height * 0.25) ..quadraticBezierTo( size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2) ..lineTo(size.width, 0) ..lineTo(0, 0) ..close(); canvas.drawPath(path2, paint2); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; }