enquiry issue fix

This commit is contained in:
Surendiran 2026-08-05 09:53:12 +05:30
parent 6c65a71d1f
commit 1ceccdcd43
4 changed files with 224 additions and 38 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,5 @@
import 'dart:ui'; import 'dart:ui';
import 'dart:async';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -70,6 +71,8 @@ class EnquiryHandlerState extends ConsumerState<EnquiryListHandler> {
bool isLoading = false; bool isLoading = false;
dynamic roleId; dynamic roleId;
bool _showFilterRow = false; bool _showFilterRow = false;
Timer? _staleAssignedBlinkTimer;
bool _isBlinkPhaseOn = false;
Map<String, TextEditingController> controllers = {}; Map<String, TextEditingController> controllers = {};
List<String> tabHeader = [ List<String> tabHeader = [
@ -240,6 +243,30 @@ class EnquiryHandlerState extends ConsumerState<EnquiryListHandler> {
getVehicleType(); getVehicleType();
getInsurers(); getInsurers();
getBroker(); getBroker();
_staleAssignedBlinkTimer = Timer.periodic(
const Duration(milliseconds: 700),
(_) {
if (!mounted) return;
setState(() {
_isBlinkPhaseOn = !_isBlinkPhaseOn;
});
},
);
}
@override
void dispose() {
_staleAssignedBlinkTimer?.cancel();
for (final controller in controllers.values) {
controller.dispose();
}
for (final rowControlMap in rowControllers.values) {
for (final controller in rowControlMap.values) {
controller.dispose();
}
}
super.dispose();
} }
Future<void> _initializeToken() async { Future<void> _initializeToken() async {
@ -2588,9 +2615,17 @@ class EnquiryHandlerState extends ConsumerState<EnquiryListHandler> {
print('idPrimary - $idPrimary'); print('idPrimary - $idPrimary');
final editing = isEditingRow[id] ?? false; final editing = isEditingRow[id] ?? false;
final controllers = rowControllers[id]; final controllers = rowControllers[id];
final shouldHighlight = _shouldHighlightAssignedRow(item);
return DataRow( return DataRow(
cells: [ color: shouldHighlight
? WidgetStatePropertyAll(
_isBlinkPhaseOn
? const Color(0xFFFFF3E0)
: const Color(0xFFFFFFFF),
)
: null,
cells: _decorateCellsForStaleAssigned([
// S.No. // S.No.
// DataCell(Text('$sno', style: _dataBold)), // DataCell(Text('$sno', style: _dataBold)),
@ -2859,10 +2894,96 @@ class EnquiryHandlerState extends ConsumerState<EnquiryListHandler> {
// Status // Status
DataCell(Text(item['status'] ?? '-', style: _dataBold)), DataCell(Text(item['status'] ?? '-', style: _dataBold)),
], ], shouldHighlight),
); );
} }
List<DataCell> _decorateCellsForStaleAssigned(
List<DataCell> cells,
bool shouldHighlight,
) {
if (!shouldHighlight) return cells;
final borderColor = _isBlinkPhaseOn
? const Color(0xFFE65100)
: const Color(0x00000000);
return cells
.map(
(cell) => DataCell(
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 3),
decoration: BoxDecoration(
border: Border.all(color: borderColor, width: 1.3),
borderRadius: BorderRadius.circular(2),
),
child: cell.child,
),
placeholder: cell.placeholder,
showEditIcon: cell.showEditIcon,
onTap: cell.onTap,
onDoubleTap: cell.onDoubleTap,
onLongPress: cell.onLongPress,
onTapDown: cell.onTapDown,
onTapCancel: cell.onTapCancel,
),
)
.toList();
}
bool _shouldHighlightAssignedRow(Map<String, dynamic> item) {
final rawStatus = (item['enquiry_status'] ?? '').toString().trim();
if (rawStatus.isEmpty) return false;
final normalizedStatus = rawStatus.toLowerCase();
final isAssignedStatus =
normalizedStatus == 'assigned' ||
(normalizedStatus.contains('assigned') &&
!normalizedStatus.contains('to be assigned'));
if (!isAssignedStatus) return false;
final assignedAt = _parseAssignedDateTime(item);
if (assignedAt == null) return false;
return DateTime.now().difference(assignedAt) >= const Duration(minutes: 30);
}
DateTime? _parseAssignedDateTime(Map<String, dynamic> item) {
final rawDate = [
item['assigned_to_datetime'],
item['assigned_datetime'],
item['assigned_on'],
item['updated_on'],
].firstWhere(
(value) => value != null && value.toString().trim().isNotEmpty,
orElse: () => null,
);
if (rawDate == null) return null;
final value = rawDate.toString().trim();
try {
return DateTime.parse(value);
} catch (_) {}
final parsers = [
DateFormat('dd-MM-yyyy hh:mm a'),
DateFormat('dd-MM-yyyy HH:mm'),
DateFormat('yyyy-MM-dd HH:mm:ss'),
DateFormat('yyyy-MM-dd HH:mm'),
];
for (final parser in parsers) {
try {
return parser.parse(value);
} catch (_) {
// try next parser
}
}
return null;
}
static final _dataBold = TextStyle( static final _dataBold = TextStyle(
fontSize: 12, fontSize: 12,

View File

@ -66,6 +66,8 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
int totalCompletedPages = 1; int totalCompletedPages = 1;
List<int> itemsPerPageOptions = [25, 50, 100, 200]; List<int> itemsPerPageOptions = [25, 50, 100, 200];
Timer? _debounce; Timer? _debounce;
Timer? _staleAssignedBlinkTimer;
bool _isBlinkPhaseOn = false;
late ApiService apiService; late ApiService apiService;
dynamic userId; dynamic userId;
dynamic idPrimary; dynamic idPrimary;
@ -275,6 +277,15 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
// timer = Timer.periodic(Duration(seconds: 10), (Timer t) => callRefresh()); //static // timer = Timer.periodic(Duration(seconds: 10), (Timer t) => callRefresh()); //static
startTimer(); startTimer();
_staleAssignedBlinkTimer = Timer.periodic(
const Duration(milliseconds: 700),
(_) {
if (!mounted) return;
setState(() {
_isBlinkPhaseOn = !_isBlinkPhaseOn;
});
},
);
for (String field in tabHeader) { for (String field in tabHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
} }
@ -501,6 +512,7 @@ if (managerId != null &&
@override @override
void dispose() { void dispose() {
_staleAssignedBlinkTimer?.cancel();
refreshSub.close(); refreshSub.close();
_horizontalScrollController.dispose(); _horizontalScrollController.dispose();
// ... dispose other controllers ... // ... dispose other controllers ...
@ -2484,15 +2496,29 @@ if (managerId != null &&
final id = int.tryParse(data['id'].toString()) ?? 0; final id = int.tryParse(data['id'].toString()) ?? 0;
final selectId = data['id'].toString() ?? '0'; final selectId = data['id'].toString() ?? '0';
return Container( final isStaleAssigned = _shouldHighlightAssignedRow(data);
width: MediaQuery final rowBorderColor = isStaleAssigned
.of(context) ? (_isBlinkPhaseOn
.size ? const Color(0xFFE65100)
.width, : const Color(0xFFFFCDD2))
margin: EdgeInsets.fromLTRB(12, 8, 12, 8), : const Color(0xFFFFECF1F7);
final rowBgColor = isStaleAssigned
? (_isBlinkPhaseOn
? const Color(0xFFFFF3E0)
: Colors.white)
: Colors.white;
return Padding(
padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
child: Container(
width: double.infinity,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: rowBgColor,
border: Border.all(color: Color(0xFFFFECF1F7)), border: Border.all(
color: rowBorderColor,
width: isStaleAssigned ? 2.0 : 1.0,
),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Column( child: Column(
@ -2503,10 +2529,11 @@ if (managerId != null &&
border: TableBorder.all(color: Colors.transparent, width: 0), border: TableBorder.all(color: Colors.transparent, width: 0),
headingRowHeight: 0, headingRowHeight: 0,
dividerThickness: 0.1, dividerThickness: 0.1,
horizontalMargin: 16,
columnSpacing: 20.0, columnSpacing: 20.0,
dataRowMinHeight: 40, dataRowMinHeight: 40,
columns: _buildDataColumns(), columns: _buildDataColumns(),
rows: [_buildDataRow(data, sno)], rows: [_buildDataRow(data, sno, isStaleAssigned: isStaleAssigned)],
), ),
// Expanded content // Expanded content
@ -2568,6 +2595,7 @@ if (managerId != null &&
), ),
], ],
), ),
),
); );
} }
}, },
@ -3314,7 +3342,41 @@ if (managerId != null &&
} }
// 2. Now the OPTIMIZED _buildDataRow method: // 2. Now the OPTIMIZED _buildDataRow method:
DataRow _buildDataRow(Map<String, dynamic> item, int sno) { bool _shouldHighlightAssignedRow(Map<String, dynamic> item) {
final rawStatus = (item['enquiry_status'] ?? '').toString().trim();
if (rawStatus.isEmpty) return false;
final normalized = rawStatus.toLowerCase();
if (normalized != 'assigned') return false;
final value = (item['assigned_to_datetime'] ?? '').toString().trim();
if (value.isEmpty) return false;
DateTime? assignedAt;
try {
assignedAt = DateTime.parse(value);
} catch (_) {
for (final fmt in [
DateFormat('dd-MM-yyyy hh:mm a'),
DateFormat('dd-MM-yyyy HH:mm'),
DateFormat('yyyy-MM-dd HH:mm:ss'),
DateFormat('yyyy-MM-dd HH:mm'),
]) {
try {
assignedAt = fmt.parse(value);
break;
} catch (_) {}
}
}
if (assignedAt == null) return false;
return DateTime.now().difference(assignedAt) >= const Duration(minutes: 30);
}
DataRow _buildDataRow(
Map<String, dynamic> item,
int sno, {
bool isStaleAssigned = false,
}) {
final id = int.tryParse(item['id'].toString()) ?? 0; final id = int.tryParse(item['id'].toString()) ?? 0;
final selectId = item['id'].toString() ?? '0'; final selectId = item['id'].toString() ?? '0';
final status = item['enquiry_status'] ?? '-'; final status = item['enquiry_status'] ?? '-';
@ -3322,6 +3384,9 @@ if (managerId != null &&
final borderColor = statusBorderColors[status] ?? Color(0xFFE2E8F0); final borderColor = statusBorderColors[status] ?? Color(0xFFE2E8F0);
return DataRow( return DataRow(
color: isStaleAssigned
? const WidgetStatePropertyAll(Colors.transparent)
: null,
cells: [ cells: [
// Enquiry Created Date // Enquiry Created Date
DataCell( DataCell(

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: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.18.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: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" version: "0.7.11"
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.10.0-0 <4.0.0"
flutter: ">=3.32.0" flutter: ">=3.32.0"