import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_state.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/events.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/state.dart'; import 'package:hl_lieferservice/feature/car_selection/presentation/car_selection_card.dart'; import 'package:hl_lieferservice/feature/cars/bloc/cars_bloc.dart'; import 'package:hl_lieferservice/feature/cars/bloc/cars_event.dart'; import 'package:hl_lieferservice/feature/cars/bloc/cars_state.dart'; import 'package:hl_lieferservice/feature/cars/presentation/car_dialog.dart'; import 'package:hl_lieferservice/model/car.dart'; class CarSelectionPage extends StatefulWidget { /// When set, the page is in "change" mode: the car is pre-highlighted /// and a cancel button is shown to revert without choosing a new car. final Car? previousCar; const CarSelectionPage({super.key, this.previousCar}); @override State createState() => _CarSelectionPageState(); } class _CarSelectionPageState extends State { Car? _selectedCar; bool get _isChanging => widget.previousCar != null; @override void initState() { super.initState(); _selectedCar = widget.previousCar; final authState = context.read().state as Authenticated; context.read().add(CarLoad(teamId: authState.user.number)); } void _onAddCar() { final authState = context.read().state as Authenticated; showDialog( context: context, builder: (_) => CarDialog( onAction: (plate) { context.read().add( CarAdd(teamId: authState.user.number, plate: plate), ); }, ), ); } void _onConfirm() { if (_selectedCar == null) return; final authState = context.read().state as Authenticated; context.read().add( CarSelectConfirm( userId: authState.user.number, car: _selectedCar!, ), ); } Widget _buildCarList(List cars) { if (cars.isEmpty) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.local_shipping_outlined, size: 72, color: Colors.grey), const SizedBox(height: 24), Text( "Noch kein Fahrzeug vorhanden.", style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( "Füge zuerst ein Fahrzeug hinzu, bevor du fortfahren kannst.", style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey, ), textAlign: TextAlign.center, ), const SizedBox(height: 24), FilledButton.icon( onPressed: _onAddCar, icon: const Icon(Icons.add), label: const Text("Fahrzeug hinzufügen"), ), ], ), ), ); } final authState = context.read().state as Authenticated; return RefreshIndicator( onRefresh: () async { context.read().add( CarLoad(teamId: authState.user.number, force: true), ); }, child: ListView.builder( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), itemCount: cars.length, itemBuilder: (context, index) { final car = cars[index]; return CarSelectionCard( car: car, isSelected: _selectedCar?.id == car.id, onTap: () => setState(() => _selectedCar = car), ); }, ), ); } @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { if (state is CarSelectFailed) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Fehler beim Speichern der Fahrzeugauswahl."), ), ); } }, child: Scaffold( appBar: AppBar( leading: _isChanging ? IconButton( icon: const Icon(Icons.close), onPressed: () => context.read().add( CarSelectCancel(car: widget.previousCar!), ), ) : null, title: Text( _isChanging ? "Fahrzeug wechseln" : "Fahrzeug auswählen", ), backgroundColor: Theme.of(context).primaryColor, foregroundColor: Theme.of(context).colorScheme.onSecondary, actions: [ // Logout-Zugang auch hier vorhalten, weil der Drawer nur an // der Home-Page hängt und der User bis zur Fahrzeugwahl // sonst keine Möglichkeit hat, sich abzumelden. _LogoutMenu(), ], ), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!_isChanging) ...[ Padding( padding: const EdgeInsets.fromLTRB(20, 24, 20, 4), child: Text( "Fahrzeug auswählen", style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( padding: const EdgeInsets.fromLTRB(20, 4, 20, 16), child: Text( "Wähle das Fahrzeug aus, das du heute verwendest.", style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey, ), ), ), ], Expanded( child: BlocBuilder( builder: (context, state) { if (state is CarsLoading) { return const Center(child: CircularProgressIndicator()); } if (state is CarsLoaded) { return _buildCarList(state.cars); } if (state is CarsLoadingFailed) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.error_outline, size: 72, color: Theme.of(context).colorScheme.error, ), const SizedBox(height: 24), const Text( "Fehler beim Laden der Fahrzeuge.", textAlign: TextAlign.center, ), const SizedBox(height: 16), FilledButton( onPressed: () { final authState = context.read().state as Authenticated; context.read().add( CarLoad( teamId: authState.user.number, ), ); }, child: const Text("Erneut versuchen"), ), ], ), ), ); } return const SizedBox.shrink(); }, ), ), Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), child: SizedBox( width: double.infinity, child: FilledButton( onPressed: _selectedCar != null ? _onConfirm : null, child: const Text("Auswählen"), ), ), ), ], ), ), ), ); } } /// Kleines PopupMenu mit Abmelden-Eintrag — bewusst kein eigener /// IconButton, weil das Confirm-Dialog-Pattern Konsistenz mit dem /// Home-Drawer hat. Auch hier: Refresh-Token wird im Provider gelöscht, /// LoginEnforcer routet automatisch auf LoginPage zurück. class _LogoutMenu extends StatelessWidget { Future _confirm(BuildContext context) async { final authBloc = context.read(); final confirmed = await showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text("Abmelden"), content: const Text( "Möchten Sie sich wirklich abmelden? " "Beim nächsten Start ist eine erneute Anmeldung erforderlich.", ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(false), child: const Text("Abbrechen"), ), FilledButton( style: FilledButton.styleFrom(backgroundColor: Colors.red), onPressed: () => Navigator.of(dialogContext).pop(true), child: const Text("Abmelden"), ), ], ), ); if (confirmed == true) { authBloc.add(const LogoutRequested()); } } @override Widget build(BuildContext context) { final auth = context.watch().state; final authenticated = auth is Authenticated ? auth : null; return PopupMenuButton( tooltip: "Account", icon: const Icon(Icons.account_circle_outlined), onSelected: (value) { if (value == 'logout') _confirm(context); }, itemBuilder: (context) => [ if (authenticated != null) PopupMenuItem( enabled: false, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( authenticated.displayName, style: const TextStyle(fontWeight: FontWeight.w600), ), Text( "Personalnummer ${authenticated.personalnummer}", style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), if (authenticated != null) const PopupMenuDivider(), const PopupMenuItem( value: 'logout', child: ListTile( leading: Icon(Icons.logout, color: Colors.red), title: Text("Abmelden", style: TextStyle(color: Colors.red)), ), ), ], ); } }