refactor: centralize city prefix stripping logic and standardize location and member-since UI styling
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user