178 lines
5.8 KiB
Dart
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'),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|