import 'dart:convert'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; import '../../../../core/config/env.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../layouts/responsive_layout.dart'; import '../../providers/manager_provider.dart'; class QuotationPopUpTab extends ConsumerStatefulWidget { String? id; final Future Function()? onRefresh; QuotationPopUpTab({super.key, this.id, this.onRefresh}); @override ConsumerState createState() => QuotationTabState(); } class QuotationTabState extends ConsumerState { late ApiService apiService; bool isLoading = false; bool blockKey = false; String? _token; dynamic userId; dynamic managerId; dynamic role; List> getQuotationData = []; List> originalData = []; List> filteredData = []; @override void initState() { super.initState(); apiService = ApiService(); _initializeToken(); Future.microtask(() { managerId = ref.watch(managerIdProvider); userId = ref.watch(userIdProvider); role = ref.watch(userRoleProvider); getQuotationList(); }); } Future _initializeToken() async { _token = await AuthService.getToken(); print("APISERTOKEN - $_token"); } void refresh() { getQuotationList(); } /// ✅ Instead of using `widget.data`, call the API directly Future getQuotationList() async { print('Fetching quotation list from API...'); setState(() => isLoading = true); try { if (widget.id == null) { throw Exception('QuotationTab: ID is null'); } // Example: adjust this endpoint based on your actual API final response = await apiService.findEnqQuotePolicyView(widget.id); // Extract quotation data safely final quotations = (response["data"]?["quotations"] as List?) ?? []; final data = quotations .map((e) => Map.from(e as Map)) .toList(); setState(() { getQuotationData = data; filteredData = List.from(data); blockKey = data.any((item) => item['status'] == 'Accepted'); }); print('Fetched quotation data: ${data.length}'); } catch (e, s) { print('❌ Error fetching quotations: $e\n$s'); } finally { setState(() => isLoading = false); } } Future handleAction(String action, String quotationId) async { final String apiUrldata = '${Env.apiUrl}quotation/acceptOrRejectQuotation'; if (_token == null) { throw Exception('Token not found. Please log in.'); } final Map data = { "id": quotationId, "status": action, "action_by": userId, "action_user": role, }; print("data------- $data"); try { final response = await http.post( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $_token', 'Content-Type': 'application/json', 'app-signature': Env.App_Signature, }, body: jsonEncode(data), ); if (response.statusCode == 200) { print("Response: ${response.body}"); ToastHelper.showSuccessToast(context, 'Status Updated'); // Close popup Navigator.of(context).pop(); // Refresh parent if (widget.onRefresh != null) { await widget.onRefresh!(); } } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } else { final responseBody = jsonDecode(response.body); ToastHelper.showErrorToast(context, 'Status Updation Failed'); print("Failed to submit. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { print("Error submitting: $e"); } } void _showQuotationPopup() { showDialog( context: context, builder: (BuildContext context) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( width: ResponsiveLayout.isMobile(context) ? MediaQuery.of(context).size.width * 0.9 : MediaQuery.of(context).size.width * 0.5, padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Quotation Details', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), ], ), const SizedBox(height: 20), Flexible( child: SingleChildScrollView( child: Column( children: [ ...filteredData .map((item) => _buildQuotationCard(item)) .toList(), ], ), ), ), ], ), ), ); }, ); } Widget _buildQuotationCard(Map item) { final bool isAccepted = item['status'] == 'Accepted'; final bool isRejected = item['status'] == 'Rejected'; final bool isPending = item['status'] == 'Pending'; return Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isAccepted ? Colors.green.withOpacity(0.1) : isRejected ? Colors.red.withOpacity(0.1) : const Color(0xFFF6FEFD), borderRadius: BorderRadius.circular(12), border: Border.all( color: isAccepted ? Colors.green : isRejected ? Colors.red : const Color(0xffD9EBE8), width: 1.5, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header with status Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( item['insurer_name'] ?? '-', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4, ), decoration: BoxDecoration( color: _getStatusColor(item['status']), borderRadius: BorderRadius.circular(12), ), child: Text( item['status'] ?? '-', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ), const SizedBox(height: 12), // Details Row( children: [ Expanded( child: _buildDetailItem( 'IDV', item['insured_declared_value']?.toString() ?? '-', ), ), Expanded( child: _buildDetailItem( 'Premium', item['premium_amount']?.toString() ?? '-', ), ), ], ), const SizedBox(height: 8), _buildDetailItem( 'Plan Type', item['insurance_plan_type']?.toString() ?? '-', ), // Action buttons (only if pending and no other quotation is accepted) if (isPending && !blockKey) ...[ const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => handleAction('Rejected', item['id']), style: TextButton.styleFrom( padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), backgroundColor: Colors.red.shade50, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text( 'Reject', style: TextStyle( color: Colors.red, fontWeight: FontWeight.w600, ), ), ), const SizedBox(width: 12), ElevatedButton( onPressed: () => handleAction('Accepted', item['id']), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), backgroundColor: const Color(0xFF425B5B), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text( 'Accept', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600, ), ), ), ], ), ], // Show message if blocked if (isPending && blockKey) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(8), ), child: const Row( children: [ Icon(Icons.info_outline, size: 16, color: Colors.orange), SizedBox(width: 8), Expanded( child: Text( 'Another quotation has been accepted', style: TextStyle(fontSize: 12, color: Colors.orange), ), ), ], ), ), ], ], ), ); } Widget _buildDetailItem(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$label: ', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF545454), ), ), Expanded( child: Text( value, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black, ), ), ), ], ), ); } Color _getStatusColor(String? status) { switch (status) { case 'Pending': return Colors.orange; case 'Accepted': return Colors.green; case 'Rejected': return Colors.red; default: return Colors.grey; } } @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, padding: const EdgeInsets.all(16), child: isLoading ? const Center(child: CircularProgressIndicator()) : filteredData.isNotEmpty ? Column( children: [ // Button to open popup ElevatedButton.icon( onPressed: _showQuotationPopup, icon: const Icon(Icons.visibility), label: Text('View Quotations (${filteredData.length})'), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF425B5B), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), const SizedBox(height: 20), // Summary cards Expanded( child: GridView.count( crossAxisCount: ResponsiveLayout.isMobile(context) ? 1 : 3, crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: ResponsiveLayout.isMobile(context) ? 3 : 2, children: [ _buildSummaryCard( 'Total Quotations', filteredData.length.toString(), Icons.description, Colors.blue, ), _buildSummaryCard( 'Accepted', filteredData .where((e) => e['status'] == 'Accepted') .length .toString(), Icons.check_circle, Colors.green, ), _buildSummaryCard( 'Pending', filteredData .where((e) => e['status'] == 'Pending') .length .toString(), Icons.pending, Colors.orange, ), ], ), ), ], ) : const Center(child: Text('No Available Data')), ); } Widget _buildSummaryCard( String title, String count, IconData icon, Color color, ) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.3)), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 32, color: color), const SizedBox(height: 8), Text( count, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 4), Text( title, style: TextStyle( fontSize: 14, color: color, fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), ], ), ); } }