FIX_Grid View

This commit is contained in:
sanjeev.p 2026-04-07 10:31:19 +05:30
parent cb9c84bf0b
commit 780159f380
3 changed files with 125 additions and 261 deletions

File diff suppressed because one or more lines are too long

View File

@ -922,7 +922,6 @@ class ApiService {
String? vehicleType, String? vehicleType,
String? segment, String? segment,
String? rto, String? rto,
String? planType,
String? search, String? search,
String? loggedId, String? loggedId,
}) async { }) async {
@ -936,7 +935,6 @@ class ApiService {
final vehicleTypeTrimmed = vehicleType?.trim(); final vehicleTypeTrimmed = vehicleType?.trim();
final segmentTrimmed = segment?.trim(); final segmentTrimmed = segment?.trim();
final rtoTrimmed = rto?.trim(); final rtoTrimmed = rto?.trim();
final planTypeTrimmed = planType?.trim();
final searchTrimmed = search?.trim(); final searchTrimmed = search?.trim();
final loggedIdTrimmed = loggedId?.trim(); final loggedIdTrimmed = loggedId?.trim();
final endpoint = Uri.parse('${Env.apiUrl}grid'); final endpoint = Uri.parse('${Env.apiUrl}grid');
@ -963,9 +961,6 @@ class ApiService {
if (rtoTrimmed != null && rtoTrimmed.isNotEmpty) { if (rtoTrimmed != null && rtoTrimmed.isNotEmpty) {
queryParameters['rto'] = rtoTrimmed; queryParameters['rto'] = rtoTrimmed;
} }
if (planTypeTrimmed != null && planTypeTrimmed.isNotEmpty) {
queryParameters['plan_type'] = planTypeTrimmed;
}
if (searchTrimmed != null && searchTrimmed.isNotEmpty) { if (searchTrimmed != null && searchTrimmed.isNotEmpty) {
queryParameters['search'] = searchTrimmed; queryParameters['search'] = searchTrimmed;
} }
@ -988,7 +983,6 @@ class ApiService {
String? vehicleType, String? vehicleType,
String? segment, String? segment,
String? rto, String? rto,
String? planType,
String? search, String? search,
String? loggedId, String? loggedId,
}) async { }) async {
@ -1007,13 +1001,17 @@ class ApiService {
} }
} }
final downloadRoleLc = role?.trim().toLowerCase();
addQuery('role', role); addQuery('role', role);
// Manager / Accounts use file_id; Agent uses logged_id only (same as loadPayoutGrid).
if (downloadRoleLc != 'agent') {
addQuery('file_id', fileId); addQuery('file_id', fileId);
}
addQuery('insurer', insurer); addQuery('insurer', insurer);
addQuery('vehicle_type', vehicleType); addQuery('vehicle_type', vehicleType);
addQuery('segment', segment); addQuery('segment', segment);
addQuery('rto', rto); addQuery('rto', rto);
addQuery('plan_type', planType);
addQuery('search', search); addQuery('search', search);
addQuery('logged_id', loggedId); addQuery('logged_id', loggedId);

View File

@ -33,9 +33,14 @@ class gridViewScreen extends ConsumerStatefulWidget {
} }
class _gridViewScreenState extends ConsumerState<gridViewScreen> { class _gridViewScreenState extends ConsumerState<gridViewScreen> {
static const String _defaultVehicleTypeLabel = 'TWO WHEELER NEW';
final ApiService _apiService = ApiService(); final ApiService _apiService = ApiService();
String? _loggedId; String? _loggedId;
/// `file_id` from the last successful Manager/Accounts grid load (export matches this).
String? _gridFileIdUsed;
bool _isLoading = false; bool _isLoading = false;
// API response // API response
@ -43,7 +48,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
List<Map<String, dynamic>> _filteredGrid = []; List<Map<String, dynamic>> _filteredGrid = [];
List<String> _insurers = []; List<String> _insurers = [];
List<Map<String, dynamic>> _planTypes = [];
List<String> _rtos = []; List<String> _rtos = [];
List<String> _vehicleTypes = []; List<String> _vehicleTypes = [];
List<String> _segments = []; List<String> _segments = [];
@ -53,7 +57,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
String? _selectedRto; String? _selectedRto;
String? _selectedVehicleType; String? _selectedVehicleType;
String? _selectedSegment; String? _selectedSegment;
String? _selectedPlanTypeValue; // comp/tp/od
final TextEditingController _searchController = TextEditingController(); final TextEditingController _searchController = TextEditingController();
final TextEditingController _editVehicleTypeController = final TextEditingController _editVehicleTypeController =
@ -267,13 +270,19 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
final queryFileId = queryParams['file_id']?.trim(); final queryFileId = queryParams['file_id']?.trim();
final effectiveRole = final effectiveRole =
(queryRole != null && queryRole.isNotEmpty) ? queryRole : apiRole; (queryRole != null && queryRole.isNotEmpty) ? queryRole : apiRole;
String? effectiveFileId = final roleLc = (effectiveRole ?? '').toLowerCase();
// Manager / Accounts: require file_id (URL or latest from file list).
// Agent: never send file_id; require logged-in user id for retention.
String? effectiveFileId;
if (roleLc == 'agent') {
effectiveFileId = null;
} else {
effectiveFileId =
(queryFileId != null && queryFileId.isNotEmpty) ? queryFileId : null; (queryFileId != null && queryFileId.isNotEmpty) ? queryFileId : null;
if (effectiveFileId == null && if (effectiveFileId == null &&
(effectiveRole?.toLowerCase() == 'manager' || (roleLc == 'manager' || roleLc == 'accounts')) {
effectiveRole?.toLowerCase() == 'agent' ||
effectiveRole?.toLowerCase() == 'accounts')) {
final fileListRes = await _apiService.fetchGridFileList(); final fileListRes = await _apiService.fetchGridFileList();
if ((fileListRes['status'] ?? '').toString().toLowerCase() == if ((fileListRes['status'] ?? '').toString().toLowerCase() ==
'success') { 'success') {
@ -291,8 +300,33 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
} }
} }
final loggedIdForGrid = if (roleLc == 'manager' || roleLc == 'accounts') {
(effectiveRole ?? '').toLowerCase() == 'agent' ? _loggedId : null; if (effectiveFileId == null || effectiveFileId.trim().isEmpty) {
if (mounted) {
ToastHelper.showErrorToast(
context,
'No grid file found. Open grid from a file or upload a payout grid first.',
);
}
return;
}
}
}
if (roleLc == 'agent') {
final lid = _loggedId?.trim();
if (lid == null || lid.isEmpty) {
if (mounted) {
ToastHelper.showErrorToast(
context,
'Unable to load grid: missing logged-in user id.',
);
}
return;
}
}
final loggedIdForGrid = roleLc == 'agent' ? _loggedId?.trim() : null;
final res = await _apiService.loadPayoutGrid( final res = await _apiService.loadPayoutGrid(
role: effectiveRole, role: effectiveRole,
@ -300,6 +334,9 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
loggedId: loggedIdForGrid, loggedId: loggedIdForGrid,
); );
if ((res['status'] ?? '').toString().toLowerCase() != 'success') { if ((res['status'] ?? '').toString().toLowerCase() != 'success') {
if (mounted) {
setState(() => _gridFileIdUsed = null);
}
ToastHelper.showErrorToast( ToastHelper.showErrorToast(
context, context,
res['data']?.toString() ?? 'Failed to load payout grid', res['data']?.toString() ?? 'Failed to load payout grid',
@ -310,7 +347,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
final payload = _extractPayload(res); final payload = _extractPayload(res);
final grid = payload['grid']; final grid = payload['grid'];
final insurers = payload['insurers']; final insurers = payload['insurers'];
final planTypes = payload['plan_types'];
final rtos = payload['rtos']; final rtos = payload['rtos'];
final vehicleTypes = payload['vehicle_types']; final vehicleTypes = payload['vehicle_types'];
final segments = payload['segments']; final segments = payload['segments'];
@ -325,13 +361,19 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
_filteredGrid = List<Map<String, dynamic>>.from(_grid); _filteredGrid = List<Map<String, dynamic>>.from(_grid);
_insurers = _toStringList(insurers); _insurers = _toStringList(insurers);
_planTypes = _toPlanTypeList(planTypes);
_rtos = _toStringList(rtos); _rtos = _toStringList(rtos);
_vehicleTypes = _toStringList(vehicleTypes); _vehicleTypes = _toStringList(vehicleTypes);
_segments = _toStringList(segments); _segments = _toStringList(segments);
_gridFileIdUsed =
(roleLc == 'manager' || roleLc == 'accounts') ? effectiveFileId : null;
_applyDefaultVehicleTypeSelection();
_applyFilters(); _applyFilters();
} catch (e) { } catch (e) {
if (mounted) {
setState(() => _gridFileIdUsed = null);
}
ToastHelper.showErrorToast(context, e.toString()); ToastHelper.showErrorToast(context, e.toString());
} finally { } finally {
if (mounted) setState(() => _isLoading = false); if (mounted) setState(() => _isLoading = false);
@ -381,25 +423,18 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
.toList(); .toList();
} }
List<Map<String, dynamic>> _toPlanTypeList(dynamic raw) { /// Sets [_selectedVehicleType] to the dropdown spelling that matches
if (raw is! List) return <Map<String, dynamic>>[]; /// [_defaultVehicleTypeLabel], or prepends that label if the API omits it.
return raw.map<Map<String, dynamic>>((item) { void _applyDefaultVehicleTypeSelection() {
if (item is Map) { final desiredLower = _defaultVehicleTypeLabel.toLowerCase();
final map = item.cast<String, dynamic>(); for (final v in _vehicleTypes) {
final value = map['value']?.toString() ?? if (v.trim().toLowerCase() == desiredLower) {
map['id']?.toString() ?? _selectedVehicleType = v;
map['key']?.toString() ?? return;
'';
final label = map['label']?.toString() ??
map['name']?.toString() ??
map['title']?.toString() ??
map['value']?.toString() ??
value;
return {'value': value, 'label': label};
} }
final v = item?.toString() ?? ''; }
return {'value': v, 'label': v.toUpperCase()}; _vehicleTypes = [_defaultVehicleTypeLabel, ..._vehicleTypes];
}).where((e) => (e['value']?.toString().trim().isNotEmpty ?? false)).toList(); _selectedVehicleType = _defaultVehicleTypeLabel;
} }
void _applyFilters() { void _applyFilters() {
@ -411,20 +446,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
return raw?.toString().toLowerCase() == selected.toLowerCase(); return raw?.toString().toLowerCase() == selected.toLowerCase();
} }
bool matchesPlanType() {
if (_selectedPlanTypeValue == null) return true;
final raw = row[_selectedPlanTypeValue];
if (raw == null) return false;
final s = raw.toString().trim().toLowerCase();
if (s.isEmpty || s == 'null' || s == '-') return false;
// Treat 0 payout as "not configured" so it doesn't match plan filters.
final n = num.tryParse(s);
if (n != null) return n != 0;
return s != '0';
}
bool matchesSearch() { bool matchesSearch() {
if (q.isEmpty) return true; if (q.isEmpty) return true;
final values = <String>[ final values = <String>[
@ -447,7 +468,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
equalsOrAll(_selectedVehicleType, row['vehicle_type']) && equalsOrAll(_selectedVehicleType, row['vehicle_type']) &&
equalsOrAll(_selectedSegment, row['segment']) && equalsOrAll(_selectedSegment, row['segment']) &&
equalsOrAll(_selectedRto, row['rto']) && equalsOrAll(_selectedRto, row['rto']) &&
matchesPlanType() &&
matchesSearch(); matchesSearch();
}).toList(); }).toList();
@ -591,24 +611,13 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
bool get _showPartnerRetentionValues => bool get _showPartnerRetentionValues =>
_showAddButton || _accountsAgentPreview; _showAddButton || _accountsAgentPreview;
bool get _showCompColumnForAgent =>
!_showAddButton ||
_selectedPlanTypeValue == 'comp';
bool get _showTpColumnForAgent =>
!_showAddButton ||
_selectedPlanTypeValue == 'tp';
bool get _showOdColumnForAgent =>
!_showAddButton ||
_selectedPlanTypeValue == 'od';
bool get _hasAnyFilterSelection { bool get _hasAnyFilterSelection {
return (_selectedInsurer != null && _selectedInsurer!.trim().isNotEmpty) || return (_selectedInsurer != null && _selectedInsurer!.trim().isNotEmpty) ||
(_selectedRto != null && _selectedRto!.trim().isNotEmpty) || (_selectedRto != null && _selectedRto!.trim().isNotEmpty) ||
(_selectedVehicleType != null && (_selectedVehicleType != null &&
_selectedVehicleType!.trim().isNotEmpty) || _selectedVehicleType!.trim().isNotEmpty) ||
(_selectedSegment != null && _selectedSegment!.trim().isNotEmpty) || (_selectedSegment != null && _selectedSegment!.trim().isNotEmpty);
(_selectedPlanTypeValue != null &&
_selectedPlanTypeValue!.trim().isNotEmpty);
} }
String _toEditText(dynamic value) { String _toEditText(dynamic value) {
@ -766,17 +775,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
Widget _buildFilterBar(BuildContext context) { Widget _buildFilterBar(BuildContext context) {
final isMobile = ResponsiveLayout.isMobile(context); final isMobile = ResponsiveLayout.isMobile(context);
final planTypeItems = List<Map<String, dynamic>>.from(_planTypes);
Map<String, dynamic>? selectedPlanType;
if (_selectedPlanTypeValue != null) {
for (final e in planTypeItems) {
if ((e['value'] as String?) == _selectedPlanTypeValue) {
selectedPlanType = e;
break;
}
}
}
Widget dropdownInsurer() { Widget dropdownInsurer() {
return SizedBox( return SizedBox(
width: isMobile ? double.infinity : 200, width: isMobile ? double.infinity : 200,
@ -811,46 +809,6 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
); );
} }
Widget dropdownPlanType() {
return SizedBox(
width: isMobile ? double.infinity : 220,
height: 40,
child: DropdownSearch<Map<String, dynamic>>(
items: (filter, infiniteScrollProps) {
final items = planTypeItems;
if (filter.isEmpty) return items;
return items
.where((e) =>
(e['label']?.toString() ?? '')
.toLowerCase()
.contains(filter.toLowerCase()))
.toList();
},
selectedItem: selectedPlanType,
itemAsString: (item) => item['label']?.toString() ?? '',
compareFn: (a, b) => (a['value'] == b['value']),
popupProps: const PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
),
dropdownBuilder: (context, selectedItem) => Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
selectedItem?['label']?.toString() ?? '',
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
overflow: TextOverflow.ellipsis,
),
),
onChanged: (val) => _setFilter(() {
_selectedPlanTypeValue = val?['value'] as String?;
}),
decoratorProps: DropDownDecoratorProps(
decoration: _dropdownDecoration('Plan Type'),
),
),
);
}
Widget dropdownRto() { Widget dropdownRto() {
return SizedBox( return SizedBox(
width: isMobile ? double.infinity : 180, width: isMobile ? double.infinity : 180,
@ -974,11 +932,10 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
onTap: () { onTap: () {
setState(() { setState(() {
_selectedInsurer = null; _selectedInsurer = null;
_selectedPlanTypeValue = null;
_selectedRto = null; _selectedRto = null;
_selectedVehicleType = null;
_selectedSegment = null; _selectedSegment = null;
_searchController.clear(); _searchController.clear();
_applyDefaultVehicleTypeSelection();
}); });
_applyFilters(); _applyFilters();
}, },
@ -996,12 +953,10 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
child: isMobile child: isMobile
? Column( ? Column(
children: [ children: [
dropdownInsurer(),
const SizedBox(height: 10),
dropdownPlanType(),
const SizedBox(height: 10),
dropdownVehicleType(), dropdownVehicleType(),
const SizedBox(height: 10), const SizedBox(height: 10),
dropdownInsurer(),
const SizedBox(height: 10),
dropdownSegment(), dropdownSegment(),
const SizedBox(height: 10), const SizedBox(height: 10),
dropdownRto(), dropdownRto(),
@ -1018,12 +973,10 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
children: [ children: [
dropdownInsurer(),
const SizedBox(width: 12),
dropdownPlanType(),
const SizedBox(width: 12),
dropdownVehicleType(), dropdownVehicleType(),
const SizedBox(width: 12), const SizedBox(width: 12),
dropdownInsurer(),
const SizedBox(width: 12),
dropdownSegment(), dropdownSegment(),
const SizedBox(width: 12), const SizedBox(width: 12),
dropdownRto(), dropdownRto(),
@ -1107,13 +1060,8 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
itemCount: _paginatedGrid.length, itemCount: _paginatedGrid.length,
separatorBuilder: (context, index) => const SizedBox(height: 10), separatorBuilder: (context, index) => const SizedBox(height: 10),
itemBuilder: (context, index) => _gridViewCard( itemBuilder: (context, index) =>
row: _paginatedGrid[index], _gridViewCard(row: _paginatedGrid[index]),
showCompColumn: _showCompColumnForAgent,
showTpColumn: _showTpColumnForAgent,
showOdColumn: _showOdColumnForAgent,
showPartnerCompValue: _showPartnerRetentionValues,
),
); );
} }
@ -1139,11 +1087,8 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
Expanded(flex: 2, child: Text('Vehicle', style: _headerStyle)), Expanded(flex: 2, child: Text('Vehicle', style: _headerStyle)),
Expanded(flex: 3, child: Text('Segment', style: _headerStyle)), Expanded(flex: 3, child: Text('Segment', style: _headerStyle)),
Expanded(flex: 2, child: Text('RTO', style: _headerStyle)), Expanded(flex: 2, child: Text('RTO', style: _headerStyle)),
if (_showCompColumnForAgent)
Expanded(flex: 2, child: Text('Comp', style: _headerStyle)), Expanded(flex: 2, child: Text('Comp', style: _headerStyle)),
if (_showTpColumnForAgent)
Expanded(flex: 2, child: Text('TP', style: _headerStyle)), Expanded(flex: 2, child: Text('TP', style: _headerStyle)),
if (_showOdColumnForAgent)
Expanded(flex: 2, child: Text('OD', style: _headerStyle)), Expanded(flex: 2, child: Text('OD', style: _headerStyle)),
Expanded(flex: 3, child: Text('Remarks', style: _headerStyle)), Expanded(flex: 3, child: Text('Remarks', style: _headerStyle)),
], ],
@ -1181,47 +1126,17 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
), ),
), ),
Expanded(flex: 2, child: Text(_nullableString(row['rto']), style: _dataBold)), Expanded(flex: 2, child: Text(_nullableString(row['rto']), style: _dataBold)),
if (_showCompColumnForAgent)
Expanded( Expanded(
flex: 2, flex: 2,
child: Text( child: Text(_nullableString(row['comp']), style: _dataBold),
_showPartnerRetentionValues
? _nullableString(row['partner_comp'])
: _nullableString(row['comp']),
style: _dataBold,
), ),
),
if (_showTpColumnForAgent)
Expanded(flex: 2, child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${_nullableString(row['tp'])}', style: _dataBold),
if (_showPartnerRetentionValues) ...[
const SizedBox(height: 2),
Text(
'${_nullableString(row['partner_tp'])}',
style: _dataBold,
),
],
],
),
),
if (_showOdColumnForAgent)
Expanded( Expanded(
flex: 2, flex: 2,
child: Column( child: Text(_nullableString(row['tp']), style: _dataBold),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${_nullableString(row['od'])}', style: _dataBold),
if (_showPartnerRetentionValues) ...[
const SizedBox(height: 2),
Text(
'${_nullableString(row['partner_od'])}',
style: _dataBold,
),
],
],
), ),
Expanded(
flex: 2,
child: Text(_nullableString(row['od']), style: _dataBold),
), ),
Expanded( Expanded(
flex: 3, flex: 3,
@ -1287,15 +1202,23 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
} }
try { try {
final queryParams = Uri.base.queryParameters; final queryParams = Uri.base.queryParameters;
final fileIdFromQuery = queryParams['file_id']?.trim();
final role = _apiRoleFromAppRole(ref.read(userRoleProvider)); final role = _apiRoleFromAppRole(ref.read(userRoleProvider));
final roleLc = (role ?? '').toLowerCase(); final roleLc = (role ?? '').toLowerCase();
final fileIdFromQuery = queryParams['file_id']?.trim();
final fileIdToSend = final fileIdToSend =
(roleLc == 'manager' || roleLc == 'accounts') (roleLc == 'manager' || roleLc == 'accounts')
? (_gridFileIdUsed ??
(fileIdFromQuery != null && fileIdFromQuery.isNotEmpty
? fileIdFromQuery ? fileIdFromQuery
: null))
: null; : null;
final previewAgentId = final previewAgentId =
queryParams['agent_id']?.trim(); queryParams['agent_id']?.trim();
final loggedIdForExport = roleLc == 'agent'
? _loggedId?.trim()
: (previewAgentId != null && previewAgentId.isNotEmpty
? previewAgentId
: null);
await _apiService.downloadGridExcel( await _apiService.downloadGridExcel(
role: role, role: role,
fileId: fileIdToSend, fileId: fileIdToSend,
@ -1303,11 +1226,8 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
vehicleType: _selectedVehicleType, vehicleType: _selectedVehicleType,
segment: _selectedSegment, segment: _selectedSegment,
rto: _selectedRto, rto: _selectedRto,
planType: _selectedPlanTypeValue,
search: _searchController.text.trim(), search: _searchController.text.trim(),
loggedId: previewAgentId != null && previewAgentId.isNotEmpty loggedId: loggedIdForExport,
? previewAgentId
: _loggedId,
); );
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
@ -1340,18 +1260,8 @@ class _gridViewScreenState extends ConsumerState<gridViewScreen> {
class _gridViewCard extends StatelessWidget { class _gridViewCard extends StatelessWidget {
final Map<String, dynamic> row; final Map<String, dynamic> row;
final bool showCompColumn;
final bool showTpColumn;
final bool showOdColumn;
final bool showPartnerCompValue;
const _gridViewCard({ const _gridViewCard({required this.row});
required this.row,
required this.showCompColumn,
required this.showTpColumn,
required this.showOdColumn,
required this.showPartnerCompValue,
});
String _nullableString(dynamic v) { String _nullableString(dynamic v) {
if (v == null) return '-'; if (v == null) return '-';
@ -1405,20 +1315,10 @@ class _gridViewCard extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
Row( Row(
children: [ children: [
if (showCompColumn) Expanded(child: _metric('Comp', row['comp'])),
Expanded(
child: _upDownMetric(
title: 'Comp',
value: showPartnerCompValue ? row['partner_comp'] : row['comp'],
),
),
if (showCompColumn && (showTpColumn || showOdColumn))
const SizedBox(width: 10), const SizedBox(width: 10),
if (showTpColumn)
Expanded(child: _metric('TP', row['tp'])), Expanded(child: _metric('TP', row['tp'])),
if (showTpColumn && showOdColumn)
const SizedBox(width: 10), const SizedBox(width: 10),
if (showOdColumn)
Expanded(child: _metric('OD', row['od'])), Expanded(child: _metric('OD', row['od'])),
], ],
), ),
@ -1460,40 +1360,6 @@ class _gridViewCard extends StatelessWidget {
), ),
); );
} }
Widget _upDownMetric({
required String title,
required dynamic value,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: GoogleFonts.poppins(
fontSize: 10,
color: const Color(0xFF64748B),
),
),
const SizedBox(height: 2),
Text(
_nullableString(value),
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w600,
),
),
],
),
);
}
} }
/* import 'package:dropdown_search/dropdown_search.dart'; /* import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';