611 lines
21 KiB
Dart
611 lines
21 KiB
Dart
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:google_fonts/google_fonts.dart';
|
||
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
|
||
import 'package:nhance_partner/presentation/screens/UserManagement/Staff/staff.dart';
|
||
|
||
import '../../../../core/routing/routes.dart';
|
||
import '../../../../core/services/api_service.dart';
|
||
import '../../../../data/utils/Pagination.dart';
|
||
import '../../../layouts/main_layout.dart';
|
||
import '../../../layouts/responsive_layout.dart';
|
||
import '../../../providers/manager_provider.dart';
|
||
import '../../../themes/indicators/export_btn.dart';
|
||
import '../../../themes/indicators/filter_btn.dart';
|
||
import '../../../themes/indicators/search_field_theme.dart';
|
||
import '../../../themes/indicators/text_field_theme.dart';
|
||
import '../../../widgets/custom_action_popup.dart';
|
||
|
||
class StaffList extends ConsumerStatefulWidget {
|
||
const StaffList({super.key});
|
||
@override
|
||
ConsumerState<StaffList> createState() => StaffListState();
|
||
}
|
||
|
||
class StaffListState extends ConsumerState<StaffList> {
|
||
int currentPage = 1;
|
||
int itemsPerPage = 10;
|
||
late ApiService apiService;
|
||
dynamic managerId;
|
||
dynamic role;
|
||
dynamic prefid;
|
||
// 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();
|
||
|
||
Future.microtask(() {
|
||
final data1 = ref.read(managerIdProvider);
|
||
// final data2 = ref.read(handlerIdProvider);
|
||
print("Edata1 => mId: $data1 -2 :");
|
||
prefid = data1;
|
||
role = ref.read(userRoleProvider);
|
||
print("E43 => mId: $prefid");
|
||
print("roleSTAFFLIST: $role");
|
||
if (prefid != null && role != null) {
|
||
getStaffList(prefid, role);
|
||
}
|
||
});
|
||
// getStaffList();
|
||
}
|
||
|
||
void refresh() {
|
||
if (prefid != null && role != null) {
|
||
getStaffList(prefid, role);
|
||
}
|
||
}
|
||
|
||
//
|
||
// @override
|
||
// void didChangeDependencies() {
|
||
// super.didChangeDependencies();
|
||
// final id = ref.watch(managerIdProvider);
|
||
// if (id != null) {
|
||
// getStaffList(id);
|
||
// }
|
||
// }
|
||
|
||
List<dynamic> get _paginatedData {
|
||
// Sort descending by id first
|
||
final sortedData = [...filteredData]
|
||
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||
|
||
if (sortedData.isEmpty) return [];
|
||
|
||
// Ensure currentPage is valid
|
||
final maxPage = (sortedData.length / itemsPerPage).ceil();
|
||
final safePage = currentPage.clamp(1, maxPage);
|
||
|
||
final startIndex = (safePage - 1) * itemsPerPage;
|
||
final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length);
|
||
|
||
return sortedData.sublist(startIndex, endIndex);
|
||
}
|
||
|
||
// void 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();
|
||
// });
|
||
// }
|
||
|
||
void filterData(String query) {
|
||
setState(() {
|
||
final q = query.toLowerCase();
|
||
|
||
if (q.isEmpty) {
|
||
filteredData = getStaffData;
|
||
return;
|
||
}
|
||
|
||
filteredData = getStaffData.where((item) {
|
||
final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive";
|
||
|
||
return (item['name'] ?? '').toString().toLowerCase().contains(q) ||
|
||
(item['email'] ?? '').toString().toLowerCase().contains(q) ||
|
||
(item['mobile'] ?? '').toString().toLowerCase().contains(q) ||
|
||
(item['address'] ?? '').toString().toLowerCase().contains(q) ||
|
||
(item['role'] ?? '').toString().toLowerCase().contains(q) ||
|
||
(item['emp_id'] ?? item['agent_code'] ?? '')
|
||
.toString()
|
||
.toLowerCase()
|
||
.contains(q) ||
|
||
isActiveStatus.contains(q);
|
||
}).toList();
|
||
});
|
||
}
|
||
|
||
final TextEditingController _searchStaffController = TextEditingController();
|
||
|
||
Future<void> getStaffList(int id, role) async {
|
||
print('E104 => Fns called => $id');
|
||
print('E104 => Fns role => $role');
|
||
setState(() {
|
||
isLoading = true;
|
||
});
|
||
|
||
try {
|
||
final response = await apiService.fetchStaffUserList(id, role);
|
||
|
||
if (response['status'] == 'success') {
|
||
print('E113 => 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;
|
||
});
|
||
}
|
||
}
|
||
|
||
Future<void> showStaff({required String id}) {
|
||
print('showAgent : $id');
|
||
return showDialog(
|
||
context: context,
|
||
builder: (ctx) => Staff(
|
||
id: id,
|
||
onSubmit: (value) {
|
||
debugPrint("New Claims: $value");
|
||
refresh();
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||
return [
|
||
GestureDetector(
|
||
onTap: () {
|
||
Navigator.pop(context);
|
||
print('EDITStaff - ${data['id']}');
|
||
dynamic id = data['id'];
|
||
context.go('/staff/$id');
|
||
},
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
|
||
SizedBox(width: 10),
|
||
Text('Edit'),
|
||
],
|
||
),
|
||
),
|
||
];
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
managerId = ref.watch(managerIdProvider);
|
||
|
||
return MainLayout(
|
||
title: "Staff",
|
||
body: SelectionArea(
|
||
child:Container(
|
||
// padding: EdgeInsets.all(8.0),
|
||
// margin: EdgeInsets.all(10.0),
|
||
|
||
// color: Colors.yellow.shade50,
|
||
width: MediaQuery.of(context).size.width,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Expanded(
|
||
child: Container(
|
||
// color: Colors.green,
|
||
// color: Colors.green.shade50,
|
||
width: MediaQuery.of(context).size.width,
|
||
// margin: EdgeInsets.all(10.0),
|
||
decoration: BoxDecoration(
|
||
// color: Colors.white,
|
||
borderRadius: BorderRadius.circular(15.0),
|
||
),
|
||
|
||
padding: EdgeInsets.all(8.0),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
// height: 40,
|
||
// color: Colors.pink,
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
'Staff',
|
||
style: GoogleFonts.poppins(
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
Spacer(),
|
||
ThemedSearchField(
|
||
hintText: 'Search',
|
||
// backgroundColor: Color(0xFFF6F8F8),
|
||
backgroundColor: Color(0xFFFFFFFF),
|
||
txtHeight: 30,
|
||
onChanged: filterData,
|
||
controller: _searchStaffController,
|
||
txtwidth: MediaQuery.of(context).size.width * 0.15,
|
||
),
|
||
|
||
SizedBox(width: 10),
|
||
ExportBtn(
|
||
sheetName: "Staff",
|
||
fileName: "staff_list",
|
||
txt: !ResponsiveLayout.isMobile(context)
|
||
? true
|
||
: false,
|
||
data: filteredData,
|
||
displayHeaders: [
|
||
'S.No.',
|
||
'Staff Name',
|
||
'Role',
|
||
'Email',
|
||
'Phone Number',
|
||
'Status',
|
||
],
|
||
keys: [
|
||
"sno", // handled internally as i + 1
|
||
"name",
|
||
"role",
|
||
"email",
|
||
"mobile",
|
||
"is_active",
|
||
],
|
||
),
|
||
|
||
SizedBox(width: 10),
|
||
InkWell(
|
||
onTap: () {
|
||
// context.go('/staff/create');
|
||
|
||
showStaff(id: 'Create');
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.all(4.8),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF2E7D6E),
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Tooltip(
|
||
message: 'Create Staff',
|
||
child: Icon(
|
||
Icons.add,
|
||
color: Colors.white,
|
||
size: 18,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 5),
|
||
Container(
|
||
decoration: BoxDecoration(
|
||
color: Color(0xFFF1F5F9),
|
||
|
||
// color: Color(0xFFEDF6F5),
|
||
borderRadius: BorderRadius.circular(6),
|
||
),
|
||
padding: const EdgeInsets.symmetric(
|
||
vertical: 10,
|
||
horizontal: 16,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('S.No.', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Staff Name', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Role', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 3,
|
||
child: Text('Email', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Phone Number', style: _headerStyle),
|
||
),
|
||
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Status', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Action', style: _headerStyle),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Expanded(child: _buildDataTable(context)),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
|
||
Container(
|
||
// height: 20,
|
||
width: MediaQuery.of(context).size.width,
|
||
// color: Colors.green.shade50,
|
||
child: PaginationControls(
|
||
currentPage: currentPage,
|
||
itemsPerPage: itemsPerPage,
|
||
// totalItems: dataVal.length,
|
||
totalItems: filteredData.length,
|
||
// activeColor: layoutColor, // your theme color
|
||
onPageChanged: (page) {
|
||
setState(() {
|
||
currentPage = page;
|
||
});
|
||
},
|
||
onItemsPerPageChanged: (items) {
|
||
setState(() {
|
||
itemsPerPage = items;
|
||
currentPage = 1;
|
||
});
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),)
|
||
);
|
||
}
|
||
|
||
Widget _buildDataTable(BuildContext context) {
|
||
if (filteredData.isEmpty) {
|
||
return const SizedBox(
|
||
height: 50,
|
||
child: Center(child: Text('No available data')),
|
||
);
|
||
}
|
||
|
||
final sortedData = [..._paginatedData];
|
||
|
||
return ListView.builder(
|
||
// itemCount: filteredData.length + 1, // +1 for header, +1 for pagination
|
||
itemCount: sortedData.length + 1, // +1 for header, +1 for pagination
|
||
itemBuilder: (context, index) {
|
||
if (index == 0) return _buildHeader();
|
||
// if (index == dataVal.length + 1)
|
||
// return _buildPagination(context);
|
||
final startIndex = ((currentPage - 1) * itemsPerPage);
|
||
// final item = filteredData[index - 1];
|
||
final item = sortedData[index - 1];
|
||
final sno = startIndex + index;
|
||
return _buildDataRow(item, sno);
|
||
},
|
||
);
|
||
}
|
||
|
||
Widget _buildHeader() {
|
||
return SizedBox.shrink();
|
||
}
|
||
|
||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||
return Container(
|
||
padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 16),
|
||
// margin: const EdgeInsets.only(top: 10),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
// color: Color(0xFFE0F7F9),
|
||
border: const Border(
|
||
bottom: BorderSide(color: Colors.blueGrey, width: 0.15),
|
||
),
|
||
// borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)),
|
||
Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)),
|
||
Expanded(flex: 1, child: Text(item['role'] ?? '-', style: _dataBold)),
|
||
|
||
Expanded(
|
||
flex: 3,
|
||
child: Text(item['email'] ?? '-', style: _dataBold),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text(item['mobile'] ?? '-', style: _dataBold),
|
||
),
|
||
|
||
Expanded(
|
||
flex: 1,
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
// color: Colors.yellow.shade50,
|
||
child: Transform.scale(
|
||
scale: 0.4, // reduce size (0.7–0.9 works well)
|
||
child: Switch(
|
||
value: item['is_active'] == "1",
|
||
onChanged: (val) {
|
||
setState(() {
|
||
item['is_active'] = val ? "1" : "0";
|
||
});
|
||
final response = apiService.updateStatus(
|
||
item['id'],
|
||
val ? "0" : "1",
|
||
'staff',
|
||
);
|
||
print("Response - $response");
|
||
},
|
||
activeColor: Color(0xFF2E7D6E), // thumb when active
|
||
activeTrackColor: Color(0xFFDCFCE7), // track when active
|
||
// 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: item['is_active'] == "1"
|
||
? Row(
|
||
children: [
|
||
// GestureDetector(
|
||
// onTap: () {
|
||
// // Navigator.pop(context);
|
||
// print('EDITStaff - ${item['id']}');
|
||
// dynamic id = item['id'];
|
||
// context.go('/staff/$id');
|
||
// },
|
||
// child: Image.asset(
|
||
// "assets/miscellaneous/Edit.png",
|
||
// height: 15,
|
||
// width: 15,
|
||
// ),
|
||
// ),
|
||
Tooltip(
|
||
message: 'Edit',
|
||
child: IconButton(
|
||
icon: Image.asset(
|
||
"assets/miscellaneous/Edit.png",
|
||
height: 12,
|
||
width: 15,
|
||
),
|
||
onPressed: () {
|
||
print('EDITStaff - ${item['id']}');
|
||
dynamic id = item['id'];
|
||
|
||
showStaff(id: id);
|
||
// context.go('/staff/$id');
|
||
},
|
||
splashRadius: 28,
|
||
hoverColor: Colors.black12,
|
||
padding: const EdgeInsets.all(8),
|
||
constraints: const BoxConstraints(),
|
||
),
|
||
),
|
||
],
|
||
)
|
||
: Row(
|
||
children: [
|
||
Image.asset(
|
||
"assets/miscellaneous/Edit_muted.png",
|
||
height: 12,
|
||
width: 15,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
// 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) => [
|
||
// if (item['is_active'] == "1")
|
||
// 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 = GoogleFonts.inter(
|
||
fontSize: 12,
|
||
|
||
fontWeight: FontWeight.w400,
|
||
color: Color(0xFF000000),
|
||
);
|
||
|
||
static final _dataSub = GoogleFonts.inter(
|
||
fontSize: 10,
|
||
fontWeight: FontWeight.w300,
|
||
color: Color(0xFF585757),
|
||
);
|
||
|
||
static final _headerStyle = GoogleFonts.poppins(
|
||
fontSize: 11.2,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1E293B),
|
||
);
|
||
}
|