UI_FILE_UPLOAD_CHNGS UI_ENQUIRY
This commit is contained in:
parent
fa484bc00e
commit
e2c02c4e12
File diff suppressed because one or more lines are too long
@ -1,12 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:nhance_partner/core/routing/routes.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/UserManagement/Profile/profile_web.dart';
|
||||||
import 'package:nhance_partner/presentation/screens/claims/claimsList.dart';
|
import 'package:nhance_partner/presentation/screens/claims/claimsList.dart';
|
||||||
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
|
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
|
||||||
import '../../data/services/auth_service.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/agent.dart';
|
||||||
import '../../presentation/screens/UserManagement/Agent/agentList.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/staff.dart';
|
||||||
import '../../presentation/screens/UserManagement/Staff/staffList.dart';
|
import '../../presentation/screens/UserManagement/Staff/staffList.dart';
|
||||||
import '../../presentation/screens/home/home_screen.dart';
|
import '../../presentation/screens/home/home_screen.dart';
|
||||||
@ -35,7 +38,7 @@ final GoRouter appRouter = GoRouter(
|
|||||||
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: AppRoutes.profile,
|
path: AppRoutes.profile,
|
||||||
builder: (context, state) => const ProfilePopUp(),
|
builder: (context, state) => const Profile(),
|
||||||
),
|
),
|
||||||
|
|
||||||
GoRoute(
|
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 {
|
redirect: (context, state) async {
|
||||||
final loggedIn = await AuthService.isLoggedIn();
|
final loggedIn = await AuthService.isLoggedIn();
|
||||||
|
|||||||
@ -8,4 +8,6 @@ class AppRoutes {
|
|||||||
static const agent = '/agent/:id';
|
static const agent = '/agent/:id';
|
||||||
static const String staffLst = '/staffLst';
|
static const String staffLst = '/staffLst';
|
||||||
static const staff = '/staff/:id';
|
static const staff = '/staff/:id';
|
||||||
|
static const String enquiryLst = '/enquiryLst';
|
||||||
|
static const String tabEnquiry = '/tabEnquiry';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:nhance_partner/data/services/auth_service.dart';
|
import 'package:nhance_partner/data/services/auth_service.dart';
|
||||||
@ -76,147 +78,7 @@ class ApiService {
|
|||||||
);
|
);
|
||||||
return _handleResponse(response);
|
return _handleResponse(response);
|
||||||
}
|
}
|
||||||
|
// --------------------------- COMMON FILES __________________________________________
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
|
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
|
||||||
if (response.statusCode == 200) {
|
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 {
|
Future<Map<String, dynamic>> updateStatus(id, status, role) async {
|
||||||
final Map<String, dynamic> data = {
|
final Map<String, dynamic> data = {
|
||||||
"id": int.parse(id),
|
"id": int.parse(id),
|
||||||
@ -324,4 +219,232 @@ class ApiService {
|
|||||||
final response = await _makePostRequestJson(url, data, headers);
|
final response = await _makePostRequestJson(url, data, headers);
|
||||||
return response;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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/manager_provider.dart';
|
||||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -31,13 +32,15 @@ Future<void> main() async {
|
|||||||
// prefs.clear();
|
// prefs.clear();
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: const FirebaseOptions(
|
options: const FirebaseOptions(
|
||||||
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
|
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
|
||||||
authDomain: "nhance-ee8d1.firebaseapp.com",
|
authDomain: "nhance-ee8d1.firebaseapp.com",
|
||||||
projectId: "nhance-ee8d1",
|
projectId: "nhance-ee8d1",
|
||||||
storageBucket: "nhance-ee8d1.firebasestorage.app",
|
storageBucket: "nhance-ee8d1.firebasestorage.app",
|
||||||
messagingSenderId: "1084115316849",
|
messagingSenderId: "1084115316849",
|
||||||
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0"));
|
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0",
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await Firebase.initializeApp();
|
await Firebase.initializeApp();
|
||||||
await FirebaseAppCheck.instance.activate(
|
await FirebaseAppCheck.instance.activate(
|
||||||
@ -77,7 +80,6 @@ class MyApp extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MyAppState extends ConsumerState<MyApp> {
|
class _MyAppState extends ConsumerState<MyApp> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -91,10 +93,14 @@ class _MyAppState extends ConsumerState<MyApp> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'Clean Architecture App',
|
title: 'Nhance Partner',
|
||||||
theme: ThemeData(primarySwatch: Colors.blue),
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
textTheme: GoogleFonts.interTextTheme(
|
||||||
|
Theme.of(context).textTheme, // base on default styles
|
||||||
|
),
|
||||||
|
),
|
||||||
routerConfig: appRouter,
|
routerConfig: appRouter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,8 +37,12 @@ class MainLayout extends StatelessWidget {
|
|||||||
bottomNavigationBar: MobileTabs(
|
bottomNavigationBar: MobileTabs(
|
||||||
onTabChanged: (index) {
|
onTabChanged: (index) {
|
||||||
debugPrint("Mobile tab changed to $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);
|
context.go(AppRoutes.profile);
|
||||||
|
} else if (index == 2) {
|
||||||
|
context.go(AppRoutes.tabEnquiry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
729
lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart
Normal file
729
lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
436
lib/presentation/screens/Enquiry/enquiry/policy_tab.dart
Normal file
436
lib/presentation/screens/Enquiry/enquiry/policy_tab.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
745
lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart
Normal file
745
lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
127
lib/presentation/screens/Enquiry/enquiry/tabs.dart
Normal file
127
lib/presentation/screens/Enquiry/enquiry/tabs.dart
Normal 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],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +1,26 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.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/routing/routes.dart';
|
||||||
import '../../../../core/services/api_service.dart';
|
import '../../../../core/services/api_service.dart';
|
||||||
import '../../../../data/utils/Pagination.dart';
|
import '../../../../data/utils/Pagination.dart';
|
||||||
import '../../layouts/main_layout.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/export_btn.dart';
|
||||||
import '../../themes/indicators/search_field_theme.dart';
|
import '../../themes/indicators/search_field_theme.dart';
|
||||||
import '../../widgets/custom_action_popup.dart';
|
import '../../widgets/custom_action_popup.dart';
|
||||||
|
|
||||||
class enquiryList extends StatefulWidget {
|
class EnquiryList extends ConsumerStatefulWidget {
|
||||||
const enquiryList({super.key});
|
const EnquiryList({super.key});
|
||||||
@override
|
@override
|
||||||
enquiryListState createState() => enquiryListState();
|
ConsumerState<EnquiryList> createState() => EnquiryListState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class enquiryListState extends State<enquiryList> {
|
class EnquiryListState extends ConsumerState<EnquiryList> {
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
int itemsPerPage = 10;
|
int itemsPerPage = 10;
|
||||||
late ApiService apiService;
|
late ApiService apiService;
|
||||||
@ -31,70 +36,102 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
apiService = ApiService();
|
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 {
|
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 startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||||
return filteredData.sublist(startIndex, endIndex);
|
|
||||||
|
return sortedData.sublist(startIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterData(String query) {
|
void filterData(String query) {
|
||||||
print("FilterDAta - $query");
|
print("FilterDAta - $query");
|
||||||
setState(() {
|
setState(() {
|
||||||
filteredData = getStaffData.where((item) {
|
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(),
|
query.toLowerCase(),
|
||||||
) ||
|
) ||
|
||||||
(item['email'] ?? '').toLowerCase().contains(query.toLowerCase()) ||
|
(item['reg_no'] ?? '').toLowerCase().contains(
|
||||||
(item['mobile'] ?? '').toLowerCase().contains(
|
|
||||||
query.toLowerCase(),
|
query.toLowerCase(),
|
||||||
) ||
|
) ||
|
||||||
(item['address'] ?? '').toLowerCase().contains(
|
(item['insurer_name'] ?? '').toLowerCase().contains(
|
||||||
query.toLowerCase(),
|
query.toLowerCase(),
|
||||||
) ||
|
) ||
|
||||||
(item['emp_id'] ?? item['agent_code'] ?? '').toLowerCase().contains(
|
(item['status'] ?? '').toLowerCase().contains(
|
||||||
query.toLowerCase(),
|
query.toLowerCase(),
|
||||||
) ||
|
) ||
|
||||||
isActiveStatus.contains(query.toLowerCase());
|
(item['remarks'] ?? '').toLowerCase().contains(query.toLowerCase());
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final TextEditingController _searchStaffController = TextEditingController();
|
final TextEditingController _searchStaffController = TextEditingController();
|
||||||
|
|
||||||
// Future<void> getStaffList() async {
|
String _formatDate(String rawDate) {
|
||||||
// print('getClaimList called');
|
try {
|
||||||
// setState(() {
|
final dateTime = DateTime.parse(rawDate);
|
||||||
// isLoading = true;
|
return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format
|
||||||
// });
|
} catch (e) {
|
||||||
//
|
return rawDate; // fallback if parsing fails
|
||||||
// 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;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||||||
return [
|
return [
|
||||||
@ -103,7 +140,7 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
print('EDITStaff - ${data['id']}');
|
print('EDITStaff - ${data['id']}');
|
||||||
dynamic id = data['id'];
|
dynamic id = data['id'];
|
||||||
context.go('/staff/$id');
|
// context.go('/staff/$id');
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -145,7 +182,7 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
color: Color(0xFF425B5B),
|
color: Color(0xFF425B5B),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Enquries",
|
"Enquiries",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -179,29 +216,34 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
backgroundColor: Color(0xFFF6F8F8),
|
backgroundColor: Color(0xFFF6F8F8),
|
||||||
onChanged: filterData,
|
onChanged: filterData,
|
||||||
controller: _searchStaffController,
|
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(),
|
Spacer(),
|
||||||
|
|
||||||
ExportBtn(
|
ExportBtn(
|
||||||
sheetName: "Staff",
|
sheetName: "Enquiry",
|
||||||
fileName: "staff_list",
|
fileName: "enquiry_list",
|
||||||
data: filteredData,
|
data: filteredData,
|
||||||
|
txt: !ResponsiveLayout.isMobile(context)
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
headers: [
|
headers: [
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"created_on",
|
||||||
"email",
|
"reg_no",
|
||||||
"mobile",
|
"insurer_name",
|
||||||
"emp_id",
|
"status",
|
||||||
"is_active",
|
"remarks",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
print('Export');
|
// print('Export');
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
@ -214,22 +256,24 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(Icons.add, color: Colors.white),
|
Icon(Icons.add, color: Colors.white),
|
||||||
|
|
||||||
SizedBox(width: 10),
|
if (!ResponsiveLayout.isMobile(context)) ...[
|
||||||
|
SizedBox(width: 10),
|
||||||
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// context.go(AppRoutes.agent / create);
|
// context.go(AppRoutes.agent / create);
|
||||||
context.go('/staff/create');
|
context.go('/staff/create');
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Create New Staff',
|
'Create New Staff',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -238,6 +282,51 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
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)),
|
Expanded(child: _buildDataTable(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -281,47 +370,31 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final sortedData = [...filteredData]
|
final sortedData = [..._paginatedData];
|
||||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
itemCount: ResponsiveLayout.isMobile(context)
|
||||||
itemCount: sortedData.length + 1, // +1 for header, +1 for pagination
|
? sortedData
|
||||||
|
.length // only cards for mobile
|
||||||
|
: sortedData.length + 1, // +1 for header in desktop
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == 0) return _buildHeader();
|
if (!ResponsiveLayout.isMobile(context) && index == 0) {
|
||||||
// if (index == dataVal.length + 1)
|
return _buildHeader();
|
||||||
// return _buildPagination(context);
|
}
|
||||||
final startIndex = ((currentPage - 1) * itemsPerPage);
|
|
||||||
// final item = filteredData[index - 1];
|
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
final item = sortedData[index - 1];
|
final item =
|
||||||
|
sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)];
|
||||||
final sno = startIndex + index;
|
final sno = startIndex + index;
|
||||||
return _buildDataRow(item, sno);
|
|
||||||
|
return !ResponsiveLayout.isMobile(context)
|
||||||
|
? _buildDataRow(item, sno)
|
||||||
|
: _buildDataCard(item, sno);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeader() {
|
Widget _buildHeader() {
|
||||||
return Container(
|
return SizedBox.shrink();
|
||||||
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||||
@ -339,42 +412,38 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
||||||
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 2,
|
||||||
child: Text(item['email'] ?? '-', style: _dataBold),
|
child: Text(
|
||||||
|
_formatDate(item['created_on']) ?? '-',
|
||||||
|
style: _dataBold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(item['reg_no'] ?? '-', style: _dataBold),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Text(item['mobile'] ?? '-', style: _dataBold),
|
child: Text(
|
||||||
),
|
item['insurer_name'] ?? '-',
|
||||||
Expanded(
|
style: _dataBold,
|
||||||
flex: 1,
|
softWrap: true,
|
||||||
child: Text(item['emp_id'] ?? '-', style: _dataBold),
|
maxLines: 3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Text(item['status'] ?? '-', style: _dataBold),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Transform.scale(
|
child: Text(
|
||||||
scale: 0.6, // reduce size (0.7–0.9 works well)
|
item['remarks'] ?? '-',
|
||||||
child: Switch(
|
style: _dataBold,
|
||||||
value: item['is_active'] == "1",
|
softWrap: true,
|
||||||
onChanged: (val) {
|
maxLines: 3,
|
||||||
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
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -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(
|
static final _dataBold = TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
||||||
@ -432,4 +618,15 @@ class enquiryListState extends State<enquiryList> {
|
|||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontWeight: FontWeight.bold,
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
// import 'dart:io' as html;
|
// import 'dart:io' as html;
|
||||||
import 'dart:typed_data'; // Import for Uint8List
|
import 'dart:typed_data'; // Import for Uint8List
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -17,9 +19,11 @@ import '../../../../data/utils/validators.dart';
|
|||||||
import '../../../layouts/main_layout.dart';
|
import '../../../layouts/main_layout.dart';
|
||||||
import '../../../providers/manager_provider.dart';
|
import '../../../providers/manager_provider.dart';
|
||||||
import '../../../providers/user_provider.dart';
|
import '../../../providers/user_provider.dart';
|
||||||
|
import '../../../themes/indicators/customizd_file_upload.dart';
|
||||||
import '../../../themes/indicators/export_btn.dart';
|
import '../../../themes/indicators/export_btn.dart';
|
||||||
import '../../../themes/indicators/search_field_theme.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;
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
class Agent extends ConsumerStatefulWidget {
|
class Agent extends ConsumerStatefulWidget {
|
||||||
@ -34,10 +38,12 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
late ApiService apiService;
|
late ApiService apiService;
|
||||||
List<String> tabHeader = ['name', 'email', 'mobile', 'code', 'address'];
|
List<String> tabHeader = ['name', 'email', 'mobile', 'code', 'address'];
|
||||||
late String isActive = "1";
|
late String isActive = "1";
|
||||||
html.File? passportFile;
|
// html.File? docUploadedFile;
|
||||||
|
// PlatformFile? passportFile;
|
||||||
String? selectedFileNames;
|
String? selectedFileNames;
|
||||||
String? passportFileUrlFromApi;
|
String? passportFileUrlFromApi;
|
||||||
String? selectedId;
|
String? selectedId;
|
||||||
|
PlatformFile? docUploadedFile;
|
||||||
|
|
||||||
Map<String, TextEditingController> controllers = {};
|
Map<String, TextEditingController> controllers = {};
|
||||||
String? _token;
|
String? _token;
|
||||||
@ -96,12 +102,15 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
|
|
||||||
String? apiDocPath = data["certificate_file_name"];
|
String? apiDocPath = data["certificate_file_name"];
|
||||||
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
||||||
|
print('apiDocPath - $apiDocPath');
|
||||||
selectedFileNames = apiDocPath.split('/').last;
|
selectedFileNames = apiDocPath.split('/').last;
|
||||||
|
print('selectedFileNames - $selectedFileNames');
|
||||||
passportFileUrlFromApi = apiDocPath;
|
passportFileUrlFromApi = apiDocPath;
|
||||||
passportFile = null;
|
print('passportFileUrlFromApi - $passportFileUrlFromApi');
|
||||||
|
docUploadedFile = null;
|
||||||
} else {
|
} else {
|
||||||
selectedFileNames = null;
|
selectedFileNames = null;
|
||||||
passportFile = null;
|
docUploadedFile = null;
|
||||||
passportFileUrlFromApi = null;
|
passportFileUrlFromApi = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -155,33 +164,64 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
|
|
||||||
print("USerDAta - $userData");
|
print("USerDAta - $userData");
|
||||||
|
|
||||||
|
// userData.forEach((key, value) {
|
||||||
|
// request.fields[key] = value.toString();
|
||||||
|
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||||
|
// });
|
||||||
|
|
||||||
userData.forEach((key, value) {
|
userData.forEach((key, value) {
|
||||||
request.fields[key] = value.toString();
|
if (key != 'certificate_file_name') {
|
||||||
print("✅ Encoded travel_details2: ${request.fields[key]}");
|
request.fields[key] = value.toString();
|
||||||
|
print("✅ Encoded $key: ${request.fields[key]}");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach file if selected
|
// 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 {
|
try {
|
||||||
final reader = html.FileReader();
|
if (docUploadedFile!.bytes != null) {
|
||||||
reader.readAsArrayBuffer(passportFile!);
|
final multipartFile = http.MultipartFile.fromBytes(
|
||||||
await reader.onLoad.first;
|
'certificate_file_name',
|
||||||
|
docUploadedFile!.bytes!,
|
||||||
final data = reader.result as Uint8List;
|
filename: docUploadedFile!.name,
|
||||||
|
);
|
||||||
final multipartFile = http.MultipartFile.fromBytes(
|
request.files.add(multipartFile);
|
||||||
'certificate_file_name',
|
} else if (docUploadedFile!.path != null) {
|
||||||
data,
|
final multipartFile = await http.MultipartFile.fromPath(
|
||||||
filename: passportFile!.name,
|
'certificate_file_name',
|
||||||
);
|
docUploadedFile!.path!,
|
||||||
|
filename: docUploadedFile!.name,
|
||||||
request.files.add(multipartFile);
|
);
|
||||||
print("📎 File attached: ${passportFile!.name}");
|
request.files.add(multipartFile);
|
||||||
|
}
|
||||||
|
print("📎 File attached: ${docUploadedFile!.name}");
|
||||||
} catch (e) {
|
} 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}");
|
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
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (var controller in controllers.values) {
|
for (var controller in controllers.values) {
|
||||||
@ -451,7 +522,9 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
children: [
|
children: [
|
||||||
Text("Upload Certificate", style: _textStyle),
|
Text("Upload Certificate", style: _textStyle),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
ThemedUploadField(
|
ThemedUploadField(
|
||||||
@ -460,58 +533,25 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
onFileSelected: (fileName, file) {
|
onFileSelected: (fileName, file) {
|
||||||
print("Picked file: $fileName (${file.size} bytes)");
|
print("Picked file: $fileName (${file.size} bytes)");
|
||||||
setState(() {
|
setState(() {
|
||||||
passportFile = file;
|
docUploadedFile = file;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
|
|
||||||
if (passportFile != null || passportFileUrlFromApi != null)
|
if (docUploadedFile != null || passportFileUrlFromApi != null)
|
||||||
// Centers the text
|
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () => apiService.downloadFile(
|
||||||
print('DOWNLOAD - $passportFile');
|
apiUrl:
|
||||||
|
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId',
|
||||||
if (passportFile != null) {
|
apiId: null,
|
||||||
try {
|
localFile: docUploadedFile,
|
||||||
// Create a blob from the response body
|
fileName: selectedFileNames,
|
||||||
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.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(5),
|
padding: const EdgeInsets.all(5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -519,9 +559,7 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
color: Colors.green.shade300,
|
color: Colors.green.shade300,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
children: const [
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
Text(
|
||||||
"Download",
|
"Download",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -529,9 +567,6 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
fontWeight: FontWeight.w200,
|
fontWeight: FontWeight.w200,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign
|
|
||||||
.end, // Ensures text is centered within the Text widget
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Icon(Icons.download, size: 13, color: Colors.white),
|
Icon(Icons.download, size: 13, color: Colors.white),
|
||||||
@ -544,23 +579,146 @@ class AgentState extends ConsumerState<Agent> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildIncentiveFile() {
|
// Column(
|
||||||
return Row(
|
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// children: [
|
||||||
children: [
|
// ThemedUploadField(
|
||||||
Text("Incentive File", style: _textStyle),
|
// hintText: selectedFileNames ?? "Upload Document",
|
||||||
SizedBox(width: 10),
|
// txtwidth: MediaQuery.of(context).size.width * 0.26,
|
||||||
ThemedUploadField(
|
//
|
||||||
hintText: "Upload Document",
|
// onFileSelected: (fileName, file) {
|
||||||
txtwidth: MediaQuery.of(context).size.width * 0.26,
|
// // print("File picked: ${fileName}");
|
||||||
onFileSelected: (fileName, file) {
|
// // print("Size: ${file.size}");
|
||||||
print("Picked file: $fileName (${file.size} bytes)");
|
// // 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),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart';
|
// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart';
|
||||||
import 'dart:typed_data'; // Import for Uint8List
|
import 'dart:typed_data'; // Import for Uint8List
|
||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:google_fonts/google_fonts.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/services/auth_service.dart';
|
||||||
import '../../../../data/utils/validators.dart';
|
import '../../../../data/utils/validators.dart';
|
||||||
import '../../../providers/manager_provider.dart';
|
import '../../../providers/manager_provider.dart';
|
||||||
|
import '../../../themes/indicators/customizd_file_upload.dart';
|
||||||
import '../../../themes/indicators/date_field_theme.dart';
|
import '../../../themes/indicators/date_field_theme.dart';
|
||||||
import '../../../themes/indicators/input_field_decoration.dart';
|
import '../../../themes/indicators/input_field_decoration.dart';
|
||||||
import '../../../themes/indicators/month_field_theme.dart';
|
import '../../../themes/indicators/month_field_theme.dart';
|
||||||
import '../../../themes/indicators/search_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
|
/// function to open the modal
|
||||||
void showUploadIncentiveModal(BuildContext context) {
|
void showUploadIncentiveModal(BuildContext context) {
|
||||||
@ -50,7 +52,8 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
List<Map<String, dynamic>> filteredIncentiveData = [];
|
List<Map<String, dynamic>> filteredIncentiveData = [];
|
||||||
Map<String, TextEditingController> controllers = {};
|
Map<String, TextEditingController> controllers = {};
|
||||||
|
|
||||||
html.File? passportFile;
|
// html.File? passportFile;
|
||||||
|
PlatformFile? passportFile;
|
||||||
String? selectedFileNames;
|
String? selectedFileNames;
|
||||||
|
|
||||||
List<String> tabHeader = ['name', 'agentId', 'date', 'search'];
|
List<String> tabHeader = ['name', 'agentId', 'date', 'search'];
|
||||||
@ -199,11 +202,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleSave() async {
|
Future<void> handleSave() async {
|
||||||
|
if (!_formKey.currentState!.validate()) return;
|
||||||
print("Handl1");
|
print("Handl1");
|
||||||
final dataSet = dataDetails();
|
final dataSet = dataDetails();
|
||||||
print("dataSetAgent - $dataSet");
|
print("dataSetAgent - $dataSet");
|
||||||
print("Handl13f");
|
print("Handl13f");
|
||||||
if (!_formKey.currentState!.validate()) return;
|
|
||||||
if (passportFile == null) {
|
if (passportFile == null) {
|
||||||
print("❌ No file selected!");
|
print("❌ No file selected!");
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@ -233,35 +237,65 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
|
|
||||||
print("USerDAta - $dataSet");
|
print("USerDAta - $dataSet");
|
||||||
|
|
||||||
|
// dataSet.forEach((key, value) {
|
||||||
|
// request.fields[key] = value.toString();
|
||||||
|
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||||
|
// });
|
||||||
|
|
||||||
dataSet.forEach((key, value) {
|
dataSet.forEach((key, value) {
|
||||||
request.fields[key] = value.toString();
|
if (key != 'incentive_file_name') {
|
||||||
print("✅ Encoded travel_details2: ${request.fields[key]}");
|
request.fields[key] = value.toString();
|
||||||
|
print("✅ Encoded $key: ${request.fields[key]}");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach file if selected
|
|
||||||
if (passportFile != null) {
|
if (passportFile != null) {
|
||||||
try {
|
try {
|
||||||
final reader = html.FileReader();
|
if (passportFile!.bytes != null) {
|
||||||
reader.readAsArrayBuffer(passportFile!);
|
final multipartFile = http.MultipartFile.fromBytes(
|
||||||
await reader.onLoad.first;
|
'incentive_file_name',
|
||||||
|
passportFile!.bytes!,
|
||||||
final data = reader.result as Uint8List;
|
filename: passportFile!.name,
|
||||||
|
);
|
||||||
final multipartFile = http.MultipartFile.fromBytes(
|
request.files.add(multipartFile);
|
||||||
'incentive_file_name',
|
} else if (passportFile!.path != null) {
|
||||||
data,
|
final multipartFile = await http.MultipartFile.fromPath(
|
||||||
filename: passportFile!.name,
|
'certificate_file_name',
|
||||||
);
|
passportFile!.path!,
|
||||||
|
filename: passportFile!.name,
|
||||||
request.files.add(multipartFile);
|
);
|
||||||
|
request.files.add(multipartFile);
|
||||||
|
}
|
||||||
print("📎 File attached: ${passportFile!.name}");
|
print("📎 File attached: ${passportFile!.name}");
|
||||||
} catch (e) {
|
} 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}");
|
print("🚀 Sending request with fields: ${request.fields}");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -276,7 +310,7 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
|
|
||||||
print("📨 Response: ${response.body}");
|
print("📨 Response: ${response.body}");
|
||||||
|
|
||||||
final data = dataDetails(); // this is a Map<String, dynamic>
|
final data = dataDetails();
|
||||||
final agentId = data["agent_id"];
|
final agentId = data["agent_id"];
|
||||||
|
|
||||||
getAgentIncenctiveFileList(agentId);
|
getAgentIncenctiveFileList(agentId);
|
||||||
@ -492,12 +526,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
// value,
|
// value,
|
||||||
// "Upload Document",
|
// "Upload Document",
|
||||||
// ),
|
// ),
|
||||||
validator: (value) {
|
// validator: (value) {
|
||||||
if (passportFile == null) {
|
// if (passportFile == null) {
|
||||||
return "Please upload a file";
|
// return "Please upload a file";
|
||||||
}
|
// }
|
||||||
return null;
|
// return null;
|
||||||
},
|
// },
|
||||||
backgroundColor: Color(0xFFECECEC),
|
backgroundColor: Color(0xFFECECEC),
|
||||||
hintText:
|
hintText:
|
||||||
(selectedFileNames == null ||
|
(selectedFileNames == null ||
|
||||||
@ -507,9 +541,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|||||||
txtwidth:
|
txtwidth:
|
||||||
MediaQuery.of(context).size.width * 0.19,
|
MediaQuery.of(context).size.width * 0.19,
|
||||||
onFileSelected: (fileName, file) {
|
onFileSelected: (fileName, file) {
|
||||||
print(
|
print("File picked: ${fileName}");
|
||||||
"Picked file: $fileName (${file.size} bytes)",
|
// print("Size: ${file.size}");
|
||||||
);
|
// print(
|
||||||
|
// "Path: ${file.path}",
|
||||||
|
// ); // works on mobile/desktop
|
||||||
|
// print("Bytes: ${file.bytes}");
|
||||||
setState(() {
|
setState(() {
|
||||||
passportFile = file;
|
passportFile = file;
|
||||||
selectedFileNames = fileName;
|
selectedFileNames = fileName;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
|
|||||||
import '../../../../core/services/api_service.dart';
|
import '../../../../core/services/api_service.dart';
|
||||||
import '../../../../data/utils/Pagination.dart';
|
import '../../../../data/utils/Pagination.dart';
|
||||||
import '../../../layouts/main_layout.dart';
|
import '../../../layouts/main_layout.dart';
|
||||||
|
import '../../../layouts/responsive_layout.dart';
|
||||||
import '../../../providers/manager_provider.dart';
|
import '../../../providers/manager_provider.dart';
|
||||||
import '../../../themes/indicators/export_btn.dart';
|
import '../../../themes/indicators/export_btn.dart';
|
||||||
import '../../../themes/indicators/filter_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>> originalData = [];
|
||||||
List<Map<String, dynamic>> filteredData = [];
|
List<Map<String, dynamic>> filteredData = [];
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
|
dynamic sortedData;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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 {
|
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 startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||||
return filteredData.sublist(startIndex, endIndex);
|
|
||||||
|
// Return only the paginated slice
|
||||||
|
return sortedData.sublist(startIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterData(String query) {
|
void filterData(String query) {
|
||||||
@ -235,6 +249,9 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
ExportBtn(
|
ExportBtn(
|
||||||
sheetName: "Agents",
|
sheetName: "Agents",
|
||||||
fileName: "agent_list",
|
fileName: "agent_list",
|
||||||
|
txt: !ResponsiveLayout.isMobile(context)
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
data: filteredData,
|
data: filteredData,
|
||||||
headers: [
|
headers: [
|
||||||
"id",
|
"id",
|
||||||
@ -278,6 +295,56 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
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)),
|
Expanded(child: _buildClaimsDataTable(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -321,8 +388,13 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final sortedData = [...filteredData]
|
// final sortedData = [..._paginatedData]
|
||||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
// ..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(
|
return ListView.builder(
|
||||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||||||
@ -341,25 +413,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeader() {
|
Widget _buildHeader() {
|
||||||
return Container(
|
return const SizedBox.shrink(); // empty widget instead of commenting
|
||||||
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||||
@ -413,7 +467,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
|||||||
});
|
});
|
||||||
final response = apiService.updateStatus(
|
final response = apiService.updateStatus(
|
||||||
item['id'],
|
item['id'],
|
||||||
val ? "1" : "0",
|
val ? "0" : "1",
|
||||||
'agent',
|
'agent',
|
||||||
);
|
);
|
||||||
print("Response - $response");
|
print("Response - $response");
|
||||||
|
|||||||
@ -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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,13 +48,13 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
|||||||
|
|
||||||
Future<void> _initializeToken() async {
|
Future<void> _initializeToken() async {
|
||||||
_token = await AuthService.getToken();
|
_token = await AuthService.getToken();
|
||||||
print("APISERTOKEN - $_token");
|
print("profileDataAPISERTOKEN - $_token");
|
||||||
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
|
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
|
||||||
setState(() {
|
setState(() {
|
||||||
profileData = decodedToken['data'];
|
profileData = decodedToken['data'];
|
||||||
});
|
});
|
||||||
print('decodedTokenProfile : $decodedToken');
|
print('profileDatadecodedTokenProfile : $decodedToken');
|
||||||
print('decodedTokenProfile : $profileData');
|
print('profileDatadecodedTokenProfile : $profileData');
|
||||||
|
|
||||||
await getList();
|
await getList();
|
||||||
}
|
}
|
||||||
@ -144,255 +144,244 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MainLayout(
|
return Container(
|
||||||
title: 'Profile',
|
padding: const EdgeInsets.all(6),
|
||||||
body: Container(
|
width: 450,
|
||||||
// color: Colors.white,
|
child: Column(
|
||||||
padding: const EdgeInsets.all(6),
|
mainAxisSize: MainAxisSize.min,
|
||||||
width: 450,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: Column(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
if (!ResponsiveLayout.isMobile(context))
|
Text(
|
||||||
Row(
|
isProfile ? 'My Profile' : 'Incentive Files',
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
style: _topheaderStyle,
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (ResponsiveLayout.isMobile(context))
|
Spacer(),
|
||||||
Row(
|
Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
width: 25,
|
||||||
children: [
|
height: 25,
|
||||||
Text(
|
decoration: BoxDecoration(
|
||||||
isProfile ? '' : 'Incentive Files',
|
color: const Color(0xFFF1F1F1),
|
||||||
style: _topheaderStyle,
|
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
|
isProfile
|
||||||
? Container(
|
? Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
padding: ResponsiveLayout.isMobile(context)
|
padding: ResponsiveLayout.isMobile(context)
|
||||||
? EdgeInsets.all(16.0)
|
? EdgeInsets.all(16.0)
|
||||||
: null,
|
: null,
|
||||||
child: Column(
|
child: isLoading
|
||||||
children: [
|
? const Center(child: Text("No incentive files found"))
|
||||||
const SizedBox(height: 25),
|
: Column(
|
||||||
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: [
|
children: [
|
||||||
const Icon(
|
const SizedBox(height: 25),
|
||||||
Icons.phone_outlined,
|
Container(
|
||||||
size: 20,
|
width: 70,
|
||||||
color: Colors.black,
|
height: 70,
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
const SizedBox(width: 8),
|
color: Color(0xFFD9D9D9),
|
||||||
Text(
|
borderRadius: BorderRadius.circular(30.0),
|
||||||
profileData?['mobile'] ?? '-',
|
),
|
||||||
style: _dataBold,
|
child: Icon(
|
||||||
),
|
Icons.person_outline,
|
||||||
],
|
size: 50,
|
||||||
),
|
|
||||||
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,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
Expanded(
|
Text(
|
||||||
child: Text(
|
profileData?['name'] ?? '-',
|
||||||
profileData?['address'] ?? '-',
|
style: _headerStyle,
|
||||||
style: _dataBold,
|
),
|
||||||
softWrap: true,
|
Text(
|
||||||
),
|
profileData?['emp_id'] ?? '-',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Color(0xFF000000),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(height: 26),
|
||||||
const SizedBox(height: 20),
|
Row(
|
||||||
],
|
children: [
|
||||||
|
const Icon(
|
||||||
if (profileData?['role_id'] != 2) ...[
|
Icons.phone_outlined,
|
||||||
Container(
|
size: 20,
|
||||||
padding: const EdgeInsets.all(6),
|
color: Colors.black,
|
||||||
color: Color(0xffEDF6F5),
|
),
|
||||||
child: GestureDetector(
|
const SizedBox(width: 8),
|
||||||
onTap: () {
|
Text(
|
||||||
print("Download - $latestId");
|
profileData?['mobile'] ?? '-',
|
||||||
dynamic path;
|
style: _dataBold,
|
||||||
|
),
|
||||||
if (roleId == 1) {
|
],
|
||||||
path =
|
),
|
||||||
'api/agent/downloadManagerIncentiveFile?id=$latestId';
|
const SizedBox(height: 8),
|
||||||
} else if (roleId != 1 && roleId != 2) {
|
Row(
|
||||||
path =
|
children: [
|
||||||
'api/agent/downloadAgentIncentiveFile?id=$latestId';
|
const Icon(
|
||||||
}
|
Icons.email_outlined,
|
||||||
apiService.getPdfDownload(path, latestId);
|
size: 18,
|
||||||
},
|
color: Colors.black,
|
||||||
child: Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
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: [
|
children: [
|
||||||
Icon(Icons.download),
|
const Icon(
|
||||||
SizedBox(width: 8),
|
Icons.location_on_outlined,
|
||||||
Text(
|
size: 18,
|
||||||
"Download Incentive file",
|
color: Colors.black,
|
||||||
style: _dataSub,
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
profileData?['address'] ?? '-',
|
||||||
|
style: _dataBold,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
),
|
],
|
||||||
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(
|
if (profileData?['role_id'] != 2) ...[
|
||||||
padding: const EdgeInsets.symmetric(
|
Container(
|
||||||
vertical: 2.0,
|
padding: const EdgeInsets.all(6),
|
||||||
),
|
color: Color(0xffEDF6F5),
|
||||||
child: IncentiveFileRow(
|
child: GestureDetector(
|
||||||
fileName:
|
onTap: () {
|
||||||
file['incentive_file_name'] ??
|
print("Download - $latestId");
|
||||||
"Unknown",
|
dynamic path;
|
||||||
date: file['incentive_month'] ?? "-",
|
|
||||||
onUpload: () {
|
|
||||||
print("Upload ${file['id']}");
|
|
||||||
final selectedId = file['id'];
|
|
||||||
|
|
||||||
dynamic path;
|
if (roleId == 1) {
|
||||||
|
path =
|
||||||
if (roleId == 1) {
|
'api/agent/downloadManagerIncentiveFile?id=$latestId';
|
||||||
path =
|
} else if (roleId != 1 && roleId != 2) {
|
||||||
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
|
path =
|
||||||
} else if (roleId != 1 &&
|
'api/agent/downloadAgentIncentiveFile?id=$latestId';
|
||||||
roleId != 2) {
|
}
|
||||||
path =
|
apiService.getPdfDownload(path, latestId);
|
||||||
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
|
|
||||||
}
|
|
||||||
apiService.getPdfDownload(
|
|
||||||
path,
|
|
||||||
selectedId,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,13 +12,10 @@ import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.d
|
|||||||
import '../../../../core/config/env.dart';
|
import '../../../../core/config/env.dart';
|
||||||
import '../../../../core/services/api_service.dart';
|
import '../../../../core/services/api_service.dart';
|
||||||
import '../../../../data/services/auth_service.dart';
|
import '../../../../data/services/auth_service.dart';
|
||||||
import '../../../../data/utils/Pagination.dart';
|
|
||||||
import '../../../../data/utils/validators.dart';
|
import '../../../../data/utils/validators.dart';
|
||||||
import '../../../layouts/main_layout.dart';
|
import '../../../layouts/main_layout.dart';
|
||||||
import '../../../providers/manager_provider.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;
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
class Staff extends ConsumerStatefulWidget {
|
class Staff extends ConsumerStatefulWidget {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
|
|||||||
import '../../../../core/services/api_service.dart';
|
import '../../../../core/services/api_service.dart';
|
||||||
import '../../../../data/utils/Pagination.dart';
|
import '../../../../data/utils/Pagination.dart';
|
||||||
import '../../../layouts/main_layout.dart';
|
import '../../../layouts/main_layout.dart';
|
||||||
|
import '../../../layouts/responsive_layout.dart';
|
||||||
import '../../../providers/manager_provider.dart';
|
import '../../../providers/manager_provider.dart';
|
||||||
import '../../../themes/indicators/export_btn.dart';
|
import '../../../themes/indicators/export_btn.dart';
|
||||||
import '../../../themes/indicators/filter_btn.dart';
|
import '../../../themes/indicators/filter_btn.dart';
|
||||||
@ -56,9 +57,14 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
List<dynamic> get _paginatedData {
|
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 startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||||
return filteredData.sublist(startIndex, endIndex);
|
|
||||||
|
return sortedData.sublist(startIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void filterData(String query) {
|
void filterData(String query) {
|
||||||
@ -210,6 +216,9 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
ExportBtn(
|
ExportBtn(
|
||||||
sheetName: "Staff",
|
sheetName: "Staff",
|
||||||
fileName: "staff_list",
|
fileName: "staff_list",
|
||||||
|
txt: !ResponsiveLayout.isMobile(context)
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
data: filteredData,
|
data: filteredData,
|
||||||
headers: [
|
headers: [
|
||||||
"id",
|
"id",
|
||||||
@ -253,6 +262,48 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
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)),
|
Expanded(child: _buildDataTable(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -296,8 +347,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final sortedData = [...filteredData]
|
final sortedData = [..._paginatedData];
|
||||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||||||
@ -316,24 +366,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeader() {
|
Widget _buildHeader() {
|
||||||
return Container(
|
return SizedBox.shrink();
|
||||||
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||||
@ -381,7 +414,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
|||||||
});
|
});
|
||||||
final response = apiService.updateStatus(
|
final response = apiService.updateStatus(
|
||||||
item['id'],
|
item['id'],
|
||||||
val ? "1" : "0",
|
val ? "0" : "1",
|
||||||
'staff',
|
'staff',
|
||||||
);
|
);
|
||||||
print("Response - $response");
|
print("Response - $response");
|
||||||
|
|||||||
215
lib/presentation/themes/indicators/customizd_file_upload.dart
Normal file
215
lib/presentation/themes/indicators/customizd_file_upload.dart
Normal 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,9 +21,10 @@ class ExportBtn extends HookWidget {
|
|||||||
final String fileName;
|
final String fileName;
|
||||||
final List<Map<String, dynamic>> data;
|
final List<Map<String, dynamic>> data;
|
||||||
final List<String> headers; // dynamic headers
|
final List<String> headers; // dynamic headers
|
||||||
|
final bool? txt;
|
||||||
const ExportBtn({
|
const ExportBtn({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.txt,
|
||||||
required this.sheetName,
|
required this.sheetName,
|
||||||
required this.fileName,
|
required this.fileName,
|
||||||
required this.data,
|
required this.data,
|
||||||
@ -50,15 +51,17 @@ class ExportBtn extends HookWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
?txt!
|
||||||
'Export',
|
? Text(
|
||||||
style: TextStyle(
|
'Export',
|
||||||
color: Colors.white,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
color: Colors.white,
|
||||||
fontSize: 14,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
)
|
||||||
|
: null,
|
||||||
|
?txt! ? SizedBox(width: 20) : null,
|
||||||
Icon(Icons.input_sharp, color: Colors.white),
|
Icon(Icons.input_sharp, color: Colors.white),
|
||||||
// Image.asset("assets/miscellaneous/export", height: 15, width: 15),
|
// Image.asset("assets/miscellaneous/export", height: 15, width: 15),
|
||||||
],
|
],
|
||||||
|
|||||||
40
lib/presentation/themes/indicators/fileUploadService.dart
Normal file
40
lib/presentation/themes/indicators/fileUploadService.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
147
lib/presentation/themes/indicators/outlineWidget
Normal file
147
lib/presentation/themes/indicators/outlineWidget
Normal 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;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
lib/presentation/themes/indicators/singleFileUpload.dart
Normal file
70
lib/presentation/themes/indicators/singleFileUpload.dart
Normal 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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,157 +1,204 @@
|
|||||||
import 'dart:html' as html;
|
// import 'dart:html' as html;
|
||||||
import 'package:flutter/material.dart';
|
// import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
// import 'package:flutter/foundation.dart';
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
class ThemedUploadField extends FormField<String> {
|
// import 'package:google_fonts/google_fonts.dart';
|
||||||
ThemedUploadField({
|
// import 'package:http/http.dart' as http;
|
||||||
super.key,
|
//
|
||||||
String? hintText,
|
// import 'package:flutter/material.dart';
|
||||||
double? txtwidth,
|
// import 'package:file_picker/file_picker.dart';
|
||||||
double? txtheight,
|
// import 'package:http/http.dart' as http;
|
||||||
Color? backgroundColor,
|
// import 'package:http_parser/http_parser.dart';
|
||||||
Color? borderColor,
|
// import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
Color? errorBorderColor,
|
//
|
||||||
FormFieldValidator<String>? validator,
|
// class ThemedUploadField extends FormField<String> {
|
||||||
Function(String fileName, dynamic file)? onFileSelected,
|
// ThemedUploadField({
|
||||||
}) : super(
|
// super.key,
|
||||||
validator: validator,
|
// String? hintText,
|
||||||
builder: (FormFieldState<String> state) {
|
// double? txtwidth,
|
||||||
return _ThemedUploadFieldBody(
|
// double? txtheight,
|
||||||
hintText: hintText,
|
// Color? backgroundColor,
|
||||||
txtwidth: txtwidth,
|
// Color? borderColor,
|
||||||
txtheight: txtheight,
|
// Color? errorBorderColor,
|
||||||
backgroundColor: backgroundColor,
|
// FormFieldValidator<String>? validator,
|
||||||
borderColor: borderColor,
|
// Function(String fileName, dynamic file)? onFileSelected,
|
||||||
errorBorderColor: errorBorderColor,
|
// }) : super(
|
||||||
onFileSelected: (fileName, file) {
|
// validator: validator,
|
||||||
state.didChange(fileName); // update validation state
|
// builder: (FormFieldState<String> state) {
|
||||||
onFileSelected?.call(fileName, file);
|
// return _ThemedUploadFieldBody(
|
||||||
},
|
// hintText: hintText,
|
||||||
errorText: state.errorText,
|
// txtwidth: txtwidth,
|
||||||
);
|
// txtheight: txtheight,
|
||||||
},
|
// backgroundColor: backgroundColor,
|
||||||
);
|
// borderColor: borderColor,
|
||||||
}
|
// errorBorderColor: errorBorderColor,
|
||||||
|
// onFileSelected: (fileName, file) {
|
||||||
class _ThemedUploadFieldBody extends StatefulWidget {
|
// state.didChange(fileName); // update validation state
|
||||||
const _ThemedUploadFieldBody({
|
// onFileSelected?.call(fileName, file);
|
||||||
this.hintText,
|
// },
|
||||||
this.txtwidth,
|
// errorText: state.errorText,
|
||||||
this.txtheight,
|
// );
|
||||||
this.backgroundColor,
|
// },
|
||||||
this.borderColor,
|
// );
|
||||||
this.errorBorderColor,
|
// }
|
||||||
this.onFileSelected,
|
//
|
||||||
this.errorText,
|
// class _ThemedUploadFieldBody extends StatefulWidget {
|
||||||
});
|
// const _ThemedUploadFieldBody({
|
||||||
|
// this.hintText,
|
||||||
final String? hintText;
|
// this.txtwidth,
|
||||||
final double? txtwidth;
|
// this.txtheight,
|
||||||
final double? txtheight;
|
// this.backgroundColor,
|
||||||
final Color? backgroundColor;
|
// this.borderColor,
|
||||||
final Color? borderColor;
|
// this.errorBorderColor,
|
||||||
final Color? errorBorderColor;
|
// this.onFileSelected,
|
||||||
final Function(String fileName, dynamic file)? onFileSelected;
|
// this.errorText,
|
||||||
final String? errorText;
|
// });
|
||||||
|
//
|
||||||
@override
|
// final String? hintText;
|
||||||
State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
|
// final double? txtwidth;
|
||||||
}
|
// final double? txtheight;
|
||||||
|
// final Color? backgroundColor;
|
||||||
class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
|
// final Color? borderColor;
|
||||||
String? selectedFileName;
|
// final Color? errorBorderColor;
|
||||||
|
// final Function(String fileName, dynamic file)? onFileSelected;
|
||||||
void pickPDFWeb() {
|
// final String? errorText;
|
||||||
final uploadInput = html.FileUploadInputElement();
|
//
|
||||||
uploadInput.accept = '.pdf';
|
// @override
|
||||||
uploadInput.click();
|
// State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
|
||||||
|
// }
|
||||||
uploadInput.onChange.listen((e) {
|
//
|
||||||
final file = uploadInput.files!.first;
|
// class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
|
||||||
|
// String? selectedFileName;
|
||||||
// Ensure PDF type
|
//
|
||||||
if (!file.type.contains("pdf")) {
|
// // void pickPDFWeb() {
|
||||||
ScaffoldMessenger.of(
|
// // final uploadInput = html.FileUploadInputElement();
|
||||||
context,
|
// // uploadInput.accept = '.pdf';
|
||||||
).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
|
// // uploadInput.click();
|
||||||
return;
|
// //
|
||||||
}
|
// // uploadInput.onChange.listen((e) {
|
||||||
|
// // final file = uploadInput.files!.first;
|
||||||
// Size check (3 MB)
|
// //
|
||||||
const maxFileSize = 3 * 1024 * 1024;
|
// // // Ensure PDF type
|
||||||
if (file.size > maxFileSize) {
|
// // if (!file.type.contains("pdf")) {
|
||||||
ScaffoldMessenger.of(
|
// // ScaffoldMessenger.of(
|
||||||
context,
|
// // context,
|
||||||
).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
|
// // ).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
|
||||||
return;
|
// // return;
|
||||||
}
|
// // }
|
||||||
|
// //
|
||||||
setState(() => selectedFileName = file.name);
|
// // // Size check (3 MB)
|
||||||
widget.onFileSelected?.call(file.name, file);
|
// // const maxFileSize = 3 * 1024 * 1024;
|
||||||
});
|
// // if (file.size > maxFileSize) {
|
||||||
}
|
// // ScaffoldMessenger.of(
|
||||||
|
// // context,
|
||||||
@override
|
// // ).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
|
||||||
Widget build(BuildContext context) {
|
// // return;
|
||||||
return Column(
|
// // }
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// //
|
||||||
children: [
|
// // setState(() => selectedFileName = file.name);
|
||||||
Container(
|
// // widget.onFileSelected?.call(file.name, file);
|
||||||
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
// // });
|
||||||
height: widget.txtheight,
|
// // }
|
||||||
child: DecoratedBox(
|
//
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
// Future<void> pickPDF() async {
|
||||||
child: InkWell(
|
// try {
|
||||||
onTap: pickPDFWeb,
|
// final result = await FilePicker.platform.pickFiles(
|
||||||
borderRadius: BorderRadius.circular(10),
|
// type: FileType.custom,
|
||||||
child: Container(
|
// allowedExtensions: ['pdf'],
|
||||||
padding: const EdgeInsets.symmetric(
|
// withData: true,
|
||||||
vertical: 10,
|
// );
|
||||||
horizontal: 15,
|
//
|
||||||
),
|
// if (result != null && result.files.isNotEmpty) {
|
||||||
decoration: BoxDecoration(
|
// final file = result.files.first; // PlatformFile
|
||||||
color: widget.backgroundColor ?? Colors.white,
|
//
|
||||||
borderRadius: BorderRadius.circular(10),
|
// if (file.extension?.toLowerCase() != "pdf") {
|
||||||
border: Border.all(
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
color: widget.errorText != null
|
// const SnackBar(content: Text("Only PDF files allowed")),
|
||||||
? (widget.errorBorderColor ?? Colors.red)
|
// );
|
||||||
: (widget.borderColor ?? Colors.grey.shade50),
|
// return;
|
||||||
width: 1,
|
// }
|
||||||
),
|
//
|
||||||
),
|
// if (file.size > 3 * 1024 * 1024) {
|
||||||
child: Row(
|
// ScaffoldMessenger.of(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// context,
|
||||||
children: [
|
// ).showSnackBar(const SnackBar(content: Text("File exceeds 3MB")));
|
||||||
Expanded(
|
// return;
|
||||||
child: Text(
|
// }
|
||||||
selectedFileName ??
|
//
|
||||||
widget.hintText ??
|
// setState(() => selectedFileName = file.name);
|
||||||
"Upload Documents",
|
//
|
||||||
style: TextStyle(
|
// // Always pass PlatformFile
|
||||||
fontSize: 12,
|
// widget.onFileSelected?.call(file.name, file);
|
||||||
color: widget.errorText != null
|
// }
|
||||||
? Colors.red
|
// } catch (e, st) {
|
||||||
: Colors.black,
|
// debugPrint("File picker error: $e\n$st"); // 👈 safer logging
|
||||||
),
|
// // ScaffoldMessenger.of(
|
||||||
overflow: TextOverflow.ellipsis,
|
// // context,
|
||||||
),
|
// // ).showSnackBar(const SnackBar(content: Text("Error picking file")));
|
||||||
),
|
// }
|
||||||
const Icon(Icons.file_upload_outlined, color: Colors.black),
|
// }
|
||||||
],
|
//
|
||||||
),
|
// @override
|
||||||
),
|
// Widget build(BuildContext context) {
|
||||||
),
|
// return Column(
|
||||||
),
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
// children: [
|
||||||
// if (widget.errorText != null)
|
// Container(
|
||||||
// Padding(
|
// width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
||||||
// padding: const EdgeInsets.only(left: 8, top: 4),
|
// height: widget.txtheight,
|
||||||
// child: Text(
|
// child: DecoratedBox(
|
||||||
// widget.errorText!,
|
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
||||||
// style: const TextStyle(color: Colors.red, fontSize: 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),
|
||||||
|
// // ),
|
||||||
|
// // ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@ -72,7 +72,7 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
|
|||||||
if (key == 'Reports') ...[
|
if (key == 'Reports') ...[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
print("Tappped Agent");
|
context.go(AppRoutes.enquiryLst);
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Enquiries",
|
"Enquiries",
|
||||||
@ -175,7 +175,9 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
|
|||||||
|
|
||||||
DrawerContentWrapper(
|
DrawerContentWrapper(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
context.go(AppRoutes.tabEnquiry);
|
||||||
|
},
|
||||||
icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black),
|
icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -20,9 +20,12 @@ class _MobileTabsState extends State<MobileTabs> {
|
|||||||
widget.onTabChanged(index);
|
widget.onTabChanged(index);
|
||||||
},
|
},
|
||||||
items: const [
|
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.person), label: "Profile"),
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Settings"),
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.event_note_sharp),
|
||||||
|
label: "Enquiry",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
|
import file_picker
|
||||||
import firebase_app_check
|
import firebase_app_check
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
@ -21,6 +22,7 @@ import url_launcher_macos
|
|||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
|||||||
16
pubspec.lock
16
pubspec.lock
@ -201,6 +201,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
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:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -297,6 +305,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
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:
|
firebase_app_check:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -67,6 +67,7 @@ dependencies:
|
|||||||
open_file: ^3.5.10
|
open_file: ^3.5.10
|
||||||
excel: ^4.0.6
|
excel: ^4.0.6
|
||||||
dropdown_search: ^6.0.2
|
dropdown_search: ^6.0.2
|
||||||
|
file_picker: ^10.3.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user