UI_USER_PROFILE

This commit is contained in:
venbaittech 2025-09-09 18:47:37 +05:30
parent cd06b555ac
commit fa484bc00e
15 changed files with 1470 additions and 69 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:nhance_partner/core/routing/routes.dart';
import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart';
import 'package:nhance_partner/presentation/screens/claims/claimsList.dart';
import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart';
import '../../data/services/auth_service.dart';
@ -32,6 +33,11 @@ final GoRouter appRouter = GoRouter(
builder: (context, state) => const DashboardScreen(),
),
GoRoute(
path: AppRoutes.profile,
builder: (context, state) => const ProfilePopUp(),
),
GoRoute(
path: AppRoutes.agentLst,
// builder: (context, state) => const Agent(),
@ -70,7 +76,6 @@ final GoRouter appRouter = GoRouter(
}
},
),
],
redirect: (context, state) async {
final loggedIn = await AuthService.isLoggedIn();

View File

@ -3,9 +3,9 @@ class AppRoutes {
static const String home = '/home';
static const String dashboard = '/dashboard';
static const String login = '/login';
static const String profile = '/profile';
static const String agentLst = '/agentLst';
static const agent = '/agent/:id';
static const String staffLst = '/staffLst';
static const staff = '/staff/:id';
}

View File

@ -98,6 +98,27 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> fetchManagerIncentiveList(mangerId) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}staff/managerIncentiveFileList?manager_id=$mangerId',
);
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}',
// );
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
Future<Map<String, dynamic>> fetchAgentUserList(int managerId) async {
// print(_token);
if (_token == null) {
@ -212,7 +233,7 @@ class ApiService {
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/downloadAgentCertificateFile?agent_id=1',
// );
final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path}');
final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path');
// final token = await getToken();

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:nhance_partner/core/routing/routes.dart';
import 'package:nhance_partner/data/services/auth_service.dart';
import '../screens/UserManagement/Profile/profile_web.dart';
import '../widgets/custom_action_popup.dart';
import '../widgets/topbar.dart';
import '../widgets/footer.dart';
import '../widgets/drawer_menu.dart';
@ -19,6 +21,7 @@ class MainLayout extends StatelessWidget {
return ResponsiveLayout(
// Mobile Layout
mobile: Scaffold(
backgroundColor: Colors.white,
appBar: TopBar(
title: title,
isMobile: true,
@ -34,6 +37,9 @@ class MainLayout extends StatelessWidget {
bottomNavigationBar: MobileTabs(
onTabChanged: (index) {
debugPrint("Mobile tab changed to $index");
if (index == 1) {
context.go(AppRoutes.profile);
}
},
),
),
@ -50,9 +56,27 @@ class MainLayout extends StatelessWidget {
AuthService.clearToken();
context.go(AppRoutes.login);
},
onProfile: () {
debugPrint("Profile tapped");
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: 500, child: ProfilePopUp()),
),
],
);
},
onNotifications: () {
debugPrint("Notifications tapped");
},
@ -60,18 +84,14 @@ class MainLayout extends StatelessWidget {
body: Row(
children: [
const SizedBox(
width: 80, // fixed width for drawer
width: 90, // fixed width for drawer
child: DrawerMenu(),
),
Expanded(
child: Container(
padding: EdgeInsets.all(10.0),
color: Colors.white,
child: Column(
children: [
Expanded(child: body),
],
),
child: Column(children: [Expanded(child: body)]),
),
),
],

View File

@ -0,0 +1,435 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../layouts/main_layout.dart';
import '../../themes/indicators/export_btn.dart';
import '../../themes/indicators/search_field_theme.dart';
import '../../widgets/custom_action_popup.dart';
class enquiryList extends StatefulWidget {
const enquiryList({super.key});
@override
enquiryListState createState() => enquiryListState();
}
class enquiryListState extends State<enquiryList> {
int currentPage = 1;
int itemsPerPage = 10;
late ApiService apiService;
// List<Map<String, dynamic>> dataVal = [];
List<Map<String, dynamic>> getStaffData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
bool isLoading = false;
@override
void initState() {
super.initState();
apiService = ApiService();
// getStaffList();
}
List<dynamic> get _paginatedData {
final startIndex = (currentPage - 1) * itemsPerPage;
final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
}
void filterData(String query) {
print("FilterDAta - $query");
setState(() {
filteredData = getStaffData.where((item) {
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
return (item['name'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['email'] ?? '').toLowerCase().contains(query.toLowerCase()) ||
(item['mobile'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['address'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
(item['emp_id'] ?? item['agent_code'] ?? '').toLowerCase().contains(
query.toLowerCase(),
) ||
isActiveStatus.contains(query.toLowerCase());
}).toList();
});
}
final TextEditingController _searchStaffController = TextEditingController();
// Future<void> getStaffList() async {
// print('getClaimList called');
// setState(() {
// isLoading = true;
// });
//
// try {
// final response = await apiService.fetchStaffUserList();
//
// if (response['status'] == 'success') {
// print('getStaffListData - ${response['data']}');
// setState(() {
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
// originalData = getStaffData;
// filteredData = List.from(originalData);
// // print('originalData - $getClaimPolicies');
// });
// } else {
// getStaffData = [];
// originalData = [];
// }
// } catch (e) {
// print('Exception occurred: $e');
// } finally {
// setState(() {
// isLoading = false;
// });
// }
// }
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
return [
GestureDetector(
onTap: () {
Navigator.pop(context);
print('EDITStaff - ${data['id']}');
dynamic id = data['id'];
context.go('/staff/$id');
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
SizedBox(width: 10),
Text('Edit'),
],
),
),
];
}
@override
Widget build(BuildContext context) {
return MainLayout(
title: "Enquiry",
body: Container(
// color: Colors.yellow.shade50,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 30,
width: MediaQuery.of(context).size.width,
child: GestureDetector(
onTap: () {
context.go(AppRoutes.dashboard);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(
Icons.arrow_left_sharp,
size: 35,
color: Color(0xFF425B5B),
),
Text(
"Enquries",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
SizedBox(height: 10),
Expanded(
child: Container(
// color: Colors.green,
// color: Colors.green.shade50,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Column(
children: [
Container(
// height: 40,
// color: Colors.pink,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
ThemedSearchField(
hintText: 'Search',
backgroundColor: Color(0xFFF6F8F8),
onChanged: filterData,
controller: _searchStaffController,
txtwidth: MediaQuery.of(context).size.width * 0.2,
),
Spacer(),
ExportBtn(
sheetName: "Staff",
fileName: "staff_list",
data: filteredData,
headers: [
"id",
"name",
"email",
"mobile",
"emp_id",
"is_active",
],
),
SizedBox(width: 10),
GestureDetector(
onTap: () {
print('Export');
},
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Color(0xFF425B5B),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, color: Colors.white),
SizedBox(width: 10),
GestureDetector(
onTap: () {
// context.go(AppRoutes.agent / create);
context.go('/staff/create');
},
child: Text(
'Create New Staff',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
],
),
),
),
],
),
),
SizedBox(height: 10),
Expanded(child: _buildDataTable(context)),
],
),
),
),
Container(
// height: 20,
width: MediaQuery.of(context).size.width,
// color: Colors.green.shade50,
child: PaginationControls(
currentPage: currentPage,
itemsPerPage: itemsPerPage,
// totalItems: dataVal.length,
totalItems: filteredData.length,
// activeColor: layoutColor, // your theme color
onPageChanged: (page) {
setState(() {
currentPage = page;
});
},
onItemsPerPageChanged: (items) {
setState(() {
itemsPerPage = items;
currentPage = 1;
});
},
),
),
],
),
),
);
}
Widget _buildDataTable(BuildContext context) {
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
return ListView.builder(
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
itemCount: sortedData.length + 1, // +1 for header, +1 for pagination
itemBuilder: (context, index) {
if (index == 0) return _buildHeader();
// if (index == dataVal.length + 1)
// return _buildPagination(context);
final startIndex = ((currentPage - 1) * itemsPerPage);
// final item = filteredData[index - 1];
final item = sortedData[index - 1];
final sno = startIndex + index;
return _buildDataRow(item, sno);
},
);
}
Widget _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Received Date & Time', style: _headerStyle),
),
Expanded(flex: 3, child: Text('Reg.No.', style: _headerStyle)),
Expanded(flex: 2, child: Text('Company', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
Expanded(flex: 1, child: Text('Remarks', style: _headerStyle)),
Expanded(flex: 1, child: Text('Action', style: _headerStyle)),
],
),
);
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
// margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Colors.white,
// color: Color(0xFFE0F7F9),
border: const Border(
bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
Expanded(
flex: 3,
child: Text(item['email'] ?? '-', style: _dataBold),
),
Expanded(
flex: 2,
child: Text(item['mobile'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['emp_id'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Transform.scale(
scale: 0.6, // reduce size (0.70.9 works well)
child: Switch(
value: item['is_active'] == "1",
onChanged: (val) {
setState(() {
item['is_active'] = val ? "1" : "0";
});
final response = apiService.updateStatus(
item['id'],
val ? "1" : "0",
'staff',
);
print("Response - $response");
},
activeColor: Color(0xFF425B5B), // thumb when active
activeTrackColor: Color(0xFFB2D8D3), // track when active
inactiveThumbColor: Colors.grey.shade400, // thumb when inactive
inactiveTrackColor: Colors.grey.shade300, // track when inactive
),
),
),
Expanded(
flex: 1,
child: Row(
children: [
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder: (context) => [
CustomPopupMenuEntry(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: _buildPopupMenuActions(context, item),
),
),
),
],
),
],
),
),
],
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
}

View File

@ -0,0 +1,498 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jwt_decode/jwt_decode.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
class ProfilePopUp extends ConsumerStatefulWidget {
const ProfilePopUp({super.key});
@override
ConsumerState<ProfilePopUp> createState() => ProfilePopUpState();
}
class ProfilePopUpState extends ConsumerState<ProfilePopUp> {
late ApiService apiService;
List<Map<String, dynamic>> filteredIncentiveData = [];
List<Map<String, dynamic>> getAgentIncentiveFileData = [];
Map<String, dynamic>? profileData;
dynamic roleIdRaw;
dynamic roleId;
bool isProfile = true;
bool isLoading = false;
String? _token;
dynamic latestId;
@override
void initState() {
super.initState();
apiService = ApiService();
// getAgentList();
_initializeToken();
// Future.microtask(() {
// final id = ref.read(managerIdProvider);
// if (id != null) {
// getAgentList(id);
//
// // api/staff/managerIncentiveFileList?manager_id=1
// }
// });
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
final Map<String, dynamic> decodedToken = Jwt.parseJwt(_token!);
setState(() {
profileData = decodedToken['data'];
});
print('decodedTokenProfile : $decodedToken');
print('decodedTokenProfile : $profileData');
await getList();
}
Future<void> getList() async {
roleIdRaw = profileData?['role_id'];
roleId = roleIdRaw is String ? int.tryParse(roleIdRaw) : roleIdRaw as int?;
if (roleId == 1) {
final managerIdRaw = profileData?['manager_id'];
final managerId = managerIdRaw is String
? int.tryParse(managerIdRaw)
: managerIdRaw as int?;
if (managerId != null) {
getIncenctiveFileList(managerId);
}
} else if (roleId != 1 && roleId != 2) {
final agentIdRaw = profileData?['id'];
final agentId = agentIdRaw is String
? int.tryParse(agentIdRaw)
: agentIdRaw as int?;
if (agentId != null) {
getIncenctiveFileList(agentId);
}
}
}
Future<void> getIncenctiveFileList(id) async {
// print('getClaimList agentId - $agentId');
setState(() {
isLoading = true;
});
try {
final response;
final roleIdRaw = profileData?['role_id'];
final roleId = roleIdRaw is String
? int.tryParse(roleIdRaw)
: roleIdRaw as int?;
if (roleId == 1) {
response = await apiService.fetchManagerIncentiveList(id);
} else {
response = await apiService.fetchAgentIncentiveList(id);
}
if (response['status'] == 'success') {
print('AgentIncentive - ${response['data']}');
setState(() {
getAgentIncentiveFileData = List<Map<String, dynamic>>.from(
response['data'],
);
print('API AgentIncentive - $getAgentIncentiveFileData');
filteredIncentiveData = List.from(getAgentIncentiveFileData);
print('originalData - $filteredIncentiveData');
// Sort descending by created_on
filteredIncentiveData.sort((a, b) {
final dateA =
DateTime.tryParse(a['created_on'].toString()) ?? DateTime(1970);
final dateB =
DateTime.tryParse(b['created_on'].toString()) ?? DateTime(1970);
return dateB.compareTo(dateA); // newest first
});
// Get the latest ID (from the first element after sorting)
if (filteredIncentiveData.isNotEmpty) {
latestId = filteredIncentiveData.first['id'];
print("Latest incentive id: $latestId");
}
print('sortedData - $filteredIncentiveData');
});
} else {
getAgentIncentiveFileData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'Profile',
body: Container(
// color: Colors.white,
padding: const EdgeInsets.all(6),
width: 450,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (!ResponsiveLayout.isMobile(context))
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? 'My Profile' : 'Incentive Files',
style: _topheaderStyle,
),
Spacer(),
Container(
width: 25,
height: 25,
decoration: BoxDecoration(
color: const Color(0xFFF1F1F1),
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
borderRadius: BorderRadius.circular(10.0),
onTap: () {
Navigator.of(context).pop(); // closes the popup
},
child: const Icon(Icons.close, size: 18),
),
),
],
),
if (ResponsiveLayout.isMobile(context))
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isProfile ? '' : 'Incentive Files',
style: _topheaderStyle,
),
],
),
isProfile
? Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 25),
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: Color(0xFFD9D9D9),
borderRadius: BorderRadius.circular(30.0),
),
child: Icon(
Icons.person_outline,
size: 50,
color: Colors.black,
),
),
const SizedBox(height: 10),
Text(profileData?['name'] ?? '-', style: _headerStyle),
Text(
profileData?['emp_id'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
),
const SizedBox(height: 26),
Row(
children: [
const Icon(
Icons.phone_outlined,
size: 20,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['mobile'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(
Icons.email_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Text(
profileData?['email'] ?? '-',
style: _dataBold,
),
],
),
const SizedBox(height: 16),
if (profileData?['role_id'] != '1' &&
profileData?['role_id'] != '2') ...[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.location_on_outlined,
size: 18,
color: Colors.black,
),
const SizedBox(width: 8),
Expanded(
child: Text(
profileData?['address'] ?? '-',
style: _dataBold,
softWrap: true,
),
),
],
),
const SizedBox(height: 20),
],
if (profileData?['role_id'] != 2) ...[
Container(
padding: const EdgeInsets.all(6),
color: Color(0xffEDF6F5),
child: GestureDetector(
onTap: () {
print("Download - $latestId");
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$latestId';
} else if (roleId != 1 && roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$latestId';
}
apiService.getPdfDownload(path, latestId);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text(
"Download Incentive file",
style: _dataSub,
),
],
),
),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
setState(() {
isProfile = false;
});
},
child: Text(
"View Previous Files",
style: TextStyle(
color: Color(0xff425B5B),
decoration:
TextDecoration.underline, // 👈 underline
decorationThickness: 1.5,
),
),
),
],
const SizedBox(height: 16),
],
),
)
: Container(
color: Colors.white,
padding: ResponsiveLayout.isMobile(context)
? EdgeInsets.all(16.0)
: null,
child: Column(
children: [
const SizedBox(height: 10),
isLoading
? const Center(child: CircularProgressIndicator())
: filteredIncentiveData.isEmpty
? const Center(
child: Text("No incentive files found"),
)
: SizedBox(
height: ResponsiveLayout.isMobile(context)
? MediaQuery.of(context).size.height * 0.4
: 450,
child: ListView.builder(
// padding: const EdgeInsets.all(8),
itemCount: filteredIncentiveData.length,
itemBuilder: (context, index) {
final file = filteredIncentiveData[index];
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
),
child: IncentiveFileRow(
fileName:
file['incentive_file_name'] ??
"Unknown",
date: file['incentive_month'] ?? "-",
onUpload: () {
print("Upload ${file['id']}");
final selectedId = file['id'];
dynamic path;
if (roleId == 1) {
path =
'api/agent/downloadManagerIncentiveFile?id=$selectedId';
} else if (roleId != 1 &&
roleId != 2) {
path =
'api/agent/downloadAgentIncentiveFile?id=$selectedId';
}
apiService.getPdfDownload(
path,
selectedId,
);
},
),
);
},
),
),
],
),
),
],
),
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF000000),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
fontSize: 20,
);
static const _topheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 19,
);
}
class IncentiveFileRow extends StatelessWidget {
final String fileName;
final String date;
final VoidCallback? onUpload;
// final VoidCallback? onDelete;
const IncentiveFileRow({
super.key,
required this.fileName,
required this.date,
this.onUpload,
// this.onDelete,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 7.0),
// margin: const EdgeInsets.only(bottom: 5), // spacing between rows
decoration: BoxDecoration(
// color: Colors.amber.shade50,
border: Border(
bottom: BorderSide(width: 0.6, color: const Color(0xFFE3E3E3)),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: const Color(0xFFF4F6F8),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xFFE3E3E3)),
),
child: const Icon(Icons.file_present, color: Color(0xFF838587)),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
fileName,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Color(0xFF1D1B20),
),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1,
),
),
Text(date, style: const TextStyle(color: Color(0xFF6E6E6E))),
],
),
const Spacer(),
GestureDetector(
onTap: onUpload,
child: const Icon(
Icons.file_download_outlined,
color: Color(0xFF6E6E6E),
),
),
],
),
);
}
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import '../../layouts/main_layout.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../layouts/main_layout.dart';
import '../../providers/manager_provider.dart';
import '../../providers/userRoleProvider.dart';
@ -18,9 +18,345 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final managerId = ref.watch(managerIdProvider);
final userId = ref.watch(userIdProvider);
final role = ref.watch(userRoleProvider);
return MainLayout(
title: "Home",
body: Center(child: Text("Dashboard Page Content $managerId $userId $role")),
body: Row(
children: [
// Main content
Expanded(
child: Column(
children: [
// Stats cards
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: const [
Expanded(
child: StatCard(
title: "Policies Issued",
today: "20",
month: "600",
year: "219000",
imagePath: "assets/dashboard/Policy issue icon.png",
),
),
SizedBox(width: 16),
Expanded(
child: StatCard(
title: "Premium Value",
today: "₹21000",
month: "₹630000",
year: "₹239950000",
imagePath: "assets/dashboard/Premium value.png",
),
),
SizedBox(width: 16),
Expanded(
child: StatCard(
title: "Earnings",
today: "₹2100",
month: "₹63000",
year: "₹23995000",
imagePath:
"assets/dashboard/Earnings.png", // uses image
),
),
],
),
),
// Tables
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: const [
Expanded(
child: DataSection(title: "Enquiries Pending (15)"),
),
SizedBox(width: 16),
Expanded(
child: DataSection(title: "Quotations Pending (10)"),
),
],
),
),
),
],
),
),
],
),
);
}
}
// Stats card widget
class StatCard extends StatelessWidget {
final String title;
final String today;
final String month;
final String year;
final String? imagePath;
const StatCard({
super.key,
required this.title,
required this.today,
required this.month,
required this.year,
this.imagePath,
});
@override
Widget build(BuildContext context) {
return Card(
color: Color(0xFFEAF6F4),
elevation: 1,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Image.asset(imagePath!, width: 28, height: 28),
const SizedBox(width: 8),
Text(
title,
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF425B5B),
),
),
],
),
Divider(),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
today,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
Expanded(
child: Text(
month,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
Expanded(
child: Text(
year,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"Today",
textAlign: TextAlign.center,
style: GoogleFonts.inter(fontSize: 12),
),
),
Expanded(
child: Text(
"This Month",
textAlign: TextAlign.center,
style: GoogleFonts.inter(fontSize: 12),
),
),
Expanded(
child: Text(
"This Year",
textAlign: TextAlign.center,
style: GoogleFonts.inter(fontSize: 12),
),
),
],
),
],
),
),
);
}
}
// Table section widget
class DataSection extends StatelessWidget {
final String title;
const DataSection({super.key, required this.title});
@override
Widget build(BuildContext context) {
final data = List.generate(
10,
(i) => {
"date": "12/09/2025 11:00 AM",
"reg": "TN64V4387",
"company": "New India",
},
);
return Card(
color: const Color(0xFFEAF6F4), // light mint background
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Color(0xff425B5B),
),
),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
decoration: BoxDecoration(
color: const Color(0xFF3E5B56),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"Date & Time",
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.w600,
fontSize: 18,
color: Colors.white,
),
),
),
Expanded(
child: Text(
"Registration number",
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.w600,
fontSize: 18,
color: Colors.white,
),
),
),
Expanded(
child: Text(
"Insurer Company",
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontWeight: FontWeight.w600,
fontSize: 18,
color: Colors.white,
),
),
),
],
),
),
Expanded(
child: ListView(
children: [
const SizedBox(height: 4),
...data.map(
(row) => Container(
margin: const EdgeInsets.symmetric(vertical: 5),
padding: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 12,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'2025-05-31',
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
Text(
'11:11 pm',
style: GoogleFonts.inter(
fontSize: 10,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
Expanded(
child: Text(
row["reg"]!,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: Text(
row["company"]!,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
],
),
),
],
),
),
);
}
}

View File

@ -9,16 +9,17 @@ class DrawerContentWrapper extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 10),
Container(
height: 45,
width: 50,
width: 55,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: const Color(0xFFB2D8D3),
),
child: Center(child: child),
),
const SizedBox(height: 30),
const SizedBox(height: 5),
],
);
}

View File

@ -15,7 +15,7 @@ class DrawerMenu extends ConsumerStatefulWidget {
class DrawerMenuState extends ConsumerState<DrawerMenu> {
OverlayEntry? _overlayEntry;
void _showPopup(BuildContext context, Offset offset, Size size) {
void _showPopup(BuildContext context, Offset offset, Size size, String key) {
_hidePopup(); // clear old one before creating new
_overlayEntry = OverlayEntry(
@ -39,33 +39,76 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
print("Tappped Agent");
context.go(AppRoutes.agentLst);
},
child: const Text(
"Agent",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
if (key == 'User') ...[
GestureDetector(
onTap: () {
print("Tappped Agent");
context.go(AppRoutes.agentLst);
},
child: const Text(
"Agent",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
),
const SizedBox(height: 5),
GestureDetector(
onTap: () {
print("Bf clicked");
context.go(AppRoutes.staffLst);
},
child: const Text(
"Staff",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
const SizedBox(height: 5),
GestureDetector(
onTap: () {
print("Bf clicked");
context.go(AppRoutes.staffLst);
},
child: const Text(
"Staff",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
),
],
if (key == 'Reports') ...[
GestureDetector(
onTap: () {
print("Tappped Agent");
},
child: const Text(
"Enquiries",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 5),
GestureDetector(
onTap: () {
print("Bf clicked");
},
child: const Text(
"Claims",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 5),
GestureDetector(
onTap: () {
print("Bf clicked");
},
child: const Text(
"Endorsement",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
],
],
),
),
@ -96,37 +139,50 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
child: ListView(
padding: EdgeInsets.zero,
children: [
// ListTile(
// title: const Text("Home"),
// onTap: () {
// context.go(AppRoutes.home);
// Navigator.pop(context);
// },
// ),
SizedBox(height: 50),
SizedBox(height: 30),
DrawerContentWrapper(
child: Image.asset(
"assets/drawer/drawerImag1",
height: 50,
width: 50,
"assets/drawer/drawerImg1.png",
height: 25,
width: 25,
),
),
DrawerContentWrapper(
child: Image.asset(
"assets/drawer/drawerImag2",
height: 50,
width: 50,
),
DrawerLabel("Dashboard"),
Builder(
builder: (itemContext) {
return MouseRegion(
// onEnter: (_) => _showPopup(itemContext),
onEnter: (_) {
final renderBox = itemContext.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
final key = "Reports";
_showPopup(context, offset, size, key);
},
child: DrawerContentWrapper(
child: Image.asset(
"assets/drawer/drawerImg2.png",
height: 25,
width: 25,
),
),
);
},
),
DrawerLabel("Reports"),
DrawerContentWrapper(
child: Image.asset(
"assets/drawer/drawerImag3",
height: 50,
width: 50,
child: IconButton(
onPressed: () {},
icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black),
),
),
if (roleId == 'manager')
DrawerLabel("Enquiry"),
if (roleId == 'manager') ...[
Builder(
builder: (itemContext) {
return MouseRegion(
@ -136,7 +192,8 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
itemContext.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
_showPopup(context, offset, size);
final key = 'User';
_showPopup(context, offset, size, key);
},
child: DrawerContentWrapper(
@ -145,8 +202,26 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
);
},
),
DrawerLabel("User"),
],
],
),
);
}
}
class DrawerLabel extends StatelessWidget {
final String text;
const DrawerLabel(this.text, {super.key});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(text, style: const TextStyle(color: Colors.white, fontSize: 11.5)),
SizedBox(height: 20),
],
);
}
}

View File

@ -6,7 +6,8 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
final VoidCallback? onBack;
final VoidCallback? onMenuPressed;
final VoidCallback? onLogout;
final VoidCallback? onProfile;
// final VoidCallback? onProfile;
final void Function(TapDownDetails details)? onProfile;
final VoidCallback? onNotifications;
final int notificationCount;
@ -33,7 +34,11 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
children: [
if (isMobile)
IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.black87, size: 18),
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.black87,
size: 18,
),
onPressed: onBack ?? () => Navigator.pop(context),
)
else if (onMenuPressed != null)
@ -56,10 +61,14 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
onPressed: onNotifications,
),
if (!isMobile) ...[
IconButton(
icon: const Icon(Icons.person_outline, color: Colors.black87),
onPressed: onProfile,
GestureDetector(
onTapDown: (details) => onProfile!(details),
child: const Icon(Icons.person_outline, color: Colors.black87),
),
// IconButton(
// icon: const Icon(Icons.person_outline, color: Colors.black87),
// onPressed: onProfile,
// ),
IconButton(
icon: const Icon(Icons.logout, color: Colors.black87),
onPressed: onLogout,

View File

@ -91,6 +91,7 @@ flutter:
- assets/login/
- assets/drawer/
- assets/miscellaneous/
- assets/dashboard/
# To add assets to your application, add an assets section, like this:
# assets: