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:go_router/go_router.dart';
|
||||
import 'package:nhance_partner/core/routing/routes.dart';
|
||||
import 'package:nhance_partner/presentation/screens/Enquiry/enquiryList.dart';
|
||||
import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart';
|
||||
import 'package:nhance_partner/presentation/screens/claims/claimsList.dart';
|
||||
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
|
||||
import '../../data/services/auth_service.dart';
|
||||
import '../../presentation/screens/Enquiry/enquiry/tabs.dart';
|
||||
import '../../presentation/screens/UserManagement/Agent/agent.dart';
|
||||
import '../../presentation/screens/UserManagement/Agent/agentList.dart';
|
||||
import '../../presentation/screens/UserManagement/Profile/profile_mobile.dart';
|
||||
import '../../presentation/screens/UserManagement/Staff/staff.dart';
|
||||
import '../../presentation/screens/UserManagement/Staff/staffList.dart';
|
||||
import '../../presentation/screens/home/home_screen.dart';
|
||||
@ -35,7 +38,7 @@ final GoRouter appRouter = GoRouter(
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.profile,
|
||||
builder: (context, state) => const ProfilePopUp(),
|
||||
builder: (context, state) => const Profile(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
@ -76,6 +79,16 @@ final GoRouter appRouter = GoRouter(
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.enquiryLst,
|
||||
builder: (context, state) => const EnquiryList(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.tabEnquiry,
|
||||
builder: (context, state) => const TabEnquiryList(),
|
||||
),
|
||||
],
|
||||
redirect: (context, state) async {
|
||||
final loggedIn = await AuthService.isLoggedIn();
|
||||
|
||||
@ -8,4 +8,6 @@ class AppRoutes {
|
||||
static const agent = '/agent/:id';
|
||||
static const String staffLst = '/staffLst';
|
||||
static const staff = '/staff/:id';
|
||||
static const String enquiryLst = '/enquiryLst';
|
||||
static const String tabEnquiry = '/tabEnquiry';
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nhance_partner/data/services/auth_service.dart';
|
||||
@ -76,147 +78,7 @@ class ApiService {
|
||||
);
|
||||
return _handleResponse(response);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchAgentIncentiveList(agentId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}agent/agentIncentiveFileList?agent_id=${agentId}',
|
||||
);
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
|
||||
// );
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchManagerIncentiveList(mangerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}staff/managerIncentiveFileList?manager_id=$mangerId',
|
||||
);
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
|
||||
// );
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchAgentUserList(int managerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final userId = '1';
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}agent/agentList?manager_id=${managerId}',
|
||||
);
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}',
|
||||
// );
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> findSingleAgentData(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/findAgent?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}agent/findAgent?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> deleteAgentIncentiveFile(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/deleteAgentIncentiveFile?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}agent/deleteAgentIncentiveFile?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchStaffUserList(int managerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
|
||||
// );
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}staff/staffList?manager_id=${managerId}',
|
||||
);
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> findSingleStaffData(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/staff/findStaff?id=$id');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/findStaff?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}staff/findStaff?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
// --------------------------- COMMON FILES __________________________________________
|
||||
|
||||
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
|
||||
if (response.statusCode == 200) {
|
||||
@ -290,6 +152,39 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> downloadFile({
|
||||
required String? apiUrl,
|
||||
required PlatformFile? localFile,
|
||||
required String? fileName,
|
||||
String? apiId, // optional if your API needs it
|
||||
}) async {
|
||||
try {
|
||||
if (localFile != null) {
|
||||
if (kIsWeb) {
|
||||
final blob = html.Blob([localFile.bytes!]);
|
||||
final url = html.Url.createObjectUrlFromBlob(blob);
|
||||
|
||||
final anchor = html.AnchorElement(href: url)
|
||||
..setAttribute('download', localFile.name)
|
||||
..click();
|
||||
|
||||
html.Url.revokeObjectUrl(url);
|
||||
print("Download triggered successfully (web)!");
|
||||
} else {
|
||||
print("Local file path: ${localFile.path}");
|
||||
// await OpenFilex.open(localFile.path!);
|
||||
}
|
||||
} else if (apiUrl != null) {
|
||||
print("Download from API: $apiUrl");
|
||||
await getPdfDownload(apiUrl, apiId);
|
||||
} else {
|
||||
print("⚠️ No file available to download");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error during download: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> updateStatus(id, status, role) async {
|
||||
final Map<String, dynamic> data = {
|
||||
"id": int.parse(id),
|
||||
@ -324,4 +219,232 @@ class ApiService {
|
||||
final response = await _makePostRequestJson(url, data, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
// -------------------------------- AGENT ----------------------------------------------
|
||||
|
||||
Future<Map<String, dynamic>> fetchAgentIncentiveList(agentId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}agent/agentIncentiveFileList?agent_id=${agentId}',
|
||||
);
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
|
||||
// );
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchManagerIncentiveList(mangerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}staff/managerIncentiveFileList?manager_id=$mangerId',
|
||||
);
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchAgentUserList(int managerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final userId = '1';
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}agent/agentList?manager_id=${managerId}',
|
||||
);
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> findSingleAgentData(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/findAgent?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}agent/findAgent?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> deleteAgentIncentiveFile(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/agent/agentList');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/deleteAgentIncentiveFile?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}agent/deleteAgentIncentiveFile?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
// ------------------------------------ STAFF -----------------------------------------------------
|
||||
|
||||
Future<Map<String, dynamic>> fetchStaffUserList(int managerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
|
||||
// );
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}staff/staffList?manager_id=${managerId}',
|
||||
);
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> findSingleStaffData(id) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
// final url = Uri.parse('${Env.apiUrl}/api/staff/findStaff?id=$id');
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/findStaff?id=$id',
|
||||
// );
|
||||
|
||||
final url = Uri.parse('${Env.apiUrl}staff/findStaff?id=$id');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
// ----------------------------------- ENQUIRY -------------------------------------------------
|
||||
|
||||
Future<Map<String, dynamic>> fetchEnquiryList(int id, role) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
final userId = '1';
|
||||
final url;
|
||||
|
||||
if (role == 'manager') {
|
||||
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?manager_id=$id');
|
||||
} else if (role == 'staff') {
|
||||
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?staff_id=$id');
|
||||
} else {
|
||||
url = Uri.parse('${Env.apiUrl}enquiry/enquiryList?agent_id=$id');
|
||||
}
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}',
|
||||
// );
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
//------------------------------------ Quotation ------------------------------------------------
|
||||
|
||||
Future<Map<String, dynamic>> fetchQuotationList(int managerId) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
// final url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}',
|
||||
// );
|
||||
|
||||
final url = Uri.parse(
|
||||
'${Env.apiUrl}quotation/quotationList?manager_id=${managerId}',
|
||||
);
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
// --------------------------------- MASTER DATA ----------------------------------------------
|
||||
|
||||
Future<Map<String, dynamic>> fetchMasterDropDown(String val) async {
|
||||
// print(_token);
|
||||
if (_token == null) {
|
||||
await _initializeToken();
|
||||
}
|
||||
|
||||
print('VAl - $val');
|
||||
|
||||
dynamic url;
|
||||
|
||||
if (val == 'staffRole') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getRoleMaster');
|
||||
} else if (val == 'vehicleType') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getVehicleTypeMaster');
|
||||
} else if (val == 'InsuranceType') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getInsurancePlanTypeMaster');
|
||||
} else if (val == 'Insurers') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getInsurersMaster');
|
||||
} else if (val == 'Staffs') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getStaffMaster');
|
||||
} else if (val == 'Claim') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getClaimMaster');
|
||||
} else if (val == 'Endorsement') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getEndorsementMaster');
|
||||
}
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token' ?? '',
|
||||
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,4 +254,4 @@ class PaginationControls extends StatelessWidget {
|
||||
child: Text('...', style: TextStyle(fontSize: 16)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/manager_provider.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -31,13 +32,15 @@ Future<void> main() async {
|
||||
// prefs.clear();
|
||||
if (kIsWeb) {
|
||||
await Firebase.initializeApp(
|
||||
options: const FirebaseOptions(
|
||||
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
|
||||
authDomain: "nhance-ee8d1.firebaseapp.com",
|
||||
projectId: "nhance-ee8d1",
|
||||
storageBucket: "nhance-ee8d1.firebasestorage.app",
|
||||
messagingSenderId: "1084115316849",
|
||||
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0"));
|
||||
options: const FirebaseOptions(
|
||||
apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc",
|
||||
authDomain: "nhance-ee8d1.firebaseapp.com",
|
||||
projectId: "nhance-ee8d1",
|
||||
storageBucket: "nhance-ee8d1.firebasestorage.app",
|
||||
messagingSenderId: "1084115316849",
|
||||
appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0",
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await Firebase.initializeApp();
|
||||
await FirebaseAppCheck.instance.activate(
|
||||
@ -77,7 +80,6 @@ class MyApp extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _MyAppState extends ConsumerState<MyApp> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -91,10 +93,14 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Clean Architecture App',
|
||||
theme: ThemeData(primarySwatch: Colors.blue),
|
||||
title: 'Nhance Partner',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
textTheme: GoogleFonts.interTextTheme(
|
||||
Theme.of(context).textTheme, // base on default styles
|
||||
),
|
||||
),
|
||||
routerConfig: appRouter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +37,12 @@ class MainLayout extends StatelessWidget {
|
||||
bottomNavigationBar: MobileTabs(
|
||||
onTabChanged: (index) {
|
||||
debugPrint("Mobile tab changed to $index");
|
||||
if (index == 1) {
|
||||
if (index == 0) {
|
||||
context.go(AppRoutes.dashboard);
|
||||
} else if (index == 1) {
|
||||
context.go(AppRoutes.profile);
|
||||
} else if (index == 2) {
|
||||
context.go(AppRoutes.tabEnquiry);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
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/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import '../../../../core/routing/routes.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/utils/Pagination.dart';
|
||||
import '../../layouts/main_layout.dart';
|
||||
import '../../layouts/responsive_layout.dart';
|
||||
import '../../providers/manager_provider.dart';
|
||||
import '../../themes/indicators/export_btn.dart';
|
||||
import '../../themes/indicators/search_field_theme.dart';
|
||||
import '../../widgets/custom_action_popup.dart';
|
||||
|
||||
class enquiryList extends StatefulWidget {
|
||||
const enquiryList({super.key});
|
||||
class EnquiryList extends ConsumerStatefulWidget {
|
||||
const EnquiryList({super.key});
|
||||
@override
|
||||
enquiryListState createState() => enquiryListState();
|
||||
ConsumerState<EnquiryList> createState() => EnquiryListState();
|
||||
}
|
||||
|
||||
class enquiryListState extends State<enquiryList> {
|
||||
class EnquiryListState extends ConsumerState<EnquiryList> {
|
||||
int currentPage = 1;
|
||||
int itemsPerPage = 10;
|
||||
late ApiService apiService;
|
||||
@ -31,70 +36,102 @@ class enquiryListState extends State<enquiryList> {
|
||||
super.initState();
|
||||
|
||||
apiService = ApiService();
|
||||
// getStaffList();
|
||||
|
||||
Future.microtask(() {
|
||||
final id = ref.read(managerIdProvider);
|
||||
final roleId = ref.read(userRoleProvider);
|
||||
|
||||
print("ENQroleId - $roleId");
|
||||
if (id != null) {
|
||||
getStaffList(id, roleId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getStaffList(int managerId, role) async {
|
||||
print('getClaimList called MagId - $managerId - $role');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchEnquiryList(managerId, role);
|
||||
|
||||
if (response['status'] == 'success') {
|
||||
final data = response['data'];
|
||||
print('getStaffListData - ${response['data']}');
|
||||
setState(() {
|
||||
if (data is List) {
|
||||
// Already a list of maps
|
||||
getStaffData = List<Map<String, dynamic>>.from(data);
|
||||
} else if (data is Map) {
|
||||
// Single object, wrap in a list
|
||||
getStaffData = [Map<String, dynamic>.from(data)];
|
||||
} else {
|
||||
getStaffData = [];
|
||||
}
|
||||
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
|
||||
originalData = getStaffData;
|
||||
filteredData = List.from(originalData);
|
||||
// print('originalData - $getClaimPolicies');
|
||||
});
|
||||
} else {
|
||||
getStaffData = [];
|
||||
originalData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
// Sort descending by id first
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
||||
return filteredData.sublist(startIndex, endIndex);
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||
|
||||
return sortedData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void filterData(String query) {
|
||||
print("FilterDAta - $query");
|
||||
setState(() {
|
||||
filteredData = getStaffData.where((item) {
|
||||
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
|
||||
// final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
|
||||
|
||||
return (item['name'] ?? '').toLowerCase().contains(
|
||||
return (item['created_on'] ?? '').toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ||
|
||||
(item['email'] ?? '').toLowerCase().contains(query.toLowerCase()) ||
|
||||
(item['mobile'] ?? '').toLowerCase().contains(
|
||||
(item['reg_no'] ?? '').toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ||
|
||||
(item['address'] ?? '').toLowerCase().contains(
|
||||
(item['insurer_name'] ?? '').toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ||
|
||||
(item['emp_id'] ?? item['agent_code'] ?? '').toLowerCase().contains(
|
||||
(item['status'] ?? '').toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ||
|
||||
isActiveStatus.contains(query.toLowerCase());
|
||||
(item['remarks'] ?? '').toLowerCase().contains(query.toLowerCase());
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
final TextEditingController _searchStaffController = TextEditingController();
|
||||
|
||||
// Future<void> getStaffList() async {
|
||||
// print('getClaimList called');
|
||||
// setState(() {
|
||||
// isLoading = true;
|
||||
// });
|
||||
//
|
||||
// try {
|
||||
// final response = await apiService.fetchStaffUserList();
|
||||
//
|
||||
// if (response['status'] == 'success') {
|
||||
// print('getStaffListData - ${response['data']}');
|
||||
// setState(() {
|
||||
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
|
||||
// originalData = getStaffData;
|
||||
// filteredData = List.from(originalData);
|
||||
// // print('originalData - $getClaimPolicies');
|
||||
// });
|
||||
// } else {
|
||||
// getStaffData = [];
|
||||
// originalData = [];
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print('Exception occurred: $e');
|
||||
// } finally {
|
||||
// setState(() {
|
||||
// isLoading = false;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
String _formatDate(String rawDate) {
|
||||
try {
|
||||
final dateTime = DateTime.parse(rawDate);
|
||||
return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format
|
||||
} catch (e) {
|
||||
return rawDate; // fallback if parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||||
return [
|
||||
@ -103,7 +140,7 @@ class enquiryListState extends State<enquiryList> {
|
||||
Navigator.pop(context);
|
||||
print('EDITStaff - ${data['id']}');
|
||||
dynamic id = data['id'];
|
||||
context.go('/staff/$id');
|
||||
// context.go('/staff/$id');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -145,7 +182,7 @@ class enquiryListState extends State<enquiryList> {
|
||||
color: Color(0xFF425B5B),
|
||||
),
|
||||
Text(
|
||||
"Enquries",
|
||||
"Enquiries",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -179,29 +216,34 @@ class enquiryListState extends State<enquiryList> {
|
||||
backgroundColor: Color(0xFFF6F8F8),
|
||||
onChanged: filterData,
|
||||
controller: _searchStaffController,
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.2,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? MediaQuery.of(context).size.width * 0.6
|
||||
: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
|
||||
Spacer(),
|
||||
|
||||
ExportBtn(
|
||||
sheetName: "Staff",
|
||||
fileName: "staff_list",
|
||||
sheetName: "Enquiry",
|
||||
fileName: "enquiry_list",
|
||||
data: filteredData,
|
||||
txt: !ResponsiveLayout.isMobile(context)
|
||||
? true
|
||||
: false,
|
||||
headers: [
|
||||
"id",
|
||||
"name",
|
||||
"email",
|
||||
"mobile",
|
||||
"emp_id",
|
||||
"is_active",
|
||||
"created_on",
|
||||
"reg_no",
|
||||
"insurer_name",
|
||||
"status",
|
||||
"remarks",
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print('Export');
|
||||
// print('Export');
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
@ -214,22 +256,24 @@ class enquiryListState extends State<enquiryList> {
|
||||
children: [
|
||||
Icon(Icons.add, color: Colors.white),
|
||||
|
||||
SizedBox(width: 10),
|
||||
if (!ResponsiveLayout.isMobile(context)) ...[
|
||||
SizedBox(width: 10),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// context.go(AppRoutes.agent / create);
|
||||
context.go('/staff/create');
|
||||
},
|
||||
child: Text(
|
||||
'Create New Staff',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// context.go(AppRoutes.agent / create);
|
||||
context.go('/staff/create');
|
||||
},
|
||||
child: Text(
|
||||
'Create New Staff',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -238,6 +282,51 @@ class enquiryListState extends State<enquiryList> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('S.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'Received Date & Time',
|
||||
style: _headerStyle,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Reg.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Company', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Status', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Remarks', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Action', style: _headerStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: _buildDataTable(context)),
|
||||
],
|
||||
),
|
||||
@ -281,47 +370,31 @@ class enquiryListState extends State<enquiryList> {
|
||||
);
|
||||
}
|
||||
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
final sortedData = [..._paginatedData];
|
||||
return ListView.builder(
|
||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||||
itemCount: sortedData.length + 1, // +1 for header, +1 for pagination
|
||||
itemCount: ResponsiveLayout.isMobile(context)
|
||||
? sortedData
|
||||
.length // only cards for mobile
|
||||
: sortedData.length + 1, // +1 for header in desktop
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) return _buildHeader();
|
||||
// if (index == dataVal.length + 1)
|
||||
// return _buildPagination(context);
|
||||
final startIndex = ((currentPage - 1) * itemsPerPage);
|
||||
// final item = filteredData[index - 1];
|
||||
final item = sortedData[index - 1];
|
||||
if (!ResponsiveLayout.isMobile(context) && index == 0) {
|
||||
return _buildHeader();
|
||||
}
|
||||
|
||||
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
final item =
|
||||
sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)];
|
||||
final sno = startIndex + index;
|
||||
return _buildDataRow(item, sno);
|
||||
|
||||
return !ResponsiveLayout.isMobile(context)
|
||||
? _buildDataRow(item, sno)
|
||||
: _buildDataCard(item, sno);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: const Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Received Date & Time', style: _headerStyle),
|
||||
),
|
||||
Expanded(flex: 3, child: Text('Reg.No.', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Company', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Remarks', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
|
||||
],
|
||||
),
|
||||
);
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
@ -339,42 +412,38 @@ class enquiryListState extends State<enquiryList> {
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
||||
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(item['email'] ?? '-', style: _dataBold),
|
||||
flex: 2,
|
||||
child: Text(
|
||||
_formatDate(item['created_on']) ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(item['reg_no'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['mobile'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(item['emp_id'] ?? '-', style: _dataBold),
|
||||
child: Text(
|
||||
item['insurer_name'] ?? '-',
|
||||
style: _dataBold,
|
||||
softWrap: true,
|
||||
maxLines: 3,
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['status'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Transform.scale(
|
||||
scale: 0.6, // reduce size (0.7–0.9 works well)
|
||||
child: Switch(
|
||||
value: item['is_active'] == "1",
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
item['is_active'] = val ? "1" : "0";
|
||||
});
|
||||
final response = apiService.updateStatus(
|
||||
item['id'],
|
||||
val ? "1" : "0",
|
||||
'staff',
|
||||
);
|
||||
print("Response - $response");
|
||||
},
|
||||
activeColor: Color(0xFF425B5B), // thumb when active
|
||||
activeTrackColor: Color(0xFFB2D8D3), // track when active
|
||||
inactiveThumbColor: Colors.grey.shade400, // thumb when inactive
|
||||
inactiveTrackColor: Colors.grey.shade300, // track when inactive
|
||||
),
|
||||
child: Text(
|
||||
item['remarks'] ?? '-',
|
||||
style: _dataBold,
|
||||
softWrap: true,
|
||||
maxLines: 3,
|
||||
),
|
||||
),
|
||||
|
||||
@ -415,6 +484,123 @@ class enquiryListState extends State<enquiryList> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF6FEFD),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
border: Border.all(color: const Color(0xffD9EBE8)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(item['reg_no'] ?? '-', style: _headerStyle),
|
||||
PopupMenuButton<int>(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.zero,
|
||||
offset: Offset(0, 30),
|
||||
icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14),
|
||||
itemBuilder: (context) => [
|
||||
CustomPopupMenuEntry(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildPopupMenuActions(context, item),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(color: Color(0xffD9EBE8), thickness: 0.8),
|
||||
|
||||
// Company + Vehicle Reg No
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const Text("Company", style: _cardheaderStyle),
|
||||
Text(
|
||||
item['insurer_name'] ?? '-',
|
||||
style: _cardBodyStyle,
|
||||
maxLines: 3,
|
||||
softWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const Text("Status", style: _cardheaderStyle),
|
||||
Text(
|
||||
item['status'] ?? '-',
|
||||
style: _cardBodyStyle,
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// Created On + Remarks
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("Received Date & Time", style: _cardheaderStyle),
|
||||
Text(
|
||||
_formatDate(item['created_on']) ?? '-',
|
||||
style: _cardBodyStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text("Remarks", style: _cardheaderStyle),
|
||||
Text(
|
||||
item['remarks'] ?? '-',
|
||||
style: _cardBodyStyle,
|
||||
maxLines: 3,
|
||||
softWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final _dataBold = TextStyle(
|
||||
fontSize: 14,
|
||||
|
||||
@ -432,4 +618,15 @@ class enquiryListState extends State<enquiryList> {
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
static const _cardheaderStyle = TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
);
|
||||
|
||||
static const _cardBodyStyle = TextStyle(
|
||||
color: Color(0xFF545454),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import 'dart:convert';
|
||||
// import 'dart:io' as html;
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@ -17,9 +19,11 @@ import '../../../../data/utils/validators.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../providers/user_provider.dart';
|
||||
import '../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../themes/indicators/export_btn.dart';
|
||||
import '../../../themes/indicators/search_field_theme.dart';
|
||||
import '../../../themes/indicators/upload_doc_theme.dart';
|
||||
import '../../../themes/indicators/singleFileUpload.dart';
|
||||
// import '../../../themes/indicators/upload_doc_theme.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
class Agent extends ConsumerStatefulWidget {
|
||||
@ -34,10 +38,12 @@ class AgentState extends ConsumerState<Agent> {
|
||||
late ApiService apiService;
|
||||
List<String> tabHeader = ['name', 'email', 'mobile', 'code', 'address'];
|
||||
late String isActive = "1";
|
||||
html.File? passportFile;
|
||||
// html.File? docUploadedFile;
|
||||
// PlatformFile? passportFile;
|
||||
String? selectedFileNames;
|
||||
String? passportFileUrlFromApi;
|
||||
String? selectedId;
|
||||
PlatformFile? docUploadedFile;
|
||||
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
String? _token;
|
||||
@ -96,12 +102,15 @@ class AgentState extends ConsumerState<Agent> {
|
||||
|
||||
String? apiDocPath = data["certificate_file_name"];
|
||||
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
||||
print('apiDocPath - $apiDocPath');
|
||||
selectedFileNames = apiDocPath.split('/').last;
|
||||
print('selectedFileNames - $selectedFileNames');
|
||||
passportFileUrlFromApi = apiDocPath;
|
||||
passportFile = null;
|
||||
print('passportFileUrlFromApi - $passportFileUrlFromApi');
|
||||
docUploadedFile = null;
|
||||
} else {
|
||||
selectedFileNames = null;
|
||||
passportFile = null;
|
||||
docUploadedFile = null;
|
||||
passportFileUrlFromApi = null;
|
||||
}
|
||||
});
|
||||
@ -155,33 +164,64 @@ class AgentState extends ConsumerState<Agent> {
|
||||
|
||||
print("USerDAta - $userData");
|
||||
|
||||
// userData.forEach((key, value) {
|
||||
// request.fields[key] = value.toString();
|
||||
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||
// });
|
||||
|
||||
userData.forEach((key, value) {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||
if (key != 'certificate_file_name') {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded $key: ${request.fields[key]}");
|
||||
}
|
||||
});
|
||||
|
||||
// Attach file if selected
|
||||
if (passportFile != null) {
|
||||
// if (docUploadedFile != null) {
|
||||
// try {
|
||||
// final reader = html.FileReader();
|
||||
// reader.readAsArrayBuffer(docUploadedFile!);
|
||||
// await reader.onLoad.first;
|
||||
//
|
||||
// final data = reader.result as Uint8List;
|
||||
//
|
||||
// final multipartFile = http.MultipartFile.fromBytes(
|
||||
// 'certificate_file_name',
|
||||
// data,
|
||||
// filename: docUploadedFile!.name,
|
||||
// );
|
||||
//
|
||||
// request.files.add(multipartFile);
|
||||
// print("📎 File attached: ${docUploadedFile!.name}");
|
||||
// } catch (e) {
|
||||
// print("❌ Failed to read file: $e");
|
||||
// }
|
||||
// } else {
|
||||
// print("⚠️ No passport file selected.");
|
||||
// }
|
||||
|
||||
// Attach file if selected
|
||||
if (docUploadedFile != null) {
|
||||
try {
|
||||
final reader = html.FileReader();
|
||||
reader.readAsArrayBuffer(passportFile!);
|
||||
await reader.onLoad.first;
|
||||
|
||||
final data = reader.result as Uint8List;
|
||||
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'certificate_file_name',
|
||||
data,
|
||||
filename: passportFile!.name,
|
||||
);
|
||||
|
||||
request.files.add(multipartFile);
|
||||
print("📎 File attached: ${passportFile!.name}");
|
||||
if (docUploadedFile!.bytes != null) {
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'certificate_file_name',
|
||||
docUploadedFile!.bytes!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
} else if (docUploadedFile!.path != null) {
|
||||
final multipartFile = await http.MultipartFile.fromPath(
|
||||
'certificate_file_name',
|
||||
docUploadedFile!.path!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
}
|
||||
print("📎 File attached: ${docUploadedFile!.name}");
|
||||
} catch (e) {
|
||||
print("❌ Failed to read file: $e");
|
||||
print("❌ Failed to attach file: $e");
|
||||
}
|
||||
} else {
|
||||
print("⚠️ No passport file selected.");
|
||||
}
|
||||
|
||||
print(" Sending request with fields: ${request.fields}");
|
||||
@ -227,6 +267,37 @@ class AgentState extends ConsumerState<Agent> {
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> _downloadFile() async {
|
||||
// if (docUploadedFile != null) {
|
||||
// try {
|
||||
// if (kIsWeb) {
|
||||
// // ✅ Web: use bytes
|
||||
// final blob = html.Blob([docUploadedFile!.bytes!]);
|
||||
// final url = html.Url.createObjectUrlFromBlob(blob);
|
||||
//
|
||||
// final anchor = html.AnchorElement(href: url)
|
||||
// ..setAttribute('download', docUploadedFile!.name)
|
||||
// ..click();
|
||||
//
|
||||
// html.Url.revokeObjectUrl(url);
|
||||
// print("Download triggered successfully (web)!");
|
||||
// } else {
|
||||
// print("Local file path: ${docUploadedFile!.path}");
|
||||
// // Use open_filex to open it:
|
||||
// // await OpenFilex.open(docUploadedFile!.path!);
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print("Error during download: $e");
|
||||
// }
|
||||
// } else if (passportFileUrlFromApi != null) {
|
||||
// print("Download from API: $passportFileUrlFromApi");
|
||||
// final path =
|
||||
// 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
|
||||
//
|
||||
// apiService.getPdfDownload(path, selectedId);
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var controller in controllers.values) {
|
||||
@ -451,7 +522,9 @@ class AgentState extends ConsumerState<Agent> {
|
||||
children: [
|
||||
Text("Upload Certificate", style: _textStyle),
|
||||
SizedBox(width: 10),
|
||||
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
ThemedUploadField(
|
||||
@ -460,58 +533,25 @@ class AgentState extends ConsumerState<Agent> {
|
||||
onFileSelected: (fileName, file) {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
setState(() {
|
||||
passportFile = file;
|
||||
docUploadedFile = file;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
if (passportFile != null || passportFileUrlFromApi != null)
|
||||
// Centers the text
|
||||
if (docUploadedFile != null || passportFileUrlFromApi != null)
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print('DOWNLOAD - $passportFile');
|
||||
|
||||
if (passportFile != null) {
|
||||
try {
|
||||
// Create a blob from the response body
|
||||
final blob = html.Blob([passportFile!]);
|
||||
|
||||
// Generate a download URL for the blob
|
||||
final url = html.Url.createObjectUrlFromBlob(blob);
|
||||
|
||||
// Create a link element to trigger the download
|
||||
final anchor = html.AnchorElement(href: url)
|
||||
..setAttribute(
|
||||
'download',
|
||||
selectedFileNames ?? "document.pdf",
|
||||
)
|
||||
..click();
|
||||
|
||||
// Revoke the download URL to free up resources
|
||||
html.Url.revokeObjectUrl(url);
|
||||
|
||||
print("Download triggered successfully!");
|
||||
} catch (e) {
|
||||
print("Error during download: $e");
|
||||
}
|
||||
} else if (passportFileUrlFromApi != null) {
|
||||
print("Raw file path: $passportFileUrlFromApi");
|
||||
|
||||
final path =
|
||||
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
|
||||
|
||||
apiService.getPdfDownload(path, selectedId);
|
||||
} else {
|
||||
print("No file available to download.");
|
||||
}
|
||||
},
|
||||
onTap: () => apiService.downloadFile(
|
||||
apiUrl:
|
||||
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId',
|
||||
apiId: null,
|
||||
localFile: docUploadedFile,
|
||||
fileName: selectedFileNames,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
@ -519,9 +559,7 @@ class AgentState extends ConsumerState<Agent> {
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
children: const [
|
||||
Text(
|
||||
"Download",
|
||||
style: TextStyle(
|
||||
@ -529,9 +567,6 @@ class AgentState extends ConsumerState<Agent> {
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.white,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign
|
||||
.end, // Ensures text is centered within the Text widget
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(Icons.download, size: 13, color: Colors.white),
|
||||
@ -544,23 +579,146 @@ class AgentState extends ConsumerState<Agent> {
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildIncentiveFile() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Incentive File", style: _textStyle),
|
||||
SizedBox(width: 10),
|
||||
ThemedUploadField(
|
||||
hintText: "Upload Document",
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.26,
|
||||
onFileSelected: (fileName, file) {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
},
|
||||
),
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// children: [
|
||||
// ThemedUploadField(
|
||||
// hintText: selectedFileNames ?? "Upload Document",
|
||||
// txtwidth: MediaQuery.of(context).size.width * 0.26,
|
||||
//
|
||||
// onFileSelected: (fileName, file) {
|
||||
// // print("File picked: ${fileName}");
|
||||
// // print("Size: ${file.size}");
|
||||
// // print("Path: ${file.path}"); // works on mobile/desktop
|
||||
// // print("Bytes: ${file.bytes}");
|
||||
// print("Picked file: $fileName (${file.size} bytes)");
|
||||
// setState(() {
|
||||
// docUploadedFile = file;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// SizedBox(height: 5),
|
||||
//
|
||||
// if (docUploadedFile != null || passportFileUrlFromApi != null)
|
||||
// // Centers the text
|
||||
// Container(
|
||||
// color: Colors.white,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// print('DOWNLOAD - $docUploadedFile');
|
||||
//
|
||||
// if (docUploadedFile != null) {
|
||||
// try {
|
||||
// final blob = html.Blob([docUploadedFile!]);
|
||||
//
|
||||
// final url = html.Url.createObjectUrlFromBlob(blob);
|
||||
//
|
||||
// final anchor = html.AnchorElement(href: url)
|
||||
// ..setAttribute(
|
||||
// 'download',
|
||||
// selectedFileNames ?? "document.pdf",
|
||||
// )
|
||||
// ..click();
|
||||
//
|
||||
// html.Url.revokeObjectUrl(url);
|
||||
//
|
||||
// print("Download triggered successfully!");
|
||||
// } catch (e) {
|
||||
// print("Error during download: $e");
|
||||
// }
|
||||
// } else if (passportFileUrlFromApi != null) {
|
||||
// print("Raw file path: $passportFileUrlFromApi");
|
||||
//
|
||||
// final path =
|
||||
// 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
|
||||
//
|
||||
// apiService.getPdfDownload(path, selectedId);
|
||||
// } else {
|
||||
// print("No file available to download.");
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// // onTap: () {
|
||||
// // print('DOWNLOAD - $docUploadedFile');
|
||||
// //
|
||||
// // if (docUploadedFile != null) {
|
||||
// // try {
|
||||
// // if (kIsWeb) {
|
||||
// // // ✅ WEB: use bytes
|
||||
// // final blob = html.Blob([docUploadedFile!.bytes!]);
|
||||
// // final url = html.Url.createObjectUrlFromBlob(
|
||||
// // blob,
|
||||
// // );
|
||||
// //
|
||||
// // final anchor = html.AnchorElement(href: url)
|
||||
// // ..setAttribute(
|
||||
// // 'download',
|
||||
// // selectedFileNames ?? "document.pdf",
|
||||
// // )
|
||||
// // ..click();
|
||||
// //
|
||||
// // html.Url.revokeObjectUrl(url);
|
||||
// // print("Download triggered successfully (web)!");
|
||||
// // } else {
|
||||
// // // ✅ MOBILE/DESKTOP: use file path
|
||||
// // print(
|
||||
// // "Local file path: ${docUploadedFile!.path}",
|
||||
// // );
|
||||
// //
|
||||
// // // Example: open the file using open_filex
|
||||
// // // await OpenFilex.open(docUploadedFile!.path!);
|
||||
// // }
|
||||
// // } catch (e) {
|
||||
// // print("Error during download: $e");
|
||||
// // }
|
||||
// // } else if (passportFileUrlFromApi != null) {
|
||||
// // print("Raw file path: $passportFileUrlFromApi");
|
||||
// //
|
||||
// // final path =
|
||||
// // 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId';
|
||||
// //
|
||||
// // apiService.getPdfDownload(path, selectedId);
|
||||
// // } else {
|
||||
// // print("No file available to download.");
|
||||
// // }
|
||||
// // },
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(5),
|
||||
// color: Colors.green.shade300,
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Download",
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w200,
|
||||
// color: Colors.white,
|
||||
// ),
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// textAlign: TextAlign
|
||||
// .end, // Ensures text is centered within the Text widget
|
||||
// ),
|
||||
// SizedBox(width: 5),
|
||||
// Icon(Icons.download, size: 13, color: Colors.white),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart';
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@ -15,11 +16,12 @@ import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/validators.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../themes/indicators/date_field_theme.dart';
|
||||
import '../../../themes/indicators/input_field_decoration.dart';
|
||||
import '../../../themes/indicators/month_field_theme.dart';
|
||||
import '../../../themes/indicators/search_field_theme.dart';
|
||||
import '../../../themes/indicators/upload_doc_theme.dart';
|
||||
// import '../../../themes/indicators/upload_doc_theme.dart';
|
||||
|
||||
/// function to open the modal
|
||||
void showUploadIncentiveModal(BuildContext context) {
|
||||
@ -50,7 +52,8 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
List<Map<String, dynamic>> filteredIncentiveData = [];
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
|
||||
html.File? passportFile;
|
||||
// html.File? passportFile;
|
||||
PlatformFile? passportFile;
|
||||
String? selectedFileNames;
|
||||
|
||||
List<String> tabHeader = ['name', 'agentId', 'date', 'search'];
|
||||
@ -199,11 +202,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
}
|
||||
|
||||
Future<void> handleSave() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
print("Handl1");
|
||||
final dataSet = dataDetails();
|
||||
print("dataSetAgent - $dataSet");
|
||||
print("Handl13f");
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
if (passportFile == null) {
|
||||
print("❌ No file selected!");
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -233,35 +237,65 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
|
||||
print("USerDAta - $dataSet");
|
||||
|
||||
// dataSet.forEach((key, value) {
|
||||
// request.fields[key] = value.toString();
|
||||
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||
// });
|
||||
|
||||
dataSet.forEach((key, value) {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||
if (key != 'incentive_file_name') {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded $key: ${request.fields[key]}");
|
||||
}
|
||||
});
|
||||
|
||||
// Attach file if selected
|
||||
if (passportFile != null) {
|
||||
try {
|
||||
final reader = html.FileReader();
|
||||
reader.readAsArrayBuffer(passportFile!);
|
||||
await reader.onLoad.first;
|
||||
|
||||
final data = reader.result as Uint8List;
|
||||
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'incentive_file_name',
|
||||
data,
|
||||
filename: passportFile!.name,
|
||||
);
|
||||
|
||||
request.files.add(multipartFile);
|
||||
if (passportFile!.bytes != null) {
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'incentive_file_name',
|
||||
passportFile!.bytes!,
|
||||
filename: passportFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
} else if (passportFile!.path != null) {
|
||||
final multipartFile = await http.MultipartFile.fromPath(
|
||||
'certificate_file_name',
|
||||
passportFile!.path!,
|
||||
filename: passportFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
}
|
||||
print("📎 File attached: ${passportFile!.name}");
|
||||
} catch (e) {
|
||||
print("❌ Failed to read file: $e");
|
||||
print("❌ Failed to attach file: $e");
|
||||
}
|
||||
} else {
|
||||
print("⚠️ No passport file selected.");
|
||||
}
|
||||
|
||||
// Attach file if selected
|
||||
// if (passportFile != null) {
|
||||
// try {
|
||||
// final reader = html.FileReader();
|
||||
// reader.readAsArrayBuffer(passportFile! as html.Blob);
|
||||
// await reader.onLoad.first;
|
||||
//
|
||||
// final data = reader.result as Uint8List;
|
||||
//
|
||||
// final multipartFile = http.MultipartFile.fromBytes(
|
||||
// 'incentive_file_name',
|
||||
// data,
|
||||
// filename: passportFile!.name,
|
||||
// );
|
||||
//
|
||||
// request.files.add(multipartFile);
|
||||
// print("📎 File attached: ${passportFile!.name}");
|
||||
// } catch (e) {
|
||||
// print("❌ Failed to read file: $e");
|
||||
// }
|
||||
// } else {
|
||||
// print("⚠️ No passport file selected.");
|
||||
// }
|
||||
|
||||
print("🚀 Sending request with fields: ${request.fields}");
|
||||
|
||||
try {
|
||||
@ -276,7 +310,7 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
|
||||
print("📨 Response: ${response.body}");
|
||||
|
||||
final data = dataDetails(); // this is a Map<String, dynamic>
|
||||
final data = dataDetails();
|
||||
final agentId = data["agent_id"];
|
||||
|
||||
getAgentIncenctiveFileList(agentId);
|
||||
@ -492,12 +526,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
// value,
|
||||
// "Upload Document",
|
||||
// ),
|
||||
validator: (value) {
|
||||
if (passportFile == null) {
|
||||
return "Please upload a file";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
// validator: (value) {
|
||||
// if (passportFile == null) {
|
||||
// return "Please upload a file";
|
||||
// }
|
||||
// return null;
|
||||
// },
|
||||
backgroundColor: Color(0xFFECECEC),
|
||||
hintText:
|
||||
(selectedFileNames == null ||
|
||||
@ -507,9 +541,12 @@ class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
||||
txtwidth:
|
||||
MediaQuery.of(context).size.width * 0.19,
|
||||
onFileSelected: (fileName, file) {
|
||||
print(
|
||||
"Picked file: $fileName (${file.size} bytes)",
|
||||
);
|
||||
print("File picked: ${fileName}");
|
||||
// print("Size: ${file.size}");
|
||||
// print(
|
||||
// "Path: ${file.path}",
|
||||
// ); // works on mobile/desktop
|
||||
// print("Bytes: ${file.bytes}");
|
||||
setState(() {
|
||||
passportFile = file;
|
||||
selectedFileNames = fileName;
|
||||
|
||||
@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/utils/Pagination.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../layouts/responsive_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../themes/indicators/export_btn.dart';
|
||||
import '../../../themes/indicators/filter_btn.dart';
|
||||
@ -31,7 +32,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
List<Map<String, dynamic>> originalData = [];
|
||||
List<Map<String, dynamic>> filteredData = [];
|
||||
bool isLoading = false;
|
||||
|
||||
dynamic sortedData;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -55,10 +56,23 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
// }
|
||||
// }
|
||||
|
||||
// List<dynamic> get _paginatedData {
|
||||
// final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
// final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
||||
// return filteredData.sublist(startIndex, endIndex);
|
||||
// }
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
// Sort descending by id first
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
// Calculate pagination indices
|
||||
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
||||
return filteredData.sublist(startIndex, endIndex);
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||
|
||||
// Return only the paginated slice
|
||||
return sortedData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void filterData(String query) {
|
||||
@ -235,6 +249,9 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
ExportBtn(
|
||||
sheetName: "Agents",
|
||||
fileName: "agent_list",
|
||||
txt: !ResponsiveLayout.isMobile(context)
|
||||
? true
|
||||
: false,
|
||||
data: filteredData,
|
||||
headers: [
|
||||
"id",
|
||||
@ -278,6 +295,56 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
|
||||
Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('S.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Agent Name', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text('Email', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Phone Number', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Id', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Address', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Status', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Action', style: _headerStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(child: _buildClaimsDataTable(context)),
|
||||
],
|
||||
),
|
||||
@ -321,8 +388,13 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
);
|
||||
}
|
||||
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
// final sortedData = [..._paginatedData]
|
||||
// ..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
final sortedData = [..._paginatedData];
|
||||
|
||||
// final sortedData = [...filteredData]
|
||||
// ..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
return ListView.builder(
|
||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||||
@ -341,25 +413,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: const Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Agent Name', style: _headerStyle)),
|
||||
Expanded(flex: 3, child: Text('Email', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Id', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Address', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
|
||||
],
|
||||
),
|
||||
);
|
||||
return const SizedBox.shrink(); // empty widget instead of commenting
|
||||
}
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
@ -413,7 +467,7 @@ class AgentListState extends ConsumerState<AgentList> {
|
||||
});
|
||||
final response = apiService.updateStatus(
|
||||
item['id'],
|
||||
val ? "1" : "0",
|
||||
val ? "0" : "1",
|
||||
'agent',
|
||||
);
|
||||
print("Response - $response");
|
||||
|
||||
@ -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 {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
print("profileDataAPISERTOKEN - $_token");
|
||||
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
|
||||
setState(() {
|
||||
profileData = decodedToken['data'];
|
||||
});
|
||||
print('decodedTokenProfile : $decodedToken');
|
||||
print('decodedTokenProfile : $profileData');
|
||||
print('profileDatadecodedTokenProfile : $decodedToken');
|
||||
print('profileDatadecodedTokenProfile : $profileData');
|
||||
|
||||
await getList();
|
||||
}
|
||||
@ -144,255 +144,244 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MainLayout(
|
||||
title: 'Profile',
|
||||
body: Container(
|
||||
// color: Colors.white,
|
||||
padding: const EdgeInsets.all(6),
|
||||
width: 450,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (!ResponsiveLayout.isMobile(context))
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
isProfile ? 'My Profile' : 'Incentive Files',
|
||||
style: _topheaderStyle,
|
||||
),
|
||||
Spacer(),
|
||||
Container(
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(); // closes the popup
|
||||
},
|
||||
child: const Icon(Icons.close, size: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
width: 450,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
isProfile ? 'My Profile' : 'Incentive Files',
|
||||
style: _topheaderStyle,
|
||||
),
|
||||
if (ResponsiveLayout.isMobile(context))
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
isProfile ? '' : 'Incentive Files',
|
||||
style: _topheaderStyle,
|
||||
),
|
||||
],
|
||||
Spacer(),
|
||||
Container(
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(); // closes the popup
|
||||
},
|
||||
child: const Icon(Icons.close, size: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
isProfile
|
||||
? Container(
|
||||
color: Colors.white,
|
||||
padding: ResponsiveLayout.isMobile(context)
|
||||
? EdgeInsets.all(16.0)
|
||||
: null,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 25),
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFD9D9D9),
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.person_outline,
|
||||
size: 50,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(profileData?['name'] ?? '-', style: _headerStyle),
|
||||
Text(
|
||||
profileData?['emp_id'] ?? '-',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 26),
|
||||
Row(
|
||||
isProfile
|
||||
? Container(
|
||||
color: Colors.white,
|
||||
padding: ResponsiveLayout.isMobile(context)
|
||||
? EdgeInsets.all(16.0)
|
||||
: null,
|
||||
child: isLoading
|
||||
? const Center(child: Text("No incentive files found"))
|
||||
: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.phone_outlined,
|
||||
size: 20,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
profileData?['mobile'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.email_outlined,
|
||||
size: 18,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
profileData?['email'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (profileData?['role_id'] != '1' &&
|
||||
profileData?['role_id'] != '2') ...[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.location_on_outlined,
|
||||
size: 18,
|
||||
const SizedBox(height: 25),
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFD9D9D9),
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.person_outline,
|
||||
size: 50,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
Expanded(
|
||||
child: Text(
|
||||
profileData?['address'] ?? '-',
|
||||
style: _dataBold,
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
profileData?['name'] ?? '-',
|
||||
style: _headerStyle,
|
||||
),
|
||||
Text(
|
||||
profileData?['emp_id'] ?? '-',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
|
||||
if (profileData?['role_id'] != 2) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
color: Color(0xffEDF6F5),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
print("Download - $latestId");
|
||||
dynamic path;
|
||||
|
||||
if (roleId == 1) {
|
||||
path =
|
||||
'api/agent/downloadManagerIncentiveFile?id=$latestId';
|
||||
} else if (roleId != 1 && roleId != 2) {
|
||||
path =
|
||||
'api/agent/downloadAgentIncentiveFile?id=$latestId';
|
||||
}
|
||||
apiService.getPdfDownload(path, latestId);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
),
|
||||
const SizedBox(height: 26),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.phone_outlined,
|
||||
size: 20,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
profileData?['mobile'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.email_outlined,
|
||||
size: 18,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
profileData?['email'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (profileData?['role_id'] != '1' &&
|
||||
profileData?['role_id'] != '2') ...[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(Icons.download),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
"Download Incentive file",
|
||||
style: _dataSub,
|
||||
const Icon(
|
||||
Icons.location_on_outlined,
|
||||
size: 18,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
||||
Expanded(
|
||||
child: Text(
|
||||
profileData?['address'] ?? '-',
|
||||
style: _dataBold,
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
isProfile = false;
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
"View Previous Files",
|
||||
style: TextStyle(
|
||||
color: Color(0xff425B5B),
|
||||
decoration:
|
||||
TextDecoration.underline, // 👈 underline
|
||||
decorationThickness: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
color: Colors.white,
|
||||
padding: ResponsiveLayout.isMobile(context)
|
||||
? EdgeInsets.all(16.0)
|
||||
: null,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: filteredIncentiveData.isEmpty
|
||||
? const Center(
|
||||
child: Text("No incentive files found"),
|
||||
)
|
||||
: SizedBox(
|
||||
height: ResponsiveLayout.isMobile(context)
|
||||
? MediaQuery.of(context).size.height * 0.4
|
||||
: 450,
|
||||
child: ListView.builder(
|
||||
// padding: const EdgeInsets.all(8),
|
||||
itemCount: filteredIncentiveData.length,
|
||||
itemBuilder: (context, index) {
|
||||
final file = filteredIncentiveData[index];
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2.0,
|
||||
),
|
||||
child: IncentiveFileRow(
|
||||
fileName:
|
||||
file['incentive_file_name'] ??
|
||||
"Unknown",
|
||||
date: file['incentive_month'] ?? "-",
|
||||
onUpload: () {
|
||||
print("Upload ${file['id']}");
|
||||
final selectedId = file['id'];
|
||||
if (profileData?['role_id'] != 2) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
color: Color(0xffEDF6F5),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
print("Download - $latestId");
|
||||
dynamic path;
|
||||
|
||||
dynamic path;
|
||||
|
||||
if (roleId == 1) {
|
||||
path =
|
||||
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
|
||||
} else if (roleId != 1 &&
|
||||
roleId != 2) {
|
||||
path =
|
||||
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
|
||||
}
|
||||
apiService.getPdfDownload(
|
||||
path,
|
||||
selectedId,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
if (roleId == 1) {
|
||||
path =
|
||||
'api/agent/downloadManagerIncentiveFile?id=$latestId';
|
||||
} else if (roleId != 1 && roleId != 2) {
|
||||
path =
|
||||
'api/agent/downloadAgentIncentiveFile?id=$latestId';
|
||||
}
|
||||
apiService.getPdfDownload(path, latestId);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.download),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
"Download Incentive file",
|
||||
style: _dataSub,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
isProfile = false;
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
"View Previous Files",
|
||||
style: TextStyle(
|
||||
color: Color(0xff425B5B),
|
||||
decoration: TextDecoration
|
||||
.underline, // 👈 underline
|
||||
decorationThickness: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
color: Colors.white,
|
||||
padding: ResponsiveLayout.isMobile(context)
|
||||
? EdgeInsets.all(16.0)
|
||||
: null,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: filteredIncentiveData.isEmpty
|
||||
? const Center(
|
||||
child: Text("No incentive files found"),
|
||||
)
|
||||
: SizedBox(
|
||||
height: ResponsiveLayout.isMobile(context)
|
||||
? MediaQuery.of(context).size.height * 0.4
|
||||
: 450,
|
||||
child: ListView.builder(
|
||||
// padding: const EdgeInsets.all(8),
|
||||
itemCount: filteredIncentiveData.length,
|
||||
itemBuilder: (context, index) {
|
||||
final file = filteredIncentiveData[index];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2.0,
|
||||
),
|
||||
child: IncentiveFileRow(
|
||||
fileName:
|
||||
file['incentive_file_name'] ??
|
||||
"Unknown",
|
||||
date: file['incentive_month'] ?? "-",
|
||||
onUpload: () {
|
||||
print("Upload ${file['id']}");
|
||||
final selectedId = file['id'];
|
||||
|
||||
dynamic path;
|
||||
|
||||
if (roleId == 1) {
|
||||
path =
|
||||
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
|
||||
} else if (roleId != 1 && roleId != 2) {
|
||||
path =
|
||||
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
|
||||
}
|
||||
apiService.getPdfDownload(
|
||||
path,
|
||||
selectedId,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,13 +12,10 @@ import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.d
|
||||
import '../../../../core/config/env.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/Pagination.dart';
|
||||
import '../../../../data/utils/validators.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../themes/indicators/export_btn.dart';
|
||||
import '../../../themes/indicators/search_field_theme.dart';
|
||||
import '../../../themes/indicators/upload_doc_theme.dart';
|
||||
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
class Staff extends ConsumerStatefulWidget {
|
||||
|
||||
@ -7,6 +7,7 @@ import '../../../../core/routing/routes.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/utils/Pagination.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../layouts/responsive_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../themes/indicators/export_btn.dart';
|
||||
import '../../../themes/indicators/filter_btn.dart';
|
||||
@ -56,9 +57,14 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
// }
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
// Sort descending by id first
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
||||
return filteredData.sublist(startIndex, endIndex);
|
||||
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
|
||||
|
||||
return sortedData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void filterData(String query) {
|
||||
@ -210,6 +216,9 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
ExportBtn(
|
||||
sheetName: "Staff",
|
||||
fileName: "staff_list",
|
||||
txt: !ResponsiveLayout.isMobile(context)
|
||||
? true
|
||||
: false,
|
||||
data: filteredData,
|
||||
headers: [
|
||||
"id",
|
||||
@ -253,6 +262,48 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('S.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Staff Name', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text('Email', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Phone Number', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Employee Id', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Status', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Action', style: _headerStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: _buildDataTable(context)),
|
||||
],
|
||||
),
|
||||
@ -296,8 +347,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
);
|
||||
}
|
||||
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
final sortedData = [..._paginatedData];
|
||||
|
||||
return ListView.builder(
|
||||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||||
@ -316,24 +366,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: const Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Staff Name', style: _headerStyle)),
|
||||
Expanded(flex: 3, child: Text('Email', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Employee Id', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
|
||||
],
|
||||
),
|
||||
);
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
@ -381,7 +414,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
});
|
||||
final response = apiService.updateStatus(
|
||||
item['id'],
|
||||
val ? "1" : "0",
|
||||
val ? "0" : "1",
|
||||
'staff',
|
||||
);
|
||||
print("Response - $response");
|
||||
|
||||
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 List<Map<String, dynamic>> data;
|
||||
final List<String> headers; // dynamic headers
|
||||
|
||||
final bool? txt;
|
||||
const ExportBtn({
|
||||
super.key,
|
||||
this.txt,
|
||||
required this.sheetName,
|
||||
required this.fileName,
|
||||
required this.data,
|
||||
@ -50,15 +51,17 @@ class ExportBtn extends HookWidget {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Export',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
?txt!
|
||||
? Text(
|
||||
'Export',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
?txt! ? SizedBox(width: 20) : null,
|
||||
Icon(Icons.input_sharp, color: Colors.white),
|
||||
// Image.asset("assets/miscellaneous/export", height: 15, width: 15),
|
||||
],
|
||||
|
||||
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 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class ThemedUploadField extends FormField<String> {
|
||||
ThemedUploadField({
|
||||
super.key,
|
||||
String? hintText,
|
||||
double? txtwidth,
|
||||
double? txtheight,
|
||||
Color? backgroundColor,
|
||||
Color? borderColor,
|
||||
Color? errorBorderColor,
|
||||
FormFieldValidator<String>? validator,
|
||||
Function(String fileName, dynamic file)? onFileSelected,
|
||||
}) : super(
|
||||
validator: validator,
|
||||
builder: (FormFieldState<String> state) {
|
||||
return _ThemedUploadFieldBody(
|
||||
hintText: hintText,
|
||||
txtwidth: txtwidth,
|
||||
txtheight: txtheight,
|
||||
backgroundColor: backgroundColor,
|
||||
borderColor: borderColor,
|
||||
errorBorderColor: errorBorderColor,
|
||||
onFileSelected: (fileName, file) {
|
||||
state.didChange(fileName); // update validation state
|
||||
onFileSelected?.call(fileName, file);
|
||||
},
|
||||
errorText: state.errorText,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _ThemedUploadFieldBody extends StatefulWidget {
|
||||
const _ThemedUploadFieldBody({
|
||||
this.hintText,
|
||||
this.txtwidth,
|
||||
this.txtheight,
|
||||
this.backgroundColor,
|
||||
this.borderColor,
|
||||
this.errorBorderColor,
|
||||
this.onFileSelected,
|
||||
this.errorText,
|
||||
});
|
||||
|
||||
final String? hintText;
|
||||
final double? txtwidth;
|
||||
final double? txtheight;
|
||||
final Color? backgroundColor;
|
||||
final Color? borderColor;
|
||||
final Color? errorBorderColor;
|
||||
final Function(String fileName, dynamic file)? onFileSelected;
|
||||
final String? errorText;
|
||||
|
||||
@override
|
||||
State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
|
||||
}
|
||||
|
||||
class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
|
||||
String? selectedFileName;
|
||||
|
||||
void pickPDFWeb() {
|
||||
final uploadInput = html.FileUploadInputElement();
|
||||
uploadInput.accept = '.pdf';
|
||||
uploadInput.click();
|
||||
|
||||
uploadInput.onChange.listen((e) {
|
||||
final file = uploadInput.files!.first;
|
||||
|
||||
// Ensure PDF type
|
||||
if (!file.type.contains("pdf")) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
|
||||
return;
|
||||
}
|
||||
|
||||
// Size check (3 MB)
|
||||
const maxFileSize = 3 * 1024 * 1024;
|
||||
if (file.size > maxFileSize) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => selectedFileName = file.name);
|
||||
widget.onFileSelected?.call(file.name, file);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
||||
height: widget.txtheight,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
||||
child: InkWell(
|
||||
onTap: pickPDFWeb,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor ?? Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.errorText != null
|
||||
? (widget.errorBorderColor ?? Colors.red)
|
||||
: (widget.borderColor ?? Colors.grey.shade50),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
selectedFileName ??
|
||||
widget.hintText ??
|
||||
"Upload Documents",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: widget.errorText != null
|
||||
? Colors.red
|
||||
: Colors.black,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const Icon(Icons.file_upload_outlined, color: Colors.black),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// if (widget.errorText != null)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(left: 8, top: 4),
|
||||
// child: Text(
|
||||
// widget.errorText!,
|
||||
// style: const TextStyle(color: Colors.red, fontSize: 10),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
// import 'dart:html' as html;
|
||||
// import 'package:file_picker/file_picker.dart';
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:google_fonts/google_fonts.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
//
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:file_picker/file_picker.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import 'package:http_parser/http_parser.dart';
|
||||
// import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
//
|
||||
// class ThemedUploadField extends FormField<String> {
|
||||
// ThemedUploadField({
|
||||
// super.key,
|
||||
// String? hintText,
|
||||
// double? txtwidth,
|
||||
// double? txtheight,
|
||||
// Color? backgroundColor,
|
||||
// Color? borderColor,
|
||||
// Color? errorBorderColor,
|
||||
// FormFieldValidator<String>? validator,
|
||||
// Function(String fileName, dynamic file)? onFileSelected,
|
||||
// }) : super(
|
||||
// validator: validator,
|
||||
// builder: (FormFieldState<String> state) {
|
||||
// return _ThemedUploadFieldBody(
|
||||
// hintText: hintText,
|
||||
// txtwidth: txtwidth,
|
||||
// txtheight: txtheight,
|
||||
// backgroundColor: backgroundColor,
|
||||
// borderColor: borderColor,
|
||||
// errorBorderColor: errorBorderColor,
|
||||
// onFileSelected: (fileName, file) {
|
||||
// state.didChange(fileName); // update validation state
|
||||
// onFileSelected?.call(fileName, file);
|
||||
// },
|
||||
// errorText: state.errorText,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// class _ThemedUploadFieldBody extends StatefulWidget {
|
||||
// const _ThemedUploadFieldBody({
|
||||
// this.hintText,
|
||||
// this.txtwidth,
|
||||
// this.txtheight,
|
||||
// this.backgroundColor,
|
||||
// this.borderColor,
|
||||
// this.errorBorderColor,
|
||||
// this.onFileSelected,
|
||||
// this.errorText,
|
||||
// });
|
||||
//
|
||||
// final String? hintText;
|
||||
// final double? txtwidth;
|
||||
// final double? txtheight;
|
||||
// final Color? backgroundColor;
|
||||
// final Color? borderColor;
|
||||
// final Color? errorBorderColor;
|
||||
// final Function(String fileName, dynamic file)? onFileSelected;
|
||||
// final String? errorText;
|
||||
//
|
||||
// @override
|
||||
// State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState();
|
||||
// }
|
||||
//
|
||||
// class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> {
|
||||
// String? selectedFileName;
|
||||
//
|
||||
// // void pickPDFWeb() {
|
||||
// // final uploadInput = html.FileUploadInputElement();
|
||||
// // uploadInput.accept = '.pdf';
|
||||
// // uploadInput.click();
|
||||
// //
|
||||
// // uploadInput.onChange.listen((e) {
|
||||
// // final file = uploadInput.files!.first;
|
||||
// //
|
||||
// // // Ensure PDF type
|
||||
// // if (!file.type.contains("pdf")) {
|
||||
// // ScaffoldMessenger.of(
|
||||
// // context,
|
||||
// // ).showSnackBar(SnackBar(content: Text("Only PDF files allowed")));
|
||||
// // return;
|
||||
// // }
|
||||
// //
|
||||
// // // Size check (3 MB)
|
||||
// // const maxFileSize = 3 * 1024 * 1024;
|
||||
// // if (file.size > maxFileSize) {
|
||||
// // ScaffoldMessenger.of(
|
||||
// // context,
|
||||
// // ).showSnackBar(SnackBar(content: Text("File exceeds 3MB")));
|
||||
// // return;
|
||||
// // }
|
||||
// //
|
||||
// // setState(() => selectedFileName = file.name);
|
||||
// // widget.onFileSelected?.call(file.name, file);
|
||||
// // });
|
||||
// // }
|
||||
//
|
||||
// Future<void> pickPDF() async {
|
||||
// try {
|
||||
// final result = await FilePicker.platform.pickFiles(
|
||||
// type: FileType.custom,
|
||||
// allowedExtensions: ['pdf'],
|
||||
// withData: true,
|
||||
// );
|
||||
//
|
||||
// if (result != null && result.files.isNotEmpty) {
|
||||
// final file = result.files.first; // PlatformFile
|
||||
//
|
||||
// if (file.extension?.toLowerCase() != "pdf") {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(content: Text("Only PDF files allowed")),
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (file.size > 3 * 1024 * 1024) {
|
||||
// ScaffoldMessenger.of(
|
||||
// context,
|
||||
// ).showSnackBar(const SnackBar(content: Text("File exceeds 3MB")));
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// setState(() => selectedFileName = file.name);
|
||||
//
|
||||
// // Always pass PlatformFile
|
||||
// widget.onFileSelected?.call(file.name, file);
|
||||
// }
|
||||
// } catch (e, st) {
|
||||
// debugPrint("File picker error: $e\n$st"); // 👈 safer logging
|
||||
// // ScaffoldMessenger.of(
|
||||
// // context,
|
||||
// // ).showSnackBar(const SnackBar(content: Text("Error picking file")));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Container(
|
||||
// width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
||||
// height: widget.txtheight,
|
||||
// child: DecoratedBox(
|
||||
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)),
|
||||
// child: InkWell(
|
||||
// onTap: pickPDF,
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// vertical: 10,
|
||||
// horizontal: 15,
|
||||
// ),
|
||||
// decoration: BoxDecoration(
|
||||
// color: widget.backgroundColor ?? Colors.white,
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// border: Border.all(
|
||||
// color: widget.errorText != null
|
||||
// ? (widget.errorBorderColor ?? Colors.red)
|
||||
// : (widget.borderColor ?? Colors.grey.shade50),
|
||||
// width: 1,
|
||||
// ),
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// selectedFileName ??
|
||||
// widget.hintText ??
|
||||
// "Upload Documents",
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// color: widget.errorText != null
|
||||
// ? Colors.red
|
||||
// : Colors.black,
|
||||
// ),
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// const Icon(Icons.file_upload_outlined, color: Colors.black),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// // if (widget.errorText != null)
|
||||
// // Padding(
|
||||
// // padding: const EdgeInsets.only(left: 8, top: 4),
|
||||
// // child: Text(
|
||||
// // widget.errorText!,
|
||||
// // style: const TextStyle(color: Colors.red, fontSize: 10),
|
||||
// // ),
|
||||
// // ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -72,7 +72,7 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
|
||||
if (key == 'Reports') ...[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print("Tappped Agent");
|
||||
context.go(AppRoutes.enquiryLst);
|
||||
},
|
||||
child: const Text(
|
||||
"Enquiries",
|
||||
@ -175,7 +175,9 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
|
||||
|
||||
DrawerContentWrapper(
|
||||
child: IconButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
context.go(AppRoutes.tabEnquiry);
|
||||
},
|
||||
icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black),
|
||||
),
|
||||
),
|
||||
|
||||
@ -20,9 +20,12 @@ class _MobileTabsState extends State<MobileTabs> {
|
||||
widget.onTabChanged(index);
|
||||
},
|
||||
items: const [
|
||||
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: "Home"),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profile"),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Settings"),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.event_note_sharp),
|
||||
label: "Enquiry",
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import connectivity_plus
|
||||
import file_picker
|
||||
import firebase_app_check
|
||||
import firebase_auth
|
||||
import firebase_core
|
||||
@ -21,6 +22,7 @@ import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
|
||||
16
pubspec.lock
16
pubspec.lock
@ -201,6 +201,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -297,6 +305,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.2"
|
||||
firebase_app_check:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -67,6 +67,7 @@ dependencies:
|
||||
open_file: ^3.5.10
|
||||
excel: ^4.0.6
|
||||
dropdown_search: ^6.0.2
|
||||
file_picker: ^10.3.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user