import 'package:flutter/material.dart'; import '../api_service.dart'; import '../settings.dart'; import 'auth_shell.dart'; import 'home_screen.dart'; class LoginScreen extends StatefulWidget { final ApiService api; final Settings settings; const LoginScreen({super.key, required this.api, required this.settings}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _email = TextEditingController(); final _pin = TextEditingController(); bool _busy = false; String? _error; /// Zoho-style: the email first, then the PIN on its own step. bool _askPin = false; bool _keep = true; static final _emailPattern = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$'); void _next() { final email = _email.text.trim(); if (!_emailPattern.hasMatch(email)) { setState(() => _error = 'Enter a valid work email address'); return; } setState(() { _askPin = true; _error = null; }); } void _change() => setState(() { _askPin = false; _pin.clear(); _error = null; }); Future _login() async { if (_pin.text.isEmpty) { setState(() => _error = 'Enter your PIN'); return; } setState(() { _busy = true; _error = null; }); try { await widget.api.login(_email.text.trim(), _pin.text); await KeepSignedIn.save(_keep); if (!mounted) return; Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomeScreen(api: widget.api, settings: widget.settings))); } on ApiException catch (e) { setState(() => _error = e.message); } finally { if (mounted) setState(() => _busy = false); } } void _forgotEmail() { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Use the work email HR added you with. Not sure which one? Ask your HR or manager.'), behavior: SnackBarBehavior.floating, )); } void _forgotPin() { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Your PIN is set by HR. Ask your HR or manager to reset it.'), behavior: SnackBarBehavior.floating, )); } @override Widget build(BuildContext context) { if (_askPin) { return AuthShell( appName: 'Scalar Presence', product: 'Presence', tagline: 'Check in, apply for leave and see your payslips.', title: 'Enter PIN', children: [ AuthIdentity(value: _email.text.trim(), onBack: _busy ? () {} : _change), const SizedBox(height: 16), if (_error != null) AuthError(_error!), AuthField( key: const ValueKey('pin'), controller: _pin, hint: 'PIN', icon: Icons.lock_outline, secret: true, autofocus: true, error: _error != null, keyboardType: TextInputType.number, action: TextInputAction.done, onSubmitted: (_) => _login(), ), const SizedBox(height: 12), AuthLink('Forgot PIN?', onPressed: _forgotPin), AuthKeepSignedIn(value: _keep, onChanged: (v) => setState(() => _keep = v)), const SizedBox(height: 20), AuthButton(label: 'Sign in', busy: _busy, onPressed: _login), ], ); } return AuthShell( appName: 'Scalar Presence', product: 'Presence', tagline: 'Check in, apply for leave and see your payslips.', children: [ if (_error != null) AuthError(_error!), AuthField( key: const ValueKey('email'), controller: _email, hint: 'Work email', icon: Icons.mail_outline, error: _error != null, keyboardType: TextInputType.emailAddress, action: TextInputAction.next, onSubmitted: (_) => _next(), ), const SizedBox(height: 12), AuthLink('Forgot email?', onPressed: _forgotEmail), const SizedBox(height: 28), AuthButton(label: 'Next', busy: false, onPressed: _next), ], ); } }