Files
2026-10-01 16:03:32 +05:30

178 lines
5.8 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../services/auth_controller.dart';
class AuthFlow extends StatefulWidget {
const AuthFlow({super.key});
@override
State<AuthFlow> createState() => _AuthFlowState();
}
class _AuthFlowState extends State<AuthFlow> {
final _phone = TextEditingController();
final _password = TextEditingController();
bool _staff = false;
bool _waiting = false;
Timer? _poll;
@override
void dispose() {
_poll?.cancel();
_phone.dispose();
_password.dispose();
super.dispose();
}
Future<void> _staffLogin() async {
final auth = context.read<AuthController>();
try {
await auth.staffLogin(_phone.text.trim(), _password.text);
} catch (_) {}
}
Future<void> _dealerLogin() async {
final auth = context.read<AuthController>();
try {
final started = await auth.startDealerLogin(_phone.text.trim());
if (!mounted) return;
setState(() => _waiting = true);
_poll = Timer.periodic(
const Duration(seconds: 2),
(_) => _check(started.requestId, started.secret),
);
await _check(started.requestId, started.secret);
} catch (_) {}
}
Future<void> _check(String requestId, String secret) async {
final auth = context.read<AuthController>();
try {
final status = await auth.pollDealerLogin(requestId, secret);
if (!mounted) return;
if (status == 'pending') return;
_poll?.cancel();
if (status != 'approved') setState(() => _waiting = false);
} catch (_) {
if (!mounted) return;
_poll?.cancel();
setState(() => _waiting = false);
}
}
void _cancelWait() {
_poll?.cancel();
context.read<AuthController>().error = null;
setState(() => _waiting = false);
}
@override
Widget build(BuildContext context) {
final auth = context.watch<AuthController>();
final theme = Theme.of(context);
return Scaffold(
body: SafeArea(
child: Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 440),
child: ListView(
padding: const EdgeInsets.fromLTRB(24, 48, 24, 24),
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(
'assets/images/senaro_logo.png',
width: 300,
semanticLabel: 'Senaro',
),
),
),
const SizedBox(height: 20),
Text(
'Dealer Network',
textAlign: TextAlign.center,
style: theme.textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
_waiting
? 'Waiting for a sales rep or super admin to approve ${_phone.text.trim()}.'
: _staff
? 'Staff sign in with the phone number and password on the account.'
: 'Dealers sign in with the phone number already registered for the dealership.',
style: theme.textTheme.bodyLarge,
),
const SizedBox(height: 28),
if (_waiting)
const Center(child: CircularProgressIndicator())
else ...[
SegmentedButton<bool>(
segments: const [
ButtonSegment(value: false, label: Text('Dealer')),
ButtonSegment(value: true, label: Text('Staff')),
],
selected: {_staff},
onSelectionChanged: auth.busy
? null
: (value) => setState(() => _staff = value.first),
),
const SizedBox(height: 16),
TextField(
controller: _phone,
keyboardType: TextInputType.phone,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: const InputDecoration(
labelText: 'Phone',
hintText: '071xxxxxxx',
),
onSubmitted: (_) => _staff ? _staffLogin() : _dealerLogin(),
),
if (_staff) ...[
const SizedBox(height: 12),
TextField(
controller: _password,
obscureText: true,
decoration: const InputDecoration(labelText: 'Password'),
onSubmitted: (_) => _staffLogin(),
),
],
],
if (auth.error != null) ...[
const SizedBox(height: 12),
Text(
auth.error!,
style: TextStyle(color: theme.colorScheme.error),
),
],
const SizedBox(height: 20),
if (_waiting)
TextButton(
onPressed: _cancelWait,
child: const Text('Cancel'),
)
else
FilledButton(
onPressed: auth.busy
? null
: (_staff ? _staffLogin : _dealerLogin),
child: Text(
auth.busy
? 'Please wait'
: (_staff ? 'Sign in' : 'Request login'),
),
),
],
),
),
),
),
);
}
}