import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:marco/controller/auth/mpin_controller.dart'; import 'package:marco/helpers/widgets/my_button.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/images.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/wave_background.dart'; class MPINAuthScreen extends StatefulWidget { const MPINAuthScreen({super.key}); @override State createState() => _MPINAuthScreenState(); } class _MPINAuthScreenState extends State with UIMixin, SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _logoAnimation; bool get _isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage"); @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 800), ); _logoAnimation = CurvedAnimation( parent: _controller, curve: Curves.easeOutBack, ); _controller.forward(); } @override void dispose() { Get.delete(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final controller = Get.put(MPINController()); return Scaffold( body: Stack( children: [ WaveBackground(color: contentTheme.brandRed), SafeArea( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 24), // Static Logo (not scrollable) ScaleTransition( scale: _logoAnimation, child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 10, offset: Offset(0, 4), ), ], ), padding: const EdgeInsets.all(20), child: Image.asset(Images.logoDark), ), ), const SizedBox(height: 8), // Scrollable content below the logo Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: Column( children: [ const SizedBox(height: 12), MyText( "Welcome to Marco", fontSize: 24, fontWeight: 800, color: Colors.black87, textAlign: TextAlign.center, ), const SizedBox(height: 10), MyText( "Streamline Project Management\nBoost Productivity with Automation.", fontSize: 14, color: Colors.black54, textAlign: TextAlign.center, ), if (_isBetaEnvironment) ...[ const SizedBox(height: 12), 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, ), ), ], const SizedBox(height: 36), _buildMPINCard(controller), ], ), ), ), ), ], ), ), ), ], ), ); } Widget _buildMPINCard(MPINController controller) { return Obx(() { final isNewUser = controller.isNewUser.value; final isChangeMpin = controller.isChangeMpin.value; return Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( children: [ MyText( isChangeMpin ? 'Change MPIN' : (isNewUser ? 'Generate MPIN' : 'Enter MPIN'), fontSize: 20, fontWeight: 700, color: Colors.black87, textAlign: TextAlign.center, ), const SizedBox(height: 10), MyText( isChangeMpin ? 'Set a new 4-digit MPIN for your account.' : (isNewUser ? 'Set your 4-digit MPIN for quick login.' : 'Enter your 4-digit MPIN to continue.'), fontSize: 14, color: Colors.black54, textAlign: TextAlign.center, ), const SizedBox(height: 30), _buildMPINForm(controller, isNewUser || isChangeMpin), const SizedBox(height: 32), _buildSubmitButton(controller, isNewUser, isChangeMpin), const SizedBox(height: 20), _buildFooterOptions(controller, isNewUser), ], ), ); }); } Widget _buildMPINForm(MPINController controller, bool showRetype) { return Form( key: controller.formKey, child: Column( children: [ _buildDigitRow(controller, isRetype: false), if (showRetype) ...[ const SizedBox(height: 20), MyText( 'Retype MPIN', fontWeight: 600, color: Colors.black.withOpacity(0.6), fontSize: 14, ), const SizedBox(height: 8), _buildDigitRow(controller, isRetype: true), ], ], ), ); } Widget _buildDigitRow(MPINController controller, {required bool isRetype}) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(4, (index) { return _buildDigitBox(controller, index, isRetype); }), ); } Widget _buildDigitBox(MPINController controller, int index, bool isRetype) { final textController = isRetype ? controller.retypeControllers[index] : controller.digitControllers[index]; final focusNode = isRetype ? controller.retypeFocusNodes[index] : controller.focusNodes[index]; return Container( margin: const EdgeInsets.symmetric(horizontal: 8), width: 48, height: 60, child: TextFormField( controller: textController, focusNode: focusNode, obscureText: false, // Digits are visible maxLength: 1, textAlign: TextAlign.center, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], onChanged: (value) { controller.onDigitChanged(value, index, isRetype: isRetype); // Auto-submit only in verification mode if (!isRetype) { final isComplete = controller.digitControllers.every((c) => c.text.isNotEmpty); if (isComplete && !controller.isLoading.value && !controller.isNewUser.value && !controller.isChangeMpin.value) { controller.onSubmitMPIN(); } } }, decoration: InputDecoration( counterText: '', filled: true, fillColor: Colors.grey.shade100, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide.none, ), ), ), ); } Widget _buildSubmitButton( MPINController controller, bool isNewUser, bool isChangeMpin) { return Obx(() { return MyButton.rounded( onPressed: controller.isLoading.value ? null : controller.onSubmitMPIN, elevation: 2, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), borderRadiusAll: 5, backgroundColor: controller.isLoading.value ? contentTheme.primary.withOpacity(0.6) : contentTheme.primary, child: controller.isLoading.value ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : MyText.bodyMedium( isChangeMpin ? 'Change MPIN' : (isNewUser ? 'Generate MPIN' : 'Submit MPIN'), color: Colors.white, fontWeight: 700, fontSize: 16, ), ); }); } Widget _buildFooterOptions(MPINController controller, bool isNewUser) { return Obx(() { final isChangeMpin = controller.isChangeMpin.value; final showBackToLogin = controller.failedAttempts.value >= 3 && !isNewUser && !isChangeMpin; return Column( children: [ if (isNewUser || isChangeMpin) TextButton.icon( onPressed: () => Get.toNamed('/dashboard'), icon: const Icon(Icons.arrow_back, size: 18, color: Colors.redAccent), label: MyText.bodyMedium( 'Back to Home Page', color: contentTheme.brandRed, fontWeight: 600, fontSize: 14, ), ), if (showBackToLogin) TextButton.icon( onPressed: () async => await LocalStorage.logout(), icon: const Icon(Icons.arrow_back, size: 18, color: Colors.redAccent), label: MyText.bodyMedium( 'Go back to Login Screen', color: contentTheme.brandRed, fontWeight: 600, fontSize: 14, ), ), ], ); }); } }