feat: add personal tagline support and improve snake_case string formatting in profile data mapping
This commit is contained in:
@@ -21,7 +21,7 @@ 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, 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
|
||||
const defaultWorkEnvironmentData: WorkEnvironmentData = {
|
||||
@@ -29,6 +29,12 @@ const defaultWorkEnvironmentData: WorkEnvironmentData = {
|
||||
content: '',
|
||||
};
|
||||
|
||||
// Default personal tagline data
|
||||
const defaultPersonalTaglineData: PersonalTaglineData = {
|
||||
label: 'Personal Tagline',
|
||||
content: '',
|
||||
};
|
||||
|
||||
// Default experience data when no field values are available
|
||||
const defaultExperience: ExperienceData = {
|
||||
years: '-',
|
||||
@@ -82,6 +88,7 @@ export default function AgentDashboard() {
|
||||
const [profileCardData, setProfileCardData] = useState<ProfileCardData>(defaultProfileCardData);
|
||||
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
|
||||
const [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData);
|
||||
const [personalTaglineData, setPersonalTaglineData] = useState<PersonalTaglineData>(defaultPersonalTaglineData);
|
||||
const [specializationFieldsData, setSpecializationFieldsData] = useState<SpecializationFieldsData>(defaultSpecializationFieldsData);
|
||||
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -157,6 +164,10 @@ export default function AgentDashboard() {
|
||||
const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues);
|
||||
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)
|
||||
const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues);
|
||||
setSpecializationFieldsData(mappedSpecializationFields);
|
||||
|
||||
@@ -21,7 +21,7 @@ import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agen
|
||||
import { connectionRequestsService, ConnectionStatus } from '@/services/connection-requests.service';
|
||||
import { uploadService } from '@/services/upload.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
|
||||
const defaultWorkEnvironmentData: WorkEnvironmentData = {
|
||||
@@ -29,6 +29,12 @@ const defaultWorkEnvironmentData: WorkEnvironmentData = {
|
||||
content: '',
|
||||
};
|
||||
|
||||
// Default personal tagline data
|
||||
const defaultPersonalTaglineData: PersonalTaglineData = {
|
||||
label: 'Personal Tagline',
|
||||
content: '',
|
||||
};
|
||||
|
||||
// Default experience data when no field values are available
|
||||
const defaultExperience: ExperienceData = {
|
||||
years: '-',
|
||||
@@ -87,6 +93,7 @@ export default function AgentProfileView() {
|
||||
const [contactInfoData, setContactInfoData] = useState<ContactInfoData>(defaultContactInfoData);
|
||||
const [availabilityData, setAvailabilityData] = useState<AvailabilityData>(defaultAvailabilityData);
|
||||
const [workEnvironmentData, setWorkEnvironmentData] = useState<WorkEnvironmentData>(defaultWorkEnvironmentData);
|
||||
const [personalTaglineData, setPersonalTaglineData] = useState<PersonalTaglineData>(defaultPersonalTaglineData);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [imageError, setImageError] = useState(false);
|
||||
@@ -150,6 +157,10 @@ export default function AgentProfileView() {
|
||||
const mappedWorkEnv = mapFieldValuesToWorkEnvironment(fieldValuesResponse.fieldValues);
|
||||
setWorkEnvironmentData(mappedWorkEnv);
|
||||
|
||||
// Map field values to personal tagline data
|
||||
const mappedTagline = mapFieldValuesToPersonalTagline(fieldValuesResponse.fieldValues);
|
||||
setPersonalTaglineData(mappedTagline);
|
||||
|
||||
// Map testimonials for TestimonialsSection
|
||||
setTestimonials(
|
||||
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>}
|
||||
/>
|
||||
)}
|
||||
{testimonials.length > 0 && (
|
||||
{personalTaglineData.content && (
|
||||
<InfoCard
|
||||
title=""
|
||||
title={personalTaglineData.label}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/testimonial-star-icon.svg"
|
||||
alt="Testimonial"
|
||||
alt={personalTaglineData.label}
|
||||
width={28}
|
||||
height={28}
|
||||
/>
|
||||
}
|
||||
content={<p className="text-[14px] font-semibold font-serif leading-[19px] text-center text-[#00293D]">“{testimonials[0].text.length > 150 ? testimonials[0].text.substring(0, 150) + '...' : testimonials[0].text}”</p>}
|
||||
content={<p className="text-[14px] font-semibold font-serif leading-[19px] text-center text-[#00293D]">“{personalTaglineData.content.length > 150 ? personalTaglineData.content.substring(0, 150) + '...' : personalTaglineData.content}”</p>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -17,12 +17,8 @@ export function ExperienceSection({ experience }: ExperienceSectionProps) {
|
||||
const [showAllExpertise, setShowAllExpertise] = useState(false);
|
||||
const [showAllCertifications, setShowAllCertifications] = useState(false);
|
||||
|
||||
// Build agency display — append designation if flag is set
|
||||
const agencyDisplay = experience.agencyName
|
||||
? (experience.agencyShowDesignation && experience.agencyDesignation
|
||||
? `${experience.agencyName} - ${experience.agencyDesignation}`
|
||||
: experience.agencyName)
|
||||
: '';
|
||||
// Show agency block whenever name OR designation is filled
|
||||
const hasAgency = !!(experience.agencyName || experience.agencyDesignation);
|
||||
|
||||
// Calculate how many more items are hidden
|
||||
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">
|
||||
{/* Left Column */}
|
||||
<div className="flex-1 space-y-4">
|
||||
{agencyDisplay && (
|
||||
{hasAgency && (
|
||||
<>
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">{experience.agencySectionLabel}</p>
|
||||
<ul className="list-disc list-inside">
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">
|
||||
{agencyDisplay}
|
||||
</li>
|
||||
</ul>
|
||||
{experience.agencyName && (
|
||||
<p className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">
|
||||
{experience.agencyName}
|
||||
</p>
|
||||
)}
|
||||
{experience.agencyDesignation && (
|
||||
<p className="font-serif font-normal text-[14px] leading-[21px] text-[#00293D]/70">
|
||||
{experience.agencyDesignation}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{/* Horizontal divider */}
|
||||
<div className="w-full h-0 border-t-[0.5px] border-solid border-[#00293D]/20" />
|
||||
|
||||
@@ -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.)
|
||||
const fullTimeKeywords = ['full_time', 'mf_9_5', 'mf_8_6', 'full time', '9-5', '9 am', '9am'];
|
||||
const partTimeKeywords = ['part_time', 'part time'];
|
||||
@@ -264,13 +281,23 @@ export function mapFieldValuesToAvailability(fieldValues: FieldValueResponse[]):
|
||||
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
|
||||
const resolvedLabels = rawValues.map((val, i) => {
|
||||
const fromBackend = labelValues[i];
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user