feat: Implement dynamic agent profile editing by introducing new services and dynamic UI components.

This commit is contained in:
pradeepkumar
2026-01-24 20:36:22 +05:30
parent cb2e17c759
commit 5e2b3d8e83
12 changed files with 1133 additions and 705 deletions

View File

@@ -1,708 +1,288 @@
'use client';
import { useState, useRef } from 'react';
import { useState, useRef, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import Image from 'next/image';
import {
QuickLinks,
FormSection,
FormInput,
FormCheckbox,
FormTextarea,
FormSelect,
FileUpload,
AttachedFile,
TagInput,
} from './components';
// Initial form data - in production this would come from API
const initialFormData = {
firstName: 'Brian',
lastName: 'Neeland',
email: 'brian@requestnetwork.com',
phone: '+9195003837493',
professionalTypes: {
firstTime: true,
solo: false,
team: false,
},
attachedDocuments: ['Brian_Photo_1_Blue_Coat.pdf'],
state: 'Colorado',
licensedAreas: ['Colorado Springs', 'Monument'],
expertiseYears: [
{ area: 'Colorado', years: '10 yrs' },
{ area: 'Falcon', years: '10 yrs' },
],
biography: "Brian brings eight years of hands-on experience helping clients confidently buy, sell, and invest in real estate. He combines strong analytical skills with deep market knowledge to identify the right opportunities and guide clients through every step of the process. With a data-driven approach and clear communication, Brian ensures smooth transactions and informed decisions tailored to each client's goals.",
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.",
expertise: ['Buyers', 'Sellers', 'Divorce', 'Luxury', 'Retirement', 'Historical', 'Condo', 'Rural'],
yearsInBusiness: {
totalYears: '10 Years',
totalTransactions: '100+',
},
contractsClosed: {
lessThan10: false,
between10And25: false,
between26And50: true,
between51And75: false,
moreThan75: false,
},
pricePoint: {
under100k: false,
between100kAnd200k: false,
between200kAnd400k: true,
between400kAnd600k: true,
between600kAnd800k: false,
between800kAnd1m: false,
above1m: false,
},
specialization: {
clientLocalization: {
local: true,
national: false,
international: false,
},
clientTypeWorkedWith: {
firstTimeBuyer: true,
firstTimeSeller: false,
},
propertyType: {
sfr: true,
condo: false,
luxury: false,
townhome: false,
commercial: false,
},
transactionType: {
relocation: true,
probate: false,
taxLien: false,
traditional: false,
newConstruction: false,
},
loanType: {
conventional: true,
fha: false,
va: false,
usda: false,
reverse: false,
},
lifestyleSpecialties: {
boating: true,
horses: false,
rv: false,
automotive: false,
aviation: false,
},
},
hobbies: ['Boating', 'Horses'],
certifications: [
{ name: 'Certified Residential Specialist (CRS)', org: 'Residential Real Estate Council' },
],
availability: {
officeHours: ['MON-FRI 8am-6pm', 'Sat 10am-2pm'],
contactMethod: {
email: true,
call: false,
},
},
};
import { QuickLinks } from './components';
import DynamicSection from './components/DynamicSection';
import { profileSectionsService, ProfileSection, AgentTypeSectionsResponse } from '@/services/profile-sections.service';
import { agentsService, AgentProfile } from '@/services/agents.service';
export default function EditProfilePage() {
const router = useRouter();
const [formData, setFormData] = useState(initialFormData);
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [stateInput, setStateInput] = useState('');
const [officeHoursInput, setOfficeHoursInput] = useState('');
// State for agent profile and dynamic sections
const [agentProfile, setAgentProfile] = useState<AgentProfile | null>(null);
const [sections, setSections] = useState<ProfileSection[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// Form data keyed by field slug
const [formData, setFormData] = useState<Record<string, unknown>>({});
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
const [isSaving, setIsSaving] = useState(false);
// Fetch agent profile and sections on mount
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
setError(null);
// First, fetch the agent's profile to get their agentTypeId
const profile = await agentsService.getMyProfile();
setAgentProfile(profile);
if (!profile.agentTypeId) {
setError('Your profile does not have an agent type assigned. Please contact support.');
setLoading(false);
return;
}
// Now fetch the sections for this agent type
const response: AgentTypeSectionsResponse = await profileSectionsService.getSectionsForAgentType(profile.agentTypeId);
// Sort sections by effectiveSortOrder
const sortedSections = [...response.sections].sort(
(a, b) => (a.effectiveSortOrder || a.sortOrder) - (b.effectiveSortOrder || b.sortOrder)
);
setSections(sortedSections);
// Initialize form data with existing profile data + default values from fields
const initialData: Record<string, unknown> = {};
// Pre-fill with existing profile data
if (profile.firstName) initialData['first-name'] = profile.firstName;
if (profile.lastName) initialData['last-name'] = profile.lastName;
if (profile.email) initialData['email'] = profile.email;
if (profile.phone) initialData['phone'] = profile.phone;
if (profile.bio) initialData['bio'] = profile.bio;
if (profile.licenseNumber) initialData['license-number'] = profile.licenseNumber;
if (profile.experience) initialData['experience'] = profile.experience;
if (profile.specializations?.length) initialData['specializations'] = profile.specializations;
if (profile.languages?.length) initialData['languages'] = profile.languages;
if (profile.serviceAreas?.length) initialData['service-areas'] = profile.serviceAreas;
// Then apply field default values where no existing data
sortedSections.forEach(section => {
section.fields?.forEach(field => {
if (field.defaultValue && initialData[field.slug] === undefined) {
// Parse default value based on field type
switch (field.fieldType) {
case 'CHECKBOX':
initialData[field.slug] = field.defaultValue === 'true';
break;
case 'NUMBER':
case 'RANGE':
initialData[field.slug] = parseFloat(field.defaultValue) || 0;
break;
case 'CHECKBOX_GROUP':
case 'MULTI_SELECT':
case 'TAG_INPUT':
try {
initialData[field.slug] = JSON.parse(field.defaultValue);
} catch {
initialData[field.slug] = [];
}
break;
default:
initialData[field.slug] = field.defaultValue;
}
}
});
});
setFormData(initialData);
} catch (err: unknown) {
console.error('Failed to fetch data:', err);
// Check for specific error types
const error = err as { response?: { status?: number } };
if (error.response?.status === 401) {
setError('You are not logged in or your session has expired. Please log in again.');
} else if (error.response?.status === 404) {
setError('Agent profile not found. Please complete your registration first.');
} else {
setError('Failed to load profile. Please try again.');
}
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const handleCancel = () => {
router.push('/agent/dashboard');
};
const handleSave = () => {
// In production, this would save to API
console.log('Saving form data:', formData);
router.push('/agent/dashboard');
};
const handleSave = async () => {
try {
setIsSaving(true);
setFormErrors({});
const updateField = (field: string, value: any) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
// Validate required fields
const errors: Record<string, string> = {};
sections.forEach(section => {
section.fields?.forEach(field => {
if (field.isRequired && field.isActive && !field.isSearchableOnly) {
const value = formData[field.slug];
const isEmpty =
value === undefined ||
value === null ||
value === '' ||
(Array.isArray(value) && value.length === 0);
const handleAddLicensedArea = (value: string) => {
const trimmedValue = value.trim();
if (trimmedValue && !formData.licensedAreas.includes(trimmedValue)) {
updateField('licensedAreas', [...formData.licensedAreas, trimmedValue]);
}
setStateInput('');
};
if (isEmpty) {
errors[field.slug] = `${field.name} is required`;
}
}
});
});
const handleStateInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
handleAddLicensedArea(stateInput);
if (Object.keys(errors).length > 0) {
setFormErrors(errors);
// Scroll to first error
const firstErrorField = Object.keys(errors)[0];
const errorElement = document.querySelector(`[data-field-slug="${firstErrorField}"]`);
errorElement?.scrollIntoView({ behavior: 'smooth', block: 'center' });
return;
}
// TODO: Call API to save form data
console.log('Saving form data:', formData);
// Simulate save
await new Promise(resolve => setTimeout(resolve, 500));
router.push('/agent/dashboard');
} catch (err) {
console.error('Failed to save:', err);
setError('Failed to save profile. Please try again.');
} finally {
setIsSaving(false);
}
};
const handleAddOfficeHours = (value: string) => {
const trimmedValue = value.trim();
if (trimmedValue && !formData.availability.officeHours.includes(trimmedValue)) {
updateField('availability', {
...formData.availability,
officeHours: [...formData.availability.officeHours, trimmedValue],
const handleFieldChange = (fieldSlug: string, value: unknown) => {
setFormData(prev => ({ ...prev, [fieldSlug]: value }));
// Clear error when field is modified
if (formErrors[fieldSlug]) {
setFormErrors(prev => {
const newErrors = { ...prev };
delete newErrors[fieldSlug];
return newErrors;
});
}
setOfficeHoursInput('');
};
const handleOfficeHoursKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
handleAddOfficeHours(officeHoursInput);
}
};
if (loading) {
return (
<div className="flex items-center justify-center h-[calc(100vh-180px)]">
<div className="flex flex-col items-center gap-4">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#E58625]"></div>
<p className="text-[14px] font-serif text-[#00293D]/70">Loading profile sections...</p>
</div>
</div>
);
}
const handleRemoveOfficeHours = (hour: string) => {
updateField('availability', {
...formData.availability,
officeHours: formData.availability.officeHours.filter((h) => h !== hour),
});
};
if (error && sections.length === 0) {
return (
<div className="flex items-center justify-center h-[calc(100vh-180px)]">
<div className="bg-white rounded-[20px] p-8 shadow-[0px_10px_20px_rgba(217,217,217,0.5)] max-w-md text-center">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h2 className="text-[18px] font-bold font-serif text-[#00293D] mb-2">Unable to Load Profile</h2>
<p className="text-[14px] font-serif text-[#00293D]/70 mb-4">{error}</p>
<button
onClick={() => window.location.reload()}
className="px-6 py-2 bg-[#E58625] rounded-full text-[14px] font-semibold font-serif text-white hover:bg-[#E58625]/90 transition-colors"
>
Try Again
</button>
</div>
</div>
);
}
return (
<div className="flex gap-6 h-[calc(100vh-180px)]">
{/* Left Sidebar - Quick Links */}
<div className="w-[220px] flex-shrink-0 hidden lg:block h-full overflow-y-auto scrollbar-thin">
<QuickLinks scrollContainerRef={scrollContainerRef} />
<QuickLinks scrollContainerRef={scrollContainerRef} sections={sections} />
</div>
{/* Main Content - Scrollable */}
<div ref={scrollContainerRef} className="flex-1 space-y-6 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
{/* Basic Information */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="basic-info" icon="/assets/icons/basic-information-icon.svg" title="Basic Information">
<div className="flex flex-col md:flex-row gap-4">
<FormInput
label="First Name"
value={formData.firstName}
onChange={(value) => updateField('firstName', value)}
placeholder="Enter first name"
/>
<FormInput
label="Last Name"
value={formData.lastName}
onChange={(value) => updateField('lastName', value)}
placeholder="Enter last name"
/>
<div
ref={scrollContainerRef}
className="flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent"
>
{/* Inner container with padding for shadow visibility */}
<div className="space-y-6 p-2">
{/* Error Banner */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-[15px] p-4 flex items-center gap-3">
<svg className="w-5 h-5 text-red-500 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<p className="text-[14px] font-serif text-red-700">{error}</p>
</div>
)}
{/* Dynamic Sections */}
{sections.map((section) => (
<DynamicSection
key={section.id}
section={section}
formData={formData}
onChange={handleFieldChange}
errors={formErrors}
/>
))}
{/* Empty State */}
{sections.length === 0 && !loading && (
<div className="bg-white rounded-[20px] p-8 shadow-[0px_10px_20px_rgba(217,217,217,0.5)] text-center">
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
</FormSection>
</div>
{/* Contact Information */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="contact-info" icon="/assets/icons/contact-icon.svg" title="Contact Information">
<div className="flex flex-col md:flex-row gap-4">
<FormInput
label="Email Address"
value={formData.email}
onChange={(value) => updateField('email', value)}
placeholder="Enter email"
type="email"
/>
<FormInput
label="Phone Number"
value={formData.phone}
onChange={(value) => updateField('phone', value)}
placeholder="Enter phone"
type="tel"
/>
</div>
</FormSection>
</div>
{/* Professional Types */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="professional-types" icon="/assets/icons/professional-icon.svg" title="Professional Types">
<div className="flex flex-wrap gap-6">
<FormCheckbox
label="First Time"
checked={formData.professionalTypes.firstTime}
onChange={(checked) =>
updateField('professionalTypes', { ...formData.professionalTypes, firstTime: checked })
}
/>
<FormCheckbox
label="Solo"
checked={formData.professionalTypes.solo}
onChange={(checked) =>
updateField('professionalTypes', { ...formData.professionalTypes, solo: checked })
}
/>
<FormCheckbox
label="Team"
checked={formData.professionalTypes.team}
onChange={(checked) =>
updateField('professionalTypes', { ...formData.professionalTypes, team: checked })
}
/>
</div>
</FormSection>
</div>
{/* Upload Documents */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="upload-documents" icon="/assets/icons/upload-icon.svg" title="Upload Documents">
<FileUpload onFileSelect={(files) => console.log('Files selected:', files)} />
{/* Attached Documents */}
{formData.attachedDocuments.length > 0 && (
<div className="mt-6">
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3 flex items-center gap-2">
<Image src="/assets/icons/attachment-icon.svg" alt="Attached" width={16} height={16} />
Attached Documents
</h4>
<div className="space-y-2">
{formData.attachedDocuments.map((doc, idx) => (
<AttachedFile
key={idx}
fileName={doc}
onRemove={() => {
const newDocs = [...formData.attachedDocuments];
newDocs.splice(idx, 1);
updateField('attachedDocuments', newDocs);
}}
/>
))}
</div>
</div>
)}
</FormSection>
</div>
{/* Licensing & Areas */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="licensing-areas" icon="/assets/icons/professional-icon.svg" title="Licensing & Areas">
<div className="space-y-4">
<div>
<label className="block text-[14px] font-semibold text-[#00293D] font-serif mb-2">
What state(s) are you licensed in?
</label>
<div className="relative">
<div className="absolute left-4 top-1/2 -translate-y-1/2">
<Image
src="/assets/icons/location-icon.svg"
alt="Location"
width={16}
height={16}
/>
</div>
<input
type="text"
value={stateInput}
onChange={(e) => setStateInput(e.target.value)}
onKeyDown={handleStateInputKeyDown}
placeholder="Enter state"
className="w-full h-[40px] pl-10 pr-4 border border-[#00293D]/20 rounded-[15px] text-[14px] font-serif text-[#00293D] placeholder:text-[#00293D]/40 focus:outline-none focus:border-[#E58625]"
/>
</div>
<p className="text-[12px] text-[#00293D]/60 font-serif mt-2">
Press Enter or comma to add a state.
</p>
</div>
<div className="flex flex-wrap gap-2">
{formData.licensedAreas.map((area) => (
<span
key={area}
className="inline-flex items-center gap-2 px-3 h-[32px] rounded-[15px] text-[14px] font-medium font-serif border border-[#00293d] text-[#00293d]"
>
{area}
<button
onClick={() => {
const newAreas = formData.licensedAreas.filter((a) => a !== area);
updateField('licensedAreas', newAreas);
}}
className="text-[#00293d] hover:text-[#E58625] transition-colors"
>
×
</button>
</span>
))}
</div>
</div>
</FormSection>
</div>
{/* Biography */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="biography" icon="/assets/icons/bio-icon.svg" title="Biography">
<div className="space-y-4">
<FormTextarea
value={formData.biography}
onChange={(value) => updateField('biography', value)}
placeholder="Write your biography..."
rows={5}
maxLength={500}
/>
<FormTextarea
label="Write a few sentences that explain yourself and also a good memorable thought."
value={formData.testimonialHighlight}
onChange={(value) => updateField('testimonialHighlight', value)}
placeholder="Write your memorable experience..."
rows={3}
maxLength={250}
/>
</div>
</FormSection>
</div>
{/* Expertise */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="expertise" icon="/assets/icons/professional-icon.svg" title="Expertise">
<TagInput
tags={formData.expertise}
onChange={(tags) => updateField('expertise', tags)}
placeholder="Add expertise..."
/>
</FormSection>
</div>
{/* Years In Business */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="years-business" icon="/assets/icons/professional-icon.svg" title="Years In Business">
<div className="flex flex-col md:flex-row gap-4">
<FormInput
label="Total Years"
value={formData.yearsInBusiness.totalYears}
onChange={(value) =>
updateField('yearsInBusiness', { ...formData.yearsInBusiness, totalYears: value })
}
/>
<FormInput
label="Total Transactions"
value={formData.yearsInBusiness.totalTransactions}
onChange={(value) =>
updateField('yearsInBusiness', { ...formData.yearsInBusiness, totalTransactions: value })
}
/>
</div>
</FormSection>
</div>
{/* Areas in expertise & Years */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection
id="areas-expertise-years"
icon="/assets/icons/basic-information-icon.svg"
title="Areas in expertise & Years"
showEdit
onEdit={() => console.log('Edit areas')}
>
<div className="flex flex-col md:flex-row gap-4">
<FormSelect
label="Select State"
value={formData.state}
onChange={(value) => updateField('state', value)}
options={[
{ value: 'Colorado', label: 'Colorado' },
{ value: 'Texas', label: 'Texas' },
]}
/>
<FormSelect
label="Select Years"
value="10"
onChange={() => {}}
options={[
{ value: '5', label: '5 Years' },
{ value: '10', label: '10 Years' },
{ value: '15', label: '15 Years' },
]}
/>
</div>
</FormSection>
</div>
{/* Contracts Closed */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="contracts-closed" icon="/assets/icons/professional-icon.svg" title="Contracts Closed">
<div className="space-y-3">
<FormCheckbox
label="< 10"
checked={formData.contractsClosed.lessThan10}
onChange={(checked) =>
updateField('contractsClosed', { ...formData.contractsClosed, lessThan10: checked })
}
/>
<FormCheckbox
label="10 to 25"
checked={formData.contractsClosed.between10And25}
onChange={(checked) =>
updateField('contractsClosed', { ...formData.contractsClosed, between10And25: checked })
}
/>
<FormCheckbox
label="26 to 50"
checked={formData.contractsClosed.between26And50}
onChange={(checked) =>
updateField('contractsClosed', { ...formData.contractsClosed, between26And50: checked })
}
/>
<FormCheckbox
label="51 to 75"
checked={formData.contractsClosed.between51And75}
onChange={(checked) =>
updateField('contractsClosed', { ...formData.contractsClosed, between51And75: checked })
}
/>
<FormCheckbox
label="75+"
checked={formData.contractsClosed.moreThan75}
onChange={(checked) =>
updateField('contractsClosed', { ...formData.contractsClosed, moreThan75: checked })
}
/>
</div>
</FormSection>
</div>
{/* Price Point */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="price-point" icon="/assets/icons/basic-information-icon.svg" title="Price Point">
<div className="space-y-3">
<FormCheckbox
label="Under $100K"
checked={formData.pricePoint.under100k}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, under100k: checked })
}
/>
<FormCheckbox
label="$100K $200K"
checked={formData.pricePoint.between100kAnd200k}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, between100kAnd200k: checked })
}
/>
<FormCheckbox
label="$200K $400K"
checked={formData.pricePoint.between200kAnd400k}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, between200kAnd400k: checked })
}
/>
<FormCheckbox
label="$400K $600K"
checked={formData.pricePoint.between400kAnd600k}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, between400kAnd600k: checked })
}
/>
<FormCheckbox
label="$600K $800K"
checked={formData.pricePoint.between600kAnd800k}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, between600kAnd800k: checked })
}
/>
<FormCheckbox
label="$800K $1M"
checked={formData.pricePoint.between800kAnd1m}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, between800kAnd1m: checked })
}
/>
<FormCheckbox
label="$1M and above"
checked={formData.pricePoint.above1m}
onChange={(checked) =>
updateField('pricePoint', { ...formData.pricePoint, above1m: checked })
}
/>
</div>
</FormSection>
</div>
{/* Specialization */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="specialization" icon="/assets/icons/basic-information-icon.svg" title="Specialization">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* Client Localization */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Client Localization</h4>
<div className="space-y-2">
<FormCheckbox label="Local" checked={formData.specialization.clientLocalization.local} onChange={() => {}} />
<FormCheckbox label="National" checked={formData.specialization.clientLocalization.national} onChange={() => {}} />
<FormCheckbox label="International" checked={formData.specialization.clientLocalization.international} onChange={() => {}} />
</div>
</div>
{/* Client Type Worked With */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Client Type Worked With</h4>
<div className="space-y-2">
<FormCheckbox label="First Time Buyer" checked={formData.specialization.clientTypeWorkedWith.firstTimeBuyer} onChange={() => {}} />
<FormCheckbox label="First Time Seller" checked={formData.specialization.clientTypeWorkedWith.firstTimeSeller} onChange={() => {}} />
</div>
</div>
{/* Property Type */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Property Type</h4>
<div className="space-y-2">
<FormCheckbox label="SFR" checked={formData.specialization.propertyType.sfr} onChange={() => {}} />
<FormCheckbox label="Condo" checked={formData.specialization.propertyType.condo} onChange={() => {}} />
<FormCheckbox label="Luxury" checked={formData.specialization.propertyType.luxury} onChange={() => {}} />
<FormCheckbox label="Townhome" checked={formData.specialization.propertyType.townhome} onChange={() => {}} />
<FormCheckbox label="Commercial" checked={formData.specialization.propertyType.commercial} onChange={() => {}} />
</div>
</div>
{/* Transaction Type */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Transaction Type</h4>
<div className="space-y-2">
<FormCheckbox label="Relocation" checked={formData.specialization.transactionType.relocation} onChange={() => {}} />
<FormCheckbox label="Probate" checked={formData.specialization.transactionType.probate} onChange={() => {}} />
<FormCheckbox label="Tax Lien" checked={formData.specialization.transactionType.taxLien} onChange={() => {}} />
<FormCheckbox label="Traditional" checked={formData.specialization.transactionType.traditional} onChange={() => {}} />
<FormCheckbox label="New Construction" checked={formData.specialization.transactionType.newConstruction} onChange={() => {}} />
</div>
</div>
{/* Loan Type */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Loan Type</h4>
<div className="space-y-2">
<FormCheckbox label="Conventional" checked={formData.specialization.loanType.conventional} onChange={() => {}} />
<FormCheckbox label="FHA" checked={formData.specialization.loanType.fha} onChange={() => {}} />
<FormCheckbox label="VA" checked={formData.specialization.loanType.va} onChange={() => {}} />
<FormCheckbox label="USDA" checked={formData.specialization.loanType.usda} onChange={() => {}} />
<FormCheckbox label="Reverse" checked={formData.specialization.loanType.reverse} onChange={() => {}} />
</div>
</div>
{/* Lifestyle Specialties */}
<div>
<h4 className="text-[14px] font-semibold text-[#00293D] font-fractul mb-3">Lifestyle Specialties</h4>
<div className="space-y-2">
<FormCheckbox label="Boating" checked={formData.specialization.lifestyleSpecialties.boating} onChange={() => {}} />
<FormCheckbox label="Horses" checked={formData.specialization.lifestyleSpecialties.horses} onChange={() => {}} />
<FormCheckbox label="RV" checked={formData.specialization.lifestyleSpecialties.rv} onChange={() => {}} />
<FormCheckbox label="Automotive" checked={formData.specialization.lifestyleSpecialties.automotive} onChange={() => {}} />
<FormCheckbox label="Aviation" checked={formData.specialization.lifestyleSpecialties.aviation} onChange={() => {}} />
</div>
</div>
</div>
</FormSection>
</div>
{/* Special Interests & Hobbies */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="hobbies" icon="/assets/icons/hobbies-icon.svg" title="Special Interests & Hobbies">
<TagInput
tags={formData.hobbies}
onChange={(tags) => updateField('hobbies', tags)}
placeholder="Add hobby..."
/>
</FormSection>
</div>
{/* Certifications */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="certifications" icon="/assets/icons/certification-icon.svg" title="Professional Info">
<p className="text-[12px] font-medium text-[#00293D]/70 font-serif mb-3">Certifications / Designations</p>
<div className="space-y-2">
{formData.certifications.map((cert, idx) => (
<div key={idx} className="p-3 bg-gray-50 rounded-[10px]">
<p className="text-[14px] font-semibold text-[#00293D] font-serif">{cert.name}</p>
<p className="text-[12px] text-[#00293D]/70 font-serif">{cert.org}</p>
</div>
))}
</div>
</FormSection>
</div>
{/* Availability */}
<div className="bg-white rounded-[15px] border border-[#00293D]/20 shadow-[0px_10px_20px_#D9D9D9] p-6">
<FormSection id="availability" icon="/assets/icons/availability-clock-icon.svg" title="Availability">
<div className="flex flex-col lg:flex-row gap-6">
{/* Typical Office Hours */}
<div className="flex-1">
<label className="block text-[14px] font-semibold text-[#00293D] font-serif mb-2">
Typical Office Hours
</label>
<div className="flex items-center gap-2 h-[40px] px-4 border border-[#00293D]/20 rounded-[15px]">
{formData.availability.officeHours.map((hour) => (
<span
key={hour}
className="inline-flex items-center gap-1 text-[14px] font-semibold font-serif text-[#00293D]"
>
{hour}
<button
onClick={() => handleRemoveOfficeHours(hour)}
className="text-[#00293d]/50 hover:text-[#E58625] transition-colors ml-1"
>
×
</button>
</span>
))}
<input
type="text"
value={officeHoursInput}
onChange={(e) => setOfficeHoursInput(e.target.value)}
onKeyDown={handleOfficeHoursKeyDown}
placeholder={formData.availability.officeHours.length === 0 ? "Add office hours" : ""}
className="flex-1 h-full text-[14px] font-serif text-[#00293D] placeholder:text-[#00293D]/40 focus:outline-none bg-transparent"
/>
</div>
</div>
{/* Preferred Contact Method */}
<div className="lg:w-[250px]">
<label className="block text-[14px] font-semibold text-[#00293D] font-serif mb-2">
Preferred Contact Method
</label>
<div className="flex items-center gap-6 h-[40px]">
<FormCheckbox
label="Email"
checked={formData.availability.contactMethod.email}
onChange={(checked) =>
updateField('availability', {
...formData.availability,
contactMethod: { ...formData.availability.contactMethod, email: checked },
})
}
/>
<FormCheckbox
label="Call"
checked={formData.availability.contactMethod.call}
onChange={(checked) =>
updateField('availability', {
...formData.availability,
contactMethod: { ...formData.availability.contactMethod, call: checked },
})
}
/>
</div>
</div>
</div>
</FormSection>
</div>
<h2 className="text-[18px] font-bold font-serif text-[#00293D] mb-2">No Profile Sections Available</h2>
<p className="text-[14px] font-serif text-[#00293D]/70">
Profile sections have not been configured for your agent type yet.
</p>
</div>
)}
{/* Action Buttons */}
<div className="flex justify-end gap-4 pb-6">
<button
onClick={handleCancel}
className="px-8 py-3 border border-[#00293D]/20 rounded-full text-[14px] font-semibold font-serif text-[#00293D] hover:bg-gray-50 transition-colors"
>
Cancel
</button>
<button
onClick={handleSave}
className="px-8 py-3 bg-[#E58625] rounded-full text-[14px] font-semibold font-serif text-white hover:bg-[#E58625]/90 transition-colors"
>
Save
</button>
{sections.length > 0 && (
<div className="flex justify-end gap-4 pb-6">
<button
onClick={handleCancel}
disabled={isSaving}
className="px-8 py-3 border border-[#00293D]/20 rounded-full text-[14px] font-semibold font-serif text-[#00293D] hover:bg-gray-50 transition-colors disabled:opacity-50"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={isSaving}
className="px-8 py-3 bg-[#E58625] rounded-full text-[14px] font-semibold font-serif text-white hover:bg-[#E58625]/90 transition-colors disabled:opacity-50 flex items-center gap-2"
>
{isSaving && (
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
)}
{isSaving ? 'Saving...' : 'Save'}
</button>
</div>
)}
</div>
</div>
</div>