payfrit-app/lib/screens/service_point_select_screen.dart
John Mizerek 33f7128b40 feat: implement user authentication with login screen
- 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
2025-12-29 10:01:35 -08:00

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);
},
);
},
);
},
),
);
}
}