import 'package:flutter/material.dart'; import '../services/crm_api_service.dart'; import '../theme/app_theme.dart'; import '../utils/snackbar_utils.dart'; class PricingScreen extends StatefulWidget { final List pricing; final bool backendReady; final bool isActive; final VoidCallback onDataChanged; const PricingScreen({ super.key, required this.pricing, required this.backendReady, required this.onDataChanged, this.isActive = false, }); @override State createState() => _PricingScreenState(); } class _PricingScreenState extends State { final CrmApiService _api = CrmApiService(); bool _isSaving = false; // Group pricing by company Map> get groupedPricing { final Map> map = {}; for (var p in widget.pricing) { final company = p['company']?.toString() ?? 'Unknown'; if (!map.containsKey(company)) { map[company] = []; } map[company]!.add(p); } return map; } void _showAddPricingDialog([String? defaultProvider]) { _showPricingFormDialog(defaultProvider: defaultProvider); } void _showEditPricingDialog(Map rateData) { _showPricingFormDialog(rateData: rateData); } void _showPricingFormDialog({Map? rateData, String? defaultProvider}) { final isEditing = rateData != null; final companyCtrl = TextEditingController(text: rateData?['company']?.toString() ?? defaultProvider ?? ''); final slabCtrl = TextEditingController(text: rateData?['weight_slab']?.toString() ?? ''); final zoneCtrl = TextEditingController(text: rateData?['zone']?.toString().isNotEmpty == true ? rateData!['zone'] : rateData?['service_type'] ?? ''); final rateCtrl = TextEditingController(text: rateData?['rate']?.toString() ?? ''); final String id = rateData?['id']?.toString() ?? ''; showDialog( context: context, barrierDismissible: false, builder: (ctx) { return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ // Header Container( padding: const EdgeInsets.fromLTRB(24, 24, 24, 20), decoration: const BoxDecoration( color: AppColors.blueLight, borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), child: Icon(isEditing ? Icons.edit_outlined : Icons.add_circle_outline, color: AppColors.primary, size: 24), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(isEditing ? 'Edit Rate' : 'Add New Rate', style: const TextStyle(fontWeight: FontWeight.w800, color: AppColors.darkBlue, fontSize: 18)), const SizedBox(height: 2), Text(isEditing ? 'Update pricing information' : 'Add to provider pricing slab', style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)), ], ), ), ], ), ), // Form Fields Padding( padding: const EdgeInsets.all(24), child: Column( children: [ _buildModernTextField( controller: companyCtrl, label: 'Provider Name', hint: 'e.g. DTDC, Delhivery', icon: Icons.storefront_outlined, ), const SizedBox(height: 16), _buildModernTextField( controller: slabCtrl, label: 'Weight Slab', hint: 'e.g. 500g, 1kg-2kg', icon: Icons.scale_outlined, ), const SizedBox(height: 16), _buildModernTextField( controller: zoneCtrl, label: 'Zone / Service', hint: 'e.g. LOCAL, SOUTH', icon: Icons.place_outlined, ), const SizedBox(height: 16), _buildModernTextField( controller: rateCtrl, label: 'Rate (₹)', hint: '0.00', icon: Icons.currency_rupee, keyboardType: TextInputType.number, ), ], ), ), // Actions Container( padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), child: Row( children: [ Expanded( child: TextButton( onPressed: () => Navigator.pop(ctx), style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), child: const Text('Cancel', style: TextStyle(color: AppColors.textSecondary, fontWeight: FontWeight.w700)), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () async { if (companyCtrl.text.isEmpty || slabCtrl.text.isEmpty || rateCtrl.text.isEmpty) { SnackbarUtils.showError(ctx, 'Please fill in required fields'); return; } setState(() => _isSaving = true); Navigator.pop(ctx); final payload = { 'company': companyCtrl.text, 'weight_slab': slabCtrl.text, 'zone': zoneCtrl.text, 'service_type': '', 'rate': rateCtrl.text, }; final success = isEditing ? await _api.updatePricing(id, payload) : await _api.createPricing(payload); setState(() => _isSaving = false); if (success) { widget.onDataChanged(); if (mounted) { SnackbarUtils.showSuccess(context, isEditing ? 'Rate updated!' : 'Rate added!'); } } else { if (mounted) { SnackbarUtils.showError(context, isEditing ? 'Update failed.' : 'Failed to add rate.'); } } }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), child: Text(isEditing ? 'Save Changes' : 'Add Rate', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700)), ), ), ], ), ), ], ), ), ); }, ); } Widget _buildModernTextField({ required TextEditingController controller, required String label, required String hint, required IconData icon, TextInputType keyboardType = TextInputType.text, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w800, color: AppColors.darkBlue, letterSpacing: 0.5), ), const SizedBox(height: 8), TextField( controller: controller, keyboardType: keyboardType, style: const TextStyle(fontWeight: FontWeight.w600, color: AppColors.darkBlue, fontSize: 14), decoration: InputDecoration( hintText: hint, hintStyle: TextStyle(color: AppColors.textSecondary.withValues(alpha: 0.5)), prefixIcon: Icon(icon, size: 18, color: AppColors.textSecondary), filled: true, fillColor: const Color(0xFFF8F9FA), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary), ), ), ), ], ); } void _deletePricing(String id) async { final confirm = await showDialog( context: context, builder: (ctx) => Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration(color: AppColors.primary.withValues(alpha: 0.1), shape: BoxShape.circle), child: const Icon(Icons.warning_amber_rounded, color: AppColors.primary, size: 32), ), const SizedBox(height: 16), const Text('Delete Pricing Rate?', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w800, color: AppColors.darkBlue)), const SizedBox(height: 8), const Text('This action cannot be undone. Are you sure you want to delete this rate?', textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: AppColors.textSecondary)), const SizedBox(height: 24), Row( children: [ Expanded( child: TextButton( onPressed: () => Navigator.pop(ctx, false), style: TextButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))), child: const Text('Cancel', style: TextStyle(color: AppColors.textSecondary, fontWeight: FontWeight.w700)), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () => Navigator.pop(ctx, true), style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, elevation: 0, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), child: const Text('Delete', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700)), ), ), ], ), ], ), ), ), ); if (confirm != true) return; setState(() => _isSaving = true); final success = await _api.deletePricing(id); setState(() => _isSaving = false); if (success) { widget.onDataChanged(); if (mounted) { SnackbarUtils.showSuccess(context, 'Pricing rate deleted.'); } } else { if (mounted) { SnackbarUtils.showError(context, 'Failed to delete pricing rate.'); } } } @override Widget build(BuildContext context) { final groups = groupedPricing; final companies = groups.keys.toList()..sort(); return Scaffold( backgroundColor: AppColors.scaffold, body: CustomScrollView( slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 10), child: WideTopBanner( backendReady: widget.backendReady, subtitle: 'CARRIER PRICING', ), ), ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'All Providers', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: AppColors.darkBlue, ), ), ElevatedButton.icon( onPressed: _isSaving ? null : () => _showAddPricingDialog(), icon: const Icon(Icons.add, size: 18, color: Colors.white), label: const Text('Add Provider / Rate', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700)), style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), ), ], ), ), ), if (_isSaving) const SliverToBoxAdapter( child: Padding( padding: EdgeInsets.all(20.0), child: Center(child: CircularProgressIndicator()), ), ), if (companies.isEmpty && !_isSaving) SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(40.0), child: Center( child: Text( 'No pricing data available.\nAdd a provider to get started.', textAlign: TextAlign.center, style: TextStyle(color: AppColors.textSecondary, fontSize: 16), ), ), ), ), SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final company = companies[index]; final rates = groups[company] ?? []; final zones = rates.map((r) => r['zone']?.toString().toUpperCase() ?? 'LOCAL').toSet().toList()..sort(); final slabs = rates.map((r) => r['weight_slab']?.toString() ?? '').toSet().toList()..sort(); final initial = company.isNotEmpty ? company.substring(0, 1).toUpperCase() : 'P'; return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: Colors.grey.shade200, width: 1), ), elevation: 0, color: Colors.white, child: ExpansionTile( shape: const Border(), tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), leading: Container( width: 48, height: 48, decoration: BoxDecoration( color: const Color(0xFFFDECEE), borderRadius: BorderRadius.circular(12), ), alignment: Alignment.center, child: Text( initial, style: const TextStyle(color: AppColors.primary, fontSize: 20, fontWeight: FontWeight.w800), ), ), title: Text( company, style: const TextStyle(fontWeight: FontWeight.w800, color: AppColors.darkBlue, fontSize: 16), ), subtitle: Row( children: [ const Icon(Icons.place_outlined, size: 14, color: AppColors.primary), const SizedBox(width: 4), Text('${rates.length} Registered Rates', style: const TextStyle(color: AppColors.textSecondary, fontSize: 13)), ], ), children: [ Container( width: double.infinity, decoration: BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: Colors.grey.shade200)), borderRadius: const BorderRadius.vertical(bottom: Radius.circular(16)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SingleChildScrollView( scrollDirection: Axis.horizontal, child: DataTable( headingRowColor: WidgetStateProperty.all(Colors.grey.shade50), dataRowMinHeight: 56, dataRowMaxHeight: 56, columnSpacing: 32, horizontalMargin: 24, columns: [ const DataColumn(label: Text('WEIGHT SLAB', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w800, color: AppColors.textSecondary))), ...zones.map((z) => DataColumn(label: Text(z, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w800, color: AppColors.textSecondary)))), ], rows: slabs.map((slab) { return DataRow( cells: [ DataCell(Text(slab, style: const TextStyle(fontWeight: FontWeight.w700, color: AppColors.darkBlue, fontSize: 13))), ...zones.map((zone) { final matchingRate = rates.firstWhere( (r) => (r['weight_slab'] == slab) && ((r['zone']?.toString().toUpperCase() ?? 'LOCAL') == zone), orElse: () => null, ); if (matchingRate == null) { return const DataCell(Center(child: Text('—', style: TextStyle(color: Colors.grey)))); } return DataCell( InkWell( onTap: () { // Show options to edit or delete showModalBottomSheet( context: context, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))), builder: (ctx) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.edit_outlined, color: AppColors.textSecondary), title: const Text('Edit Rate'), onTap: () { Navigator.pop(ctx); _showEditPricingDialog(matchingRate as Map); }, ), ListTile( leading: const Icon(Icons.delete_outline, color: AppColors.primary), title: const Text('Delete Rate', style: TextStyle(color: AppColors.primary)), onTap: () { Navigator.pop(ctx); _deletePricing(matchingRate['id'].toString()); }, ), ], ), ), ); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: AppColors.green.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Text( '₹${matchingRate['rate']}', style: const TextStyle(fontWeight: FontWeight.w800, color: AppColors.greenDark, fontSize: 14), ), ), ), ); }), ], ); }).toList(), ), ), Padding( padding: const EdgeInsets.all(16), child: Align( alignment: Alignment.centerRight, child: TextButton.icon( onPressed: () => _showAddPricingDialog(company), icon: const Icon(Icons.add, size: 16, color: AppColors.primary), label: const Text('Add Rate', style: TextStyle(color: AppColors.primary, fontWeight: FontWeight.w700)), ), ), ), ], ), ), ], ), ); }, childCount: companies.length, ), ), const SliverToBoxAdapter(child: SizedBox(height: 100)), ], ), ); } }