import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; import 'package:inventory_system/services/api_service.dart'; import 'package:inventory_system/services/item_movement_service.dart'; import 'package:inventory_system/services/item_service.dart'; import 'package:inventory_system/services/session_manager.dart'; import 'package:inventory_system/services/station_service.dart'; import 'package:inventory_system/services/store_service.dart'; import 'package:inventory_system/services/supplier_service.dart'; import 'package:inventory_system/services/user_service.dart'; import 'package:inventory_system/screens/title_bar.dart'; class ScanResultScreen extends StatefulWidget { final String scannedCode; const ScanResultScreen({super.key, required this.scannedCode}); @override State createState() => _ScanResultScreenState(); } class _ScanResultScreenState extends State { final ItemService _itemService = ItemService(); final ItemMovementService _itemMovementService = ItemMovementService(); final UserService _userService = UserService(); final StationService _stationService = StationService(); final StoreService _storeService = StoreService(); final SupplierService _supplierService = SupplierService(); bool _isLoading = true; String? _errorMessage; Map? _item; Map? _currentUser; // Dropdown lists List _userList = []; List _stationList = []; List _storeList = []; List _supplierList = []; // Form states String? _selectedAction; // user, station, store, supplier, faulty dynamic _selectedUser; dynamic _selectedStation; dynamic _selectedStore; dynamic _selectedSupplier; String? _selectedOther; // Repair, Calibration (for supplier) final TextEditingController _remarkController = TextEditingController(); final TextEditingController _cancelRemarkController = TextEditingController(); final TextEditingController _quantityController = TextEditingController(); DateTime _assignDate = DateTime.now(); // Consignment Note String? _consignmentNoteBase64; String? _consignmentNoteFileName; // Derived states String _itemLatestStatus = ""; bool _itemAssignedToUser = false; int _quantity = 1; int? _maxQuantity; @override void initState() { super.initState(); _loadData(); } Future _loadData() async { setState(() { _isLoading = true; _errorMessage = null; }); try { _currentUser = SessionManager.instance.currentUser; if (_currentUser == null) { throw Exception("User session not found."); } // Fetch lists in parallel final results = await Future.wait([ _userService.fetchUsers(), _stationService.fetchStations(), _storeService.fetchStores(), _supplierService.fetchSuppliers(), // Extract unique ID from URL if scanned code is a URL _itemService.getItem(widget.scannedCode.contains('/') ? widget.scannedCode.split('/').last : widget.scannedCode), ]); _userList = results[0] as List; _stationList = results[1] as List; _storeList = results[2] as List; _supplierList = results[3] as List; _item = results[4] as Map; // Process Item Logic if (_item != null) { _processItemLogic(); } } catch (e) { setState(() => _errorMessage = e.toString()); } finally { setState(() => _isLoading = false); } } void _processItemLogic() { final item = _item!; final user = _currentUser!; // Handle Disposable Quantity if (item['category'] == 'Disposable') { _quantity = 1; _maxQuantity = item['quantity']; _quantityController.text = "1"; } else { _quantity = 1; _maxQuantity = null; _quantityController.text = "1"; } _itemLatestStatus = item['latestStatus'] ?? item['toOther'] ?? ""; // Logic from Vue: final toUserId = item['toUser']; final lastUserId = item['lastUser']; final lastStoreId = item['lastStore']; final currentUserId = user['id']; final currentUserStoreId = user['store']; _itemAssignedToUser = (toUserId == currentUserId || lastUserId == currentUserId) && (lastStoreId == currentUserStoreId); } String _getPICName() { if (_item == null) return 'N/A'; // 1. Try toUserName final toUserName = _item!['toUserName']; if (toUserName != null && toUserName.toString().isNotEmpty) { return toUserName.toString(); } // 2. If station, find station PIC final toStationId = _item!['toStation']; if (toStationId != null) { final station = _stationList.firstWhere( (s) => s['stationId'] == toStationId, orElse: () => null ); if (station != null && station['stationPic'] != null) { return station['stationPic']['fullName'] ?? 'N/A'; } } return 'N/A'; } String _getStationPicName() { if (_selectedStation == null) return ""; final station = _stationList.firstWhere((s) => s['stationId'] == _selectedStation, orElse: () => null); return station != null && station['stationPic'] != null ? station['stationPic']['fullName'] ?? "" : ""; } Future _addItemMovement() async { if (_selectedAction == null) return; if (_item == null || _currentUser == null) return; // Validation if (_item!['category'] == 'Disposable' && _quantity > (_item!['quantity'] ?? 0)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Quantity exceeds available stock (${_item!['quantity']})')), ); return; } setState(() => _isLoading = true); try { int itemQuantityToSend = 1; if (_item!['category'] == 'Disposable') { itemQuantityToSend = _quantity; } final Map baseData = { 'ItemId': _item!['itemID'], 'Action': 'Stock Out', 'Quantity': itemQuantityToSend, }; Map specificData = {}; final now = DateTime.now(); if (_selectedAction == 'user') { specificData = { 'toStore': _currentUser!['store'], 'toUser': _currentUser!['id'], 'toOther': 'On Delivery', 'SendDate': _assignDate.toIso8601String(), 'lastUser': _selectedUser, 'MovementComplete': false, 'Remark': _remarkController.text, 'ConsignmentNote': _consignmentNoteBase64, }; } else if (_selectedAction == 'station') { // Find station PIC ID final station = _stationList.firstWhere((s) => s['stationId'] == _selectedStation, orElse: () => null); final stationPicId = station?['stationPicID']; specificData = { 'toStore': _currentUser!['store'], 'toUser': _currentUser!['id'], 'toOther': 'On Delivery', 'SendDate': _assignDate.toIso8601String(), 'lastStation': _selectedStation, 'lastUser': stationPicId, 'MovementComplete': false, 'Remark': _remarkController.text, 'ConsignmentNote': _consignmentNoteBase64, }; } else if (_selectedAction == 'store') { specificData = { 'toStore': _currentUser!['store'], 'toUser': _currentUser!['id'], 'toOther': 'On Delivery', 'SendDate': _assignDate.toIso8601String(), 'lastStore': _selectedStore, 'MovementComplete': false, 'Remark': _remarkController.text, 'ConsignmentNote': _consignmentNoteBase64, }; } else if (_selectedAction == 'supplier') { specificData = { 'toStore': _currentUser!['store'], 'toUser': _currentUser!['id'], 'toOther': _selectedOther, 'SendDate': _assignDate.toIso8601String(), 'Remark': "${_remarkController.text}. Item sent to $_selectedSupplier for $_selectedOther", 'lastUser': _currentUser!['id'], 'lastStore': _currentUser!['store'], 'MovementComplete': false, 'ConsignmentNote': _consignmentNoteBase64, }; } else if (_selectedAction == 'faulty') { specificData = { 'toStore': _currentUser!['store'], 'toUser': _currentUser!['id'], 'toOther': 'Faulty', 'SendDate': now.add(const Duration(hours: 8)).toIso8601String(), 'ReceiveDate': now.add(const Duration(hours: 8)).toIso8601String(), 'Remark': _remarkController.text, 'MovementComplete': true, 'ConsignmentNote': _consignmentNoteBase64, }; } final formData = {...baseData, ...specificData}; await _itemMovementService.addItemMovement(formData); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item movement added successfully')), ); Navigator.pop(context); // Close Modal Navigator.pop(context); // Close Screen } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } finally { if (mounted) setState(() => _isLoading = false); } } Future _receiveItemMovement() async { if (_item == null || _currentUser == null) return; setState(() => _isLoading = true); try { final now = DateTime.now(); String latestStatus = ""; if (_item!['toOther'] == "Return") { latestStatus = "Faulty"; } else if (["Calibration", "Repair", "On Delivery"].contains(_item!['toOther'])) { latestStatus = "Ready To Deploy"; } else if (_itemLatestStatus == 'On Delivery' && _item!['lastStore'] == _currentUser!['store']) { latestStatus = "Delivered"; } final formData = { 'Id': _item!['movementId'], 'ReceiveDate': now.add(const Duration(hours: 8)).toIso8601String(), 'Remark': _item!['remark'], 'LastUser': _item!['lastuser'] ?? _currentUser!['id'], 'LatestStatus': latestStatus, }; await _itemMovementService.updateItemMovementMaster(formData); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item received successfully')), ); Navigator.pop(context); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } finally { if (mounted) setState(() => _isLoading = false); } } Future _confirmCancelItemMovement() async { if (_item == null || _currentUser == null) return; bool confirm = await showDialog( context: context, builder: (c) => AlertDialog( title: const Text('Confirm Cancellation'), content: const Text('Are you sure you want to cancel this item movement?'), actions: [ TextButton(onPressed: () => Navigator.pop(c, false), child: const Text('No')), TextButton(onPressed: () => Navigator.pop(c, true), child: const Text('Yes')), ], ) ) ?? false; if (!confirm) return; setState(() => _isLoading = true); try { final originalMovement = await _itemMovementService.getItemMovementById(_item!['movementId']); final currentRemark = originalMovement['remark'] ?? ''; await _itemMovementService.updateItemMovementMaster({ 'Id': _item!['movementId'], 'MovementComplete': true, 'LatestStatus': 'Cancelled', 'Remark': '$currentRemark${currentRemark.isNotEmpty ? ' / ' : ''}Movement cancelled: ${_cancelRemarkController.text}', }); final registrationMovementData = { 'ItemId': _item!['itemID'], 'ToStore': _currentUser!['store'], 'ToUser': _currentUser!['id'], 'ToOther': null, 'sendDate': null, 'Action': 'Register', 'Quantity': _item!['movementQuantity'], 'Remark': null, 'ConsignmentNote': null, 'LastUser': _currentUser!['id'], 'LastStore': _currentUser!['store'], 'LatestStatus': 'Ready To Deploy', 'receiveDate': null, 'MovementComplete': true, }; await _itemMovementService.addItemMovement(registrationMovementData); await _itemService.updateItemQuantity({ 'ItemId': _item!['itemID'], 'MovementId': _item!['movementId'] }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Cancelled successfully')), ); Navigator.pop(context); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } finally { if (mounted) setState(() => _isLoading = false); } } // New method to show modal void _showAddMovementModal(List allowedActions) { // Reset form setState(() { _selectedAction = null; _selectedUser = null; _selectedStation = null; _selectedStore = null; _selectedSupplier = null; _selectedOther = null; _remarkController.clear(); _consignmentNoteBase64 = null; _consignmentNoteFileName = null; _quantity = 1; _quantityController.text = "1"; if (_item!['category'] == 'Disposable') { _maxQuantity = _item!['quantity']; } else { _maxQuantity = null; } }); showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) { return StatefulBuilder( builder: (BuildContext context, StateSetter setModalState) { return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, left: 16, right: 16, top: 16, ), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text("Add Item Movement", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ) ], ), const Divider(), const SizedBox(height: 12), DropdownButtonFormField( value: _selectedAction, hint: const Text("Select Action"), items: allowedActions.map((action) { String label = ""; switch(action) { case 'user': label = "Assign to User"; break; case 'station': label = "Assign to Station"; break; case 'store': label = "Assign to Store"; break; case 'supplier': label = "Assign to Supplier"; break; case 'faulty': label = "Faulty"; break; } return DropdownMenuItem(value: action, child: Text(label)); }).toList(), onChanged: (val) { setModalState(() { _selectedAction = val; // Sync with parent state if needed, but local state is enough for UI // We must also update parent state because _addItemMovement uses it setState(() { _selectedAction = val; _selectedUser = null; _selectedStation = null; _selectedStore = null; _selectedSupplier = null; _selectedOther = null; }); }); }, decoration: const InputDecoration(border: OutlineInputBorder()), ), const SizedBox(height: 16), if (_selectedAction == 'user') ...[ DropdownButtonFormField( value: _selectedUser, hint: const Text("Select User"), items: _userList.map((u) => DropdownMenuItem(value: u['id'], child: Text(u['fullName']))).toList(), onChanged: (val) { setModalState(() => _selectedUser = val); setState(() => _selectedUser = val); }, decoration: const InputDecoration(labelText: "To User", border: OutlineInputBorder()), ), ], if (_selectedAction == 'station') ...[ DropdownButtonFormField( value: _selectedStation, hint: const Text("Select Station"), items: _stationList.map((s) => DropdownMenuItem(value: s['stationId'], child: Text(s['stationName']))).toList(), onChanged: (val) { setModalState(() => _selectedStation = val); setState(() => _selectedStation = val); }, decoration: const InputDecoration(labelText: "To Station", border: OutlineInputBorder()), ), const SizedBox(height: 12), TextField( readOnly: true, controller: TextEditingController(text: _getStationPicName()), decoration: const InputDecoration(labelText: "To PIC", border: OutlineInputBorder(), filled: true), ), ], if (_selectedAction == 'store') ...[ DropdownButtonFormField( value: _selectedStore, hint: const Text("Select Store"), items: _storeList.map((s) => DropdownMenuItem(value: s['id'], child: Text(s['storeName']))).toList(), onChanged: (val) { setModalState(() => _selectedStore = val); setState(() => _selectedStore = val); }, decoration: const InputDecoration(labelText: "To Store", border: OutlineInputBorder()), ), ], if (_selectedAction == 'supplier') ...[ DropdownButtonFormField( value: _selectedSupplier, hint: const Text("Select Supplier"), items: _supplierList.map((s) => DropdownMenuItem(value: s['supplierCompName'], child: Text(s['supplierCompName']))).toList(), onChanged: (val) { setModalState(() => _selectedSupplier = val); setState(() => _selectedSupplier = val); }, decoration: const InputDecoration(labelText: "To Supplier", border: OutlineInputBorder()), ), const SizedBox(height: 12), DropdownButtonFormField( value: _selectedOther, hint: const Text("Select Type"), items: const [ DropdownMenuItem(value: "Repair", child: Text("Repair")), DropdownMenuItem(value: "Calibration", child: Text("Calibration")), ], onChanged: (val) { setModalState(() => _selectedOther = val); setState(() => _selectedOther = val); }, decoration: const InputDecoration(labelText: "To Other", border: OutlineInputBorder()), ), ], if (_selectedAction != null) ...[ if (_item!['category'] == 'Disposable') ...[ const SizedBox(height: 12), TextField( controller: _quantityController, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: "Quantity (Max: $_maxQuantity)", border: const OutlineInputBorder()), onChanged: (val) { setState(() => _quantity = int.tryParse(val) ?? 1); }, ), ], const SizedBox(height: 12), InkWell( onTap: () async { final picked = await showDatePicker(context: context, initialDate: _assignDate, firstDate: DateTime(2000), lastDate: DateTime(2100)); if (picked != null) { setState(() => _assignDate = picked); setModalState(() {}); // refresh modal to show date } }, child: InputDecorator( decoration: const InputDecoration(labelText: "Assign Date", border: OutlineInputBorder()), child: Text(DateFormat('yyyy-MM-dd').format(_assignDate)), ), ), const SizedBox(height: 12), // Consignment Note Picker Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8) ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text("Consignment Note", style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), Row( children: [ ElevatedButton.icon( onPressed: () { showModalBottomSheet(context: context, builder: (c) { return SafeArea( child: Wrap(children: [ ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Camera'), onTap: () async { Navigator.pop(c); final XFile? image = await ImagePicker().pickImage(source: ImageSource.camera); if (image != null) { final bytes = await image.readAsBytes(); setState(() { _consignmentNoteBase64 = base64Encode(bytes); _consignmentNoteFileName = image.name; }); setModalState(() {}); } } ), ListTile( leading: const Icon(Icons.attach_file), title: const Text('File / Gallery'), onTap: () async { Navigator.pop(c); FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['jpg', 'pdf', 'png', 'jpeg'] ); if (result != null) { File file = File(result.files.single.path!); final bytes = await file.readAsBytes(); setState(() { _consignmentNoteBase64 = base64Encode(bytes); _consignmentNoteFileName = result.files.single.name; }); setModalState(() {}); } } ) ]), ); }); }, icon: const Icon(Icons.upload_file), label: const Text("Upload") ), const SizedBox(width: 12), Expanded( child: Text(_consignmentNoteFileName ?? "No file selected", overflow: TextOverflow.ellipsis) ), if (_consignmentNoteFileName != null) IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () { setState(() { _consignmentNoteBase64 = null; _consignmentNoteFileName = null; }); setModalState(() {}); }, ) ], ) ], ), ), const SizedBox(height: 12), TextField( controller: _remarkController, decoration: const InputDecoration(labelText: "Remark", border: OutlineInputBorder()), maxLines: 2, ), const SizedBox(height: 24), ElevatedButton( onPressed: () async { await _addItemMovement(); // _addItemMovement handles navigation on success }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), child: const Text("Submit Movement", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), ), const SizedBox(height: 20), ], ], ), ), ); }, ); }, ); } @override Widget build(BuildContext context) { if (_isLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } if (_errorMessage != null) { return Scaffold( appBar: const TitleBar(title: 'Error'), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 64, color: Colors.red), const SizedBox(height: 16), Text(_errorMessage!, textAlign: TextAlign.center), const SizedBox(height: 16), ElevatedButton( onPressed: () => Navigator.pop(context), child: const Text('Go Back'), ) ], ), ), ); } if (_item == null) { return Scaffold( appBar: const TitleBar(title: 'Not Found'), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Item not found."), ElevatedButton(onPressed: () => Navigator.pop(context), child: const Text("Back")) ], ), ), ); } return DefaultTabController( length: 2, child: Scaffold( backgroundColor: const Color(0xFFF5F5F7), appBar: const TitleBar(title: 'Scan Result'), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ _buildTabs(), const SizedBox(height: 12), Expanded( child: TabBarView( children: [ _buildItemMovementTab(), _buildItemInformationTab(), ], ), ), ], ), ), ), ); } Widget _buildTabs() { return Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: Colors.purple.shade50, borderRadius: BorderRadius.circular(12), ), child: SizedBox( height: 44, child: TabBar( isScrollable: false, indicatorSize: TabBarIndicatorSize.tab, indicatorPadding: EdgeInsets.zero, labelPadding: EdgeInsets.zero, dividerColor: Colors.transparent, indicator: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.purple.shade100), ), labelColor: Colors.purple.shade900, unselectedLabelColor: Colors.black54, tabs: const [ Tab(child: Center(child: Text('Item Movement'))), Tab(child: Center(child: Text('Item Information'))), ], ), ), ); } Widget _buildItemMovementTab() { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Center( child: Column( children: [ const SizedBox(height: 4), const Text( 'Item Status:', style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16), ), const SizedBox(height: 4), Text( _itemLatestStatus, style: TextStyle(color: Colors.blue.shade700, fontSize: 14, fontWeight: FontWeight.bold), ), ], ), ), const SizedBox(height: 8), _buildFramedImage(_item!), const SizedBox(height: 16), _buildKV('Item Name', _item!['productName'] ?? 'N/A', icon: Icons.edit_note_rounded), _buildKV('Part Number', _item!['partNumber'] ?? 'N/A', icon: Icons.confirmation_number_rounded), _buildKV('Serial Number', _item!['serialNumber'] ?? 'N/A', icon: Icons.numbers_rounded), _buildKV('PIC', _getPICName(), icon: Icons.person), const SizedBox(height: 12), _buildCurrentInfoSection(_item!), const SizedBox(height: 16), _buildDynamicActionSection(), ], ), ); } Widget _buildItemInformationTab() { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 8), _buildFramedImage(_item!), const SizedBox(height: 16), _buildKV('Unique ID', _item!['uniqueID'] ?? 'N/A', icon: Icons.qr_code), _buildKV('Item Name', _item!['productName'] ?? 'N/A', icon: Icons.edit_note_rounded), _buildKV('Category', _item!['category'] ?? 'N/A', icon: Icons.category), _buildKV('Part Number', _item!['partNumber'] ?? 'N/A', icon: Icons.confirmation_number_rounded), _buildKV('Serial Number', _item!['serialNumber'] ?? 'N/A', icon: Icons.numbers_rounded), _buildKV('PIC', _getPICName(), icon: Icons.person), _buildKV('Quantity', _item!['quantity']?.toString() ?? '0', icon: Icons.production_quantity_limits), ], ), ); } Widget _buildDynamicActionSection() { final toUser = _item!['toUser']; final lastStore = _item!['lastStore']; final currentUserStore = _currentUser!['store']; final currentUserId = _currentUser!['id']; // 1. On Delivery & To User -> Cancel Movement if (_itemLatestStatus == 'On Delivery' && toUser == currentUserId) { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ const Text("Cancel Item Movement", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.red)), const SizedBox(height: 8), TextField( controller: _cancelRemarkController, decoration: const InputDecoration(labelText: 'Reason for Cancellation', border: OutlineInputBorder()), ), const SizedBox(height: 12), ElevatedButton( onPressed: _confirmCancelItemMovement, style: ElevatedButton.styleFrom(backgroundColor: Colors.red, foregroundColor: Colors.white), child: const Text("Confirm Cancel"), ) ], ), ), ); } // 2. On Delivery & Last Store is Current User's Store -> Receive Item if (_itemLatestStatus == 'On Delivery' && lastStore == currentUserStore) { return _buildReceiveButton("Receive Item"); } // 3. Repair/Calibration & Item Assigned to User -> Receive if ((_itemLatestStatus == 'Repair' || _itemLatestStatus == 'Calibration') && _itemAssignedToUser) { return _buildReceiveButton("Receive Repair/Calibration"); } // 4. Return & Item Assigned to User -> Receive if (_itemLatestStatus == 'Return' && _itemAssignedToUser) { return _buildReceiveButton("Receive Item Return"); } // 5. Faulty & Item Assigned to User -> Add Movement (Supplier or Faulty) if (_itemLatestStatus == 'Faulty' && _itemAssignedToUser) { return Center( child: ElevatedButton.icon( onPressed: () => _showAddMovementModal(['supplier', 'faulty']), icon: const Icon(Icons.add), label: const Text("Add Item Movement"), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white ), ), ); } // 6. Ready To Deploy & Item Assigned to User -> Add Movement (Any) if (_itemLatestStatus == 'Ready To Deploy' && _itemAssignedToUser) { return Center( child: ElevatedButton.icon( onPressed: () => _showAddMovementModal(['user', 'station', 'store', 'supplier', 'faulty']), icon: const Icon(Icons.add), label: const Text("Add Item Movement"), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white ), ), ); } return const SizedBox.shrink(); } Widget _buildReceiveButton(String label) { return Center( child: ElevatedButton( onPressed: _receiveItemMovement, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12) ), child: Text(label), ), ); } Widget _buildKV(String label, String value, {IconData? icon}) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (icon != null) ...[ Icon(icon, size: 18, color: Colors.black87), const SizedBox(width: 8), ], Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(color: Colors.grey.shade700, fontSize: 12, fontWeight: FontWeight.w600)), const SizedBox(height: 2), Text(value, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), ], ), ), ], ), ); } Widget _buildFramedImage(Map item) { final imagePath = item['imageProduct']; ImageProvider imageProvider; if (imagePath != null && imagePath.toString().isNotEmpty) { if (imagePath.toString().startsWith('http')) { imageProvider = NetworkImage(imagePath); } else if (imagePath.toString().startsWith('/')) { imageProvider = NetworkImage('${ApiService.baseUrl}$imagePath'); } else { // Fallback or local asset if applicable, but likely generic imageProvider = const AssetImage('assets/images/ram.jpg'); } } else { imageProvider = const AssetImage('assets/images/ram.jpg'); } return Center( child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.grey.shade300), ), child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: Image( image: imageProvider, width: 160, height: 100, fit: BoxFit.cover, errorBuilder: (c, e, s) => Image.asset('assets/images/ram.jpg', width: 160, height: 100), ), ), const SizedBox(height: 6), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade500), borderRadius: BorderRadius.circular(6), ), child: Text(item['uniqueID'] ?? '-', style: const TextStyle(fontSize: 12)), ), ], ), ), ); } Widget _buildCurrentInfoSection(Map item) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.purple.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.info_rounded, color: Colors.purple.shade700), const SizedBox(width: 8), const Text('Current Information', style: TextStyle(fontWeight: FontWeight.w700)), ], ), const SizedBox(height: 8), _buildKV('User', item['toUserName'] ?? 'N/A', icon: Icons.person_rounded), _buildKV('Store', item['toStoreName'] ?? 'N/A', icon: Icons.store_rounded), _buildKV('Station', item['toStationName'] ?? 'N/A', icon: Icons.place_rounded), ], ), ); } }