import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hl_lieferservice/feature/delivery/bloc/tour_event.dart'; import 'package:hl_lieferservice/model/article.dart'; import 'package:hl_lieferservice/model/delivery.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../../bloc/tour_bloc.dart'; import '../../../bloc/tour_state.dart'; enum _StatusAction { hold, cancel, reactivate } class DeliveryStepInfo extends StatefulWidget { final Delivery delivery; const DeliveryStepInfo({required this.delivery, super.key}); @override State createState() => _DeliveryStepInfo(); } class _DeliveryStepInfo extends State { void _launchMapsUrl(String mapsApp) async { final address = widget.delivery.customer.address.toString(); final encodedAddress = Uri.encodeComponent(address); Uri url; switch (mapsApp) { case 'google': url = Uri.parse( 'https://www.google.com/maps/search/?api=1&query=$encodedAddress', ); break; case 'apple': url = Uri.parse('http://maps.apple.com/?daddr=$encodedAddress'); break; default: return; } await launchUrl(url, mode: LaunchMode.externalApplication); } Widget _statusOverflow() { final state = widget.delivery.state; final List> entries; if (state == DeliveryState.ongoing) { entries = const [ PopupMenuItem( value: _StatusAction.hold, child: Row( children: [ Icon(Icons.change_circle, color: Colors.orangeAccent), SizedBox(width: 12), Text("Zurückstellen"), ], ), ), PopupMenuItem( value: _StatusAction.cancel, child: Row( children: [ Icon(Icons.cancel, color: Colors.red), SizedBox(width: 12), Text("Abbrechen"), ], ), ), ]; } else { entries = const [ PopupMenuItem( value: _StatusAction.reactivate, child: Row( children: [ Icon(Icons.published_with_changes, color: Colors.blueAccent), SizedBox(width: 12), Text("Reaktivieren"), ], ), ), ]; } return PopupMenuButton<_StatusAction>( icon: const Icon(Icons.more_vert), tooltip: "Status ändern", itemBuilder: (context) => entries, onSelected: (action) { switch (action) { case _StatusAction.hold: context.read().add( HoldDeliveryEvent(deliveryId: widget.delivery.id), ); Navigator.of(context).pop(); break; case _StatusAction.cancel: context.read().add( CancelDeliveryEvent(deliveryId: widget.delivery.id), ); Navigator.of(context).pop(); break; case _StatusAction.reactivate: context.read().add( ReactivateDeliveryEvent(deliveryId: widget.delivery.id), ); break; } }, ); } Widget _fastActions() { return SizedBox( width: double.infinity, child: Card( color: Theme.of(context).colorScheme.onSecondary, child: Padding( padding: const EdgeInsets.all(10), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Builder( builder: (context) { final phone = widget.delivery.contactPerson?.phoneNumber; final bool hasPhone = phone != null && phone.isNotEmpty; return Column( mainAxisSize: MainAxisSize.min, children: [ IconButton.filled( onPressed: hasPhone ? () async { await launchUrl( Uri(scheme: "tel", path: phone), ); } : null, icon: const Icon(Icons.phone), ), const Text("Anrufen"), ], ); }, ), ), Expanded( child: Column( mainAxisSize: MainAxisSize.min, children: [ IconButton.filled( onPressed: () => _launchMapsUrl("google"), icon: const Icon(Icons.map_outlined), ), const Text("Google Maps"), ], ), ), _statusOverflow(), ], ), ), ), ); } Widget _customerInformation() { final phone = widget.delivery.contactPerson?.phoneNumber; final String phoneText = (phone != null && phone.isNotEmpty) ? phone : "keine Nummer angegeben"; final email = widget.delivery.customer.email; final String emailText = (email != null && email.isNotEmpty) ? email : "keine E-Mail angegeben"; return SizedBox( width: double.infinity, child: Card( color: Theme.of(context).colorScheme.onSecondary, child: Padding( padding: const EdgeInsets.all(10), child: Column( children: [ Row( children: [ Icon(Icons.person, color: Theme.of(context).primaryColor), Padding( padding: const EdgeInsets.only(left: 10), child: Text( widget.delivery.customer.name, style: TextStyle(fontWeight: FontWeight.bold), ), ), ], ), Padding( padding: const EdgeInsets.only(top: 15), child: Row( children: [ Icon( Icons.other_houses, color: Theme.of(context).primaryColor, ), Padding( padding: const EdgeInsets.only(left: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.delivery.customer.address.street), Text( "${widget.delivery.customer.address.postalCode} ${widget.delivery.customer.address.city}", ), ], ), ), ], ), ), Padding( padding: const EdgeInsets.only(top: 15), child: Row( children: [ Icon(Icons.phone, color: Theme.of(context).primaryColor), Padding( padding: const EdgeInsets.only(left: 10), child: Text(phoneText), ), ], ), ), Padding( padding: const EdgeInsets.only(top: 15), child: Row( children: [ Icon(Icons.mail, color: Theme.of(context).primaryColor), Expanded( child: Padding( padding: const EdgeInsets.only(left: 10), child: Text( emailText, overflow: TextOverflow.ellipsis, ), ), ), ], ), ), ], ), ), ), ); } Widget _articleList() { TourLoaded tour = context.read().state as TourLoaded; List
filteredArticles = widget.delivery.articles .where( (article) => article.articleNumber != tour.tour.discountArticleNumber, ) .toList(); return ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) { Article article = filteredArticles[index]; return DecoratedBox( decoration: BoxDecoration( color: Theme.of(context).colorScheme.onSecondary, ), child: ListTile( title: Text(article.name), subtitle: Text("Artikelnr. ${article.articleNumber}"), leading: Chip(label: Text("${article.amount.toString()}x")), ), ); }, separatorBuilder: (context, index) => const Divider(height: 0), itemCount: filteredArticles.length, ); } Widget _agreementsAndDesiredTime() { String agreements = "keine Vereinbarungen getroffen!"; if (widget.delivery.specialAgreements != null && widget.delivery.specialAgreements != "") { agreements = widget.delivery.specialAgreements!; } final desiredTime = widget.delivery.desiredTime; final bool hasDesiredTime = desiredTime != null && desiredTime.isNotEmpty; final primary = Theme.of(context).primaryColor; return Card( color: Theme.of(context).colorScheme.onSecondary, child: Padding( padding: const EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (hasDesiredTime) ...[ Row( children: [ Padding( padding: const EdgeInsets.all(15), child: Icon(Icons.schedule, color: primary, size: 28), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Wunschtermin", style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), Text( desiredTime, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: primary, ), ), ], ), ), ], ), const Divider(height: 24), ], Row( children: [ Padding( padding: const EdgeInsets.all(15), child: Icon(Icons.warning, color: primary, size: 28), ), Expanded(child: Text(agreements)), ], ), ], ), ), ); } @override Widget build(BuildContext context) { return Container( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(10), child: ListView( children: [ Text( "Schnellaktionen", style: Theme.of(context).textTheme.headlineSmall, ), Padding( padding: const EdgeInsets.only(top: 10), child: _fastActions(), ), Padding( padding: const EdgeInsets.only(top: 20), child: Text( "Sondervereinbarungen", style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( padding: const EdgeInsets.only(top: 10), child: _agreementsAndDesiredTime(), ), Padding( padding: const EdgeInsets.only(top: 20), child: Text( "Kundeninformationen", style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( padding: const EdgeInsets.only(top: 10), child: _customerInformation(), ), Padding( padding: const EdgeInsets.only(top: 20), child: Text( "Zu liefernde Artikel", style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( padding: const EdgeInsets.only(top: 20), child: _articleList(), ), ], ), ), ); } }