import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:intl/intl.dart'; import '../providers/product_categories_provider.dart'; import '../domain/category_rate_history.dart'; class DailyRatesScreen extends ConsumerStatefulWidget { const DailyRatesScreen({super.key}); @override ConsumerState createState() => _DailyRatesScreenState(); } class _DailyRatesScreenState extends ConsumerState { final Map _rateControllers = {}; final Map _isExpanded = {}; final Map> _historyCache = {}; final Map _isLoadingHistory = {}; @override void dispose() { for (var controller in _rateControllers.values) { controller.dispose(); } super.dispose(); } Future _fetchHistory(int categoryId) async { setState(() => _isLoadingHistory[categoryId] = true); final historyData = await ref.read(productCategoriesProvider.notifier).fetchRateHistory(categoryId); setState(() { _historyCache[categoryId] = historyData.map((e) => CategoryRateHistory.fromJson(e)).toList(); _isLoadingHistory[categoryId] = false; }); } void _toggleExpand(int categoryId) { setState(() { _isExpanded[categoryId] = !(_isExpanded[categoryId] ?? false); }); if (_isExpanded[categoryId] == true && _historyCache[categoryId] == null) { _fetchHistory(categoryId); } } Future _saveRate(int categoryId) async { final text = _rateControllers[categoryId]?.text; if (text == null || text.isEmpty) return; final rate = double.tryParse(text); if (rate == null) return; try { showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); await ref.read(productCategoriesProvider.notifier).updateCategoryRate(categoryId, rate); if (mounted) { Navigator.pop(context); // close loading ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Rate updated successfully!'), backgroundColor: Colors.green), ); _fetchHistory(categoryId); // refresh history } } catch (e) { if (mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error updating rate: $e'), backgroundColor: Colors.red), ); } } } Future _syncRates(int categoryId) async { try { showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); final count = await ref.read(productCategoriesProvider.notifier).syncRates(categoryId); if (mounted) { Navigator.pop(context); // close loading ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Successfully synced rates for $count products!'), backgroundColor: Colors.green), ); } } catch (e) { if (mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error syncing rates: $e'), backgroundColor: Colors.red), ); } } } @override Widget build(BuildContext context) { final categoriesState = ref.watch(productCategoriesProvider); return Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( title: const Text('Daily Commodity Rates', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), backgroundColor: Colors.white, foregroundColor: Colors.black, elevation: 0, centerTitle: true, ), body: categoriesState.when( data: (categories) { final commodities = categories.where((c) => c.isCommodity).toList(); if (commodities.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(LucideIcons.boxes, size: 64, color: Colors.grey.shade300), const SizedBox(height: 16), Text('No Commodity Categories', style: TextStyle(fontSize: 18, color: Colors.grey.shade600)), const SizedBox(height: 8), Text('Mark a category as a commodity to manage its daily rates.', style: TextStyle(color: Colors.grey.shade500)), ], ), ); } return RefreshIndicator( onRefresh: () async { ref.invalidate(productCategoriesProvider); await ref.read(productCategoriesProvider.future); }, child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: commodities.length, itemBuilder: (context, index) { final category = commodities[index]; if (!_rateControllers.containsKey(category.id)) { _rateControllers[category.id!] = TextEditingController(text: category.dailyRate?.toString() ?? ''); } final controller = _rateControllers[category.id]!; final isExpanded = _isExpanded[category.id] ?? false; final history = _historyCache[category.id]; final isLoadingHistory = _isLoadingHistory[category.id] ?? false; DateTime? lastSyncDate; if (history != null && history.isNotEmpty) { lastSyncDate = history.first.updatedAt; } return Container( margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow(color: Colors.grey.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 4)), ], border: Border.all(color: Colors.grey.shade200), ), child: Column( children: [ // Main Card Header Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12), ), child: Icon(LucideIcons.trendingUp, color: Colors.blue.shade700, size: 24), ), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( category.name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 4), if (lastSyncDate != null) Text( 'Last updated: ${DateFormat('MMM dd, yyyy HH:mm').format(lastSyncDate)}', style: TextStyle(fontSize: 12, color: Colors.grey.shade500), ) else Text( 'Base Unit: ${category.baseUnit}', style: TextStyle(fontSize: 12, color: Colors.grey.shade500), ), ], ), ], ), IconButton( icon: Icon(isExpanded ? LucideIcons.chevronUp : LucideIcons.history, color: Colors.grey.shade600), onPressed: () => _toggleExpand(category.id!), tooltip: 'View History', ), ], ), const SizedBox(height: 24), Row( children: [ Expanded( flex: 3, child: TextFormField( controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration( labelText: 'Today\'s Rate (per ${category.baseUnit})', prefixText: '₹ ', border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), ), ), const SizedBox(width: 12), Expanded( flex: 2, child: ElevatedButton.icon( onPressed: () => _saveRate(category.id!), icon: const Icon(LucideIcons.save, size: 18), label: const Text('Save'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: () => _syncRates(category.id!), icon: Icon(LucideIcons.refreshCw, size: 18, color: Colors.blue.shade700), label: const Text('Sync Rate to All Products', style: TextStyle(fontWeight: FontWeight.bold)), style: OutlinedButton.styleFrom( foregroundColor: Colors.blue.shade700, side: BorderSide(color: Colors.blue.shade700), padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ), ), ], ), ), // Expandable History Section if (isExpanded) Container( decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: const BorderRadius.vertical(bottom: Radius.circular(16)), border: Border(top: BorderSide(color: Colors.grey.shade200)), ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Rate History', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 12), if (isLoadingHistory) const Center(child: Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator())) else if (history == null || history.isEmpty) const Padding( padding: EdgeInsets.all(16.0), child: Text('No history found.'), ) else ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: history.length > 5 ? 5 : history.length, // Show up to 5 recent separatorBuilder: (_, __) => Divider(color: Colors.grey.shade300), itemBuilder: (context, idx) { final item = history[idx]; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(DateFormat('MMM dd, yyyy').format(item.date), style: const TextStyle(fontWeight: FontWeight.w500)), Text('₹ ${item.rate.toStringAsFixed(2)}', style: const TextStyle(fontWeight: FontWeight.bold)), ], ), ); }, ), ], ), ), ], ), ); }, )); }, loading: () => const Center(child: CircularProgressIndicator()), error: (err, stack) => Center(child: Text('Error: $err')), ), ); } }