UI_HEADER_ICON UI_ERR_MSG_CONTENT

This commit is contained in:
venbaittech 2025-09-17 12:31:30 +05:30
parent 3c7ce422a9
commit 5e6c091faa
8 changed files with 338 additions and 201 deletions

View File

@ -368,7 +368,6 @@ class ApiService {
await _initializeToken();
}
final userId = '1';
final url;
if (role == 'manager') {
@ -389,6 +388,20 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> findSingleEnquiryData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?enquiry_id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
//------------------------------------ Quotation ------------------------------------------------
Future<Map<String, dynamic>> fetchQuotationList(int managerId) async {

View File

@ -1,10 +1,15 @@
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_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:nhance_partner/presentation/layouts/responsive_layout.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';
@ -47,18 +52,24 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
'remarks',
];
late String isActive = "1";
html.File? docUploadedRCFile;
html.File? docUploadedIDProof;
html.File? docUploadedPrevPolicy;
// PlatformFile? passportFile;
PlatformFile? docUploadedRCFile;
PlatformFile? docUploadedIDProof;
PlatformFile? docUploadedPrevPolicy;
String? selectedRCFile;
String? selectedIdProof;
String? selectedPrevPolicy;
String? passportFileUrlFromApi;
String? rcFileUrlFromApi;
String? idProofFileUrlFromApi;
String? prevPolicyFileUrlFromApi;
String? selectedId;
bool isLoading = false;
String? selectedVehicleType;
int? selectedVehicleTypeId;
String? selectedInsurer;
Map<String, TextEditingController> controllers = {};
@ -109,7 +120,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
for (String field in tabHeader) {
controllers[field] = TextEditingController();
}
// updateData();
_initializeToken();
Future.microtask(() {
managerId = ref.watch(managerIdProvider);
@ -118,8 +129,10 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
getVehicleType();
getInsurers();
updateData();
}
// api/enquiry/enquiryList?enquiry_id=1
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
@ -189,119 +202,205 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
}
}
Future<void> handleSave() async {
print("Handl1");
final dataSet = dataDetails();
print("dataSetAgent - $dataSet");
print("Handl13f");
if (!_formKey.currentState!.validate()) return;
// if (passportFile == null) {
// print("❌ No file selected!");
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text("Please select a file before submitting")),
// );
// return;
// }
void updateData() async {
// if (widget.id != null && widget.id != 'create') {
dynamic response = await apiService.findSingleEnquiryData(1);
// dynamic response = await apiService.findSingleAgentData(widget.id!);
final data = response['data'];
print("updateData - ${response['data']}");
if (data == null) return;
setState(() {
// uploadIncentiveData(dataSet);
});
// 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'] ?? '2';
selectedVehicleTypeId = 2;
selectedInsurer = data['insurer_id'] ?? '3';
print("Handl2");
String? apiDocPath = data["rc_file_name"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
print('apiDocPath - $apiDocPath');
selectedRCFile = apiDocPath.split('/').last;
print('selectedRCFile - $selectedRCFile');
rcFileUrlFromApi = apiDocPath;
print('rcFileUrlFromApi - $rcFileUrlFromApi');
docUploadedRCFile = null;
} else {
selectedRCFile = null;
docUploadedRCFile = null;
rcFileUrlFromApi = null;
}
String? apiDocPathRC = data["id_proof_file_name"];
if (apiDocPathRC != null && apiDocPathRC.isNotEmpty) {
print('apiDocPathRC - $apiDocPathRC');
selectedIdProof = apiDocPathRC.split('/').last;
print('selectedIdProof - $selectedIdProof');
prevPolicyFileUrlFromApi = apiDocPathRC;
print('prevPolicyFileUrlFromApi - $prevPolicyFileUrlFromApi');
docUploadedIDProof = null;
} else {
selectedIdProof = null;
docUploadedIDProof = null;
prevPolicyFileUrlFromApi = null;
}
String? apiDocPathPrevPolicy = data["previous_policy_file_name"];
if (apiDocPathPrevPolicy != null && apiDocPathPrevPolicy.isNotEmpty) {
print('apiDocPathPrevPolicy - $apiDocPathPrevPolicy');
selectedPrevPolicy = apiDocPathPrevPolicy.split('/').last;
print('selectedPrevPolicy - $selectedPrevPolicy');
idProofFileUrlFromApi = apiDocPathPrevPolicy;
print('idProofFileUrlFromApi - $idProofFileUrlFromApi');
docUploadedPrevPolicy = null;
} else {
selectedPrevPolicy = null;
docUploadedPrevPolicy = null;
idProofFileUrlFromApi = null;
}
});
}
// Future<void> uploadIncentiveData(Map<String, dynamic> dataSet) async {
// final uri = Uri.parse('${Env.apiUrl}agent/uploadAgentIncentiveFile');
// // 'https://venbait.in/nhance/partner/dev/api/agent/uploadAgentIncentiveFile',
// 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'] =
// 'nhance-partner-2025-signature-35468846JRhH551HK';
//
// print("USerDAta - $dataSet");
//
// dataSet.forEach((key, value) {
// request.fields[key] = value.toString();
// print("✅ Encoded travel_details2: ${request.fields[key]}");
// });
//
// // Attach file if selected
// if (passportFile != null) {
// try {
// final reader = html.FileReader();
// reader.readAsArrayBuffer(passportFile!);
// await reader.onLoad.first;
//
// final data = reader.result as Uint8List;
//
// final multipartFile = http.MultipartFile.fromBytes(
// 'incentive_file_name',
// data,
// filename: passportFile!.name,
// );
//
// request.files.add(multipartFile);
// print("📎 File attached: ${passportFile!.name}");
// } catch (e) {
// print("❌ Failed to read file: $e");
// }
// } else {
// print("⚠️ No passport file selected.");
// }
//
// 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!");
//
// print("📨 Response: ${response.body}");
//
// final data = dataDetails(); // this is a Map<String, dynamic>
// final agentId = data["agent_id"];
//
// getAgentIncenctiveFileList(agentId);
// // context.go(AppRoutes.agentLst);
// } else {
// print("❌ Submission failed. Status: ${response.statusCode}");
// print("📨 Body: ${response.body}");
//
// // Sort by created_on (latest first)
//
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// title: Text("Agent Creation Failed"),
// content: Text(
// "There was a problem in creating user. Please try again.",
// ),
// actions: [
// TextButton(
// child: Text("OK"),
// onPressed: () {
// Navigator.of(context).pop();
// },
// ),
// ],
// );
// },
// );
// }
// } catch (e) {
// print("🔥 Error submitting user: $e");
// }
// }
Future<void> handleSave() async {
if (!_formKey.currentState!.validate()) return;
setState(() {
if (_formKey.currentState!.validate()) {
dataDetails();
final dataSet = dataDetails();
print("dataSetAgent - $dataSet");
print("managerId - $managerId ,userId - $userId ");
createUserData(dataSet);
} else {
// isDisable = false;
}
});
}
Future<void> attachFiles(http.MultipartRequest request) async {
Future<void> addFile(PlatformFile? file, String fieldName) async {
if (file == null) return;
try {
if (file.bytes != null) {
final multipartFile = http.MultipartFile.fromBytes(
fieldName,
file.bytes!,
filename: file.name,
);
request.files.add(multipartFile);
} else if (file.path != null) {
final multipartFile = await http.MultipartFile.fromPath(
fieldName,
file.path!,
filename: file.name,
);
request.files.add(multipartFile);
}
print("📎 File attached: ${file.name}$fieldName");
} catch (e) {
print("❌ Failed to attach $fieldName: $e");
}
}
await addFile(docUploadedRCFile, 'rc_file_name');
await addFile(docUploadedIDProof, 'id_proof_file_name');
await addFile(docUploadedPrevPolicy, 'previous_policy_file_name');
}
Future<void> createUserData(Map<String, dynamic> userData) async {
// final bool isUpdating = widget.id != null && widget.id != 'create';
// final id = widget.id;
final bool isUpdating = false;
final uri = Uri.parse(
// isUpdating
// ? '${Env.apiUrl}agent/updateAgent'
// : '${Env.apiUrl}agent/createAgent',
'${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'] =
'nhance-partner-2025-signature-35468846JRhH551HK';
// 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]}");
// });
userData.forEach((key, value) {
if (key != 'certificate_file_name') {
request.fields[key] = value.toString();
print("✅ Encoded $key: ${request.fields[key]}");
}
});
// attach files
await attachFiles(request);
// 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!");
print("Response: ${response.body}");
context.go(AppRoutes.tabEnquiry);
} else {
print("❌ Submission failed. Status: ${response.statusCode}");
print("Body: ${response.body}");
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Agent Creation Failed"),
content: Text(
"There was a problem in creating user. 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) {
@ -425,7 +524,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
field,
],
@ -440,12 +539,6 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
}) {
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 uploadWidget = ThemedUploadField(
hintText: hintText ?? "Upload Document",
txtwidth: MediaQuery.of(context).size.width * 0.26,
@ -466,7 +559,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
uploadWidget,
],
@ -476,7 +569,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildName(BuildContext context) {
return buildResponsiveField(
label: "Name *",
label: "Full Name ",
field: ThemedFormField(
controller: controllers['name']!,
validator: (value) => Validators.requiredField(value, "name"),
@ -489,7 +582,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildEmail(BuildContext context) {
return buildResponsiveField(
label: "Email *",
label: "Email",
field: ThemedFormField(
controller: controllers['email']!,
validator: (value) => Validators.email(value, "email"),
@ -502,7 +595,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildPhNumber(BuildContext context) {
return buildResponsiveField(
label: "Phone Number *",
label: "Phone Number",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
@ -515,7 +608,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildId(BuildContext context) {
return buildResponsiveField(
label: "Registration Number *",
label: "Registration Number",
field: ThemedFormField(
controller: controllers['regNo']!,
validator: (value) => Validators.requiredField(value, "regNo"),
@ -527,6 +620,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
}
Widget buildVehicleType(BuildContext context) {
// Find the matching map from your list
Map<String, dynamic>? selectedVehicle = filteredVechicleData.firstWhere(
(item) => item['id'] == selectedVehicleTypeId,
orElse: () => {},
);
return buildResponsiveField(
label: "Vehicle Type",
field: Container(
@ -537,12 +636,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
// height: 40,
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKey,
selectedItem: null,
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
items: (filter, infiniteScrollProps) {
return filteredVechicleData;
},
itemAsString: (val) => val['vehicle_type'].toString(), // what to show
itemAsString: (val) => val['vehicle_type'].toString(),
compareFn: (item, selectedItem) =>
item['id'] == selectedItem['id'], // compare by id
decoratorProps: DropDownDecoratorProps(
@ -713,7 +812,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
return buildResponsiveField(
label: "Remarks",
field: ThemedFormField(
maxLength: 3,
maxLength: 500,
controller: controllers['remarks']!,
txtwidth: ResponsiveLayout.isMobile(context)
? null

View File

@ -302,7 +302,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(height: 8),
field,
const SizedBox(height: 16),
@ -312,7 +312,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
field,
],

View File

@ -409,7 +409,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
field,
],
@ -444,7 +444,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
uploadWidget,
],
@ -465,38 +465,6 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
);
}
Widget _buildDataTable5(BuildContext context) {
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
return ListView.builder(
itemCount: ResponsiveLayout.isMobile(context)
? sortedData
.length // only cards for mobile
: sortedData.length + 1, // +1 for header in desktop
itemBuilder: (context, index) {
if (!ResponsiveLayout.isMobile(context) && index == 0) {
return _buildHeader();
}
final startIndex = (currentPage - 1) * itemsPerPage;
final item =
sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)];
final sno = startIndex + index;
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
);
}
Widget _buildDataTable(BuildContext context) {
if (filteredData.isEmpty) {
return const SizedBox(

View File

@ -239,6 +239,8 @@ class AgentState extends ConsumerState<Agent> {
print("Response: ${response.body}");
context.go(AppRoutes.agentLst);
} else {
final responseBody = jsonDecode(response.body);
dynamic msg = responseBody['data'];
print("❌ Submission failed. Status: ${response.statusCode}");
print("Body: ${response.body}");
@ -246,9 +248,10 @@ class AgentState extends ConsumerState<Agent> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Agent Creation Failed"),
title: Text("Agent User Creation Failed"),
content: Text(
"There was a problem in creating user. Please try again.",
msg,
// "There was a problem in creating user. Please try again.",
),
actions: [
TextButton(
@ -570,7 +573,8 @@ class AgentState extends ConsumerState<Agent> {
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.green.shade300,
color: Color(0xFF425B5B),
// color: Colors.green.shade300,
),
child: Row(
children: const [

View File

@ -161,6 +161,9 @@ class StaffState extends ConsumerState<Staff> {
print("Response: ${response.body}");
context.go(AppRoutes.staffLst);
} else {
final responseBody = jsonDecode(response.body);
dynamic msg = responseBody['data'];
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
@ -168,10 +171,8 @@ class StaffState extends ConsumerState<Staff> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Staff Creation Failed"),
content: Text(
"There was a problem submitting your plan. Please try again.",
),
title: Text("Staff User Creation Failed"),
content: Text(msg),
actions: [
TextButton(
child: Text("OK"),

View File

@ -19,11 +19,25 @@ class _MobileTabsState extends State<MobileTabs> {
setState(() => _currentIndex = index);
widget.onTabChanged(index);
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profile"),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.event_note_sharp),
icon: Image.asset(
"assets/drawer/drawerImg1.png",
height: 20,
width: 20,
),
label: "Dashboard",
),
const BottomNavigationBarItem(
icon: Icon(Icons.person),
label: "Profile",
),
BottomNavigationBarItem(
icon: Image.asset(
"assets/drawer/drawerImg2.png",
height: 20,
width: 20,
),
label: "Enquiry",
),
],

View File

@ -29,10 +29,16 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
backgroundColor: const Color(0xFFD6F6F4), // light cyan
elevation: 0,
automaticallyImplyLeading: false,
titleSpacing: 0,
titleSpacing: isMobile ? 0 : 30,
title: Row(
children: [
if (isMobile)
if (!isMobile)
Image.asset(
"assets/login/nhance-partner-logo.png",
height: 60,
width: 120,
),
if (isMobile) ...[
IconButton(
icon: const Icon(
Icons.arrow_back_ios,
@ -40,38 +46,70 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
size: 18,
),
onPressed: onBack ?? () => Navigator.pop(context),
)
else if (onMenuPressed != null)
IconButton(
icon: const Icon(Icons.menu, color: Colors.black87),
onPressed: onMenuPressed,
),
Expanded(
child: Text(
title,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w500,
fontSize: 16,
Expanded(
child: Text(
title,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w500,
fontSize: 16,
),
),
),
),
IconButton(
icon: const Icon(Icons.notifications_none, color: Colors.black87),
onPressed: onNotifications,
],
Spacer(),
// else if (onMenuPressed != null)
// IconButton(
// icon: const Icon(Icons.menu, color: Colors.black87),
// onPressed: onMenuPressed,
// ),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0),
),
child: IconButton(
icon: const Icon(Icons.notifications_none, color: Colors.black87),
onPressed: onNotifications,
),
),
if (!isMobile) ...[
GestureDetector(
onTapDown: (details) => onProfile!(details),
child: const Icon(Icons.person_outline, color: Colors.black87),
SizedBox(width: 10),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0),
),
child: GestureDetector(
onTapDown: (details) => onProfile!(details),
child: const Icon(Icons.person_outline, color: Colors.black87),
),
),
// IconButton(
// icon: const Icon(Icons.person_outline, color: Colors.black87),
// onPressed: onProfile,
// ),
IconButton(
icon: const Icon(Icons.logout, color: Colors.black87),
onPressed: onLogout,
SizedBox(width: 10),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0),
),
child: IconButton(
icon: const Icon(Icons.logout, color: Colors.black87),
onPressed: onLogout,
),
),
],
],