From 1faed9140c11a623daaa84dd8d025044066aba5f Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Mon, 2 Feb 2026 10:19:55 +0530 Subject: [PATCH] feat: implement dynamic contact info and availability mapping from field values, and enhance phone number masking. --- src/app/(agent)/agent/dashboard/page.tsx | 36 +++++++----------- src/app/(user)/user/profile/[id]/page.tsx | 46 +++++++++++++++++------ src/components/profile/ContactInfo.tsx | 3 +- src/utils/profileDataMapper.ts | 26 +++++++++++++ 4 files changed, 77 insertions(+), 34 deletions(-) diff --git a/src/app/(agent)/agent/dashboard/page.tsx b/src/app/(agent)/agent/dashboard/page.tsx index e385528..2754f04 100644 --- a/src/app/(agent)/agent/dashboard/page.tsx +++ b/src/app/(agent)/agent/dashboard/page.tsx @@ -17,29 +17,10 @@ import { } from '@/components/profile'; import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service'; import { uploadService } from '@/services/upload.service'; -import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData } from '@/utils/profileDataMapper'; +import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, mapFieldValuesToContactInfo, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData, ContactInfoData } from '@/utils/profileDataMapper'; // Mock data for sections not yet available from API const mockData = { - experience: { - years: '10+ years', - contracts: '10+ Contracts', - licensingAreas: ['Colorado Springs', 'Monument', 'Falcon', 'Castle Rock', 'Fountain', 'Momentum'], - expertiseYears: [ - { area: 'Colorado', years: '10 yrs' }, - { area: 'Falcon', years: '10 yrs' }, - { area: 'Colorado', years: '10 yrs' }, - { area: 'Colorado', years: '10 yrs' }, - ], - certifications: [ - { name: 'Certified Residential Specialist (CRS)', org: 'RESIDENTIAL REAL ESTATE COUNCIL' }, - { name: 'Certified Residential Specialist (CRS)', org: 'RESIDENTIAL REAL ESTATE COUNCIL' }, - ], - }, - availability: { - type: 'Full-time', - days: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - }, 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.", testimonials: [ @@ -96,6 +77,12 @@ const defaultSpecializationFieldsData: SpecializationFieldsData = { fields: [], }; +// Default contact info data when no field values are available +const defaultContactInfoData: ContactInfoData = { + email: null, + phone: null, +}; + export default function AgentDashboard() { const { data: session } = useSession(); const [agentProfile, setAgentProfile] = useState(null); @@ -104,6 +91,7 @@ export default function AgentDashboard() { const [profileCardData, setProfileCardData] = useState(defaultProfileCardData); const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); + const [contactInfoData, setContactInfoData] = useState(defaultContactInfoData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false); @@ -154,6 +142,10 @@ export default function AgentDashboard() { const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); setSpecializationFieldsData(mappedSpecializationFields); + // Map field values to contact info data (email, phone) + const mappedContactInfo = mapFieldValuesToContactInfo(fieldValuesResponse.fieldValues); + setContactInfoData(mappedContactInfo); + // If avatar is an S3 key, fetch presigned URL if (profile.avatar && isS3Key(profile.avatar)) { try { @@ -334,8 +326,8 @@ export default function AgentDashboard() { {/* Contact Info */} diff --git a/src/app/(user)/user/profile/[id]/page.tsx b/src/app/(user)/user/profile/[id]/page.tsx index c3871e5..0ad6494 100644 --- a/src/app/(user)/user/profile/[id]/page.tsx +++ b/src/app/(user)/user/profile/[id]/page.tsx @@ -19,14 +19,10 @@ import { ConnectRequestModal } from '@/components/modals'; import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service'; import { connectionRequestsService, ConnectionStatus } from '@/services/connection-requests.service'; import { uploadService } from '@/services/upload.service'; -import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, mapFieldValuesToProfileCard, ExperienceData, SpecializationFieldsData, ProfileCardData } from '@/utils/profileDataMapper'; +import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, mapFieldValuesToProfileCard, mapFieldValuesToContactInfo, mapFieldValuesToAvailability, ExperienceData, SpecializationFieldsData, ProfileCardData, ContactInfoData, AvailabilityData } from '@/utils/profileDataMapper'; // Mock data for sections not yet available from API const mockData = { - availability: { - type: 'Full-time', - days: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - }, 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.", testimonials: [ @@ -77,6 +73,18 @@ const defaultProfileCardData: ProfileCardData = { state: null, }; +// Default contact info data when no field values are available +const defaultContactInfoData: ContactInfoData = { + email: null, + phone: null, +}; + +// Default availability data when no field values are available +const defaultAvailabilityData: AvailabilityData = { + type: '', + schedule: [], +}; + export default function AgentProfileView() { const params = useParams(); const id = params.id as string; @@ -87,6 +95,8 @@ export default function AgentProfileView() { const [experienceData, setExperienceData] = useState(defaultExperience); const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); const [profileCardData, setProfileCardData] = useState(defaultProfileCardData); + const [contactInfoData, setContactInfoData] = useState(defaultContactInfoData); + const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [imageError, setImageError] = useState(false); @@ -136,6 +146,14 @@ export default function AgentProfileView() { const mappedProfileCard = mapFieldValuesToProfileCard(fieldValuesResponse.fieldValues); setProfileCardData(mappedProfileCard); + // Map field values to contact info data (email, phone) + const mappedContactInfo = mapFieldValuesToContactInfo(fieldValuesResponse.fieldValues); + setContactInfoData(mappedContactInfo); + + // Map field values to availability data + const mappedAvailability = mapFieldValuesToAvailability(fieldValuesResponse.fieldValues); + setAvailabilityData(mappedAvailability); + // If avatar is an S3 key, fetch presigned URL if (profile.avatar && isS3Key(profile.avatar)) { try { @@ -304,8 +322,8 @@ export default function AgentProfileView() { {/* Contact Info */} @@ -347,11 +365,17 @@ export default function AgentProfileView() { } content={
-

{mockData.availability.type}

+ {availabilityData.type && ( +

{availabilityData.type}

+ )}
- {mockData.availability.days.map((day) => ( -

{day}

- ))} + {availabilityData.schedule.length > 0 ? ( + availabilityData.schedule.map((item, index) => ( +

{item}

+ )) + ) : ( +

Not specified

+ )}
} diff --git a/src/components/profile/ContactInfo.tsx b/src/components/profile/ContactInfo.tsx index 09f458a..40a00b8 100644 --- a/src/components/profile/ContactInfo.tsx +++ b/src/components/profile/ContactInfo.tsx @@ -18,7 +18,8 @@ function maskEmail(email: string): string { // Helper function to mask phone function maskPhone(phone: string): string { - if (phone.length <= 4) return '********'; + if (!phone || phone.trim() === '') return '-'; + if (phone.length <= 4) return '****' + phone; return phone.slice(0, -4).replace(/./g, '*') + phone.slice(-4); } diff --git a/src/utils/profileDataMapper.ts b/src/utils/profileDataMapper.ts index b57a5c3..e29f3b4 100644 --- a/src/utils/profileDataMapper.ts +++ b/src/utils/profileDataMapper.ts @@ -134,6 +134,32 @@ export interface ProfileCardData { state: string | null; } +// Contact info data structure +export interface ContactInfoData { + email: string | null; + phone: string | null; +} + +/** + * Maps field values to contact info data (email, phone) + */ +export function mapFieldValuesToContactInfo(fieldValues: FieldValueResponse[]): ContactInfoData { + // Get phone from various possible field slugs + const phoneNumber = getFieldValue(fieldValues, 'phone_number') as string | undefined; + const phone = getFieldValue(fieldValues, 'phone') as string | undefined; + const cellNumber = getFieldValue(fieldValues, 'cell_number') as string | undefined; + const officeNumber = getFieldValue(fieldValues, 'office_number') as string | undefined; + + // Get email from various possible field slugs + const emailField = getFieldValue(fieldValues, 'email') as string | undefined; + const emailAddress = getFieldValue(fieldValues, 'email_address') as string | undefined; + + return { + email: emailField || emailAddress || null, + phone: phoneNumber || phone || cellNumber || officeNumber || null, + }; +} + // Dynamic field data structure for SpecializationSection component // Each field within the "Specialization" section becomes a card export interface SpecializationFieldItem {