import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; import '../models/delivery.dart'; import '../providers/providers.dart'; import '../api/client.dart'; import '../api/openapi_config.dart'; import '../models/delivery_commands.dart'; import '../utils/breakpoints.dart'; import '../utils/responsive.dart'; class DeliveriesPage extends ConsumerStatefulWidget { final int routeFragmentId; final String routeName; const DeliveriesPage({ super.key, required this.routeFragmentId, required this.routeName, }); @override ConsumerState createState() => _DeliveriesPageState(); } class _DeliveriesPageState extends ConsumerState { late PageController _pageController; int _currentSegment = 0; @override void initState() { super.initState(); _pageController = PageController(); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final deliveriesData = ref.watch(deliveriesProvider(widget.routeFragmentId)); final token = ref.watch(authTokenProvider).valueOrNull; return Scaffold( appBar: AppBar( title: Text(widget.routeName), elevation: 0, ), body: deliveriesData.when( data: (deliveries) { final todoDeliveries = deliveries .where((d) => !d.delivered && !d.isSkipped) .toList(); final completedDeliveries = deliveries .where((d) => d.delivered) .toList(); return Column( children: [ Padding( padding: const EdgeInsets.all(16.0), child: SegmentedButton( segments: const [ ButtonSegment( value: 0, label: Text('To Do'), ), ButtonSegment( value: 1, label: Text('Delivered'), ), ], selected: {_currentSegment}, onSelectionChanged: (Set newSelection) { setState(() { _currentSegment = newSelection.first; _pageController.animateToPage( _currentSegment, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); }); }, ), ), Expanded( child: PageView( controller: _pageController, onPageChanged: (index) { setState(() { _currentSegment = index; }); }, children: [ DeliveryListView( deliveries: todoDeliveries, onAction: (delivery, action) => _handleDeliveryAction(context, delivery, action, token), ), DeliveryListView( deliveries: completedDeliveries, onAction: (delivery, action) => _handleDeliveryAction(context, delivery, action, token), ), ], ), ), ], ); }, loading: () => const Center( child: CircularProgressIndicator(), ), error: (error, stackTrace) => Center( child: Text('Error: $error'), ), ), ); } Future _handleDeliveryAction( BuildContext context, Delivery delivery, String action, String? token, ) async { if (token == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Authentication required')), ); return; } final authClient = CqrsApiClient( config: ApiClientConfig( baseUrl: ApiClientConfig.production.baseUrl, defaultHeaders: {'Authorization': 'Bearer $token'}, ), ); switch (action) { case 'complete': final result = await authClient.executeCommand( endpoint: 'completeDelivery', command: CompleteDeliveryCommand( deliveryId: delivery.id, deliveredAt: DateTime.now().toIso8601String(), ), ); result.when( success: (_) { // ignore: unused_result ref.refresh(deliveriesProvider(widget.routeFragmentId)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Delivery marked as completed')), ); }, onError: (error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: ${error.message}')), ); }, ); break; case 'uncomplete': final result = await authClient.executeCommand( endpoint: 'markDeliveryAsUncompleted', command: MarkDeliveryAsUncompletedCommand(deliveryId: delivery.id), ); result.when( success: (_) { // ignore: unused_result ref.refresh(deliveriesProvider(widget.routeFragmentId)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Delivery marked as uncompleted')), ); }, onError: (error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: ${error.message}')), ); }, ); break; case 'call': final contact = delivery.orders.isNotEmpty && delivery.orders.first.contact != null ? delivery.orders.first.contact : null; if (contact?.phoneNumber != null) { final Uri phoneUri = Uri(scheme: 'tel', path: contact!.phoneNumber); if (await canLaunchUrl(phoneUri)) { await launchUrl(phoneUri); } } break; case 'map': if (delivery.deliveryAddress != null) { final address = delivery.deliveryAddress!; final Uri mapUri = Uri( scheme: 'https', host: 'maps.google.com', queryParameters: { 'q': '${address.latitude},${address.longitude}', }, ); if (await canLaunchUrl(mapUri)) { await launchUrl(mapUri); } } break; } } } class DeliveryListView extends StatelessWidget { final List deliveries; final Function(Delivery, String) onAction; const DeliveryListView({ super.key, required this.deliveries, required this.onAction, }); @override Widget build(BuildContext context) { if (deliveries.isEmpty) { return const Center( child: Text('No deliveries'), ); } return RefreshIndicator( onRefresh: () async { // Trigger refresh via provider }, child: ListView.builder( itemCount: deliveries.length, itemBuilder: (context, index) { final delivery = deliveries[index]; return DeliveryCard( delivery: delivery, onAction: onAction, ); }, ), ); } } class DeliveryCard extends StatelessWidget { final Delivery delivery; final Function(Delivery, String) onAction; const DeliveryCard({ super.key, required this.delivery, required this.onAction, }); @override Widget build(BuildContext context) { final contact = delivery.orders.isNotEmpty && delivery.orders.first.contact != null ? delivery.orders.first.contact : null; final order = delivery.orders.isNotEmpty ? delivery.orders.first : null; return Card( margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( delivery.name, style: Theme.of(context).textTheme.titleMedium, maxLines: 2, overflow: TextOverflow.ellipsis, ), if (contact != null) Text( contact.fullName, style: Theme.of(context).textTheme.bodySmall, ), ], ), ), if (delivery.delivered) Chip( label: const Text('Delivered'), backgroundColor: Theme.of(context).colorScheme.primaryContainer, ) else if (order?.isNewCustomer ?? false) Chip( label: const Text('New Customer'), backgroundColor: Colors.orange.shade100, ), ], ), const SizedBox(height: 12), if (delivery.deliveryAddress != null) Text( delivery.deliveryAddress!.formattedAddress, style: Theme.of(context).textTheme.bodySmall, maxLines: 2, overflow: TextOverflow.ellipsis, ), if (order != null) ...[ const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (order.totalItems != null) Text( '${order.totalItems} items', style: Theme.of(context).textTheme.bodySmall, ), Text( '${order.totalAmount} MAD', style: Theme.of(context).textTheme.titleSmall?.copyWith( color: Theme.of(context).colorScheme.primary, ), ), ], ), ], const SizedBox(height: 12), Wrap( spacing: 8, children: [ if (contact?.phoneNumber != null) OutlinedButton.icon( onPressed: () => onAction(delivery, 'call'), icon: const Icon(Icons.phone), label: const Text('Call'), ), if (delivery.deliveryAddress != null) OutlinedButton.icon( onPressed: () => onAction(delivery, 'map'), icon: const Icon(Icons.map), label: const Text('Map'), ), OutlinedButton.icon( onPressed: () => _showDeliveryActions(context), icon: const Icon(Icons.more_vert), label: const Text('More'), ), ], ), ], ), ), ); } void _showDeliveryActions(BuildContext context) { showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ if (!delivery.delivered) ListTile( leading: const Icon(Icons.check_circle), title: const Text('Mark as Completed'), onTap: () { Navigator.pop(context); onAction(delivery, 'complete'); }, ) else ListTile( leading: const Icon(Icons.undo), title: const Text('Mark as Uncompleted'), onTap: () { Navigator.pop(context); onAction(delivery, 'uncomplete'); }, ), ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Upload Photo'), onTap: () { Navigator.pop(context); // TODO: Implement photo upload }, ), ListTile( leading: const Icon(Icons.description), title: const Text('View Details'), onTap: () { Navigator.pop(context); // TODO: Navigate to delivery details }, ), ], ), ), ); } }