FIX_Bug
This commit is contained in:
parent
c35d42f39c
commit
2d98e666a9
@ -103,6 +103,21 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
||||
_bootstrap();
|
||||
}
|
||||
|
||||
/// After create, [context.go] to `/agentDetails/:id` can update [widget.agentId] while
|
||||
/// reusing this [State] — [initState] does not run again, so we must load the new id here.
|
||||
@override
|
||||
void didUpdateWidget(covariant AgentDetailsScreen oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.agentId != widget.agentId && !widget.isCreate) {
|
||||
_reloadAgentForRouteIdChange();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reloadAgentForRouteIdChange() async {
|
||||
await _loadAgent();
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _bootstrap() async {
|
||||
_token = await AuthService.getToken();
|
||||
await _loadSalesExecutives();
|
||||
@ -186,7 +201,11 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
||||
return;
|
||||
}
|
||||
final data = Map<String, dynamic>.from(res['data'] as Map);
|
||||
_loadedAgentPk = data['id']?.toString();
|
||||
final pk = data['id'] ?? data['agent_id'];
|
||||
_loadedAgentPk = pk?.toString().trim();
|
||||
if (_loadedAgentPk != null && _loadedAgentPk!.isEmpty) {
|
||||
_loadedAgentPk = null;
|
||||
}
|
||||
|
||||
_nameCtrl.text = (data['name'] ?? '').toString();
|
||||
_emailCtrl.text = (data['email'] ?? '').toString();
|
||||
@ -317,6 +336,30 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Backend may return the new row id under [data.id], [data.agent_id], or top-level keys.
|
||||
String? _newAgentIdFromCreateBody(dynamic body) {
|
||||
if (body is! Map) return null;
|
||||
final m = Map<String, dynamic>.from(body);
|
||||
final data = m['data'];
|
||||
if (data is Map) {
|
||||
final dm = Map<String, dynamic>.from(data);
|
||||
for (final key in ['agent_id', 'id', 'agentId']) {
|
||||
final v = dm[key];
|
||||
final s = v?.toString().trim();
|
||||
if (s != null && s.isNotEmpty && s != 'null') return s;
|
||||
}
|
||||
} else if (data != null && data is! List) {
|
||||
final s = data.toString().trim();
|
||||
if (s.isNotEmpty && s != 'null') return s;
|
||||
}
|
||||
for (final key in ['agent_id', 'id']) {
|
||||
final v = m[key];
|
||||
final s = v?.toString().trim();
|
||||
if (s != null && s.isNotEmpty && s != 'null') return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _saveRowRetention(int vehicleTypeId) async {
|
||||
if (!_retentionTableEnabled) {
|
||||
ToastHelper.showWarningToast(
|
||||
@ -466,12 +509,9 @@ class _AgentDetailsScreenState extends ConsumerState<AgentDetailsScreen> {
|
||||
return;
|
||||
}
|
||||
if (!isUpdate && mounted) {
|
||||
final data = body['data'];
|
||||
String? newId;
|
||||
if (data is Map && data['agent_id'] != null) {
|
||||
newId = data['agent_id'].toString();
|
||||
}
|
||||
final newId = _newAgentIdFromCreateBody(body);
|
||||
if (newId != null && newId.isNotEmpty) {
|
||||
setState(() => _loadedAgentPk = newId);
|
||||
context.go(AppRoutes.agentDetailsFor(newId));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../core/services/api_service.dart';
|
||||
import '../../providers/manager_provider.dart';
|
||||
@ -137,12 +138,13 @@ class _State extends ConsumerState<PartnerPortalDashboard> {
|
||||
|
||||
double _d(dynamic v) => double.tryParse(v?.toString() ?? '0') ?? 0;
|
||||
|
||||
/// Indian-grouped full rupee amount (no L/Cr/K shorthand).
|
||||
String _inr(dynamic v) {
|
||||
final n = _d(v);
|
||||
if (n >= 10000000) return 'Rs.${(n/10000000).toStringAsFixed(1)}Cr';
|
||||
if (n >= 100000) return 'Rs.${(n/100000).toStringAsFixed(1)}L';
|
||||
if (n >= 1000) return 'Rs.${(n/1000).toStringAsFixed(1)}K';
|
||||
return 'Rs.${n.toStringAsFixed(0)}';
|
||||
if (n == n.roundToDouble()) {
|
||||
return 'Rs.${NumberFormat('#,##,###', 'en_IN').format(n.toInt())}';
|
||||
}
|
||||
return 'Rs.${NumberFormat('#,##,##0.00', 'en_IN').format(n)}';
|
||||
}
|
||||
String _initials(String name) {
|
||||
final p = name.trim().split(' ');
|
||||
@ -218,8 +220,8 @@ class _State extends ConsumerState<PartnerPortalDashboard> {
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// PARTNER PROFILE CARD
|
||||
// Fields: agent_name, agent_code, mobile, email, manager_name, status
|
||||
// All come from partnerDetails() → partner_agent table
|
||||
// Fields: agent_name, agent_code, mobile, email, manager_name, manager_mobile, status
|
||||
// All come from GET partner/{id}/details (partner_agent + manager join)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
class _PartnerProfileCard extends StatelessWidget {
|
||||
final Map<String, dynamic> details; final String initials;
|
||||
@ -231,6 +233,8 @@ class _PartnerProfileCard extends StatelessWidget {
|
||||
final mobile = details['mobile'] ?? '--';
|
||||
final email = details['email'] ?? '--';
|
||||
final manager = details['manager_name'] ?? '--';
|
||||
final managerMobile = details['manager_mobile'] ?? '--';
|
||||
final managerEmail = details['manager_email'] ?? '--';
|
||||
final status = details['status'] ?? 'Active';
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18),
|
||||
@ -249,6 +253,8 @@ class _PartnerProfileCard extends StatelessWidget {
|
||||
_m('Mobile', mobile.toString()),
|
||||
_m('Email', email.toString()),
|
||||
_m('Manager', manager.toString()),
|
||||
_m('Manager Mobile', managerMobile),
|
||||
_m('Manager Email', managerEmail),
|
||||
]),
|
||||
])),
|
||||
const SizedBox(width: 12),
|
||||
@ -438,20 +444,20 @@ class _RenewalAlertsCard extends StatelessWidget {
|
||||
children: [
|
||||
Text('Renewal Alerts', style: _chartHeader),
|
||||
Text(
|
||||
'At-risk policies',
|
||||
'',
|
||||
style: GoogleFonts.inter(fontSize: 9.5, color: const Color(0xFF8AABB5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
if (totalCount > 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Text(
|
||||
'$totalCount total',
|
||||
style: GoogleFonts.inter(fontSize: 10, color: const Color(0xFF8AABB5)),
|
||||
),
|
||||
),
|
||||
// if (totalCount > 0)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(right: 8),
|
||||
// child: Text(
|
||||
// '$totalCount total',
|
||||
// style: GoogleFonts.inter(fontSize: 10, color: const Color(0xFF8AABB5)),
|
||||
// ),
|
||||
// ),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(3),
|
||||
decoration: BoxDecoration(
|
||||
@ -501,13 +507,13 @@ class _RenewalAlertsCard extends StatelessWidget {
|
||||
color: const Color(0xFFE3F9F8),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 88, child: Text('Due', style: _tableHeadStyle)),
|
||||
SizedBox(width: 40, child: Text('S.No', style: _tableHeadStyle)),
|
||||
Expanded(flex: 2, child: Text('Policy No.', style: _tableHeadStyle)),
|
||||
Expanded(flex: 2, child: Text('Holder', style: _tableHeadStyle)),
|
||||
Expanded(flex: 2, child: Text('Expires', style: _tableHeadStyle)),
|
||||
Expanded(flex: 2, child: Text('Policy Holder Name', style: _tableHeadStyle)),
|
||||
Expanded(flex: 2, child: Text('Expiry Date', style: _tableHeadStyle)),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text('Premium', textAlign: TextAlign.right, style: _tableHeadStyle),
|
||||
child: Text('Premium Amount', textAlign: TextAlign.right, style: _tableHeadStyle),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -518,7 +524,11 @@ class _RenewalAlertsCard extends StatelessWidget {
|
||||
physics: const ClampingScrollPhysics(),
|
||||
itemCount: renewals.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1, color: Color(0xFFD9F0ED)),
|
||||
itemBuilder: (_, i) => _RenewalTableRow(renewal: renewals[i], inr: inr),
|
||||
itemBuilder: (_, i) => _RenewalTableRow(
|
||||
serialNo: i + 1,
|
||||
renewal: renewals[i],
|
||||
inr: inr,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -527,52 +537,42 @@ class _RenewalAlertsCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _RenewalTableRow extends StatelessWidget {
|
||||
final int serialNo;
|
||||
final Map<String, dynamic> renewal;
|
||||
final String Function(dynamic) inr;
|
||||
|
||||
const _RenewalTableRow({required this.renewal, required this.inr});
|
||||
const _RenewalTableRow({
|
||||
required this.serialNo,
|
||||
required this.renewal,
|
||||
required this.inr,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final d = int.tryParse(renewal['days_left']?.toString() ?? '0') ?? 0;
|
||||
Color chipBg, chipFg;
|
||||
final Color chipFg;
|
||||
if (d <= 10) {
|
||||
chipBg = const Color(0xFFFFEDED);
|
||||
chipFg = const Color(0xFFEF4444);
|
||||
} else if (d <= 20) {
|
||||
chipBg = const Color(0xFFFEF0E7);
|
||||
chipFg = const Color(0xFFF97316);
|
||||
} else {
|
||||
chipBg = const Color(0xFFF1F5F9);
|
||||
chipFg = const Color(0xFF64748B);
|
||||
}
|
||||
final endDate = renewal['end_date']?.toString() ?? '--';
|
||||
|
||||
return SizedBox(
|
||||
height: _RenewalAlertsCard.kRowHeight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 88,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: chipBg,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
'$d d',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 9.5,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: chipFg,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
width: 40,
|
||||
child: Text(
|
||||
'$serialNo',
|
||||
style: _tableData,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@ -593,18 +593,32 @@ class _RenewalTableRow extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
renewal['end_date']?.toString() ?? '--',
|
||||
style: _tableData,
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
style: _tableData,
|
||||
children: [
|
||||
TextSpan(text: endDate),
|
||||
TextSpan(
|
||||
text: ' ($d d)',
|
||||
style: GoogleFonts.inter(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: chipFg,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
inr(renewal['premium'] ?? 0),
|
||||
inr(renewal['premium'] ?? renewal['premium_amount'] ?? 0),
|
||||
style: _tableDataBold,
|
||||
textAlign: TextAlign.right,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1089,7 +1089,7 @@ class _PayOutDetailsState extends ConsumerState<PayOutDetails> {
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
key: ValueKey(
|
||||
'commission_${p["policy_id"]}_${p["commission_amount"]}',
|
||||
'commission_${p["id"] ?? p["policy_id"]}',
|
||||
),
|
||||
initialValue: (p["commission_amount"] ?? '')
|
||||
.toString(),
|
||||
|
||||
@ -5,6 +5,11 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
// import 'package:uae_stat/config/my_theme.dart';
|
||||
|
||||
/// Characters allowed while typing an email (matches typical local-part + domain).
|
||||
final List<TextInputFormatter> kEmailAddressInputFormatters = [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9@._%+-]')),
|
||||
];
|
||||
|
||||
class ThemedFormField extends HookWidget {
|
||||
ThemedFormField({
|
||||
super.key,
|
||||
@ -202,9 +207,10 @@ class ThemedFormField extends HookWidget {
|
||||
maxLength: maxLength, // ✅ max length
|
||||
showCursor: isShowCursor ?? true,
|
||||
enableInteractiveSelection: isShowCursor ?? true,
|
||||
inputFormatters:
|
||||
inputFormatters ??
|
||||
[FilteringTextInputFormatter.allow(RegExp(r'[a-z A-Z]'))],
|
||||
inputFormatters: inputFormatters ??
|
||||
(keyboardType == TextInputType.emailAddress
|
||||
? kEmailAddressInputFormatters
|
||||
: null),
|
||||
// ✅ allow multiline if user sets maxLines / minLines
|
||||
minLines: (keyboardType == TextInputType.multiline) ? 3 : 1,
|
||||
maxLines: (keyboardType == TextInputType.multiline) ? null : 1,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user