nhance_partner/lib/presentation/screens/handler/new
2025-11-13 18:10:21 +05:30

516 lines
16 KiB
Plaintext

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<void> Function()? onRefresh;
QuotationPopUpTab({super.key, this.id, this.onRefresh});
@override
ConsumerState<QuotationPopUpTab> createState() => QuotationTabState();
}
class QuotationTabState extends ConsumerState<QuotationPopUpTab> {
late ApiService apiService;
bool isLoading = false;
bool blockKey = false;
String? _token;
dynamic userId;
dynamic managerId;
dynamic role;
List<Map<String, dynamic>> getQuotationData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> 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<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
void refresh() {
getQuotationList();
}
/// ✅ Instead of using `widget.data`, call the API directly
Future<void> 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<String, dynamic>.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<void> 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<String, dynamic> 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<String, dynamic> 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,
),
],
),
);
}
}