feat: enhance profile input fields with new styling and customization options, and update calendar and location icons
This commit is contained in:
@@ -73,7 +73,11 @@ class AgentDetailState {
|
||||
String get contractsClosed {
|
||||
final val = _getFieldValue('contracts_completed');
|
||||
if (val == null) return '-';
|
||||
final num = val is int ? val : int.tryParse(val.toString()) ?? -1;
|
||||
final str = val.toString();
|
||||
// Parse leading integer from value (handles "10-20", "50+", "<3", etc.)
|
||||
final match = RegExp(r'\d+').firstMatch(str);
|
||||
if (match == null) return '-';
|
||||
final num = int.tryParse(match.group(0)!) ?? -1;
|
||||
if (num < 0) return '-';
|
||||
if (num >= 100) return '100+ Contracts';
|
||||
if (num >= 50) return '50+ Contracts';
|
||||
@@ -115,6 +119,127 @@ class AgentDetailState {
|
||||
.toList();
|
||||
}
|
||||
|
||||
// ── Expertise tags (matching web mapFieldValuesToProfileCard) ──
|
||||
|
||||
List<String> get expertiseTags {
|
||||
const slugs = [
|
||||
'about_me_expertise',
|
||||
'expertise_areas',
|
||||
'specializations',
|
||||
'areas_of_expertise',
|
||||
'expertise',
|
||||
'specialization',
|
||||
];
|
||||
for (final slug in slugs) {
|
||||
final val = _getFieldValue(slug);
|
||||
if (val is List && val.isNotEmpty) {
|
||||
return val.map((v) => titleCase(v.toString())).toList();
|
||||
}
|
||||
}
|
||||
// Fallback to agent model specializations
|
||||
if (agent != null) {
|
||||
final tags = agent!.specializations;
|
||||
if (tags.isNotEmpty) return tags;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// ── Description / Bio (from fieldValues first, then agent model) ──
|
||||
|
||||
String get descriptionText {
|
||||
// Check detail fieldValues for 'description' slug
|
||||
for (final fv in fieldValues) {
|
||||
if (fv.fieldSlug == 'description' &&
|
||||
fv.textValue != null &&
|
||||
fv.textValue!.isNotEmpty) {
|
||||
return fv.textValue!;
|
||||
}
|
||||
}
|
||||
// Fallback to agent model bio
|
||||
return agent?.bio ?? '';
|
||||
}
|
||||
|
||||
// ── Location (from fieldValues, matching web profileDataMapper) ──
|
||||
|
||||
/// All location parts as individual items (for "+N more" display).
|
||||
/// Sources: detail fieldValues city/state arrays → agent model → serviceAreas
|
||||
List<String> get locationParts {
|
||||
final parts = <String>[];
|
||||
|
||||
// 1. Extract from detail fieldValues (full arrays, not just first item)
|
||||
List<String>? fvCities;
|
||||
List<String>? fvStates;
|
||||
for (final fv in fieldValues) {
|
||||
final slug = fv.fieldSlug.toLowerCase();
|
||||
if (slug == 'city' || slug == 'city_name') {
|
||||
fvCities = _extractAllValuesAsList(fv);
|
||||
} else if (slug == 'state' || slug == 'state_name') {
|
||||
fvStates = _extractAllValuesAsList(fv);
|
||||
}
|
||||
}
|
||||
if (fvCities != null) parts.addAll(fvCities);
|
||||
if (fvStates != null) {
|
||||
for (final s in fvStates) {
|
||||
if (!parts.any((p) => p.toLowerCase() == s.toLowerCase())) {
|
||||
parts.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: agent model direct city/state
|
||||
if (parts.isEmpty && agent != null) {
|
||||
if (agent!.city != null && agent!.city!.isNotEmpty) {
|
||||
parts.add(agent!.city!);
|
||||
}
|
||||
if (agent!.state != null && agent!.state!.isNotEmpty) {
|
||||
parts.add(agent!.state!);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Fallback: serviceAreas
|
||||
if (parts.isEmpty && agent != null) {
|
||||
for (final area in agent!.serviceAreas) {
|
||||
final trimmed = area.trim();
|
||||
if (trimmed.isNotEmpty) parts.add(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// Combined location text for single-line display.
|
||||
String get locationText {
|
||||
final parts = locationParts;
|
||||
if (parts.isEmpty) return '';
|
||||
return parts.join(', ');
|
||||
}
|
||||
|
||||
/// Extract all values from a field as a list of title-cased strings.
|
||||
static List<String>? _extractAllValuesAsList(AgentFieldValue fv) {
|
||||
if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) {
|
||||
return (fv.jsonValue as List)
|
||||
.map((v) => titleCase(v.toString()))
|
||||
.toList();
|
||||
}
|
||||
if (fv.textValue != null && fv.textValue!.isNotEmpty) {
|
||||
return [titleCase(fv.textValue!)];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Extract all values from a field (array or string), title-cased.
|
||||
static String? _extractAllValues(AgentFieldValue fv) {
|
||||
if (fv.jsonValue is List && (fv.jsonValue as List).isNotEmpty) {
|
||||
return (fv.jsonValue as List)
|
||||
.map((v) => titleCase(v.toString()))
|
||||
.join(', ');
|
||||
}
|
||||
if (fv.textValue != null && fv.textValue!.isNotEmpty) {
|
||||
return titleCase(fv.textValue!);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Contact info (masked) ──
|
||||
|
||||
String? get contactEmail {
|
||||
|
||||
Reference in New Issue
Block a user