feat: add personal tagline support and improve snake_case string formatting in profile data mapping

This commit is contained in:
pradeepkumar
2026-04-14 10:11:02 +05:30
parent dc2b279947
commit 9042913e22
4 changed files with 70 additions and 20 deletions

View File

@@ -21,7 +21,7 @@ import { connectionRequestsService } from '@/services/connection-requests.servic
import { uploadService } from '@/services/upload.service'; import { uploadService } from '@/services/upload.service';
import { usersService } from '@/services/users.service'; import { usersService } from '@/services/users.service';
import { testimonialsService, Testimonial } from '@/services/testimonials.service'; import { testimonialsService, Testimonial } from '@/services/testimonials.service';
import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, mapFieldValuesToContactInfo, mapFieldValuesToWorkEnvironment, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData, ContactInfoData, WorkEnvironmentData } from '@/utils/profileDataMapper'; import { mapFieldValuesToExperience, mapFieldValuesToProfileCard, mapFieldValuesToAvailability, mapFieldValuesToSpecializationFields, mapFieldValuesToContactInfo, mapFieldValuesToWorkEnvironment, mapFieldValuesToPersonalTagline, ExperienceData, ProfileCardData, AvailabilityData, SpecializationFieldsData, ContactInfoData, WorkEnvironmentData, PersonalTaglineData } from '@/utils/profileDataMapper';
// Default work environment data // Default work environment data
const defaultWorkEnvironmentData: WorkEnvironmentData = { const defaultWorkEnvironmentData: WorkEnvironmentData = {
@@ -29,6 +29,12 @@ const defaultWorkEnvironmentData: WorkEnvironmentData = {
content: '', content: '',
}; };
// Default personal tagline data
const defaultPersonalTaglineData: PersonalTaglineData = {
label: 'Personal Tagline',
content: '',
};
// Default experience data when no field values are available // Default experience data when no field values are available
const defaultExperience: ExperienceData = { const defaultExperience: ExperienceData = {
years: '-', years: '-',
@@ -82,6 +88,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 [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData); const [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData);
const [personalTaglineData, setPersonalTaglineData] = useState<PersonalTaglineData>(defaultPersonalTaglineData);
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData); const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData); const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -157,6 +164,10 @@ export default function AgentDashboard() {
const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues); const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues);
setWorkEnvironmentData(mappedWorkEnv); setWorkEnvironmentData(mappedWorkEnv);
// Map field values to personal tagline data
const mappedTagline = mapFieldValuesToPersonalTagline(fieldValuesResponse.fieldValues);
setPersonalTaglineData(mappedTagline);
// Map field values to specialization fields data (only fields from "Specialization" section) // Map field values to specialization fields data (only fields from "Specialization" section)
const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues);
setSpecializationFieldsData(mappedSpecializationFields); setSpecializationFieldsData(mappedSpecializationFields);

View File

@@ -21,7 +21,7 @@ import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agen
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 { testimonialsService, Testimonial } from '@/services/testimonials.service'; import { testimonialsService, Testimonial } from '@/services/testimonials.service';
import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, mapFieldValuesToProfileCard, mapFieldValuesToContactInfo, mapFieldValuesToAvailability, mapFieldValuesToWorkEnvironment, ExperienceData, SpecializationFieldsData, ProfileCardData, ContactInfoData, AvailabilityData, WorkEnvironmentData } from '@/utils/profileDataMapper'; import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, mapFieldValuesToProfileCard, mapFieldValuesToContactInfo, mapFieldValuesToAvailability, mapFieldValuesToWorkEnvironment, mapFieldValuesToPersonalTagline, ExperienceData, SpecializationFieldsData, ProfileCardData, ContactInfoData, AvailabilityData, WorkEnvironmentData, PersonalTaglineData } from '@/utils/profileDataMapper';
// Default work environment data // Default work environment data
const defaultWorkEnvironmentData: WorkEnvironmentData = { const defaultWorkEnvironmentData: WorkEnvironmentData = {
@@ -29,6 +29,12 @@ const defaultWorkEnvironmentData: WorkEnvironmentData = {
content: '', content: '',
}; };
// Default personal tagline data
const defaultPersonalTaglineData: PersonalTaglineData = {
label: 'Personal Tagline',
content: '',
};
// Default experience data when no field values are available // Default experience data when no field values are available
const defaultExperience: ExperienceData = { const defaultExperience: ExperienceData = {
years: '-', years: '-',
@@ -87,6 +93,7 @@ export default function AgentProfileView() {
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData); const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData); const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
const [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData); const [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData);
const [personalTaglineData, setPersonalTaglineData] = useState<PersonalTaglineData>(defaultPersonalTaglineData);
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);
@@ -150,6 +157,10 @@ export default function AgentProfileView() {
const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues); const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues);
setWorkEnvironmentData(mappedWorkEnv); setWorkEnvironmentData(mappedWorkEnv);
// Map field values to personal tagline data
const mappedTagline = mapFieldValuesToPersonalTagline(fieldValuesResponse.fieldValues);
setPersonalTaglineData(mappedTagline);
// Map testimonials for TestimonialsSection // Map testimonials for TestimonialsSection
setTestimonials( setTestimonials(
testimonialsData.map((t) => ({ testimonialsData.map((t) => ({
@@ -427,18 +438,18 @@ export default function AgentProfileView() {
content={<p className="font-serif font-semibold text-[14px] leading-[19px] text-[#00293D]">{workEnvironmentData.content}</p>} content={<p className="font-serif font-semibold text-[14px] leading-[19px] text-[#00293D]">{workEnvironmentData.content}</p>}
/> />
)} )}
{testimonials.length > 0 && ( {personalTaglineData.content && (
<InfoCard <InfoCard
title="" title={personalTaglineData.label}
icon={ icon={
<Image <Image
src="/assets/icons/testimonial-star-icon.svg" src="/assets/icons/testimonial-star-icon.svg"
alt="Testimonial" alt={personalTaglineData.label}
width={28} width={28}
height={28} height={28}
/> />
} }
content={<p className="text-[14px] font-semibold font-serif leading-[19px] text-center text-[#00293D]">&ldquo;{testimonials[0].text.length > 150 ? testimonials[0].text.substring(0, 150) + '...' : testimonials[0].text}&rdquo;</p>} content={<p className="text-[14px] font-semibold font-serif leading-[19px] text-center text-[#00293D]">&ldquo;{personalTaglineData.content.length > 150 ? personalTaglineData.content.substring(0, 150) + '...' : personalTaglineData.content}&rdquo;</p>}
/> />
)} )}
</div> </div>

View File

@@ -17,12 +17,8 @@ export function ExperienceSection({ experience }: ExperienceSectionProps) {
const [showAllExpertise, setShowAllExpertise] = useState(false); const [showAllExpertise, setShowAllExpertise] = useState(false);
const [showAllCertifications, setShowAllCertifications] = useState(false); const [showAllCertifications, setShowAllCertifications] = useState(false);
// Build agency display — append designation if flag is set // Show agency block whenever name OR designation is filled
const agencyDisplay = experience.agencyName const hasAgency = !!(experience.agencyName || experience.agencyDesignation);
? (experience.agencyShowDesignation && experience.agencyDesignation
? `${experience.agencyName} - ${experience.agencyDesignation}`
: experience.agencyName)
: '';
// Calculate how many more items are hidden // Calculate how many more items are hidden
const licensingRemaining = experience.licensingAreas.length - INITIAL_DISPLAY_COUNT; const licensingRemaining = experience.licensingAreas.length - INITIAL_DISPLAY_COUNT;
@@ -46,15 +42,20 @@ export function ExperienceSection({ experience }: ExperienceSectionProps) {
<div className="flex flex-col lg:flex-row gap-4 lg:gap-8"> <div className="flex flex-col lg:flex-row gap-4 lg:gap-8">
{/* Left Column */} {/* Left Column */}
<div className="flex-1 space-y-4"> <div className="flex-1 space-y-4">
{agencyDisplay && ( {hasAgency && (
<> <>
<div> <div>
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">{experience.agencySectionLabel}</p> <p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">{experience.agencySectionLabel}</p>
<ul className="list-disc list-inside"> {experience.agencyName && (
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]"> <p className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">
{agencyDisplay} {experience.agencyName}
</li> </p>
</ul> )}
{experience.agencyDesignation && (
<p className="font-serif font-normal text-[14px] leading-[21px] text-[#00293D]/70">
{experience.agencyDesignation}
</p>
)}
</div> </div>
{/* Horizontal divider */} {/* Horizontal divider */}
<div className="w-full h-0 border-t-[0.5px] border-solid border-[#00293D]/20" /> <div className="w-full h-0 border-t-[0.5px] border-solid border-[#00293D]/20" />

View File

@@ -239,6 +239,23 @@ export function mapFieldValuesToWorkEnvironment(fieldValues: FieldValueResponse[
}; };
} }
// Personal tagline data structure
export interface PersonalTaglineData {
label: string;
content: string;
}
/**
* Maps field values to personal tagline data (label + content)
*/
export function mapFieldValuesToPersonalTagline(fieldValues: FieldValueResponse[]): PersonalTaglineData {
const field = fieldValues.find(f => f.fieldSlug === 'personal_tagline');
return {
label: field?.fieldName || 'Personal Tagline',
content: (field?.value as string | undefined)?.trim() || '',
};
}
// Type-determining values (business hours → Full-time, etc.) // 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 fullTimeKeywords = ['full_time', 'mf_9_5', 'mf_8_6', 'full time', '9-5', '9 am', '9am'];
const partTimeKeywords = ['part_time', 'part time']; const partTimeKeywords = ['part_time', 'part time'];
@@ -264,13 +281,23 @@ export function mapFieldValuesToAvailability(fieldValues: FieldValueResponse[]):
return { type: '', schedule: [], label }; return { type: '', schedule: [], label };
} }
// Helper to format snake_case strings to Title Case (for unresolved raw values)
const formatIfSnakeCase = (s: string): string => {
const trimmed = s.trim();
// If it looks like a snake_case key (e.g. "mf_9_5"), format it
if (/^[a-z0-9]+(_[a-z0-9]+)+$/i.test(trimmed)) {
return trimmed.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
}
return trimmed;
};
// Prefer backend-resolved labels, fall back to raw value with snake_case formatting // Prefer backend-resolved labels, fall back to raw value with snake_case formatting
const resolvedLabels = rawValues.map((val, i) => { const resolvedLabels = rawValues.map((val, i) => {
const fromBackend = labelValues[i]; const fromBackend = labelValues[i];
if (fromBackend && typeof fromBackend === 'string' && fromBackend.trim()) { if (fromBackend && typeof fromBackend === 'string' && fromBackend.trim()) {
return fromBackend.trim(); return formatIfSnakeCase(fromBackend);
} }
return val.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); return formatIfSnakeCase(val);
}); });
// Deduplicate by normalized comparison (case/whitespace insensitive) // Deduplicate by normalized comparison (case/whitespace insensitive)