997 lines
36 KiB
Dart
997 lines
36 KiB
Dart
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:file_picker/file_picker.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:google_fonts/google_fonts.dart';
|
||
|
||
import '../../../../core/routing/routes.dart';
|
||
import '../../../../core/services/api_service.dart';
|
||
import '../../../../data/utils/Pagination.dart';
|
||
import '../../../../data/utils/toastNotification.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';
|
||
import 'agentIncentiveFile.dart';
|
||
|
||
class AgentList extends ConsumerStatefulWidget {
|
||
const AgentList({super.key});
|
||
@override
|
||
ConsumerState<AgentList> createState() => AgentListState();
|
||
}
|
||
|
||
class AgentListState extends ConsumerState<AgentList> {
|
||
int currentPage = 1;
|
||
int itemsPerPage = 10;
|
||
late ApiService apiService;
|
||
dynamic managerId;
|
||
dynamic selected_id;
|
||
// List<Map<String, dynamic>> dataVal = [];
|
||
List<Map<String, dynamic>> getAgentData = [];
|
||
List<Map<String, dynamic>> originalData = [];
|
||
List<Map<String, dynamic>> filteredData = [];
|
||
bool isLoading = false;
|
||
bool isRetentionImportLoading = false;
|
||
dynamic sortedData;
|
||
dynamic managerID;
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
apiService = ApiService();
|
||
// getAgentList();
|
||
|
||
Future.microtask(() {
|
||
managerID = ref.read(managerIdProvider);
|
||
if (managerID != null) {
|
||
getAgentList(managerID);
|
||
}
|
||
});
|
||
}
|
||
|
||
void refresh() {
|
||
if (managerID != null) {
|
||
getAgentList(managerID);
|
||
}
|
||
}
|
||
|
||
// @override
|
||
// void didChangeDependencies() {
|
||
// super.didChangeDependencies();
|
||
// final id = ref.watch(managerIdProvider);
|
||
// if (id != null) {
|
||
// getAgentList(id);
|
||
// }
|
||
// }
|
||
|
||
// List<dynamic> get _paginatedData {
|
||
// final startIndex = (currentPage - 1) * itemsPerPage;
|
||
// final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length);
|
||
// return filteredData.sublist(startIndex, endIndex);
|
||
// }
|
||
|
||
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) {
|
||
setState(() {
|
||
filteredData = getAgentData.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['agent_code'] ?? '-').toLowerCase().contains(
|
||
query.toLowerCase(),
|
||
) ||
|
||
(item['sales_executive_name'] ?? '-').toLowerCase().contains(
|
||
query.toLowerCase(),
|
||
) ||
|
||
(isActiveStatus.contains(query.toLowerCase()));
|
||
}).toList();
|
||
});
|
||
}
|
||
|
||
final TextEditingController _searchController = TextEditingController();
|
||
|
||
Future<void> getAgentList(int managerId) async {
|
||
print('getClaimList called');
|
||
setState(() {
|
||
isLoading = true;
|
||
});
|
||
|
||
try {
|
||
final response = await apiService.fetchAgentUserList(managerId);
|
||
|
||
if (response['status'] == 'success') {
|
||
print('getAgentListData - ${response['data']}');
|
||
setState(() {
|
||
getAgentData = List<Map<String, dynamic>>.from(response['data']);
|
||
// print('API Data - $getClaimPolicies');
|
||
//
|
||
originalData = getAgentData;
|
||
filteredData = List.from(originalData);
|
||
// print('originalData - $getClaimPolicies');
|
||
});
|
||
} else {
|
||
// getClaimPolicies = [];
|
||
// originalData = getClaimPolicies;
|
||
// filteredData = List.from(originalData);
|
||
// print('originalData - $getClaimPolicies');
|
||
// print('Request failed: ${response['code']}');
|
||
}
|
||
} catch (e) {
|
||
print('Exception occurred: $e');
|
||
} finally {
|
||
setState(() {
|
||
isLoading = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
Future<void> _exportRetentionRateExcel() async {
|
||
/****** Export confirmation dialog kept for future use ********/
|
||
// final proceed = await showDialog<bool>(
|
||
// context: context,
|
||
// builder: (ctx) {
|
||
// return AlertDialog(
|
||
// title: const Text('Export Rentation Rate'),
|
||
// content: const Text(
|
||
// 'The downloaded Excel will include input validation.\n\n'
|
||
// 'Allowed values: only numbers from 0 to 100.\n'
|
||
// 'Not allowed: negative values, values above 100, text/special characters.',
|
||
// ),
|
||
// actions: [
|
||
// TextButton(
|
||
// onPressed: () => Navigator.pop(ctx, false),
|
||
// child: const Text('Cancel'),
|
||
// ),
|
||
// ElevatedButton(
|
||
// onPressed: () => Navigator.pop(ctx, true),
|
||
// child: const Text('Download'),
|
||
// ),
|
||
// ],
|
||
// );
|
||
// },
|
||
// );
|
||
// if (proceed != true) return;
|
||
/****** *************************************************** ********/
|
||
|
||
try {
|
||
await apiService.downloadAgentRetentionRateExcel();
|
||
if (mounted) {
|
||
ToastHelper.showSuccessToast(
|
||
context,
|
||
'Retention rate Excel downloaded',
|
||
);
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
ToastHelper.showErrorToast(
|
||
context,
|
||
'Failed to export retention rate Excel',
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
Future<void> _importRetentionRateExcel() async {
|
||
final picked = await FilePicker.platform.pickFiles(
|
||
type: FileType.custom,
|
||
allowedExtensions: const ['xls', 'xlsx'],
|
||
withData: true,
|
||
);
|
||
|
||
if (picked == null || picked.files.isEmpty) {
|
||
return;
|
||
}
|
||
|
||
final file = picked.files.first;
|
||
if (file.bytes == null) {
|
||
if (mounted) {
|
||
ToastHelper.showErrorToast(context, 'Could not read selected file');
|
||
}
|
||
return;
|
||
}
|
||
|
||
setState(() {
|
||
isRetentionImportLoading = true;
|
||
});
|
||
|
||
try {
|
||
final response = await apiService.importAgentRetentionRateExcel(file: file);
|
||
final status = (response['status'] ?? '').toString().toLowerCase();
|
||
if (status == 'success') {
|
||
final data = (response['data'] is Map<String, dynamic>)
|
||
? response['data'] as Map<String, dynamic>
|
||
: <String, dynamic>{};
|
||
final report = (response['report'] is Map<String, dynamic>)
|
||
? response['report'] as Map<String, dynamic>
|
||
: <String, dynamic>{};
|
||
final inserted = (data['inserted_cells'] ?? data['inserted_rows'] ?? 0);
|
||
final updated = (data['updated_cells'] ?? data['updated_rows'] ?? 0);
|
||
final message = (response['message'] ?? '').toString().trim();
|
||
if (mounted) {
|
||
ToastHelper.showSuccessToast(
|
||
context,
|
||
message.isNotEmpty
|
||
? message
|
||
: 'Import done',
|
||
// 'Import done (Inserted: $inserted, Updated: $updated)',
|
||
|
||
);
|
||
}
|
||
// if (report.isNotEmpty && mounted) {
|
||
// await _showRetentionImportReportDialog(data, report);
|
||
// }
|
||
refresh();
|
||
} else {
|
||
if (mounted) {
|
||
final message = (response['message'] ?? 'Import failed').toString();
|
||
ToastHelper.showErrorToast(context, message);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
ToastHelper.showErrorToast(context, 'Failed to import retention rate');
|
||
}
|
||
} finally {
|
||
if (mounted) {
|
||
setState(() {
|
||
isRetentionImportLoading = false;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
Future<void> _showRetentionImportReportDialog(
|
||
Map<String, dynamic> data,
|
||
Map<String, dynamic> report,
|
||
) async {
|
||
if (!mounted) return;
|
||
|
||
final inserted = data['inserted_cells'] ?? 0;
|
||
final updated = data['updated_cells'] ?? 0;
|
||
final skippedZero = data['skipped_zero'] ?? 0;
|
||
final skippedSame = data['skipped_same'] ?? 0;
|
||
final skippedEmptyRow = data['skipped_empty_row'] ?? 0;
|
||
final skippedEmptyCell = data['skipped_empty_cell'] ?? 0;
|
||
final skippedUnknownAgent = data['skipped_unknown_agent'] ?? 0;
|
||
final invalidRange = data['invalid_range'] ?? 0;
|
||
final invalidNumber = data['invalid_number'] ?? 0;
|
||
|
||
final unknownAgents = (report['unknown_agent_codes'] as List?)
|
||
?.map((e) => e.toString())
|
||
.where((e) => e.trim().isNotEmpty)
|
||
.toList() ??
|
||
<String>[];
|
||
final unknownVehicleTypes = ((report['unknown_vehicle_types'] ??
|
||
report['unknown_vehicle_type_names']) as List?)
|
||
?.map((e) => e.toString())
|
||
.where((e) => e.trim().isNotEmpty)
|
||
.toList() ??
|
||
<String>[];
|
||
final unknownSegments = ((report['unknown_segments'] ??
|
||
report['unknown_segment_names']) as List?)
|
||
?.map((e) => e.toString())
|
||
.where((e) => e.trim().isNotEmpty)
|
||
.toList() ??
|
||
<String>[];
|
||
final invalidCells = (report['invalid_cells'] as List?)
|
||
?.map((e) => e.toString())
|
||
.where((e) => e.trim().isNotEmpty)
|
||
.toList() ??
|
||
<String>[];
|
||
final agentWiseSkippedRaw = (report['agent_wise_skipped_counts'] is Map)
|
||
? Map<String, dynamic>.from(report['agent_wise_skipped_counts'] as Map)
|
||
: <String, dynamic>{};
|
||
|
||
final lines = <String>[
|
||
'Inserted: $inserted',
|
||
'Updated: $updated',
|
||
// 'Skipped (value = 0): $skippedZero',
|
||
// 'Skipped (Excel value equals DB value): $skippedSame',
|
||
// 'Skipped (empty first column / agent code): $skippedEmptyRow',
|
||
// 'Skipped (empty cell): $skippedEmptyCell',
|
||
// 'Skipped (unknown agent code): $skippedUnknownAgent',
|
||
'Skipped (out of range 0-100): $invalidRange',
|
||
'Skipped (non-numeric value): $invalidNumber',
|
||
];
|
||
|
||
if (unknownAgents.isNotEmpty) {
|
||
lines.add('');
|
||
lines.add('Unknown Agent Codes:');
|
||
lines.addAll(unknownAgents.take(10).map((e) => '- $e'));
|
||
if (unknownAgents.length > 10) {
|
||
lines.add('- ...and ${unknownAgents.length - 10} more');
|
||
}
|
||
}
|
||
|
||
if (unknownVehicleTypes.isNotEmpty) {
|
||
lines.add('');
|
||
lines.add('Unknown Vehicle Types:');
|
||
lines.addAll(unknownVehicleTypes.take(10).map((e) => '- $e'));
|
||
if (unknownVehicleTypes.length > 10) {
|
||
lines.add('- ...and ${unknownVehicleTypes.length - 10} more');
|
||
}
|
||
}
|
||
|
||
if (unknownSegments.isNotEmpty) {
|
||
lines.add('');
|
||
lines.add('Unknown Segments:');
|
||
lines.addAll(unknownSegments.take(10).map((e) => '- $e'));
|
||
if (unknownSegments.length > 10) {
|
||
lines.add('- ...and ${unknownSegments.length - 10} more');
|
||
}
|
||
}
|
||
|
||
if (invalidCells.isNotEmpty) {
|
||
lines.add('');
|
||
lines.add('Invalid Cell Samples:');
|
||
lines.addAll(invalidCells.take(10).map((e) => '- $e'));
|
||
if (invalidCells.length > 10) {
|
||
lines.add('- ...and ${invalidCells.length - 10} more');
|
||
}
|
||
}
|
||
|
||
if (agentWiseSkippedRaw.isNotEmpty) {
|
||
final sorted = agentWiseSkippedRaw.entries.toList()
|
||
..sort((a, b) {
|
||
final av = int.tryParse(a.value.toString()) ?? 0;
|
||
final bv = int.tryParse(b.value.toString()) ?? 0;
|
||
return bv.compareTo(av);
|
||
});
|
||
final nonZero = sorted
|
||
.where((e) => (int.tryParse(e.value.toString()) ?? 0) > 0)
|
||
.toList();
|
||
if (nonZero.isNotEmpty) {
|
||
lines.add('');
|
||
lines.add('Skipped Count (Agent Wise):');
|
||
lines.addAll(
|
||
nonZero
|
||
.take(20)
|
||
.map((e) => '- ${e.key}: ${int.tryParse(e.value.toString()) ?? 0}'),
|
||
);
|
||
if (nonZero.length > 20) {
|
||
lines.add('- ...and ${nonZero.length - 20} more');
|
||
}
|
||
}
|
||
}
|
||
|
||
await showDialog<void>(
|
||
context: context,
|
||
builder: (ctx) {
|
||
return AlertDialog(
|
||
title: const Text('Retention Rate Import Report'),
|
||
content: SizedBox(
|
||
width: 520,
|
||
child: SingleChildScrollView(
|
||
child: SelectableText(lines.join('\n')),
|
||
),
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(ctx),
|
||
child: const Text('OK'),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
|
||
return [
|
||
GestureDetector(
|
||
onTap: () {
|
||
Navigator.pop(context);
|
||
print('EDIT - ${data['id']}');
|
||
dynamic id = data['id'];
|
||
context.go(AppRoutes.agentDetailsFor(id.toString()));
|
||
},
|
||
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: "Partner",
|
||
body: SelectionArea(
|
||
child: Container(
|
||
// color: Colors.yellow.shade50,
|
||
width: MediaQuery.of(context).size.width,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
// Container(
|
||
// // height: 30,
|
||
// // color: Colors.red.shade50,
|
||
// width: MediaQuery.of(context).size.width,
|
||
// child: GestureDetector(
|
||
// onTap: () {
|
||
// context.go(AppRoutes.dashboard);
|
||
// },
|
||
// child: Row(
|
||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||
// mainAxisAlignment: MainAxisAlignment.start,
|
||
// children: [
|
||
// // Tooltip(
|
||
// // message: 'Back',
|
||
// // child: IconButton(
|
||
// // icon: const Icon(
|
||
// // Icons.arrow_left_sharp,
|
||
// // size: 25,
|
||
// // color: Color(0xFF425B5B),
|
||
// // ),
|
||
// // onPressed: () {
|
||
// // context.go(AppRoutes.dashboard);
|
||
// // },
|
||
// // splashRadius: 18,
|
||
// // hoverColor: Colors.black12,
|
||
// // padding: const EdgeInsets.all(4),
|
||
// // constraints: const BoxConstraints(),
|
||
// // ),
|
||
// // ),
|
||
// // const SizedBox(width: 5), // spacing between icon and text
|
||
// Text(
|
||
// "Partner",
|
||
// style: GoogleFonts.poppins(
|
||
// fontSize: 12,
|
||
// fontWeight: FontWeight.w500,
|
||
// ),
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
|
||
// SizedBox(height: 5),
|
||
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: [
|
||
Text(
|
||
"Partner",
|
||
style: GoogleFonts.poppins(
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
Spacer(),
|
||
ThemedSearchField(
|
||
hintText: 'Search',
|
||
backgroundColor: Color(0xFFFFFFFF),
|
||
// backgroundColor: Color(0xFFF6F8F8),
|
||
txtHeight: 30,
|
||
onChanged: filterData,
|
||
controller: _searchController,
|
||
txtwidth: MediaQuery.of(context).size.width * 0.15,
|
||
),
|
||
|
||
SizedBox(width: 10),
|
||
InkWell(
|
||
onTap: _exportRetentionRateExcel,
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 10,
|
||
vertical: 6,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF2E7D6E),
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Icon(
|
||
Icons.file_download_outlined,
|
||
color: Colors.white,
|
||
size: 16,
|
||
),
|
||
SizedBox(width: 6),
|
||
Text(
|
||
'Export Rentation Rate',
|
||
style: GoogleFonts.poppins(
|
||
color: Colors.white,
|
||
fontSize: 11,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
SizedBox(width: 10),
|
||
InkWell(
|
||
onTap: isRetentionImportLoading
|
||
? null
|
||
: _importRetentionRateExcel,
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 10,
|
||
vertical: 6,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF2E7D6E),
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
isRetentionImportLoading
|
||
? SizedBox(
|
||
width: 14,
|
||
height: 14,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 2,
|
||
color: Colors.white,
|
||
),
|
||
)
|
||
: Icon(
|
||
Icons.file_upload_outlined,
|
||
color: Colors.white,
|
||
size: 16,
|
||
),
|
||
SizedBox(width: 6),
|
||
Text(
|
||
'Import Rentation Rate',
|
||
style: GoogleFonts.poppins(
|
||
color: Colors.white,
|
||
fontSize: 11,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
SizedBox(width: 10),
|
||
InkWell(
|
||
onTap: () {
|
||
showUploadIncentiveModal(context);
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.all(5.4),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFF2E7D6E),
|
||
// color: Color(0xFF425B5B),
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
),
|
||
child: Row(
|
||
// mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
// Text(
|
||
// 'Incentive File',
|
||
// style: TextStyle(
|
||
// color: Colors.white,
|
||
// fontWeight: FontWeight.w600,
|
||
// fontSize: 12,
|
||
// ),
|
||
// ),
|
||
// SizedBox(width: 10),
|
||
Tooltip(
|
||
message: 'Incentive File',
|
||
child: Icon(
|
||
Icons.file_open_outlined,
|
||
color: Colors.white,
|
||
size: 18,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
SizedBox(width: 10),
|
||
ExportBtn(
|
||
sheetName: "Partner",
|
||
fileName: "partner_list",
|
||
txt: !ResponsiveLayout.isMobile(context)
|
||
? true
|
||
: false,
|
||
data: filteredData,
|
||
displayHeaders: [
|
||
'S.No.',
|
||
'Partner Id',
|
||
'Partner Name',
|
||
'Email',
|
||
'Sales Executive Name',
|
||
'Phone Number',
|
||
'Address',
|
||
'Status',
|
||
],
|
||
keys: [
|
||
"sno", // handled internally as i + 1
|
||
"agent_code",
|
||
"name",
|
||
"email",
|
||
"sales_executive_name",
|
||
"mobile",
|
||
"address",
|
||
"is_active",
|
||
],
|
||
),
|
||
|
||
SizedBox(width: 10),
|
||
InkWell(
|
||
onTap: () {
|
||
context.go(AppRoutes.agentDetailsFor('new'));
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.all(5.0),
|
||
decoration: BoxDecoration(
|
||
// color: Color(0xFF425B5B),
|
||
color: const Color(0xFF2E7D6E),
|
||
borderRadius: BorderRadius.circular(8.0),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
// Text(
|
||
// 'Create New Partner',
|
||
// style: TextStyle(
|
||
// color: Colors.white,
|
||
// fontWeight: FontWeight.w600,
|
||
// fontSize: 12,
|
||
// ),
|
||
// ),
|
||
// SizedBox(width: 10),
|
||
Tooltip(
|
||
message: 'Create New Partner',
|
||
child: Icon(
|
||
Icons.add,
|
||
color: Colors.white,
|
||
size: 18,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 10),
|
||
|
||
Container(
|
||
// height: 50,
|
||
decoration: BoxDecoration(
|
||
color: Color(0xFFF1F5F9),
|
||
|
||
// color: Color(0xFFEDF6F5),
|
||
borderRadius: BorderRadius.circular(6),
|
||
),
|
||
padding: const EdgeInsets.symmetric(
|
||
vertical: 8,
|
||
horizontal: 16,
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('S.No.', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Partner Id', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Partner Name', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 3,
|
||
child: Text('Email', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Phone Number', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Sales Executive Name', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text('Address', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Status', style: _headerStyle),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Text('Action', style: _headerStyle),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
Expanded(
|
||
child: Container(
|
||
color: Colors.white,
|
||
child: _buildClaimsDataTable(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 _buildClaimsDataTable(BuildContext context) {
|
||
if (filteredData.isEmpty) {
|
||
return const SizedBox(
|
||
height: 50,
|
||
child: Center(child: Text('No available data')),
|
||
);
|
||
}
|
||
|
||
// final sortedData = [..._paginatedData]
|
||
// ..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
|
||
|
||
final sortedData = [..._paginatedData];
|
||
|
||
// 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 const SizedBox.shrink(); // empty widget instead of commenting
|
||
}
|
||
|
||
Widget _buildDataRow(Map<String, dynamic> item, sno) {
|
||
return Container(
|
||
padding: const EdgeInsets.symmetric(vertical: 2, 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: 1, child: Text(item['agent_code'] ?? '-', 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: 2,
|
||
child: Text(item['sales_executive_name'] ?? '-', style: _dataBold),
|
||
),
|
||
Expanded(
|
||
flex: 2,
|
||
child: Text(
|
||
item['address'] ?? '-',
|
||
style: _dataBold,
|
||
softWrap: true,
|
||
maxLines: 3,
|
||
),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: Row(
|
||
children: [
|
||
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",
|
||
'agent',
|
||
);
|
||
print("Response - $response");
|
||
},
|
||
// activeColor: Color(0xFF425B5B),
|
||
// activeTrackColor: Color(0xFFB2D8D3),
|
||
activeColor: Color(0xFF2E7D6E), // thumb when active
|
||
activeTrackColor: Color(0xFFDCFCE7), // track when active
|
||
inactiveThumbColor:
|
||
Colors.grey.shade400, // thumb when inactive
|
||
inactiveTrackColor:
|
||
Colors.grey.shade300, // track when inactive
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Expanded(
|
||
flex: 1,
|
||
child: item['is_active'] == "1"
|
||
? Row(
|
||
children: [
|
||
Tooltip(
|
||
message: 'Edit',
|
||
child: IconButton(
|
||
icon: Image.asset(
|
||
"assets/miscellaneous/Edit.png",
|
||
height: 12,
|
||
width: 15,
|
||
),
|
||
onPressed: () {
|
||
print('EDITStaff - ${item['id']}');
|
||
selected_id = item['id'];
|
||
|
||
context.go(AppRoutes.agentDetailsFor(selected_id.toString()));
|
||
},
|
||
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 = TextStyle(
|
||
fontSize: 10,
|
||
fontWeight: FontWeight.w300,
|
||
color: Color(0xFF585757),
|
||
);
|
||
|
||
static final _headerStyle = GoogleFonts.poppins(
|
||
fontSize: 11.2,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1E293B),
|
||
);
|
||
}
|