This commit is contained in:
Surendiran 2026-06-12 14:20:36 +05:30
parent c6b15a050a
commit 197a699c16

View File

@ -4,6 +4,7 @@ import 'package:csv/csv.dart';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:dropdown_search/dropdown_search.dart'; import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
@ -51,6 +52,9 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
String? _postPreToken = ''; String? _postPreToken = '';
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
TextEditingController searchClaimsStatusController = TextEditingController(); TextEditingController searchClaimsStatusController = TextEditingController();
final ScrollController _statusScrollController = ScrollController();
bool _canScrollStatusLeft = false;
bool _canScrollStatusRight = false;
Map<String, TextEditingController> controllers = {}; Map<String, TextEditingController> controllers = {};
List<dynamic> reversedDataPolicy = []; List<dynamic> reversedDataPolicy = [];
@ -77,25 +81,33 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
int? appliedClaimStatus; int? appliedClaimStatus;
Color getStatusColor(String status) { Color getStatusColor(String status) {
switch (status.toLowerCase()) { switch (status.toLowerCase().trim()) {
case 'claim received': case 'claim intimation received':
return const Color(0xFF4A90E2); // blue return const Color(0xFF4A90E2);
case 'under process': case 'claim intimated':
return const Color(0xFFF5A623); // orange return const Color(0xFF5B9BD5);
case 'information required': case 'insurer pending':
return const Color(0xFFFF8C42); // amber return const Color(0xFFF5A623);
case 'approved': case 'surveyor pending':
return const Color(0xFF2ECC71); // green return const Color(0xFFFF8C42);
case 'settled': case 'insured pending':
return const Color(0xFF1ABC9C); // teal-green return const Color(0xFFE67E22);
case 'rejected': case 'insurer / surveyor pending':
return const Color(0xFFE74C3C); // red return const Color(0xFFD35400);
case 'denial review awaite': case 'workshop pending':
return const Color(0xFF9B59B6); // purple return const Color(0xFF9B59B6);
case 'closed': case 'claim approved':
return const Color(0xFFB0BEC5); // purple return const Color(0xFF2ECC71);
case 'claim settled':
return const Color(0xFF1ABC9C);
case 'claim closed':
return const Color(0xFF95A5A6);
case 'claim rejected':
return const Color(0xFFE74C3C);
case 'claim withdrawn':
return const Color(0xFF7F8C8D);
default: default:
return const Color(0xFFB0BEC5); // grey fallback return const Color(0xFFB0BEC5);
} }
} }
@ -125,6 +137,9 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
for (String field in tabHeader) { for (String field in tabHeader) {
controllers[field] = TextEditingController(); controllers[field] = TextEditingController();
} }
_statusScrollController.addListener(_updateStatusScrollArrows);
WidgetsBinding.instance
.addPostFrameCallback((_) => _updateStatusScrollArrows());
// if (_postPreToken != null && _postPreToken!.isNotEmpty) { // if (_postPreToken != null && _postPreToken!.isNotEmpty) {
// //
@ -178,6 +193,7 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
@override @override
void dispose() { void dispose() {
_statusScrollController.dispose();
for (var controller in controllers.values) { for (var controller in controllers.values) {
controller.dispose(); controller.dispose();
} }
@ -274,14 +290,29 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
} }
} }
String _claimStatusLabel(Map<String, dynamic> item) {
return (item['status_display'] ?? item['status'] ?? '').toString();
}
int getStatusCount(String status) { int getStatusCount(String status) {
return filteredData return filteredData
.where((item) => .where((item) =>
(item['status'] ?? '').toString().toLowerCase() == _claimStatusLabel(item).toLowerCase() == status.toLowerCase())
status.toLowerCase())
.length; .length;
} }
List<String> _uniqueDisplayNames(List<dynamic> statusMaster) {
final seen = <String>{};
final result = <String>[];
for (final status in statusMaster) {
final name = status['display_name']?.toString() ?? '';
if (name.isNotEmpty && seen.add(name.toLowerCase())) {
result.add(name);
}
}
return result;
}
Future<void> applyFilter() async { Future<void> applyFilter() async {
logDebug("Filtersss"); logDebug("Filtersss");
@ -734,6 +765,7 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
} }
Widget _buildDataRow(Map<String, dynamic> item) { Widget _buildDataRow(Map<String, dynamic> item) {
final statusLabel = _claimStatusLabel(item);
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
margin: const EdgeInsets.only(top: 10), margin: const EdgeInsets.only(top: 10),
@ -795,10 +827,12 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: getStatusColor(item['status_display'] ?? ''), color: getStatusColor(statusLabel),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Text(item['status_display'] ?? '-', style: _dataBoldStatus)), child: Text(
statusLabel.isEmpty ? '-' : statusLabel,
style: _dataBoldStatus)),
], ],
), ),
), ),
@ -874,39 +908,110 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
); );
} }
void _updateStatusScrollArrows() {
if (!_statusScrollController.hasClients) return;
final position = _statusScrollController.position;
final canLeft = position.pixels > position.minScrollExtent + 1;
final canRight = position.pixels < position.maxScrollExtent - 1;
if (canLeft != _canScrollStatusLeft || canRight != _canScrollStatusRight) {
setState(() {
_canScrollStatusLeft = canLeft;
_canScrollStatusRight = canRight;
});
}
}
void _scrollStatusBy(double delta) {
if (!_statusScrollController.hasClients) return;
final target = (_statusScrollController.offset + delta)
.clamp(0.0, _statusScrollController.position.maxScrollExtent);
_statusScrollController.animateTo(
target,
duration: const Duration(milliseconds: 280),
curve: Curves.easeOutCubic,
);
}
Widget _buildStatusScrollButton({
required IconData icon,
required bool enabled,
required VoidCallback onTap,
}) {
return SizedBox(
width: 32,
height: 40,
child: Material(
color: enabled ? const Color(0xFF00999E) : const Color(0xFFE8E8E8),
borderRadius: BorderRadius.circular(8),
child: InkWell(
onTap: enabled ? onTap : null,
borderRadius: BorderRadius.circular(8),
child: Icon(
icon,
size: 22,
color: enabled ? Colors.white : Colors.black38,
),
),
),
);
}
Widget _buildStatusSummary() { Widget _buildStatusSummary() {
final List<dynamic> statusMaster = _claimStatusMaster; final List<dynamic> statusMaster = _claimStatusMaster;
if (statusMaster.isEmpty) return const SizedBox(); if (statusMaster.isEmpty) return const SizedBox();
final List<dynamic> displayStatuses = appliedClaimStatus != null final List<String> displayStatuses = appliedClaimStatus != null
? statusMaster ? statusMaster
.where((status) => .where((status) =>
int.parse(status['id'].toString()) == appliedClaimStatus) int.parse(status['id'].toString()) == appliedClaimStatus)
.map((status) => status['display_name']?.toString() ?? '')
.where((name) => name.isNotEmpty)
.toList() .toList()
: statusMaster; : _uniqueDisplayNames(statusMaster);
return SizedBox( return Row(
height: 35, children: [
_buildStatusScrollButton(
icon: Icons.chevron_left,
enabled: _canScrollStatusLeft,
onTap: () => _scrollStatusBy(-240),
),
const SizedBox(width: 8),
Expanded(
child: ScrollConfiguration(
behavior: _StatusDragScrollBehavior(),
child: MouseRegion(
cursor: SystemMouseCursors.grab,
child: SizedBox(
height: 40,
child: NotificationListener<ScrollMetricsNotification>(
onNotification: (_) {
_updateStatusScrollArrows();
return false;
},
child: ListView.separated( child: ListView.separated(
controller: _statusScrollController,
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const ClampingScrollPhysics(),
itemCount: displayStatuses.length, itemCount: displayStatuses.length,
separatorBuilder: (_, __) => const SizedBox(width: 10), separatorBuilder: (_, __) => const SizedBox(width: 10),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final statusName = final statusName = displayStatuses[index];
displayStatuses[index]['display_name']?.toString() ?? '';
final count = getStatusCount(statusName); final count = getStatusCount(statusName);
final color = getStatusColor(statusName); final color = getStatusColor(statusName);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: color,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: Text( child: Text(
'$statusName - $count', '$statusName - $count',
softWrap: false,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -916,6 +1021,18 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
); );
}, },
), ),
),
),
),
),
),
const SizedBox(width: 8),
_buildStatusScrollButton(
icon: Icons.chevron_right,
enabled: _canScrollStatusRight,
onTap: () => _scrollStatusBy(240),
),
],
); );
} }
@ -1620,6 +1737,16 @@ class _ClaimsHeaderDelegate extends SliverPersistentHeaderDelegate {
bool shouldRebuild(_) => false; bool shouldRebuild(_) => false;
} }
class _StatusDragScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.stylus,
PointerDeviceKind.trackpad,
};
}
// ================= ICON BUTTON ================= // ================= ICON BUTTON =================
class _IconActionButton extends StatelessWidget { class _IconActionButton extends StatelessWidget {
final IconData icon; final IconData icon;