From 4ffd6305b175d645489218f115a94ff6a42982cc Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sun, 25 Jan 2026 01:15:02 +0530 Subject: [PATCH] fix --- src/app/(agent)/agent/dashboard/page.tsx | 21 +-- src/app/(user)/user/profile/[id]/page.tsx | 21 +-- .../profile/SpecializationSection.tsx | 134 ++++++++---------- src/utils/profileDataMapper.ts | 112 +++++++++++++++ 4 files changed, 198 insertions(+), 90 deletions(-) diff --git a/src/app/(agent)/agent/dashboard/page.tsx b/src/app/(agent)/agent/dashboard/page.tsx index b7d6d44..0a954e6 100644 --- a/src/app/(agent)/agent/dashboard/page.tsx +++ b/src/app/(agent)/agent/dashboard/page.tsx @@ -17,7 +17,7 @@ import { } from '@/components/profile'; import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service'; import { uploadService } from '@/services/upload.service'; -import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, ExperienceData, ProfileCardData, AvailabilityData } from '@/utils/profileDataMapper'; +import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData } from '@/utils/profileDataMapper'; // Mock data for sections not yet available from API const mockData = { @@ -42,13 +42,6 @@ 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', testimonialHighlight: "The most amazing experience I've had as a real estate professional is helping a family secure their dream home and seeing their happiness when they received the keys.", - specialization: { - types: ['Buyers', 'Sellers', 'First time Buyers', 'First time Sellers', 'Divorce'], - loanTypes: ['Conventional', 'FHA', 'VA', 'USDA'], - propertyTypes: ['SFR', 'Town home', 'Rural', 'Luxury', 'Condo'], - hobbies: ['Boating', 'Horses', 'RV', 'Automotive', 'Aviation'], - pricePoints: ['Under $100K', '$100K – $300K', '$300K – $500K', '$500K – $1M', '$1M and above'], - }, testimonials: [ { id: 1, @@ -96,6 +89,11 @@ const defaultAvailabilityData: AvailabilityData = { schedule: [], }; +// Default specialization fields data when no field values are available +const defaultSpecializationFieldsData: SpecializationFieldsData = { + fields: [], +}; + export default function AgentDashboard() { const { data: session } = useSession(); const [agentProfile, setAgentProfile] = useState(null); @@ -103,6 +101,7 @@ export default function AgentDashboard() { const [experienceData, setExperienceData] = useState(defaultExperience); const [profileCardData, setProfileCardData] = useState(defaultProfileCardData); const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); + const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false); @@ -146,6 +145,10 @@ export default function AgentDashboard() { const mappedAvailability = mapFieldValuesToAvailability(fieldValuesResponse.fieldValues); setAvailabilityData(mappedAvailability); + // Map field values to specialization fields data (only fields from "Specialization" section) + const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); + setSpecializationFieldsData(mappedSpecializationFields); + // If avatar is an S3 key, fetch presigned URL if (profile.avatar && isS3Key(profile.avatar)) { try { @@ -382,7 +385,7 @@ export default function AgentDashboard() { {/* Specialization Section */} - + {/* Testimonials Section */} diff --git a/src/app/(user)/user/profile/[id]/page.tsx b/src/app/(user)/user/profile/[id]/page.tsx index e68edef..72303e9 100644 --- a/src/app/(user)/user/profile/[id]/page.tsx +++ b/src/app/(user)/user/profile/[id]/page.tsx @@ -16,7 +16,7 @@ import { } from '@/components/profile'; import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service'; import { getProxyImageUrl } from '@/lib/imageProxy'; -import { mapFieldValuesToExperience, ExperienceData } from '@/utils/profileDataMapper'; +import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, ExperienceData, SpecializationFieldsData } from '@/utils/profileDataMapper'; // Mock data for sections not yet available from API const mockData = { @@ -26,13 +26,6 @@ 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', testimonialHighlight: "The most amazing experience I've had as a real estate professional is helping a family secure their dream home and seeing their happiness when they received the keys.", - specialization: { - types: ['Buyers', 'Sellers', 'First time Buyers', 'First time Sellers', 'Divorce'], - loanTypes: ['Conventional', 'FHA', 'VA', 'USDA'], - propertyTypes: ['SFR', 'Town home', 'Rural', 'Luxury', 'Condo'], - hobbies: ['Boating', 'Horses', 'RV', 'Automotive', 'Aviation'], - pricePoints: ['Under $100K', '$100K – $300K', '$300K – $500K', '$500K – $1M', '$1M and above'], - }, testimonials: [ { id: 1, @@ -67,6 +60,11 @@ const defaultExperience: ExperienceData = { certifications: [], }; +// Default specialization fields data when no field values are available +const defaultSpecializationFieldsData: SpecializationFieldsData = { + fields: [], +}; + export default function AgentProfileView() { const params = useParams(); const id = params.id as string; @@ -74,6 +72,7 @@ export default function AgentProfileView() { const [agentProfile, setAgentProfile] = useState(null); const [fieldValues, setFieldValues] = useState([]); const [experienceData, setExperienceData] = useState(defaultExperience); + const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [imageError, setImageError] = useState(false); @@ -101,6 +100,10 @@ export default function AgentProfileView() { // Map field values to experience data structure const mappedExperience = mapFieldValuesToExperience(fieldValuesResponse.fieldValues); setExperienceData(mappedExperience); + + // Map field values to specialization fields data (only fields from "Specialization" section) + const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); + setSpecializationFieldsData(mappedSpecializationFields); } catch (err) { console.error('Failed to fetch profile:', err); setError('Failed to load profile data'); @@ -284,7 +287,7 @@ export default function AgentProfileView() { {/* Specialization Section */} - + {/* Testimonials Section */} diff --git a/src/components/profile/SpecializationSection.tsx b/src/components/profile/SpecializationSection.tsx index 9c953b5..88bfceb 100644 --- a/src/components/profile/SpecializationSection.tsx +++ b/src/components/profile/SpecializationSection.tsx @@ -2,90 +2,80 @@ import Image from 'next/image'; import { SpecializationCard } from './SpecializationCard'; - -interface SpecializationData { - types: string[]; - loanTypes: string[]; - propertyTypes: string[]; - hobbies: string[]; - pricePoints: string[]; -} +import { SpecializationFieldsData } from '@/utils/profileDataMapper'; interface SpecializationSectionProps { - specialization: SpecializationData; + fieldsData: SpecializationFieldsData; } -export function SpecializationSection({ specialization }: SpecializationSectionProps) { +export function SpecializationSection({ fieldsData }: SpecializationSectionProps) { + const { fields } = fieldsData; + + // Don't render if no fields with data + if (!fields || fields.length === 0) { + return null; + } + + // Split fields for layout: first 3 in top row, rest in bottom row + const topRowFields = fields.slice(0, 3); + const bottomRowFields = fields.slice(3); + return (

Specialization

Area Of Expertise and Focus

-
- 0 && ( +
+ {topRowFields.map((field) => ( + + } /> - } - /> - + )} + + {/* Bottom row - remaining cards */} + {bottomRowFields.length > 0 && ( +
+ {bottomRowFields.map((field) => ( + + } /> - } - /> - - } - /> -
-
- - } - /> - - } - /> -
+ ))} +
+ )}
); } diff --git a/src/utils/profileDataMapper.ts b/src/utils/profileDataMapper.ts index 88a3a73..8521c83 100644 --- a/src/utils/profileDataMapper.ts +++ b/src/utils/profileDataMapper.ts @@ -132,6 +132,19 @@ export interface ProfileCardData { serviceAreas: string[]; } +// Dynamic field data structure for SpecializationSection component +// Each field within the "Specialization" section becomes a card +export interface SpecializationFieldItem { + fieldSlug: string; + fieldName: string; + icon?: string; + values: string[]; +} + +export interface SpecializationFieldsData { + fields: SpecializationFieldItem[]; +} + // Availability data structure export interface AvailabilityData { type: string; @@ -219,3 +232,102 @@ export function mapFieldValuesToProfileCard(fieldValues: FieldValueResponse[]): serviceAreas: serviceAreas || licensedAreas || coverageAreas || [], }; } + +// Field slug to icon mapping for specialization fields +const fieldIconMapping: Record = { + 'client_specialization': '/assets/icons/home-icon.svg', + 'property_type': '/assets/icons/chart-icon.svg', + 'loan_type': '/assets/icons/wallet-icon.svg', + 'areas_expertise_years': '/assets/icons/certification-icon.svg', + 'licensing_areas': '/assets/icons/location-icon.svg', + 'hobbies': '/assets/icons/heart-icon.svg', + 'hobbies_interests': '/assets/icons/heart-icon.svg', + 'price_points': '/assets/icons/wallet-icon.svg', + 'price_point': '/assets/icons/wallet-icon.svg', + 'default': '/assets/icons/home-icon.svg', +}; + +/** + * Get icon for a field based on its slug + */ +function getFieldIcon(fieldSlug: string): string { + const normalizedSlug = fieldSlug.toLowerCase(); + + // Check for exact match first + if (fieldIconMapping[normalizedSlug]) { + return fieldIconMapping[normalizedSlug]; + } + + // Check for partial matches + if (normalizedSlug.includes('loan')) return fieldIconMapping['loan_type']; + if (normalizedSlug.includes('property')) return fieldIconMapping['property_type']; + if (normalizedSlug.includes('hobby') || normalizedSlug.includes('interest')) return fieldIconMapping['hobbies']; + if (normalizedSlug.includes('price')) return fieldIconMapping['price_points']; + if (normalizedSlug.includes('client') || normalizedSlug.includes('special')) return fieldIconMapping['client_specialization']; + if (normalizedSlug.includes('license') || normalizedSlug.includes('area')) return fieldIconMapping['licensing_areas']; + if (normalizedSlug.includes('expertise') || normalizedSlug.includes('certification')) return fieldIconMapping['areas_expertise_years']; + + return fieldIconMapping['default']; +} + +/** + * Convert field value to string array for display + */ +function valueToStringArray(value: unknown): string[] { + if (!value) return []; + + if (Array.isArray(value)) { + return value.map(v => String(v)).filter(v => v.trim() !== ''); + } + + if (typeof value === 'string' && value.trim()) { + return [value]; + } + + if (typeof value === 'number' || typeof value === 'boolean') { + return [String(value)]; + } + + return []; +} + +/** + * Maps field values from the "Specialization" section to individual field cards + * Each field within the Specialization section becomes its own card + */ +export function mapFieldValuesToSpecializationFields(fieldValues: FieldValueResponse[]): SpecializationFieldsData { + // Filter only fields from the "Specialization" section + const specializationFields = fieldValues.filter(field => { + const sectionSlug = field.sectionSlug?.toLowerCase() || ''; + return sectionSlug === 'specialization' || sectionSlug.includes('specialization'); + }); + + // Create a card for each field + const fields: SpecializationFieldItem[] = []; + + for (const field of specializationFields) { + const { fieldSlug, fieldName, value } = field; + + // Skip if no field info or no value + if (!fieldSlug || !fieldName) continue; + + const values = valueToStringArray(value); + if (values.length === 0) continue; + + fields.push({ + fieldSlug, + fieldName, + icon: getFieldIcon(fieldSlug), + values, + }); + } + + return { fields }; +} + +/** + * Check if specialization fields data has any meaningful content + */ +export function hasSpecializationFieldsData(data: SpecializationFieldsData): boolean { + return data.fields.length > 0 && data.fields.some(f => f.values.length > 0); +}