feat: implement dynamic contact info and availability mapping from field values, and enhance phone number masking.
This commit is contained in:
@@ -17,29 +17,10 @@ import {
|
|||||||
} from '@/components/profile';
|
} from '@/components/profile';
|
||||||
import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service';
|
import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service';
|
||||||
import { uploadService } from '@/services/upload.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
|
// Mock data for sections not yet available from API
|
||||||
const mockData = {
|
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',
|
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.",
|
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: [
|
testimonials: [
|
||||||
@@ -96,6 +77,12 @@ const defaultSpecializationFieldsData: SpecializationFieldsData = {
|
|||||||
fields: [],
|
fields: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Default contact info data when no field values are available
|
||||||
|
const defaultContactInfoData: ContactInfoData = {
|
||||||
|
email: null,
|
||||||
|
phone: null,
|
||||||
|
};
|
||||||
|
|
||||||
export default function AgentDashboard() {
|
export default function AgentDashboard() {
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const [agentProfile, setAgentProfile] = useState<AgentProfile | null>(null);
|
const [agentProfile, setAgentProfile] = useState<AgentProfile | null>(null);
|
||||||
@@ -104,6 +91,7 @@ export default function AgentDashboard() {
|
|||||||
const [profileCardData, setProfileCardData] = useState<ProfileCardData>(defaultProfileCardData);
|
const [profileCardData, setProfileCardData] = useState<ProfileCardData>(defaultProfileCardData);
|
||||||
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
|
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
|
||||||
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
|
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
|
||||||
|
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false);
|
const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false);
|
||||||
@@ -154,6 +142,10 @@ export default function AgentDashboard() {
|
|||||||
const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues);
|
const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues);
|
||||||
setSpecializationFieldsData(mappedSpecializationFields);
|
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 avatar is an S3 key, fetch presigned URL
|
||||||
if (profile.avatar && isS3Key(profile.avatar)) {
|
if (profile.avatar && isS3Key(profile.avatar)) {
|
||||||
try {
|
try {
|
||||||
@@ -334,8 +326,8 @@ export default function AgentDashboard() {
|
|||||||
|
|
||||||
{/* Contact Info */}
|
{/* Contact Info */}
|
||||||
<ContactInfo
|
<ContactInfo
|
||||||
email={agentProfile.email || agentProfile.user?.email || ''}
|
email={agentProfile.email || agentProfile.user?.email || contactInfoData.email || ''}
|
||||||
phone={agentProfile.phone || ''}
|
phone={agentProfile.phone || contactInfoData.phone || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -19,14 +19,10 @@ import { ConnectRequestModal } from '@/components/modals';
|
|||||||
import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service';
|
import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service';
|
||||||
import { connectionRequestsService, ConnectionStatus } from '@/services/connection-requests.service';
|
import { connectionRequestsService, ConnectionStatus } from '@/services/connection-requests.service';
|
||||||
import { uploadService } from '@/services/upload.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
|
// Mock data for sections not yet available from API
|
||||||
const mockData = {
|
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',
|
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.",
|
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: [
|
testimonials: [
|
||||||
@@ -77,6 +73,18 @@ const defaultProfileCardData: ProfileCardData = {
|
|||||||
state: null,
|
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() {
|
export default function AgentProfileView() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const id = params.id as string;
|
const id = params.id as string;
|
||||||
@@ -87,6 +95,8 @@ export default function AgentProfileView() {
|
|||||||
const [experienceData, setExperienceData] = useState<ExperienceData>(defaultExperience);
|
const [experienceData, setExperienceData] = useState<ExperienceData>(defaultExperience);
|
||||||
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
|
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
|
||||||
const [profileCardData, setProfileCardData] = useState<ProfileCardData>(defaultProfileCardData);
|
const [profileCardData, setProfileCardData] = useState<ProfileCardData>(defaultProfileCardData);
|
||||||
|
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
|
||||||
|
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [imageError, setImageError] = useState(false);
|
const [imageError, setImageError] = useState(false);
|
||||||
@@ -136,6 +146,14 @@ export default function AgentProfileView() {
|
|||||||
const mappedProfileCard = mapFieldValuesToProfileCard(fieldValuesResponse.fieldValues);
|
const mappedProfileCard = mapFieldValuesToProfileCard(fieldValuesResponse.fieldValues);
|
||||||
setProfileCardData(mappedProfileCard);
|
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 avatar is an S3 key, fetch presigned URL
|
||||||
if (profile.avatar && isS3Key(profile.avatar)) {
|
if (profile.avatar && isS3Key(profile.avatar)) {
|
||||||
try {
|
try {
|
||||||
@@ -304,8 +322,8 @@ export default function AgentProfileView() {
|
|||||||
|
|
||||||
{/* Contact Info */}
|
{/* Contact Info */}
|
||||||
<ContactInfo
|
<ContactInfo
|
||||||
email={agentProfile.email || agentProfile.user?.email || ''}
|
email={agentProfile.email || agentProfile.user?.email || contactInfoData.email || ''}
|
||||||
phone={agentProfile.phone || ''}
|
phone={agentProfile.phone || contactInfoData.phone || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -347,11 +365,17 @@ export default function AgentProfileView() {
|
|||||||
}
|
}
|
||||||
content={
|
content={
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold font-serif text-[14px] leading-[19px] text-[#00293D] mb-2">{mockData.availability.type}</p>
|
{availabilityData.type && (
|
||||||
|
<p className="font-semibold font-serif text-[14px] leading-[19px] text-[#00293D] mb-2">{availabilityData.type}</p>
|
||||||
|
)}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{mockData.availability.days.map((day) => (
|
{availabilityData.schedule.length > 0 ? (
|
||||||
<p key={day} className="font-normal font-serif text-[14px] leading-[19px] text-[#00293D]">{day}</p>
|
availabilityData.schedule.map((item, index) => (
|
||||||
))}
|
<p key={index} className="font-normal font-serif text-[14px] leading-[19px] text-[#00293D]">{item}</p>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="font-normal font-serif text-[14px] leading-[19px] text-[#00293D]/60">Not specified</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ function maskEmail(email: string): string {
|
|||||||
|
|
||||||
// Helper function to mask phone
|
// Helper function to mask phone
|
||||||
function maskPhone(phone: string): string {
|
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);
|
return phone.slice(0, -4).replace(/./g, '*') + phone.slice(-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,32 @@ export interface ProfileCardData {
|
|||||||
state: string | null;
|
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
|
// Dynamic field data structure for SpecializationSection component
|
||||||
// Each field within the "Specialization" section becomes a card
|
// Each field within the "Specialization" section becomes a card
|
||||||
export interface SpecializationFieldItem {
|
export interface SpecializationFieldItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user