UI_REDESIGN
This commit is contained in:
parent
7b0e14c1ba
commit
eb721cd164
File diff suppressed because one or more lines are too long
@ -7,6 +7,8 @@ import 'package:nhance_partner/data/services/auth_service.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'dart:convert';
|
||||
import '../../data/utils/toastNotification.dart';
|
||||
import '../../presentation/screens/staff/enquiry_single_page/model/dropdown_option.dart';
|
||||
import '../../presentation/screens/staff/enquiry_single_page/model/enquiry_model.dart';
|
||||
import '../config/env.dart';
|
||||
import 'package:universal_html/html.dart' as html;
|
||||
import 'package:http/http.dart' as http;
|
||||
@ -1079,4 +1081,89 @@ class ApiService {
|
||||
print('fetchAGENTNameDropDown 4 - $response');
|
||||
return response;
|
||||
}
|
||||
|
||||
static late String _baseUrl;
|
||||
|
||||
static void initialize(String baseUrl) {
|
||||
_baseUrl = Env.apiUrl;
|
||||
}
|
||||
|
||||
// Fetch all enquiries
|
||||
static Future<List<EnquiryModel>> fetchEnquiries() async {
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse('$_baseUrl/api/enquiries'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => EnquiryModel.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load enquiries');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error fetching enquiries: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Create new enquiry
|
||||
static Future<EnquiryModel> createEnquiry(EnquiryModel enquiry) async {
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('$_baseUrl/api/enquiries'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(enquiry.toJson()),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return EnquiryModel.fromJson(json.decode(response.body));
|
||||
} else {
|
||||
throw Exception('Failed to create enquiry');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error creating enquiry: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Update enquiry
|
||||
static Future<EnquiryModel> updateEnquiry(
|
||||
String id,
|
||||
EnquiryModel enquiry,
|
||||
) async {
|
||||
try {
|
||||
final response = await http.put(
|
||||
Uri.parse('$_baseUrl/api/enquiries/$id'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(enquiry.toJson()),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return EnquiryModel.fromJson(json.decode(response.body));
|
||||
} else {
|
||||
throw Exception('Failed to update enquiry');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error updating enquiry: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch dropdown options
|
||||
static Future<List<DropdownOption>> fetchDropdownOptions(String type) async {
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse('$_baseUrl/api/options/$type'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((json) => DropdownOption.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load options');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error fetching options: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:nhance_partner/presentation/providers/manager_provider.dart';
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import 'package:nhance_partner/presentation/screens/staff/enquiry_single_page/widgets/enquiry_screen.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'core/routing/app_router.dart';
|
||||
|
||||
@ -110,6 +111,21 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// return MaterialApp(
|
||||
// title: 'Nhance Partner',
|
||||
// debugShowCheckedModeBanner: false,
|
||||
// theme: ThemeData(
|
||||
// primaryColor: const Color(0xFFb8e6e1),
|
||||
// colorScheme: ColorScheme.fromSeed(
|
||||
// seedColor: const Color(0xFFb8e6e1),
|
||||
// primary: const Color(0xFF4a90e2),
|
||||
// ),
|
||||
// scaffoldBackgroundColor: const Color(0xFFf5f5f5),
|
||||
// useMaterial3: true,
|
||||
// ),
|
||||
// home: const EnquiryScreen(),
|
||||
// );
|
||||
|
||||
return MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Nhance Partner',
|
||||
@ -126,6 +142,7 @@ class _MyAppState extends ConsumerState<MyApp> {
|
||||
Theme.of(context).textTheme, // base on default styles
|
||||
),
|
||||
),
|
||||
|
||||
routerConfig: appRouter,
|
||||
);
|
||||
}
|
||||
|
||||
133
lib/presentation/layouts/aphed
Normal file
133
lib/presentation/layouts/aphed
Normal file
@ -0,0 +1,133 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
const AppHeader({super.key});
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(60);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
backgroundColor: const Color(0xFFb8e6e1),
|
||||
elevation: 2,
|
||||
title: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Nhance',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF00acc1),
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Partner',
|
||||
style: TextStyle(fontSize: 10, color: Color(0xFF666666)),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text(
|
||||
'Dashboard',
|
||||
style: TextStyle(color: Colors.black87),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Enquiry', style: TextStyle(color: Colors.black87)),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Text('Users', style: TextStyle(color: Colors.black87)),
|
||||
Icon(Icons.arrow_drop_down, color: Colors.black87),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(value: 'staff', child: Text('Staff')),
|
||||
const PopupMenuItem(value: 'agent', child: Text('Agent')),
|
||||
],
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Text('Reports', style: TextStyle(color: Colors.black87)),
|
||||
Icon(Icons.arrow_drop_down, color: Colors.black87),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(value: 'claims', child: Text('Claims')),
|
||||
const PopupMenuItem(
|
||||
value: 'endorsement',
|
||||
child: Text('Endorsement'),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: PopupMenuButton<String>(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'John Doe',
|
||||
style: TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
Text(
|
||||
'Administrator',
|
||||
style: TextStyle(fontSize: 11, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: const Color(0xFF4a90e2),
|
||||
child: const Text(
|
||||
'JD',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'profile',
|
||||
child: Text('👤 Profile Details'),
|
||||
),
|
||||
const PopupMenuItem(value: 'logout', child: Text('🚪 Logout')),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
545
lib/presentation/layouts/appheader.dart
Normal file
545
lib/presentation/layouts/appheader.dart
Normal file
@ -0,0 +1,545 @@
|
||||
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:jwt_decode/jwt_decode.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 '../providers/manager_provider.dart';
|
||||
import '../providers/userRoleProvider.dart';
|
||||
import '../screens/UserManagement/Profile/profile_web.dart';
|
||||
|
||||
class AppHeader extends ConsumerStatefulWidget implements PreferredSizeWidget {
|
||||
const AppHeader({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<AppHeader> createState() => _AppHeaderState();
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
|
||||
class _AppHeaderState extends ConsumerState<AppHeader> {
|
||||
String? role;
|
||||
ApiService apiService = ApiService();
|
||||
Map<String, dynamic>? profileData;
|
||||
String? _token;
|
||||
OverlayEntry? _overlayEntry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadUser();
|
||||
_initializeToken();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hidePopup();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
if (_token != null) {
|
||||
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
|
||||
setState(() {
|
||||
profileData = decodedToken['data'];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadUser() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
role = prefs.getString('userRole') ?? "Guest";
|
||||
});
|
||||
print('AppHeader Role - $role');
|
||||
}
|
||||
|
||||
// Clear dashboard filters
|
||||
Future<void> _clearDashboardFilters() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('dashboardKeyProvider');
|
||||
await prefs.remove('dashboardStatusProvider');
|
||||
await prefs.remove('dashboardStaffIdProvider');
|
||||
}
|
||||
|
||||
// Show hover popup
|
||||
void _showPopup(BuildContext context, Offset offset, Size size, String key) {
|
||||
_hidePopup();
|
||||
|
||||
_overlayEntry = OverlayEntry(
|
||||
builder: (_) {
|
||||
return Positioned(
|
||||
left: offset.dx,
|
||||
top: offset.dy + size.height + 8,
|
||||
child: MouseRegion(
|
||||
onExit: (_) => _hidePopup(),
|
||||
child: Material(
|
||||
elevation: 8,
|
||||
shadowColor: Colors.black26,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Colors.white,
|
||||
border: Border.all(color: Colors.grey.shade200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (key == 'User') ...[
|
||||
_buildPopupItem(
|
||||
label: "Partner",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.agentLst);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
_buildPopupItem(
|
||||
label: "Staff",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.staffLst);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (key == 'Reports') ...[
|
||||
if (role == 'manager') ...[
|
||||
_buildPopupItem(
|
||||
label: "Attendance",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.allStaffAttendance);
|
||||
},
|
||||
),
|
||||
],
|
||||
SizedBox(height: 2),
|
||||
_buildPopupItem(
|
||||
label: "Claims",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.claimlist);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
_buildPopupItem(
|
||||
label: "Endorsement",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.endosement);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
_buildPopupItem(
|
||||
label: "Policy",
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
context.go(AppRoutes.policylist);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Overlay.of(context, rootOverlay: true).insert(_overlayEntry!);
|
||||
}
|
||||
|
||||
void _hidePopup() {
|
||||
_overlayEntry?.remove();
|
||||
_overlayEntry = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final roleId = ref.watch(userRoleProvider);
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: const Color(0xFFD6F6F4),
|
||||
elevation: 0,
|
||||
automaticallyImplyLeading: false,
|
||||
titleSpacing: 24,
|
||||
toolbarHeight: 64,
|
||||
title: Row(
|
||||
children: [
|
||||
// Logo
|
||||
Image.asset(
|
||||
"assets/login/nhance-partner-logo.png",
|
||||
height: 45,
|
||||
width: 95,
|
||||
),
|
||||
|
||||
Spacer(),
|
||||
|
||||
// Dashboard
|
||||
_buildMenuItem(
|
||||
icon: Icons.dashboard,
|
||||
label: "Dashboard",
|
||||
onTap: () async {
|
||||
_hidePopup();
|
||||
await _clearDashboardFilters();
|
||||
context.go(AppRoutes.dashboard);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(width: 15),
|
||||
|
||||
// Enquiry
|
||||
_buildMenuItem(
|
||||
icon: Icons.list_alt_rounded,
|
||||
label: "Enquiry",
|
||||
onTap: () async {
|
||||
_hidePopup();
|
||||
await _clearDashboardFilters();
|
||||
if (roleId == 'agent') {
|
||||
context.go(AppRoutes.enquiryLst);
|
||||
} else {
|
||||
context.go(AppRoutes.enquiryForStaff);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// User (Manager Only) - with hover popup
|
||||
if (roleId == 'manager') ...[
|
||||
_buildHoverMenuItem(
|
||||
icon: Icons.person_add_alt,
|
||||
label: "User",
|
||||
popupKey: 'User',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
// Reports (Not Staff) - with hover popup
|
||||
if (roleId != 'staff') ...[
|
||||
_buildHoverMenuItem(
|
||||
icon: Icons.receipt_long,
|
||||
label: "Reports",
|
||||
popupKey: 'Reports',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
const Spacer(),
|
||||
|
||||
// Raise Enquiry button for agents
|
||||
if (role == 'agent') ...[
|
||||
Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF425B5B),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('enqAgentDataId');
|
||||
ref.read(enquiryIdProvider.notifier).state = null;
|
||||
context.go(AppRoutes.tabEnquiry);
|
||||
},
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Raise Enquiry',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
|
||||
// Notifications button
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey.shade200, width: 1),
|
||||
),
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(
|
||||
Icons.notifications_none,
|
||||
color: Colors.black87,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
debugPrint("Notifications tapped");
|
||||
},
|
||||
tooltip: 'Notifications',
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Profile with popup
|
||||
PopupMenuButton<String>(
|
||||
color: Colors.white,
|
||||
offset: const Offset(0, 52),
|
||||
child: Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// border: Border.all(color: Colors.grey.shade200, width: 1),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 14,
|
||||
// backgroundColor: const Color(0xFF4a90e2),
|
||||
backgroundColor: const Color(0xFF2E7D6E),
|
||||
child: Text(
|
||||
profileData?['name']?.substring(0, 2).toUpperCase() ??
|
||||
'P',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
profileData?['name'] ?? 'Manager',
|
||||
style: GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 11,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(role == 'agent') ? 'partner' : role ?? 'manager',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 9,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
size: 16,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
|
||||
child: Container(
|
||||
width: 480,
|
||||
color: Colors.white,
|
||||
child: ProfilePopUp(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Logout button
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey.shade200, width: 1),
|
||||
),
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.logout, color: Colors.black87, size: 18),
|
||||
onPressed: () async {
|
||||
debugPrint("Logout tapped");
|
||||
AuthService.clearToken();
|
||||
if (role != 'handler') {
|
||||
await apiService.logoutUsingAPI(context);
|
||||
}
|
||||
context.go(AppRoutes.login);
|
||||
},
|
||||
tooltip: 'Logout',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Simple menu item widget
|
||||
Widget _buildMenuItem({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => _hidePopup(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 18, color: Colors.black87),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.black87,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Menu item with hover popup
|
||||
Widget _buildHoverMenuItem({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required String popupKey,
|
||||
}) {
|
||||
return Builder(
|
||||
builder: (itemContext) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) {
|
||||
final renderBox = itemContext.findRenderObject() as RenderBox;
|
||||
final offset = renderBox.localToGlobal(Offset.zero);
|
||||
final size = renderBox.size;
|
||||
_showPopup(context, offset, size, popupKey);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 18, color: Colors.black87),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.black87,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Icon(Icons.arrow_drop_down, color: Colors.black87, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Popup item widget
|
||||
Widget _buildPopupItem({required String label, required VoidCallback onTap}) {
|
||||
return _HoverableContainer(
|
||||
label: label,
|
||||
onTap: onTap,
|
||||
normalColor: Colors.white,
|
||||
hoverColor: const Color(0xFFF5F5F5),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(64);
|
||||
}
|
||||
|
||||
// Hoverable container for popup items
|
||||
class _HoverableContainer extends StatefulWidget {
|
||||
final String label;
|
||||
final VoidCallback onTap;
|
||||
final Color normalColor;
|
||||
final Color hoverColor;
|
||||
|
||||
const _HoverableContainer({
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
required this.normalColor,
|
||||
required this.hoverColor,
|
||||
});
|
||||
|
||||
@override
|
||||
_HoverableContainerState createState() => _HoverableContainerState();
|
||||
}
|
||||
|
||||
class _HoverableContainerState extends State<_HoverableContainer> {
|
||||
bool _isHovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _isHovered = true),
|
||||
onExit: (_) => setState(() => _isHovered = false),
|
||||
child: InkWell(
|
||||
onTap: widget.onTap,
|
||||
child: Container(
|
||||
width: 140,
|
||||
decoration: BoxDecoration(
|
||||
color: _isHovered ? widget.hoverColor : widget.normalColor,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.label,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios_rounded,
|
||||
size: 10,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ import '../widgets/topbar.dart';
|
||||
import '../widgets/footer.dart';
|
||||
import '../widgets/drawer_menu.dart';
|
||||
import '../widgets/mobile_tabs.dart';
|
||||
import 'appheader.dart';
|
||||
import 'responsive_layout.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
// import 'package:http/http.dart' as ref;
|
||||
@ -90,70 +91,72 @@ class MainLayoutState extends ConsumerState<MainLayout> {
|
||||
|
||||
// Web Layout
|
||||
web: Scaffold(
|
||||
appBar: TopBar(
|
||||
title: widget.title,
|
||||
onMenuPressed: () {
|
||||
// Scaffold.of(context).openDrawer();
|
||||
// Scaffold.of(context).openDrawer();
|
||||
|
||||
setState(() {
|
||||
isDrawerOpen = !isDrawerOpen;
|
||||
});
|
||||
},
|
||||
onLogout: () async {
|
||||
debugPrint("Logout tapped");
|
||||
AuthService.clearToken();
|
||||
if (role != 'handler') {
|
||||
await apiService.logoutUsingAPI(context);
|
||||
}
|
||||
context.go(AppRoutes.login);
|
||||
},
|
||||
|
||||
onProfile: (TapDownDetails details) {
|
||||
final RenderBox overlay =
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
|
||||
showMenu(
|
||||
context: context,
|
||||
color: Colors.white,
|
||||
position: RelativeRect.fromRect(
|
||||
details.globalPosition & const Size(40, 40),
|
||||
Offset.zero & overlay.size,
|
||||
),
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
child: SizedBox(width: 80, child: ProfilePopUp()),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
onNotifications: () {
|
||||
debugPrint("Notifications tapped");
|
||||
},
|
||||
),
|
||||
// appBar: TopBar(
|
||||
// title: widget.title,
|
||||
// onMenuPressed: () {
|
||||
// // Scaffold.of(context).openDrawer();
|
||||
// // Scaffold.of(context).openDrawer();
|
||||
//
|
||||
// setState(() {
|
||||
// isDrawerOpen = !isDrawerOpen;
|
||||
// });
|
||||
// },
|
||||
// onLogout: () async {
|
||||
// debugPrint("Logout tapped");
|
||||
// AuthService.clearToken();
|
||||
// if (role != 'handler') {
|
||||
// await apiService.logoutUsingAPI(context);
|
||||
// }
|
||||
// context.go(AppRoutes.login);
|
||||
// },
|
||||
//
|
||||
// onProfile: (TapDownDetails details) {
|
||||
// final RenderBox overlay =
|
||||
// Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
//
|
||||
// showMenu(
|
||||
// context: context,
|
||||
//
|
||||
// color: Colors.white,
|
||||
// position: RelativeRect.fromRect(
|
||||
// details.globalPosition & const Size(40, 40),
|
||||
// Offset.zero & overlay.size,
|
||||
// ),
|
||||
// items: [
|
||||
// PopupMenuItem(
|
||||
// enabled: false,
|
||||
// child: SizedBox(width: 480, child: ProfilePopUp()),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
//
|
||||
// onNotifications: () {
|
||||
// debugPrint("Notifications tapped");
|
||||
// },
|
||||
// ),
|
||||
appBar: AppHeader(),
|
||||
body: Row(
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
width: isDrawerOpen ? 100 : 0, // 👈 adjust width
|
||||
child: isDrawerOpen
|
||||
? const DrawerMenu()
|
||||
: const SizedBox.shrink(), // hides drawer
|
||||
),
|
||||
|
||||
// AnimatedContainer(
|
||||
// duration: const Duration(milliseconds: 250),
|
||||
// width: isDrawerOpen ? 100 : 0, // 👈 adjust width
|
||||
// child: isDrawerOpen
|
||||
// ? const DrawerMenu()
|
||||
// : const SizedBox.shrink(), // hides drawer
|
||||
// ),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
color: Color(0xFFFCFDFD),
|
||||
// color: Color(0xffF8FAFB),
|
||||
// color: Colors.white,
|
||||
child: Column(children: [Expanded(child: widget.body)]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// body: Row(
|
||||
// children: [
|
||||
// const SizedBox(
|
||||
|
||||
@ -164,6 +164,8 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
width: 450,
|
||||
color: Colors.white,
|
||||
// color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@ -180,7 +182,8 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
color: Colors.white,
|
||||
// color: const Color(0xFFF1F1F1),
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
child: InkWell(
|
||||
@ -212,7 +215,8 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFD9D9D9),
|
||||
// color: Color(0xFFD9D9D9),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: Icon(
|
||||
@ -429,7 +433,7 @@ class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
|
||||
static const _topheaderStyle = TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 19,
|
||||
fontSize: 18,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1854,32 +1854,29 @@ class UnassignedEnq extends StatelessWidget {
|
||||
focusColor: Color(0xFF3E5B56),
|
||||
highlightColor: Color(0xFF3E5B56),
|
||||
borderRadius: BorderRadius.circular(2), // for ripple effect
|
||||
// border: Border.all(
|
||||
// color: Colors.green ,
|
||||
// width: 2,
|
||||
// ),
|
||||
onTap: (role == 'handler')
|
||||
? () {
|
||||
// Print the id when row is clicked
|
||||
print("Clicked ID fd: ${row['id']}");
|
||||
// You can also navigate or perform any action here
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AssignStaffDialog(
|
||||
enquiryPrimaryId: row['id'],
|
||||
regNum: row['reg_no'],
|
||||
userId: 1,
|
||||
onSubmit: (value) {
|
||||
debugPrint("New assignY: $value");
|
||||
if (onRefresh != null) {
|
||||
onRefresh!(); // ✅ call the parent's refresh
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
onTap: null,
|
||||
// onTap: (role == 'handler')
|
||||
// ? () {
|
||||
// // Print the id when row is clicked
|
||||
// print("Clicked ID fd: ${row['id']}");
|
||||
// // You can also navigate or perform any action here
|
||||
//
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (ctx) => AssignStaffDialog(
|
||||
// enquiryPrimaryId: row['id'],
|
||||
// regNum: row['reg_no'],
|
||||
// userId: 1,
|
||||
// onSubmit: (value) {
|
||||
// debugPrint("New assignY: $value");
|
||||
// if (onRefresh != null) {
|
||||
// onRefresh!(); // ✅ call the parent's refresh
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// : null,
|
||||
child: Container(
|
||||
// height: 5200,
|
||||
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@ -8,7 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:nhance_partner/presentation/screens/handler/quotationPopUp.dart';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../../core/routing/routes.dart';
|
||||
@ -36,6 +38,7 @@ import '../../themes/indicators/text_field_theme_inline_editor.dart'
|
||||
hide UpperCaseTextFormatter;
|
||||
import '../../widgets/custom_Stdate_EnDate_Filter.dart';
|
||||
import '../../widgets/custom_action_popup.dart';
|
||||
import '../staff/Enquiry/quotationPopUpAccptReject.dart';
|
||||
import '../staff/assignStaff.dart';
|
||||
|
||||
class EnquiryListHandler extends ConsumerStatefulWidget {
|
||||
@ -2418,56 +2421,74 @@ class EnquiryHandlerState extends ConsumerState<EnquiryListHandler> {
|
||||
// Build both-direction scrollable DataTable
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
double minWidth = constraints.maxWidth < 1300
|
||||
? 1300
|
||||
: constraints.maxWidth;
|
||||
return ScrollConfiguration(
|
||||
behavior: const MaterialScrollBehavior().copyWith(
|
||||
dragDevices: {PointerDeviceKind.mouse, PointerDeviceKind.touch},
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStatePropertyAll(Color(0xFFEDF6F5)),
|
||||
dividerThickness: 0.5,
|
||||
headingRowHeight: 30,
|
||||
columnSpacing: isDesktop ? 20.0 : 16.0,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5,
|
||||
color: Colors.grey.shade200,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStatePropertyAll(Color(0xFFEDF6F5)),
|
||||
dividerThickness: 0.5,
|
||||
headingRowHeight: 30,
|
||||
columnSpacing: isDesktop ? 20.0 : 16.0,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5,
|
||||
color: Colors.grey.shade200,
|
||||
),
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('S.No', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Received Date', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Partner *', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Assigned To *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Broker *', style: _headerStyle)),
|
||||
DataColumn(label: Text('Insurer *', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Insured Name *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text('Vehicle No *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text('Vehicle Type *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Email', style: _headerStyle)),
|
||||
DataColumn(label: Text('Mobile', style: _headerStyle)),
|
||||
DataColumn(label: Text('Documents', style: _headerStyle)),
|
||||
DataColumn(label: Text('Remarks', style: _headerStyle)),
|
||||
DataColumn(label: Text('Action', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Assigned Date', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Premium', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Payment Mode', style: _headerStyle),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text('Policy Number', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Status', style: _headerStyle)),
|
||||
],
|
||||
rows: sortedData.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final item = entry.value;
|
||||
final sno = startIndex + index + 1;
|
||||
return _buildDataRow(item, sno);
|
||||
}).toList(),
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('S.No', style: _headerStyle)),
|
||||
DataColumn(label: Text('Received Date', style: _headerStyle)),
|
||||
DataColumn(label: Text('Partner *', style: _headerStyle)),
|
||||
DataColumn(label: Text('Assigned To *', style: _headerStyle)),
|
||||
DataColumn(label: Text('Broker *', style: _headerStyle)),
|
||||
DataColumn(label: Text('Insurer *', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Insured Name *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Vehicle No *', style: _headerStyle)),
|
||||
DataColumn(
|
||||
label: Text('Vehicle Type *', style: _headerStyle),
|
||||
),
|
||||
DataColumn(label: Text('Email', style: _headerStyle)),
|
||||
DataColumn(label: Text('Mobile', style: _headerStyle)),
|
||||
DataColumn(label: Text('Documents', style: _headerStyle)),
|
||||
DataColumn(label: Text('Remarks', style: _headerStyle)),
|
||||
DataColumn(label: Text('Action', style: _headerStyle)),
|
||||
DataColumn(label: Text('Assigned Date', style: _headerStyle)),
|
||||
DataColumn(label: Text('Premium', style: _headerStyle)),
|
||||
DataColumn(label: Text('Payment Mode', style: _headerStyle)),
|
||||
DataColumn(label: Text('Policy Number', style: _headerStyle)),
|
||||
DataColumn(label: Text('Status', style: _headerStyle)),
|
||||
],
|
||||
rows: sortedData.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final item = entry.value;
|
||||
final sno = startIndex + index + 1;
|
||||
return _buildDataRow(item, sno);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -263,9 +263,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
|
||||
final message = data['data']['message'];
|
||||
|
||||
setState(() {
|
||||
enteredEmailOrMobile = true;
|
||||
});
|
||||
// setState(() {
|
||||
// enteredEmailOrMobile = true;
|
||||
// });
|
||||
|
||||
if (verification == true) {
|
||||
// if (switcherStatus == 1) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3108
lib/presentation/screens/staff/Enquiry/enquiry_inline_old.dart
Normal file
3108
lib/presentation/screens/staff/Enquiry/enquiry_inline_old.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,12 +4,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||||
import '../../../../core/config/env.dart';
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/toastNotification.dart';
|
||||
import '../../layouts/responsive_layout.dart';
|
||||
import '../../providers/manager_provider.dart';
|
||||
import '../../../../../core/config/env.dart';
|
||||
import '../../../../../core/services/api_service.dart';
|
||||
import '../../../../../data/services/auth_service.dart';
|
||||
import '../../../../../data/utils/toastNotification.dart';
|
||||
import '../../../layouts/responsive_layout.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
|
||||
class QuotationPopUpTab extends ConsumerStatefulWidget {
|
||||
String? id;
|
||||
@ -168,7 +168,7 @@ class QuotationTabState extends ConsumerState<QuotationPopUpTab> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'Quotation Details',
|
||||
'Proposal Details',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
|
||||
933
lib/presentation/screens/staff/Enquiry/raise_enq_form.dart
Normal file
933
lib/presentation/screens/staff/Enquiry/raise_enq_form.dart
Normal file
@ -0,0 +1,933 @@
|
||||
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/src/widgets/framework.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../../../data/services/auth_service.dart';
|
||||
import '../../../../data/utils/validators.dart';
|
||||
import '../../../providers/manager_provider.dart';
|
||||
import '../../../providers/userRoleProvider.dart';
|
||||
import '../../../themes/indicators/input_field_decoration.dart';
|
||||
import '../../../themes/indicators/text_field_theme_inline_editor.dart';
|
||||
|
||||
class RaiseEnqForm extends ConsumerStatefulWidget {
|
||||
final Function(Map<String, dynamic> data) onSubmit;
|
||||
const RaiseEnqForm({super.key, required this.onSubmit});
|
||||
@override
|
||||
ConsumerState<RaiseEnqForm> createState() => RaiseEnqFormState();
|
||||
}
|
||||
|
||||
class RaiseEnqFormState extends ConsumerState<RaiseEnqForm> {
|
||||
late ApiService apiService;
|
||||
dynamic userId;
|
||||
dynamic idPrimary;
|
||||
dynamic managerId;
|
||||
dynamic dashboardKey;
|
||||
dynamic SelectedStatus;
|
||||
dynamic SelectedStaffId;
|
||||
dynamic handlerId;
|
||||
String? _token;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
Map<String, String?> fieldErrors = {};
|
||||
|
||||
Map<String, TextEditingController> controllers = {};
|
||||
List<String> tabHeader = [
|
||||
'startDate',
|
||||
'endDate',
|
||||
'name',
|
||||
'email',
|
||||
'mobile',
|
||||
'code',
|
||||
'address',
|
||||
'regNo',
|
||||
'remarks',
|
||||
];
|
||||
|
||||
List<Map<String, dynamic>> getVehicleTypeData = [];
|
||||
List<Map<String, dynamic>> filteredVechicleData = [];
|
||||
|
||||
List<Map<String, dynamic>> getAgentListData = [];
|
||||
List<Map<String, dynamic>> filteredAgentData = [];
|
||||
|
||||
List<Map<String, dynamic>> getInsurersData = [];
|
||||
List<Map<String, dynamic>> filteredInsurersData = [];
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownKeyInsurerEnqAsgn =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyBroker =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownSelectStaffKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
||||
dropDownKeyInsurer = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyAgent =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
List<Map<String, dynamic>> getStaffDetailsDataEnqAsgn = [];
|
||||
List<Map<String, dynamic>> filteredStaffDataEnqAsgn = [];
|
||||
|
||||
String? selectedStaff;
|
||||
String? selectedRegNum;
|
||||
String? selectedStaffName;
|
||||
List<Map<String, dynamic>> getBrokerData = [];
|
||||
List<Map<String, dynamic>> filteredBrokerData = [];
|
||||
String? selectedBroker;
|
||||
|
||||
String? selectedVehicleType;
|
||||
String? selectedInsurer;
|
||||
String? selectedAgent;
|
||||
dynamic roleId;
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
final data = {
|
||||
"agent_id": ((roleId == 'handler') || (roleId == 'manager'))
|
||||
? selectedAgent
|
||||
: userId,
|
||||
"name": controllers["name"]?.text,
|
||||
"mobile": controllers["mobile"]?.text,
|
||||
"email": controllers["email"]?.text,
|
||||
"reg_no": controllers["regNo"]?.text,
|
||||
"vehicle_type_id": selectedVehicleType,
|
||||
"is_data_created_by_handler": roleId == 'handler' ? '1' : '0',
|
||||
"is_data_created_by_manager": roleId == 'manager' ? '1' : '0',
|
||||
// "insurer_id": selectedInsurer,
|
||||
// "rc_file_name": "rc_doc.pdf",
|
||||
// "id_proof_file_name": "id_proof.pdf",
|
||||
// "previous_policy_file_name": "previous_policy.pdf",
|
||||
"remarks": controllers["remarks"]?.text,
|
||||
"assigned_to": (roleId == 'staff') ? userId : selectedStaff,
|
||||
"insurer_id": selectedInsurer,
|
||||
"broker_id": selectedBroker,
|
||||
"manager_id": managerId,
|
||||
"created_by": userId,
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
apiService = ApiService();
|
||||
|
||||
for (String field in tabHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
}
|
||||
_initializeToken();
|
||||
|
||||
Future.microtask(() async {
|
||||
// final id = ref.read(managerIdProvider);
|
||||
roleId = ref.read(userRoleProvider);
|
||||
userId = ref.read(userIdProvider);
|
||||
managerId = ref.read(managerIdProvider);
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
dashboardKey = prefs.getString('dashboardKeyProvider');
|
||||
final dashboardStatus = prefs.getString('dashboardStatusProvider');
|
||||
final dashboardStaffId = prefs.getString('dashboardStaffIdProvider');
|
||||
|
||||
print('handlerIdENQ - $handlerId');
|
||||
print("C72 => r : $roleId | uId: $userId !mID : $managerId ");
|
||||
|
||||
print('dashboardKey - $dashboardKey');
|
||||
print('dashboardStatus - $dashboardStatus');
|
||||
print('dashboardStaffId - $dashboardStaffId');
|
||||
|
||||
if (managerId != null) {
|
||||
print('managerId - $managerId');
|
||||
getAgentList(managerId);
|
||||
}
|
||||
|
||||
final userID = ref.watch(userIdProvider);
|
||||
|
||||
print("managerId - $managerId");
|
||||
if (userID != null) {
|
||||
print('hansles');
|
||||
getStaffDetailsForEnquiryAssignment(userID);
|
||||
}
|
||||
});
|
||||
|
||||
getVehicleType();
|
||||
getInsurers();
|
||||
}
|
||||
|
||||
Future<void> _initializeToken() async {
|
||||
_token = await AuthService.getToken();
|
||||
print("APISERTOKEN - $_token");
|
||||
}
|
||||
|
||||
void resetFormFields() {
|
||||
// 1. Reset all text controllers
|
||||
controllers.forEach((key, controller) {
|
||||
controller.clear();
|
||||
});
|
||||
|
||||
// 2. Reset dropdown selections
|
||||
setState(() {
|
||||
selectedAgent = null;
|
||||
selectedInsurer = null;
|
||||
// Add any other dropdowns
|
||||
});
|
||||
|
||||
// 3. Reset form validation
|
||||
_formKey.currentState?.reset();
|
||||
}
|
||||
|
||||
Future<void> getVehicleType() async {
|
||||
print('getClaimList called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('vehicleType');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getVehicleTypeData - ${response['data']}');
|
||||
setState(() {
|
||||
getVehicleTypeData = List<Map<String, dynamic>>.from(
|
||||
response['data'],
|
||||
);
|
||||
print('API Data - $getVehicleTypeData');
|
||||
|
||||
filteredVechicleData = List.from(getVehicleTypeData);
|
||||
print('originalData - $filteredVechicleData');
|
||||
});
|
||||
} else {
|
||||
getVehicleTypeData = [];
|
||||
filteredVechicleData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getInsurers() async {
|
||||
print('Insurers called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('Insurers');
|
||||
|
||||
if (response['status'] == 200) {
|
||||
print('getInsurers - ${response['data']}');
|
||||
setState(() {
|
||||
getInsurersData = List<Map<String, dynamic>>.from(response['data']);
|
||||
print('API Data - $getInsurersData');
|
||||
|
||||
filteredInsurersData = List.from(getInsurersData);
|
||||
print('originalData - $filteredInsurersData');
|
||||
});
|
||||
} else {
|
||||
getInsurersData = [];
|
||||
filteredInsurersData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getAgentList(id) async {
|
||||
print('getAgentListData called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchAgentNameDropDown(id);
|
||||
print('getAgentListData called response');
|
||||
print('get Agent- ${response['data']}');
|
||||
if (response['status'] == 'success') {
|
||||
print('get Agent- ${response['data']}');
|
||||
setState(() {
|
||||
getAgentListData = List<Map<String, dynamic>>.from(response['data']);
|
||||
print('API Data - $getAgentListData');
|
||||
|
||||
filteredAgentData = List.from(getAgentListData);
|
||||
print('originalAgentData - $filteredAgentData');
|
||||
});
|
||||
} else {
|
||||
getAgentListData = [];
|
||||
filteredAgentData = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getStaffDetailsForEnquiryAssignment(int id) async {
|
||||
print('getStaffDetailsForEnquiryAssignment called By handler');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// final response = await apiService.fetchStaffUserList(id, role);
|
||||
final response = await apiService.fetchStaffListForEnquiryAssignDropDown(
|
||||
id,
|
||||
roleId,
|
||||
);
|
||||
|
||||
if (response['status'] == 'success') {
|
||||
print('getStaffDetailsForEnquiryAssignment - ${response['data']}');
|
||||
setState(() {
|
||||
getStaffDetailsDataEnqAsgn = List<Map<String, dynamic>>.from(
|
||||
response['data'],
|
||||
);
|
||||
print('API Data - $getStaffDetailsDataEnqAsgn');
|
||||
|
||||
filteredStaffDataEnqAsgn = List.from(getStaffDetailsDataEnqAsgn);
|
||||
print('originalData - $filteredStaffDataEnqAsgn');
|
||||
});
|
||||
} else {
|
||||
getStaffDetailsDataEnqAsgn = [];
|
||||
filteredStaffDataEnqAsgn = [];
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return Container(
|
||||
// color: Color(0xFFFCFCFC),
|
||||
// margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 25),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.08),
|
||||
blurRadius: 4,
|
||||
spreadRadius: 1,
|
||||
offset: const Offset(0, 2), // soft drop shadow
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
buildAgentName(context, fromHeader: true),
|
||||
if (roleId != 'staff') ...[buildSelectStaffMem(context)],
|
||||
buildInsurer(context, fromHeader: true),
|
||||
buildInsuredName(context, fromHeader: true),
|
||||
|
||||
buildVehicleNumber(context),
|
||||
|
||||
buildSave(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildAgentName(BuildContext context, {fromHeader = true}) {
|
||||
Map<String, dynamic>? selectedAgntName = filteredAgentData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedAgent,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Partner', style: _subLabelTimeStyle),
|
||||
|
||||
SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
// width: fromHeader
|
||||
// ? MediaQuery.of(context).size.width * 0.11
|
||||
// : MediaQuery.of(context).size.width * 0.08,
|
||||
height: 30,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyAgent,
|
||||
selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredAgentData;
|
||||
},
|
||||
|
||||
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;
|
||||
},
|
||||
suffixProps: DropdownSuffixProps(
|
||||
// make sure the dropdown button is visible
|
||||
dropdownButtonProps: DropdownButtonProps(
|
||||
isVisible: true,
|
||||
padding: EdgeInsets.zero, // remove default padding
|
||||
constraints: const BoxConstraints(
|
||||
// shrink icon tap area
|
||||
minWidth: 12,
|
||||
minHeight: 12,
|
||||
),
|
||||
iconSize: 15, // smaller icon
|
||||
// icon: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem != null
|
||||
? selectedItem['name'].toString()
|
||||
: "", // ✅ FIXED
|
||||
style: GoogleFonts.poppins(fontSize: 11, color: Colors.black),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Partner Name",
|
||||
).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(
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Partner Name",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
color: Colors.grey,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
), // 👈 Normal border
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.white,
|
||||
width: 1.5,
|
||||
), // 👈 Focused border
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// constraints: BoxConstraints(),
|
||||
itemBuilder: (context, item, isDisabled, isSelected) {
|
||||
return Container(
|
||||
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['name'].toString(),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item['agent_code'].toString(),
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
print("Selected Partner : ${val['name']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedAgent = val['id'];
|
||||
// controllers['agentId']?.text = val['agent_code'];
|
||||
// agentId = agent['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInsuredName(BuildContext context, {fromHeader = true}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Insured', style: _subLabelTimeStyle),
|
||||
|
||||
SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
|
||||
child: ThemedFormInlineField(
|
||||
controller: controllers['name']!,
|
||||
hintText: 'Insured Name',
|
||||
// errorText: fieldErrors['name'],
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
isdense: true,
|
||||
validator: (value) => Validators.requiredField(value, "name"),
|
||||
widthNone: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInsurer(BuildContext context, {fromHeader = true}) {
|
||||
Map<String, dynamic>? selectedInsurerd = filteredInsurersData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedInsurer,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Insurer', style: _subLabelTimeStyle),
|
||||
|
||||
SizedBox(height: 5),
|
||||
Container(
|
||||
height: 30,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyInsurerEnqAsgn,
|
||||
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;
|
||||
},
|
||||
suffixProps: DropdownSuffixProps(
|
||||
// make sure the dropdown button is visible
|
||||
dropdownButtonProps: DropdownButtonProps(
|
||||
isVisible: true,
|
||||
padding: EdgeInsets.zero, // remove default padding
|
||||
constraints: const BoxConstraints(
|
||||
// shrink icon tap area
|
||||
minWidth: 12,
|
||||
minHeight: 12,
|
||||
),
|
||||
iconSize: 15, // smaller icon
|
||||
// icon: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem != null ? selectedItem['name'].toString() : "",
|
||||
style: GoogleFonts.poppins(fontSize: 11, color: Colors.black),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Insurer",
|
||||
).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(
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Insurer...",
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
// 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 buildVehicleNumber(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Vehicle Number', style: _subLabelTimeStyle),
|
||||
|
||||
SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
child: ThemedFormInlineField(
|
||||
controller: controllers['regNo']!,
|
||||
hintText: 'Vehicle Number',
|
||||
borderColor: Color(0xFFE2E8F0),
|
||||
isdense: true,
|
||||
inputFormatters: [
|
||||
UpperCaseTextFormatter(), // 👈 custom formatter for uppercase
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')),
|
||||
],
|
||||
validator: (value) => Validators.requiredVechileNum(value, "regNo"),
|
||||
widthNone: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSelectStaffMem(ctx) {
|
||||
Map<String, dynamic>? selectedVehicle;
|
||||
try {
|
||||
selectedVehicle = filteredStaffDataEnqAsgn.firstWhere(
|
||||
(item) => item['id'].toString() == selectedStaff,
|
||||
);
|
||||
} catch (e) {
|
||||
selectedVehicle = null; // ✅ fallback
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Assigned To', style: _subLabelTimeStyle),
|
||||
|
||||
SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownSelectStaffKey,
|
||||
|
||||
// selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
|
||||
selectedItem: selectedVehicle,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredStaffDataEnqAsgn;
|
||||
},
|
||||
|
||||
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;
|
||||
},
|
||||
suffixProps: DropdownSuffixProps(
|
||||
// make sure the dropdown button is visible
|
||||
dropdownButtonProps: DropdownButtonProps(
|
||||
isVisible: true,
|
||||
padding: EdgeInsets.zero, // remove default padding
|
||||
constraints: const BoxConstraints(
|
||||
// shrink icon tap area
|
||||
minWidth: 12,
|
||||
minHeight: 12,
|
||||
),
|
||||
iconSize: 15, // smaller icon
|
||||
// icon: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
),
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration:
|
||||
AppInputDecorations.dropdownDecoration(
|
||||
label: "Select Staff Member",
|
||||
).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,
|
||||
),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem != null ? selectedItem['name'].toString() : "",
|
||||
style: GoogleFonts.poppins(fontSize: 11, color: Colors.black),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
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 Staff ...",
|
||||
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
|
||||
),
|
||||
),
|
||||
),
|
||||
// constraints: BoxConstraints(),
|
||||
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 Staff : ${val['name']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedStaffName = val['name'];
|
||||
selectedStaff = val['id'];
|
||||
// controllers['agentId']?.text = val['agent_code'];
|
||||
// agentId = agent['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSave(context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
final dataSet = dataDetails();
|
||||
|
||||
widget.onSubmit(dataSet);
|
||||
resetFields();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2E7D6E),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
child: Text('Create', style: GoogleFonts.inter(color: Colors.white)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void resetFields() {
|
||||
print('Field ReSET');
|
||||
// 1. Reset all text controllers
|
||||
controllers.forEach((key, controller) {
|
||||
controller.clear();
|
||||
});
|
||||
|
||||
// 2. Reset dropdown selections
|
||||
setState(() {
|
||||
selectedAgent = null;
|
||||
selectedInsurer = null;
|
||||
selectedStaff = null;
|
||||
selectedStaffName = null;
|
||||
selectedVehicleType = null;
|
||||
selectedRegNum = null;
|
||||
selectedBroker = null;
|
||||
});
|
||||
|
||||
// 3. Reset dropdown UI (important for DropdownSearch)
|
||||
dropDownKeyAgent.currentState?.clear();
|
||||
dropDownKeyInsurerEnqAsgn.currentState?.clear();
|
||||
dropDownSelectStaffKey.currentState?.clear();
|
||||
dropDownKeyInsurer.currentState?.clear();
|
||||
dropDownKey.currentState?.clear();
|
||||
dropDownKeyBroker.currentState?.clear();
|
||||
|
||||
// 4. Reset validators
|
||||
_formKey.currentState?.reset();
|
||||
}
|
||||
|
||||
final _subLabelTimeStyle = GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
);
|
||||
}
|
||||
@ -31,6 +31,7 @@ import '../../../../providers/quotation_staff_proivder.dart';
|
||||
import '../../../../themes/indicators/custom_loader.dart';
|
||||
import '../../../../themes/indicators/customizd_file_upload.dart';
|
||||
import '../../../../themes/indicators/date_field_theme.dart';
|
||||
import '../../../../themes/indicators/input_field_decoration.dart';
|
||||
import '../../../../themes/indicators/text_field_theme.dart';
|
||||
|
||||
class PolicyStaffTab extends ConsumerStatefulWidget {
|
||||
@ -52,6 +53,14 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyBroker =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
List<Map<String, dynamic>> getBrokerData = [];
|
||||
List<Map<String, dynamic>> filteredBrokerData = [];
|
||||
String? selectedBroker;
|
||||
String? selectedBrokerName;
|
||||
|
||||
List<String> tabHeader = [
|
||||
'name',
|
||||
'email',
|
||||
@ -143,7 +152,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// "premium_amount": controllers["policyPremiumAmount"]?.text,
|
||||
"policy_number": controllers["policyNumber"]?.text,
|
||||
"payment_mode": controllers["policyPaymentMode"]?.text,
|
||||
|
||||
"broker_id": selectedBroker,
|
||||
"tp": controllers["tp"]?.text,
|
||||
"od": controllers["od"]?.text,
|
||||
"pa": controllers["pa"]?.text,
|
||||
@ -214,7 +223,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
controllers["cgst"]?.text = '0';
|
||||
controllers["sgst"]?.text = '0';
|
||||
controllers["igst"]?.text = '0';
|
||||
|
||||
getBroker();
|
||||
if (enqQuotation != null) {
|
||||
_loadData(enqQuotation);
|
||||
}
|
||||
@ -321,6 +330,37 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
}
|
||||
}
|
||||
|
||||
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 updateEnquiryData(enquiryData) {
|
||||
setState(() {
|
||||
selectedEnquiryId = enquiryData['id'];
|
||||
@ -394,6 +434,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
controllers["premiumTOT"]?.text =
|
||||
policyData['premium_amount']?.toString() ?? '';
|
||||
|
||||
selectedBroker = policyData['broker_id'] ?? '';
|
||||
// RC FILE
|
||||
String? rcPath = policyData["policy_pdf_file_name"];
|
||||
if (rcPath != null && rcPath.isNotEmpty) {
|
||||
@ -490,13 +531,14 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
|
||||
final bool isUpdating = choosedPolcyId != null;
|
||||
|
||||
if (!isUpdating && enqBrokerName == 'Nhance') {
|
||||
if (!isUpdating && selectedBrokerName == 'Nhance') {
|
||||
// if (!isUpdating && enqBrokerName == 'Nhance') {
|
||||
ToastHelper.showErrorToast(context, "Policy PDF is required");
|
||||
return;
|
||||
}
|
||||
// (enqBrokerName != 'Nhance')
|
||||
// File validations
|
||||
if ((enqBrokerName != 'Nhance') &&
|
||||
if ((selectedBrokerName != 'Nhance') &&
|
||||
(docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) &&
|
||||
(docUploadedPDFFileUrlFromApi == null ||
|
||||
docUploadedPDFFileUrlFromApi!.isEmpty)) {
|
||||
@ -1022,54 +1064,62 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildUploadPolicyPdf(context),
|
||||
SizedBox(width: 20),
|
||||
Paymentmode(context),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
policynumber(context),
|
||||
SizedBox(width: 20),
|
||||
policyissuedate(context),
|
||||
SizedBox(width: 20),
|
||||
policystartdate(context),
|
||||
SizedBox(width: 20),
|
||||
policyenddate(context),
|
||||
buildBroker(context),
|
||||
if (selectedBrokerName != null) ...[
|
||||
SizedBox(width: 20),
|
||||
buildUploadPolicyPdf(context),
|
||||
SizedBox(width: 20),
|
||||
|
||||
Paymentmode(context),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
if (selectedBrokerName != null) ...[
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
policynumber(context),
|
||||
SizedBox(width: 20),
|
||||
policyissuedate(context),
|
||||
SizedBox(width: 20),
|
||||
policystartdate(context),
|
||||
SizedBox(width: 20),
|
||||
policyenddate(context),
|
||||
],
|
||||
),
|
||||
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildThirdParty(context),
|
||||
buildOwnDamage(context),
|
||||
buildPersonalAccident(context),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
SizedBox(width: 20),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildCGST(context),
|
||||
buildSGST(context),
|
||||
buildIGST(context),
|
||||
buildPremiumTotal(context),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildThirdParty(context),
|
||||
buildOwnDamage(context),
|
||||
buildPersonalAccident(context),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(width: 20),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildCGST(context),
|
||||
buildSGST(context),
|
||||
buildIGST(context),
|
||||
buildPremiumTotal(context),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -1348,6 +1398,154 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBroker(BuildContext context) {
|
||||
Map<String, dynamic>? selectedBrokers = filteredBrokerData.firstWhere(
|
||||
(item) => item['id'].toString() == selectedBroker,
|
||||
orElse: () => {},
|
||||
);
|
||||
return Row(
|
||||
children: [
|
||||
Text('Broker *', style: _textStyle),
|
||||
SizedBox(width: 15),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: MediaQuery.of(context).size.width * 0.1,
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key: dropDownKeyBroker,
|
||||
selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null,
|
||||
items: (filter, infiniteScrollProps) {
|
||||
return filteredBrokerData;
|
||||
},
|
||||
|
||||
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;
|
||||
},
|
||||
suffixProps: DropdownSuffixProps(
|
||||
// make sure the dropdown button is visible
|
||||
dropdownButtonProps: DropdownButtonProps(
|
||||
isVisible: true,
|
||||
padding: EdgeInsets.zero, // remove default padding
|
||||
constraints: const BoxConstraints(
|
||||
// shrink icon tap area
|
||||
minWidth: 12,
|
||||
minHeight: 12,
|
||||
),
|
||||
iconSize: 15, // smaller icon
|
||||
// icon: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem != null ? selectedItem['name'].toString() : "",
|
||||
style: GoogleFonts.poppins(fontSize: 11, color: Colors.black),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
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: Colors.black,
|
||||
width: 0.1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.black,
|
||||
width: 0.1,
|
||||
),
|
||||
),
|
||||
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(
|
||||
decoration: InputDecoration(
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
// constraints: BoxConstraints(),
|
||||
),
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
print("Selected Broker : ${val['name']}");
|
||||
print("Id: ${val['id']}");
|
||||
selectedBroker = val['id'];
|
||||
setState(() {
|
||||
selectedBrokerName = val['name'];
|
||||
print('selectedBrokerName - $selectedBrokerName');
|
||||
});
|
||||
// controllers['agentId']?.text = val['agent_code'];
|
||||
// agentId = agent['id'];
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget Paymentmode(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@ -1365,7 +1563,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
Validators.requiredField(value, "policyPaymentMode"),
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.15,
|
||||
: MediaQuery.of(context).size.width * 0.07,
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -1535,7 +1733,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.15,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: Text('Upload Policy PDF * ', style: _textStyle),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
@ -1577,7 +1775,7 @@ class PolicyStaffTabState extends ConsumerState<PolicyStaffTab> {
|
||||
docPDFUploadedFile = file;
|
||||
});
|
||||
|
||||
if (enqBrokerName == 'Nhance') {
|
||||
if (selectedBrokerName == 'Nhance') {
|
||||
handleUploadPolicy();
|
||||
}
|
||||
},
|
||||
|
||||
@ -229,8 +229,8 @@ class TabEnquiryStaffListState extends ConsumerState<TabEnquiryStaffList> {
|
||||
children: [
|
||||
insured_name(context),
|
||||
vehcile_num(context),
|
||||
vehcile_Type(context),
|
||||
broker(context),
|
||||
// vehcile_Type(context),
|
||||
// broker(context),
|
||||
SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
class DropdownOption {
|
||||
final String value;
|
||||
final String label;
|
||||
|
||||
DropdownOption({required this.value, required this.label});
|
||||
|
||||
factory DropdownOption.fromJson(Map<String, dynamic> json) {
|
||||
return DropdownOption(
|
||||
value: json['value'] ?? '',
|
||||
label: json['label'] ?? json['value'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
class EnquiryModel {
|
||||
final String? id;
|
||||
final DateTime receivedDate;
|
||||
final String partner;
|
||||
final String assignedTo;
|
||||
final String broker;
|
||||
final String insurer;
|
||||
final String insuredName;
|
||||
final String vehicleNo;
|
||||
final String vehicleType;
|
||||
final String email;
|
||||
final String mobile;
|
||||
final String remarks;
|
||||
final String status;
|
||||
|
||||
EnquiryModel({
|
||||
this.id,
|
||||
required this.receivedDate,
|
||||
required this.partner,
|
||||
required this.assignedTo,
|
||||
required this.broker,
|
||||
required this.insurer,
|
||||
required this.insuredName,
|
||||
required this.vehicleNo,
|
||||
required this.vehicleType,
|
||||
required this.email,
|
||||
required this.mobile,
|
||||
required this.remarks,
|
||||
required this.status,
|
||||
});
|
||||
|
||||
factory EnquiryModel.fromJson(Map<String, dynamic> json) {
|
||||
return EnquiryModel(
|
||||
id: json['id']?.toString(),
|
||||
receivedDate: DateTime.parse(json['received_date']),
|
||||
partner: json['partner'] ?? '',
|
||||
assignedTo: json['assigned_to'] ?? '',
|
||||
broker: json['broker'] ?? '',
|
||||
insurer: json['insurer'] ?? '',
|
||||
insuredName: json['insured_name'] ?? '',
|
||||
vehicleNo: json['vehicle_no'] ?? '',
|
||||
vehicleType: json['vehicle_type'] ?? '',
|
||||
email: json['email'] ?? '',
|
||||
mobile: json['mobile'] ?? '',
|
||||
remarks: json['remarks'] ?? '',
|
||||
status: json['status'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'received_date': receivedDate.toIso8601String(),
|
||||
'partner': partner,
|
||||
'assigned_to': assignedTo,
|
||||
'broker': broker,
|
||||
'insurer': insurer,
|
||||
'insured_name': insuredName,
|
||||
'vehicle_no': vehicleNo,
|
||||
'vehicle_type': vehicleType,
|
||||
'email': email,
|
||||
'mobile': mobile,
|
||||
'remarks': remarks,
|
||||
'status': status,
|
||||
};
|
||||
}
|
||||
|
||||
EnquiryModel copyWith({
|
||||
String? id,
|
||||
DateTime? receivedDate,
|
||||
String? partner,
|
||||
String? assignedTo,
|
||||
String? broker,
|
||||
String? insurer,
|
||||
String? insuredName,
|
||||
String? vehicleNo,
|
||||
String? vehicleType,
|
||||
String? email,
|
||||
String? mobile,
|
||||
String? remarks,
|
||||
String? status,
|
||||
}) {
|
||||
return EnquiryModel(
|
||||
id: id ?? this.id,
|
||||
receivedDate: receivedDate ?? this.receivedDate,
|
||||
partner: partner ?? this.partner,
|
||||
assignedTo: assignedTo ?? this.assignedTo,
|
||||
broker: broker ?? this.broker,
|
||||
insurer: insurer ?? this.insurer,
|
||||
insuredName: insuredName ?? this.insuredName,
|
||||
vehicleNo: vehicleNo ?? this.vehicleNo,
|
||||
vehicleType: vehicleType ?? this.vehicleType,
|
||||
email: email ?? this.email,
|
||||
mobile: mobile ?? this.mobile,
|
||||
remarks: remarks ?? this.remarks,
|
||||
status: status ?? this.status,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../model/dropdown_option.dart';
|
||||
import '../model/enquiry_model.dart';
|
||||
import '../widgets/searchable_dropdown.dart';
|
||||
|
||||
class EnquiryForm extends StatefulWidget {
|
||||
final Function(EnquiryModel) onSubmit;
|
||||
final List<DropdownOption> partners;
|
||||
final List<DropdownOption> insurers;
|
||||
final List<DropdownOption> assignees;
|
||||
|
||||
const EnquiryForm({
|
||||
super.key,
|
||||
required this.onSubmit,
|
||||
required this.partners,
|
||||
required this.insurers,
|
||||
required this.assignees,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EnquiryForm> createState() => _EnquiryFormState();
|
||||
}
|
||||
|
||||
class _EnquiryFormState extends State<EnquiryForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? _selectedPartner;
|
||||
String? _selectedInsurer;
|
||||
String? _selectedAssignee;
|
||||
final _insuredNameController = TextEditingController();
|
||||
final _regNoController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_insuredNameController.dispose();
|
||||
_regNoController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _submit() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final enquiry = EnquiryModel(
|
||||
receivedDate: DateTime.now(),
|
||||
partner: _selectedPartner!,
|
||||
assignedTo: _selectedAssignee!,
|
||||
broker: 'Nhance',
|
||||
insurer: _selectedInsurer!,
|
||||
insuredName: _insuredNameController.text,
|
||||
vehicleNo: _regNoController.text,
|
||||
vehicleType: '',
|
||||
email: '',
|
||||
mobile: '',
|
||||
remarks: '',
|
||||
status: 'awaiting',
|
||||
);
|
||||
|
||||
widget.onSubmit(enquiry);
|
||||
|
||||
// Reset form
|
||||
_formKey.currentState!.reset();
|
||||
_insuredNameController.clear();
|
||||
_regNoController.clear();
|
||||
setState(() {
|
||||
_selectedPartner = null;
|
||||
_selectedInsurer = null;
|
||||
_selectedAssignee = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
alignment: WrapAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: SearchableDropdown(
|
||||
label: 'Partner',
|
||||
value: _selectedPartner,
|
||||
items: widget.partners,
|
||||
onChanged: (value) => setState(() => _selectedPartner = value),
|
||||
validator: (value) => value == null ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: SearchableDropdown(
|
||||
label: 'Insurer',
|
||||
value: _selectedInsurer,
|
||||
items: widget.insurers,
|
||||
onChanged: (value) => setState(() => _selectedInsurer = value),
|
||||
validator: (value) => value == null ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: TextFormField(
|
||||
controller: _insuredNameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Insured Name',
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
style: const TextStyle(fontSize: 13),
|
||||
validator: (value) =>
|
||||
value?.isEmpty ?? true ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: TextFormField(
|
||||
controller: _regNoController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Reg No',
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
style: const TextStyle(fontSize: 13),
|
||||
validator: (value) =>
|
||||
value?.isEmpty ?? true ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: SearchableDropdown(
|
||||
label: 'Assigned To',
|
||||
value: _selectedAssignee,
|
||||
items: widget.assignees,
|
||||
onChanged: (value) => setState(() => _selectedAssignee = value),
|
||||
validator: (value) => value == null ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _submit,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF4a90e2),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 16,
|
||||
),
|
||||
),
|
||||
child: const Text('Create', style: TextStyle(fontSize: 13)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../model/dropdown_option.dart';
|
||||
import '../model/enquiry_model.dart';
|
||||
import 'enquiry_row.dart';
|
||||
|
||||
class EnquiryGroup extends StatefulWidget {
|
||||
final String title;
|
||||
final String icon;
|
||||
final String groupKey;
|
||||
final List<EnquiryModel> enquiries;
|
||||
final Function(EnquiryModel) onUpdate;
|
||||
final List<DropdownOption> partners;
|
||||
final List<DropdownOption> insurers;
|
||||
final List<DropdownOption> assignees;
|
||||
|
||||
const EnquiryGroup({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
required this.groupKey,
|
||||
required this.enquiries,
|
||||
required this.onUpdate,
|
||||
required this.partners,
|
||||
required this.insurers,
|
||||
required this.assignees,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EnquiryGroup> createState() => _EnquiryGroupState();
|
||||
}
|
||||
|
||||
class _EnquiryGroupState extends State<EnquiryGroup> {
|
||||
bool _isExpanded = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => setState(() => _isExpanded = !_isExpanded),
|
||||
child: Container(
|
||||
color: const Color(0xFFf8f9fa),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
_isExpanded ? Icons.arrow_drop_down : Icons.arrow_right,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${widget.icon} ${widget.title}',
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFe3f2fd),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'${widget.enquiries.length} Records',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Color(0xFF1976d2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_isExpanded)
|
||||
...widget.enquiries.isEmpty
|
||||
? [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(30),
|
||||
child: Text(
|
||||
'No records found',
|
||||
style: TextStyle(
|
||||
color: Colors.grey,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
: widget.enquiries.map((enquiry) {
|
||||
return EnquiryRow(
|
||||
enquiry: enquiry,
|
||||
onUpdate: widget.onUpdate,
|
||||
partners: widget.partners,
|
||||
insurers: widget.insurers,
|
||||
assignees: widget.assignees,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,255 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../model/dropdown_option.dart';
|
||||
import '../model/enquiry_model.dart';
|
||||
import '../widgets/searchable_dropdown.dart';
|
||||
|
||||
class EnquiryRow extends StatefulWidget {
|
||||
final EnquiryModel enquiry;
|
||||
final Function(EnquiryModel) onUpdate;
|
||||
final List<DropdownOption> partners;
|
||||
final List<DropdownOption> insurers;
|
||||
final List<DropdownOption> assignees;
|
||||
|
||||
const EnquiryRow({
|
||||
super.key,
|
||||
required this.enquiry,
|
||||
required this.onUpdate,
|
||||
required this.partners,
|
||||
required this.insurers,
|
||||
required this.assignees,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EnquiryRow> createState() => _EnquiryRowState();
|
||||
}
|
||||
|
||||
class _EnquiryRowState extends State<EnquiryRow> {
|
||||
bool _isEditMode = false;
|
||||
late EnquiryModel _editedEnquiry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_editedEnquiry = widget.enquiry;
|
||||
}
|
||||
|
||||
void _toggleEditMode() {
|
||||
setState(() {
|
||||
if (_isEditMode) {
|
||||
widget.onUpdate(_editedEnquiry);
|
||||
}
|
||||
_isEditMode = !_isEditMode;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildCell(String value, {Function(String)? onChanged}) {
|
||||
if (_isEditMode && onChanged != null) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFe3f2fd),
|
||||
border: Border.all(color: const Color(0xFF4a90e2), width: 1),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: TextFormField(
|
||||
initialValue: value,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.all(4),
|
||||
),
|
||||
onChanged: onChanged,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Text(value, style: const TextStyle(fontSize: 12));
|
||||
}
|
||||
|
||||
Widget _buildDropdownCell(
|
||||
String value,
|
||||
List<DropdownOption> options,
|
||||
Function(String?) onChanged,
|
||||
) {
|
||||
if (_isEditMode) {
|
||||
return Container(
|
||||
constraints: const BoxConstraints(minHeight: 36),
|
||||
child: SearchableDropdown(
|
||||
label: '',
|
||||
value: value,
|
||||
items: options,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Text(value, style: const TextStyle(fontSize: 12));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dateFormat = DateFormat('dd-MM-yyyy\nhh:mm a');
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _isEditMode ? const Color(0xFFf8f9fa) : Colors.white,
|
||||
border: const Border(bottom: BorderSide(color: Color(0xFFf0f0f0))),
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Text(
|
||||
dateFormat.format(widget.enquiry.receivedDate),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildDropdownCell(
|
||||
_editedEnquiry.partner,
|
||||
widget.partners,
|
||||
(v) => setState(
|
||||
() => _editedEnquiry = _editedEnquiry.copyWith(partner: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildDropdownCell(
|
||||
_editedEnquiry.assignedTo,
|
||||
widget.assignees,
|
||||
(v) => setState(
|
||||
() =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(assignedTo: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(_editedEnquiry.broker),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildDropdownCell(
|
||||
_editedEnquiry.insurer,
|
||||
widget.insurers,
|
||||
(v) => setState(
|
||||
() => _editedEnquiry = _editedEnquiry.copyWith(insurer: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.insuredName,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(insuredName: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.vehicleNo,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(vehicleNo: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.vehicleType,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(vehicleType: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.email,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(email: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.mobile,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(mobile: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildCell(
|
||||
_editedEnquiry.remarks,
|
||||
onChanged: (v) =>
|
||||
_editedEnquiry = _editedEnquiry.copyWith(remarks: v),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Text(_isEditMode ? '💾' : '✏️'),
|
||||
onPressed: _toggleEditMode,
|
||||
tooltip: _isEditMode ? 'Save' : 'Edit',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
icon: const Text('📄'),
|
||||
onPressed: () {},
|
||||
tooltip: 'Documents',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,413 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../core/services/api_service.dart';
|
||||
import '../../../../layouts/appheader.dart';
|
||||
import '../model/dropdown_option.dart';
|
||||
import '../model/enquiry_model.dart';
|
||||
import '../screens/enquiry_form.dart';
|
||||
import '../screens/enquiry_group.dart';
|
||||
|
||||
class EnquiryScreen extends StatefulWidget {
|
||||
const EnquiryScreen({super.key});
|
||||
|
||||
@override
|
||||
State<EnquiryScreen> createState() => _EnquiryScreenState();
|
||||
}
|
||||
|
||||
class _EnquiryScreenState extends State<EnquiryScreen> {
|
||||
List<EnquiryModel> _enquiries = [];
|
||||
List<DropdownOption> _partners = [];
|
||||
List<DropdownOption> _insurers = [];
|
||||
List<DropdownOption> _assignees = [];
|
||||
bool _isLoading = true;
|
||||
String _searchQuery = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
ApiService.fetchEnquiries(),
|
||||
ApiService.fetchDropdownOptions('partners'),
|
||||
ApiService.fetchDropdownOptions('insurers'),
|
||||
ApiService.fetchDropdownOptions('assignees'),
|
||||
]);
|
||||
|
||||
setState(() {
|
||||
_enquiries = results[0] as List<EnquiryModel>;
|
||||
_partners = results[1] as List<DropdownOption>;
|
||||
_insurers = results[2] as List<DropdownOption>;
|
||||
_assignees = results[3] as List<DropdownOption>;
|
||||
_isLoading = false;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _isLoading = false);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error loading data: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _createEnquiry(EnquiryModel enquiry) async {
|
||||
try {
|
||||
final created = await ApiService.createEnquiry(enquiry);
|
||||
setState(() {
|
||||
_enquiries.insert(0, created);
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Enquiry created successfully')),
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error creating enquiry: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateEnquiry(EnquiryModel enquiry) async {
|
||||
try {
|
||||
final updated = await ApiService.updateEnquiry(enquiry.id!, enquiry);
|
||||
setState(() {
|
||||
final index = _enquiries.indexWhere((e) => e.id == enquiry.id);
|
||||
if (index != -1) {
|
||||
_enquiries[index] = updated;
|
||||
}
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Enquiry updated successfully')),
|
||||
);
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Error updating enquiry: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<EnquiryModel>> _groupEnquiries() {
|
||||
final filtered = _enquiries.where((e) {
|
||||
if (_searchQuery.isEmpty) return true;
|
||||
final query = _searchQuery.toLowerCase();
|
||||
return e.partner.toLowerCase().contains(query) ||
|
||||
e.insuredName.toLowerCase().contains(query) ||
|
||||
e.vehicleNo.toLowerCase().contains(query) ||
|
||||
e.email.toLowerCase().contains(query) ||
|
||||
e.mobile.contains(query);
|
||||
}).toList();
|
||||
|
||||
return {
|
||||
'awaiting': filtered.where((e) => e.status == 'awaiting').toList(),
|
||||
'created': filtered.where((e) => e.status == 'created').toList(),
|
||||
'accepted': filtered.where((e) => e.status == 'accepted').toList(),
|
||||
'rejected': filtered.where((e) => e.status == 'rejected').toList(),
|
||||
'policy': filtered.where((e) => e.status == 'policy').toList(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final groupedEnquiries = _groupEnquiries();
|
||||
|
||||
return Scaffold(
|
||||
appBar: const AppHeader(),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
children: [
|
||||
EnquiryForm(
|
||||
onSubmit: _createEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'📋 Enquiries',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 250,
|
||||
child: TextField(
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Search...',
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() => _searchQuery = value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Table Header
|
||||
Container(
|
||||
color: const Color(0xFFf8f9fa),
|
||||
child: const Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Received Date',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Partner',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Assigned To',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Broker',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Insurer',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Insured Name',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Vehicle No',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Vehicle Type',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Email',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Mobile',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Remarks',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
'Actions',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF555555),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Grouped Enquiries
|
||||
EnquiryGroup(
|
||||
title: 'Awaiting Proposal',
|
||||
icon: '🕐',
|
||||
groupKey: 'awaiting',
|
||||
enquiries: groupedEnquiries['awaiting']!,
|
||||
onUpdate: _updateEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
EnquiryGroup(
|
||||
title: 'Proposal Created',
|
||||
icon: '📝',
|
||||
groupKey: 'created',
|
||||
enquiries: groupedEnquiries['created']!,
|
||||
onUpdate: _updateEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
EnquiryGroup(
|
||||
title: 'Proposal Accepted',
|
||||
icon: '✅',
|
||||
groupKey: 'accepted',
|
||||
enquiries: groupedEnquiries['accepted']!,
|
||||
onUpdate: _updateEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
EnquiryGroup(
|
||||
title: 'Proposal Rejected',
|
||||
icon: '❌',
|
||||
groupKey: 'rejected',
|
||||
enquiries: groupedEnquiries['rejected']!,
|
||||
onUpdate: _updateEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
EnquiryGroup(
|
||||
title: 'Policy Created',
|
||||
icon: '📋',
|
||||
groupKey: 'policy',
|
||||
enquiries: groupedEnquiries['policy']!,
|
||||
onUpdate: _updateEnquiry,
|
||||
partners: _partners,
|
||||
insurers: _insurers,
|
||||
assignees: _assignees,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
473
lib/presentation/screens/staff/enquiry_single_page/widgets/s
Normal file
473
lib/presentation/screens/staff/enquiry_single_page/widgets/s
Normal file
@ -0,0 +1,473 @@
|
||||
// 5. Replace your existing _buildDataTable method with this grouped version
|
||||
Widget _buildDataTable(BuildContext context) {
|
||||
if (filteredData.isEmpty) {
|
||||
return const SizedBox(
|
||||
height: 50,
|
||||
child: Center(child: Text('No available data')),
|
||||
);
|
||||
}
|
||||
|
||||
final isDesktop = !ResponsiveLayout.isMobile(context);
|
||||
final groupedData = _groupedEnquiries;
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
double minWidth = constraints.maxWidth < 1300 ? 1300 : constraints.maxWidth;
|
||||
|
||||
return ScrollConfiguration(
|
||||
behavior: const MaterialScrollBehavior().copyWith(
|
||||
dragDevices: {PointerDeviceKind.mouse, PointerDeviceKind.touch},
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: Column(
|
||||
children: [
|
||||
// Table Header (sticky)
|
||||
Container(
|
||||
color: Color(0xFFEDF6F5),
|
||||
child: _buildTableHeader(isDesktop),
|
||||
),
|
||||
|
||||
// Grouped Rows
|
||||
...groupedData.entries.map((entry) {
|
||||
final status = entry.key;
|
||||
final items = entry.value;
|
||||
final isExpanded = groupExpanded[status] ?? true;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Group Header
|
||||
_buildGroupHeader(status, items.length),
|
||||
|
||||
// Group Rows (if expanded)
|
||||
if (isExpanded)
|
||||
...items.isEmpty
|
||||
? [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'No records found',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
: items.asMap().entries.map((itemEntry) {
|
||||
final index = itemEntry.key;
|
||||
final item = itemEntry.value;
|
||||
final startIndex = (currentPage - 1) * itemsPerPage;
|
||||
final sno = startIndex + index + 1;
|
||||
|
||||
return _buildGroupedDataRow(
|
||||
item,
|
||||
sno,
|
||||
isDesktop,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 6. Build table header
|
||||
Widget _buildTableHeader(bool isDesktop) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildHeaderCell('Received Date', 120),
|
||||
_buildHeaderCell('Partner *', 110),
|
||||
_buildHeaderCell('Assigned To *', 110),
|
||||
_buildHeaderCell('Broker *', 100),
|
||||
_buildHeaderCell('Insurer *', 110),
|
||||
_buildHeaderCell('Insured Name *', 120),
|
||||
_buildHeaderCell('Vehicle No *', 120),
|
||||
_buildHeaderCell('Vehicle Type *', 110),
|
||||
_buildHeaderCell('Email', 150),
|
||||
_buildHeaderCell('Mobile', 100),
|
||||
_buildHeaderCell('Documents', 100),
|
||||
_buildHeaderCell('Remarks', 120),
|
||||
if (roleId != 'staff') _buildHeaderCell('Action', 100),
|
||||
_buildHeaderCell('Assigned Date', 120),
|
||||
_buildHeaderCell('Premium', 100),
|
||||
_buildHeaderCell('Payment Mode', 100),
|
||||
_buildHeaderCell('Policy Number', 120),
|
||||
_buildHeaderCell('Status', 100),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderCell(String label, double width) {
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 7. Build grouped data row (container-based instead of DataTable)
|
||||
Widget _buildGroupedDataRow(
|
||||
Map<String, dynamic> item,
|
||||
int sno,
|
||||
bool isDesktop,
|
||||
) {
|
||||
final id = int.tryParse(item['id'].toString()) ?? 0;
|
||||
final selectId = item['id'].toString() ?? '0';
|
||||
final editing = isEditingRow[id] ?? false;
|
||||
|
||||
final bool isNewRow =
|
||||
int.tryParse(id.toString()) != null &&
|
||||
int.parse(id.toString()) > 1000000000000;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: editing ? Color(0xFFf8f9fa) : Colors.white,
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Colors.grey.shade200, width: 0.5),
|
||||
),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// Optional: handle row tap
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
// Received Date
|
||||
_buildDataCell(
|
||||
formatDateTimeForTable(item['created_on'], newLine: true),
|
||||
120,
|
||||
),
|
||||
|
||||
// Partner
|
||||
_buildDataCell(
|
||||
editing ? null : item['agent_name'] ?? '-',
|
||||
110,
|
||||
widget: editing ? buildAgentName(context, fromHeader: false) : null,
|
||||
),
|
||||
|
||||
// Assigned To
|
||||
_buildDataCell(
|
||||
editing ? null : item['assigned_to_name'] ?? '-',
|
||||
110,
|
||||
widget: editing ? buildSelectStaffMem(context) : null,
|
||||
subtitle: editing ? null : item['agent_code'],
|
||||
),
|
||||
|
||||
// Broker
|
||||
_buildDataCell(
|
||||
editing ? null : item['broker_name'] ?? '-',
|
||||
100,
|
||||
widget: editing ? buildBroker(context) : null,
|
||||
),
|
||||
|
||||
// Insurer
|
||||
_buildDataCell(
|
||||
editing ? null : item['insurer_short_name'] ?? '-',
|
||||
110,
|
||||
widget: editing ? buildInsurer(context, fromHeader: false) : null,
|
||||
),
|
||||
|
||||
// Insured Name
|
||||
_buildDataCell(
|
||||
editing ? null : item['insured_name'] ?? '-',
|
||||
120,
|
||||
widget: editing ? buildInsuredName(context, fromHeader: false) : null,
|
||||
),
|
||||
|
||||
// Vehicle No
|
||||
_buildDataCell(
|
||||
editing ? null : item['reg_no'] ?? '-',
|
||||
120,
|
||||
widget: editing ? buildVehicleNumber(context) : null,
|
||||
),
|
||||
|
||||
// Vehicle Type
|
||||
_buildDataCell(
|
||||
editing ? null : _wrapText(item['vehicle_type'] ?? '-', 13),
|
||||
110,
|
||||
widget: editing ? buildVehicleType(context) : null,
|
||||
),
|
||||
|
||||
// Email
|
||||
_buildDataCell(
|
||||
editing ? null : item['email'] ?? '-',
|
||||
150,
|
||||
widget: editing ? buildEmail(context) : null,
|
||||
),
|
||||
|
||||
// Mobile
|
||||
_buildDataCell(
|
||||
editing ? null : item['mobile'] ?? '-',
|
||||
100,
|
||||
widget: editing ? buildPhNumber(context) : null,
|
||||
),
|
||||
|
||||
// Documents
|
||||
_buildDocumentCell(selectId, editing, 100),
|
||||
|
||||
// Remarks
|
||||
_buildDataCell(
|
||||
editing ? null : _truncateText(item['remarks'] ?? '-', 10),
|
||||
120,
|
||||
widget: editing ? buildRemarks(context) : null,
|
||||
tooltip: item['remarks'],
|
||||
),
|
||||
|
||||
// Action (if not staff)
|
||||
if (roleId != 'staff')
|
||||
_buildActionCell(item, id, editing, isNewRow, 100),
|
||||
|
||||
// Assigned Date
|
||||
_buildDataCell(
|
||||
formatDateTimeForTable(item['updated_on'], newLine: true),
|
||||
120,
|
||||
),
|
||||
|
||||
// Premium
|
||||
_buildPremiumCell(item, 100),
|
||||
|
||||
// Payment Mode
|
||||
_buildDataCell(item['payment_mode'] ?? '-', 100),
|
||||
|
||||
// Policy Number
|
||||
_buildDataCell(item['policy_number'] ?? '-', 120),
|
||||
|
||||
// Status
|
||||
_buildStatusCell(item, 100),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 8. Helper widget builders
|
||||
Widget _buildDataCell(
|
||||
String? text,
|
||||
double width, {
|
||||
Widget? widget,
|
||||
String? subtitle,
|
||||
String? tooltip,
|
||||
}) {
|
||||
if (widget != null) {
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: widget,
|
||||
);
|
||||
}
|
||||
|
||||
Widget child = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
text ?? '-',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (subtitle != null)
|
||||
Text(
|
||||
subtitle,
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (tooltip != null && tooltip.isNotEmpty) {
|
||||
child = Tooltip(
|
||||
message: tooltip,
|
||||
waitDuration: Duration(milliseconds: 400),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDocumentCell(String selectId, bool editing, double width) {
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final RenderBox button = context.findRenderObject() as RenderBox;
|
||||
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
final Offset position = button.localToGlobal(Offset.zero, ancestor: overlay);
|
||||
|
||||
await showMenu(
|
||||
context: context,
|
||||
position: RelativeRect.fromLTRB(
|
||||
position.dx,
|
||||
position.dy + button.size.height,
|
||||
overlay.size.width,
|
||||
0,
|
||||
),
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildPopupDownload(context, selectId, editing),
|
||||
),
|
||||
),
|
||||
],
|
||||
color: Colors.white,
|
||||
);
|
||||
},
|
||||
child: Tooltip(
|
||||
message: editing ? 'Click To Upload Documents' : 'Click To View Documents',
|
||||
child: Icon(
|
||||
editing ? Icons.upload_file : Icons.download_for_offline,
|
||||
color: editing ? Colors.blue : Colors.green,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionCell(
|
||||
Map<String, dynamic> item,
|
||||
int id,
|
||||
bool editing,
|
||||
bool isNewRow,
|
||||
double width,
|
||||
) {
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
if (editing) ...[
|
||||
IconButton(
|
||||
icon: Icon(Icons.check, color: Colors.green, size: 18),
|
||||
tooltip: "Save",
|
||||
onPressed: () => handleSave(id),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: BoxConstraints(),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
isNewRow ? Icons.close : Icons.edit_off_sharp,
|
||||
color: isNewRow ? Colors.red : Colors.blue,
|
||||
size: 18,
|
||||
),
|
||||
tooltip: isNewRow ? "Delete" : "Cancel Edit Mode",
|
||||
onPressed: () => handleCancel(id),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: BoxConstraints(),
|
||||
),
|
||||
] else ...[
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.blue, size: 18),
|
||||
tooltip: "Edit",
|
||||
onPressed: () => handleEdit(item),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: BoxConstraints(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPremiumCell(Map<String, dynamic> item, double width) {
|
||||
final showApproval = item['status'] == 'Proposal Created' ||
|
||||
item['status'] == 'Proposal Rejected';
|
||||
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: showApproval
|
||||
? InkWell(
|
||||
onTap: () => handleProposalAccept(context, item['id']),
|
||||
child: Text(
|
||||
'Click To\nApprove',
|
||||
style: GoogleFonts.inter(
|
||||
color: Colors.green,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
item['premium_amount']?.toString() ?? '-',
|
||||
style: GoogleFonts.inter(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusCell(Map<String, dynamic> item, double width) {
|
||||
return Container(
|
||||
width: width,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
var status = item['status'];
|
||||
var enqId = item['id'];
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('enqStaffDataId');
|
||||
await prefs.setString('enqStaffDataId', enqId.toString());
|
||||
ref.read(quotationStaffIdProvider.notifier).state = enqId;
|
||||
buildStatusActions(context, status, enqId);
|
||||
},
|
||||
child: Tooltip(
|
||||
message: 'Click to View or Process Enquiry',
|
||||
waitDuration: Duration(milliseconds: 500),
|
||||
child: Text(
|
||||
item['status'] ?? '-',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
|
||||
import '../model/dropdown_option.dart';
|
||||
|
||||
class SearchableDropdown extends StatelessWidget {
|
||||
final String label;
|
||||
final String? value;
|
||||
final List<DropdownOption> items;
|
||||
final Function(String?) onChanged;
|
||||
final String? Function(String?)? validator;
|
||||
final bool enabled;
|
||||
|
||||
const SearchableDropdown({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.items,
|
||||
required this.onChanged,
|
||||
this.validator,
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownSearch<DropdownOption>(
|
||||
// items: items,
|
||||
selectedItem: items.where((item) => item.value == value).firstOrNull,
|
||||
itemAsString: (item) => item.label,
|
||||
onChanged: (item) => onChanged(item?.value),
|
||||
enabled: enabled,
|
||||
// dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
// dropdownSearchDecoration: InputDecoration(
|
||||
// labelText: label,
|
||||
// border: const OutlineInputBorder(),
|
||||
// contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
// ),
|
||||
// ),
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
searchFieldProps: const TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search...',
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
),
|
||||
),
|
||||
// itemBuilder: (context, item, isSelected) {
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
// decoration: BoxDecoration(
|
||||
// color: isSelected ? const Color(0xFFe3f2fd) : null,
|
||||
// ),
|
||||
// child: Text(
|
||||
// item.label,
|
||||
// style: TextStyle(
|
||||
// fontSize: 13,
|
||||
// fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
menuProps: const MenuProps(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 8,
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
),
|
||||
searchDelay: const Duration(milliseconds: 300),
|
||||
emptyBuilder: (context, searchEntry) {
|
||||
return const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'No items found',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
compareFn: (item1, item2) => item1.value == item2.value,
|
||||
// validator: validator,
|
||||
dropdownBuilder: (context, selectedItem) {
|
||||
return Text(
|
||||
selectedItem?.label ?? '',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -132,6 +132,10 @@ class _ThemedUploadFieldState extends State<ThemedUploadField> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// const Icon(
|
||||
// Icons.file_upload_outlined,
|
||||
// color: Colors.black,
|
||||
// ),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@ -85,7 +85,7 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
width: widget.txtwidth ?? MediaQuery.of(context).size.width,
|
||||
height: widget.txtheight,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
vertical: 8,
|
||||
horizontal: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
@ -94,7 +94,7 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
border: Border.all(
|
||||
color: hasError
|
||||
? (widget.errorBorderColor ?? Color(0xFFD83731))
|
||||
: (widget.borderColor ?? Colors.grey.shade50),
|
||||
: (widget.borderColor ?? Color(0xFFE2E8F0)),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
@ -107,7 +107,7 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
? widget.controller!.text
|
||||
: widget.hintText ?? "Select Date",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontSize: 11,
|
||||
color: hasError ? Color(0xFFD83731) : Colors.black,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@ -116,7 +116,8 @@ class _ThemedDateFieldState extends State<ThemedDateField> {
|
||||
const Icon(
|
||||
Icons.calendar_today,
|
||||
size: 16,
|
||||
color: Colors.black,
|
||||
|
||||
color: Color(0xFF5A6C7D),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -74,7 +74,8 @@ class ExportBtn extends HookWidget {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF425B5B),
|
||||
color: const Color(0xFF2E7D6E),
|
||||
// color: const Color(0xFF425B5B),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: Row(
|
||||
|
||||
@ -4,7 +4,7 @@ class AppInputDecorations {
|
||||
static InputDecoration dropdownDecoration({String? label, String? hint}) {
|
||||
return InputDecoration(
|
||||
labelText: label,
|
||||
labelStyle: TextStyle(fontSize: 12),
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
hintText: hint,
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFECECEC),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
// import 'package:uae_stat/config/my_theme.dart';
|
||||
|
||||
@ -55,20 +56,23 @@ class ThemedSearchField extends HookWidget {
|
||||
// contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 15),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Color(0xFFF6F8F8)),
|
||||
borderSide: BorderSide(color: Color(0xFFE2E8F0)),
|
||||
// borderSide: BorderSide(color: Color(0xFFF6F8F8)),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Color(0xFFF6F8F8)),
|
||||
// borderSide: BorderSide(color: Color(0xFFF6F8F8)),
|
||||
borderSide: BorderSide(color: Color(0xFFE2E8F0)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Color(0xFF50A398), width: 2),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
color: const Color(0xFF686868),
|
||||
fontSize: (txtHeight ?? 45) <= 45 ? 14 : 16,
|
||||
hintStyle: GoogleFonts.inter(
|
||||
// color: const Color(0xFF686868),
|
||||
color: const Color(0xFF94A3B8),
|
||||
fontSize: (txtHeight ?? 45) <= 45 ? 12 : 14,
|
||||
),
|
||||
// suffixIconConstraints: const BoxConstraints(
|
||||
// maxWidth: 25 + 16 + 10,
|
||||
@ -76,7 +80,8 @@ class ThemedSearchField extends HookWidget {
|
||||
// ),
|
||||
suffixIcon: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16, end: 10),
|
||||
child: Icon(Icons.search, color: const Color(0xFF686868), size: 20),
|
||||
child: Icon(Icons.search, color: const Color(0xFF94A3B8), size: 18),
|
||||
// child: Icon(Icons.search, color: const Color(0xFF686868), size: 20),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@ -31,12 +31,14 @@ class ThemedFormField extends HookWidget {
|
||||
this.verticalPad,
|
||||
this.horizonalPad,
|
||||
this.enableBorderWidth,
|
||||
this.textColr,
|
||||
});
|
||||
|
||||
final double? verticalPad;
|
||||
final double? horizonalPad;
|
||||
final double? enableBorderWidth;
|
||||
final String? hintText;
|
||||
final Color? textColr;
|
||||
|
||||
final String? imgPath;
|
||||
final String? Function(String? text)? validator;
|
||||
@ -130,7 +132,10 @@ class ThemedFormField extends HookWidget {
|
||||
)
|
||||
: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Color(0xFF50A398), width: 2),
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ?? Color(0xFF50A398),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
@ -187,7 +192,7 @@ class ThemedFormField extends HookWidget {
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, // 👈 Change this to your desired size
|
||||
fontWeight: FontWeight.w400, // optional
|
||||
color: Colors.black, // optional
|
||||
color: textColr ?? Colors.black, // optional
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -18,6 +18,7 @@ class ThemedFormInlineField extends HookWidget {
|
||||
this.isObscurable = false,
|
||||
this.hintColor,
|
||||
this.backgroundColor,
|
||||
this.textColr,
|
||||
this.borderColor,
|
||||
this.errorBorderColor,
|
||||
this.errorTextColor,
|
||||
@ -41,6 +42,7 @@ class ThemedFormInlineField extends HookWidget {
|
||||
final Color? borderColor;
|
||||
final Color? errorBorderColor;
|
||||
final Color? errorTextColor;
|
||||
final Color? textColr;
|
||||
final double? txtwidth;
|
||||
final bool readOnly;
|
||||
final bool? isdense;
|
||||
@ -96,14 +98,14 @@ class ThemedFormInlineField extends HookWidget {
|
||||
filled: true,
|
||||
fillColor: backgroundColor ?? Colors.white,
|
||||
// contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 2),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
// borderSide: BorderSide(color: Color(0xFF50A398)),
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ?? Color(0xFFFFFFFF),
|
||||
width: 0.1,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
@ -111,7 +113,7 @@ class ThemedFormInlineField extends HookWidget {
|
||||
// borderSide: BorderSide(color: Color(0xFF50A398)),
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ?? backgroundColor ?? Color(0xFFFFFFFF),
|
||||
width: 0.1,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
focusedBorder: readOnly
|
||||
@ -122,7 +124,10 @@ class ThemedFormInlineField extends HookWidget {
|
||||
)
|
||||
: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(color: Color(0xFF50A398), width: 2),
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ?? Color(0xFF50A398),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
@ -177,7 +182,7 @@ class ThemedFormInlineField extends HookWidget {
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 12, // 👈 Change this to your desired size
|
||||
fontWeight: FontWeight.w400, // optional
|
||||
color: Colors.black, // optional
|
||||
color: textColr ?? Colors.black, // optional
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -196,7 +196,12 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
child: Tooltip(
|
||||
message: 'Filter',
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.filter_alt_outlined),
|
||||
icon: const Icon(
|
||||
Icons.filter_alt_outlined,
|
||||
size: 18,
|
||||
color: const Color(0xFF94A3B8),
|
||||
),
|
||||
|
||||
onPressed: () {
|
||||
if (widget.formKey.currentState!.validate()) {
|
||||
widget.onFilter();
|
||||
@ -212,7 +217,11 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
message: 'Refresh',
|
||||
child: IconButton(
|
||||
onPressed: widget.onRefresh,
|
||||
icon: const Icon(Icons.refresh),
|
||||
icon: const Icon(
|
||||
Icons.refresh,
|
||||
size: 18,
|
||||
color: const Color(0xFF94A3B8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -251,20 +260,25 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
children: [
|
||||
// Text('Start Date', style: _textStyle),
|
||||
// SizedBox(height: 5),
|
||||
ThemedDateField(
|
||||
hintText: "Select Date",
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.1,
|
||||
// txtheight: 50,
|
||||
// backgroundColor: const Color(0xFFECECEC),
|
||||
// validator: (value) => Validators.requiredField(value, "date"),
|
||||
controller: widget.startController,
|
||||
onDateSelected: (date) {
|
||||
print("Picked Date: $date");
|
||||
widget.startController.text = DateFormat('dd-MM-yyyy').format(date);
|
||||
// controllers['date']?.text = date as String;
|
||||
},
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: ThemedDateField(
|
||||
hintText: "Select Date",
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.1,
|
||||
// txtheight: 50,
|
||||
// backgroundColor: const Color(0xFFECECEC),
|
||||
// validator: (value) => Validators.requiredField(value, "date"),
|
||||
controller: widget.startController,
|
||||
onDateSelected: (date) {
|
||||
print("Picked Date: $date");
|
||||
widget.startController.text = DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(date);
|
||||
// controllers['date']?.text = date as String;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -276,35 +290,38 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
children: [
|
||||
// Text('End Date', style: _textStyle),
|
||||
// SizedBox(height: 5),
|
||||
ThemedDateField(
|
||||
hintText: "Select Date",
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.1,
|
||||
// txtheight: 50,
|
||||
// backgroundColor: const Color(0xFFECECEC),
|
||||
validator: (value) {
|
||||
final fromText = widget.startController.text ?? '';
|
||||
if (fromText.isNotEmpty) {
|
||||
final startDate = DateFormat('dd-MM-yyyy').parse(fromText);
|
||||
if (value == null || value.isEmpty) {
|
||||
return "End Date is required";
|
||||
}
|
||||
final endDate = DateFormat('dd-MM-yyyy').parse(value);
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: ThemedDateField(
|
||||
hintText: "Select Date",
|
||||
txtwidth: ResponsiveLayout.isMobile(context)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width * 0.1,
|
||||
// txtheight: 50,
|
||||
// backgroundColor: const Color(0xFFECECEC),
|
||||
validator: (value) {
|
||||
final fromText = widget.startController.text ?? '';
|
||||
if (fromText.isNotEmpty) {
|
||||
final startDate = DateFormat('dd-MM-yyyy').parse(fromText);
|
||||
if (value == null || value.isEmpty) {
|
||||
return "End Date is required";
|
||||
}
|
||||
final endDate = DateFormat('dd-MM-yyyy').parse(value);
|
||||
|
||||
if (endDate.isBefore(startDate)) {
|
||||
return "End Date cannot be earlier than Start Date";
|
||||
if (endDate.isBefore(startDate)) {
|
||||
return "End Date cannot be earlier than Start Date";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null; // ✅ no error
|
||||
},
|
||||
controller: widget.endController,
|
||||
lastDate: DateTime.now(),
|
||||
onDateSelected: (date) {
|
||||
print("Picked Date: $date");
|
||||
widget.endController.text = DateFormat('dd-MM-yyyy').format(date);
|
||||
// controllers['date']?.text = date as String;
|
||||
},
|
||||
return null; // ✅ no error
|
||||
},
|
||||
controller: widget.endController,
|
||||
lastDate: DateTime.now(),
|
||||
onDateSelected: (date) {
|
||||
print("Picked Date: $date");
|
||||
widget.endController.text = DateFormat('dd-MM-yyyy').format(date);
|
||||
// controllers['date']?.text = date as String;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -339,11 +356,11 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
// Text('Status', style: _textStyle),
|
||||
// SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
height: 35,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
border: Border.all(color: Color(0xFFE2E8F0)),
|
||||
// color: Colors.black,
|
||||
),
|
||||
width: ResponsiveLayout.isMobile(context)
|
||||
@ -390,7 +407,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
isDense: true, // 👈 reduces built-in vertical padding
|
||||
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 2,
|
||||
horizontal: 6,
|
||||
vertical: 1, // 👈 adjust this to make the field shorter
|
||||
),
|
||||
),
|
||||
@ -480,11 +497,11 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
// Text("Staff Name", style: _textStyle),
|
||||
// SizedBox(height: 10),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
height: 35,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
border: Border.all(color: Color(0xFFE2E8F0)),
|
||||
// color: Colors.black,
|
||||
),
|
||||
|
||||
@ -524,7 +541,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
|
||||
fillColor:
|
||||
Colors.white, // 👈 makes the dropdown input white
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 2,
|
||||
horizontal: 6,
|
||||
vertical: 1, // 👈 adjust this to make the field shorter
|
||||
),
|
||||
),
|
||||
|
||||
@ -137,9 +137,11 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
|
||||
_hidePopup();
|
||||
if (roleId == 'agent') {
|
||||
context.go(AppRoutes.enquiryLst);
|
||||
} else if (roleId == 'handler') {
|
||||
context.go(AppRoutes.enquiryHandlerLst);
|
||||
} else {
|
||||
}
|
||||
// else if (roleId == 'handler') {
|
||||
// context.go(AppRoutes.enquiryHandlerLst);
|
||||
// }
|
||||
else {
|
||||
context.go(AppRoutes.enquiryForStaff);
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user