refactor: normalize location display by stripping state prefixes from cities and uppercasing state codes
This commit is contained in:
@@ -96,10 +96,12 @@ class AgentProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get location {
|
String get location {
|
||||||
// Direct fields first (state first, then city)
|
// Direct fields first (state first, then city) — normalized
|
||||||
if (city != null && state != null) return '$state, $city';
|
if (city != null && state != null) {
|
||||||
if (state != null) return state!;
|
return '${_normalizeState(state!)}, ${_stripStatePrefix(city!)}';
|
||||||
if (city != null) return city!;
|
}
|
||||||
|
if (state != null) return _normalizeState(state!);
|
||||||
|
if (city != null) return _stripStatePrefix(city!);
|
||||||
|
|
||||||
// Fallback: extract from fieldValues (matches web getLocationString)
|
// Fallback: extract from fieldValues (matches web getLocationString)
|
||||||
String? fvCity;
|
String? fvCity;
|
||||||
@@ -107,9 +109,9 @@ class AgentProfile {
|
|||||||
for (final fv in fieldValues) {
|
for (final fv in fieldValues) {
|
||||||
final slug = fv.fieldSlug.toLowerCase();
|
final slug = fv.fieldSlug.toLowerCase();
|
||||||
if (slug == 'city' || slug == 'city_name') {
|
if (slug == 'city' || slug == 'city_name') {
|
||||||
fvCity = _extractFieldString(fv);
|
fvCity = _extractFieldString(fv, isCity: true);
|
||||||
} else if (slug == 'state' || slug == 'state_name') {
|
} else if (slug == 'state' || slug == 'state_name') {
|
||||||
fvState = _extractFieldString(fv);
|
fvState = _extractFieldString(fv, isCity: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final parts = [fvState, fvCity].where((s) => s != null && s.isNotEmpty);
|
final parts = [fvState, fvCity].where((s) => s != null && s.isNotEmpty);
|
||||||
@@ -121,20 +123,50 @@ class AgentProfile {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Uppercase 2-letter state codes (e.g. "nd" → "ND"); leave full names alone.
|
||||||
|
static String _normalizeState(String raw) {
|
||||||
|
final trimmed = raw.trim();
|
||||||
|
if (trimmed.length == 2) return trimmed.toUpperCase();
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strip the 2-letter state prefix from a city value ("nd_hurley" → "Hurley",
|
||||||
|
/// "Nd Hurley" → "Hurley"). Matches web's `stripCityPrefix` behavior.
|
||||||
|
static String _stripStatePrefix(String raw) {
|
||||||
|
var s = raw.trim();
|
||||||
|
// Snake-case prefix: nd_hurley
|
||||||
|
s = s.replaceFirst(RegExp(r'^[a-z]{2}_', caseSensitive: false), '');
|
||||||
|
// Title-cased prefix token: "Nd Hurley"
|
||||||
|
s = s.replaceFirst(RegExp(r'^[A-Za-z]{2}\s+'), '');
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/// Extract a display string from a field value (handles jsonValue arrays).
|
/// Extract a display string from a field value (handles jsonValue arrays).
|
||||||
static String? _extractFieldString(AgentFieldValue fv) {
|
/// For city fields, strip the state-code prefix before title-casing.
|
||||||
|
static String? _extractFieldString(AgentFieldValue fv, {required bool isCity}) {
|
||||||
|
String format(String raw) {
|
||||||
|
var s = raw;
|
||||||
|
if (isCity) {
|
||||||
|
// Strip slug state prefix: "nd_hurley" → "hurley"
|
||||||
|
s = s.replaceFirst(RegExp(r'^[a-z]{2}_', caseSensitive: false), '');
|
||||||
|
}
|
||||||
|
final titled = s
|
||||||
|
.split('_')
|
||||||
|
.map((w) => w.isNotEmpty
|
||||||
|
? '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}'
|
||||||
|
: '')
|
||||||
|
.join(' ');
|
||||||
|
if (!isCity && titled.length == 2) return titled.toUpperCase();
|
||||||
|
return titled;
|
||||||
|
}
|
||||||
|
|
||||||
if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) {
|
if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) {
|
||||||
final val = (fv.jsonValue as List).first;
|
final val = (fv.jsonValue as List).first;
|
||||||
if (val is String && val.isNotEmpty) {
|
if (val is String && val.isNotEmpty) return format(val);
|
||||||
return val
|
}
|
||||||
.split('_')
|
if (fv.textValue != null && fv.textValue!.isNotEmpty) {
|
||||||
.map((w) => w.isNotEmpty
|
return format(fv.textValue!);
|
||||||
? '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}'
|
|
||||||
: '')
|
|
||||||
.join(' ');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (fv.textValue != null && fv.textValue!.isNotEmpty) return fv.textValue;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -594,23 +594,41 @@ class _AgentCardState extends State<_AgentCard> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Builder(builder: (_) {
|
Builder(builder: (_) {
|
||||||
final allParts = <String>[];
|
final allParts = <String>[];
|
||||||
// Extract states first, then cities (state-first display)
|
// Format raw slug/label. For cities, strip 2-letter
|
||||||
List<String> extractValues(dynamic fv) {
|
// state prefix ("nd_hurley" → "Hurley"). For states,
|
||||||
|
// uppercase 2-letter codes ("nd" → "ND"). Matches
|
||||||
|
// web's stripCityPrefix + extractAllValues.
|
||||||
|
String formatPart(String raw, {required bool isCity}) {
|
||||||
|
var s = raw.trim();
|
||||||
|
if (isCity) {
|
||||||
|
s = s.replaceFirst(
|
||||||
|
RegExp(r'^[a-z]{2}_', caseSensitive: false), '');
|
||||||
|
}
|
||||||
|
final titled = s
|
||||||
|
.split('_')
|
||||||
|
.map((w) => w.isNotEmpty
|
||||||
|
? '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}'
|
||||||
|
: '')
|
||||||
|
.join(' ');
|
||||||
|
if (!isCity && titled.length == 2) {
|
||||||
|
return titled.toUpperCase();
|
||||||
|
}
|
||||||
|
return titled;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> extractValues(dynamic fv,
|
||||||
|
{required bool isCity}) {
|
||||||
final out = <String>[];
|
final out = <String>[];
|
||||||
if (fv.jsonValue is List) {
|
if (fv.jsonValue is List) {
|
||||||
for (final v in (fv.jsonValue as List)) {
|
for (final v in (fv.jsonValue as List)) {
|
||||||
final s = v.toString().trim();
|
final s = v.toString().trim();
|
||||||
if (s.isEmpty) continue;
|
if (s.isEmpty) continue;
|
||||||
out.add(s
|
out.add(formatPart(s, isCity: isCity));
|
||||||
.split('_')
|
|
||||||
.map((w) => w.isNotEmpty
|
|
||||||
? '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}'
|
|
||||||
: '')
|
|
||||||
.join(' '));
|
|
||||||
}
|
}
|
||||||
} else if (fv.textValue != null &&
|
} else if (fv.textValue != null &&
|
||||||
(fv.textValue as String).isNotEmpty) {
|
(fv.textValue as String).isNotEmpty) {
|
||||||
out.add(fv.textValue as String);
|
out.add(formatPart(fv.textValue as String,
|
||||||
|
isCity: isCity));
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -626,7 +644,7 @@ class _AgentCardState extends State<_AgentCard> {
|
|||||||
for (final fv in agent.fieldValues) {
|
for (final fv in agent.fieldValues) {
|
||||||
final slug = fv.fieldSlug.toLowerCase();
|
final slug = fv.fieldSlug.toLowerCase();
|
||||||
if (slug == 'state' || slug == 'state_name') {
|
if (slug == 'state' || slug == 'state_name') {
|
||||||
for (final v in extractValues(fv)) {
|
for (final v in extractValues(fv, isCity: false)) {
|
||||||
addUnique(v);
|
addUnique(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -635,7 +653,7 @@ class _AgentCardState extends State<_AgentCard> {
|
|||||||
for (final fv in agent.fieldValues) {
|
for (final fv in agent.fieldValues) {
|
||||||
final slug = fv.fieldSlug.toLowerCase();
|
final slug = fv.fieldSlug.toLowerCase();
|
||||||
if (slug == 'city' || slug == 'city_name') {
|
if (slug == 'city' || slug == 'city_name') {
|
||||||
for (final v in extractValues(fv)) {
|
for (final v in extractValues(fv, isCity: true)) {
|
||||||
addUnique(v);
|
addUnique(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,13 +160,13 @@ class _AgentFilterSheetState extends State<AgentFilterSheet> {
|
|||||||
top: false,
|
top: false,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// Clear All
|
// Clear All — reset selections but keep the sheet open
|
||||||
|
// so the user sees the cleared state and can continue.
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_clearAll();
|
_clearAll();
|
||||||
widget.onClearAll();
|
widget.onClearAll();
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 29,
|
height: 29,
|
||||||
|
|||||||
Reference in New Issue
Block a user