All checks were successful
Release / meta (push) Successful in 6s
Release / linux-build (push) Successful in 57s
Release / web-build (push) Successful in 1m14s
Release / android-build (push) Successful in 5m33s
Release / release-master (push) Successful in 18s
Release / release-dev (push) Successful in 20s
141 lines
3.8 KiB
Dart
141 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:mileograph_flutter/components/traction/traction_card.dart';
|
|
import 'package:mileograph_flutter/objects/objects.dart';
|
|
import 'package:mileograph_flutter/services/api_service.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class TractionPendingPage extends StatefulWidget {
|
|
const TractionPendingPage({super.key});
|
|
|
|
@override
|
|
State<TractionPendingPage> createState() => _TractionPendingPageState();
|
|
}
|
|
|
|
class _TractionPendingPageState extends State<TractionPendingPage> {
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
List<LocoSummary> _locos = const [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => _load());
|
|
}
|
|
|
|
Future<void> _load() async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_error = null;
|
|
});
|
|
try {
|
|
final api = context.read<ApiService>();
|
|
final params = '?limit=200&offset=0';
|
|
final json = await api.get('/loco/pending$params');
|
|
if (json is List) {
|
|
setState(() {
|
|
_locos = json
|
|
.whereType<Map>()
|
|
.map((e) => LocoSummary.fromJson(
|
|
e.map((k, v) => MapEntry(k.toString(), v)),
|
|
))
|
|
.toList();
|
|
});
|
|
} else {
|
|
setState(() {
|
|
_error = 'Unexpected response';
|
|
_locos = const [];
|
|
});
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_error = e.toString();
|
|
_locos = const [];
|
|
});
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () => Navigator.of(context).maybePop(),
|
|
),
|
|
title: const Text('Pending traction'),
|
|
),
|
|
body: RefreshIndicator(
|
|
onRefresh: _load,
|
|
child: _buildBody(context),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBody(BuildContext context) {
|
|
if (_isLoading && _locos.isEmpty) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
if (_error != null) {
|
|
return ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
Text(
|
|
'Failed to load pending traction: $_error',
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.bodyMedium
|
|
?.copyWith(color: Theme.of(context).colorScheme.error),
|
|
),
|
|
const SizedBox(height: 12),
|
|
FilledButton.icon(
|
|
onPressed: _load,
|
|
icon: const Icon(Icons.refresh),
|
|
label: const Text('Retry'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
if (_locos.isEmpty) {
|
|
return ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: const [
|
|
Text('No pending traction found.'),
|
|
],
|
|
);
|
|
}
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(12),
|
|
itemCount: _locos.length,
|
|
itemBuilder: (context, index) {
|
|
final loco = _locos[index];
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
|
child: TractionCard(
|
|
loco: loco,
|
|
selectionMode: false,
|
|
isSelected: false,
|
|
onShowInfo: () => showTractionDetails(
|
|
context,
|
|
loco,
|
|
onActionComplete: _load,
|
|
),
|
|
onOpenTimeline: () => context.push(
|
|
'/traction/${loco.id}/timeline',
|
|
extra: {'label': '${loco.locoClass} ${loco.number}'.trim()},
|
|
),
|
|
onOpenLegs: () => context.push('/traction/${loco.id}/legs'),
|
|
onActionComplete: _load,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|