141 lines
4.9 KiB
TypeScript
141 lines
4.9 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import Image from 'next/image';
|
|
import { ProfileSection } from '@/services/profile-sections.service';
|
|
import DynamicField from './DynamicField';
|
|
|
|
// Map section slugs to icon paths
|
|
const sectionIcons: Record<string, string> = {
|
|
'basic-information': '/assets/icons/basic-information-icon.svg',
|
|
'contact-information': '/assets/icons/email-orange-icon.svg',
|
|
'location': '/assets/icons/location-orange-icon.svg',
|
|
'professional-info': '/assets/icons/certification-icon.svg',
|
|
'specialization': '/assets/icons/home-icon.svg',
|
|
'experience': '/assets/icons/star-icon.svg',
|
|
'availability': '/assets/icons/availability-clock-icon.svg',
|
|
'biography': '/assets/icons/bio-icon.svg',
|
|
'attached-documents': '/assets/icons/attachment-icon.svg',
|
|
'hobbies': '/assets/icons/heart-icon.svg',
|
|
'testimonials': '/assets/icons/quote-icon.svg',
|
|
'certification': '/assets/icons/certification-icon.svg',
|
|
};
|
|
|
|
// Map icon names (from database) to icon paths
|
|
const iconNameToPath: Record<string, string> = {
|
|
'star': '/assets/icons/star-icon.svg',
|
|
'home': '/assets/icons/home-icon.svg',
|
|
'location': '/assets/icons/location-orange-icon.svg',
|
|
'heart': '/assets/icons/heart-icon.svg',
|
|
'clock': '/assets/icons/availability-clock-icon.svg',
|
|
'user': '/assets/icons/basic-information-icon.svg',
|
|
'email': '/assets/icons/email-orange-icon.svg',
|
|
'phone': '/assets/icons/phone-orange-icon.svg',
|
|
'document': '/assets/icons/attachment-icon.svg',
|
|
'certificate': '/assets/icons/certification-icon.svg',
|
|
'certification': '/assets/icons/certification-icon.svg',
|
|
'quote': '/assets/icons/quote-icon.svg',
|
|
'bio': '/assets/icons/bio-icon.svg',
|
|
'professional': '/assets/icons/professional-icon.svg',
|
|
'wallet': '/assets/icons/wallet-icon.svg',
|
|
'chart': '/assets/icons/chart-icon.svg',
|
|
'dollar': '/assets/icons/dollar-icon.svg',
|
|
'calendar': '/assets/icons/calendar-icon.svg',
|
|
};
|
|
|
|
// Default icon for sections without a specific icon
|
|
const defaultIcon = '/assets/icons/professional-icon.svg';
|
|
|
|
// Helper to check if a string is an emoji
|
|
const isEmojiString = (str: string): boolean => {
|
|
// Emoji regex pattern - matches most common emojis
|
|
const emojiRegex = /^[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]/u;
|
|
return emojiRegex.test(str);
|
|
};
|
|
|
|
interface DynamicSectionProps {
|
|
section: ProfileSection;
|
|
formData: Record<string, unknown>;
|
|
onChange: (fieldSlug: string, value: unknown) => void;
|
|
errors?: Record<string, string>;
|
|
}
|
|
|
|
export default function DynamicSection({ section, formData, onChange, errors }: DynamicSectionProps) {
|
|
// Determine icon source
|
|
let iconPath: string = defaultIcon;
|
|
let isEmoji = false;
|
|
|
|
if (section.icon) {
|
|
if (section.icon.startsWith('/') || section.icon.startsWith('http')) {
|
|
// It's a valid URL path
|
|
iconPath = section.icon;
|
|
} else if (isEmojiString(section.icon)) {
|
|
// It's an emoji
|
|
isEmoji = true;
|
|
} else if (iconNameToPath[section.icon.toLowerCase()]) {
|
|
// It's an icon name like "star", "home", etc.
|
|
iconPath = iconNameToPath[section.icon.toLowerCase()];
|
|
} else {
|
|
// Fallback to slug mapping or default
|
|
iconPath = sectionIcons[section.slug] || defaultIcon;
|
|
}
|
|
} else {
|
|
// No icon set, use slug mapping or default
|
|
iconPath = sectionIcons[section.slug] || defaultIcon;
|
|
}
|
|
|
|
// Sort fields by sortOrder
|
|
const sortedFields = [...(section.fields || [])].sort((a, b) => a.sortOrder - b.sortOrder);
|
|
const activeFields = sortedFields.filter(f => f.isActive && !f.isSearchableOnly);
|
|
|
|
if (activeFields.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
id={section.slug}
|
|
className="bg-white rounded-[20px] p-6 shadow-[0px_10px_20px_rgba(217,217,217,0.5)] scroll-mt-[100px]"
|
|
>
|
|
{/* Section Header */}
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<div className="w-[40px] h-[40px] flex items-center justify-center">
|
|
{isEmoji ? (
|
|
<span className="text-2xl">{section.icon}</span>
|
|
) : (
|
|
<Image
|
|
src={iconPath}
|
|
alt={section.name}
|
|
width={24}
|
|
height={24}
|
|
className="object-contain"
|
|
/>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<h2 className="text-[18px] font-bold font-serif text-[#00293D]">
|
|
{section.name}
|
|
{section.isRequired && <span className="text-red-500 ml-1">*</span>}
|
|
</h2>
|
|
{section.description && (
|
|
<p className="text-[12px] text-gray-500">{section.description}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Section Fields */}
|
|
<div className="space-y-4">
|
|
{activeFields.map((field) => (
|
|
<DynamicField
|
|
key={field.id}
|
|
field={field}
|
|
value={formData[field.slug]}
|
|
onChange={onChange}
|
|
error={errors?.[field.slug]}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|