1322 lines
42 KiB
Dart
1322 lines
42 KiB
Dart
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:nhance_partner/presentation/layouts/responsive_layout.dart';
|
|
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
|
import 'package:toastification/toastification.dart';
|
|
import 'package:universal_html/html.dart' as html;
|
|
|
|
import '../../../../core/config/env.dart';
|
|
import '../../../../core/routing/routes.dart';
|
|
import '../../../../core/services/api_service.dart';
|
|
import '../../../../data/services/auth_service.dart';
|
|
import '../../../../data/utils/Pagination.dart';
|
|
import '../../../../data/utils/toastNotification.dart';
|
|
import '../../../../data/utils/validators.dart';
|
|
import '../../../layouts/main_layout.dart';
|
|
import '../../../providers/manager_provider.dart';
|
|
import '../../../themes/indicators/customizd_file_upload.dart';
|
|
import '../../../themes/indicators/export_btn.dart';
|
|
import '../../../themes/indicators/input_field_decoration.dart';
|
|
import '../../../themes/indicators/search_field_theme.dart';
|
|
import '../../../themes/indicators/text_field_theme.dart';
|
|
import '../../../themes/indicators/upload_doc_theme.dart'
|
|
hide ThemedUploadField;
|
|
|
|
class EnquiryTab extends ConsumerStatefulWidget {
|
|
String? id;
|
|
final Map<String, dynamic>? data;
|
|
EnquiryTab({super.key, this.data, this.id});
|
|
@override
|
|
ConsumerState<EnquiryTab> createState() => EnquiryTabState();
|
|
}
|
|
|
|
class EnquiryTabState extends ConsumerState<EnquiryTab> {
|
|
List<Map<String, dynamic>> dataVal = [];
|
|
|
|
final TextEditingController _searchController = TextEditingController();
|
|
|
|
late ApiService apiService;
|
|
final _formKey = GlobalKey<FormState>();
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
|
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
|
dropDownKeyInsurer = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyAgent =
|
|
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
|
|
List<String> tabHeader = [
|
|
'name',
|
|
'email',
|
|
'mobile',
|
|
'code',
|
|
'address',
|
|
'regNo',
|
|
'remarks',
|
|
];
|
|
late String isActive = "1";
|
|
|
|
PlatformFile? docUploadedRCFile;
|
|
PlatformFile? docUploadedIDProof;
|
|
PlatformFile? docUploadedPrevPolicy;
|
|
|
|
String? selectedRCFile;
|
|
String? selectedIdProof;
|
|
String? selectedPrevPolicy;
|
|
|
|
String? selectedRCFileName;
|
|
|
|
String? rcFileUrlFromApi;
|
|
String? idProofFileUrlFromApi;
|
|
String? prevPolicyFileUrlFromApi;
|
|
|
|
String? selectedId;
|
|
bool isLoading = false;
|
|
|
|
bool isSaving = false;
|
|
|
|
String? selectedVehicleType;
|
|
// int? selectedVehicleTypeId;
|
|
String? selectedInsurer;
|
|
String? selectedAgent;
|
|
|
|
String? _token;
|
|
dynamic userId;
|
|
dynamic managerId;
|
|
dynamic role;
|
|
|
|
Map<String, TextEditingController> controllers = {};
|
|
List<Map<String, dynamic>> getVehicleTypeData = [];
|
|
List<Map<String, dynamic>> filteredVechicleData = [];
|
|
|
|
List<Map<String, dynamic>> getAgentListData = [];
|
|
List<Map<String, dynamic>> filteredAgentData = [];
|
|
|
|
List<Map<String, dynamic>> getInsurersData = [];
|
|
List<Map<String, dynamic>> filteredInsurersData = [];
|
|
|
|
Map<String, dynamic> dataDetails() {
|
|
final data = {
|
|
"agent_id": ((role == 'handler') || (role == 'manager'))
|
|
? selectedAgent
|
|
: userId,
|
|
"name": controllers["name"]?.text,
|
|
"mobile": controllers["mobile"]?.text,
|
|
"email": controllers["email"]?.text,
|
|
"reg_no": controllers["regNo"]?.text,
|
|
"vehicle_type_id": selectedVehicleType,
|
|
"is_data_created_by_handler": role == 'handler' ? '1' : '0',
|
|
"is_data_created_by_manager": role == 'manager' ? '1' : '0',
|
|
// "insurer_id": selectedInsurer,
|
|
// "rc_file_name": "rc_doc.pdf",
|
|
// "id_proof_file_name": "id_proof.pdf",
|
|
// "previous_policy_file_name": "previous_policy.pdf",
|
|
"remarks": controllers["remarks"]?.text,
|
|
"manager_id": managerId,
|
|
"created_by": userId,
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
apiService = ApiService();
|
|
|
|
for (String field in tabHeader) {
|
|
controllers[field] = TextEditingController();
|
|
}
|
|
|
|
if (widget.data != null) {
|
|
print("EnQ has Data");
|
|
} else {
|
|
print("EnQ has Data 1");
|
|
}
|
|
_initializeToken();
|
|
Future.microtask(() {
|
|
managerId = ref.watch(managerIdProvider);
|
|
userId = ref.watch(userIdProvider);
|
|
role = ref.watch(userRoleProvider);
|
|
|
|
if (managerId != null) {
|
|
print('managerId - $managerId');
|
|
getAgentList(managerId);
|
|
}
|
|
});
|
|
|
|
getVehicleType();
|
|
getInsurers();
|
|
updateData();
|
|
}
|
|
|
|
// enquiry/enquiryList?enquiry_id=1
|
|
Future<void> _initializeToken() async {
|
|
_token = await AuthService.getToken();
|
|
print("APISERTOKEN - $_token");
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
// Dispose all TextEditingControllers
|
|
for (var controller in controllers.values) {
|
|
controller.dispose();
|
|
}
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> getVehicleType() async {
|
|
print('getClaimList called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchMasterDropDown('vehicleType','dropdown');
|
|
|
|
if (response['status'] == 200) {
|
|
print('getVehicleTypeData - ${response['data']}');
|
|
setState(() {
|
|
getVehicleTypeData = List<Map<String, dynamic>>.from(
|
|
response['data'],
|
|
);
|
|
print('API Data - $getVehicleTypeData');
|
|
|
|
filteredVechicleData = List.from(getVehicleTypeData);
|
|
// print('originalData - $filteredVechicleData');
|
|
});
|
|
} else {
|
|
getVehicleTypeData = [];
|
|
filteredVechicleData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getInsurers() async {
|
|
print('Insurers called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchMasterDropDown('Insurers');
|
|
|
|
if (response['status'] == 200) {
|
|
print('getInsurers - ${response['data']}');
|
|
setState(() {
|
|
getInsurersData = List<Map<String, dynamic>>.from(response['data']);
|
|
print('API Data - $getInsurersData');
|
|
|
|
filteredInsurersData = List.from(getInsurersData);
|
|
// print('originalData - $filteredInsurersData');
|
|
});
|
|
} else {
|
|
getInsurersData = [];
|
|
filteredInsurersData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getAgentList(id) async {
|
|
print('getAgentListData called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchAgentNameDropDown(id);
|
|
print('getAgentListData called response');
|
|
print('get Agent- ${response['data']}');
|
|
if (response['status'] == 'success') {
|
|
print('get Agent- ${response['data']}');
|
|
setState(() {
|
|
getAgentListData = List<Map<String, dynamic>>.from(response['data']);
|
|
print('API Data - $getAgentListData');
|
|
|
|
filteredAgentData = List.from(getAgentListData);
|
|
print('originalAgentData - $filteredAgentData');
|
|
});
|
|
} else {
|
|
getAgentListData = [];
|
|
filteredAgentData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
void updateData() async {
|
|
if (widget.data != null && widget.data != 'tab' && widget.id != null) {
|
|
// dynamic response = await apiService.findEnqQuotePolicyView(widget.id!);
|
|
// final enq = response['data'];
|
|
final data = widget.data;
|
|
print("updateDataEnq - $data");
|
|
|
|
if (data == null) return;
|
|
|
|
setState(() {
|
|
selectedId = widget.id;
|
|
controllers['name']?.text = data['name'] ?? '';
|
|
controllers['email']?.text = data['email'] ?? '';
|
|
controllers['mobile']?.text = data['mobile'] ?? '';
|
|
controllers['regNo']?.text = data['reg_no'] ?? '';
|
|
controllers['remarks']?.text = data['remarks'] ?? '';
|
|
isActive = data["is_active"];
|
|
selectedVehicleType = data['vehicle_type_id'] ?? '';
|
|
selectedInsurer = data['insurer_id'] ?? '';
|
|
selectedAgent = data['agent_id'] ?? '';
|
|
|
|
String? apiDocPath = data["certificate_file_name"];
|
|
|
|
// RC FILE
|
|
String? rcPath = data["rc_file_name"];
|
|
if (rcPath != null && rcPath.isNotEmpty) {
|
|
selectedRCFile = rcPath.split('/').last; // UI filename
|
|
selectedRCFileName = rcPath.split('/').last;
|
|
rcFileUrlFromApi = rcPath; // API download URL
|
|
docUploadedRCFile = null;
|
|
// docUploadedRCFile =
|
|
// rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet
|
|
} else {
|
|
selectedRCFile = null; // no UI file shown
|
|
rcFileUrlFromApi = null;
|
|
docUploadedRCFile = null;
|
|
}
|
|
|
|
// ID PROOF FILE
|
|
String? idProofPath = data["id_proof_file_name"];
|
|
if (idProofPath != null && idProofPath.isNotEmpty) {
|
|
selectedIdProof = idProofPath.split('/').last;
|
|
idProofFileUrlFromApi = idProofPath; // ✅ FIXED → correct variable
|
|
docUploadedIDProof = null;
|
|
// docUploadedIDProof = idProofFileUrlFromApi as PlatformFile?;
|
|
} else {
|
|
selectedIdProof = null;
|
|
idProofFileUrlFromApi = null;
|
|
docUploadedIDProof = null;
|
|
}
|
|
|
|
// PREVIOUS POLICY FILE
|
|
String? prevPolicyPath = data["previous_policy_file_name"];
|
|
if (prevPolicyPath != null && prevPolicyPath.isNotEmpty) {
|
|
selectedPrevPolicy = prevPolicyPath.split('/').last;
|
|
prevPolicyFileUrlFromApi =
|
|
prevPolicyPath; // ✅ FIXED → correct variable
|
|
docUploadedPrevPolicy = null;
|
|
// docUploadedPrevPolicy = prevPolicyFileUrlFromApi as PlatformFile?;
|
|
} else {
|
|
selectedPrevPolicy = null;
|
|
prevPolicyFileUrlFromApi = null;
|
|
docUploadedPrevPolicy = null;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Future<void> handleSave() async {
|
|
// if (!_formKey.currentState!.validate()) return;
|
|
// setState(() {
|
|
// if (_formKey.currentState!.validate()) {
|
|
// isSaving = true;
|
|
// dataDetails();
|
|
// final dataSet = dataDetails();
|
|
// print("dataSetAgent - $dataSet");
|
|
// print("managerId - $managerId ,userId - $userId ");
|
|
//
|
|
// if ((docUploadedRCFile == null ||
|
|
// (docUploadedRCFile?.bytes == null &&
|
|
// docUploadedRCFile?.path == null)) &&
|
|
// (rcFileUrlFromApi == null || rcFileUrlFromApi!.isEmpty)) {
|
|
// ToastHelper.showErrorToast(context, "Please upload RC Document");
|
|
// return;
|
|
// }
|
|
//
|
|
// if ((docUploadedIDProof == null ||
|
|
// (docUploadedIDProof?.bytes == null &&
|
|
// docUploadedIDProof?.path == null)) &&
|
|
// (idProofFileUrlFromApi == null || idProofFileUrlFromApi!.isEmpty)) {
|
|
// ToastHelper.showErrorToast(context, "Please upload ID Proof");
|
|
// return;
|
|
// }
|
|
// createUserData(dataSet);
|
|
// } else {
|
|
// // isDisable = false;
|
|
//
|
|
// setState(() => isSaving = false);
|
|
// }
|
|
// });
|
|
// }
|
|
|
|
Future<void> handleSave() async {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
|
|
setState(() {
|
|
isSaving = true; // start saving
|
|
});
|
|
|
|
final dataSet = dataDetails();
|
|
|
|
try {
|
|
await createUserData(dataSet); // API call
|
|
// ToastHelper.showInfoToast(context, "Enquiry saved successfully");
|
|
} catch (e) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (_) => AlertDialog(
|
|
title: const Text('Error'),
|
|
content: Text(e.toString()),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
} finally {
|
|
setState(() => isSaving = false); // re-enable button after save
|
|
}
|
|
}
|
|
|
|
Future<void> attachFiles(http.MultipartRequest request) async {
|
|
Future<void> addFileOrKeepName(
|
|
PlatformFile? file,
|
|
String? apiFileName,
|
|
String fieldName,
|
|
) async {
|
|
if (file != null) {
|
|
// User uploaded a new file → send as multipart
|
|
if (file.bytes != null) {
|
|
request.files.add(
|
|
http.MultipartFile.fromBytes(
|
|
fieldName,
|
|
file.bytes!,
|
|
filename: file.name,
|
|
),
|
|
);
|
|
} else if (file.path != null) {
|
|
request.files.add(
|
|
await http.MultipartFile.fromPath(
|
|
fieldName,
|
|
file.path!,
|
|
filename: file.name,
|
|
),
|
|
);
|
|
}
|
|
print("📎 Attached new file → $fieldName");
|
|
} else if (apiFileName != null && apiFileName.isNotEmpty) {
|
|
// No new upload → tell backend to keep old file
|
|
request.fields[fieldName] = apiFileName;
|
|
print("🔗 Kept old file → $fieldName = $apiFileName");
|
|
} else {
|
|
// Nothing at all
|
|
request.fields[fieldName] = "";
|
|
}
|
|
}
|
|
|
|
await addFileOrKeepName(
|
|
docUploadedRCFile,
|
|
rcFileUrlFromApi,
|
|
'rc_file_name',
|
|
);
|
|
await addFileOrKeepName(
|
|
docUploadedIDProof,
|
|
idProofFileUrlFromApi,
|
|
'id_proof_file_name',
|
|
);
|
|
await addFileOrKeepName(
|
|
docUploadedPrevPolicy,
|
|
prevPolicyFileUrlFromApi,
|
|
'previous_policy_file_name',
|
|
);
|
|
}
|
|
|
|
Future<void> createUserData(Map<String, dynamic> userData) async {
|
|
final bool isUpdating =
|
|
widget.data != null && widget.id != 'tab' && widget.id != 'null';
|
|
final id = widget.id;
|
|
print('UPDId- $id');
|
|
|
|
// final bool isUpdating = false;
|
|
final uri = Uri.parse(
|
|
isUpdating
|
|
? '${Env.apiUrl}enquiry/updateEnquiry'
|
|
: '${Env.apiUrl}enquiry/createEnquiry',
|
|
);
|
|
if (_token == null) {
|
|
throw Exception('Token not found. Please log in.');
|
|
}
|
|
|
|
// Use MultipartRequest (POST only)
|
|
final request = http.MultipartRequest('POST', uri);
|
|
request.headers['Authorization'] = 'Bearer $_token';
|
|
request.headers['app-signature'] = Env.App_Signature;
|
|
|
|
// If updating, spoof the method Laravel-style
|
|
if (isUpdating) {
|
|
request.fields['id'] = id!;
|
|
request.fields['updated_by'] = userId!.toString();
|
|
} else {
|
|
request.fields['created_by'] = userId!.toString();
|
|
}
|
|
|
|
print("USerDAta - $userData");
|
|
|
|
// userData.forEach((key, value) {
|
|
// request.fields[key] = value.toString();
|
|
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
|
// });
|
|
await attachFiles(request);
|
|
userData.forEach((key, value) {
|
|
if (key != 'rc_file_name' && key != 'id_proof_file_name') {
|
|
request.fields[key] = value.toString();
|
|
print("✅ Encoded $key: ${request.fields[key]}");
|
|
} else {
|
|
print('Something Missing..');
|
|
}
|
|
});
|
|
|
|
// attach files
|
|
|
|
// request.fields['agent_id'] = selectedId.toString();
|
|
|
|
print(" Sending request with fields: ${request.fields}");
|
|
|
|
try {
|
|
final streamedResponse = await request.send();
|
|
final response = await http.Response.fromStream(streamedResponse);
|
|
print("Response status: ${response.statusCode}");
|
|
print("Response body: ${response.body}");
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
// dispose();
|
|
print("✅ Agent submitted successfully!");
|
|
ToastHelper.showSuccessToast(context, 'Saved Enquiry');
|
|
print("Response: ${response.body}");
|
|
|
|
if (role == 'handler') {
|
|
print('Im handler');
|
|
print('ROle - $role');
|
|
if (isUpdating) {
|
|
context.go(AppRoutes.tabEnquiry);
|
|
// context.go(AppRoutes.enquiryHandlerLst);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryHandlerLst);
|
|
}
|
|
} else if (role == 'manager') {
|
|
print('Im manager');
|
|
print('ROle - $role');
|
|
if (isUpdating) {
|
|
context.go(AppRoutes.tabEnquiry);
|
|
// context.go(AppRoutes.enquiryHandlerLst);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryForStaff);
|
|
}
|
|
} else {
|
|
print('Im agent');
|
|
if (isUpdating) {
|
|
context.go(AppRoutes.tabEnquiry);
|
|
} else {
|
|
ref.read(enquiryIdProvider.notifier).state = null;
|
|
context.go(AppRoutes.enquiryLst);
|
|
}
|
|
}
|
|
setState(() => isSaving = false);
|
|
} else if (response.statusCode == 403) {
|
|
await apiService.clearLocalStorageAndRedirect();
|
|
} else {
|
|
print("❌ Submission failed. Status: ${response.statusCode}");
|
|
print("Body: ${response.body}");
|
|
|
|
setState(() => isSaving = false);
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text("Enquiry Creation Failed"),
|
|
content: Text(
|
|
"There was a problem in creating enquiry. Please try again.",
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
child: Text("OK"),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
} catch (e) {
|
|
print("🔥 Error submitting user: $e");
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
bool isMobile = ResponsiveLayout.isMobile(context);
|
|
return SelectionArea(
|
|
child: Container(
|
|
// color: Colors.green,
|
|
decoration: BoxDecoration(
|
|
color: Color(0xffEDF6F5),
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: MediaQuery.of(context).size.width,
|
|
// height: MediaQuery.of(context).size.height * 0.8,
|
|
padding: isMobile
|
|
? EdgeInsets.only(left: 14.0, right: 14.0)
|
|
: EdgeInsets.all(26.0),
|
|
child: Column(
|
|
children: [
|
|
isMobile
|
|
? Row(children: [Expanded(child: buildFormFields(context))])
|
|
: Expanded(
|
|
child: SingleChildScrollView(child: buildFormFields(context)),
|
|
),
|
|
const SizedBox(height: 20),
|
|
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
GestureDetector(
|
|
// onTap: () {
|
|
// handleSave();
|
|
// },
|
|
onTap: isSaving ? null : handleSave,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 45.0, vertical: 8),
|
|
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
// color: Color(0xFF425B5B),
|
|
color: isSaving ? Colors.grey : Color(0xFF425B5B),
|
|
),
|
|
child: Text(
|
|
isSaving ? 'Saving...' : 'Save',
|
|
style: TextStyle(color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 30),
|
|
],
|
|
),
|
|
) );
|
|
}
|
|
|
|
Widget buildFormFields(BuildContext context) {
|
|
bool isMobile = ResponsiveLayout.isMobile(context);
|
|
return Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
?((role == 'handler') || (role == 'manager'))
|
|
? _buildResponsiveRow(
|
|
context,
|
|
buildAgentName(context),
|
|
SizedBox.shrink(),
|
|
)
|
|
: null,
|
|
?((role == 'handler') || (role == 'manager'))
|
|
? isMobile
|
|
? SizedBox(height: 5)
|
|
: SizedBox(height: 15)
|
|
: null,
|
|
_buildResponsiveRow(context, buildName(context), buildId(context)),
|
|
|
|
isMobile ? SizedBox(height: 5) : SizedBox(height: 15),
|
|
|
|
_buildResponsiveRow(
|
|
context,
|
|
buildVehicleType(context),
|
|
buildEmail(context),
|
|
),
|
|
isMobile ? SizedBox(height: 5) : SizedBox(height: 15),
|
|
|
|
_buildResponsiveRow(
|
|
context,
|
|
buildPhNumber(context),
|
|
// buildInsurer(context),
|
|
buildUploadRCDocument(context),
|
|
),
|
|
isMobile ? SizedBox(height: 5) : SizedBox(height: 15),
|
|
|
|
_buildResponsiveRow(
|
|
context,
|
|
// buildUploadRCDocument(context),
|
|
buildUploadIDDocument(context),
|
|
buildUploadPolicyDocument(context),
|
|
),
|
|
|
|
isMobile ? SizedBox(height: 5) : SizedBox(height: 15),
|
|
_buildResponsiveRow(
|
|
context,
|
|
// buildUploadPolicyDocument(context),
|
|
buildRemarks(context),
|
|
SizedBox.shrink(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildResponsiveRow(
|
|
BuildContext context,
|
|
Widget first,
|
|
Widget second,
|
|
) {
|
|
if (ResponsiveLayout.isMobile(context)) {
|
|
// Stack vertically
|
|
return Column(children: [first, const SizedBox(height: 2), second]);
|
|
} else {
|
|
// Place side by side
|
|
return Row(
|
|
children: [
|
|
Expanded(child: first),
|
|
const SizedBox(width: 25),
|
|
Expanded(child: second),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget buildResponsiveField({required String label, required Widget field}) {
|
|
final isMobile = ResponsiveLayout.isMobile(context);
|
|
|
|
if (isMobile) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: _textStyle),
|
|
const SizedBox(height: 8),
|
|
field,
|
|
const SizedBox(height: 16),
|
|
],
|
|
);
|
|
} else {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(child: Text(label, style: _textStyle)),
|
|
const SizedBox(width: 10),
|
|
field,
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget buildName(BuildContext context) {
|
|
return buildResponsiveField(
|
|
label: "Insured Name *",
|
|
field: ThemedFormField(
|
|
controller: controllers['name']!,
|
|
|
|
validator: (value) => Validators.requiredField(value, "name"),
|
|
txtwidth: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildEmail(BuildContext context) {
|
|
return buildResponsiveField(
|
|
label: "Email",
|
|
field: ThemedFormField(
|
|
controller: controllers['email']!,
|
|
validator: (value) => Validators.nonReqemail(value, "email"),
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')),
|
|
],
|
|
txtwidth: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildPhNumber(BuildContext context) {
|
|
return buildResponsiveField(
|
|
label: "Phone Number",
|
|
field: ThemedFormField(
|
|
controller: controllers['mobile']!,
|
|
validator: (value) => Validators.nonReqphone(value, "phone"),
|
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[ 0-9]'))],
|
|
txtwidth: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildId(BuildContext context) {
|
|
return buildResponsiveField(
|
|
label: "Vehicle Number *",
|
|
field: ThemedFormField(
|
|
controller: controllers['regNo']!,
|
|
inputFormatters: [
|
|
UpperCaseTextFormatter(), // 👈 custom formatter for uppercase
|
|
FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')),
|
|
],
|
|
validator: (value) => Validators.requiredVechileNum(value, "regNo"),
|
|
txtwidth: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildVehicleType(BuildContext context) {
|
|
// Find the matching map from your list
|
|
|
|
Map<String, dynamic>? selectedVehicle = filteredVechicleData.firstWhere(
|
|
(item) => item['id'].toString() == selectedVehicleType,
|
|
orElse: () => {},
|
|
);
|
|
|
|
return buildResponsiveField(
|
|
label: "Vehicle Type *",
|
|
field: Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
// height: 40,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKey,
|
|
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredVechicleData;
|
|
},
|
|
validator: (val) {
|
|
if (val == null) {
|
|
return "Required"; // ✅ error message
|
|
}
|
|
return null;
|
|
},
|
|
|
|
itemAsString: (val) => val['vehicle_type'].toString(),
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
// decoratorProps: DropDownDecoratorProps(
|
|
// decoration:
|
|
// AppInputDecorations.dropdownDecoration(
|
|
// label: "Select Vehicle Type",
|
|
// ).copyWith(
|
|
// filled: true,
|
|
// fillColor: Colors.white, // 👈 makes the dropdown input white
|
|
// ),
|
|
// ),
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Vehicle Type",
|
|
).copyWith(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
contentPadding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
|
|
),
|
|
),
|
|
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 200),
|
|
menuProps: MenuProps(
|
|
backgroundColor:
|
|
Colors.white, // 👈 sets dropdown background to white
|
|
),
|
|
showSearchBox: true,
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
hintText: "Search Vehicle Type...",
|
|
hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
), // 👈 Normal border
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
width: 1.5,
|
|
), // 👈 Focused border
|
|
),
|
|
),
|
|
),
|
|
|
|
// constraints: BoxConstraints(),
|
|
itemBuilder: (context, item, isDisabled, isSelected) {
|
|
return Container(
|
|
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
|
child: Text(
|
|
item['vehicle_type'].toString(),
|
|
style: GoogleFonts.inter(fontSize: 13, color: Colors.black),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
|
|
onChanged: (val) {
|
|
if (val != null) {
|
|
print("Selected vehicle_type : ${val['vehicle_type']}");
|
|
print("Id: ${val['id']}");
|
|
selectedVehicleType = val['id'];
|
|
// controllers['agentId']?.text = val['agent_code'];
|
|
// agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildInsurer(BuildContext context) {
|
|
Map<String, dynamic>? selectedInsurerd = filteredInsurersData.firstWhere(
|
|
(item) => item['id'].toString() == selectedInsurer,
|
|
orElse: () => {},
|
|
);
|
|
return buildResponsiveField(
|
|
label: "Insurer *",
|
|
field: Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
// height: 40,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKeyInsurer,
|
|
selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredInsurersData;
|
|
},
|
|
|
|
itemAsString: (val) => val['name'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
validator: (val) {
|
|
if (val == null) {
|
|
return "Required"; // ✅ error message
|
|
}
|
|
return null;
|
|
},
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Insurer",
|
|
).copyWith(
|
|
filled: true,
|
|
fillColor: Colors.white, // 👈 makes the dropdown input white
|
|
),
|
|
),
|
|
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
menuProps: MenuProps(
|
|
backgroundColor:
|
|
Colors.white, // 👈 sets dropdown background to white
|
|
),
|
|
showSearchBox: true,
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
hintText: "Search Insurer...",
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
), // 👈 Normal border
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
width: 1.5,
|
|
), // 👈 Focused border
|
|
),
|
|
),
|
|
),
|
|
// constraints: BoxConstraints(),
|
|
),
|
|
|
|
onChanged: (val) {
|
|
if (val != null) {
|
|
print("Selected Insurer : ${val['name']}");
|
|
print("Id: ${val['id']}");
|
|
selectedInsurer = val['id'];
|
|
// controllers['agentId']?.text = val['agent_code'];
|
|
// agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildAgentName(BuildContext context) {
|
|
Map<String, dynamic>? selectedAgntName = filteredAgentData.firstWhere(
|
|
(item) => item['id'].toString() == selectedAgent,
|
|
orElse: () => {},
|
|
);
|
|
return buildResponsiveField(
|
|
label: "Select Partner *",
|
|
field: Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
// height: 40,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKeyAgent,
|
|
selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredAgentData;
|
|
},
|
|
|
|
itemAsString: (val) => val['name'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
validator: (val) {
|
|
if (val == null) {
|
|
return "Required"; // ✅ error message
|
|
}
|
|
return null;
|
|
},
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Partner",
|
|
).copyWith(
|
|
filled: true,
|
|
fillColor: Colors.white, // 👈 makes the dropdown input white
|
|
),
|
|
),
|
|
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
menuProps: MenuProps(
|
|
backgroundColor:
|
|
Colors.white, // 👈 sets dropdown background to white
|
|
),
|
|
showSearchBox: true,
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
hintText: "Search Partner...",
|
|
hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
), // 👈 Normal border
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
width: 1.5,
|
|
), // 👈 Focused border
|
|
),
|
|
),
|
|
),
|
|
|
|
// constraints: BoxConstraints(),
|
|
itemBuilder: (context, item, isDisabled, isSelected) {
|
|
return Container(
|
|
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
item['name'].toString(),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 13,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
Text(
|
|
item['agent_code'].toString(),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 13,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
|
|
onChanged: (val) {
|
|
if (val != null) {
|
|
print("Selected Partner : ${val['name']}");
|
|
print("Id: ${val['id']}");
|
|
selectedAgent = val['id'];
|
|
// controllers['agentId']?.text = val['agent_code'];
|
|
// agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Widget buildUploadRCDocument(BuildContext context) {
|
|
// return buildResponsiveUploadField(
|
|
// label: "Upload RC Document",
|
|
// hintText: selectedRCFile,
|
|
// onFileSelected: (fileName, file) {
|
|
// print("File picked: $fileName");
|
|
// setState(() {
|
|
// docUploadedRCFile = file;
|
|
// });
|
|
// },
|
|
// );
|
|
// }
|
|
|
|
Widget buildResponsiveUploadField({
|
|
required String label,
|
|
required String? hintText,
|
|
required void Function(String fileName, dynamic file) onFileSelected,
|
|
bool showDownload = false,
|
|
VoidCallback? onRemove,
|
|
VoidCallback? onDownload,
|
|
}) {
|
|
final isMobile = ResponsiveLayout.isMobile(context);
|
|
|
|
final uploadWidget = ThemedUploadField(
|
|
hintText: hintText ?? "Upload Document",
|
|
txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26,
|
|
onFileSelected: onFileSelected,
|
|
);
|
|
|
|
final downloadRow = showDownload
|
|
? Padding(
|
|
padding: const EdgeInsets.only(top: 5),
|
|
child: Row(
|
|
children: [
|
|
// IconButton(
|
|
// onPressed: onRemove,
|
|
// icon: const Icon(
|
|
// Icons.remove_circle_outline_rounded,
|
|
// color: Colors.redAccent,
|
|
// ),
|
|
// tooltip: 'Remove Upload',
|
|
// ),
|
|
GestureDetector(
|
|
onTap: onDownload,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(5),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(5),
|
|
color: const Color(0xFF425B5B),
|
|
),
|
|
child: Row(
|
|
children: const [
|
|
Text(
|
|
"Download",
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w200,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(width: 5),
|
|
Icon(Icons.download, size: 13, color: Colors.white),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: const SizedBox.shrink();
|
|
|
|
if (isMobile) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(label, style: _textStyle),
|
|
const SizedBox(height: 8),
|
|
uploadWidget,
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [downloadRow],
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
);
|
|
} else {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(child: Text(label, style: _textStyle)),
|
|
const SizedBox(width: 10),
|
|
uploadWidget,
|
|
],
|
|
),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [SizedBox.shrink(), downloadRow],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget buildUploadRCDocument(BuildContext context) {
|
|
return buildResponsiveUploadField(
|
|
label: "Upload RC Document ",
|
|
hintText: selectedRCFile,
|
|
onFileSelected: (fileName, file) {
|
|
setState(() {
|
|
docUploadedRCFile = file;
|
|
selectedRCFileName = fileName;
|
|
});
|
|
},
|
|
showDownload: docUploadedRCFile != null || rcFileUrlFromApi != null,
|
|
onRemove: () {
|
|
setState(() {
|
|
docUploadedRCFile = null;
|
|
rcFileUrlFromApi = null;
|
|
selectedRCFileName = null;
|
|
});
|
|
},
|
|
onDownload: () => apiService.downloadFile(
|
|
// apiUrl: 'agent/downloadAgentCertificateFile?agent_id=$selectedId',
|
|
apiUrl:
|
|
'enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc',
|
|
|
|
apiId: selectedId.toString(),
|
|
|
|
localFile: docUploadedRCFile,
|
|
fileName: 'RC',
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildUploadIDDocument(BuildContext context) {
|
|
return buildResponsiveUploadField(
|
|
label: "Upload ID Proof",
|
|
hintText: selectedIdProof,
|
|
onFileSelected: (fileName, file) {
|
|
setState(() {
|
|
docUploadedIDProof = file;
|
|
// selectedFileNames = fileName;
|
|
});
|
|
},
|
|
showDownload: docUploadedIDProof != null || idProofFileUrlFromApi != null,
|
|
onRemove: () {
|
|
setState(() {
|
|
docUploadedIDProof = null;
|
|
idProofFileUrlFromApi = null;
|
|
// selectedFileNames = null;
|
|
});
|
|
},
|
|
onDownload: () => apiService.downloadFile(
|
|
// apiUrl: 'agent/downloadAgentCertificateFile?agent_id=$selectedId',
|
|
apiUrl:
|
|
'enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof',
|
|
|
|
apiId: selectedId.toString(),
|
|
localFile: docUploadedIDProof,
|
|
fileName: 'Id_Proof',
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildUploadPolicyDocument(BuildContext context) {
|
|
return buildResponsiveUploadField(
|
|
label: "Upload Previous Policy",
|
|
hintText: selectedPrevPolicy,
|
|
onFileSelected: (fileName, file) {
|
|
setState(() {
|
|
docUploadedPrevPolicy = file;
|
|
// selectedFileNames = fileName;
|
|
});
|
|
},
|
|
showDownload:
|
|
docUploadedPrevPolicy != null || prevPolicyFileUrlFromApi != null,
|
|
onRemove: () {
|
|
setState(() {
|
|
docUploadedPrevPolicy = null;
|
|
prevPolicyFileUrlFromApi = null;
|
|
// selectedFileNames = null;
|
|
});
|
|
},
|
|
onDownload: () => apiService.downloadFile(
|
|
// apiUrl: 'agent/downloadAgentCertificateFile?agent_id=$selectedId',
|
|
apiUrl:
|
|
'enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy',
|
|
|
|
apiId: selectedId.toString(),
|
|
localFile: docUploadedPrevPolicy,
|
|
fileName: 'Previous_Policy',
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildRemarks(BuildContext context) {
|
|
return buildResponsiveField(
|
|
label: "Remarks",
|
|
field: ThemedFormField(
|
|
maxLength: 500,
|
|
controller: controllers['remarks']!,
|
|
keyboardType: TextInputType.multiline,
|
|
txtwidth: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.26,
|
|
),
|
|
);
|
|
}
|
|
|
|
static const _textStyle = TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
);
|
|
}
|