refactor: centralize city prefix stripping logic and standardize location and member-since UI styling

This commit is contained in:
pradeepkumar
2026-04-18 11:22:27 +05:30
parent 0a241dd8c4
commit 164d590d31
3 changed files with 55 additions and 52 deletions

View File

@@ -184,10 +184,8 @@ class AgentDetailState {
if (fvStates != null) parts.addAll(fvStates.map(_normalizeStateCode)); if (fvStates != null) parts.addAll(fvStates.map(_normalizeStateCode));
if (fvCities != null) { if (fvCities != null) {
for (final c in fvCities) { for (final c in fvCities) {
// City values carry a state prefix (e.g. "ne_adams"); strip it for display. if (!parts.any((p) => p.toLowerCase() == c.toLowerCase())) {
final cleaned = _stripCityStatePrefix(c); parts.add(c);
if (!parts.any((p) => p.toLowerCase() == cleaned.toLowerCase())) {
parts.add(cleaned);
} }
} }
} }
@@ -198,7 +196,7 @@ class AgentDetailState {
parts.add(_normalizeStateCode(agent!.state!)); parts.add(_normalizeStateCode(agent!.state!));
} }
if (agent!.city != null && agent!.city!.isNotEmpty) { if (agent!.city != null && agent!.city!.isNotEmpty) {
parts.add(_stripCityStatePrefix(agent!.city!)); parts.add(agent!.city!);
} }
} }
@@ -220,23 +218,6 @@ class AgentDetailState {
return trimmed; return trimmed;
} }
/// Strip the state-code prefix from a city value (e.g. "Ne Adams" / "ne_adams" → "Adams").
/// After removing the prefix, title-case the remainder.
String _stripCityStatePrefix(String raw) {
final trimmed = raw.trim();
// Snake-case with a 2-letter state prefix: ne_adams, co_aetna_estates
final snakeMatch = RegExp(r'^[a-z]{2}_', caseSensitive: false).firstMatch(trimmed);
if (snakeMatch != null) {
return titleCase(trimmed.substring(snakeMatch.end));
}
// Title-cased with a 2-letter prefix token: "Ne Adams", "Co Aetna Estates"
final spaceMatch = RegExp(r'^[A-Za-z]{2}\s+').firstMatch(trimmed);
if (spaceMatch != null) {
return trimmed.substring(spaceMatch.end);
}
return trimmed;
}
/// Combined location text for single-line display. /// Combined location text for single-line display.
String get locationText { String get locationText {
final parts = locationParts; final parts = locationParts;
@@ -245,14 +226,28 @@ class AgentDetailState {
} }
/// Extract all values from a field as a list of title-cased strings. /// Extract all values from a field as a list of title-cased strings.
/// For city fields, strip the 2-letter state prefix before title-casing
/// (matches web's `stripCityPrefix`: "ne_adams" → "Adams").
static List<String>? _extractAllValuesAsList(AgentFieldValue fv) { static List<String>? _extractAllValuesAsList(AgentFieldValue fv) {
final isCity = (fv.fieldSlug.toLowerCase() == 'city' ||
fv.fieldSlug.toLowerCase() == 'city_name');
String format(String raw) {
var s = raw;
if (isCity) {
// Strip 2-letter state prefix: "ne_adams" → "adams", "co_aetna_estates" → "aetna_estates"
s = s.replaceFirst(RegExp(r'^[a-z]{2}_', caseSensitive: false), '');
}
return titleCase(s);
}
if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) { if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) {
return (fv.jsonValue as List) return (fv.jsonValue as List)
.map((v) => titleCase(v.toString())) .map((v) => format(v.toString()))
.toList(); .toList();
} }
if (fv.textValue != null && fv.textValue!.isNotEmpty) { if (fv.textValue != null && fv.textValue!.isNotEmpty) {
return [titleCase(fv.textValue!)]; return [format(fv.textValue!)];
} }
return null; return null;
} }

View File

@@ -692,15 +692,12 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
onTap: remaining > 0 ? () => _showLocationModal(parts) : null, onTap: remaining > 0 ? () => _showLocationModal(parts) : null,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Padding( SvgPicture.asset(
padding: const EdgeInsets.only(top: 2), 'assets/icons/location_pin_icon.svg',
child: SvgPicture.asset( width: 16,
'assets/icons/location_pin_icon.svg', height: 16,
width: 18,
height: 18,
),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Flexible( Flexible(
@@ -712,7 +709,7 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
style: const TextStyle( style: const TextStyle(
fontFamily: 'SourceSerif4', fontFamily: 'SourceSerif4',
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w600,
color: AppColors.primaryDark, color: AppColors.primaryDark,
), ),
), ),
@@ -738,19 +735,20 @@ class _AgentDetailScreenState extends ConsumerState<AgentDetailScreen> {
], ],
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
'assets/icons/calendar_icon.svg', 'assets/icons/calendar_icon.svg',
width: 18, width: 16,
height: 18, height: 16,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
agent.memberSinceText, agent.memberSinceText,
style: const TextStyle( style: const TextStyle(
fontFamily: 'SourceSerif4', fontFamily: 'SourceSerif4',
fontSize: 13, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
color: AppColors.primaryDark, color: AppColors.primaryDark,
), ),
), ),

View File

@@ -759,28 +759,38 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
), ),
], ],
const SizedBox(height: 8), const SizedBox(height: 8),
// Location + Member Since // Location + Member Since (center-aligned, uniform sizing)
Row( if (locationDisplay.isNotEmpty) ...[
mainAxisAlignment: MainAxisAlignment.center, Row(
children: [ mainAxisAlignment: MainAxisAlignment.center,
if (locationDisplay.isNotEmpty) ...[ crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset( SvgPicture.asset(
'assets/icons/location_filled_icon.svg', 'assets/icons/location_filled_icon.svg',
width: 16, width: 16,
height: 16, height: 16,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Flexible(
locationDisplay, child: Text(
style: const TextStyle( locationDisplay,
fontFamily: 'SourceSerif4', style: const TextStyle(
fontSize: 14, fontFamily: 'SourceSerif4',
fontWeight: FontWeight.w700, fontSize: 14,
color: AppColors.primaryDark, fontWeight: FontWeight.w600,
color: AppColors.primaryDark,
),
textAlign: TextAlign.center,
), ),
), ),
const SizedBox(width: 20),
], ],
),
const SizedBox(height: 6),
],
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset( SvgPicture.asset(
'assets/icons/calendar_icon.svg', 'assets/icons/calendar_icon.svg',
width: 16, width: 16,
@@ -795,8 +805,8 @@ class _AgentHomeContentState extends ConsumerState<_AgentHomeContent> {
agent.memberSinceText, agent.memberSinceText,
style: const TextStyle( style: const TextStyle(
fontFamily: 'SourceSerif4', fontFamily: 'SourceSerif4',
fontSize: 13, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
color: AppColors.primaryDark, color: AppColors.primaryDark,
), ),
), ),