import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.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/app_style.dart'; import 'package:taxglide/consts/comman_button.dart'; import 'package:taxglide/consts/comman_container_auth.dart'; import 'package:taxglide/consts/comman_textformfileds.dart'; import 'package:taxglide/consts/responsive_helper.dart'; import 'package:taxglide/consts/validation_popup.dart'; import 'package:taxglide/controller/api_contoller.dart'; import 'package:taxglide/router/consts_routers.dart'; import 'package:taxglide/view/Main_controller/main_controller.dart'; class EmployeeLoginScreen extends ConsumerStatefulWidget { const EmployeeLoginScreen({super.key}); @override ConsumerState createState() => _EmployeeLoginScreenState(); } class _EmployeeLoginScreenState extends ConsumerState { final ValidationPopup _validationPopup = ValidationPopup(); final TextEditingController _emailController = TextEditingController(); // Multi-box PIN logic final int pinLength = 4; final List _pinControllers = []; final List _pinFocusNodes = []; String pinValue = ''; // Error states bool _emailHasError = false; bool _pinHasError = false; @override void initState() { super.initState(); // Initialize PIN controllers and focus nodes for (int i = 0; i < pinLength; i++) { _pinControllers.add(TextEditingController()); _pinFocusNodes.add(FocusNode()); } final args = Get.arguments; if (args != null && args is Map) { final email = args['email'] ?? ''; if (email.isNotEmpty) { _emailController.text = email; } } } @override void dispose() { _emailController.dispose(); for (var controller in _pinControllers) { controller.dispose(); } for (var node in _pinFocusNodes) { node.dispose(); } super.dispose(); } void _onPinChange(int index, String value) { if (_pinHasError) { setState(() => _pinHasError = false); } if (value.isNotEmpty && index < pinLength - 1) { _pinFocusNodes[index + 1].requestFocus(); } if (value.isEmpty && index > 0) { _pinFocusNodes[index - 1].requestFocus(); } setState(() { pinValue = _pinControllers.map((c) => c.text).join(); }); } Future _handleLogin() async { final email = _emailController.text.trim(); final pin = pinValue.trim(); setState(() { _emailHasError = false; _pinHasError = false; }); if (!_validationPopup.validateEmail(context, email)) { setState(() => _emailHasError = true); return; } if (!_validationPopup.validatePin(context, pin)) { setState(() => _pinHasError = true); return; } await ref.read(employeeloginProvider.notifier).loginWithPin(email, pin); final state = ref.read(employeeloginProvider); state.when( data: (data) { if (data['success'] == true) { _validationPopup.showSuccessMessage( context, "Login Successful!", ); Future.delayed(const Duration(seconds: 1), () { Get.offAll(() => const MainController()); }); } else if (data['error'] != null) { _validationPopup.showErrorMessage(context, data['error'].toString()); setState(() { _emailHasError = true; _pinHasError = true; }); } }, loading: () {}, error: (err, _) { _validationPopup.showErrorMessage(context, "Error: $err"); }, ); } @override Widget build(BuildContext context) { final loginState = ref.watch(employeeloginProvider); // Initialize responsive utils final r = ResponsiveUtils(context); // Responsive values final logoWidth = r.getValue(mobile: 120, tablet: 141, desktop: 160); final logoHeight = r.getValue(mobile: 85, tablet: 100, desktop: 115); final titleFontSize = r.fontSize(mobile: 26, tablet: 32, desktop: 36); final subtitleFontSize = r.fontSize(mobile: 13, tablet: 14, desktop: 15); final termsFontSize = r.fontSize(mobile: 10.5, tablet: 11.34, desktop: 12); final linkFontSize = r.fontSize(mobile: 13, tablet: 14, desktop: 15); final otherLoginFontSize = r.fontSize(mobile: 12, tablet: 13, desktop: 14); final pinTitleFontSize = r.fontSize(mobile: 20, tablet: 24, desktop: 26); final pinBoxWidth = r.getValue(mobile: 55, tablet: 60, desktop: 68); final pinBoxHeight = r.getValue(mobile: 55, tablet: 60, desktop: 68); final pinFontSize = r.fontSize(mobile: 22, tablet: 28, desktop: 32); final pinBoxMargin = r.getValue(mobile: 8, tablet: 6, desktop: 8); final pinBoxRadius = r.getValue(mobile: 5, tablet: 6, desktop: 8); final spacingXS = r.spacing(mobile: 10, tablet: 10, desktop: 12); final spacingSM = r.spacing(mobile: 15, tablet: 20, desktop: 24); final spacingMD = r.spacing(mobile: 20, tablet: 20, desktop: 24); final spacingLG = r.spacing(mobile: 20, tablet: 22, desktop: 28); return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; Get.offAllNamed(ConstRouters.login); }, child: Scaffold( resizeToAvoidBottomInset: true, body: Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFFFFF8F0), Color(0xFFEBC894), Color(0xFFE8DAF2), Color(0xFFB49EF4), ], stops: [0.0, 0.3, 0.6, 1.0], ), ), child: SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints(minHeight: r.screenHeight), child: IntrinsicHeight( child: Center( child: CommonContainerAuth( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ // Logo Image.asset( AppAssets.taxgildelogoauth, width: logoWidth, height: logoHeight, fit: BoxFit.contain, ), SizedBox(height: spacingMD), // Login Title Text( "Employee Login", textAlign: TextAlign.center, style: AppTextStyles.bold.copyWith( fontSize: titleFontSize, color: AppColors.authheading, ), ), SizedBox(height: spacingMD), // Subtitle Text( "Enter your Email and PIN", textAlign: TextAlign.center, style: AppTextStyles.bold.copyWith( fontSize: subtitleFontSize, color: AppColors.authleading, ), ), SizedBox(height: spacingLG), Align( alignment: Alignment.centerLeft, child: Text( "Enter Your Mail ID *", style: AppTextStyles.semiBold.copyWith( fontSize: 14, color: AppColors.authheading, ), ), ), SizedBox(height: spacingSM), CommanTextFormField( controller: _emailController, hintText: 'Enter your Email', keyboardType: TextInputType.emailAddress, prefixIcon: Icons.email_outlined, hasError: _emailHasError, onChanged: (value) { if (_emailHasError) { setState(() => _emailHasError = false); } }, ), SizedBox(height: spacingSM), Align( alignment: Alignment.centerLeft, child: Text( "Enter Your Pin *", style: AppTextStyles.semiBold.copyWith( fontSize: 14, color: AppColors.authheading, ), ), ), SizedBox(height: spacingSM), // PIN Boxes Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(pinLength, (index) { bool isFilled = _pinControllers[index].text.isNotEmpty; return Container( margin: EdgeInsets.symmetric(horizontal: pinBoxMargin), width: pinBoxWidth, height: pinBoxHeight, decoration: BoxDecoration( color: isFilled ? AppColors.commanbutton : Colors.white, borderRadius: BorderRadius.circular(pinBoxRadius), border: Border.all( color: _pinHasError ? Colors.red : const Color(0xFFDFDFDF), width: _pinHasError ? 1.5 : 1.0, ), boxShadow: [ BoxShadow( color: _pinHasError ? Colors.red.withOpacity(0.1) : const Color(0xFFBDBDBD).withOpacity(0.25), blurRadius: 7, offset: const Offset(0, 1), ), ], ), child: Center( child: KeyboardListener( focusNode: FocusNode(), onKeyEvent: (event) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.backspace) { if (_pinControllers[index] .text .isEmpty && index > 0) { _pinFocusNodes[index - 1] .requestFocus(); } } }, child: TextField( controller: _pinControllers[index], focusNode: _pinFocusNodes[index], textAlign: TextAlign.center, keyboardType: TextInputType.number, maxLength: 1, obscureText: true, enabled: !loginState.isLoading, style: TextStyle( fontFamily: "Gilroy", fontWeight: FontWeight.w400, fontSize: pinFontSize, color: isFilled ? Colors.white : Colors.black, ), decoration: const InputDecoration( counterText: '', border: InputBorder.none, ), onChanged: (value) => _onPinChange(index, value), ), ), ), ); }), ), SizedBox(height: spacingXS), Align( alignment: Alignment.centerRight, child: GestureDetector( onTap: () { Get.toNamed( ConstRouters.forgotPassword, arguments: {'isEmployee': true}, ); }, child: Text( "Forgot PIN?", style: AppTextStyles.bold.copyWith( fontSize: linkFontSize, color: AppColors.authchange, ), ), ), ), SizedBox(height: spacingLG), // Terms and Conditions Text.rich( TextSpan( text: "By signing up, you agree to the ", style: AppTextStyles.medium.copyWith( fontSize: termsFontSize, color: AppColors.authleading, ), children: [ TextSpan( text: "Terms of Service ", style: AppTextStyles.bold.copyWith( fontSize: termsFontSize, color: AppColors.authtermsandcondition, ), ), TextSpan( text: "and ", style: AppTextStyles.medium.copyWith( fontSize: termsFontSize, color: AppColors.authleading, ), ), TextSpan( text: "Data Processing Agreement", style: AppTextStyles.bold.copyWith( fontSize: termsFontSize, color: AppColors.authtermsandcondition, ), ), ], ), textAlign: TextAlign.center, ), SizedBox(height: spacingLG), // Login Button or Loading loginState.isLoading ? const CircularProgressIndicator() : CommanButton( text: "Login", onPressed: _handleLogin, ), SizedBox(height: spacingSM), Text( "Other Login", textAlign: TextAlign.center, style: AppTextStyles.semiBold.copyWith( fontSize: otherLoginFontSize, color: AppColors.authleading, ), ), SizedBox(height: spacingXS), Text.rich( TextSpan( text: "User Login? ", style: AppTextStyles.bold.copyWith( fontSize: linkFontSize, color: AppColors.black, ), children: [ TextSpan( text: "Click Here", style: AppTextStyles.bold.copyWith( fontSize: linkFontSize, color: AppColors.authsignup, ), recognizer: TapGestureRecognizer() ..onTap = () { Get.offNamed(ConstRouters.login); }, ), ], ), ), ], ), ), ), ), ), ), ), ), ); } }