pay out module

This commit is contained in:
Surendiran 2025-11-21 18:41:25 +05:30
commit 12daf54cb7
20 changed files with 4049 additions and 738 deletions

File diff suppressed because one or more lines are too long

View File

@ -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();

View File

@ -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';
}

View File

@ -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 = {

View File

@ -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(

View 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,
);
}

View 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.70.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,
);
}

View 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,
);
}

View 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.70.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,
);
}

View File

@ -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: [

View File

@ -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),

View File

@ -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, //

View File

@ -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

View File

@ -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,
);
}

View File

@ -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,
);
}

View File

@ -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,

View File

@ -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,

View File

@ -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,
);
}

View File

@ -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),
],
);

View File

@ -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,