- Add LoginScreen with form validation and error handling - Add Api.login() method with LoginResponse model - Add login route to AppRouter - Update SplashScreen to check auth status and route to login if needed - Store auth token in Api service for authenticated requests - Fix restaurant selection to work with authenticated users
111 lines
3.5 KiB
Dart
111 lines
3.5 KiB
Dart
// lib/screens/service_point_select_screen.dart
|
|
|
|
import "package:flutter/material.dart";
|
|
import "../models/service_point.dart";
|
|
import "../services/api.dart";
|
|
|
|
class ServicePointSelectScreen extends StatefulWidget {
|
|
const ServicePointSelectScreen({super.key});
|
|
|
|
@override
|
|
State<ServicePointSelectScreen> createState() => _ServicePointSelectScreenState();
|
|
}
|
|
|
|
class _ServicePointSelectScreenState extends State<ServicePointSelectScreen> {
|
|
Future<List<ServicePoint>>? _future;
|
|
int? _businessId;
|
|
int? _userId;
|
|
|
|
int? _asIntNullable(dynamic v) {
|
|
if (v == null) return null;
|
|
if (v is int) return v;
|
|
if (v is num) return v.toInt();
|
|
if (v is String) {
|
|
final s = v.trim();
|
|
if (s.isEmpty) return null;
|
|
return int.tryParse(s);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
|
|
final args = ModalRoute.of(context)?.settings.arguments;
|
|
if (args is Map) {
|
|
final b = _asIntNullable(args["BusinessID"]) ?? _asIntNullable(args["businessId"]);
|
|
final u = _asIntNullable(args["UserID"]) ?? _asIntNullable(args["userId"]);
|
|
|
|
if (_businessId != b || _userId != u || _future == null) {
|
|
_businessId = b;
|
|
_userId = u;
|
|
|
|
if (_businessId != null && _businessId! > 0) {
|
|
_future = Api.listServicePoints(businessId: _businessId!);
|
|
} else {
|
|
_future = Future.value(<ServicePoint>[]);
|
|
}
|
|
}
|
|
} else {
|
|
// No args at all
|
|
_businessId = null;
|
|
_userId = null;
|
|
_future = Future.value(<ServicePoint>[]);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final businessId = _businessId;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text("Select Service Point")),
|
|
body: (businessId == null || businessId <= 0)
|
|
? const Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child: Text("Missing route arguments: BusinessID"),
|
|
)
|
|
: FutureBuilder<List<ServicePoint>>(
|
|
future: _future,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
|
|
if (snapshot.hasError) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Text(snapshot.error.toString()),
|
|
);
|
|
}
|
|
|
|
final items = snapshot.data ?? const <ServicePoint>[];
|
|
if (items.isEmpty) {
|
|
return const Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child: Text("No service points returned."),
|
|
);
|
|
}
|
|
|
|
return ListView.separated(
|
|
itemCount: items.length,
|
|
separatorBuilder: (_, __) => const Divider(height: 1),
|
|
itemBuilder: (context, i) {
|
|
final sp = items[i];
|
|
return ListTile(
|
|
title: Text(sp.name),
|
|
subtitle: Text("ID: ${sp.servicePointId}"),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () {
|
|
// Return selection to the caller.
|
|
Navigator.of(context).pop<ServicePoint>(sp);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|