pay out module
This commit is contained in:
commit
12daf54cb7
File diff suppressed because one or more lines are too long
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.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/Masters/PaymentMode/paymentList.dart';
|
||||
import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart';
|
||||
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
|
||||
import 'package:nhance_partner/presentation/screens/payout/invoice_list.dart';
|
||||
@ -12,6 +13,7 @@ import '../../presentation/providers/manager_provider.dart';
|
||||
import '../../presentation/screens/Enquiry/enquiry/tabs.dart';
|
||||
import '../../presentation/screens/Enquiry/policy_claims_endros/claims.dart';
|
||||
import '../../presentation/screens/Enquiry/policy_claims_endros/endrosment.dart';
|
||||
import '../../presentation/screens/Masters/Brokers/brokerList.dart';
|
||||
import '../../presentation/screens/StaffAttendance/attendanceAllDetails.dart';
|
||||
import '../../presentation/screens/StaffAttendance/individual_Attendance.dart';
|
||||
import '../../presentation/screens/UserManagement/Agent/agent.dart';
|
||||
@ -192,6 +194,16 @@ final GoRouter appRouter = GoRouter(
|
||||
path: AppRoutes.individualAttendance,
|
||||
builder: (context, state) => const IndividualAttendanceDetails(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.brokerLst,
|
||||
builder: (context, state) => const BrokerList(),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.paymentModeLst,
|
||||
builder: (context, state) => const PaymentLsit(),
|
||||
),
|
||||
],
|
||||
redirect: (context, state) async {
|
||||
final loggedIn = await AuthService.isLoggedIn();
|
||||
|
||||
@ -24,4 +24,6 @@ class AppRoutes {
|
||||
|
||||
static const String payout = '/payout';
|
||||
static const String invoiceList = '/invoiceList';
|
||||
static const String brokerLst = '/brokerLst';
|
||||
static const String paymentModeLst = '/paymentMode';
|
||||
}
|
||||
|
||||
@ -342,14 +342,8 @@ class ApiService {
|
||||
final url;
|
||||
|
||||
if (role == 'staff') {
|
||||
// url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/staff/changeStaffStatus',
|
||||
// );
|
||||
url = Uri.parse('${Env.apiUrl}staff/changeStaffStatus');
|
||||
} else {
|
||||
// url = Uri.parse(
|
||||
// 'https://venbait.in/nhance/partner/dev/api/agent/changeAgentStatus',
|
||||
// );
|
||||
url = Uri.parse('${Env.apiUrl}/api/agent/changeAgentStatus');
|
||||
}
|
||||
|
||||
@ -372,6 +366,44 @@ class ApiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> updateStatusMasters(
|
||||
id,
|
||||
status,
|
||||
masterName,
|
||||
userId,
|
||||
) async {
|
||||
print('updateStatusMaster $id $status $masterName $userId');
|
||||
final Map<String, dynamic> data = {
|
||||
"is_active": int.parse(status),
|
||||
"updated_by": userId,
|
||||
};
|
||||
final url;
|
||||
|
||||
if (masterName == 'Broker') {
|
||||
url = Uri.parse('${Env.apiUrl}master/updateBrokerStatus/$id');
|
||||
} else {
|
||||
url = Uri.parse('${Env.apiUrl}master/updatePaymentModeStatus/$id');
|
||||
}
|
||||
|
||||
// final token = await getToken(); // Fetch token
|
||||
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
print("data------- $data}");
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $_token',
|
||||
'Content-Type': 'application/json',
|
||||
'app-signature': Env.App_Signature,
|
||||
// 'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
|
||||
};
|
||||
|
||||
final response = await _makePostRequestJson(url, data, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
// ----------------------------------- Dashboard -------------------------------------------------
|
||||
Future<Map<String, dynamic>> fetchDashboard(int id, role, userId) async {
|
||||
// print(_token);
|
||||
@ -1005,6 +1037,8 @@ class ApiService {
|
||||
url = Uri.parse('${Env.apiUrl}master/getEndorsementMaster');
|
||||
} else if (val == 'Broker') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getAllBrokers');
|
||||
} else if (val == 'PaymentMode') {
|
||||
url = Uri.parse('${Env.apiUrl}master/getAllPaymentModelist');
|
||||
}
|
||||
|
||||
final headers = {
|
||||
|
||||
@ -146,6 +146,26 @@ class _AppHeaderState extends ConsumerState<AppHeader> {
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
if (key == 'Masters') ...[
|
||||
if (role == 'manager') ...[
|
||||
_buildPopupItem(
|
||||
label: "Broker",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.brokerLst);
|
||||
},
|
||||
),
|
||||
],
|
||||
SizedBox(height: 2),
|
||||
_buildPopupItem(
|
||||
label: "Payment Mode",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.paymentModeLst);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -227,6 +247,16 @@ class _AppHeaderState extends ConsumerState<AppHeader> {
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
// User (Manager Only) - with hover popup
|
||||
if (roleId == 'manager') ...[
|
||||
_buildHoverMenuItem(
|
||||
icon: Icons.settings_suggest_outlined,
|
||||
label: "Masters",
|
||||
popupKey: 'Masters',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
// Reports (Not Staff) - with hover popup
|
||||
if (roleId != 'staff') ...[
|
||||
_buildHoverMenuItem(
|
||||
|
||||
310
lib/presentation/screens/Masters/Brokers/broker.dart
Normal file
310
lib/presentation/screens/Masters/Brokers/broker.dart
Normal file
@ -0,0 +1,310 @@
|
||||
import 'dart:convert';
|
||||
// import 'dart:io' as html;
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nhance_partner/core/routing/routes.dart';
|
||||
import 'package:nhance_partner/data/utils/toastNotification.dart';
|
||||
import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart';
|
||||
|
||||
import '../../../../core/config/env.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/validators.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../layouts/responsive_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
import '../../../themes/indicators/input_field_decoration.dart';
|
||||
|
||||
class Broker extends ConsumerStatefulWidget {
|
||||
final String? id;
|
||||
final Map<String, dynamic>? data;
|
||||
final VoidCallback onSubmit;
|
||||
const Broker({super.key, this.id, this.data, required this.onSubmit});
|
||||
@override
|
||||
ConsumerState<Broker> createState() => BrokerState();
|
||||
}
|
||||
|
||||
class BrokerState extends ConsumerState<Broker> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late ApiService apiService;
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownKeyHandler = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
List<String> tabHeader = ['name', 'email', 'mobile', 'code'];
|
||||
late String isActive = "1";
|
||||
html.File? passportFile;
|
||||
String? selectedFileNames;
|
||||
String? passportFileUrlFromApi;
|
||||
dynamic selectedId;
|
||||
String? _token;
|
||||
bool isLoading = false;
|
||||
bool showHandler = false;
|
||||
|
||||
String? selectedRole;
|
||||
List<Map<String, dynamic>> filteredRolesData = [];
|
||||
List<Map<String, dynamic>> getRolesData = [];
|
||||
|
||||
List<dynamic>? selectedHandlerIds = [];
|
||||
String? selectedHandler;
|
||||
List<Map<String, dynamic>> filteredHandlersData = [];
|
||||
List<Map<String, dynamic>> getHandlersData = [];
|
||||
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
|
||||
dynamic userId;
|
||||
dynamic managerId;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
final data = {"name": controllers["name"]?.text, "is_active": isActive};
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService();
|
||||
for (String field in tabHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
}
|
||||
|
||||
_initializeToken();
|
||||
updateData();
|
||||
|
||||
Future.microtask(() {
|
||||
managerId = ref.watch(managerIdProvider);
|
||||
userId = ref.watch(userIdProvider);
|
||||
if (managerId != null) {
|
||||
print('hansles');
|
||||
// getHandlers(managerId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
}
|
||||
|
||||
void updateData() async {
|
||||
if (widget.data != null) {
|
||||
dynamic id = widget.data?['id'];
|
||||
final data = widget.data;
|
||||
|
||||
if (data == null) return;
|
||||
setState(() {
|
||||
selectedId = id;
|
||||
controllers['name']?.text = data['name'] ?? '';
|
||||
controllers["is_active"]?.text = data['is_active'].toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleSave() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
setState(() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
dataDetails();
|
||||
final dataSet = dataDetails();
|
||||
print("dataSetAgent - $dataSet");
|
||||
|
||||
print("TYPE of is_active → ${dataSet['is_active'].runtimeType}");
|
||||
print("TYPE of name → ${dataSet['name'].runtimeType}");
|
||||
createUserData(dataSet);
|
||||
} else {
|
||||
// isDisable = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
print('REfresj');
|
||||
widget.onSubmit();
|
||||
setState(() {
|
||||
// clear all text controllers
|
||||
for (var controller in controllers.values) {
|
||||
controller.clear();
|
||||
} // 👈 clear selected agent
|
||||
controllers['name']?.clear();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createUserData(data) async {
|
||||
final bool isUpdating = selectedId != null;
|
||||
final dynamic id = isUpdating ? int.tryParse(selectedId.toString()) : 0;
|
||||
final String apiUrldata;
|
||||
|
||||
print("TYPE of id → $id - ${id.runtimeType}");
|
||||
print("TYPE of id → $userId - ${userId.runtimeType}");
|
||||
|
||||
apiUrldata = isUpdating
|
||||
? '${Env.apiUrl}/master/updateBroker/$selectedId'
|
||||
: '${Env.apiUrl}/master/createBroker';
|
||||
|
||||
// final token = await getToken(); // Fetch token
|
||||
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
if (isUpdating) {
|
||||
data['updated_by'] = userId.toString();
|
||||
} else {
|
||||
data['created_by'] = userId.toString();
|
||||
}
|
||||
|
||||
print("data------- $data}");
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $_token',
|
||||
'Content-Type': 'application/json',
|
||||
'app-signature': Env.App_Signature,
|
||||
},
|
||||
body: jsonEncode(data), // Convert map to JSON
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("Broker submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
refresh();
|
||||
isUpdating
|
||||
? ToastHelper.showSuccessToast(
|
||||
context,
|
||||
'Broker Updated Successfully',
|
||||
)
|
||||
: ToastHelper.showSuccessToast(
|
||||
context,
|
||||
'Broker Created Successfully',
|
||||
);
|
||||
} else if (response.statusCode == 403) {
|
||||
await apiService.clearLocalStorageAndRedirect();
|
||||
} else {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
dynamic msg = responseBody['data'];
|
||||
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("Broker Creation Failed"),
|
||||
content: Text(msg),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text("OK"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting Staff: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var controller in controllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
buildFormFields(),
|
||||
SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
handleSave();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 6),
|
||||
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: Color(0xFF2E7D6E),
|
||||
),
|
||||
child: Text(
|
||||
'Save',
|
||||
style: GoogleFonts.inter(color: Colors.white, fontSize: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
refresh();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(6.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2E7D6E),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
child: Icon(Icons.refresh, size: 13, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFormFields() {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Column(children: [buildName()]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildName() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Broker *", style: _textStyle),
|
||||
SizedBox(width: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['name']!,
|
||||
validator: (value) => Validators.requiredField(value, "name"),
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.15,
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
highlightColor: Color(0xFF50A398),
|
||||
txtheight: 30,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static final _textStyle = GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
475
lib/presentation/screens/Masters/Brokers/brokerList.dart
Normal file
475
lib/presentation/screens/Masters/Brokers/brokerList.dart
Normal file
@ -0,0 +1,475 @@
|
||||
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:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:nhance_partner/presentation/screens/Masters/Brokers/broker.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/filter_btn.dart';
|
||||
import '../../../themes/indicators/search_field_theme.dart';
|
||||
import '../../../themes/indicators/text_field_theme.dart';
|
||||
import '../../../widgets/custom_action_popup.dart';
|
||||
|
||||
class BrokerList extends ConsumerStatefulWidget {
|
||||
const BrokerList({super.key});
|
||||
@override
|
||||
ConsumerState<BrokerList> createState() => BrokerListState();
|
||||
}
|
||||
|
||||
class BrokerListState extends ConsumerState<BrokerList> {
|
||||
int currentPage = 1;
|
||||
int itemsPerPage = 10;
|
||||
late ApiService apiService;
|
||||
dynamic managerId;
|
||||
dynamic role;
|
||||
dynamic prefid;
|
||||
dynamic userId;
|
||||
// List<Map<String, dynamic>> dataVal = [];
|
||||
List<Map<String, dynamic>> getBrokerData = [];
|
||||
List<Map<String, dynamic>> originalData = [];
|
||||
List<Map<String, dynamic>> filteredData = [];
|
||||
bool isLoading = false;
|
||||
|
||||
Map<String, dynamic>? selectedBroker;
|
||||
dynamic selectedId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
apiService = ApiService();
|
||||
|
||||
Future.microtask(() {
|
||||
final data1 = ref.read(managerIdProvider);
|
||||
userId = ref.watch(userIdProvider);
|
||||
// final data2 = ref.read(handlerIdProvider);
|
||||
print("Edata1 => mId: $data1 -2 :");
|
||||
prefid = data1;
|
||||
role = ref.read(userRoleProvider);
|
||||
print("E43 => mId: $prefid");
|
||||
if (prefid != null && role != null) {
|
||||
getBroker();
|
||||
}
|
||||
});
|
||||
// getBrokerList();
|
||||
}
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
// Sort descending by id first
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
if (sortedData.isEmpty) return [];
|
||||
|
||||
// Ensure currentPage is valid
|
||||
final maxPage = (sortedData.length / itemsPerPage).ceil();
|
||||
final safePage = currentPage.clamp(1, maxPage);
|
||||
|
||||
final startIndex = (safePage - 1) * itemsPerPage;
|
||||
final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length);
|
||||
|
||||
return sortedData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void handleEdit(Map<String, dynamic> item) {
|
||||
setState(() {
|
||||
selectedId = item['id'];
|
||||
selectedBroker = item;
|
||||
});
|
||||
}
|
||||
|
||||
void filterData(String query) {
|
||||
setState(() {
|
||||
final q = query.toLowerCase();
|
||||
|
||||
if (q.isEmpty) {
|
||||
filteredData = getBrokerData;
|
||||
return;
|
||||
}
|
||||
|
||||
filteredData = getBrokerData.where((item) {
|
||||
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
|
||||
|
||||
return (item['name'] ?? '').toString().toLowerCase().contains(q) ||
|
||||
(item['email'] ?? '').toString().toLowerCase().contains(q) ||
|
||||
(item['mobile'] ?? '').toString().toLowerCase().contains(q) ||
|
||||
(item['address'] ?? '').toString().toLowerCase().contains(q) ||
|
||||
(item['emp_id'] ?? item['agent_code'] ?? '')
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.contains(q) ||
|
||||
isActiveStatus.contains(q);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
final TextEditingController _searchStaffController = TextEditingController();
|
||||
|
||||
Future<void> getBroker() async {
|
||||
print('getBroker called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('Broker');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getBroker - ${response['data']}');
|
||||
setState(() {
|
||||
getBrokerData = List<Map<String, dynamic>>.from(response['data']);
|
||||
print('API Data - $getBrokerData');
|
||||
|
||||
filteredData = List.from(getBrokerData);
|
||||
print('originalData - $filteredData');
|
||||
});
|
||||
} else {
|
||||
getBrokerData = [];
|
||||
filteredData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||||
return [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
print('EDITStaff - ${data['id']}');
|
||||
dynamic id = data['id'];
|
||||
context.go('/staff/$id');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
|
||||
SizedBox(width: 10),
|
||||
Text('Edit'),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
managerId = ref.watch(managerIdProvider);
|
||||
|
||||
return MainLayout(
|
||||
title: "Broker",
|
||||
body: Container(
|
||||
// padding: EdgeInsets.all(8.0),
|
||||
// margin: EdgeInsets.all(10.0),
|
||||
|
||||
// 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: GestureDetector(
|
||||
onTap: () {
|
||||
context.go(AppRoutes.dashboard);
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [Text('Broker List', style: _headerStyle)],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 5),
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color: Colors.green,
|
||||
// color: Colors.green.shade50,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// margin: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
// height: 40,
|
||||
// color: Colors.pink,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Broker(
|
||||
key: ValueKey(selectedId),
|
||||
data: selectedBroker,
|
||||
id: selectedId,
|
||||
onSubmit: () {
|
||||
getBroker();
|
||||
setState(() {
|
||||
selectedBroker = null;
|
||||
selectedId = null;
|
||||
}); // reset after save
|
||||
},
|
||||
),
|
||||
Spacer(),
|
||||
ThemedSearchField(
|
||||
hintText: 'Search',
|
||||
// backgroundColor: Color(0xFFF6F8F8),
|
||||
backgroundColor: Color(0xFFFFFFFF),
|
||||
txtHeight: 30,
|
||||
onChanged: filterData,
|
||||
controller: _searchStaffController,
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
|
||||
SizedBox(width: 10),
|
||||
ExportBtn(
|
||||
sheetName: "Broker",
|
||||
fileName: "broker_list",
|
||||
txt: !ResponsiveLayout.isMobile(context)
|
||||
? true
|
||||
: false,
|
||||
data: filteredData,
|
||||
displayHeaders: ['S.No.', 'Broker', 'Status'],
|
||||
keys: ["sno", "name", "is_active"],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF1F5F9),
|
||||
|
||||
// color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('S.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Broker', style: _headerStyle),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Status', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Action', style: _headerStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: _buildDataTable(context)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
// height: 20,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// color: Colors.green.shade50,
|
||||
child: PaginationControls(
|
||||
currentPage: currentPage,
|
||||
itemsPerPage: itemsPerPage,
|
||||
// totalItems: dataVal.length,
|
||||
totalItems: filteredData.length,
|
||||
// activeColor: layoutColor, // your theme color
|
||||
onPageChanged: (page) {
|
||||
setState(() {
|
||||
currentPage = page;
|
||||
});
|
||||
},
|
||||
onItemsPerPageChanged: (items) {
|
||||
setState(() {
|
||||
itemsPerPage = items;
|
||||
currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDataTable(BuildContext context) {
|
||||
if (filteredData.isEmpty) {
|
||||
return const SizedBox(
|
||||
height: 50,
|
||||
child: Center(child: Text('No available data')),
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
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];
|
||||
final sno = startIndex + index;
|
||||
return _buildDataRow(item, sno);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 16),
|
||||
// margin: const EdgeInsets.only(top: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFE0F7F9),
|
||||
border: const Border(
|
||||
bottom: BorderSide(color: Colors.blueGrey, width: 0.15),
|
||||
),
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
||||
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
// color: Colors.yellow.shade50,
|
||||
child: Transform.scale(
|
||||
scale: 0.4, // 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.updateStatusMasters(
|
||||
item['id'],
|
||||
val ? "0" : "1",
|
||||
'Broker',
|
||||
userId,
|
||||
);
|
||||
print("Response - $response");
|
||||
},
|
||||
activeColor: Color(0xFF2E7D6E), // thumb when active
|
||||
// activeColor: Color(0xFF425B5B), // thumb when active
|
||||
activeTrackColor: Color(0xFFDCFCE7), // track when active
|
||||
// activeTrackColor: Color(0xFFB2D8D3), // track when active
|
||||
inactiveThumbColor:
|
||||
Colors.grey.shade400, // thumb when inactive
|
||||
inactiveTrackColor:
|
||||
Colors.grey.shade300, // track when inactive
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: item['is_active'] == "1"
|
||||
? Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 12,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
print('EDITBrokwer - ${item['id']}');
|
||||
print('EDITBrokwer - ${item}');
|
||||
dynamic id = item['id'];
|
||||
|
||||
handleEdit(item);
|
||||
|
||||
print(item);
|
||||
},
|
||||
splashRadius: 28,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final _dataBold = GoogleFonts.inter(
|
||||
fontSize: 11.5,
|
||||
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
);
|
||||
|
||||
static final _dataSub = GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: Color(0xFF585757),
|
||||
);
|
||||
|
||||
static final _headerStyle = GoogleFonts.inter(
|
||||
color: Color(0XFF1e293b),
|
||||
fontSize: 12,
|
||||
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
318
lib/presentation/screens/Masters/PaymentMode/payment.dart
Normal file
318
lib/presentation/screens/Masters/PaymentMode/payment.dart
Normal file
@ -0,0 +1,318 @@
|
||||
import 'dart:convert';
|
||||
// import 'dart:io' as html;
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nhance_partner/core/routing/routes.dart';
|
||||
import 'package:nhance_partner/data/utils/toastNotification.dart';
|
||||
import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart';
|
||||
|
||||
import '../../../../core/config/env.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/validators.dart';
|
||||
import '../../../layouts/main_layout.dart';
|
||||
import '../../../layouts/responsive_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
|
||||
import '../../../themes/indicators/input_field_decoration.dart';
|
||||
|
||||
class Payment extends ConsumerStatefulWidget {
|
||||
final String? id;
|
||||
final Map<String, dynamic>? data;
|
||||
final VoidCallback onSubmit;
|
||||
const Payment({super.key, this.id, this.data, required this.onSubmit});
|
||||
@override
|
||||
ConsumerState<Payment> createState() => PaymentState();
|
||||
}
|
||||
|
||||
class PaymentState extends ConsumerState<Payment> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late ApiService apiService;
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownKeyHandler = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
List<String> tabHeader = ['value', 'email', 'mobile', 'code'];
|
||||
late String isActive = "1";
|
||||
html.File? passportFile;
|
||||
String? selectedFileNames;
|
||||
String? passportFileUrlFromApi;
|
||||
dynamic selectedId;
|
||||
String? _token;
|
||||
bool isLoading = false;
|
||||
bool showHandler = false;
|
||||
|
||||
String? selectedRole;
|
||||
List<Map<String, dynamic>> filteredRolesData = [];
|
||||
List<Map<String, dynamic>> getRolesData = [];
|
||||
|
||||
List<dynamic>? selectedHandlerIds = [];
|
||||
String? selectedHandler;
|
||||
List<Map<String, dynamic>> filteredHandlersData = [];
|
||||
List<Map<String, dynamic>> getHandlersData = [];
|
||||
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
|
||||
dynamic userId;
|
||||
dynamic managerId;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
final data = {"value": controllers["value"]?.text, "is_active": isActive};
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService();
|
||||
for (String field in tabHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
}
|
||||
|
||||
_initializeToken();
|
||||
updateData();
|
||||
|
||||
Future.microtask(() {
|
||||
managerId = ref.watch(managerIdProvider);
|
||||
userId = ref.watch(userIdProvider);
|
||||
if (managerId != null) {
|
||||
print('hansles');
|
||||
// getHandlers(managerId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
}
|
||||
|
||||
void updateData() async {
|
||||
if (widget.data != null) {
|
||||
dynamic id = widget.data?['id'];
|
||||
final data = widget.data;
|
||||
|
||||
if (data == null) return;
|
||||
setState(() {
|
||||
selectedId = id;
|
||||
controllers['value']?.text = data['value'] ?? '';
|
||||
controllers["is_active"]?.text = data['is_active'].toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleSave() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
setState(() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
dataDetails();
|
||||
final dataSet = dataDetails();
|
||||
print("dataSetAgent - $dataSet");
|
||||
|
||||
print("TYPE of is_active → ${dataSet['is_active'].runtimeType}");
|
||||
print("TYPE of value → ${dataSet['value'].runtimeType}");
|
||||
createUserData(dataSet);
|
||||
} else {
|
||||
// isDisable = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
print('REfresj');
|
||||
widget.onSubmit();
|
||||
setState(() {
|
||||
// clear all text controllers
|
||||
for (var controller in controllers.values) {
|
||||
controller.clear();
|
||||
} // 👈 clear selected agent
|
||||
controllers['value']?.clear();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createUserData(data) async {
|
||||
final bool isUpdating = selectedId != null;
|
||||
final dynamic id = isUpdating ? int.tryParse(selectedId.toString()) : 0;
|
||||
final String apiUrldata;
|
||||
|
||||
print("TYPE of id → $id - ${id.runtimeType}");
|
||||
print("TYPE of id → $userId - ${userId.runtimeType}");
|
||||
|
||||
apiUrldata = isUpdating
|
||||
? '${Env.apiUrl}/master/updatePaymentMode/$selectedId'
|
||||
: '${Env.apiUrl}/master/createPaymentMode';
|
||||
|
||||
print('apiUrldata - $apiUrldata');
|
||||
// final token = await getToken(); // Fetch token
|
||||
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
if (isUpdating) {
|
||||
data['updated_by'] = userId.toString();
|
||||
} else {
|
||||
data['created_by'] = userId.toString();
|
||||
}
|
||||
|
||||
// if (isUpdating) {
|
||||
// data['updated_by'] = userId;
|
||||
// // data.remove('id');
|
||||
// } else {
|
||||
// data['created_by'] = userId;
|
||||
// }
|
||||
|
||||
print("data------- $data}");
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $_token',
|
||||
'Content-Type': 'application/json',
|
||||
'app-signature': Env.App_Signature,
|
||||
},
|
||||
body: jsonEncode(data), // Convert map to JSON
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("Payment submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
refresh();
|
||||
isUpdating
|
||||
? ToastHelper.showSuccessToast(
|
||||
context,
|
||||
'Payment Updated Successfully',
|
||||
)
|
||||
: ToastHelper.showSuccessToast(
|
||||
context,
|
||||
'Payment Created Successfully',
|
||||
);
|
||||
} else if (response.statusCode == 403) {
|
||||
await apiService.clearLocalStorageAndRedirect();
|
||||
} else {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
dynamic msg = responseBody['data'];
|
||||
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("Payment Creation Failed"),
|
||||
content: Text(msg),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text("OK"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting Staff: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var controller in controllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
buildFormFields(),
|
||||
SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
handleSave();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 6),
|
||||
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: Color(0xFF2E7D6E),
|
||||
),
|
||||
child: Text(
|
||||
'Save',
|
||||
style: GoogleFonts.inter(color: Colors.white, fontSize: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
refresh();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(6.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2E7D6E),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
child: Icon(Icons.refresh, size: 13, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFormFields() {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Column(children: [buildName()]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildName() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Payment Mode *", style: _textStyle),
|
||||
SizedBox(width: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['value']!,
|
||||
validator: (value) => Validators.requiredField(value, "value"),
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.15,
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
highlightColor: Color(0xFF50A398),
|
||||
txtheight: 30,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static final _textStyle = GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
471
lib/presentation/screens/Masters/PaymentMode/paymentList.dart
Normal file
471
lib/presentation/screens/Masters/PaymentMode/paymentList.dart
Normal file
@ -0,0 +1,471 @@
|
||||
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:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:nhance_partner/presentation/screens/Masters/PaymentMode/payment.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/filter_btn.dart';
|
||||
import '../../../themes/indicators/search_field_theme.dart';
|
||||
import '../../../themes/indicators/text_field_theme.dart';
|
||||
import '../../../widgets/custom_action_popup.dart';
|
||||
|
||||
class PaymentLsit extends ConsumerStatefulWidget {
|
||||
const PaymentLsit({super.key});
|
||||
@override
|
||||
ConsumerState<PaymentLsit> createState() => PaymentLsitState();
|
||||
}
|
||||
|
||||
class PaymentLsitState extends ConsumerState<PaymentLsit> {
|
||||
int currentPage = 1;
|
||||
int itemsPerPage = 10;
|
||||
late ApiService apiService;
|
||||
dynamic managerId;
|
||||
dynamic role;
|
||||
dynamic prefid;
|
||||
dynamic userId;
|
||||
// List<Map<String, dynamic>> dataVal = [];
|
||||
List<Map<String, dynamic>> getPaymentData = [];
|
||||
List<Map<String, dynamic>> originalData = [];
|
||||
List<Map<String, dynamic>> filteredData = [];
|
||||
bool isLoading = false;
|
||||
|
||||
Map<String, dynamic>? selectedPayment;
|
||||
dynamic selectedId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
apiService = ApiService();
|
||||
|
||||
Future.microtask(() {
|
||||
final data1 = ref.read(managerIdProvider);
|
||||
// final data2 = ref.read(handlerIdProvider);
|
||||
print("Edata1 => mId: $data1 -2 :");
|
||||
prefid = data1;
|
||||
role = ref.read(userRoleProvider);
|
||||
userId = ref.watch(userIdProvider);
|
||||
print("E43 => mId: $prefid");
|
||||
if (prefid != null && role != null) {
|
||||
getPayment();
|
||||
}
|
||||
});
|
||||
// getPaymentList();
|
||||
}
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
// Sort descending by id first
|
||||
final sortedData = [...filteredData]
|
||||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||||
|
||||
if (sortedData.isEmpty) return [];
|
||||
|
||||
// Ensure currentPage is valid
|
||||
final maxPage = (sortedData.length / itemsPerPage).ceil();
|
||||
final safePage = currentPage.clamp(1, maxPage);
|
||||
|
||||
final startIndex = (safePage - 1) * itemsPerPage;
|
||||
final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length);
|
||||
|
||||
return sortedData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void handleEdit(Map<String, dynamic> item) {
|
||||
setState(() {
|
||||
selectedId = item['id'];
|
||||
selectedPayment = item;
|
||||
});
|
||||
}
|
||||
|
||||
void filterData(String query) {
|
||||
setState(() {
|
||||
final q = query.toLowerCase();
|
||||
|
||||
if (q.isEmpty) {
|
||||
filteredData = getPaymentData;
|
||||
return;
|
||||
}
|
||||
|
||||
filteredData = getPaymentData.where((item) {
|
||||
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
|
||||
|
||||
return (item['value'] ?? '').toString().toLowerCase().contains(q) ||
|
||||
isActiveStatus.contains(q);
|
||||
}).toList();
|
||||
});
|
||||
}
|
||||
|
||||
final TextEditingController _searchStaffController = TextEditingController();
|
||||
|
||||
Future<void> getPayment() async {
|
||||
print('getPayment called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('PaymentMode');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getPayment - ${response['data']}');
|
||||
setState(() {
|
||||
getPaymentData = List<Map<String, dynamic>>.from(response['data']);
|
||||
print('API Data - $getPaymentData');
|
||||
|
||||
filteredData = List.from(getPaymentData);
|
||||
print('originalData - $filteredData');
|
||||
});
|
||||
} else {
|
||||
getPaymentData = [];
|
||||
filteredData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||||
return [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
print('EDITStaff - ${data['id']}');
|
||||
dynamic id = data['id'];
|
||||
context.go('/staff/$id');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
|
||||
SizedBox(width: 10),
|
||||
Text('Edit'),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
managerId = ref.watch(managerIdProvider);
|
||||
|
||||
return MainLayout(
|
||||
title: "Staff",
|
||||
body: Container(
|
||||
// padding: EdgeInsets.all(8.0),
|
||||
// margin: EdgeInsets.all(10.0),
|
||||
|
||||
// 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: GestureDetector(
|
||||
onTap: () {
|
||||
context.go(AppRoutes.dashboard);
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [Text('Payment List', style: _headerStyle)],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 5),
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color: Colors.green,
|
||||
// color: Colors.green.shade50,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// margin: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
// height: 40,
|
||||
// color: Colors.pink,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Payment(
|
||||
key: ValueKey(selectedId),
|
||||
data: selectedPayment,
|
||||
id: selectedId,
|
||||
onSubmit: () {
|
||||
getPayment();
|
||||
setState(() {
|
||||
selectedPayment = null;
|
||||
selectedId = null;
|
||||
}); // reset after save
|
||||
},
|
||||
),
|
||||
Spacer(),
|
||||
ThemedSearchField(
|
||||
hintText: 'Search',
|
||||
// backgroundColor: Color(0xFFF6F8F8),
|
||||
backgroundColor: Color(0xFFFFFFFF),
|
||||
txtHeight: 30,
|
||||
onChanged: filterData,
|
||||
controller: _searchStaffController,
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
|
||||
SizedBox(width: 10),
|
||||
ExportBtn(
|
||||
sheetName: "Payment",
|
||||
fileName: "Payment_list",
|
||||
txt: !ResponsiveLayout.isMobile(context)
|
||||
? true
|
||||
: false,
|
||||
data: filteredData,
|
||||
displayHeaders: ['S.No.', 'Payment', 'Status'],
|
||||
keys: ["sno", "value", "is_active"],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF1F5F9),
|
||||
|
||||
// color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('S.No.', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Payment', style: _headerStyle),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Status', style: _headerStyle),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text('Action', style: _headerStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: _buildDataTable(context)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
// height: 20,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// color: Colors.green.shade50,
|
||||
child: PaginationControls(
|
||||
currentPage: currentPage,
|
||||
itemsPerPage: itemsPerPage,
|
||||
// totalItems: dataVal.length,
|
||||
totalItems: filteredData.length,
|
||||
// activeColor: layoutColor, // your theme color
|
||||
onPageChanged: (page) {
|
||||
setState(() {
|
||||
currentPage = page;
|
||||
});
|
||||
},
|
||||
onItemsPerPageChanged: (items) {
|
||||
setState(() {
|
||||
itemsPerPage = items;
|
||||
currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDataTable(BuildContext context) {
|
||||
if (filteredData.isEmpty) {
|
||||
return const SizedBox(
|
||||
height: 50,
|
||||
child: Center(child: Text('No available data')),
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
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];
|
||||
final sno = startIndex + index;
|
||||
return _buildDataRow(item, sno);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 16),
|
||||
// margin: const EdgeInsets.only(top: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFE0F7F9),
|
||||
border: const Border(
|
||||
bottom: BorderSide(color: Colors.blueGrey, width: 0.15),
|
||||
),
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['value'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
// color: Colors.yellow.shade50,
|
||||
child: Transform.scale(
|
||||
scale: 0.4, // 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.updateStatusMasters(
|
||||
item['id'],
|
||||
val ? "0" : "1",
|
||||
'PaymentMode',
|
||||
userId,
|
||||
);
|
||||
print("Response - $response");
|
||||
},
|
||||
activeColor: Color(0xFF2E7D6E), // thumb when active
|
||||
// activeColor: Color(0xFF425B5B), // thumb when active
|
||||
activeTrackColor: Color(0xFFDCFCE7), // track when active
|
||||
// activeTrackColor: Color(0xFFB2D8D3), // track when active
|
||||
inactiveThumbColor:
|
||||
Colors.grey.shade400, // thumb when inactive
|
||||
inactiveTrackColor:
|
||||
Colors.grey.shade300, // track when inactive
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: item['is_active'] == "1"
|
||||
? Row(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 12,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
print('EDITBrokwer - ${item['id']}');
|
||||
print('EDITBrokwer - ${item}');
|
||||
dynamic id = item['id'];
|
||||
|
||||
handleEdit(item);
|
||||
|
||||
print(item);
|
||||
},
|
||||
splashRadius: 28,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final _dataBold = GoogleFonts.inter(
|
||||
fontSize: 11.5,
|
||||
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
);
|
||||
|
||||
static final _dataSub = GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: Color(0xFF585757),
|
||||
);
|
||||
|
||||
static final _headerStyle = GoogleFonts.inter(
|
||||
color: Color(0XFF1e293b),
|
||||
fontSize: 12,
|
||||
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
@ -385,8 +385,8 @@ class StaffState extends ConsumerState<Staff> {
|
||||
padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 20.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFEDF6F5),
|
||||
// color: Colors.white,
|
||||
color: Color(0xFFEDF6F5),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
@ -203,55 +203,60 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// Container(
|
||||
// // height: 30,
|
||||
// // color: Colors.red.shade50,
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// context.go(AppRoutes.dashboard);
|
||||
// },
|
||||
// child: Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: [
|
||||
// // Icon(
|
||||
// // Icons.arrow_left_sharp,
|
||||
// // size: 35,
|
||||
// // color: Color(0xFF425B5B),
|
||||
// // ),
|
||||
// // Tooltip(
|
||||
// // message: 'Back',
|
||||
// // child: IconButton(
|
||||
// // icon: const Icon(
|
||||
// // Icons.arrow_left_sharp,
|
||||
// // size: 25,
|
||||
// // color: Color(0xFF425B5B),
|
||||
// // ),
|
||||
// // onPressed: () {
|
||||
// // context.go(AppRoutes.dashboard);
|
||||
// // },
|
||||
// // splashRadius: 18,
|
||||
// // hoverColor: Colors.black12,
|
||||
// // padding: const EdgeInsets.all(4),
|
||||
// // constraints: const BoxConstraints(),
|
||||
// // ),
|
||||
// // ),
|
||||
// // const SizedBox(width: 15), // spacing between icon and text
|
||||
//
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// SizedBox(height: 5),
|
||||
Container(
|
||||
// height: 30,
|
||||
// color: Colors.red.shade50,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.go(AppRoutes.dashboard);
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text('Staff List', style: _headerStyle),
|
||||
// Icon(
|
||||
// Icons.arrow_left_sharp,
|
||||
// size: 35,
|
||||
// color: Color(0xFF425B5B),
|
||||
// ),
|
||||
// Tooltip(
|
||||
// message: 'Back',
|
||||
// child: IconButton(
|
||||
// icon: const Icon(
|
||||
// Icons.arrow_left_sharp,
|
||||
// size: 25,
|
||||
// color: Color(0xFF425B5B),
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// context.go(AppRoutes.dashboard);
|
||||
// },
|
||||
// splashRadius: 18,
|
||||
// hoverColor: Colors.black12,
|
||||
// padding: const EdgeInsets.all(4),
|
||||
// constraints: const BoxConstraints(),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 15), // spacing between icon and text
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 5),
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color: Colors.green,
|
||||
// color: Colors.green.shade50,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// margin: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
|
||||
// padding: EdgeInsets.all(8.0),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
@ -261,14 +266,6 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Staff",
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
ThemedSearchField(
|
||||
hintText: 'Search',
|
||||
// backgroundColor: Color(0xFFF6F8F8),
|
||||
@ -279,8 +276,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
txtwidth: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
|
||||
SizedBox(width: 5),
|
||||
|
||||
SizedBox(width: 10),
|
||||
ExportBtn(
|
||||
sheetName: "Staff",
|
||||
fileName: "staff_list",
|
||||
@ -306,7 +302,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(width: 10),
|
||||
Spacer(),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.go('/staff/create');
|
||||
@ -320,15 +316,6 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Text(
|
||||
// 'Create New Staff',
|
||||
// style: GoogleFonts.inter(
|
||||
// color: Colors.white,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// fontSize: 11,
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(width: 10),
|
||||
Tooltip(
|
||||
message: 'Create Staff',
|
||||
child: Icon(
|
||||
@ -457,15 +444,15 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
|
||||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(vertical: 1, 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),
|
||||
bottom: BorderSide(color: Colors.blueGrey, width: 0.15),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@ -607,7 +594,7 @@ class StaffListState extends ConsumerState<StaffList> {
|
||||
}
|
||||
|
||||
static final _dataBold = GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontSize: 11.5,
|
||||
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
|
||||
@ -713,8 +713,19 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
key: dropDownSelectStaffKey,
|
||||
// selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
||||
selectedItem: selectedVehicle,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredStaffDataEnqAsgn;
|
||||
|
||||
// items: (filter, infiniteScrollProps) {
|
||||
// return filteredStaffDataEnqAsgn;
|
||||
// },
|
||||
items: (filter, infiniteScrollProps) async {
|
||||
if (filter.isEmpty) {
|
||||
return filteredStaffDataEnqAsgn;
|
||||
}
|
||||
return filteredStaffDataEnqAsgn.where((item) {
|
||||
return item['name'].toString().toLowerCase().contains(
|
||||
filter.toLowerCase(),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
|
||||
itemAsString: (val) => val['name'].toString(),
|
||||
@ -791,7 +802,10 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
style: GoogleFonts.inter(fontSize: 11, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Staff ...",
|
||||
@ -951,8 +965,11 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
style: GoogleFonts.inter(fontSize: 11, color: Colors.black),
|
||||
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Insurer...",
|
||||
@ -1084,7 +1101,9 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
style: GoogleFonts.inter(fontSize: 11, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Broker...",
|
||||
@ -1331,14 +1350,18 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
showSearchBox: true,
|
||||
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
style: GoogleFonts.inter(fontSize: 11, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Partner Name",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
color: Colors.grey,
|
||||
color: Colors.black,
|
||||
),
|
||||
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
@ -1538,8 +1561,10 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
style: GoogleFonts.inter(fontSize: 11, color: Colors.black),
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Vehicle Type...",
|
||||
@ -1549,10 +1574,10 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
borderSide: BorderSide(color: Colors.white), // 👈 Normal border
|
||||
),
|
||||
isDense: true, // reduces height
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8, // reduce this value for smaller height
|
||||
),
|
||||
// contentPadding: const EdgeInsets.symmetric(
|
||||
// horizontal: 8,
|
||||
// vertical: 8, // reduce this value for smaller height
|
||||
// ),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
@ -3340,33 +3365,34 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
|
||||
// Premium
|
||||
DataCell(
|
||||
(item['status'] == 'Proposal Created' ||
|
||||
item['status'] == 'Proposal Rejected')
|
||||
? SizedBox(
|
||||
width: 80,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// handle approval logic
|
||||
|
||||
handleProposalAccept(context, item['id']);
|
||||
},
|
||||
child: Text(
|
||||
'Click To\nApprove',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.green,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
item['premium_amount']?.toString() ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
),
|
||||
// (item['status'] == 'Proposal Created' ||
|
||||
// item['status'] == 'Proposal Rejected')
|
||||
// ? SizedBox(
|
||||
// width: 80,
|
||||
// child: InkWell(
|
||||
// onTap: () {
|
||||
// // handle approval logic
|
||||
//
|
||||
// handleProposalAccept(context, item['id']);
|
||||
// },
|
||||
// child: Text(
|
||||
// 'Click To\nApprove',
|
||||
// style: GoogleFonts.inter(
|
||||
// color: Colors.green,
|
||||
// fontSize: 11,
|
||||
// fontWeight: FontWeight.w700,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// :
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
item['premium_amount']?.toString() ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Payment Mode
|
||||
@ -3430,7 +3456,8 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
||||
item['status'] ?? '-',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
color: statusTextColors[status] ?? Colors.black,
|
||||
color: Color(0XFF1e293b),
|
||||
// color: statusTextColors[status] ?? Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
// overflow: TextOverflow.clip, //
|
||||
|
||||
@ -355,8 +355,13 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
buildInsuredName(context, fromHeader: true),
|
||||
|
||||
buildVehicleNumber(context),
|
||||
|
||||
buildSave(context),
|
||||
Row(
|
||||
children: [
|
||||
buildRefresh(context),
|
||||
SizedBox(width: 5),
|
||||
buildSave(context),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -474,6 +479,7 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Partner Name",
|
||||
@ -673,6 +679,7 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Insurer...",
|
||||
@ -864,6 +871,7 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Staff ...",
|
||||
@ -927,7 +935,7 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
resetFields();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
padding: EdgeInsets.all(5.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2E7D6E),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
@ -937,6 +945,22 @@ class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRefresh(context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
resetFields();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(6.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2E7D6E),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
child: Icon(Icons.refresh, size: 18, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void resetFields() {
|
||||
print('Field ReSET');
|
||||
// 1. Reset all text controllers
|
||||
|
||||
@ -25,7 +25,7 @@ import '../../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../../themes/indicators/input_field_decoration.dart';
|
||||
import '../../../../themes/indicators/text_field_theme.dart';
|
||||
|
||||
class CreateQuotationForm extends ConsumerStatefulWidget {
|
||||
class CreateProposalForm extends ConsumerStatefulWidget {
|
||||
final dynamic userId;
|
||||
final dynamic managerId;
|
||||
final dynamic selectedEnquiryId;
|
||||
@ -34,7 +34,7 @@ class CreateQuotationForm extends ConsumerStatefulWidget {
|
||||
final dynamic selectedQuotationFrmListId;
|
||||
final void Function(String value) onSubmit;
|
||||
|
||||
const CreateQuotationForm({
|
||||
const CreateProposalForm({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.managerId,
|
||||
@ -46,12 +46,11 @@ class CreateQuotationForm extends ConsumerStatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CreateQuotationForm> createState() =>
|
||||
CreateQuotationFormState();
|
||||
// State<CreateQuotationForm> createState() => _CreateQuotationFormState();
|
||||
ConsumerState<CreateProposalForm> createState() => CreateProposalFormState();
|
||||
// State<CreateProposalForm> createState() => _CreateProposalFormState();
|
||||
}
|
||||
|
||||
class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
class CreateProposalFormState extends ConsumerState<CreateProposalForm> {
|
||||
late ApiService apiService;
|
||||
String? _token;
|
||||
|
||||
@ -75,16 +74,31 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyBroker =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownKeyInsurer = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownSelectPaymentModeKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
List<String> tabHeader = ['idv', 'premium_Amount', 'insurer'];
|
||||
|
||||
List<Map<String, dynamic>> getBrokerData = [];
|
||||
List<Map<String, dynamic>> filteredBrokerData = [];
|
||||
String? selectedBroker;
|
||||
|
||||
List<Map<String, dynamic>> getPaymentModeData = [];
|
||||
List<Map<String, dynamic>> filteredPaymentModeData = [];
|
||||
|
||||
List<Map<String, dynamic>> getInsuranceTypeData = [];
|
||||
List<Map<String, dynamic>> filteredInsuranceData = [];
|
||||
|
||||
String? selectedInsPlanType;
|
||||
String? selectedEndorsement;
|
||||
String? selectedPaymentMode;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
final data = {
|
||||
@ -94,6 +108,8 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// "insurer_id": selectedInsurer,
|
||||
"premium_amount": controllers["premium_Amount"]?.text,
|
||||
"insurance_plan_type_id": selectedInsPlanType,
|
||||
"payment_mode_id": selectedPaymentMode,
|
||||
"broker_id": selectedBroker,
|
||||
// "additional_uploaded_file_name": "extra_doc.pdf",
|
||||
// "created_by": widget.userId,
|
||||
"manager_id": widget.managerId,
|
||||
@ -112,8 +128,10 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// 🔹 Init logic here (API calls, token fetch, etc.)
|
||||
_initializeToken();
|
||||
getInsuranceType();
|
||||
getPaymentMode();
|
||||
// getInsurers();
|
||||
updateData();
|
||||
getBroker();
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
@ -157,6 +175,37 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> getBroker() async {
|
||||
print('getBroker called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('Broker');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getBroker - ${response['data']}');
|
||||
setState(() {
|
||||
getBrokerData = List<Map<String, dynamic>>.from(response['data']);
|
||||
print('API Data - $getBrokerData');
|
||||
|
||||
filteredBrokerData = List.from(getBrokerData);
|
||||
print('originalData - $filteredBrokerData');
|
||||
});
|
||||
} else {
|
||||
getBrokerData = [];
|
||||
filteredBrokerData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
if (widget.selectedQuotationFrmListId != null &&
|
||||
widget.selectedQuotationFrmListdata!.isNotEmpty) {
|
||||
@ -174,8 +223,11 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
selectedInsPlanType = widget
|
||||
.selectedQuotationFrmListdata!['insurance_plan_type_id']
|
||||
?.toString();
|
||||
selectedBroker = widget.selectedQuotationFrmListdata!['broker_id']
|
||||
?.toString();
|
||||
|
||||
selectedInsurer = '1';
|
||||
selectedPaymentMode = widget.selectedQuotationFrmListdata!['broker_id']
|
||||
?.toString();
|
||||
|
||||
// controllers["insurer"]?.text = 'LIC';
|
||||
|
||||
@ -183,7 +235,7 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? '';
|
||||
|
||||
String? apiDocPath =
|
||||
widget.selectedQuotationFrmListdata!["additional_uploaded_file_name"];
|
||||
widget.selectedQuotationFrmListdata!["policy_pdf_file_name"];
|
||||
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
||||
print('apiDocPath - $apiDocPath');
|
||||
selectedFileNames = apiDocPath.split('/').last;
|
||||
@ -263,6 +315,39 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getPaymentMode() async {
|
||||
print('getPaymentMode called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('PaymentMode');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getPaymentModeData - ${response['data']}');
|
||||
setState(() {
|
||||
getPaymentModeData = List<Map<String, dynamic>>.from(
|
||||
response['data'],
|
||||
);
|
||||
print('API Data - $getPaymentModeData');
|
||||
|
||||
filteredPaymentModeData = List.from(getPaymentModeData);
|
||||
print('originalData - $filteredPaymentModeData');
|
||||
});
|
||||
} else {
|
||||
getPaymentModeData = [];
|
||||
filteredPaymentModeData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDone() {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
@ -292,11 +377,9 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
print('id - $id');
|
||||
final uri = Uri.parse(
|
||||
// isUpdating
|
||||
// ? 'https://venbait.in/nhance/partner/dev/api/agent/updateAgent'
|
||||
// : 'https://venbait.in/nhance/partner/dev/api/agent/createAgent',
|
||||
isUpdating
|
||||
? '${Env.apiUrl}quotation/updateQuotation'
|
||||
: '${Env.apiUrl}quotation/createQuotation',
|
||||
// ? '${Env.apiUrl}quotation/updateQuotation'
|
||||
// : '${Env.apiUrl}quotation/createQuotation',
|
||||
'${Env.apiUrl}quotation/proceedQuotation',
|
||||
);
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
@ -336,14 +419,14 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
try {
|
||||
if (docUploadedFile!.bytes != null) {
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'additional_uploaded_file_name',
|
||||
'policy_pdf_file_name',
|
||||
docUploadedFile!.bytes!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
} else if (docUploadedFile!.path != null) {
|
||||
final multipartFile = await http.MultipartFile.fromPath(
|
||||
'additional_uploaded_file_name',
|
||||
'policy_pdf_file_name',
|
||||
docUploadedFile!.path!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
@ -369,8 +452,9 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
|
||||
print("Response: ${response.body}");
|
||||
widget.onSubmit('Success');
|
||||
// ToastHelper.showErrorToast(context, 'Proposal Created');
|
||||
reset();
|
||||
// Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
} else if (response.statusCode == 403) {
|
||||
await apiService.clearLocalStorageAndRedirect();
|
||||
} else {
|
||||
@ -411,7 +495,43 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// const SizedBox(height: 8),
|
||||
|
||||
// 🔹 Switch content dynamically
|
||||
buildFormFields(context),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height * 0.525,
|
||||
child: buildFormFields(context),
|
||||
),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
handleDone();
|
||||
// widget.onSubmit(controller.text.trim());
|
||||
// Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 25.0,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: const Color(0xFF425B5B),
|
||||
),
|
||||
child: Text(
|
||||
'Save',
|
||||
style: GoogleFonts.poppins(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -419,54 +539,43 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
Widget buildFormFields(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
// autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
// autovalidateMode: _autoValidate
|
||||
// ? AutovalidateMode.onUserInteraction
|
||||
// : AutovalidateMode.disabled,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
buildIdv(context),
|
||||
Spacer(),
|
||||
// buildInsurer(context),
|
||||
buildInsurancePlanType(context),
|
||||
Spacer(),
|
||||
buildPremiumAmnt(context),
|
||||
Spacer(),
|
||||
// buildDocuments(context),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
handleDone();
|
||||
// widget.onSubmit(controller.text.trim());
|
||||
// Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 25.0,
|
||||
vertical: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: const Color(0xFF425B5B),
|
||||
),
|
||||
child: const Text(
|
||||
'Save',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
|
||||
// if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
|
||||
// _buildResponsiveRow(
|
||||
// context,
|
||||
// buildAdditonalDocuments(context),
|
||||
// buildRemarks(context),
|
||||
// ),
|
||||
children: [
|
||||
buildIdv(context),
|
||||
Spacer(),
|
||||
// buildInsurer(context),
|
||||
buildInsurancePlanType(context),
|
||||
Spacer(),
|
||||
buildPremiumAmnt(context),
|
||||
Spacer(),
|
||||
// buildDocuments(context),
|
||||
|
||||
// if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
|
||||
// _buildResponsiveRow(
|
||||
// context,
|
||||
// buildAdditonalDocuments(context),
|
||||
// buildRemarks(context),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
|
||||
children: [
|
||||
buildPaymentMode(context),
|
||||
Spacer(),
|
||||
// buildInsurer(context),
|
||||
buildBroker(context),
|
||||
Spacer(),
|
||||
buildDocuments(context), Spacer(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -486,103 +595,15 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
|
||||
],
|
||||
validator: (value) => Validators.doubleNumber(value, "IDV"),
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
highlightColor: Color(0xFF50A398),
|
||||
// validator: (value) => Validators.number(value, "IDV"),
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
// backgroundColor: Color(0xFFEDF6F5),
|
||||
// readOnly: true,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInsurer2(BuildContext context) {
|
||||
Map<String, dynamic>? selectedInsurerd = filteredInsurersData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedInsurer,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Insurer *', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
// borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.26,
|
||||
// height: 40,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyInsurer,
|
||||
selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredInsurersData;
|
||||
},
|
||||
|
||||
itemAsString: (val) => val['name'].toString(), // what to show
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
validator: (val) {
|
||||
if (val == null) {
|
||||
return "Required"; // ✅ error message
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Insurer",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor: Color(
|
||||
0xFFEDF6F5,
|
||||
), // 👈 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'];
|
||||
}
|
||||
},
|
||||
),
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
txtheight: 35,
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -596,7 +617,9 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['premium_Amount']!,
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
highlightColor: Color(0xFF50A398),
|
||||
// backgroundColor: Color(0xFFEDF6F5),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
|
||||
],
|
||||
@ -604,7 +627,8 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// validator: (value) => Validators.number(value, "PremiumAmount "),
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
txtheight: 35,
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -625,15 +649,24 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
color: Colors.white,
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
// height: 40,
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
height: 35,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKey,
|
||||
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredInsuranceData;
|
||||
// items: (filter, infiniteScrollProps) {
|
||||
// return filteredInsuranceData;
|
||||
// },
|
||||
items: (filter, infiniteScrollProps) async {
|
||||
if (filter.isEmpty) {
|
||||
return filteredInsuranceData;
|
||||
}
|
||||
return filteredInsuranceData.where((item) {
|
||||
return item['name'].toString().toLowerCase().contains(
|
||||
filter.toLowerCase(),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
|
||||
itemAsString: (val) => val['insurance_plan_type'].toString(),
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
@ -643,18 +676,36 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Plan Type",
|
||||
label: "Select Staff Member",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor: Color(
|
||||
0xFFEDF6F5,
|
||||
), // 👈 makes the dropdown input white
|
||||
fillColor:
|
||||
Colors.white, // 👈 makes the dropdown input white
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 6,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
popupProps: PopupProps.menu(
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
@ -664,10 +715,16 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Plan Type...",
|
||||
hintText: "Search Plan Type ...",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
@ -681,6 +738,38 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
itemBuilder: (context, item, isDisabled, isSelected) {
|
||||
return Container(
|
||||
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
child: Text(
|
||||
item['insurance_plan_type'].toString(),
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
);
|
||||
},
|
||||
// searchFieldProps: TextFieldProps(
|
||||
// decoration: InputDecoration(
|
||||
// filled: true,
|
||||
// fillColor: Colors.white,
|
||||
// hintText: "Search Plan Type...",
|
||||
// enabledBorder: OutlineInputBorder(
|
||||
// borderSide: BorderSide(
|
||||
// color: Colors.white,
|
||||
// ), // 👈 Normal border
|
||||
// ),
|
||||
// focusedBorder: OutlineInputBorder(
|
||||
// borderSide: BorderSide(
|
||||
// color: Colors.white,
|
||||
// width: 1.5,
|
||||
// ), // 👈 Focused border
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// constraints: BoxConstraints(),
|
||||
),
|
||||
|
||||
@ -699,22 +788,291 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildPaymentMode(context) {
|
||||
Map<String, dynamic>? selectedVehicle = filteredPaymentModeData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedPaymentMode,
|
||||
orElse: () => {},
|
||||
);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Payment Mode", style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
height: 35,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownSelectPaymentModeKey,
|
||||
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
||||
// items: (filter, infiniteScrollProps) {
|
||||
// return filteredInsuranceData;
|
||||
// },
|
||||
items: (filter, infiniteScrollProps) async {
|
||||
if (filter.isEmpty) {
|
||||
return filteredPaymentModeData;
|
||||
}
|
||||
return filteredPaymentModeData.where((item) {
|
||||
return item['name'].toString().toLowerCase().contains(
|
||||
filter.toLowerCase(),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
itemAsString: (val) => val['value'].toString(),
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
validator: (val) {
|
||||
if (val == null) {
|
||||
return "Required"; // ✅ error message
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Payment Mode",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor:
|
||||
Colors.white, // 👈 makes the dropdown input white
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 6,
|
||||
),
|
||||
),
|
||||
),
|
||||
popupProps: PopupProps.menu(
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
menuProps: MenuProps(
|
||||
backgroundColor:
|
||||
Colors.white, // 👈 sets dropdown background to white
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Payment Mode ...",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
), // 👈 Normal border
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
width: 1.5,
|
||||
), // 👈 Focused border
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
itemBuilder: (context, item, isDisabled, isSelected) {
|
||||
return Container(
|
||||
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
child: Text(
|
||||
item['value'].toString(),
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
print("Selected value : ${val['value']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedPaymentMode = val['id'];
|
||||
// controllers['agentId']?.text = val['agent_code'];
|
||||
// agentId = agent['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBroker(context) {
|
||||
Map<String, dynamic>? selectedBrokers = filteredBrokerData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedBroker,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Broker", style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
height: 35,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyBroker,
|
||||
selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null,
|
||||
// items: (filter, infiniteScrollProps) {
|
||||
// return filteredInsuranceData;
|
||||
// },
|
||||
items: (filter, infiniteScrollProps) async {
|
||||
if (filter.isEmpty) {
|
||||
return filteredBrokerData;
|
||||
}
|
||||
return filteredBrokerData.where((item) {
|
||||
return item['name'].toString().toLowerCase().contains(
|
||||
filter.toLowerCase(),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
itemAsString: (val) => val['name'].toString(),
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
validator: (val) {
|
||||
if (val == null) {
|
||||
return "Required"; // ✅ error message
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Broker",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor:
|
||||
Colors.white, // 👈 makes the dropdown input white
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFFE2E8F0),
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 6,
|
||||
),
|
||||
),
|
||||
),
|
||||
popupProps: PopupProps.menu(
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
menuProps: MenuProps(
|
||||
backgroundColor:
|
||||
Colors.white, // 👈 sets dropdown background to white
|
||||
),
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(1),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Broker...",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
), // 👈 Normal border
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
width: 1.5,
|
||||
), // 👈 Focused border
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
itemBuilder: (context, item, isDisabled, isSelected) {
|
||||
return Container(
|
||||
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
child: Text(
|
||||
item['name'].toString(),
|
||||
style: GoogleFonts.inter(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
print("Selected ClaimsType : ${val['id']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedBroker = val['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildDocuments(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Proposal Documents ', style: _textStyle),
|
||||
Text('Upload Policy PDF', style: _textStyle),
|
||||
const SizedBox(height: 10),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ThemedUploadField(
|
||||
hintText: selectedFileNames ?? "Upload Document",
|
||||
txtheight: 40,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.26,
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
: MediaQuery.of(context).size.width * 0.18,
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
// highlightColor: Color(0xFF50A398),
|
||||
// backgroundColor: Color(0xFFEDF6F5),
|
||||
onFileSelected: (fileName, file) {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
setState(() {
|
||||
@ -722,47 +1080,40 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
if (docUploadedFileUrlFromApi != null)
|
||||
Container(
|
||||
// color: Colors.white,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => apiService.downloadFile(
|
||||
apiUrl:
|
||||
'api/quotation/downloadAdditionalUploadedFile?id=$selectedId',
|
||||
apiId: selectedId,
|
||||
localFile: docUploadedFile,
|
||||
fileName: selectedFileNames,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Color(0xFF425B5B),
|
||||
// color: Colors.green.shade300,
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
Text(
|
||||
"Download",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(Icons.download, size: 13, color: Colors.white),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 5),
|
||||
//
|
||||
// if (docUploadedFileUrlFromApi != null)
|
||||
// Container(
|
||||
// // color: Colors.white,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: () => apiService.downloadFile(
|
||||
// // apiUrl:
|
||||
// // 'api/quotation/downloadAdditionalUploadedFile?id=$selectedId',
|
||||
// apiUrl:
|
||||
// 'api/policy/downloadPolicyFile?policy_id=$selectedId&file_type=policy_pdf',
|
||||
// apiId: selectedId,
|
||||
// localFile: docUploadedFile,
|
||||
// fileName: selectedFileNames,
|
||||
// ),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(5),
|
||||
// color: Color(0xFF425B5B),
|
||||
// // color: Colors.green.shade300,
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: const [
|
||||
// Icon(Icons.download, size: 13, color: Colors.white),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -772,7 +1123,8 @@ class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
// ------------------- STyle ---------------------------------
|
||||
|
||||
static final TextStyle _textStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
color: Color(0XFF334155),
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,778 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nhance_partner/data/utils/toastNotification.dart';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../../../core/config/env.dart';
|
||||
import '../../../../../core/routing/routes.dart';
|
||||
import '../../../../../core/services/api_service.dart';
|
||||
import '../../../../../data/services/auth_service.dart';
|
||||
import '../../../../../data/utils/validators.dart';
|
||||
import '../../../../layouts/responsive_layout.dart';
|
||||
import '../../../../providers/manager_provider.dart';
|
||||
import '../../../../providers/quotation_staff_proivder.dart';
|
||||
import '../../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../../themes/indicators/input_field_decoration.dart';
|
||||
import '../../../../themes/indicators/text_field_theme.dart';
|
||||
|
||||
class CreateQuotationForm extends ConsumerStatefulWidget {
|
||||
final dynamic userId;
|
||||
final dynamic managerId;
|
||||
final dynamic selectedEnquiryId;
|
||||
final dynamic selectedInsurdId;
|
||||
final dynamic selectedQuotationFrmListdata;
|
||||
final dynamic selectedQuotationFrmListId;
|
||||
final void Function(String value) onSubmit;
|
||||
|
||||
const CreateQuotationForm({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.managerId,
|
||||
required this.selectedEnquiryId,
|
||||
required this.selectedInsurdId,
|
||||
this.selectedQuotationFrmListdata,
|
||||
this.selectedQuotationFrmListId,
|
||||
required this.onSubmit,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CreateQuotationForm> createState() =>
|
||||
CreateQuotationFormState();
|
||||
// State<CreateQuotationForm> createState() => _CreateQuotationFormState();
|
||||
}
|
||||
|
||||
class CreateQuotationFormState extends ConsumerState<CreateQuotationForm> {
|
||||
late ApiService apiService;
|
||||
String? _token;
|
||||
|
||||
String? selectedFileNames;
|
||||
|
||||
List<Map<String, dynamic>> getInsurersData = [];
|
||||
List<Map<String, dynamic>> filteredInsurersData = [];
|
||||
String? selectedInsurer;
|
||||
|
||||
bool isLoading = false;
|
||||
bool _autoValidate = false;
|
||||
late TextEditingController controller;
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
String? docUploadedFileUrlFromApi;
|
||||
String? selectedId;
|
||||
PlatformFile? docUploadedFile;
|
||||
|
||||
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 = ['idv', 'premium_Amount', 'insurer'];
|
||||
|
||||
List<Map<String, dynamic>> getInsuranceTypeData = [];
|
||||
List<Map<String, dynamic>> filteredInsuranceData = [];
|
||||
|
||||
String? selectedInsPlanType;
|
||||
String? selectedEndorsement;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
final data = {
|
||||
"enquiry_id": widget.selectedEnquiryId,
|
||||
"insured_declared_value": controllers["idv"]?.text,
|
||||
"insurer_id": widget.selectedInsurdId,
|
||||
// "insurer_id": selectedInsurer,
|
||||
"premium_amount": controllers["premium_Amount"]?.text,
|
||||
"insurance_plan_type_id": selectedInsPlanType,
|
||||
// "additional_uploaded_file_name": "extra_doc.pdf",
|
||||
// "created_by": widget.userId,
|
||||
"manager_id": widget.managerId,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService();
|
||||
for (String field in tabHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
}
|
||||
|
||||
// 🔹 Init logic here (API calls, token fetch, etc.)
|
||||
_initializeToken();
|
||||
getInsuranceType();
|
||||
// getInsurers();
|
||||
updateData();
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Dispose all TextEditingControllers
|
||||
for (var controller in controllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
print('RESET');
|
||||
_formKey.currentState?.reset();
|
||||
// Clear all TextEditingControllers
|
||||
for (var controller in controllers.values) {
|
||||
controller.clear();
|
||||
}
|
||||
dropDownKey.currentState?.changeSelectedItem(null);
|
||||
dropDownKeyInsurer.currentState?.changeSelectedItem(null);
|
||||
|
||||
// Reset dropdowns / selections
|
||||
selectedInsurer = null;
|
||||
selectedInsPlanType = null;
|
||||
selectedEndorsement = null;
|
||||
|
||||
// Reset file selection
|
||||
selectedFileNames = null;
|
||||
docUploadedFile = null;
|
||||
docUploadedFileUrlFromApi = null;
|
||||
|
||||
// Reset selected ID
|
||||
selectedId = null;
|
||||
|
||||
// Trigger UI update
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
if (widget.selectedQuotationFrmListId != null &&
|
||||
widget.selectedQuotationFrmListdata!.isNotEmpty) {
|
||||
print('checkData -- ${widget.selectedQuotationFrmListdata}');
|
||||
selectedId = widget.selectedQuotationFrmListId!;
|
||||
// Pre-fill controllers
|
||||
controllers["idv"]?.text =
|
||||
widget.selectedQuotationFrmListdata!['insured_declared_value']
|
||||
?.toString() ??
|
||||
'';
|
||||
controllers["premium_Amount"]?.text =
|
||||
widget.selectedQuotationFrmListdata!['premium_amount']?.toString() ??
|
||||
'';
|
||||
|
||||
selectedInsPlanType = widget
|
||||
.selectedQuotationFrmListdata!['insurance_plan_type_id']
|
||||
?.toString();
|
||||
|
||||
selectedInsurer = '1';
|
||||
|
||||
// controllers["insurer"]?.text = 'LIC';
|
||||
|
||||
// selectedInsurer =
|
||||
// widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? '';
|
||||
|
||||
String? apiDocPath =
|
||||
widget.selectedQuotationFrmListdata!["additional_uploaded_file_name"];
|
||||
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
||||
print('apiDocPath - $apiDocPath');
|
||||
selectedFileNames = apiDocPath.split('/').last;
|
||||
print('selectedFileNames - $selectedFileNames');
|
||||
docUploadedFileUrlFromApi = apiDocPath;
|
||||
print('passportFileUrlFromApi - $docUploadedFileUrlFromApi');
|
||||
docUploadedFile = null;
|
||||
} else {
|
||||
selectedFileNames = null;
|
||||
docUploadedFile = null;
|
||||
docUploadedFileUrlFromApi = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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> getInsuranceType() async {
|
||||
print('getClaimList called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('InsuranceType');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getInsuranceTypeData - ${response['data']}');
|
||||
setState(() {
|
||||
getInsuranceTypeData = List<Map<String, dynamic>>.from(
|
||||
response['data'],
|
||||
);
|
||||
print('API Data - $getInsuranceTypeData');
|
||||
|
||||
filteredInsuranceData = List.from(getInsuranceTypeData);
|
||||
print('originalData - $filteredInsuranceData');
|
||||
});
|
||||
} else {
|
||||
getInsuranceTypeData = [];
|
||||
filteredInsuranceData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleDone() {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
// setState(() {
|
||||
// _autoValidate = true; // enable autovalidation after first save attempt
|
||||
// });
|
||||
setState(() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
dataDetails();
|
||||
final dataSet = dataDetails();
|
||||
print("dataSetAgent - $dataSet");
|
||||
// print("managerId - $managerId ,userId - $userId ");
|
||||
createUserData(dataSet);
|
||||
} else {
|
||||
// isDi sable = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createUserData(Map<String, dynamic> userData) async {
|
||||
// final bool isUpdating = widget.selectedQuotationFrmListId != null;
|
||||
// final id = widget.selectedQuotationFrmListId!;
|
||||
|
||||
final bool isUpdating = widget.selectedQuotationFrmListId != null;
|
||||
final id = widget.selectedQuotationFrmListId; // keep nullable
|
||||
|
||||
print('id - $id');
|
||||
final uri = Uri.parse(
|
||||
// isUpdating
|
||||
// ? 'https://venbait.in/nhance/partner/dev/api/agent/updateAgent'
|
||||
// : 'https://venbait.in/nhance/partner/dev/api/agent/createAgent',
|
||||
isUpdating
|
||||
? '${Env.apiUrl}quotation/updateQuotation'
|
||||
: '${Env.apiUrl}quotation/createQuotation',
|
||||
);
|
||||
if (_token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
// Use MultipartRequest (POST only)
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
request.headers['Authorization'] = 'Bearer $_token';
|
||||
request.headers['app-signature'] = Env.App_Signature;
|
||||
|
||||
// If updating, spoof the method Laravel-style
|
||||
if (isUpdating) {
|
||||
print('Updatrinf');
|
||||
// request.fields['_method'] = 'PUT';
|
||||
request.fields['id'] = id!;
|
||||
request.fields['updated_by'] = widget.userId!.toString();
|
||||
} else {
|
||||
print('Not Updatrinf');
|
||||
request.fields['created_by'] = widget.userId!.toString();
|
||||
}
|
||||
|
||||
print("USerDAta - $userData");
|
||||
|
||||
// userData.forEach((key, value) {
|
||||
// request.fields[key] = value.toString();
|
||||
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
||||
// });
|
||||
|
||||
userData.forEach((key, value) {
|
||||
// if (key != 'certificate_file_name') {
|
||||
request.fields[key] = value.toString();
|
||||
print("✅ Encoded $key: ${request.fields[key]}");
|
||||
// }
|
||||
});
|
||||
|
||||
if (docUploadedFile != null) {
|
||||
try {
|
||||
if (docUploadedFile!.bytes != null) {
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'additional_uploaded_file_name',
|
||||
docUploadedFile!.bytes!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
} else if (docUploadedFile!.path != null) {
|
||||
final multipartFile = await http.MultipartFile.fromPath(
|
||||
'additional_uploaded_file_name',
|
||||
docUploadedFile!.path!,
|
||||
filename: docUploadedFile!.name,
|
||||
);
|
||||
request.files.add(multipartFile);
|
||||
}
|
||||
print("📎 File attached: ${docUploadedFile!.name}");
|
||||
} catch (e) {
|
||||
print("❌ Failed to attach file: $e");
|
||||
}
|
||||
}
|
||||
|
||||
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}");
|
||||
widget.onSubmit('Success');
|
||||
reset();
|
||||
// Navigator.of(context).pop();
|
||||
} else if (response.statusCode == 403) {
|
||||
await apiService.clearLocalStorageAndRedirect();
|
||||
} else {
|
||||
final responseBody = jsonDecode(response.body);
|
||||
dynamic msg = responseBody['data'];
|
||||
print("❌ Submission failed. Status: ${response.statusCode}");
|
||||
print("Body: ${response.body}");
|
||||
|
||||
ToastHelper.showErrorToast(context, 'Proposal Creation Failed');
|
||||
}
|
||||
} catch (e) {
|
||||
print("🔥 Error submitting user: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header row
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(
|
||||
// widget.selectedQuotationFrmListId != null
|
||||
// ? 'Update Proposal'
|
||||
// : 'Create Proposal',
|
||||
// style: GoogleFonts.inter(
|
||||
// color: const Color(0xFF374141),
|
||||
// fontSize: 14,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
//
|
||||
// const SizedBox(height: 8),
|
||||
|
||||
// 🔹 Switch content dynamically
|
||||
buildFormFields(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFormFields(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
// autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
// autovalidateMode: _autoValidate
|
||||
// ? AutovalidateMode.onUserInteraction
|
||||
// : AutovalidateMode.disabled,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
|
||||
children: [
|
||||
buildIdv(context),
|
||||
Spacer(),
|
||||
// buildInsurer(context),
|
||||
buildInsurancePlanType(context),
|
||||
Spacer(),
|
||||
buildPremiumAmnt(context),
|
||||
Spacer(),
|
||||
// buildDocuments(context),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
handleDone();
|
||||
// widget.onSubmit(controller.text.trim());
|
||||
// Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 25.0,
|
||||
vertical: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: const Color(0xFF425B5B),
|
||||
),
|
||||
child: const Text(
|
||||
'Save',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
|
||||
// _buildResponsiveRow(
|
||||
// context,
|
||||
// buildAdditonalDocuments(context),
|
||||
// buildRemarks(context),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------- Claims Part -----------------------------------
|
||||
|
||||
Widget buildIdv(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('IDV *', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['idv']!,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
|
||||
],
|
||||
validator: (value) => Validators.doubleNumber(value, "IDV"),
|
||||
// validator: (value) => Validators.number(value, "IDV"),
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
// readOnly: true,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInsurer2(BuildContext context) {
|
||||
Map<String, dynamic>? selectedInsurerd = filteredInsurersData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedInsurer,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Insurer *', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
// borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.26,
|
||||
// height: 40,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyInsurer,
|
||||
selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredInsurersData;
|
||||
},
|
||||
|
||||
itemAsString: (val) => val['name'].toString(), // what to show
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
validator: (val) {
|
||||
if (val == null) {
|
||||
return "Required"; // ✅ error message
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Insurer",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor: Color(
|
||||
0xFFEDF6F5,
|
||||
), // 👈 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 buildPremiumAmnt(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Premium Amount *', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['premium_Amount']!,
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
|
||||
],
|
||||
validator: (value) => Validators.doubleNumber(value, "PremiumAmount"),
|
||||
// validator: (value) => Validators.number(value, "PremiumAmount "),
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInsurancePlanType(context) {
|
||||
Map<String, dynamic>? selectedVehicle = filteredInsuranceData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedInsPlanType,
|
||||
orElse: () => {},
|
||||
);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Plan Type *", style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
// height: 40,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKey,
|
||||
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredInsuranceData;
|
||||
},
|
||||
|
||||
itemAsString: (val) => val['insurance_plan_type'].toString(),
|
||||
compareFn: (item, selectedItem) =>
|
||||
item['id'] == selectedItem['id'], // ✅ compare by id
|
||||
validator: (val) {
|
||||
if (val == null) {
|
||||
return "Required"; // ✅ error message
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Plan Type",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor: Color(
|
||||
0xFFEDF6F5,
|
||||
), // 👈 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 Plan 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 ClaimsType : ${val['insurance_plan_type']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedInsPlanType = val['id'];
|
||||
// controllers['agentId']?.text = val['agent_code'];
|
||||
// agentId = agent['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildDocuments(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Proposal Documents ', style: _textStyle),
|
||||
const SizedBox(height: 10),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
ThemedUploadField(
|
||||
hintText: selectedFileNames ?? "Upload Document",
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.26,
|
||||
backgroundColor: Color(0xFFEDF6F5),
|
||||
onFileSelected: (fileName, file) {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
setState(() {
|
||||
docUploadedFile = file;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
if (docUploadedFileUrlFromApi != null)
|
||||
Container(
|
||||
// color: Colors.white,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => apiService.downloadFile(
|
||||
apiUrl:
|
||||
'api/quotation/downloadAdditionalUploadedFile?id=$selectedId',
|
||||
apiId: selectedId,
|
||||
localFile: docUploadedFile,
|
||||
fileName: selectedFileNames,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Color(0xFF425B5B),
|
||||
// color: Colors.green.shade300,
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
Text(
|
||||
"Download",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(Icons.download, size: 13, color: Colors.white),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------- STyle ---------------------------------
|
||||
|
||||
static final TextStyle _textStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
}
|
||||
@ -160,7 +160,6 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
"sgst": controllers["sgst"]?.text,
|
||||
"igst": controllers["igst"]?.text,
|
||||
"premium_amount": controllers["premiumTOT"]?.text,
|
||||
|
||||
"manager_id": managerId,
|
||||
// "policy_pdf_file_name": "policy_doc.pdf",
|
||||
// "policy_payment_receipt_file_name": "receipt_doc.pdf",
|
||||
@ -378,10 +377,11 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
);
|
||||
|
||||
quotationAcceptedData = acceptedItem;
|
||||
print('AcceptedData - $quotationAcceptedData');
|
||||
print('AcceptedData YYY - $quotationAcceptedData');
|
||||
|
||||
if (quotationAcceptedData != null) {
|
||||
if (!mounted) return;
|
||||
print('AcceptedData YYY2');
|
||||
setState(() {
|
||||
controllers["insurer"]?.text = quotationAcceptedData?['insurer_name'];
|
||||
acceptedQuotationId = quotationAcceptedData?['id']?.toString() ?? '';
|
||||
@ -392,6 +392,10 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
quotationAcceptedData?['premium_amount']?.toString() ?? '';
|
||||
controllers["planType"]?.text =
|
||||
quotationAcceptedData?['insurance_plan_type']?.toString() ?? '';
|
||||
controllers["policyPaymentMode"]?.text =
|
||||
quotationAcceptedData?['payment_mode_value']?.toString() ?? '';
|
||||
selectedBroker = quotationAcceptedData?['broker_id'] ?? '';
|
||||
selectedBrokerName = quotationAcceptedData?['broker_name'] ?? '';
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
@ -435,6 +439,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
policyData['premium_amount']?.toString() ?? '';
|
||||
|
||||
selectedBroker = policyData['broker_id'] ?? '';
|
||||
selectedBrokerName = policyData['broker_name'] ?? '';
|
||||
// RC FILE
|
||||
String? rcPath = policyData["policy_pdf_file_name"];
|
||||
if (rcPath != null && rcPath.isNotEmpty) {
|
||||
@ -485,6 +490,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
policyData['policy_number']?.toString() ?? '';
|
||||
controllers["policyPaymentMode"]?.text =
|
||||
policyData['payment_mode']?.toString() ?? '';
|
||||
|
||||
controllers["issueDate"]?.text =
|
||||
_formatDate(policyData['issued_date']!.toString()) ?? '';
|
||||
controllers["startDate"]?.text =
|
||||
@ -531,15 +537,16 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
|
||||
final bool isUpdating = choosedPolcyId != null;
|
||||
|
||||
if (!isUpdating && selectedBrokerName == 'Nhance') {
|
||||
// if (!isUpdating && enqBrokerName == 'Nhance') {
|
||||
ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
return;
|
||||
}
|
||||
// if (!isUpdating && selectedBrokerName == 'Nhance') {
|
||||
// // if (!isUpdating && enqBrokerName == 'Nhance') {
|
||||
// ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
// return;
|
||||
// }
|
||||
// (enqBrokerName != 'Nhance')
|
||||
// File validations
|
||||
if ((selectedBrokerName != 'Nhance') &&
|
||||
(docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
if (
|
||||
// (selectedBrokerName != 'Nhance') &&
|
||||
(docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
(docUploadedPDFFileUrlFromApi == null ||
|
||||
docUploadedPDFFileUrlFromApi!.isEmpty)) {
|
||||
ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
@ -849,8 +856,11 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// color: Colors.green,
|
||||
decoration: BoxDecoration(
|
||||
// color: Colors.orange.shade50,
|
||||
color: Color(0xffEDF6F5),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
// color: Color(0xffEDF6F5),
|
||||
border: Border(
|
||||
top: BorderSide(color: Colors.blueGrey.shade200, width: 0.2),
|
||||
),
|
||||
// borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height * 0.58,
|
||||
@ -1064,13 +1074,14 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Paymentmode(context),
|
||||
SizedBox(width: 20),
|
||||
buildBroker(context),
|
||||
if (selectedBrokerName != null) ...[
|
||||
SizedBox(width: 20),
|
||||
buildUploadPolicyPdf(context),
|
||||
SizedBox(width: 20),
|
||||
|
||||
Paymentmode(context),
|
||||
// SizedBox(width: 20),
|
||||
],
|
||||
],
|
||||
),
|
||||
@ -1386,6 +1397,8 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['policyNumber']!,
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
highlightColor: Color(0xFF50A398),
|
||||
validator: (value) => Validators.requiredField(value, "policyNumber"),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z 0-9]')),
|
||||
@ -1406,7 +1419,14 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
return Row(
|
||||
children: [
|
||||
Text('Broker *', style: _textStyle),
|
||||
SizedBox(width: 15),
|
||||
SizedBox(width: 10),
|
||||
// SizedBox(
|
||||
// width: MediaQuery.of(context).size.width * 0.095,
|
||||
// child: Text(
|
||||
// '$selectedBrokerName',
|
||||
// style: GoogleFonts.inter(fontSize: 12),
|
||||
// ),
|
||||
// ),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: MediaQuery.of(context).size.width * 0.1,
|
||||
@ -1456,20 +1476,21 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
label: "Select Broker",
|
||||
).copyWith(
|
||||
filled: true,
|
||||
fillColor:
|
||||
Colors.white, // 👈 makes the dropdown input white
|
||||
fillColor: Color(0xffEDF6F5),
|
||||
// fillColor:
|
||||
// Colors.white, // 👈 makes the dropdown input white
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.black,
|
||||
color: Color(0xffEDF6F5),
|
||||
width: 0.1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.black,
|
||||
color: Color(0xffEDF6F5),
|
||||
width: 0.1,
|
||||
),
|
||||
),
|
||||
@ -1548,23 +1569,34 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
|
||||
Widget Paymentmode(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Payment Mode *', style: _textStyle),
|
||||
SizedBox(width: 25),
|
||||
ThemedFormField(
|
||||
controller: controllers['policyPaymentMode']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: 'Online / Cash',
|
||||
// borderColor: Color(0xffEDF6F5),
|
||||
validator: (value) =>
|
||||
Validators.requiredField(value, "policyPaymentMode"),
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.07,
|
||||
SizedBox(width: 15),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.075,
|
||||
child: Text(
|
||||
controllers['policyPaymentMode']?.text ?? 'Online',
|
||||
style: GoogleFonts.inter(fontSize: 12),
|
||||
),
|
||||
),
|
||||
// ThemedFormField(
|
||||
// controller: controllers['policyPaymentMode']!,
|
||||
// // borderColor: Color(0xFFE2E8F0),
|
||||
// borderColor: Colors.white,
|
||||
// highlightColor: Colors.white,
|
||||
// // highlightColor: Color(0xFF50A398),
|
||||
// errorBorderColor: Color(0xffEDF6F5),
|
||||
// hintText: 'Online / Cash',
|
||||
// // borderColor: Color(0xffEDF6F5),
|
||||
// readOnly: true,
|
||||
// validator: (value) =>
|
||||
// Validators.requiredField(value, "policyPaymentMode"),
|
||||
// txtwidth: ResponsiveLayout.isMobile(context)
|
||||
// ? null
|
||||
// : MediaQuery.of(context).size.width * 0.07,
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -1733,7 +1765,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
child: Text('Upload Policy PDF * ', style: _textStyle),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
@ -1745,13 +1777,17 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
ThemedUploadField(
|
||||
hintText: selectedPDFFileNames ?? "Upload Document",
|
||||
allowedExtensions: ['pdf'],
|
||||
// backgroundColor: Colors.white,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
txtheight: 35,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: (docUploadedPDFFileUrlFromApi != null)
|
||||
? MediaQuery.of(context).size.width * 0.135
|
||||
? MediaQuery.of(context).size.width * 0.117
|
||||
// ? MediaQuery.of(context).size.width * 0.135
|
||||
// ? MediaQuery.of(context).size.width * 0.12
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
: MediaQuery.of(context).size.width * 0.14,
|
||||
// : MediaQuery.of(context).size.width * 0.15,
|
||||
// backgroundColor: Color(0xFFEDF6F5),
|
||||
onFileSelected: (fileName, file) async {
|
||||
print("Picked file: $fileName (${file.size} bytes)");
|
||||
@ -1775,9 +1811,9 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
docPDFUploadedFile = file;
|
||||
});
|
||||
|
||||
if (selectedBrokerName == 'Nhance') {
|
||||
handleUploadPolicy();
|
||||
}
|
||||
// if (selectedBrokerName == 'Nhance') {
|
||||
// handleUploadPolicy();
|
||||
// }
|
||||
},
|
||||
),
|
||||
|
||||
@ -1962,7 +1998,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['tp']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
validator: (value) => Validators.requiredField(value, "Third Party"),
|
||||
@ -1994,7 +2030,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['od']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
validator: (value) => Validators.requiredField(value, "OwnDamage"),
|
||||
@ -2022,7 +2058,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['pa']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
onChanged: (value) => calculateAmount(),
|
||||
@ -2051,7 +2087,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['cgst']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
validator: (value) => Validators.requiredField(value, "CGST"),
|
||||
@ -2078,7 +2114,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['sgst']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
inputFormatters: [
|
||||
@ -2105,7 +2141,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['igst']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
validator: (value) => Validators.requiredField(value, "IGST"),
|
||||
@ -2132,7 +2168,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
SizedBox(width: 20),
|
||||
ThemedFormField(
|
||||
controller: controllers['premiumTOT']!,
|
||||
backgroundColor: Color(0xffEDF6F5),
|
||||
// backgroundColor: Color(0xffEDF6F5),
|
||||
errorBorderColor: Color(0xffEDF6F5),
|
||||
hintText: '0',
|
||||
validator: (value) => Validators.requiredField(value, "premium"),
|
||||
@ -2381,9 +2417,10 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
}
|
||||
}
|
||||
|
||||
static const _textStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
static final _textStyle = GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
color: Color(0XFF334155),
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
static const _textStrongStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
|
||||
@ -215,11 +215,32 @@ class QuotationStaffTabState extends ConsumerState<QuotationStaffTab> {
|
||||
originalData = getQuotationData;
|
||||
filteredData = List.from(originalData);
|
||||
|
||||
blockKey = getQuotationData.any(
|
||||
(item) => item['status'] == 'Accepted',
|
||||
);
|
||||
// dynamic data = getQuotationData
|
||||
// .where((item) => item['status'] == 'Accepted')
|
||||
// .toList();
|
||||
// blockKey = getQuotationData.any(
|
||||
// (item) => item['status'] == 'Accepted',
|
||||
// );
|
||||
|
||||
final acceptedList = getQuotationData
|
||||
.where((item) => item['status'] == 'Accepted')
|
||||
.toList();
|
||||
|
||||
print('blockKey- $blockKey');
|
||||
// print('originalData - $getClaimPolicies');
|
||||
print('originalDataData - $acceptedList');
|
||||
|
||||
final data = acceptedList.isNotEmpty ? acceptedList.first : null;
|
||||
|
||||
final id = selectedQuotationFrmListData?['id'];
|
||||
|
||||
handleEdit(id, data);
|
||||
|
||||
// selectedQuotationFrmListData = data;
|
||||
|
||||
print('originalDatasdata- $data');
|
||||
print(
|
||||
'originalDataselectedQuotationFrmListData - $selectedQuotationFrmListData',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
getQuotationData = [];
|
||||
@ -265,51 +286,35 @@ class QuotationStaffTabState extends ConsumerState<QuotationStaffTab> {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(color: Colors.blueGrey.shade200, width: 0.2),
|
||||
),
|
||||
),
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
if (!blockKey) ...[
|
||||
// if (!blockKey && roleId != 'manager') ...[
|
||||
CreateQuotationForm(
|
||||
key: ValueKey(selectedQuotationFrmListId ?? "new"),
|
||||
userId: userId,
|
||||
selectedQuotationFrmListdata: selectedQuotationFrmListData,
|
||||
selectedQuotationFrmListId: selectedQuotationFrmListId,
|
||||
managerId: managerId,
|
||||
selectedInsurdId: selectedInsurdId,
|
||||
selectedEnquiryId: selectedEnquiryId,
|
||||
onSubmit: (value) {
|
||||
debugPrint("New Endorsement: $value");
|
||||
if (value == 'Success') {
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
|
||||
Container(
|
||||
// color: Colors.green,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: !blockKey
|
||||
? MediaQuery.of(context).size.height * 0.4
|
||||
: MediaQuery.of(context).size.height * 0.55,
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
// Text('datta')
|
||||
_buildDataTable(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
// if (!blockKey) ...[
|
||||
// if (!blockKey && roleId != 'manager') ...[
|
||||
CreateProposalForm(
|
||||
key: ValueKey(selectedQuotationFrmListId ?? "new"),
|
||||
userId: userId,
|
||||
selectedQuotationFrmListdata: selectedQuotationFrmListData,
|
||||
selectedQuotationFrmListId: selectedQuotationFrmListId,
|
||||
managerId: managerId,
|
||||
selectedInsurdId: selectedInsurdId,
|
||||
selectedEnquiryId: selectedEnquiryId,
|
||||
onSubmit: (value) {
|
||||
debugPrint("New Endorsement: $value");
|
||||
if (value == 'Success') {
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// ],
|
||||
],
|
||||
),
|
||||
// ),
|
||||
@ -388,299 +393,6 @@ class QuotationStaffTabState extends ConsumerState<QuotationStaffTab> {
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
// ? _buildDataCard(item, sno)
|
||||
return _buildDataRow(item, sno);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
// Expanded(flex: 3, child: Text(' ', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Vehicle Number', style: _headerStyle)),
|
||||
Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('IDV', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Plan Type', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Premium Amount', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Status', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('', 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: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['reg_no'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(item['insurer_name'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
item['insured_declared_value'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['premium_amount'] ?? '-', style: _dataBold),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
item['status'] ?? '-',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: _getStatusColor(item['status']),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: item['status'] == 'Pending'
|
||||
? Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
final id = item['id'];
|
||||
print('RAV - $id');
|
||||
handleEdit(id, item);
|
||||
},
|
||||
splashRadius: 20,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
)
|
||||
: Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// final id = item['id'];
|
||||
// print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
// },
|
||||
// child: item['status'] == 'Pending'
|
||||
// ? Image.asset(
|
||||
// "assets/miscellaneous/Edit.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// )
|
||||
// : Image.asset(
|
||||
// "assets/miscellaneous/Edit_muted.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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(0xFFFFFFFF),
|
||||
// color: const Color(0xFFF6FEFD),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
border: Border.all(color: const Color(0xffD9EBE8)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
// "TN64V3456",
|
||||
item['reg_no'] ?? '-',
|
||||
style: _cardheaderStyle,
|
||||
),
|
||||
item['status'] == 'Pending'
|
||||
// ? GestureDetector(
|
||||
// onTap: () {
|
||||
// final id = item['id'];
|
||||
// print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
// },
|
||||
// child: Image.asset(
|
||||
// "assets/miscellaneous/Edit.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// ),
|
||||
// )
|
||||
? Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
final id = item['id'];
|
||||
print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
},
|
||||
splashRadius: 28,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
)
|
||||
: Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
// flex: 2,
|
||||
child: Text(
|
||||
item['insurer_name'] ?? '-',
|
||||
style: _cardRow1BodyStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
item['insurance_plan_type'] ?? '-',
|
||||
style: _cardRow1BodyStyle,
|
||||
),
|
||||
|
||||
Text(
|
||||
'IDV: ${item['insured_declared_value']}' ?? '-',
|
||||
style: _cardRow2BodyStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Status : ', // key
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
), // key color
|
||||
),
|
||||
TextSpan(
|
||||
text: '${item['status'] ?? '-'}', // value
|
||||
style: GoogleFonts.inter(
|
||||
color: _getStatusColor(item['status']),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
), // value color
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item['premium_amount']}' ?? '-',
|
||||
style: _cardheaderStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final _dataBold = GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
|
||||
|
||||
@ -0,0 +1,733 @@
|
||||
import 'dart:io' as html;
|
||||
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
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';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../../../core/routing/routes.dart';
|
||||
import '../../../../../core/services/api_service.dart';
|
||||
import '../../../../../data/services/auth_service.dart';
|
||||
import '../../../../../data/utils/validators.dart';
|
||||
import '../../../../layouts/main_layout.dart';
|
||||
import '../../../../layouts/responsive_layout.dart';
|
||||
import '../../../../providers/manager_provider.dart';
|
||||
import '../../../../providers/quotation_staff_proivder.dart';
|
||||
import '../../../../themes/indicators/text_field_theme.dart';
|
||||
import '../../quotations/createQuotationPopUp.dart';
|
||||
import 'createQuotation.dart';
|
||||
import 'createQuotationOld.dart';
|
||||
|
||||
class PropoalStaffTab extends ConsumerStatefulWidget {
|
||||
String? id;
|
||||
List<Map<String, dynamic>>? data;
|
||||
final Future<void> Function()? onRefresh;
|
||||
|
||||
PropoalStaffTab({super.key, this.data, this.id, this.onRefresh});
|
||||
@override
|
||||
ConsumerState<PropoalStaffTab> createState() => PropoalStaffTabState();
|
||||
}
|
||||
|
||||
class PropoalStaffTabState extends ConsumerState<PropoalStaffTab> {
|
||||
int currentPage = 0;
|
||||
int itemsPerPage = 10;
|
||||
|
||||
bool blockKey = false;
|
||||
|
||||
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 = [
|
||||
'regNum',
|
||||
'insurer',
|
||||
'mobile',
|
||||
'code',
|
||||
'address',
|
||||
'regNo',
|
||||
];
|
||||
late String isActive = "1";
|
||||
PlatformFile? docUploadedFile;
|
||||
|
||||
// PlatformFile? passportFile;
|
||||
String? selectedFileNames;
|
||||
String? selectedInsurdId;
|
||||
String? passportFileUrlFromApi;
|
||||
String? selectedId;
|
||||
bool isLoading = false;
|
||||
bool isquotation = false;
|
||||
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
String? _token;
|
||||
dynamic userId;
|
||||
dynamic roleId;
|
||||
dynamic selectedEnquiryId;
|
||||
dynamic managerId;
|
||||
dynamic enqQuotation;
|
||||
dynamic selectedQuotationFrmListId;
|
||||
|
||||
Map<String, dynamic>? selectedQuotationFrmListData;
|
||||
|
||||
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 = {
|
||||
"regNum": controllers["regNum"]?.text,
|
||||
// "insurer": controllers["insurer"]?.text,
|
||||
"insurer": controllers["insurer"]?.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 = [];
|
||||
late Map<String, dynamic> enquiryData;
|
||||
List<Map<String, dynamic>> originalData = [];
|
||||
List<Map<String, dynamic>> filteredData = [];
|
||||
// bool isLoading = false;
|
||||
|
||||
Future<void> _restoreManagerId(WidgetRef ref) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
final String? savedId = prefs.getString(
|
||||
'enqStaffDataId',
|
||||
); // already a string
|
||||
if (savedId != null) {
|
||||
ref.read(quotationStaffIdProvider.notifier).state = savedId;
|
||||
print("savedenqStaffDataId ID restored: $savedId");
|
||||
|
||||
_loadData(savedId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService();
|
||||
|
||||
if (kIsWeb) {
|
||||
Future.microtask(() => _restoreManagerId(ref));
|
||||
}
|
||||
|
||||
for (String field in tabHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
}
|
||||
|
||||
_initializeToken();
|
||||
|
||||
Future.microtask(() {
|
||||
managerId = ref.read(managerIdProvider); // ✅ use read
|
||||
userId = ref.read(userIdProvider); // ✅ use read
|
||||
roleId = ref.read(userRoleProvider); // ✅ use read
|
||||
|
||||
print('QmanagerId - $managerId');
|
||||
print('QuserId - $userId');
|
||||
print('QroleId - $roleId');
|
||||
enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read
|
||||
print('QenqQuotation - $enqQuotation');
|
||||
if (enqQuotation != null) {
|
||||
_loadData(enqQuotation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Dispose all TextEditingControllers
|
||||
for (var controller in controllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
}
|
||||
|
||||
void updateEnquiryData(enquiryData) {
|
||||
print('updateEnquiryData - $enquiryData');
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
selectedInsurdId = enquiryData['insurer_id'];
|
||||
selectedEnquiryId = enquiryData['id'];
|
||||
print('selectedEnquiryId- $selectedEnquiryId');
|
||||
controllers["regNum"]?.text = enquiryData['reg_no'];
|
||||
// controllers["insurer"]?.text = enquiryData['insurer_name'];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadData(enqQuotation) async {
|
||||
getQuotationList(enqQuotation);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
print("refresh--");
|
||||
setState(() {
|
||||
selectedQuotationFrmListId = null;
|
||||
selectedQuotationFrmListData = null;
|
||||
});
|
||||
|
||||
if (enqQuotation != null) {
|
||||
_loadData(enqQuotation);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getQuotationList(enqQuotation) async {
|
||||
print('getClaimList called enqQuotation- $enqQuotation');
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.findEnqQuotePolicyView(enqQuotation);
|
||||
if (!mounted) return;
|
||||
if (response['status'] == 'success') {
|
||||
print('quoationListData - ${response['data']}');
|
||||
setState(() {
|
||||
enquiryData = Map<String, dynamic>.from(response['data']['enquiry']);
|
||||
print('enquiryData - ${enquiryData}');
|
||||
|
||||
updateEnquiryData(enquiryData);
|
||||
// getQuotationData = List<Map<String, dynamic>>.from(response['data']);
|
||||
getQuotationData = List<Map<String, dynamic>>.from(
|
||||
response['data']['quotations'],
|
||||
);
|
||||
originalData = getQuotationData;
|
||||
filteredData = List.from(originalData);
|
||||
|
||||
blockKey = getQuotationData.any(
|
||||
(item) => item['status'] == 'Accepted',
|
||||
);
|
||||
print('blockKey- $blockKey');
|
||||
// print('originalData - $getClaimPolicies');
|
||||
});
|
||||
} else {
|
||||
getQuotationData = [];
|
||||
originalData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Color _getStatusColor(String? status) {
|
||||
switch (status) {
|
||||
case 'Pending':
|
||||
return Colors.yellow;
|
||||
case 'Accepted':
|
||||
return Colors.green;
|
||||
case 'Rejected':
|
||||
return Colors.red;
|
||||
default:
|
||||
return Colors.grey;
|
||||
}
|
||||
}
|
||||
|
||||
void handleEdit(id, data) {
|
||||
print("sfd - $id");
|
||||
print("sfdewe - $data");
|
||||
setState(() {
|
||||
selectedQuotationFrmListId = id;
|
||||
selectedQuotationFrmListData = data;
|
||||
// data = data;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isMobile = ResponsiveLayout.isMobile(context);
|
||||
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
if (!blockKey) ...[
|
||||
// if (!blockKey && roleId != 'manager') ...[
|
||||
CreateQuotationForm(
|
||||
key: ValueKey(selectedQuotationFrmListId ?? "new"),
|
||||
userId: userId,
|
||||
selectedQuotationFrmListdata: selectedQuotationFrmListData,
|
||||
selectedQuotationFrmListId: selectedQuotationFrmListId,
|
||||
managerId: managerId,
|
||||
selectedInsurdId: selectedInsurdId,
|
||||
selectedEnquiryId: selectedEnquiryId,
|
||||
onSubmit: (value) {
|
||||
debugPrint("New Endorsement: $value");
|
||||
if (value == 'Success') {
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
|
||||
Container(
|
||||
// color: Colors.green,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: !blockKey
|
||||
? MediaQuery.of(context).size.height * 0.4
|
||||
: MediaQuery.of(context).size.height * 0.55,
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
// Text('datta')
|
||||
_buildDataTable(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// ),
|
||||
);
|
||||
}
|
||||
|
||||
Widget registerNumber(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Vehicle Number', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['regNum']!,
|
||||
readOnly: true,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget insurer(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Insurer', style: _textStyle),
|
||||
SizedBox(height: 10),
|
||||
ThemedFormField(
|
||||
controller: controllers['insurer']!,
|
||||
readOnly: true,
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.2,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildResponsiveField({required String label, required Widget field}) {
|
||||
final isMobile = ResponsiveLayout.isMobile(context);
|
||||
|
||||
if (isMobile) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: _textStyle),
|
||||
const SizedBox(height: 8),
|
||||
field,
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text(label, style: _textStyle)),
|
||||
const SizedBox(width: 10),
|
||||
field,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildId(BuildContext context) {
|
||||
return buildResponsiveField(
|
||||
label: "Vehicle 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 _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)
|
||||
// ? _buildDataCard(item, sno)
|
||||
return _buildDataRow(item, sno);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFEDF6F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 3),
|
||||
child: Row(
|
||||
children: [
|
||||
// Expanded(flex: 3, child: Text(' ', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Vehicle Number', style: _headerStyle)),
|
||||
Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('IDV', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Plan Type', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Premium Amount', style: _headerStyle)),
|
||||
Expanded(flex: 2, child: Text('Status', style: _headerStyle)),
|
||||
Expanded(flex: 1, child: Text('', 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: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['reg_no'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(item['insurer_name'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
item['insured_declared_value'] ?? '-',
|
||||
style: _dataBold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(item['premium_amount'] ?? '-', style: _dataBold),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
item['status'] ?? '-',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: _getStatusColor(item['status']),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: item['status'] == 'Pending'
|
||||
? Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
final id = item['id'];
|
||||
print('RAV - $id');
|
||||
handleEdit(id, item);
|
||||
},
|
||||
splashRadius: 20,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
)
|
||||
: Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// final id = item['id'];
|
||||
// print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
// },
|
||||
// child: item['status'] == 'Pending'
|
||||
// ? Image.asset(
|
||||
// "assets/miscellaneous/Edit.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// )
|
||||
// : Image.asset(
|
||||
// "assets/miscellaneous/Edit_muted.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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(0xFFFFFFFF),
|
||||
// color: const Color(0xFFF6FEFD),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
border: Border.all(color: const Color(0xffD9EBE8)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
// "TN64V3456",
|
||||
item['reg_no'] ?? '-',
|
||||
style: _cardheaderStyle,
|
||||
),
|
||||
item['status'] == 'Pending'
|
||||
// ? GestureDetector(
|
||||
// onTap: () {
|
||||
// final id = item['id'];
|
||||
// print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
// },
|
||||
// child: Image.asset(
|
||||
// "assets/miscellaneous/Edit.png",
|
||||
// height: 15,
|
||||
// width: 15,
|
||||
// ),
|
||||
// )
|
||||
? Tooltip(
|
||||
message: 'Edit',
|
||||
child: IconButton(
|
||||
icon: Image.asset(
|
||||
"assets/miscellaneous/Edit.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
final id = item['id'];
|
||||
print('RAV - $id');
|
||||
// handleEdit(id, item);
|
||||
},
|
||||
splashRadius: 28,
|
||||
hoverColor: Colors.black12,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
)
|
||||
: Image.asset(
|
||||
"assets/miscellaneous/Edit_muted.png",
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
// flex: 2,
|
||||
child: Text(
|
||||
item['insurer_name'] ?? '-',
|
||||
style: _cardRow1BodyStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
item['insurance_plan_type'] ?? '-',
|
||||
style: _cardRow1BodyStyle,
|
||||
),
|
||||
|
||||
Text(
|
||||
'IDV: ${item['insured_declared_value']}' ?? '-',
|
||||
style: _cardRow2BodyStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Status : ', // key
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
), // key color
|
||||
),
|
||||
TextSpan(
|
||||
text: '${item['status'] ?? '-'}', // value
|
||||
style: GoogleFonts.inter(
|
||||
color: _getStatusColor(item['status']),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
), // value color
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item['premium_amount']}' ?? '-',
|
||||
style: _cardheaderStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final _dataBold = GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF000000),
|
||||
);
|
||||
|
||||
static final _dataSub = GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: Color(0xFF585757),
|
||||
);
|
||||
|
||||
static final _headerStyle = GoogleFonts.inter(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13,
|
||||
);
|
||||
|
||||
static final _textStyle = GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
static final _cardheaderStyle = GoogleFonts.inter(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
);
|
||||
|
||||
static final _cardBodyStyle = GoogleFonts.inter(
|
||||
color: Color(0xFF545454),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12,
|
||||
);
|
||||
|
||||
static final _cardRow1BodyStyle = GoogleFonts.inter(
|
||||
// color: Color(0xFF545454),
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
);
|
||||
static final _cardRow2BodyStyle = GoogleFonts.inter(
|
||||
color: const Color(0xFF545454),
|
||||
// color: Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
);
|
||||
}
|
||||
@ -6,6 +6,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/policyTab.dart';
|
||||
import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/quotationTab.dart';
|
||||
import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/quotationTab_old.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../../../core/routing/routes.dart';
|
||||
@ -62,8 +63,9 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
} else {
|
||||
setState(() => isLoading = false);
|
||||
tabs = [
|
||||
// TabItem("Proposal", PropoalStaffTab()),
|
||||
TabItem("Proposal", QuotationStaffTab()),
|
||||
// TabItem("Policy", PolicyStaffTab()),
|
||||
TabItem("Policy", PolicyStaffTab()),
|
||||
];
|
||||
}
|
||||
});
|
||||
@ -103,19 +105,20 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
selectedVehicleType = data['enquiry']?['vehicle_type'];
|
||||
selectedBrokerName = data['enquiry']?['broker_name'];
|
||||
selectedAgentName = data['enquiry']?['agent_name'];
|
||||
selectedInsurer = data['enquiry']?['insurer_short_name'];
|
||||
selectedInsurer = data['enquiry']?['short_name'];
|
||||
print(
|
||||
'loadQuotationTab 4s -$selectedBrokerName $selectedInsuredName - $selectedVehicleNum - $selectedVehicleType',
|
||||
);
|
||||
|
||||
tabs = [
|
||||
// TabItem("Proposal", PropoalStaffTab()),
|
||||
TabItem("Proposal", QuotationStaffTab()),
|
||||
// TabItem("Policy", PolicyStaffTab()),
|
||||
TabItem("Policy", PolicyStaffTab()),
|
||||
];
|
||||
|
||||
if (widget.showKey == "Policy") {
|
||||
tabs.add(TabItem("Policy", PolicyStaffTab()));
|
||||
}
|
||||
// if (widget.showKey == "Policy") {
|
||||
// tabs.add(TabItem("Policy", PolicyStaffTab()));
|
||||
// }
|
||||
|
||||
// if (tabIndex != null) {
|
||||
// selectedIndex = tabIndex;
|
||||
@ -246,12 +249,13 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 10),
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
children: List.generate(tabs.length, (index) {
|
||||
final isSelected = selectedIndex == index;
|
||||
|
||||
return Padding(
|
||||
return Container(
|
||||
// decoration: BoxDecoration(border: Border(bottom: BorderSide())),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1.0),
|
||||
child: InkWell(
|
||||
onTap: () => setState(() => selectedIndex = index),
|
||||
@ -259,18 +263,21 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// ---- TEXT ----
|
||||
Text(
|
||||
tabs[index].title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: isSelected
|
||||
? FontWeight.w600
|
||||
: FontWeight.w400,
|
||||
color: isSelected
|
||||
? const Color(
|
||||
0xFF2E7D6E,
|
||||
) // Purple-ish like screenshot
|
||||
: Colors.black87,
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
tabs[index].title,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontWeight: isSelected
|
||||
? FontWeight.w600
|
||||
: FontWeight.w600,
|
||||
color: isSelected
|
||||
? const Color(
|
||||
0xFF2E7D6E,
|
||||
) // Purple-ish like screenshot
|
||||
: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -295,6 +302,7 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
Expanded(child: tabs[selectedIndex].widget),
|
||||
],
|
||||
);
|
||||
|
||||
@ -18,6 +18,7 @@ class ThemedFormField extends HookWidget {
|
||||
this.isObscurable = false,
|
||||
this.hintColor,
|
||||
this.backgroundColor,
|
||||
this.highlightColor,
|
||||
this.borderColor,
|
||||
this.errorBorderColor,
|
||||
this.errorTextColor,
|
||||
@ -39,6 +40,7 @@ class ThemedFormField extends HookWidget {
|
||||
final double? enableBorderWidth;
|
||||
final String? hintText;
|
||||
final Color? textColr;
|
||||
final Color? highlightColor;
|
||||
|
||||
final String? imgPath;
|
||||
final String? Function(String? text)? validator;
|
||||
@ -133,7 +135,7 @@ class ThemedFormField extends HookWidget {
|
||||
: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ?? Color(0xFF50A398),
|
||||
color: highlightColor ?? borderColor ?? Color(0xFF50A398),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@ -169,7 +171,6 @@ class ThemedFormField extends HookWidget {
|
||||
);
|
||||
return Container(
|
||||
width: txtwidth ?? MediaQuery.of(context).size.width,
|
||||
|
||||
height: txtheight ?? null,
|
||||
child: DecoratedBox(
|
||||
decoration: boxDecoration,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user