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"); } } Widget _buildQuotationPopupContent() { return Container( // width: ResponsiveLayout.isMobile(context) // ? MediaQuery.of(context).size.width * 0.9 // : MediaQuery.of(context).size.width * 0.5, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), // border: Border.all(color: const Color(0xFFD9EBE8)), // boxShadow: [ // BoxShadow( // color: Colors.black.withOpacity(0.05), // blurRadius: 8, // offset: const Offset(0, 4), // ), // ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Proposal Details', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.close), ), ], ), const SizedBox(height: 5), Flexible( child: SingleChildScrollView( child: _buildQuotationTable(), // ✅ Only one table ), ), ], ), ); } Widget _buildQuotationTable() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFD9EBE8)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 🔹 Table Header Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), color: const Color(0xFFEFF6F5), child: Row( children: const [ Expanded( flex: 1, child: Text( "IDV", style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( "Plan Type", style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 1, child: Text( "Premium", style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 3, child: Center( child: Text( "Action", style: TextStyle(fontWeight: FontWeight.bold), ), ), ), ], ), ), const Divider(height: 1, color: Color(0xFFD9EBE8)), // 🔹 Table Rows ...filteredData.map((item) { final bool isAccepted = item['status'] == 'Accepted'; final bool isRejected = item['status'] == 'Rejected'; final bool isPending = item['status'] == 'Pending'; return Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( border: Border( bottom: BorderSide(color: Colors.grey.withOpacity(0.2)), ), color: isAccepted ? Colors.green.withOpacity(0.05) : isRejected ? Colors.red.withOpacity(0.05) : Colors.white, ), child: Row( children: [ Expanded( flex: 1, child: Text( item['insured_declared_value']?.toString() ?? '-', style: const TextStyle(fontSize: 14), ), ), Expanded( flex: 2, child: Text( item['insurance_plan_type']?.toString() ?? '-', style: const TextStyle(fontSize: 14), ), ), Expanded( flex: 1, child: Text( item['premium_amount']?.toString() ?? '-', style: const TextStyle(fontSize: 14), ), ), Expanded( flex: 3, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (isPending && !blockKey) ...[ TextButton( onPressed: () => handleAction('Rejected', item['id']), style: TextButton.styleFrom( backgroundColor: Colors.red.shade50, padding: const EdgeInsets.symmetric( horizontal: 12, ), ), child: const Text( 'Reject', style: TextStyle( color: Colors.red, fontWeight: FontWeight.w600, ), ), ), const SizedBox(width: 8), ElevatedButton( onPressed: () => handleAction('Accepted', item['id']), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF425B5B), padding: const EdgeInsets.symmetric( horizontal: 12, ), ), child: const Text( 'Accept', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600, ), ), ), ], if (isAccepted) const Text( 'Accepted', style: TextStyle( color: Colors.green, fontWeight: FontWeight.bold, ), ), if (isRejected) const Text( 'Rejected', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), ], ), ), ], ), ); }), ], ), ); } 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 @override Widget build(BuildContext context) { return SelectionArea( child: 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 ? SingleChildScrollView( child: Column( children: [ _buildQuotationPopupContent(), // ✅ now renders inline const SizedBox(height: 20), ], ), ) : const Center(child: Text('No Available Data')), ),); } }