UI_FILE_UPLOAD_CHNGS UI_ENQUIRY

This commit is contained in:
venbaittech 2025-09-12 16:15:43 +05:30
parent fa484bc00e
commit e2c02c4e12
30 changed files with 4808 additions and 863 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,15 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:nhance_partner/core/routing/routes.dart';
import 'package:nhance_partner/presentation/screens/Enquiry/enquiryList.dart';
import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart';
import 'package:nhance_partner/presentation/screens/claims/claimsList.dart';
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
import '../../data/services/auth_service.dart';
import '../../presentation/screens/Enquiry/enquiry/tabs.dart';
import '../../presentation/screens/UserManagement/Agent/agent.dart';
import '../../presentation/screens/UserManagement/Agent/agentList.dart';
import '../../presentation/screens/UserManagement/Profile/profile_mobile.dart';
import '../../presentation/screens/UserManagement/Staff/staff.dart';
import '../../presentation/screens/UserManagement/Staff/staffList.dart';
import '../../presentation/screens/home/home_screen.dart';
@ -35,7 +38,7 @@ final GoRouter appRouter = GoRouter(
GoRoute(
path: AppRoutes.profile,
builder: (context, state) => const ProfilePopUp(),
builder: (context, state) => const Profile(),
),
GoRoute(
@ -76,6 +79,16 @@ final GoRouter appRouter = GoRouter(
}
},
),
GoRoute(
path: AppRoutes.enquiryLst,
builder: (context, state) => const EnquiryList(),
),
GoRoute(
path: AppRoutes.tabEnquiry,
builder: (context, state) => const TabEnquiryList(),
),
],
redirect: (context, state) async {
final loggedIn = await AuthService.isLoggedIn();

View File

@ -8,4 +8,6 @@ class AppRoutes {
static const agent = '/agent/:id';
static const String staffLst = '/staffLst';
static const staff = '/staff/:id';
static const String enquiryLst = '/enquiryLst';
static const String tabEnquiry = '/tabEnquiry';
}

View File

@ -1,3 +1,5 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:nhance_partner/data/services/auth_service.dart';
@ -76,147 +78,7 @@ class ApiService {
);
return _handleResponse(response);
}
Future<Map<String, dynamic>> fetchAgentIncentiveList(agentId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}agent/agentIncentiveFileList?agent_id=${agentId}',
);
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
// );
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchManagerIncentiveList(mangerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}staff/managerIncentiveFileList?manager_id=$mangerId',
);
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
// );
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchAgentUserList(int managerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final userId = '1';
final url = Uri.parse(
'${Env.apiUrl}agent/agentList?manager_id=${managerId}',
);
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}',
// );
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> findSingleAgentData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/findAgent?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}agent/findAgent?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> deleteAgentIncentiveFile(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/deleteAgentIncentiveFile?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}agent/deleteAgentIncentiveFile?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchStaffUserList(int managerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
// );
final url = Uri.parse(
'${Env.apiUrl}staff/staffList?manager_id=${managerId}',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> findSingleStaffData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/staff/findStaff?id=$id');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/staff/findStaff?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}staff/findStaff?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// --------------------------- COMMON FILES __________________________________________
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
if (response.statusCode == 200) {
@ -290,6 +152,39 @@ class ApiService {
}
}
Future<void> downloadFile({
required String? apiUrl,
required PlatformFile? localFile,
required String? fileName,
String? apiId, // optional if your API needs it
}) async {
try {
if (localFile != null) {
if (kIsWeb) {
final blob = html.Blob([localFile.bytes!]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.AnchorElement(href: url)
..setAttribute('download', localFile.name)
..click();
html.Url.revokeObjectUrl(url);
print("Download triggered successfully (web)!");
} else {
print("Local file path: ${localFile.path}");
// await OpenFilex.open(localFile.path!);
}
} else if (apiUrl != null) {
print("Download from API: $apiUrl");
await getPdfDownload(apiUrl, apiId);
} else {
print("⚠️ No file available to download");
}
} catch (e) {
print("Error during download: $e");
}
}
Future<Map<String, dynamic>> updateStatus(id, status, role) async {
final Map<String, dynamic> data = {
"id": int.parse(id),
@ -324,4 +219,232 @@ class ApiService {
final response = await _makePostRequestJson(url, data, headers);
return response;
}
// -------------------------------- AGENT ----------------------------------------------
Future<Map<String, dynamic>> fetchAgentIncentiveList(agentId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}agent/agentIncentiveFileList?agent_id=${agentId}',
);
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
// );
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchManagerIncentiveList(mangerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}staff/managerIncentiveFileList?manager_id=$mangerId',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchAgentUserList(int managerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final userId = '1';
final url = Uri.parse(
'${Env.apiUrl}agent/agentList?manager_id=${managerId}',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> findSingleAgentData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/findAgent?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}agent/findAgent?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> deleteAgentIncentiveFile(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/deleteAgentIncentiveFile?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}agent/deleteAgentIncentiveFile?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// ------------------------------------ STAFF -----------------------------------------------------
Future<Map<String, dynamic>> fetchStaffUserList(int managerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
// );
final url = Uri.parse(
'${Env.apiUrl}staff/staffList?manager_id=${managerId}',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> findSingleStaffData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse('${Env.apiUrl}/api/staff/findStaff?id=$id');
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/staff/findStaff?id=$id',
// );
final url = Uri.parse('${Env.apiUrl}staff/findStaff?id=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// ----------------------------------- ENQUIRY -------------------------------------------------
Future<Map<String, dynamic>> fetchEnquiryList(int id, role) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final userId = '1';
final url;
if (role == 'manager') {
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?manager_id=$id');
} else if (role == 'staff') {
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?staff_id=$id');
} else {
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?agent_id=$id');
}
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}',
// );
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 {
// print(_token);
if (_token == null) {
await _initializeToken();
}
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
// );
final url = Uri.parse(
'${Env.apiUrl}quotation/quotationList?manager_id=${managerId}',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// --------------------------------- MASTER DATA ----------------------------------------------
Future<Map<String, dynamic>> fetchMasterDropDown(String val) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
print('VAl - $val');
dynamic url;
if (val == 'staffRole') {
url = Uri.parse('${Env.apiUrl}master/getRoleMaster');
} else if (val == 'vehicleType') {
url = Uri.parse('${Env.apiUrl}master/getVehicleTypeMaster');
} else if (val == 'InsuranceType') {
url = Uri.parse('${Env.apiUrl}master/getInsurancePlanTypeMaster');
} else if (val == 'Insurers') {
url = Uri.parse('${Env.apiUrl}master/getInsurersMaster');
} else if (val == 'Staffs') {
url = Uri.parse('${Env.apiUrl}master/getStaffMaster');
} else if (val == 'Claim') {
url = Uri.parse('${Env.apiUrl}master/getClaimMaster');
} else if (val == 'Endorsement') {
url = Uri.parse('${Env.apiUrl}master/getEndorsementMaster');
}
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
}

View File

@ -254,4 +254,4 @@ class PaginationControls extends StatelessWidget {
child: Text('...', style: TextStyle(fontSize: 16)),
);
}
}
}

View File

@ -4,6 +4,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:nhance_partner/presentation/providers/manager_provider.dart';
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -31,13 +32,15 @@ Future<void> main() async {
// prefs.clear();
if (kIsWeb) {
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
authDomain: "nhance-ee8d1.firebaseapp.com",
projectId: "nhance-ee8d1",
storageBucket: "nhance-ee8d1.firebasestorage.app",
messagingSenderId: "1084115316849",
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0"));
options: const FirebaseOptions(
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
authDomain: "nhance-ee8d1.firebaseapp.com",
projectId: "nhance-ee8d1",
storageBucket: "nhance-ee8d1.firebasestorage.app",
messagingSenderId: "1084115316849",
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0",
),
);
} else {
await Firebase.initializeApp();
await FirebaseAppCheck.instance.activate(
@ -77,7 +80,6 @@ class MyApp extends ConsumerStatefulWidget {
}
class _MyAppState extends ConsumerState<MyApp> {
@override
void initState() {
super.initState();
@ -91,10 +93,14 @@ class _MyAppState extends ConsumerState<MyApp> {
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Clean Architecture App',
theme: ThemeData(primarySwatch: Colors.blue),
title: 'Nhance Partner',
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: GoogleFonts.interTextTheme(
Theme.of(context).textTheme, // base on default styles
),
),
routerConfig: appRouter,
);
}
}

View File

@ -37,8 +37,12 @@ class MainLayout extends StatelessWidget {
bottomNavigationBar: MobileTabs(
onTabChanged: (index) {
debugPrint("Mobile tab changed to $index");
if (index == 1) {
if (index == 0) {
context.go(AppRoutes.dashboard);
} else if (index == 1) {
context.go(AppRoutes.profile);
} else if (index == 2) {
context.go(AppRoutes.tabEnquiry);
}
},
),

View File

@ -0,0 +1,729 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:nhance_partner/presentation/layouts/responsive_layout.dart';
import 'package:universal_html/html.dart' as html;
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/Pagination.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 {
const EnquiryTab({super.key});
@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>>>();
List<String> tabHeader = [
'name',
'email',
'mobile',
'code',
'address',
'regNo',
'remarks',
];
late String isActive = "1";
html.File? docUploadedRCFile;
html.File? docUploadedIDProof;
html.File? docUploadedPrevPolicy;
// PlatformFile? passportFile;
String? selectedRCFile;
String? selectedIdProof;
String? selectedPrevPolicy;
String? passportFileUrlFromApi;
String? selectedId;
bool isLoading = false;
String? selectedVehicleType;
String? selectedInsurer;
Map<String, TextEditingController> controllers = {};
String? _token;
dynamic userId;
dynamic managerId;
List<Map<String, dynamic>> getVehicleTypeData = [];
List<Map<String, dynamic>> filteredVechicleData = [];
List<Map<String, dynamic>> getInsurersData = [];
List<Map<String, dynamic>> filteredInsurersData = [];
Map<String, dynamic> dataDetails() {
// final data = {
// "name": controllers["name"]?.text,
// "email": controllers["email"]?.text,
// "mobile": controllers["mobile"]?.text,
// "address": controllers["address"]?.text,
// "agent_code": controllers["code"]?.text,
// "is_active": isActive,
// "manager_id": managerId,
// };
final data = {
"agent_id": 1,
"name": controllers["name"]?.text,
"mobile": controllers["mobile"]?.text,
"email": controllers["email"]?.text,
"reg_no": controllers["regNo"]?.text,
"vehicle_type_id": selectedVehicleType,
"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();
}
// updateData();
_initializeToken();
Future.microtask(() {
managerId = ref.watch(managerIdProvider);
userId = ref.watch(userIdProvider);
});
getVehicleType();
getInsurers();
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
Future<void> getVehicleType() async {
print('getClaimList called');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchMasterDropDown('vehicleType');
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> 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;
// }
setState(() {
// uploadIncentiveData(dataSet);
});
print("Handl2");
}
// 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");
// }
// }
@override
Widget build(BuildContext context) {
return 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: EdgeInsets.all(26.0),
child: Column(
children: [
// Row(children: [Expanded(child: buildFormFields(context))]),
Expanded(
child: SingleChildScrollView(child: buildFormFields(context)),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
handleSave();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 45.0, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text('Save', style: TextStyle(color: Colors.white)),
),
),
],
),
SizedBox(height: 30),
],
),
);
}
Widget buildFormFields(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResponsiveRow(context, buildName(context), buildEmail(context)),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
_buildResponsiveRow(
context,
buildPhNumber(context),
buildId(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
_buildResponsiveRow(
context,
buildVehicleType(context),
buildInsurer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
_buildResponsiveRow(
context,
buildUploadRCDocument(context),
buildUploadIDDocument(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
_buildResponsiveRow(
context,
buildUploadPolicyDocument(context),
buildRemarks(context),
),
],
),
);
}
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: [
Text(label, style: _textStyle),
const SizedBox(width: 10),
field,
],
);
}
}
Widget buildResponsiveUploadField({
required String label,
required String? hintText,
required void Function(String fileName, dynamic file) onFileSelected,
}) {
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,
onFileSelected: onFileSelected,
);
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: _textStyle),
const SizedBox(height: 8),
uploadWidget,
const SizedBox(height: 16),
],
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
const SizedBox(width: 10),
uploadWidget,
],
);
}
}
Widget buildName(BuildContext context) {
return buildResponsiveField(
label: "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.email(value, "email"),
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.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildId(BuildContext context) {
return buildResponsiveField(
label: "Registration Number *",
field: ThemedFormField(
controller: controllers['regNo']!,
validator: (value) => Validators.requiredField(value, "regNo"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildVehicleType(BuildContext context) {
return buildResponsiveField(
label: "Vehicle Type",
field: Container(
color: Colors.white,
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
// height: 40,
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKey,
selectedItem: null,
items: (filter, infiniteScrollProps) {
return filteredVechicleData;
},
itemAsString: (val) => val['vehicle_type'].toString(), // what to show
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
),
),
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 Vehicle Type...",
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 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) {
return buildResponsiveField(
label: "Insurer",
field: Container(
color: Colors.white,
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
// height: 40,
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKeyInsurer,
selectedItem: null,
items: (filter, infiniteScrollProps) {
return filteredInsurersData;
},
itemAsString: (val) => val['name'].toString(), // what to show
compareFn: (item, selectedItem) =>
item['id'] == selectedItem['id'], // compare by id
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 buildUploadRCDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload RC Document",
hintText: selectedRCFile,
onFileSelected: (fileName, file) {
print("File picked: $fileName");
setState(() {
docUploadedRCFile = file;
});
},
);
}
Widget buildUploadIDDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload ID Proof",
hintText: selectedIdProof,
onFileSelected: (fileName, file) {
print("File picked: $fileName");
setState(() {
docUploadedIDProof = file;
});
},
);
}
Widget buildUploadPolicyDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload Previous Policy",
hintText: selectedPrevPolicy,
onFileSelected: (fileName, file) {
print("File picked: $fileName");
setState(() {
docUploadedPrevPolicy = file;
});
},
);
}
Widget buildRemarks(BuildContext context) {
return buildResponsiveField(
label: "Remarks",
field: ThemedFormField(
maxLength: 3,
controller: controllers['remarks']!,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
static const _textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
);
}

View File

@ -0,0 +1,436 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../../data/utils/validators.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../themes/indicators/text_field_theme.dart';
class PolicyTab extends ConsumerStatefulWidget {
const PolicyTab({super.key});
@override
ConsumerState<PolicyTab> createState() => PolicyTabState();
}
class PolicyTabState extends ConsumerState<PolicyTab> {
List<Map<String, dynamic>> dataVal = [];
final TextEditingController _searchController = TextEditingController();
List<Map<String, dynamic>> getQuotationData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
late ApiService apiService;
final _formKey = GlobalKey<FormState>();
bool isLoading = false;
List<String> tabHeader = [
'name',
'email',
'mobile',
'code',
'address',
'regNo',
];
Map<String, TextEditingController> controllers = {};
String? _token;
dynamic userId;
dynamic managerId;
@override
void initState() {
super.initState();
apiService = ApiService();
for (String field in tabHeader) {
controllers[field] = TextEditingController();
}
// updateData();
_initializeToken();
Future.microtask(() {
managerId = ref.watch(managerIdProvider);
userId = ref.watch(userIdProvider);
getQuotationList(managerId);
});
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
Future<void> getQuotationList(int managerId) async {
print('getClaimList called MagId - $managerId');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchQuotationList(managerId);
if (response['status'] == 'success') {
print('quoationListData - ${response['data']}');
setState(() {
getQuotationData = List<Map<String, dynamic>>.from(response['data']);
originalData = getQuotationData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getQuotationData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
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: EdgeInsets.all(26.0),
child: Column(
children: [
SingleChildScrollView(child: buildFormFields(context)),
const SizedBox(height: 20),
],
),
),
const SizedBox(height: 20),
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: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('More Information', style: _headerStyle),
Row(
children: [
SizedBox(
width: 100,
child: Text('Claim', style: _headerStyle),
),
SizedBox(width: 20),
Expanded(
child: Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('', style: _headerStyle),
IconButton(
onPressed: () {},
icon: Icon(Icons.add),
),
],
),
),
),
],
),
SizedBox(height: 10),
Row(
children: [
SizedBox(
width: 100,
child: Text('Endorsement', style: _headerStyle),
),
SizedBox(width: 20),
Expanded(
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('', style: _headerStyle),
IconButton(
onPressed: () {},
icon: Icon(Icons.add),
),
],
),
),
),
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
// handleSave();
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
),
),
],
),
// SizedBox(height: 30),
// _buildDataTable(context),
],
),
),
],
),
),
);
}
Widget buildFormFields(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResponsiveRow(
context,
buildName(context),
buildInsusrer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildIdv(context),
buildInsurdName(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPolicyNum(context),
buildPaymentMode(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPlanType(context),
buildPremiumAmount(context),
),
],
),
);
}
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: [
Text(label, style: _textStyle),
const SizedBox(width: 10),
field,
],
);
}
}
Widget buildName(BuildContext context) {
return buildResponsiveField(
label: "Registration Number",
field: ThemedFormField(
controller: controllers['name']!,
validator: (value) => Validators.requiredField(value, "name"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildInsusrer(BuildContext context) {
return buildResponsiveField(
label: "Insusrer",
field: ThemedFormField(
controller: controllers['email']!,
validator: (value) => Validators.email(value, "email"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildIdv(BuildContext context) {
return buildResponsiveField(
label: "IDV",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildInsurdName(BuildContext context) {
return buildResponsiveField(
label: "Insured Name",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPolicyNum(BuildContext context) {
return buildResponsiveField(
label: "Policy Number",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPaymentMode(BuildContext context) {
return buildResponsiveField(
label: "Payment Mode",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPlanType(BuildContext context) {
return buildResponsiveField(
label: "Plan Type",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPremiumAmount(BuildContext context) {
return buildResponsiveField(
label: "Premium Amount",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
static const _textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
);
}

View File

@ -0,0 +1,745 @@
import 'dart:io' as html;
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/validators.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/customizd_file_upload.dart';
import '../../../themes/indicators/input_field_decoration.dart';
import '../../../themes/indicators/text_field_theme.dart';
import '../../../themes/indicators/upload_doc_theme.dart'
hide ThemedUploadField;
class QuotationTab extends ConsumerStatefulWidget {
const QuotationTab({super.key});
@override
ConsumerState<QuotationTab> createState() => QuotationTabState();
}
class QuotationTabState extends ConsumerState<QuotationTab> {
int currentPage = 0;
int itemsPerPage = 10;
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>>>();
List<String> tabHeader = [
'name',
'email',
'mobile',
'code',
'address',
'regNo',
];
late String isActive = "1";
html.File? docUploadedFile;
// PlatformFile? passportFile;
String? selectedFileNames;
String? passportFileUrlFromApi;
String? selectedId;
bool isLoading = false;
bool isquotation = false;
Map<String, TextEditingController> controllers = {};
String? _token;
dynamic userId;
dynamic managerId;
List<Map<String, dynamic>> getVehicleTypeData = [];
List<Map<String, dynamic>> filteredVechicleData = [];
List<Map<String, dynamic>> getInsurersData = [];
List<Map<String, dynamic>> filteredInsurersData = [];
Map<String, dynamic> dataDetails() {
final data = {
"name": controllers["name"]?.text,
"email": controllers["email"]?.text,
"mobile": controllers["mobile"]?.text,
"address": controllers["address"]?.text,
"agent_code": controllers["code"]?.text,
"is_active": isActive,
"manager_id": managerId,
};
return data;
}
List<Map<String, dynamic>> getQuotationData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
// bool isLoading = false;
@override
void initState() {
super.initState();
apiService = ApiService();
for (String field in tabHeader) {
controllers[field] = TextEditingController();
}
// updateData();
_initializeToken();
Future.microtask(() {
managerId = ref.watch(managerIdProvider);
userId = ref.watch(userIdProvider);
getQuotationList(managerId);
});
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
Future<void> getQuotationList(int managerId) async {
print('getClaimList called MagId - $managerId');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchQuotationList(managerId);
if (response['status'] == 'success') {
print('quoationListData - ${response['data']}');
setState(() {
getQuotationData = List<Map<String, dynamic>>.from(response['data']);
originalData = getQuotationData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getQuotationData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
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: EdgeInsets.all(16.0),
child: Column(
children: [
// Text('datta')
_buildDataTable(context),
],
),
),
const SizedBox(height: 20),
if (isquotation)
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: EdgeInsets.all(26.0),
child: Column(
children: [
SingleChildScrollView(child: buildFormFields(context)),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
// handleSave();
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Reject',
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(width: 10),
GestureDetector(
onTap: () {
// handleSave();
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Accept',
style: TextStyle(color: Colors.white),
),
),
),
],
),
],
),
),
],
),
),
);
}
Widget buildFormFields(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResponsiveRow(
context,
buildName(context),
buildInsusrer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildIdv(context),
buildPremiumAmnt(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPlanType(context),
buildDocuments(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildAdditonalDocuments(context),
buildRemarks(context),
),
],
),
);
}
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 buildName(BuildContext context) {
return buildResponsiveField(
label: "Registration Number",
field: ThemedFormField(
controller: controllers['name']!,
validator: (value) => Validators.requiredField(value, "name"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildInsusrer(BuildContext context) {
return buildResponsiveField(
label: "Insusrer",
field: ThemedFormField(
controller: controllers['email']!,
validator: (value) => Validators.email(value, "email"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildIdv(BuildContext context) {
return buildResponsiveField(
label: "IDV",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPremiumAmnt(BuildContext context) {
return buildResponsiveField(
label: "Premium Amount",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPlanType(BuildContext context) {
return buildResponsiveField(
label: "Plan Type",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildDocuments(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload RC Certificate",
hintText: selectedFileNames,
onFileSelected: (fileName, file) {
print("File picked: $fileName");
setState(() {
docUploadedFile = file;
});
},
);
}
Widget buildAdditonalDocuments(BuildContext context) {
return buildResponsiveField(
label: "Additional Documents",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildRemarks(BuildContext context) {
return buildResponsiveField(
label: "Remarks",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
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: [
Text(label, style: _textStyle),
const SizedBox(width: 10),
field,
],
);
}
}
Widget buildResponsiveUploadField({
required String label,
required String? hintText,
required void Function(String fileName, dynamic file) onFileSelected,
}) {
final isMobile = ResponsiveLayout.isMobile(context);
final uploadWidget = ThemedUploadField(
hintText: hintText ?? "Upload Document",
txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26,
onFileSelected: onFileSelected,
);
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: _textStyle),
const SizedBox(height: 8),
uploadWidget,
const SizedBox(height: 16),
],
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: _textStyle),
const SizedBox(width: 10),
uploadWidget,
],
);
}
}
Widget buildId(BuildContext context) {
return buildResponsiveField(
label: "Registration Number *",
field: ThemedFormField(
controller: controllers['regNo']!,
validator: (value) => Validators.requiredField(value, "regNo"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
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(
height: 50,
child: Center(child: Text('No available data')),
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
return SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
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 _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 1, child: Text(' ', style: _headerStyle)),
Expanded(
flex: 1,
child: Text('Registration Number', style: _headerStyle),
),
Expanded(flex: 1, child: Text('Insurer', style: _headerStyle)),
Expanded(flex: 1, child: Text('IDV', style: _headerStyle)),
Expanded(flex: 1, child: Text('Plan Type', style: _headerStyle)),
Expanded(flex: 1, child: Text('Premium Amount', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
],
),
);
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
// margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Colors.white,
// color: Color(0xFFE0F7F9),
border: const Border(
bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
// Single-select checkbox
Expanded(
flex: 1,
child: Checkbox(
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
if (val == true) {
selectedId = item['id']; // select this row
isquotation = true;
} else {
selectedId = null;
isquotation = false; // unselect
}
});
print("Selected row id: $selectedId");
},
),
),
Expanded(flex: 1, child: Text(item['id'] ?? '-', style: _dataBold)),
Expanded(
flex: 1,
child: Text(item['email'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['mobile'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(
item['insured_declared_value'] ?? '-',
style: _dataBold,
),
),
Expanded(
flex: 1,
child: Text(
item['status'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: item['status'] == 'Accepted' ? Colors.green : Colors.red,
),
),
),
],
),
);
}
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF6FEFD),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
child: Row(
children: [
Column(
children: [
// Single-select checkbox
Checkbox(
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
if (val == true) {
selectedId = item['id']; // select this row
isquotation = true;
} else {
selectedId = null;
isquotation = false; // unselect
}
});
print("Selected row id: $selectedId");
},
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Company + Vehicle Reg No
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("New India", style: _cardBodyStyle),
const Text("TN64V3456", style: _cardheaderStyle),
const Text("Status: Rejected", style: _cardBodyStyle),
],
),
),
SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("Comprehensive", style: _cardBodyStyle),
const Text("IDV:₹145000", style: _cardBodyStyle),
const Text("₹1000", style: _cardheaderStyle),
],
),
),
],
),
],
),
),
],
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
static const _textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardBodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
fontSize: 12,
);
}

View File

@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../../../core/routing/routes.dart';
import 'enquiry_tab.dart';
import 'quotation_tab.dart';
import 'policy_tab.dart';
import '../../../layouts/main_layout.dart';
class TabEnquiryList extends ConsumerStatefulWidget {
const TabEnquiryList({super.key});
@override
ConsumerState<TabEnquiryList> createState() => TabEnquiryListState();
}
class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
int selectedIndex = 0; // 0 = Enquiry, 1 = Quotation, 2 = Policy
final List<String> tabs = ["Enquiry", "Quotation", "Policy"];
final List<Widget> tabWidgets = [EnquiryTab(), QuotationTab(), PolicyTab()];
@override
Widget build(BuildContext context) {
return MainLayout(
title: "Enquiry",
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
// Header
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
child: GestureDetector(
onTap: () {
context.go(AppRoutes.dashboard);
},
child: Row(
children: [
const Icon(
Icons.arrow_left_sharp,
size: 35,
color: Color(0xFF425B5B),
),
const SizedBox(width: 8),
Text(
"Enquiry",
style: GoogleFonts.inter(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.black87,
),
),
],
),
),
),
const SizedBox(height: 10),
// Tab Buttons
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: List.generate(tabs.length, (index) {
final bool isSelected = selectedIndex == index;
return Padding(
padding: const EdgeInsets.only(right: 12),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 2,
backgroundColor: isSelected
? const Color(0xFF425B5B) // selected dark green
: const Color(0xFFEDFFFC), // light teal
foregroundColor: isSelected
? Colors.white
: Colors.black87,
side: BorderSide(
color: isSelected
? const Color(0xFF425B5B)
: const Color(0xFFEDFFFC),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
onPressed: () {
setState(() {
selectedIndex = index;
});
},
child: Text(
tabs[index],
style: GoogleFonts.inter(
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
fontSize: 15,
),
),
),
);
}),
),
),
const SizedBox(height: 16),
// Tab content
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: tabWidgets[selectedIndex],
),
),
],
),
),
);
}
}

View File

@ -1,21 +1,26 @@
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:intl/intl.dart';
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../layouts/main_layout.dart';
import '../../layouts/responsive_layout.dart';
import '../../providers/manager_provider.dart';
import '../../themes/indicators/export_btn.dart';
import '../../themes/indicators/search_field_theme.dart';
import '../../widgets/custom_action_popup.dart';
class enquiryList extends StatefulWidget {
const enquiryList({super.key});
class EnquiryList extends ConsumerStatefulWidget {
const EnquiryList({super.key});
@override
enquiryListState createState() => enquiryListState();
ConsumerState<EnquiryList> createState() => EnquiryListState();
}
class enquiryListState extends State<enquiryList> {
class EnquiryListState extends ConsumerState<EnquiryList> {
int currentPage = 1;
int itemsPerPage = 10;
late ApiService apiService;
@ -31,70 +36,102 @@ class enquiryListState extends State<enquiryList> {
super.initState();
apiService = ApiService();
// getStaffList();
Future.microtask(() {
final id = ref.read(managerIdProvider);
final roleId = ref.read(userRoleProvider);
print("ENQroleId - $roleId");
if (id != null) {
getStaffList(id, roleId);
}
});
}
Future<void> getStaffList(int managerId, role) async {
print('getClaimList called MagId - $managerId - $role');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchEnquiryList(managerId, role);
if (response['status'] == 'success') {
final data = response['data'];
print('getStaffListData - ${response['data']}');
setState(() {
if (data is List) {
// Already a list of maps
getStaffData = List<Map<String, dynamic>>.from(data);
} else if (data is Map) {
// Single object, wrap in a list
getStaffData = [Map<String, dynamic>.from(data)];
} else {
getStaffData = [];
}
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
originalData = getStaffData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getStaffData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
List<dynamic> get _paginatedData {
// Sort descending by id first
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final startIndex = (currentPage - 1) * itemsPerPage;
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
return sortedData.sublist(startIndex, endIndex);
}
void filterData(String query) {
print("FilterDAta - $query");
setState(() {
filteredData = getStaffData.where((item) {
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
// final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
return (item['name'] ?? '').toLowerCase().contains(
return (item['created_on'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['email'] ?? '').toLowerCase().contains(query.toLowerCase()) ||
(item['mobile'] ?? '').toLowerCase().contains(
(item['reg_no'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['address'] ?? '').toLowerCase().contains(
(item['insurer_name'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['emp_id'] ?? item['agent_code'] ?? '').toLowerCase().contains(
(item['status'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
isActiveStatus.contains(query.toLowerCase());
(item['remarks'] ?? '').toLowerCase().contains(query.toLowerCase());
}).toList();
});
}
final TextEditingController _searchStaffController = TextEditingController();
// Future<void> getStaffList() async {
// print('getClaimList called');
// setState(() {
// isLoading = true;
// });
//
// try {
// final response = await apiService.fetchStaffUserList();
//
// if (response['status'] == 'success') {
// print('getStaffListData - ${response['data']}');
// setState(() {
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
// originalData = getStaffData;
// filteredData = List.from(originalData);
// // print('originalData - $getClaimPolicies');
// });
// } else {
// getStaffData = [];
// originalData = [];
// }
// } catch (e) {
// print('Exception occurred: $e');
// } finally {
// setState(() {
// isLoading = false;
// });
// }
// }
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
return [
@ -103,7 +140,7 @@ class enquiryListState extends State<enquiryList> {
Navigator.pop(context);
print('EDITStaff - ${data['id']}');
dynamic id = data['id'];
context.go('/staff/$id');
// context.go('/staff/$id');
},
child: Row(
mainAxisSize: MainAxisSize.min,
@ -145,7 +182,7 @@ class enquiryListState extends State<enquiryList> {
color: Color(0xFF425B5B),
),
Text(
"Enquries",
"Enquiries",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
@ -179,29 +216,34 @@ class enquiryListState extends State<enquiryList> {
backgroundColor: Color(0xFFF6F8F8),
onChanged: filterData,
controller: _searchStaffController,
txtwidth: MediaQuery.of(context).size.width * 0.2,
txtwidth: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.width * 0.6
: MediaQuery.of(context).size.width * 0.2,
),
Spacer(),
ExportBtn(
sheetName: "Staff",
fileName: "staff_list",
sheetName: "Enquiry",
fileName: "enquiry_list",
data: filteredData,
txt: !ResponsiveLayout.isMobile(context)
? true
: false,
headers: [
"id",
"name",
"email",
"mobile",
"emp_id",
"is_active",
"created_on",
"reg_no",
"insurer_name",
"status",
"remarks",
],
),
SizedBox(width: 10),
GestureDetector(
onTap: () {
print('Export');
// print('Export');
},
child: Container(
padding: EdgeInsets.all(8.0),
@ -214,22 +256,24 @@ class enquiryListState extends State<enquiryList> {
children: [
Icon(Icons.add, color: Colors.white),
SizedBox(width: 10),
if (!ResponsiveLayout.isMobile(context)) ...[
SizedBox(width: 10),
GestureDetector(
onTap: () {
// context.go(AppRoutes.agent / create);
context.go('/staff/create');
},
child: Text(
'Create New Staff',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
GestureDetector(
onTap: () {
// context.go(AppRoutes.agent / create);
context.go('/staff/create');
},
child: Text(
'Create New Staff',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
),
],
],
),
),
@ -238,6 +282,51 @@ class enquiryListState extends State<enquiryList> {
),
),
SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 1,
child: Text('S.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text(
'Received Date & Time',
style: _headerStyle,
),
),
Expanded(
flex: 1,
child: Text('Reg.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Company', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Remarks', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
Expanded(child: _buildDataTable(context)),
],
),
@ -281,47 +370,31 @@ class enquiryListState extends State<enquiryList> {
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final sortedData = [..._paginatedData];
return ListView.builder(
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
itemCount: sortedData.length + 1, // +1 for header, +1 for pagination
itemCount: ResponsiveLayout.isMobile(context)
? sortedData
.length // only cards for mobile
: sortedData.length + 1, // +1 for header in desktop
itemBuilder: (context, index) {
if (index == 0) return _buildHeader();
// if (index == dataVal.length + 1)
// return _buildPagination(context);
final startIndex = ((currentPage - 1) * itemsPerPage);
// final item = filteredData[index - 1];
final item = sortedData[index - 1];
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 _buildDataRow(item, sno);
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
);
}
Widget _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Received Date & Time', style: _headerStyle),
),
Expanded(flex: 3, child: Text('Reg.No.', style: _headerStyle)),
Expanded(flex: 2, child: Text('Company', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
Expanded(flex: 1, child: Text('Remarks', style: _headerStyle)),
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
],
),
);
return SizedBox.shrink();
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
@ -339,42 +412,38 @@ class enquiryListState extends State<enquiryList> {
child: Row(
children: [
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
Expanded(
flex: 3,
child: Text(item['email'] ?? '-', style: _dataBold),
flex: 2,
child: Text(
_formatDate(item['created_on']) ?? '-',
style: _dataBold,
),
),
Expanded(
flex: 1,
child: Text(item['reg_no'] ?? '-', style: _dataBold),
),
Expanded(
flex: 2,
child: Text(item['mobile'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['emp_id'] ?? '-', style: _dataBold),
child: Text(
item['insurer_name'] ?? '-',
style: _dataBold,
softWrap: true,
maxLines: 3,
),
),
Expanded(
flex: 2,
child: Text(item['status'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Transform.scale(
scale: 0.6, // reduce size (0.70.9 works well)
child: Switch(
value: item['is_active'] == "1",
onChanged: (val) {
setState(() {
item['is_active'] = val ? "1" : "0";
});
final response = apiService.updateStatus(
item['id'],
val ? "1" : "0",
'staff',
);
print("Response - $response");
},
activeColor: Color(0xFF425B5B), // thumb when active
activeTrackColor: Color(0xFFB2D8D3), // track when active
inactiveThumbColor: Colors.grey.shade400, // thumb when inactive
inactiveTrackColor: Colors.grey.shade300, // track when inactive
),
child: Text(
item['remarks'] ?? '-',
style: _dataBold,
softWrap: true,
maxLines: 3,
),
),
@ -415,6 +484,123 @@ class enquiryListState extends State<enquiryList> {
);
}
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF6FEFD),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(item['reg_no'] ?? '-', style: _headerStyle),
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14),
itemBuilder: (context) => [
CustomPopupMenuEntry(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: _buildPopupMenuActions(context, item),
),
),
),
],
),
],
),
const Divider(color: Color(0xffD9EBE8), thickness: 0.8),
// Company + Vehicle Reg No
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("Company", style: _cardheaderStyle),
Text(
item['insurer_name'] ?? '-',
style: _cardBodyStyle,
maxLines: 3,
softWrap: true,
),
],
),
),
SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("Status", style: _cardheaderStyle),
Text(
item['status'] ?? '-',
style: _cardBodyStyle,
maxLines: 2,
softWrap: true,
),
],
),
),
],
),
const SizedBox(height: 15),
// Created On + Remarks
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Received Date & Time", style: _cardheaderStyle),
Text(
_formatDate(item['created_on']) ?? '-',
style: _cardBodyStyle,
),
],
),
),
SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Remarks", style: _cardheaderStyle),
Text(
item['remarks'] ?? '-',
style: _cardBodyStyle,
maxLines: 3,
softWrap: true,
),
],
),
),
],
),
],
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
@ -432,4 +618,15 @@ class enquiryListState extends State<enquiryList> {
color: Colors.black,
fontWeight: FontWeight.bold,
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardBodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
fontSize: 12,
);
}

View File

@ -1,7 +1,9 @@
import 'dart:convert';
// import 'dart:io' as html;
import 'dart:typed_data'; // Import for Uint8List
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -17,9 +19,11 @@ import '../../../../data/utils/validators.dart';
import '../../../layouts/main_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../providers/user_provider.dart';
import '../../../themes/indicators/customizd_file_upload.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../themes/indicators/upload_doc_theme.dart';
import '../../../themes/indicators/singleFileUpload.dart';
// import '../../../themes/indicators/upload_doc_theme.dart';
import 'package:universal_html/html.dart' as html;
class Agent extends ConsumerStatefulWidget {
@ -34,10 +38,12 @@ class AgentState extends ConsumerState<Agent> {
late ApiService apiService;
List<String> tabHeader = ['name', 'email', 'mobile', 'code', 'address'];
late String isActive = "1";
html.File? passportFile;
// html.File? docUploadedFile;
// PlatformFile? passportFile;
String? selectedFileNames;
String? passportFileUrlFromApi;
String? selectedId;
PlatformFile? docUploadedFile;
Map<String, TextEditingController> controllers = {};
String? _token;
@ -96,12 +102,15 @@ class AgentState extends ConsumerState<Agent> {
String? apiDocPath = data["certificate_file_name"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
print('apiDocPath - $apiDocPath');
selectedFileNames = apiDocPath.split('/').last;
print('selectedFileNames - $selectedFileNames');
passportFileUrlFromApi = apiDocPath;
passportFile = null;
print('passportFileUrlFromApi - $passportFileUrlFromApi');
docUploadedFile = null;
} else {
selectedFileNames = null;
passportFile = null;
docUploadedFile = null;
passportFileUrlFromApi = null;
}
});
@ -155,33 +164,64 @@ class AgentState extends ConsumerState<Agent> {
print("USerDAta - $userData");
// userData.forEach((key, value) {
// request.fields[key] = value.toString();
// print("✅ Encoded travel_details2: ${request.fields[key]}");
// });
userData.forEach((key, value) {
request.fields[key] = value.toString();
print("✅ Encoded travel_details2: ${request.fields[key]}");
if (key != 'certificate_file_name') {
request.fields[key] = value.toString();
print("✅ Encoded $key: ${request.fields[key]}");
}
});
// Attach file if selected
if (passportFile != null) {
// if (docUploadedFile != null) {
// try {
// final reader = html.FileReader();
// reader.readAsArrayBuffer(docUploadedFile!);
// await reader.onLoad.first;
//
// final data = reader.result as Uint8List;
//
// final multipartFile = http.MultipartFile.fromBytes(
// 'certificate_file_name',
// data,
// filename: docUploadedFile!.name,
// );
//
// request.files.add(multipartFile);
// print("📎 File attached: ${docUploadedFile!.name}");
// } catch (e) {
// print("❌ Failed to read file: $e");
// }
// } else {
// print("⚠️ No passport file selected.");
// }
// Attach file if selected
if (docUploadedFile != null) {
try {
final reader = html.FileReader();
reader.readAsArrayBuffer(passportFile!);
await reader.onLoad.first;
final data = reader.result as Uint8List;
final multipartFile = http.MultipartFile.fromBytes(
'certificate_file_name',
data,
filename: passportFile!.name,
);
request.files.add(multipartFile);
print("📎 File attached: ${passportFile!.name}");
if (docUploadedFile!.bytes != null) {
final multipartFile = http.MultipartFile.fromBytes(
'certificate_file_name',
docUploadedFile!.bytes!,
filename: docUploadedFile!.name,
);
request.files.add(multipartFile);
} else if (docUploadedFile!.path != null) {
final multipartFile = await http.MultipartFile.fromPath(
'certificate_file_name',
docUploadedFile!.path!,
filename: docUploadedFile!.name,
);
request.files.add(multipartFile);
}
print("📎 File attached: ${docUploadedFile!.name}");
} catch (e) {
print("❌ Failed to read file: $e");
print("❌ Failed to attach file: $e");
}
} else {
print("⚠️ No passport file selected.");
}
print(" Sending request with fields: ${request.fields}");
@ -227,6 +267,37 @@ class AgentState extends ConsumerState<Agent> {
}
}
// Future<void> _downloadFile() async {
// if (docUploadedFile != null) {
// try {
// if (kIsWeb) {
// // Web: use bytes
// final blob = html.Blob([docUploadedFile!.bytes!]);
// final url = html.Url.createObjectUrlFromBlob(blob);
//
// final anchor = html.AnchorElement(href: url)
// ..setAttribute('download', docUploadedFile!.name)
// ..click();
//
// html.Url.revokeObjectUrl(url);
// print("Download triggered successfully (web)!");
// } else {
// print("Local file path: ${docUploadedFile!.path}");
// // Use open_filex to open it:
// // await OpenFilex.open(docUploadedFile!.path!);
// }
// } catch (e) {
// print("Error during download: $e");
// }
// } else if (passportFileUrlFromApi != null) {
// print("Download from API: $passportFileUrlFromApi");
// final path =
// 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
//
// apiService.getPdfDownload(path, selectedId);
// }
// }
@override
void dispose() {
for (var controller in controllers.values) {
@ -451,7 +522,9 @@ class AgentState extends ConsumerState<Agent> {
children: [
Text("Upload Certificate", style: _textStyle),
SizedBox(width: 10),
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ThemedUploadField(
@ -460,58 +533,25 @@ class AgentState extends ConsumerState<Agent> {
onFileSelected: (fileName, file) {
print("Picked file: $fileName (${file.size} bytes)");
setState(() {
passportFile = file;
docUploadedFile = file;
});
},
),
SizedBox(height: 5),
const SizedBox(height: 5),
if (passportFile != null || passportFileUrlFromApi != null)
// Centers the text
if (docUploadedFile != null || passportFileUrlFromApi != null)
Container(
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
print('DOWNLOAD - $passportFile');
if (passportFile != null) {
try {
// Create a blob from the response body
final blob = html.Blob([passportFile!]);
// Generate a download URL for the blob
final url = html.Url.createObjectUrlFromBlob(blob);
// Create a link element to trigger the download
final anchor = html.AnchorElement(href: url)
..setAttribute(
'download',
selectedFileNames ?? "document.pdf",
)
..click();
// Revoke the download URL to free up resources
html.Url.revokeObjectUrl(url);
print("Download triggered successfully!");
} catch (e) {
print("Error during download: $e");
}
} else if (passportFileUrlFromApi != null) {
print("Raw file path: $passportFileUrlFromApi");
final path =
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
apiService.getPdfDownload(path, selectedId);
} else {
print("No file available to download.");
}
},
onTap: () => apiService.downloadFile(
apiUrl:
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId',
apiId: null,
localFile: docUploadedFile,
fileName: selectedFileNames,
),
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
@ -519,9 +559,7 @@ class AgentState extends ConsumerState<Agent> {
color: Colors.green.shade300,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
children: const [
Text(
"Download",
style: TextStyle(
@ -529,9 +567,6 @@ class AgentState extends ConsumerState<Agent> {
fontWeight: FontWeight.w200,
color: Colors.white,
),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign
.end, // Ensures text is centered within the Text widget
),
SizedBox(width: 5),
Icon(Icons.download, size: 13, color: Colors.white),
@ -544,23 +579,146 @@ class AgentState extends ConsumerState<Agent> {
),
],
),
],
);
}
Widget buildIncentiveFile() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Incentive File", style: _textStyle),
SizedBox(width: 10),
ThemedUploadField(
hintText: "Upload Document",
txtwidth: MediaQuery.of(context).size.width * 0.26,
onFileSelected: (fileName, file) {
print("Picked file: $fileName (${file.size} bytes)");
},
),
// Column(
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// ThemedUploadField(
// hintText: selectedFileNames ?? "Upload Document",
// txtwidth: MediaQuery.of(context).size.width * 0.26,
//
// onFileSelected: (fileName, file) {
// // print("File picked: ${fileName}");
// // print("Size: ${file.size}");
// // print("Path: ${file.path}"); // works on mobile/desktop
// // print("Bytes: ${file.bytes}");
// print("Picked file: $fileName (${file.size} bytes)");
// setState(() {
// docUploadedFile = file;
// });
// },
// ),
// SizedBox(height: 5),
//
// if (docUploadedFile != null || passportFileUrlFromApi != null)
// // Centers the text
// Container(
// color: Colors.white,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// GestureDetector(
// onTap: () {
// print('DOWNLOAD - $docUploadedFile');
//
// if (docUploadedFile != null) {
// try {
// final blob = html.Blob([docUploadedFile!]);
//
// final url = html.Url.createObjectUrlFromBlob(blob);
//
// final anchor = html.AnchorElement(href: url)
// ..setAttribute(
// 'download',
// selectedFileNames ?? "document.pdf",
// )
// ..click();
//
// html.Url.revokeObjectUrl(url);
//
// print("Download triggered successfully!");
// } catch (e) {
// print("Error during download: $e");
// }
// } else if (passportFileUrlFromApi != null) {
// print("Raw file path: $passportFileUrlFromApi");
//
// final path =
// 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
//
// apiService.getPdfDownload(path, selectedId);
// } else {
// print("No file available to download.");
// }
// },
//
// // onTap: () {
// // print('DOWNLOAD - $docUploadedFile');
// //
// // if (docUploadedFile != null) {
// // try {
// // if (kIsWeb) {
// // // WEB: use bytes
// // final blob = html.Blob([docUploadedFile!.bytes!]);
// // final url = html.Url.createObjectUrlFromBlob(
// // blob,
// // );
// //
// // final anchor = html.AnchorElement(href: url)
// // ..setAttribute(
// // 'download',
// // selectedFileNames ?? "document.pdf",
// // )
// // ..click();
// //
// // html.Url.revokeObjectUrl(url);
// // print("Download triggered successfully (web)!");
// // } else {
// // // MOBILE/DESKTOP: use file path
// // print(
// // "Local file path: ${docUploadedFile!.path}",
// // );
// //
// // // Example: open the file using open_filex
// // // await OpenFilex.open(docUploadedFile!.path!);
// // }
// // } catch (e) {
// // print("Error during download: $e");
// // }
// // } else if (passportFileUrlFromApi != null) {
// // print("Raw file path: $passportFileUrlFromApi");
// //
// // final path =
// // 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
// //
// // apiService.getPdfDownload(path, selectedId);
// // } else {
// // print("No file available to download.");
// // }
// // },
// child: Container(
// padding: const EdgeInsets.all(5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(5),
// color: Colors.green.shade300,
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// Text(
// "Download",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w200,
// color: Colors.white,
// ),
// overflow: TextOverflow.ellipsis,
// textAlign: TextAlign
// .end, // Ensures text is centered within the Text widget
// ),
// SizedBox(width: 5),
// Icon(Icons.download, size: 13, color: Colors.white),
// ],
// ),
// ),
// ),
// ],
// ),
// ),
// ],
// ),
],
);
}

View File

@ -2,6 +2,7 @@
// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart';
import 'dart:typed_data'; // Import for Uint8List
import 'package:dropdown_search/dropdown_search.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
@ -15,11 +16,12 @@ import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/validators.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/customizd_file_upload.dart';
import '../../../themes/indicators/date_field_theme.dart';
import '../../../themes/indicators/input_field_decoration.dart';
import '../../../themes/indicators/month_field_theme.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../themes/indicators/upload_doc_theme.dart';
// import '../../../themes/indicators/upload_doc_theme.dart';
/// function to open the modal
void showUploadIncentiveModal(BuildContext context) {
@ -50,7 +52,8 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
List<Map<String, dynamic>> filteredIncentiveData = [];
Map<String, TextEditingController> controllers = {};
html.File? passportFile;
// html.File? passportFile;
PlatformFile? passportFile;
String? selectedFileNames;
List<String> tabHeader = ['name', 'agentId', 'date', 'search'];
@ -199,11 +202,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
}
Future<void> handleSave() async {
if (!_formKey.currentState!.validate()) return;
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(
@ -233,35 +237,65 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
print("USerDAta - $dataSet");
// dataSet.forEach((key, value) {
// request.fields[key] = value.toString();
// print("✅ Encoded travel_details2: ${request.fields[key]}");
// });
dataSet.forEach((key, value) {
request.fields[key] = value.toString();
print("✅ Encoded travel_details2: ${request.fields[key]}");
if (key != 'incentive_file_name') {
request.fields[key] = value.toString();
print("✅ Encoded $key: ${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);
if (passportFile!.bytes != null) {
final multipartFile = http.MultipartFile.fromBytes(
'incentive_file_name',
passportFile!.bytes!,
filename: passportFile!.name,
);
request.files.add(multipartFile);
} else if (passportFile!.path != null) {
final multipartFile = await http.MultipartFile.fromPath(
'certificate_file_name',
passportFile!.path!,
filename: passportFile!.name,
);
request.files.add(multipartFile);
}
print("📎 File attached: ${passportFile!.name}");
} catch (e) {
print("❌ Failed to read file: $e");
print("❌ Failed to attach file: $e");
}
} else {
print("⚠️ No passport file selected.");
}
// Attach file if selected
// if (passportFile != null) {
// try {
// final reader = html.FileReader();
// reader.readAsArrayBuffer(passportFile! as html.Blob);
// 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 {
@ -276,7 +310,7 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
print("📨 Response: ${response.body}");
final data = dataDetails(); // this is a Map<String, dynamic>
final data = dataDetails();
final agentId = data["agent_id"];
getAgentIncenctiveFileList(agentId);
@ -492,12 +526,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
// value,
// "Upload Document",
// ),
validator: (value) {
if (passportFile == null) {
return "Please upload a file";
}
return null;
},
// validator: (value) {
// if (passportFile == null) {
// return "Please upload a file";
// }
// return null;
// },
backgroundColor: Color(0xFFECECEC),
hintText:
(selectedFileNames == null ||
@ -507,9 +541,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
txtwidth:
MediaQuery.of(context).size.width * 0.19,
onFileSelected: (fileName, file) {
print(
"Picked file: $fileName (${file.size} bytes)",
);
print("File picked: ${fileName}");
// print("Size: ${file.size}");
// print(
// "Path: ${file.path}",
// ); // works on mobile/desktop
// print("Bytes: ${file.bytes}");
setState(() {
passportFile = file;
selectedFileNames = fileName;

View File

@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/filter_btn.dart';
@ -31,7 +32,7 @@ class AgentListState extends ConsumerState<AgentList> {
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
bool isLoading = false;
dynamic sortedData;
@override
void initState() {
super.initState();
@ -55,10 +56,23 @@ class AgentListState extends ConsumerState<AgentList> {
// }
// }
// List<dynamic> get _paginatedData {
// final startIndex = (currentPage - 1) * itemsPerPage;
// final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
// return filteredData.sublist(startIndex, endIndex);
// }
List<dynamic> get _paginatedData {
// Sort descending by id first
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
// Calculate pagination indices
final startIndex = (currentPage - 1) * itemsPerPage;
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
// Return only the paginated slice
return sortedData.sublist(startIndex, endIndex);
}
void filterData(String query) {
@ -235,6 +249,9 @@ class AgentListState extends ConsumerState<AgentList> {
ExportBtn(
sheetName: "Agents",
fileName: "agent_list",
txt: !ResponsiveLayout.isMobile(context)
? true
: false,
data: filteredData,
headers: [
"id",
@ -278,6 +295,56 @@ class AgentListState extends ConsumerState<AgentList> {
),
),
SizedBox(height: 10),
Container(
height: 50,
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: Text('S.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Agent Name', style: _headerStyle),
),
Expanded(
flex: 3,
child: Text('Email', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Phone Number', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Id', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Address', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
Expanded(child: _buildClaimsDataTable(context)),
],
),
@ -321,8 +388,13 @@ class AgentListState extends ConsumerState<AgentList> {
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
// final sortedData = [..._paginatedData]
// ..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final sortedData = [..._paginatedData];
// final sortedData = [...filteredData]
// ..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
return ListView.builder(
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
@ -341,25 +413,7 @@ class AgentListState extends ConsumerState<AgentList> {
}
Widget _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
Expanded(flex: 2, child: Text('Agent Name', style: _headerStyle)),
Expanded(flex: 3, child: Text('Email', style: _headerStyle)),
Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)),
Expanded(flex: 1, child: Text('Id', style: _headerStyle)),
Expanded(flex: 2, child: Text('Address', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
],
),
);
return const SizedBox.shrink(); // empty widget instead of commenting
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
@ -413,7 +467,7 @@ class AgentListState extends ConsumerState<AgentList> {
});
final response = apiService.updateStatus(
item['id'],
val ? "1" : "0",
val ? "0" : "1",
'agent',
);
print("Response - $response");

View File

@ -0,0 +1,749 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jwt_decode/jwt_decode.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
class Profile extends ConsumerStatefulWidget {
const Profile({super.key});
@override
ConsumerState<Profile> createState() => ProfileState();
}
class ProfileState extends ConsumerState<Profile> {
late ApiService apiService;
List<Map<String, dynamic>> filteredIncentiveData = [];
List<Map<String, dynamic>> getAgentIncentiveFileData = [];
Map<String, dynamic>? profileData;
dynamic roleIdRaw;
dynamic roleId;
bool isProfile = true;
bool isLoading = false;
String? _token;
dynamic latestId;
@override
void initState() {
super.initState();
apiService = ApiService();
// getAgentList();
_initializeToken();
// Future.microtask(() {
// final id = ref.read(managerIdProvider);
// if (id != null) {
// getAgentList(id);
//
// // api/staff/managerIncentiveFileList?manager_id=1
// }
// });
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("profileDataAPISERTOKEN - $_token");
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
setState(() {
profileData = decodedToken['data'];
});
print('profileDatadecodedTokenProfile : $decodedToken');
print('profileDatadecodedTokenProfile : $profileData');
await getList();
}
Future<void> getList() async {
roleIdRaw = profileData?['role_id'];
roleId = roleIdRaw is String ? int.tryParse(roleIdRaw) : roleIdRaw as int?;
if (roleId == 1) {
final managerIdRaw = profileData?['manager_id'];
final managerId = managerIdRaw is String
? int.tryParse(managerIdRaw)
: managerIdRaw as int?;
if (managerId != null) {
getIncenctiveFileList(managerId);
}
} else if (roleId != 1 && roleId != 2) {
final agentIdRaw = profileData?['id'];
final agentId = agentIdRaw is String
? int.tryParse(agentIdRaw)
: agentIdRaw as int?;
if (agentId != null) {
getIncenctiveFileList(agentId);
}
}
}
Future<void> getIncenctiveFileList(id) async {
// print('getClaimList agentId - $agentId');
setState(() {
isLoading = true;
});
try {
final response;
final roleIdRaw = profileData?['role_id'];
final roleId = roleIdRaw is String
? int.tryParse(roleIdRaw)
: roleIdRaw as int?;
if (roleId == 1) {
response = await apiService.fetchManagerIncentiveList(id);
} else {
response = await apiService.fetchAgentIncentiveList(id);
}
if (response['status'] == 'success') {
print('AgentIncentive - ${response['data']}');
setState(() {
getAgentIncentiveFileData = List<Map<String, dynamic>>.from(
response['data'],
);
print('API AgentIncentive - $getAgentIncentiveFileData');
filteredIncentiveData = List.from(getAgentIncentiveFileData);
print('originalData - $filteredIncentiveData');
// Sort descending by created_on
filteredIncentiveData.sort((a, b) {
final dateA =
DateTime.tryParse(a['created_on'].toString()) ?? DateTime(1970);
final dateB =
DateTime.tryParse(b['created_on'].toString()) ?? DateTime(1970);
return dateB.compareTo(dateA); // newest first
});
// Get the latest ID (from the first element after sorting)
if (filteredIncentiveData.isNotEmpty) {
latestId = filteredIncentiveData.first['id'];
print("Latest incentive id: $latestId");
}
print('sortedData - $filteredIncentiveData');
});
} else {
getAgentIncentiveFileData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'Profile',
body: ResponsiveLayout.isMobile(context)
? Container(
padding: const EdgeInsets.all(6),
width: 450,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? '' : 'Incentive Files',
style: _topheaderStyle,
),
],
),
isProfile
? Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: isLoading
? const Center(
child: Text("No incentive files found"),
)
: Column(
children: [
const SizedBox(height: 25),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.circular(
30.0,
),
),
child: Icon(
Icons.person_outline,
size: 50,
color: Colors.black,
),
),
const SizedBox(height: 10),
Text(
profileData?['name'] ?? '-',
style: _headerStyle,
),
Text(
profileData?['emp_id'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
),
const SizedBox(height: 26),
Row(
children: [
const Icon(
Icons.phone_outlined,
size: 20,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['mobile'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(
Icons.email_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['email'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 16),
if (profileData?['role_id'] != '1' &&
profileData?['role_id'] != '2') ...[
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Icon(
Icons.location_on_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Expanded(
child: Text(
profileData?['address'] ?? '-',
style: _dataBold,
softWrap: true,
),
),
],
),
const SizedBox(height: 20),
],
if (profileData?['role_id'] != 2) ...[
Container(
padding: const EdgeInsets.all(6),
color: Color(0xffEDF6F5),
child: GestureDetector(
onTap: () {
print("Download - $latestId");
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$latestId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$latestId';
}
apiService.getPdfDownload(
path,
latestId,
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text(
"Download Incentive file",
style: _dataSub,
),
],
),
),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
setState(() {
isProfile = false;
});
},
child: Text(
"View Previous Files",
style: TextStyle(
color: Color(0xff425B5B),
decoration: TextDecoration
.underline, // 👈 underline
decorationThickness: 1.5,
),
),
),
],
const SizedBox(height: 16),
],
),
)
: Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 10),
isLoading
? const Center(
child: CircularProgressIndicator(),
)
: filteredIncentiveData.isEmpty
? const Center(
child: Text("No incentive files found"),
)
: SizedBox(
height: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.height *
0.4
: 450,
child: ListView.builder(
// padding: const EdgeInsets.all(8),
itemCount: filteredIncentiveData.length,
itemBuilder: (context, index) {
final file =
filteredIncentiveData[index];
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
),
child: IncentiveFileRow(
fileName:
file['incentive_file_name'] ??
"Unknown",
date:
file['incentive_month'] ??
"-",
onUpload: () {
print("Upload ${file['id']}");
final selectedId = file['id'];
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
}
apiService.getPdfDownload(
path,
selectedId,
);
},
),
);
},
),
),
],
),
),
],
),
)
: Container(
padding: const EdgeInsets.all(6),
width: 450,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? 'My Profile' : 'Incentive Files',
style: _topheaderStyle,
),
Spacer(),
Container(
width: 25,
height: 25,
decoration: BoxDecoration(
color: const Color(0xFFF1F1F1),
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
borderRadius: BorderRadius.circular(10.0),
onTap: () {
Navigator.of(context).pop(); // closes the popup
},
child: const Icon(Icons.close, size: 18),
),
),
],
),
isProfile
? Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: isLoading
? const Center(
child: Text("No incentive files found"),
)
: Column(
children: [
const SizedBox(height: 25),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.circular(
30.0,
),
),
child: Icon(
Icons.person_outline,
size: 50,
color: Colors.black,
),
),
const SizedBox(height: 10),
Text(
profileData?['name'] ?? '-',
style: _headerStyle,
),
Text(
profileData?['emp_id'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
),
const SizedBox(height: 26),
Row(
children: [
const Icon(
Icons.phone_outlined,
size: 20,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['mobile'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(
Icons.email_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['email'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 16),
if (profileData?['role_id'] != '1' &&
profileData?['role_id'] != '2') ...[
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Icon(
Icons.location_on_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Expanded(
child: Text(
profileData?['address'] ?? '-',
style: _dataBold,
softWrap: true,
),
),
],
),
const SizedBox(height: 20),
],
if (profileData?['role_id'] != 2) ...[
Container(
padding: const EdgeInsets.all(6),
color: Color(0xffEDF6F5),
child: GestureDetector(
onTap: () {
print("Download - $latestId");
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$latestId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$latestId';
}
apiService.getPdfDownload(
path,
latestId,
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text(
"Download Incentive file",
style: _dataSub,
),
],
),
),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
setState(() {
isProfile = false;
});
},
child: Text(
"View Previous Files",
style: TextStyle(
color: Color(0xff425B5B),
decoration: TextDecoration
.underline, // 👈 underline
decorationThickness: 1.5,
),
),
),
],
const SizedBox(height: 16),
],
),
)
: Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 10),
isLoading
? const Center(
child: CircularProgressIndicator(),
)
: filteredIncentiveData.isEmpty
? const Center(
child: Text("No incentive files found"),
)
: SizedBox(
height: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.height *
0.4
: 450,
child: ListView.builder(
// padding: const EdgeInsets.all(8),
itemCount: filteredIncentiveData.length,
itemBuilder: (context, index) {
final file =
filteredIncentiveData[index];
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
),
child: IncentiveFileRow(
fileName:
file['incentive_file_name'] ??
"Unknown",
date:
file['incentive_month'] ??
"-",
onUpload: () {
print("Upload ${file['id']}");
final selectedId = file['id'];
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
}
apiService.getPdfDownload(
path,
selectedId,
);
},
),
);
},
),
),
],
),
),
],
),
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF000000),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
fontSize: 20,
);
static const _topheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 19,
);
}
class IncentiveFileRow extends StatelessWidget {
final String fileName;
final String date;
final VoidCallback? onUpload;
// final VoidCallback? onDelete;
const IncentiveFileRow({
super.key,
required this.fileName,
required this.date,
this.onUpload,
// this.onDelete,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 7.0),
// margin: const EdgeInsets.only(bottom: 5), // spacing between rows
decoration: BoxDecoration(
// color: Colors.amber.shade50,
border: Border(
bottom: BorderSide(width: 0.6, color: const Color(0xFFE3E3E3)),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: const Color(0xFFF4F6F8),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xFFE3E3E3)),
),
child: const Icon(Icons.file_present, color: Color(0xFF838587)),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
fileName,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Color(0xFF1D1B20),
),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1,
),
),
Text(date, style: const TextStyle(color: Color(0xFF6E6E6E))),
],
),
const Spacer(),
GestureDetector(
onTap: onUpload,
child: const Icon(
Icons.file_download_outlined,
color: Color(0xFF6E6E6E),
),
),
],
),
);
}
}

View File

@ -48,13 +48,13 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
print("profileDataAPISERTOKEN - $_token");
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
setState(() {
profileData = decodedToken['data'];
});
print('decodedTokenProfile : $decodedToken');
print('decodedTokenProfile : $profileData');
print('profileDatadecodedTokenProfile : $decodedToken');
print('profileDatadecodedTokenProfile : $profileData');
await getList();
}
@ -144,255 +144,244 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'Profile',
body: Container(
// color: Colors.white,
padding: const EdgeInsets.all(6),
width: 450,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (!ResponsiveLayout.isMobile(context))
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? 'My Profile' : 'Incentive Files',
style: _topheaderStyle,
),
Spacer(),
Container(
width: 25,
height: 25,
decoration: BoxDecoration(
color: const Color(0xFFF1F1F1),
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
borderRadius: BorderRadius.circular(10.0),
onTap: () {
Navigator.of(context).pop(); // closes the popup
},
child: const Icon(Icons.close, size: 18),
),
),
],
return Container(
padding: const EdgeInsets.all(6),
width: 450,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? 'My Profile' : 'Incentive Files',
style: _topheaderStyle,
),
if (ResponsiveLayout.isMobile(context))
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? '' : 'Incentive Files',
style: _topheaderStyle,
),
],
Spacer(),
Container(
width: 25,
height: 25,
decoration: BoxDecoration(
color: const Color(0xFFF1F1F1),
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
borderRadius: BorderRadius.circular(10.0),
onTap: () {
Navigator.of(context).pop(); // closes the popup
},
child: const Icon(Icons.close, size: 18),
),
),
],
),
isProfile
? Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 25),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.circular(30.0),
),
child: Icon(
Icons.person_outline,
size: 50,
color: Colors.black,
),
),
const SizedBox(height: 10),
Text(profileData?['name'] ?? '-', style: _headerStyle),
Text(
profileData?['emp_id'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
),
const SizedBox(height: 26),
Row(
isProfile
? Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: isLoading
? const Center(child: Text("No incentive files found"))
: Column(
children: [
const Icon(
Icons.phone_outlined,
size: 20,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['mobile'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(
Icons.email_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['email'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 16),
if (profileData?['role_id'] != '1' &&
profileData?['role_id'] != '2') ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.location_on_outlined,
size: 18,
const SizedBox(height: 25),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.circular(30.0),
),
child: Icon(
Icons.person_outline,
size: 50,
color: Colors.black,
),
const SizedBox(width: 8),
Expanded(
child: Text(
profileData?['address'] ?? '-',
style: _dataBold,
softWrap: true,
),
),
const SizedBox(height: 10),
Text(
profileData?['name'] ?? '-',
style: _headerStyle,
),
Text(
profileData?['emp_id'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
],
),
const SizedBox(height: 20),
],
if (profileData?['role_id'] != 2) ...[
Container(
padding: const EdgeInsets.all(6),
color: Color(0xffEDF6F5),
child: GestureDetector(
onTap: () {
print("Download - $latestId");
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$latestId';
} else if (roleId != 1 && roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$latestId';
}
apiService.getPdfDownload(path, latestId);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
),
const SizedBox(height: 26),
Row(
children: [
const Icon(
Icons.phone_outlined,
size: 20,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['mobile'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(
Icons.email_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['email'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 16),
if (profileData?['role_id'] != '1' &&
profileData?['role_id'] != '2') ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text(
"Download Incentive file",
style: _dataSub,
const Icon(
Icons.location_on_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Expanded(
child: Text(
profileData?['address'] ?? '-',
style: _dataBold,
softWrap: true,
),
),
],
),
),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
setState(() {
isProfile = false;
});
},
child: Text(
"View Previous Files",
style: TextStyle(
color: Color(0xff425B5B),
decoration:
TextDecoration.underline, // 👈 underline
decorationThickness: 1.5,
),
),
),
],
const SizedBox(height: 16),
],
),
)
: Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 10),
isLoading
? const Center(child: CircularProgressIndicator())
: filteredIncentiveData.isEmpty
? const Center(
child: Text("No incentive files found"),
)
: SizedBox(
height: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.height * 0.4
: 450,
child: ListView.builder(
// padding: const EdgeInsets.all(8),
itemCount: filteredIncentiveData.length,
itemBuilder: (context, index) {
final file = filteredIncentiveData[index];
const SizedBox(height: 20),
],
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
),
child: IncentiveFileRow(
fileName:
file['incentive_file_name'] ??
"Unknown",
date: file['incentive_month'] ?? "-",
onUpload: () {
print("Upload ${file['id']}");
final selectedId = file['id'];
if (profileData?['role_id'] != 2) ...[
Container(
padding: const EdgeInsets.all(6),
color: Color(0xffEDF6F5),
child: GestureDetector(
onTap: () {
print("Download - $latestId");
dynamic path;
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
}
apiService.getPdfDownload(
path,
selectedId,
);
},
),
);
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$latestId';
} else if (roleId != 1 && roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$latestId';
}
apiService.getPdfDownload(path, latestId);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text(
"Download Incentive file",
style: _dataSub,
),
],
),
),
),
],
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
setState(() {
isProfile = false;
});
},
child: Text(
"View Previous Files",
style: TextStyle(
color: Color(0xff425B5B),
decoration: TextDecoration
.underline, // 👈 underline
decorationThickness: 1.5,
),
),
),
],
const SizedBox(height: 16),
],
),
)
: Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 10),
isLoading
? const Center(child: CircularProgressIndicator())
: filteredIncentiveData.isEmpty
? const Center(
child: Text("No incentive files found"),
)
: SizedBox(
height: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.height * 0.4
: 450,
child: ListView.builder(
// padding: const EdgeInsets.all(8),
itemCount: filteredIncentiveData.length,
itemBuilder: (context, index) {
final file = filteredIncentiveData[index];
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
),
child: IncentiveFileRow(
fileName:
file['incentive_file_name'] ??
"Unknown",
date: file['incentive_month'] ?? "-",
onUpload: () {
print("Upload ${file['id']}");
final selectedId = file['id'];
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
} else if (roleId != 1 && roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
}
apiService.getPdfDownload(
path,
selectedId,
);
},
),
);
},
),
),
],
),
],
),
),
],
),
);
}

View File

@ -12,13 +12,10 @@ import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.d
import '../../../../core/config/env.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../../data/utils/validators.dart';
import '../../../layouts/main_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../themes/indicators/upload_doc_theme.dart';
import 'package:universal_html/html.dart' as html;
class Staff extends ConsumerStatefulWidget {

View File

@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/filter_btn.dart';
@ -56,9 +57,14 @@ class StaffListState extends ConsumerState<StaffList> {
// }
List<dynamic> get _paginatedData {
// Sort descending by id first
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final startIndex = (currentPage - 1) * itemsPerPage;
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
return sortedData.sublist(startIndex, endIndex);
}
void filterData(String query) {
@ -210,6 +216,9 @@ class StaffListState extends ConsumerState<StaffList> {
ExportBtn(
sheetName: "Staff",
fileName: "staff_list",
txt: !ResponsiveLayout.isMobile(context)
? true
: false,
data: filteredData,
headers: [
"id",
@ -253,6 +262,48 @@ class StaffListState extends ConsumerState<StaffList> {
),
),
SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 1,
child: Text('S.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Staff Name', style: _headerStyle),
),
Expanded(
flex: 3,
child: Text('Email', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Phone Number', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Employee Id', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
Expanded(child: _buildDataTable(context)),
],
),
@ -296,8 +347,7 @@ class StaffListState extends ConsumerState<StaffList> {
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final sortedData = [..._paginatedData];
return ListView.builder(
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
@ -316,24 +366,7 @@ class StaffListState extends ConsumerState<StaffList> {
}
Widget _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
Expanded(flex: 2, child: Text('Staff Name', style: _headerStyle)),
Expanded(flex: 3, child: Text('Email', style: _headerStyle)),
Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)),
Expanded(flex: 1, child: Text('Employee Id', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
],
),
);
return SizedBox.shrink();
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
@ -381,7 +414,7 @@ class StaffListState extends ConsumerState<StaffList> {
});
final response = apiService.updateStatus(
item['id'],
val ? "1" : "0",
val ? "0" : "1",
'staff',
);
print("Response - $response");

View File

@ -0,0 +1,215 @@
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'fileUploadService.dart';
// class ThemedUploadField extends FormField<String> {
// ThemedUploadField({
// Key? key,
// String? hintText,
// double? txtwidth,
// double? txtheight,
// Color? backgroundColor,
// Color? borderColor,
// Color? errorBorderColor,
// FormFieldValidator<String>? validator,
// void Function(String fileName, PlatformFile file)? onFileSelected,
// }) : super(
// key: key,
// validator: validator,
// builder: (FormFieldState<String> state) {
// final fileService = FileUploadService();
// String? selectedFileName;
//
// Future<void> _pickFile(BuildContext context) async {
// final error = await fileService.pickSingleFile(maxFileSizeInMB: 3);
// if (error != null) {
// ScaffoldMessenger.of(
// context,
// ).showSnackBar(SnackBar(content: Text(error)));
// state.didChange(null); // mark invalid
// } else if (fileService.singleFile != null) {
// selectedFileName = fileService.singleFile!.name;
// state.didChange(selectedFileName); // update form value
//
// onFileSelected?.call(
// fileService.singleFile!.name,
// fileService.singleFile!,
// );
// }
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container(
// width: txtwidth ?? MediaQuery.of(state.context).size.width,
// height: txtheight,
// child: InkWell(
// onTap: () => _pickFile(state.context),
// child: Container(
// padding: const EdgeInsets.symmetric(
// vertical: 10,
// horizontal: 15,
// ),
// decoration: BoxDecoration(
// color: backgroundColor ?? Colors.white,
// borderRadius: BorderRadius.circular(10),
// border: Border.all(
// color: state.hasError
// ? (errorBorderColor ?? Colors.red)
// : (borderColor ?? Colors.grey.shade50),
// ),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: Text(
// selectedFileName ?? hintText ?? "Upload Document",
// style: TextStyle(
// fontSize: 12,
// color: state.hasError
// ? Colors.red
// : Colors.black,
// ),
// overflow: TextOverflow.ellipsis,
// ),
// ),
// const Icon(
// Icons.file_upload_outlined,
// color: Colors.black,
// ),
// ],
// ),
// ),
// ),
// ),
// if (state.hasError)
// Padding(
// padding: const EdgeInsets.only(left: 8, top: 4),
// child: Text(
// state.errorText ?? "",
// style: const TextStyle(color: Colors.red, fontSize: 10),
// ),
// ),
// ],
// );
// },
// );
// }
class ThemedUploadField extends StatefulWidget {
final String? hintText;
final double? txtwidth;
final double? txtheight;
final Color? backgroundColor;
final Color? borderColor;
final Color? errorBorderColor;
final Function(String fileName, PlatformFile file)? onFileSelected;
FormFieldValidator<String>? validator;
ThemedUploadField({
super.key,
this.hintText,
this.txtwidth,
this.txtheight,
this.backgroundColor,
this.borderColor,
this.errorBorderColor,
this.onFileSelected,
this.validator,
});
@override
State<ThemedUploadField> createState() => _ThemedUploadFieldState();
}
class _ThemedUploadFieldState extends State<ThemedUploadField> {
final fileService = FileUploadService();
String? selectedFileName;
String? errorMessage;
Future<void> _pickFile() async {
final error = await fileService.pickSingleFile(maxFileSizeInMB: 3);
if (error != null) {
setState(() => errorMessage = error);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(error)));
} else if (fileService.singleFile != null) {
setState(() {
selectedFileName = fileService.singleFile!.name;
errorMessage = null;
});
widget.onFileSelected?.call(
fileService.singleFile!.name,
fileService.singleFile!,
);
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
height: widget.txtheight,
child: DecoratedBox(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
child: InkWell(
onTap: _pickFile,
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
decoration: BoxDecoration(
color: widget.backgroundColor ?? Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: errorMessage != null
? (widget.errorBorderColor ?? Colors.red)
: (widget.borderColor ?? Colors.grey.shade50),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
selectedFileName ??
widget.hintText ??
"Upload Document",
style: TextStyle(
fontSize: 12,
color: errorMessage != null
? Colors.red
: Colors.black,
),
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.file_upload_outlined, color: Colors.black),
],
),
),
),
),
),
if (errorMessage != null)
Padding(
padding: const EdgeInsets.only(left: 8, top: 4),
child: Text(
errorMessage!,
style: const TextStyle(color: Colors.red, fontSize: 10),
),
),
],
);
}
}

View File

@ -21,9 +21,10 @@ class ExportBtn extends HookWidget {
final String fileName;
final List<Map<String, dynamic>> data;
final List<String> headers; // dynamic headers
final bool? txt;
const ExportBtn({
super.key,
this.txt,
required this.sheetName,
required this.fileName,
required this.data,
@ -50,15 +51,17 @@ class ExportBtn extends HookWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Export',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
const SizedBox(width: 20),
?txt!
? Text(
'Export',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
)
: null,
?txt! ? SizedBox(width: 20) : null,
Icon(Icons.input_sharp, color: Colors.white),
// Image.asset("assets/miscellaneous/export", height: 15, width: 15),
],

View File

@ -0,0 +1,40 @@
import 'package:file_picker/file_picker.dart';
class FileUploadService {
FileUploadService._();
static final FileUploadService _instance = FileUploadService._();
factory FileUploadService() => _instance;
PlatformFile? singleFile;
static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
Future<String?> pickSingleFile({int maxFileSizeInMB = 5}) async {
final result = await FilePicker.platform.pickFiles(
allowMultiple: false,
withData: true,
type: FileType.custom,
allowedExtensions: allowedExtensions,
);
if (result != null && result.files.isNotEmpty) {
final file = result.files.first;
final ext = file.extension?.toLowerCase() ?? '';
final sizeInMB = file.size / (1024 * 1024);
if (!allowedExtensions.contains(ext)) {
return "Unsupported format: ${file.name}";
}
if (sizeInMB > maxFileSizeInMB) {
return "File too large (${file.name}). Max $maxFileSizeInMB MB allowed.";
}
singleFile = file;
}
return null; // success
}
void clearSingle() {
singleFile = null;
}
}

View File

@ -0,0 +1,147 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/main_layout.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
class SampleList extends StatefulWidget {
const SampleList({super.key});
@override
AgentListState createState() => AgentListState();
}
class SampleListState extends State<SampleList> {
int currentPage = 0;
int itemsPerPage = 10;
List<Map<String, dynamic>> dataVal = [];
final TextEditingController _searchController = TextEditingController();
@override
Widget build(BuildContext context) {
return MainLayout(
title: "Agent",
body: Container(
// color: Colors.yellow.shade50,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
// height: 30,
// color: Colors.red.shade50,
width: MediaQuery.of(context).size.width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(
Icons.arrow_left_sharp,
size: 35,
color: Color(0xFF425B5B),
),
Text(
"Agent",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
],
),
),
SizedBox(height: 10),
Expanded(
child: Container(
// color: Colors.green,
color: Colors.green.shade50,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Column(
children: [
Container(
// height: 40,
// color: Colors.pink,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ThemedSearchField(
hintText: 'Search',
backgroundColor: Color(0xFFF6F8F8),
controller: _searchController,
txtwidth: MediaQuery.of(context).size.width * 0.2,
),
Text("Home Page Content 2"),
Spacer(),
ExportBtn(),
SizedBox(width: 10),
GestureDetector(
onTap: () {
print('Export');
},
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Color(0xFF425B5B),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, color: Colors.white),
SizedBox(width: 10),
Text(
'Create New Agent',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
],
),
),
),
],
),
),
],
),
),
),
Container(
// height: 20,
width: MediaQuery.of(context).size.width,
// color: Colors.green.shade50,
child: PaginationControls(
currentPage: currentPage,
itemsPerPage: itemsPerPage,
totalItems: dataVal.length,
// activeColor: layoutColor, // your theme color
onPageChanged: (page) {
setState(() {
currentPage = page;
});
},
onItemsPerPageChanged: (items) {
setState(() {
itemsPerPage = items;
currentPage = 0;
});
},
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
class SingleFileUploadWidget extends StatefulWidget {
const SingleFileUploadWidget({super.key});
@override
State<SingleFileUploadWidget> createState() => SingleFileUploadWidgetState();
}
class SingleFileUploadWidgetState extends State<SingleFileUploadWidget> {
PlatformFile? selectedFile;
Future<void> pickFile() async {
final result = await FilePicker.platform.pickFiles(
allowMultiple: false,
withData: true,
type: FileType.custom,
allowedExtensions: ['pdf', 'png', 'jpg', 'jpeg', 'heic'],
);
if (result != null && result.files.isNotEmpty) {
setState(() {
selectedFile = result.files.first;
});
}
}
@override
Widget build(BuildContext context) {
return InkWell(
onTap: pickFile,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE26728)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
selectedFile != null ? selectedFile!.name : "Upload Document",
style: const TextStyle(
fontSize: 14,
color: Colors.black,
overflow: TextOverflow.ellipsis,
),
),
),
const Icon(
Icons.file_upload_outlined,
color: Color(0xFFE26728),
size: 22,
),
],
),
),
);
}
}

View File

@ -1,157 +1,204 @@
import 'dart:html' as html;
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class ThemedUploadField extends FormField<String> {
ThemedUploadField({
super.key,
String? hintText,
double? txtwidth,
double? txtheight,
Color? backgroundColor,
Color? borderColor,
Color? errorBorderColor,
FormFieldValidator<String>? validator,
Function(String fileName, dynamic file)? onFileSelected,
}) : super(
validator: validator,
builder: (FormFieldState<String> state) {
return _ThemedUploadFieldBody(
hintText: hintText,
txtwidth: txtwidth,
txtheight: txtheight,
backgroundColor: backgroundColor,
borderColor: borderColor,
errorBorderColor: errorBorderColor,
onFileSelected: (fileName, file) {
state.didChange(fileName); // update validation state
onFileSelected?.call(fileName, file);
},
errorText: state.errorText,
);
},
);
}
class _ThemedUploadFieldBody extends StatefulWidget {
const _ThemedUploadFieldBody({
this.hintText,
this.txtwidth,
this.txtheight,
this.backgroundColor,
this.borderColor,
this.errorBorderColor,
this.onFileSelected,
this.errorText,
});
final String? hintText;
final double? txtwidth;
final double? txtheight;
final Color? backgroundColor;
final Color? borderColor;
final Color? errorBorderColor;
final Function(String fileName, dynamic file)? onFileSelected;
final String? errorText;
@override
State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
}
class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
String? selectedFileName;
void pickPDFWeb() {
final uploadInput = html.FileUploadInputElement();
uploadInput.accept = '.pdf';
uploadInput.click();
uploadInput.onChange.listen((e) {
final file = uploadInput.files!.first;
// Ensure PDF type
if (!file.type.contains("pdf")) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
return;
}
// Size check (3 MB)
const maxFileSize = 3 * 1024 * 1024;
if (file.size > maxFileSize) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
return;
}
setState(() => selectedFileName = file.name);
widget.onFileSelected?.call(file.name, file);
});
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
height: widget.txtheight,
child: DecoratedBox(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
child: InkWell(
onTap: pickPDFWeb,
borderRadius: BorderRadius.circular(10),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
decoration: BoxDecoration(
color: widget.backgroundColor ?? Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: widget.errorText != null
? (widget.errorBorderColor ?? Colors.red)
: (widget.borderColor ?? Colors.grey.shade50),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
selectedFileName ??
widget.hintText ??
"Upload Documents",
style: TextStyle(
fontSize: 12,
color: widget.errorText != null
? Colors.red
: Colors.black,
),
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.file_upload_outlined, color: Colors.black),
],
),
),
),
),
),
// if (widget.errorText != null)
// Padding(
// padding: const EdgeInsets.only(left: 8, top: 4),
// child: Text(
// widget.errorText!,
// style: const TextStyle(color: Colors.red, fontSize: 10),
// ),
// ),
],
);
}
}
// import 'dart:html' as html;
// import 'package:file_picker/file_picker.dart';
// import 'package:flutter/foundation.dart';
// import 'package:flutter/material.dart';
// import 'package:google_fonts/google_fonts.dart';
// import 'package:http/http.dart' as http;
//
// import 'package:flutter/material.dart';
// import 'package:file_picker/file_picker.dart';
// import 'package:http/http.dart' as http;
// import 'package:http_parser/http_parser.dart';
// import 'package:flutter/foundation.dart' show kIsWeb;
//
// class ThemedUploadField extends FormField<String> {
// ThemedUploadField({
// super.key,
// String? hintText,
// double? txtwidth,
// double? txtheight,
// Color? backgroundColor,
// Color? borderColor,
// Color? errorBorderColor,
// FormFieldValidator<String>? validator,
// Function(String fileName, dynamic file)? onFileSelected,
// }) : super(
// validator: validator,
// builder: (FormFieldState<String> state) {
// return _ThemedUploadFieldBody(
// hintText: hintText,
// txtwidth: txtwidth,
// txtheight: txtheight,
// backgroundColor: backgroundColor,
// borderColor: borderColor,
// errorBorderColor: errorBorderColor,
// onFileSelected: (fileName, file) {
// state.didChange(fileName); // update validation state
// onFileSelected?.call(fileName, file);
// },
// errorText: state.errorText,
// );
// },
// );
// }
//
// class _ThemedUploadFieldBody extends StatefulWidget {
// const _ThemedUploadFieldBody({
// this.hintText,
// this.txtwidth,
// this.txtheight,
// this.backgroundColor,
// this.borderColor,
// this.errorBorderColor,
// this.onFileSelected,
// this.errorText,
// });
//
// final String? hintText;
// final double? txtwidth;
// final double? txtheight;
// final Color? backgroundColor;
// final Color? borderColor;
// final Color? errorBorderColor;
// final Function(String fileName, dynamic file)? onFileSelected;
// final String? errorText;
//
// @override
// State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
// }
//
// class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
// String? selectedFileName;
//
// // void pickPDFWeb() {
// // final uploadInput = html.FileUploadInputElement();
// // uploadInput.accept = '.pdf';
// // uploadInput.click();
// //
// // uploadInput.onChange.listen((e) {
// // final file = uploadInput.files!.first;
// //
// // // Ensure PDF type
// // if (!file.type.contains("pdf")) {
// // ScaffoldMessenger.of(
// // context,
// // ).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
// // return;
// // }
// //
// // // Size check (3 MB)
// // const maxFileSize = 3 * 1024 * 1024;
// // if (file.size > maxFileSize) {
// // ScaffoldMessenger.of(
// // context,
// // ).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
// // return;
// // }
// //
// // setState(() => selectedFileName = file.name);
// // widget.onFileSelected?.call(file.name, file);
// // });
// // }
//
// Future<void> pickPDF() async {
// try {
// final result = await FilePicker.platform.pickFiles(
// type: FileType.custom,
// allowedExtensions: ['pdf'],
// withData: true,
// );
//
// if (result != null && result.files.isNotEmpty) {
// final file = result.files.first; // PlatformFile
//
// if (file.extension?.toLowerCase() != "pdf") {
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text("Only PDF files allowed")),
// );
// return;
// }
//
// if (file.size > 3 * 1024 * 1024) {
// ScaffoldMessenger.of(
// context,
// ).showSnackBar(const SnackBar(content: Text("File exceeds 3MB")));
// return;
// }
//
// setState(() => selectedFileName = file.name);
//
// // Always pass PlatformFile
// widget.onFileSelected?.call(file.name, file);
// }
// } catch (e, st) {
// debugPrint("File picker error: $e\n$st"); // 👈 safer logging
// // ScaffoldMessenger.of(
// // context,
// // ).showSnackBar(const SnackBar(content: Text("Error picking file")));
// }
// }
//
// @override
// Widget build(BuildContext context) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container(
// width: widget.txtwidth ?? MediaQuery.of(context).size.width,
// height: widget.txtheight,
// child: DecoratedBox(
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
// child: InkWell(
// onTap: pickPDF,
// borderRadius: BorderRadius.circular(10),
// child: Container(
// padding: const EdgeInsets.symmetric(
// vertical: 10,
// horizontal: 15,
// ),
// decoration: BoxDecoration(
// color: widget.backgroundColor ?? Colors.white,
// borderRadius: BorderRadius.circular(10),
// border: Border.all(
// color: widget.errorText != null
// ? (widget.errorBorderColor ?? Colors.red)
// : (widget.borderColor ?? Colors.grey.shade50),
// width: 1,
// ),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: Text(
// selectedFileName ??
// widget.hintText ??
// "Upload Documents",
// style: TextStyle(
// fontSize: 12,
// color: widget.errorText != null
// ? Colors.red
// : Colors.black,
// ),
// overflow: TextOverflow.ellipsis,
// ),
// ),
// const Icon(Icons.file_upload_outlined, color: Colors.black),
// ],
// ),
// ),
// ),
// ),
// ),
// // if (widget.errorText != null)
// // Padding(
// // padding: const EdgeInsets.only(left: 8, top: 4),
// // child: Text(
// // widget.errorText!,
// // style: const TextStyle(color: Colors.red, fontSize: 10),
// // ),
// // ),
// ],
// );
// }
// }

View File

@ -72,7 +72,7 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
if (key == 'Reports') ...[
GestureDetector(
onTap: () {
print("Tappped Agent");
context.go(AppRoutes.enquiryLst);
},
child: const Text(
"Enquiries",
@ -175,7 +175,9 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
DrawerContentWrapper(
child: IconButton(
onPressed: () {},
onPressed: () {
context.go(AppRoutes.tabEnquiry);
},
icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black),
),
),

View File

@ -20,9 +20,12 @@ class _MobileTabsState extends State<MobileTabs> {
widget.onTabChanged(index);
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profile"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Settings"),
BottomNavigationBarItem(
icon: Icon(Icons.event_note_sharp),
label: "Enquiry",
),
],
);
}

View File

@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import connectivity_plus
import file_picker
import firebase_app_check
import firebase_auth
import firebase_core
@ -21,6 +22,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))

View File

@ -201,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
crypto:
dependency: transitive
description:
@ -297,6 +305,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
url: "https://pub.dev"
source: hosted
version: "10.3.2"
firebase_app_check:
dependency: "direct main"
description:

View File

@ -67,6 +67,7 @@ dependencies:
open_file: ^3.5.10
excel: ^4.0.6
dropdown_search: ^6.0.2
file_picker: ^10.3.2
dev_dependencies:
flutter_test: