This commit is contained in:
Surendiran 2026-04-04 18:20:57 +05:30
commit f1a57d1d8c
5 changed files with 117 additions and 149 deletions

View File

@ -2192,13 +2192,26 @@ class ApiService {
return response; return response;
} }
Future<Map<String, dynamic>> getPayoutList() async { Future<Map<String, dynamic>> getPayoutList({
String? fromDate,
String? toDate,
}) async {
// print(_token); // print(_token);
if (_token == null) { if (_token == null) {
await _initializeToken(); await _initializeToken();
} }
final url = Uri.parse('${Env.apiUrl}invoice/list'); final Uri url;
if (fromDate != null &&
fromDate.isNotEmpty &&
toDate != null &&
toDate.isNotEmpty) {
url = Uri.parse('${Env.apiUrl}invoice/list').replace(
queryParameters: {'from_date': fromDate, 'to_date': toDate},
);
} else {
url = Uri.parse('${Env.apiUrl}invoice/list');
}
final headers = { final headers = {
'Authorization': 'Bearer $_token' ?? '', 'Authorization': 'Bearer $_token' ?? '',

View File

@ -70,7 +70,12 @@ class _PayoutListState extends ConsumerState<PayoutList> {
}); });
try { try {
final response = await apiService.getPayoutList(); final from = controllers['startDate']?.text.trim() ?? '';
final to = controllers['endDate']?.text.trim() ?? '';
final response = await apiService.getPayoutList(
fromDate: from.isNotEmpty ? from : null,
toDate: to.isNotEmpty ? to : null,
);
if (response['status'] == 'success') { if (response['status'] == 'success') {
print('PL 3 => getPayoutList - ${response['data']}'); print('PL 3 => getPayoutList - ${response['data']}');
@ -78,7 +83,8 @@ class _PayoutListState extends ConsumerState<PayoutList> {
print(' PL 4 => getPayoutList => ${response['data']}'); print(' PL 4 => getPayoutList => ${response['data']}');
setState(() { setState(() {
currentPage = 1;
if (data is List) { if (data is List) {
// Already a list of maps // Already a list of maps
getPayoutData = List<Map<String, dynamic>>.from(data); getPayoutData = List<Map<String, dynamic>>.from(data);
@ -145,7 +151,9 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return sortedData.sublist(startIndex, endIndex); return sortedData.sublist(startIndex, endIndex);
} }
void filterDateRange() {} void filterDateRange() {
getPayoutList();
}
void refrshfilterDateRange() { void refrshfilterDateRange() {
setState(() { setState(() {
@ -166,22 +174,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
final q = query.toLowerCase(); final q = query.toLowerCase();
setState(() { setState(() {
filteredData = getPayoutData.where((item) { filteredData = getPayoutData.where((item) {
// Search against the same display strings as the table (not raw API values). // Search matches visible table columns only (broker / referer / updated hidden).
final updatedAtSearch = _exportUpdatedAtForExcel(item['updated_at'])
.toLowerCase();
final invoiceDateSearch = _formatDateSafe(item['invoice_date']) final invoiceDateSearch = _formatDateSafe(item['invoice_date'])
.toLowerCase(); .toLowerCase();
final statusSearch = final statusSearch =
_exportPayoutStatusLabel(item['payout_status']).toLowerCase(); _exportPayoutStatusLabel(item['payout_status']).toLowerCase();
return updatedAtSearch.contains(q) || return invoiceDateSearch.contains(q) ||
invoiceDateSearch.contains(q) ||
(item['invoice_no'] ?? '-').toLowerCase().contains(q) || (item['invoice_no'] ?? '-').toLowerCase().contains(q) ||
(item['invoice_amount_indian_format'] ?? '-') (item['invoice_amount_indian_format'] ?? '-')
.toLowerCase() .toLowerCase()
.contains(q) || .contains(q) ||
(item['broker_name'] ?? '-').toLowerCase().contains(q) ||
(item['partner_names'] ?? '-').toLowerCase().contains(q) ||
(item['utr_numbers'] ?? '-').toLowerCase().contains(q) || (item['utr_numbers'] ?? '-').toLowerCase().contains(q) ||
(item['invoiced_amount'] ?? '0').toLowerCase().contains(q) || (item['invoiced_amount'] ?? '0').toLowerCase().contains(q) ||
(item['payout_amount'] ?? '0').toLowerCase().contains(q) || (item['payout_amount'] ?? '0').toLowerCase().contains(q) ||
@ -214,15 +217,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
} }
} }
String _formatTime(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('HH:mm').format(dateTime);
} catch (e) {
return '-';
}
}
/// Excel export: match table (1 = Pending, 2 = Completed). /// Excel export: match table (1 = Pending, 2 = Completed).
String _exportPayoutStatusLabel(dynamic v) { String _exportPayoutStatusLabel(dynamic v) {
if (v == null) return '-'; if (v == null) return '-';
@ -233,19 +227,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return 'Completed'; return 'Completed';
} }
/// Excel export: same as table dd-MM-yyyy + 24h time in one cell.
String _exportUpdatedAtForExcel(dynamic v) {
if (v == null) return '-';
final raw = v.toString().trim();
if (raw.isEmpty || raw.toLowerCase() == 'null') return '-';
try {
final dateTime = DateTime.parse(raw);
return '${DateFormat('dd-MM-yyyy').format(dateTime)} ${DateFormat('HH:mm').format(dateTime)}';
} catch (_) {
return raw;
}
}
String _displayText(dynamic value) { String _displayText(dynamic value) {
if (value == null) return '-'; if (value == null) return '-';
final text = value.toString().trim(); final text = value.toString().trim();
@ -258,18 +239,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
return '${value.substring(0, maxChars)}...'; return '${value.substring(0, maxChars)}...';
} }
String _partnerTooltipText(dynamic value) {
final text = _displayText(value);
if (text == '-') return text;
final items = text
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
if (items.isEmpty) return text;
return items.join('\n');
}
String _utrTooltipText(dynamic value) { String _utrTooltipText(dynamic value) {
final text = _displayText(value); final text = _displayText(value);
if (text == '-') return text; if (text == '-') return text;
@ -685,14 +654,36 @@ class _PayoutListState extends ConsumerState<PayoutList> {
); );
} }
Widget _payoutInvoiceDateFilter(BuildContext context) {
return DateFilterRow(
compactDateFiltersOnly: true,
dataFrom: 'Payout',
role: roleId,
id: userId,
selectedStaffId: null,
startController: controllers['startDate']!,
endController: controllers['endDate']!,
onStatusChanged: (_) {},
formKey: _formKey,
isMobile: ResponsiveLayout.isMobile(context),
onFilter: filterDateRange,
onRefresh: refrshfilterDateRange,
);
}
Widget _buildContent(BuildContext context) { Widget _buildContent(BuildContext context) {
return Column( return Column(
children: [ children: [
if (ResponsiveLayout.isMobile(context))
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _payoutInvoiceDateFilter(context),
),
Container( Container(
// height: 40, // height: 40,
// color: Colors.pink, // color: Colors.pink,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Text( Text(
@ -702,8 +693,20 @@ class _PayoutListState extends ConsumerState<PayoutList> {
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
if (!ResponsiveLayout.isMobile(context)) ...[
Spacer(), const SizedBox(width: 16),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _payoutInvoiceDateFilter(context),
),
),
),
] else ...[
const Spacer(),
],
ThemedSearchField( ThemedSearchField(
hintText: 'Search', hintText: 'Search',
// backgroundColor: Color(0xFFF6F8F8), // backgroundColor: Color(0xFFF6F8F8),
@ -753,31 +756,24 @@ class _PayoutListState extends ConsumerState<PayoutList> {
"S.No", "S.No",
"Invoice No", "Invoice No",
"Invoice Date", "Invoice Date",
"Broker Name", "UTR Number",
"Referer",
"UTR Details",
"Invoiced Amount", "Invoiced Amount",
"Payout Amount", "Payout Amount",
"Balance Amount", "Balance Amount",
"Status", "Status",
"Updated Date",
], ],
keys: [ keys: [
"id", "id",
"invoice_no", "invoice_no",
"invoice_date", "invoice_date",
"broker_name",
"partner_names",
"utr_numbers", "utr_numbers",
"invoiced_amount", "invoiced_amount",
"payout_amount", "payout_amount",
"balance_amount", "balance_amount",
"payout_status", "payout_status",
"updated_at",
], ],
valueFormatters: { valueFormatters: {
'payout_status': (v, _) => _exportPayoutStatusLabel(v), 'payout_status': (v, _) => _exportPayoutStatusLabel(v),
'updated_at': (v, _) => _exportUpdatedAtForExcel(v),
}, },
), ),
], ],
@ -801,29 +797,17 @@ class _PayoutListState extends ConsumerState<PayoutList> {
flex: 3, flex: 3,
child: Text('Invoice No', style: _headerStyle), child: Text('Invoice No', style: _headerStyle),
), ),
Expanded(flex: 2, child: Text('UTR Number', style: _headerStyle)),
Expanded(flex: 2, child: Text('Invoiced Amount', style: _headerStyle),),
Expanded( Expanded(
flex: 2, flex: 2,
child: Text('Broker Name', style: _headerStyle), child: Text('Payout Amount', style: _headerStyle),
),
Expanded(flex: 3, child: Text('Referer', style: _headerStyle)),
Expanded(flex: 2, child: Text('UTR Details', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Invoiced\nAmount', style: _headerStyle),
), ),
Expanded( Expanded(
flex: 2, flex: 2,
child: Text('Payout\nAmount', style: _headerStyle), child: Text('Balance Amount', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Balance\nAmount', style: _headerStyle),
), ),
Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded(flex: 2, child: Text('Status', style: _headerStyle)),
Expanded(
flex: 2,
child: Text('Updated Date', style: _headerStyle),
),
Expanded(flex: 2, child: Text('Action', style: _headerStyle)), Expanded(flex: 2, child: Text('Action', style: _headerStyle)),
], ],
), ),
@ -897,29 +881,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
maxLines: 3, maxLines: 3,
), ),
), ),
Expanded(
flex: 2,
child: Text(
_displayText(item['broker_name']),
style: _dataBold,
softWrap: true,
maxLines: 3,
),
),
Expanded(
flex: 3,
child: Tooltip(
message: _partnerTooltipText(item['partner_names']),
waitDuration: const Duration(milliseconds: 250),
child: Text(
_truncateText(_displayText(item['partner_names']), maxChars: 25),
style: _dataBold,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
Expanded( Expanded(
flex: 2, flex: 2,
child: Tooltip( child: Tooltip(
@ -976,30 +937,6 @@ class _PayoutListState extends ConsumerState<PayoutList> {
), ),
), ),
Expanded(
flex: 2,
child: item['updated_at'] != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_formatDate(item['updated_at']),
style: _dataBold,
softWrap: false,
maxLines: 1,
),
Text(
_formatTime(item['updated_at']),
style: _dataBold,
softWrap: false,
maxLines: 1,
),
],
)
: Text('-', style: _dataBold),
),
// ACTION ICONS (Edit + Delete) // ACTION ICONS (Edit + Delete)
Expanded( Expanded(
flex: 2, flex: 2,

View File

@ -61,6 +61,14 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
return v == 'no'; return v == 'no';
} }
/// UI / export: capitalize first letter, lowercase the rest (e.g. `yes` `Yes`).
String _receivedLabel(dynamic v) {
final s = v?.toString().trim() ?? '';
if (s.isEmpty) return '-';
return s[0].toUpperCase() +
(s.length > 1 ? s.substring(1).toLowerCase() : '');
}
String? _agentIdForApi() { String? _agentIdForApi() {
if (_isAgentRole()) { if (_isAgentRole()) {
final uid = ref.read(userIdProvider); final uid = ref.read(userIdProvider);
@ -793,6 +801,9 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
'utr_no', 'utr_no',
'received_or_not', 'received_or_not',
], ],
valueFormatters: {
'received_or_not': (v, _) => _receivedLabel(v),
},
); );
if (mounted) { if (mounted) {
ToastHelper.showSuccessToast(context, 'Export finished'); ToastHelper.showSuccessToast(context, 'Export finished');
@ -974,7 +985,7 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
_buildSearchAndActions(context), _buildSearchAndActions(context),
], ],
); );
} }
Widget _tableHeader() { Widget _tableHeader() {
return Container( return Container(
@ -1103,7 +1114,7 @@ class _PayoutReportScreenState extends ConsumerState<PayoutReportScreen> {
Expanded( Expanded(
flex: 2, flex: 2,
child: Text( child: Text(
_cell(item, ['received_or_not']), _receivedLabel(item['received_or_not']),
style: rowStyle, style: rowStyle,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -54,6 +54,9 @@ class DateFilterRow extends ConsumerStatefulWidget {
final VoidCallback onRefresh; final VoidCallback onRefresh;
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
final bool isMobile; final bool isMobile;
/// When true, only start/end date fields and filter/refresh actions are shown
/// (no status, staff, insurer, or partner controls).
final bool compactDateFiltersOnly;
final role; final role;
final id; final id;
@ -77,6 +80,7 @@ class DateFilterRow extends ConsumerStatefulWidget {
required this.role, required this.role,
required this.id, required this.id,
this.isMobile = false, this.isMobile = false,
this.compactDateFiltersOnly = false,
this.dataFrom, this.dataFrom,
}); });
@ -116,6 +120,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
// final handlerId = ref.watch(handlerIdProvider); // final handlerId = ref.watch(handlerIdProvider);
role = ref.watch(userRoleProvider); role = ref.watch(userRoleProvider);
print("managerId - $managerId"); print("managerId - $managerId");
if (widget.compactDateFiltersOnly) return;
if (userID != null && role != null) { if (userID != null && role != null) {
print('hansles'); print('hansles');
getStaffDetails(userID); getStaffDetails(userID);
@ -235,7 +240,8 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.role == 'Accounts' && if (!widget.compactDateFiltersOnly &&
widget.role == 'Accounts' &&
widget.dataFrom == 'Policy' && widget.dataFrom == 'Policy' &&
filteredPartnerData.isEmpty && filteredPartnerData.isEmpty &&
!_partnerFetchAttempted) { !_partnerFetchAttempted) {
@ -275,23 +281,24 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
buildEndDate(context), buildEndDate(context),
SizedBox(width: spacing), SizedBox(width: spacing),
if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[ if (!widget.compactDateFiltersOnly) ...[
buildStatusSearch(context,widget.role), if (widget.dataFrom == null && widget.dataFrom != 'Policy') ...[
SizedBox(width: spacing), buildStatusSearch(context, widget.role),
], SizedBox(width: spacing),
],
if (widget.role == 'manager' || widget.role == 'handler') ...[ if (widget.role == 'manager' || widget.role == 'handler') ...[
buildSelectStaffMem(context), buildSelectStaffMem(context),
SizedBox(width: spacing), SizedBox(width: spacing),
],
if (widget.role == 'Accounts' && widget.dataFrom == 'Policy') ...[
buildSelectPartnerAccountsPolicy(context),
SizedBox(width: spacing),
buildPolicyReportFlagSearch(context),
SizedBox(width: spacing),
buildSelectInsurer(context),
],
], ],
if (widget.role == 'Accounts' && widget.dataFrom == 'Policy') ...[
buildSelectPartnerAccountsPolicy(context),
SizedBox(width: spacing),
buildPolicyReportFlagSearch(context),
SizedBox(width: spacing),
buildSelectInsurer(context),
],
]; ];
final buttons = [ final buttons = [

View File

@ -117,10 +117,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.1"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -780,26 +780,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.17" version: "0.12.19"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.1" version: "0.13.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.17.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -1297,10 +1297,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" version: "0.7.10"
toastification: toastification:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1502,5 +1502,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.8.1 <4.0.0" dart: ">=3.9.0-0 <4.0.0"
flutter: ">=3.32.0" flutter: ">=3.32.0"