diff --git a/lib/pages/login.dart b/lib/pages/login.dart index 54753d0..48f49db 100755 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -59,6 +59,8 @@ class _loginState extends State { int? _resendToken; bool _isLoading = false; bool _isLoadingSAML = false; + bool _isSsoButtonEnabled = false; + List _samlDomains = []; bool isEmailFieldVisible = false; bool _obscurePassword = true; bool _resetObscurePassword = true; @@ -100,10 +102,17 @@ class _loginState extends State { decoration: TextDecoration.underline, ); + static const String _appSignature = + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + @override void initState() { super.initState(); countryController.text = "+91"; + emailMobileController.addListener(_updateSsoButtonEnabledState); + WidgetsBinding.instance.addPostFrameCallback((_) { + _fetchSamlDomainsList(); + }); _privacyPolicyTap.onTap = () { if (mounted) context.go('/privacypolicy'); }; @@ -171,11 +180,71 @@ class _loginState extends State { @override void dispose() { + emailMobileController.removeListener(_updateSsoButtonEnabledState); _privacyPolicyTap.dispose(); _termsOfUseTap.dispose(); super.dispose(); } + Future _fetchSamlDomainsList() async { + try { + final response = await http.get( + Uri.parse('${Environment.apiUrlEnrollment}getSamlDomainsList'), + headers: {'APP-SIGNATURE': _appSignature}, + ); + if (!mounted) return; + if (response.statusCode != 200) { + logDebug('getSamlDomainsList failed: ${response.statusCode}'); + return; + } + final body = json.decode(response.body); + if (body is! Map) return; + if (body['status']?.toString() != 'success') { + logDebug('getSamlDomainsList: ${body['message']}'); + return; + } + final domains = _parseSamlDomainsFromResponse(body); + if (!mounted) return; + setState(() { + _samlDomains = domains; + }); + _updateSsoButtonEnabledState(); + } catch (e) { + logDebug('getSamlDomainsList error: $e'); + } + } + + /// API: `{ "data": ["travelspends.com", "azurevenbainfotech.onmicrosoft.com"] }` + List _parseSamlDomainsFromResponse(Map body) { + final data = body['data']; + if (data is! List) return []; + return data + .whereType() + .map((d) => d.trim().toLowerCase()) + .where((d) => d.isNotEmpty) + .toList(); + } + + String? _emailDomainFromInput(String input) { + final trimmed = input.trim(); + if (!_isValidEmailForSso(trimmed)) return null; + return trimmed.split('@').last.toLowerCase(); + } + + bool _emailDomainMatchesSamlList(String input) { + final domain = _emailDomainFromInput(input); + if (domain == null || _samlDomains.isEmpty) return false; + return _samlDomains.contains(domain); + } + + void _updateSsoButtonEnabledState() { + final enabled = + _emailDomainMatchesSamlList(emailMobileController.text); + if (enabled == _isSsoButtonEnabled) return; + if (!mounted) return; + setState(() => _isSsoButtonEnabled = enabled); + } + void validatePassword(String password) { setState(() { hasMinLength = password.length >= 8; @@ -1817,18 +1886,27 @@ class _loginState extends State { width: double.infinity, height: 45, child: ElevatedButton( - onPressed: _handleSsoLoginButtonTap, + onPressed: _isSsoButtonEnabled && + !_isLoadingSAML + ? _handleSsoLoginButtonTap + : null, style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black, + disabledBackgroundColor: + const Color(0xFFF5F5F5), + disabledForegroundColor: + Colors.black38, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 5, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(18), - side: const BorderSide( - color: Colors.black, + side: BorderSide( + color: _isSsoButtonEnabled + ? Colors.black + : Colors.black26, width: 0.5, ), ),