From dc2b2799475482fcffbead6dbcb6155eb26aa17e Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Tue, 14 Apr 2026 09:07:37 +0530 Subject: [PATCH] feat: implement dynamic work environment section and improve availability data mapping with backend-resolved labels --- src/app/(agent)/agent/dashboard/page.tsx | 45 ++++++----- src/app/(user)/user/profile/[id]/page.tsx | 45 ++++++----- src/utils/profileDataMapper.ts | 97 ++++++++++++++--------- 3 files changed, 114 insertions(+), 73 deletions(-) diff --git a/src/app/(agent)/agent/dashboard/page.tsx b/src/app/(agent)/agent/dashboard/page.tsx index 3624fae..d2b8d6f 100644 --- a/src/app/(agent)/agent/dashboard/page.tsx +++ b/src/app/(agent)/agent/dashboard/page.tsx @@ -21,11 +21,12 @@ import { connectionRequestsService } from '@/services/connection-requests.servic import { uploadService } from '@/services/upload.service'; import { usersService } from '@/services/users.service'; import { testimonialsService, Testimonial } from '@/services/testimonials.service'; -import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, mapFieldValuesToContactInfo, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData, ContactInfoData } from '@/utils/profileDataMapper'; +import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, mapFieldValuesToContactInfo, mapFieldValuesToWorkEnvironment, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData, ContactInfoData, WorkEnvironmentData } from '@/utils/profileDataMapper'; -// Mock data for sections not yet available from API -const mockData = { - preferredWorkEnvironment: 'Preferred work environment includes on-site property visits, in-depth market research, client consultations, property tours, and active involvement in negotiations to ensure the best outcomes', +// Default work environment data +const defaultWorkEnvironmentData: WorkEnvironmentData = { + label: 'Preferred Work Environment', + content: '', }; // Default experience data when no field values are available @@ -59,6 +60,7 @@ const defaultProfileCardData: ProfileCardData = { const defaultAvailabilityData: AvailabilityData = { type: '', schedule: [], + label: 'Availability', }; // Default specialization fields data when no field values are available @@ -79,6 +81,7 @@ export default function AgentDashboard() { const [experienceData, setExperienceData] = useState(defaultExperience); const [profileCardData, setProfileCardData] = useState(defaultProfileCardData); const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); + const [workEnvironmentData, setWorkEnvironmentData] = useState(defaultWorkEnvironmentData); const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); const [contactInfoData, setContactInfoData] = useState(defaultContactInfoData); const [loading, setLoading] = useState(true); @@ -150,6 +153,10 @@ export default function AgentDashboard() { const mappedAvailability = mapFieldValuesToAvailability(fieldValuesResponse.fieldValues); setAvailabilityData(mappedAvailability); + // Map field values to work environment data + const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues); + setWorkEnvironmentData(mappedWorkEnv); + // Map field values to specialization fields data (only fields from "Specialization" section) const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); setSpecializationFieldsData(mappedSpecializationFields); @@ -452,11 +459,11 @@ export default function AgentDashboard() { {/* Info Cards Section */}
@@ -478,18 +485,20 @@ export default function AgentDashboard() {
} /> - - } - content={

{mockData.preferredWorkEnvironment}

} - /> + {workEnvironmentData.content && ( + + } + content={

{workEnvironmentData.content}

} + /> + )} {testimonials.length > 0 && ( (defaultProfileCardData); const [contactInfoData, setContactInfoData] = useState(defaultContactInfoData); const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); + const [workEnvironmentData, setWorkEnvironmentData] = useState(defaultWorkEnvironmentData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [imageError, setImageError] = useState(false); @@ -143,6 +146,10 @@ export default function AgentProfileView() { const mappedAvailability = mapFieldValuesToAvailability(fieldValuesResponse.fieldValues); setAvailabilityData(mappedAvailability); + // Map field values to work environment data + const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues); + setWorkEnvironmentData(mappedWorkEnv); + // Map testimonials for TestimonialsSection setTestimonials( testimonialsData.map((t) => ({ @@ -380,11 +387,11 @@ export default function AgentProfileView() { {/* Info Cards Section */}
@@ -406,18 +413,20 @@ export default function AgentProfileView() {
} /> - - } - content={

{mockData.preferredWorkEnvironment}

} - /> + {workEnvironmentData.content && ( + + } + content={

{workEnvironmentData.content}

} + /> + )} {testimonials.length > 0 && ( = { - 'mf_9_5': 'Monday - Friday, 9 AM - 5 PM', - 'mf_8_6': 'Monday - Friday, 8 AM - 6 PM', - 'weekends': 'Weekends', - 'evenings': 'Evenings', - 'flexible': 'Flexible Schedule', - 'full_time': 'Full-time', - 'part_time': 'Part-time', - '24_7': '24/7 Available', - 'by_appointment': 'By Appointment Only', - 'monday': 'Monday', - 'tuesday': 'Tuesday', - 'wednesday': 'Wednesday', - 'thursday': 'Thursday', - 'friday': 'Friday', - 'saturday': 'Saturday', - 'sunday': 'Sunday', -}; +// Work environment data structure +export interface WorkEnvironmentData { + label: string; + content: string; +} + +/** + * Maps field values to work environment data (label + content) + */ +export function mapFieldValuesToWorkEnvironment(fieldValues: FieldValueResponse[]): WorkEnvironmentData { + const field = fieldValues.find(f => f.fieldSlug === 'preferred_work_environment'); + return { + label: field?.fieldName || 'Preferred Work Environment', + content: (field?.value as string | undefined)?.trim() || '', + }; +} + +// Type-determining values (business hours → Full-time, etc.) +const fullTimeKeywords = ['full_time', 'mf_9_5', 'mf_8_6', 'full time', '9-5', '9 am', '9am']; +const partTimeKeywords = ['part_time', 'part time']; +const flexibleKeywords = ['flexible']; + +function matchesKeyword(value: string, keywords: string[]): boolean { + const normalized = value.toLowerCase(); + return keywords.some(kw => normalized.includes(kw)); +} /** * Maps field values to availability data + * Uses backend-resolved `valueLabel` and deduplicates by normalized label. */ export function mapFieldValuesToAvailability(fieldValues: FieldValueResponse[]): AvailabilityData { - const availabilityValues = getFieldValue(fieldValues, 'availability') as string[] | undefined; + const field = fieldValues.find(f => f.fieldSlug === 'availability'); + const label = field?.fieldName || 'Availability'; - if (!availabilityValues || availabilityValues.length === 0) { - return { - type: '', - schedule: [], - }; + const rawValues = (field?.value as string[] | undefined) || []; + const labelValues = (field?.valueLabel as string[] | undefined) || []; + + if (rawValues.length === 0) { + return { type: '', schedule: [], label }; } - // Map values to human-readable labels - const schedule = availabilityValues.map(val => - availabilityLabels[val] || val.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()) - ); + // Prefer backend-resolved labels, fall back to raw value with snake_case formatting + const resolvedLabels = rawValues.map((val, i) => { + const fromBackend = labelValues[i]; + if (fromBackend && typeof fromBackend === 'string' && fromBackend.trim()) { + return fromBackend.trim(); + } + return val.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); + }); - // Determine type based on values + // Deduplicate by normalized comparison (case/whitespace insensitive) + const seen = new Set(); + const schedule: string[] = []; + for (const item of resolvedLabels) { + const key = item.toLowerCase().replace(/[\s,.-]+/g, ''); + if (!seen.has(key)) { + seen.add(key); + schedule.push(item); + } + } + + // Determine availability type based on values OR resolved labels + const allValues = [...rawValues.map(v => String(v)), ...resolvedLabels]; let type = 'Available'; - if (availabilityValues.includes('full_time') || availabilityValues.includes('mf_9_5') || availabilityValues.includes('mf_8_6')) { + if (allValues.some(v => matchesKeyword(v, fullTimeKeywords))) { type = 'Full-time'; - } else if (availabilityValues.includes('part_time')) { + } else if (allValues.some(v => matchesKeyword(v, partTimeKeywords))) { type = 'Part-time'; - } else if (availabilityValues.includes('flexible')) { + } else if (allValues.some(v => matchesKeyword(v, flexibleKeywords))) { type = 'Flexible'; } - return { - type, - schedule, - }; + return { type, schedule, label }; } /**