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 createState() => _TractionPendingPageState(); } class _TractionPendingPageState extends State { bool _isLoading = false; String? _error; List _locos = const []; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _load()); } Future _load() async { setState(() { _isLoading = true; _error = null; }); try { final api = context.read(); final params = '?limit=200&offset=0'; final json = await api.get('/loco/pending$params'); if (json is List) { setState(() { _locos = json .whereType() .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, ), ); }, ); } }