// lib/screens/order_track_screen.dart import 'dart:math'; import 'package:flutter/material.dart'; import '../services/dio_client.dart'; import '../services/order_service.dart'; class OrderTrackScreen extends StatefulWidget { final String orderId; const OrderTrackScreen({super.key, required this.orderId}); @override State createState() => _OrderTrackScreenState(); } class _OrderTrackScreenState extends State with TickerProviderStateMixin { bool loading = true; Map? shipment; Map trackData = {}; late final AnimationController progressController; late final AnimationController shipController; @override void initState() { super.initState(); progressController = AnimationController( vsync: this, duration: const Duration(milliseconds: 900), ); shipController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1600), )..repeat(reverse: true); WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } @override void dispose() { progressController.dispose(); shipController.dispose(); super.dispose(); } // ---------------- LOAD DATA ---------------- Future _loadData() async { if (!mounted) return; setState(() => loading = true); try { final service = OrderService(DioClient.getInstance(context)); final results = await Future.wait([ service.getShipment(widget.orderId).catchError((_) => {"success": false}), service.trackOrder(widget.orderId).catchError((_) => {"success": false}), ]); final shipRes = results[0] as Map; final trackRes = results[1] as Map; /// ------------------- SHIPMENT DATA ------------------- shipment = shipRes["success"] == true ? Map.from(shipRes["shipment"] ?? {}) : null; /// ------------------- TRACKING DATA ------------------- trackData = trackRes["success"] == true ? Map.from(trackRes["track"] ?? {}) : {}; } catch (_) {} final target = _computeProgress(); if (mounted) { try { await progressController.animateTo( target, curve: Curves.easeInOutCubic, ); } catch (_) {} } if (mounted) setState(() => loading = false); } // ---------------- PROGRESS LOGIC ---------------- double _computeProgress() { final status = (trackData["shipment_status"] ?? "") .toString() .toLowerCase(); if (status.contains("delivered")) return 1.0; if (status.contains("dispatched")) return 0.85; if (status.contains("transit")) return 0.65; if (status.contains("loading")) return 0.40; if (status.contains("pending")) return 0.25; if (_hasTimestamp("delivered_at")) return 1.0; if (_hasTimestamp("dispatched_at")) return 0.85; if (_hasTimestamp("in_transit_at")) return 0.65; if (_hasTimestamp("loading_at")) return 0.40; if (_hasTimestamp("pending_at")) return 0.25; return 0.05; } bool _hasTimestamp(String key) { final v = trackData[key]; if (v == null) return false; final s = v.toString().trim().toLowerCase(); return s.isNotEmpty && s != "null"; } String _fmt(dynamic v) { if (v == null) return "-"; try { final d = DateTime.parse(v.toString()).toLocal(); return "${d.day}/${d.month}/${d.year}"; } catch (_) { return v.toString(); } } // ---------------- UI BUILD ---------------- @override Widget build(BuildContext context) { final width = MediaQuery.of(context).size.width; final scale = (width / 430).clamp(0.75, 1.25); return Scaffold( appBar: AppBar( title: const Text("Shipment & Tracking"), elevation: 0.8, ), body: loading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: EdgeInsets.all(16 * scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _headerCard(scale), SizedBox(height: 16 * scale), _shipmentSummary(scale), SizedBox(height: 16 * scale), _shipmentTotals(scale), SizedBox(height: 16 * scale), _shipmentItems(scale), // USING SHIPMENT API ONLY SizedBox(height: 20 * scale), _trackingStatus(scale), SizedBox(height: 16 * scale), _progressBar(scale, width), SizedBox(height: 16 * scale), _detailsCard(scale), ], ), ), ); } // ---------------- HEADER ---------------- Widget _headerCard(double scale) { return Container( padding: EdgeInsets.all(14 * scale), decoration: _boxDecoration(scale), child: Row( children: [ Container( padding: EdgeInsets.all(10 * scale), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12 * scale), ), child: Icon(Icons.local_shipping, color: Colors.blue, size: 28 * scale), ), SizedBox(width: 12 * scale), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Order #${trackData['order_id'] ?? '-'}", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16 * scale), ), SizedBox(height: 6 * scale), Text( trackData["shipment_status"] ?? "-", style: TextStyle(color: Colors.black54, fontSize: 13 * scale), ), ], ) ], ), ); } // ---------------- SHIPMENT SUMMARY ---------------- Widget _shipmentSummary(double scale) { if (shipment == null) { return _simpleCard(scale, "Shipment not created yet"); } return Container( padding: EdgeInsets.all(16 * scale), decoration: _boxDecoration(scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Shipment Summary", style: TextStyle(fontSize: 18 * scale, fontWeight: FontWeight.bold)), SizedBox(height: 12 * scale), // Shipment ID Container( padding: EdgeInsets.all(12 * scale), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12 * scale), ), child: Row( children: [ Icon(Icons.qr_code_2, color: Colors.blue, size: 22 * scale), SizedBox(width: 10 * scale), Text("Shipment ID: ${shipment!['shipment_id']}", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15 * scale)), ], ), ), SizedBox(height: 14 * scale), _twoCol("Status", shipment!['status'], scale), _twoCol("Shipment Date", _fmt(shipment!['shipment_date']), scale), _twoCol("Origin", shipment!['origin'], scale), _twoCol("Destination", shipment!['destination'], scale), ], ), ); } Widget _simpleCard(double scale, String text) { return Container( padding: EdgeInsets.all(14 * scale), decoration: _boxDecoration(scale), child: Text(text, style: TextStyle(fontSize: 15 * scale, color: Colors.grey.shade700)), ); } Widget _twoCol(String title, dynamic value, double scale) { return Padding( padding: EdgeInsets.symmetric(vertical: 6 * scale), child: Row( children: [ Expanded( child: Text(title, style: TextStyle(color: Colors.grey, fontSize: 13 * scale)), ), Expanded( child: Text( value?.toString() ?? "-", textAlign: TextAlign.right, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13 * scale), ), ), ], ), ); } // ---------------- SHIPMENT TOTALS ---------------- Widget _shipmentTotals(double scale) { if (shipment == null) return const SizedBox.shrink(); return Container( padding: EdgeInsets.all(14 * scale), decoration: _boxDecoration(scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Totals", style: TextStyle(fontSize: 18 * scale, fontWeight: FontWeight.bold)), SizedBox(height: 10 * scale), _twoCol("Total CTN", shipment!['total_ctn'], scale), _twoCol("Total Qty", shipment!['total_qty'], scale), _twoCol("Total Amount", shipment!['total_amount'], scale), _twoCol("Total CBM", shipment!['total_cbm'], scale), _twoCol("Total KG", shipment!['total_kg'], scale), ], ), ); } // ---------------- SHIPMENT ITEMS (FROM SHIPMENT API ONLY) ---------------- Widget _shipmentItems(double scale) { if (shipment == null) return const SizedBox.shrink(); final items = (shipment!['items'] as List?) ?? []; return Container( padding: EdgeInsets.all(14 * scale), decoration: _boxDecoration(scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Shipment Items", style: TextStyle(fontSize: 18 * scale, fontWeight: FontWeight.bold)), SizedBox(height: 14 * scale), ...items.map((item) { final orderId = item["order_id"]?.toString() ?? "-"; final qty = item["total_ttl_qty"]?.toString() ?? "-"; final cbm = item["total_ttl_cbm"]?.toString() ?? "-"; final kg = item["total_ttl_kg"]?.toString() ?? "-"; final amount = item["total_amount"]?.toString() ?? "-"; return Card( elevation: 2, margin: EdgeInsets.only(bottom: 12 * scale), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14 * scale), ), child: Padding( padding: EdgeInsets.all(14 * scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( children: [ Container( padding: EdgeInsets.all(8 * scale), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12 * scale), ), child: Icon(Icons.inventory_2, color: Colors.blue, size: 20 * scale), ), SizedBox(width: 12 * scale), Text( "Order ID: $orderId", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15 * scale), ), ], ), SizedBox(height: 12 * scale), if (item["mark_no"] != null) Text("Mark No: ${item["mark_no"]}", style: TextStyle(fontSize: 13 * scale)), SizedBox(height: 6 * scale), Text("Quantity: $qty", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 14 * scale)), Text("CBM: $cbm", style: TextStyle(fontSize: 13 * scale)), Text("KG: $kg", style: TextStyle(fontSize: 13 * scale)), SizedBox(height: 10 * scale), Container( padding: EdgeInsets.symmetric( vertical: 8 * scale, horizontal: 12 * scale), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(10 * scale), border: Border.all(color: Colors.blue, width: 1), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Icon(Icons.currency_rupee, color: Colors.blue), Text( "Amount: ₹$amount", style: TextStyle( fontSize: 15 * scale, fontWeight: FontWeight.bold, color: Colors.blue), ), ], ), ), ], ), ), ); }).toList(), ], ), ); } // ---------------- TRACKING STATUS ---------------- Widget _trackingStatus(double scale) { final p = _computeProgress(); final delivered = p >= 1.0; return Container( padding: EdgeInsets.symmetric(vertical: 10 * scale, horizontal: 14 * scale), decoration: BoxDecoration( gradient: delivered ? const LinearGradient(colors: [Colors.green, Colors.lightGreen]) : const LinearGradient(colors: [Colors.blue, Colors.purple]), borderRadius: BorderRadius.circular(40 * scale), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( delivered ? Icons.verified : Icons.local_shipping, color: Colors.white, size: 16 * scale, ), SizedBox(width: 8 * scale), Text( (trackData["shipment_status"] ?? "-") .toString() .toUpperCase(), style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13 * scale), ), ], ), ); } // ---------------- PROGRESS BAR ---------------- Widget _progressBar(double scale, double width) { final usableWidth = width - (48 * scale); return Container( padding: EdgeInsets.all(12 * scale), decoration: _boxDecoration(scale), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Shipment Progress", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15 * scale)), SizedBox(height: 12 * scale), Stack( alignment: Alignment.centerLeft, children: [ // Background Container( width: usableWidth, height: 10 * scale, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(20 * scale)), ), // Progress Bar AnimatedBuilder( animation: progressController, builder: (_, __) { final w = usableWidth * progressController.value.clamp(0.0, 1.0); return Container( width: w, height: 10 * scale, decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF4F8CFF), Color(0xFF8A4DFF)]), borderRadius: BorderRadius.circular(20 * scale), ), ); }, ), // Moving Truck Icon AnimatedBuilder( animation: shipController, builder: (_, __) { final progress = progressController.value.clamp(0.0, 1.0); final bob = sin(shipController.value * 2 * pi) * (4 * scale); return Positioned( left: (usableWidth - 26 * scale) * progress, top: -6 * scale + bob, child: Container( width: 26 * scale, height: 26 * scale, decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF4F8CFF), Color(0xFF8A4DFF)], ), borderRadius: BorderRadius.circular(8 * scale), ), child: Icon( Icons.local_shipping, size: 16 * scale, color: Colors.white, ), ), ); }, ), ], ), SizedBox(height: 10 * scale), AnimatedBuilder( animation: progressController, builder: (_, __) => Text( "${(progressController.value * 100).toInt()}% Completed", style: TextStyle(color: Colors.black54, fontSize: 13 * scale), ), ) ], ), ); } // ---------------- DETAILS CARD ---------------- Widget _detailsCard(double scale) { return Container( padding: EdgeInsets.all(14 * scale), decoration: _boxDecoration(scale), child: Column( children: [ _detailsRow("Order ID", trackData["order_id"], scale), SizedBox(height: 10 * scale), _detailsRow("Status", trackData["shipment_status"], scale), SizedBox(height: 10 * scale), _detailsRow("Date", _fmt(trackData["shipment_date"]), scale), ], ), ); } Widget _detailsRow(String title, dynamic value, double scale) { return Row( children: [ Expanded( child: Text(title, style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14 * scale))), Expanded( child: Text(value?.toString() ?? "-", textAlign: TextAlign.right, style: TextStyle(color: Colors.black87, fontSize: 14 * scale))), ], ); } // ---------------- BOX DECORATION ---------------- BoxDecoration _boxDecoration(double scale) { return BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12 * scale), boxShadow: [ BoxShadow( color: Colors.black12.withOpacity(0.05), blurRadius: 10 * scale, offset: Offset(0, 4 * scale), ) ], ); } }