feat: Add user profile page and new modular profile components, integrating them into the agent dashboard.
This commit is contained in:
67
src/components/profile/ContactInfo.tsx
Normal file
67
src/components/profile/ContactInfo.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface ContactInfoProps {
|
||||
email: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
// Helper function to mask email
|
||||
function maskEmail(email: string): string {
|
||||
const [localPart, domain] = email.split('@');
|
||||
if (!domain) return '********';
|
||||
const maskedLocal = localPart.slice(0, 2) + '********';
|
||||
return `${maskedLocal}@${domain}`;
|
||||
}
|
||||
|
||||
// Helper function to mask phone
|
||||
function maskPhone(phone: string): string {
|
||||
if (phone.length <= 4) return '********';
|
||||
return phone.slice(0, -4).replace(/./g, '*') + phone.slice(-4);
|
||||
}
|
||||
|
||||
export function ContactInfo({ email, phone }: ContactInfoProps) {
|
||||
const [showEmail, setShowEmail] = useState(false);
|
||||
const [showPhone, setShowPhone] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[354px] lg:max-w-none border border-[#00293d]/10 rounded-[15px] p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="font-bold text-[#00293d] text-sm">Email:</span>
|
||||
<span className="text-[#00293d] text-sm font-serif">
|
||||
{showEmail ? email : maskEmail(email)}
|
||||
</span>
|
||||
<button
|
||||
className="p-1 hover:bg-gray-100 rounded ml-auto"
|
||||
onClick={() => setShowEmail(!showEmail)}
|
||||
>
|
||||
<Image
|
||||
src={showEmail ? "/assets/icons/eye-off-icon.svg" : "/assets/icons/eye-icon.svg"}
|
||||
alt={showEmail ? "Hide email" : "Show email"}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold text-[#00293d] text-sm">Ph.No:</span>
|
||||
<span className="text-[#00293d] text-sm font-serif">
|
||||
{showPhone ? phone : maskPhone(phone)}
|
||||
</span>
|
||||
<button
|
||||
className="p-1 hover:bg-gray-100 rounded ml-auto"
|
||||
onClick={() => setShowPhone(!showPhone)}
|
||||
>
|
||||
<Image
|
||||
src={showPhone ? "/assets/icons/eye-off-icon.svg" : "/assets/icons/eye-icon.svg"}
|
||||
alt={showPhone ? "Hide phone" : "Show phone"}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
83
src/components/profile/ExperienceSection.tsx
Normal file
83
src/components/profile/ExperienceSection.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
'use client';
|
||||
|
||||
import { Tag } from './Tag';
|
||||
|
||||
interface ExperienceData {
|
||||
years: string;
|
||||
contracts: string;
|
||||
licensingAreas: string[];
|
||||
expertiseYears: { area: string; years: string }[];
|
||||
certifications: { name: string; org: string }[];
|
||||
}
|
||||
|
||||
interface ExperienceSectionProps {
|
||||
experience: ExperienceData;
|
||||
}
|
||||
|
||||
export function ExperienceSection({ experience }: ExperienceSectionProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-[20px] p-4 lg:p-5">
|
||||
<h2 className="text-[20px] font-bold text-[#00293D] font-fractul leading-[24px] mb-4 text-center lg:text-left">Experience</h2>
|
||||
<div className="flex flex-col lg:flex-row gap-4 lg:gap-8">
|
||||
{/* Left Column */}
|
||||
<div className="flex-1 space-y-4">
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">Years in Experience</p>
|
||||
<ul className="list-disc list-inside">
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">{experience.years}</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* Horizontal divider */}
|
||||
<div className="w-full h-0 border-t-[0.5px] border-solid border-[#00293D]/20" />
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-2">Number of contract closed</p>
|
||||
<ul className="list-disc list-inside">
|
||||
<li className="font-serif font-medium text-[15px] leading-[21px] text-[#00293D]">{experience.contracts}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Licensing & Areas</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{experience.licensingAreas.map((area, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{area}
|
||||
</Tag>
|
||||
))}
|
||||
<span className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer">+3 More</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider - Hidden on mobile, visible on desktop */}
|
||||
<div className="hidden lg:block w-px bg-gray-200" />
|
||||
<div className="lg:hidden border-t border-gray-200" />
|
||||
|
||||
{/* Right Column */}
|
||||
<div className="flex-1 space-y-4">
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Areas in expertise & Years</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{experience.expertiseYears.slice(0, 4).map((item, idx) => (
|
||||
<Tag key={idx} variant="light">
|
||||
{item.area} – {item.years}
|
||||
</Tag>
|
||||
))}
|
||||
<span className="inline-flex items-center justify-center h-[28px] px-3 rounded-[15px] border border-[#00293d]/10 text-[15px] font-light font-serif text-[#00293d] cursor-pointer">+3 More</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-fractul font-bold text-[14px] leading-[17px] text-[#00293D] mb-3">Certifications</p>
|
||||
<ul className="space-y-3">
|
||||
{experience.certifications.map((cert, idx) => (
|
||||
<li key={idx}>
|
||||
<span className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D]">{cert.name}</span>
|
||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] opacity-50 ml-3">{cert.org}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
src/components/profile/InfoCard.tsx
Normal file
17
src/components/profile/InfoCard.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
'use client';
|
||||
|
||||
interface InfoCardProps {
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export function InfoCard({ title, content, icon }: InfoCardProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-[10px] shadow-[0px_10px_20px_0px_rgba(217,217,217,0.5)] p-6 text-center">
|
||||
<div className="flex justify-center mb-3">{icon}</div>
|
||||
{title && <h4 className="font-semibold text-[#00293D] text-[14px] leading-[19px] mb-3 font-serif">{title}</h4>}
|
||||
<div className="text-[#00293d] text-sm font-serif">{content}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
172
src/components/profile/ProfileCard.tsx
Normal file
172
src/components/profile/ProfileCard.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { Tag } from './Tag';
|
||||
|
||||
interface ProfileCardProps {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
isVerified: boolean;
|
||||
title: string;
|
||||
location: string;
|
||||
memberSince: string;
|
||||
bio: string;
|
||||
expertise: string[];
|
||||
showEditButton?: boolean;
|
||||
editHref?: string;
|
||||
messageHref?: string;
|
||||
requestsHref?: string;
|
||||
}
|
||||
|
||||
export function ProfileCard({
|
||||
firstName,
|
||||
lastName,
|
||||
isVerified,
|
||||
title,
|
||||
location,
|
||||
memberSince,
|
||||
bio,
|
||||
expertise,
|
||||
showEditButton = false,
|
||||
editHref = '/agent/edit',
|
||||
messageHref,
|
||||
requestsHref,
|
||||
}: ProfileCardProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-[20px] p-4 lg:p-5">
|
||||
{/* Name and Actions Row */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4 mb-4">
|
||||
<div className="text-center lg:text-left">
|
||||
<div className="flex items-center justify-center lg:justify-start gap-2 mb-1">
|
||||
<h1 className="text-[18px] text-[#00293d] font-serif">
|
||||
<span className="font-semibold">{firstName}</span>{' '}
|
||||
<span className="font-normal">{lastName}</span>
|
||||
</h1>
|
||||
{isVerified && (
|
||||
<>
|
||||
<Image
|
||||
src="/assets/icons/verified-icon.svg"
|
||||
alt="Verified"
|
||||
width={15}
|
||||
height={14}
|
||||
/>
|
||||
<span className="text-[14px] text-[#638559] font-medium font-serif">
|
||||
(Verified Expert)
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{showEditButton && (
|
||||
<Link href={editHref} className="p-1 hover:bg-gray-100 rounded transition-colors">
|
||||
<Image
|
||||
src="/assets/icons/edit-pencil-orange-icon.svg"
|
||||
alt="Edit profile"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-[#00293d] text-sm mb-2 font-fractul">{title}</p>
|
||||
<div className="flex items-center justify-center lg:justify-start gap-4 text-sm text-[#00293d] font-fractul">
|
||||
<span className="flex items-center gap-1">
|
||||
<Image
|
||||
src="/assets/icons/location-pin-icon.svg"
|
||||
alt="Location"
|
||||
width={21}
|
||||
height={19}
|
||||
/>
|
||||
<span className="text-[14px] font-bold text-[#00293D] font-serif leading-[19px]">
|
||||
{location}
|
||||
</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Image
|
||||
src="/assets/icons/calendar-icon.svg"
|
||||
alt="Calendar"
|
||||
width={23}
|
||||
height={21}
|
||||
/>
|
||||
<span className="text-[13px] font-medium text-[#00293D] font-serif leading-[18px]">
|
||||
Member Since {memberSince}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex items-center justify-center lg:justify-start gap-3">
|
||||
{messageHref ? (
|
||||
<Link
|
||||
href={messageHref}
|
||||
className="flex items-center gap-2 px-4 py-1.5 bg-[#e58625] text-[#00293d] text-sm font-normal rounded-[15px] border border-[#00293d]/10 hover:bg-[#d47720] transition-colors font-serif cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
src="/assets/icons/message-dark-icon.svg"
|
||||
alt="Message"
|
||||
width={14}
|
||||
height={13}
|
||||
/>
|
||||
Message
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
className="flex items-center gap-2 px-4 py-1.5 bg-[#e58625] text-[#00293d] text-sm font-normal rounded-[15px] border border-[#00293d]/10 hover:bg-[#d47720] transition-colors font-serif cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
src="/assets/icons/message-dark-icon.svg"
|
||||
alt="Message"
|
||||
width={14}
|
||||
height={13}
|
||||
/>
|
||||
Message
|
||||
</button>
|
||||
)}
|
||||
{requestsHref ? (
|
||||
<Link
|
||||
href={requestsHref}
|
||||
className="flex items-center gap-2 px-4 py-1.5 bg-[#e58625] text-[#00293d] text-sm font-normal rounded-[15px] border border-[#00293d]/10 hover:bg-[#d47720] transition-colors font-serif cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
src="/assets/icons/requests-dark-icon.svg"
|
||||
alt="Connect"
|
||||
width={14}
|
||||
height={13}
|
||||
/>
|
||||
{showEditButton ? 'Requests' : 'Connect'}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
className="flex items-center gap-2 px-4 py-1.5 bg-[#e58625] text-[#00293d] text-sm font-normal rounded-[15px] border border-[#00293d]/10 hover:bg-[#d47720] transition-colors font-serif cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
src="/assets/icons/requests-dark-icon.svg"
|
||||
alt="Connect"
|
||||
width={14}
|
||||
height={13}
|
||||
/>
|
||||
{showEditButton ? 'Requests' : 'Connect'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bio */}
|
||||
<p className="text-[14px] font-normal text-[#00293D] font-serif leading-[20px] mb-4 text-center lg:text-left">
|
||||
{bio}
|
||||
</p>
|
||||
|
||||
{/* Expertise Tags */}
|
||||
<div>
|
||||
<p className="text-[14px] font-bold text-[#00293D] font-fractul leading-[17px] mb-2 text-center lg:text-left">
|
||||
Expertise:
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2 justify-center lg:justify-start">
|
||||
{expertise.map((tag, idx) => (
|
||||
<Tag key={idx}>{tag}</Tag>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
36
src/components/profile/SpecializationCard.tsx
Normal file
36
src/components/profile/SpecializationCard.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
interface SpecializationCardProps {
|
||||
title: string;
|
||||
items: string[];
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export function SpecializationCard({ title, items, icon }: SpecializationCardProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-[20px] p-6 text-center flex flex-col h-full border-[0.7px] border-[#E58625]">
|
||||
<div className="flex justify-center mb-3">{icon}</div>
|
||||
<h4 className="font-bold text-[#00293D] text-[14px] leading-[17px] mb-4 font-fractul">{title}</h4>
|
||||
<div className="flex-1">
|
||||
{items.map((item, idx) => (
|
||||
<p key={idx} className="text-[#00293D] text-[14px] leading-[25px] font-normal font-serif text-center">
|
||||
{item}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 flex justify-center">
|
||||
<button className="inline-flex items-center justify-center gap-1 h-[20px] w-[87px] border border-[#e58625] rounded-[20px] text-[#e58625] text-[10px] leading-[22px] font-bold font-serif hover:bg-[#e58625]/5 transition-colors">
|
||||
Show More
|
||||
<Image
|
||||
src="/assets/icons/chevron-down-icon.svg"
|
||||
alt="Show more"
|
||||
width={10}
|
||||
height={10}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
91
src/components/profile/SpecializationSection.tsx
Normal file
91
src/components/profile/SpecializationSection.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { SpecializationCard } from './SpecializationCard';
|
||||
|
||||
interface SpecializationData {
|
||||
types: string[];
|
||||
loanTypes: string[];
|
||||
propertyTypes: string[];
|
||||
hobbies: string[];
|
||||
pricePoints: string[];
|
||||
}
|
||||
|
||||
interface SpecializationSectionProps {
|
||||
specialization: SpecializationData;
|
||||
}
|
||||
|
||||
export function SpecializationSection({ specialization }: SpecializationSectionProps) {
|
||||
return (
|
||||
<div className="bg-[#e8e8e8] rounded-[20px] p-8">
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-[20px] font-bold text-[#00293D] font-fractul leading-[24px]">Specialization</h2>
|
||||
<p className="text-[14px] font-medium text-[#00293D] font-fractul leading-[17px]">Area Of Expertise and Focus</p>
|
||||
</div>
|
||||
<div className="flex flex-col lg:grid lg:grid-cols-3 gap-6 mb-6">
|
||||
<SpecializationCard
|
||||
title="Specialization"
|
||||
items={specialization.types}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/home-icon.svg"
|
||||
alt="Specialization"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<SpecializationCard
|
||||
title="Loan Type"
|
||||
items={specialization.loanTypes}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/wallet-icon.svg"
|
||||
alt="Loan Type"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<SpecializationCard
|
||||
title="Property Type"
|
||||
items={specialization.propertyTypes}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/chart-icon.svg"
|
||||
alt="Property Type"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col lg:grid lg:grid-cols-2 gap-6 lg:max-w-2xl lg:mx-auto">
|
||||
<SpecializationCard
|
||||
title="Hobbies & Interests"
|
||||
items={specialization.hobbies}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/heart-icon.svg"
|
||||
alt="Hobbies"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<SpecializationCard
|
||||
title="Price Point"
|
||||
items={specialization.pricePoints}
|
||||
icon={
|
||||
<Image
|
||||
src="/assets/icons/wallet-icon.svg"
|
||||
alt="Price Point"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/components/profile/StarRating.tsx
Normal file
23
src/components/profile/StarRating.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
interface StarRatingProps {
|
||||
rating: number;
|
||||
}
|
||||
|
||||
export function StarRating({ rating }: StarRatingProps) {
|
||||
return (
|
||||
<div className="flex gap-0.5">
|
||||
{[1, 2, 3, 4, 5].map((star) => (
|
||||
<Image
|
||||
key={star}
|
||||
src={star <= rating ? '/assets/icons/star-filled-icon.svg' : '/assets/icons/star-empty-icon.svg'}
|
||||
alt={star <= rating ? 'Filled star' : 'Empty star'}
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
src/components/profile/StatusButtons.tsx
Normal file
30
src/components/profile/StatusButtons.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
|
||||
interface StatusButtonsProps {
|
||||
isAvailable?: boolean;
|
||||
}
|
||||
|
||||
export function StatusButtons({ isAvailable = true }: StatusButtonsProps) {
|
||||
return (
|
||||
<div className="w-full max-w-[354px] lg:max-w-none space-y-2">
|
||||
<div className="flex items-center border border-[#00293d]/10 rounded-[15px] h-[50px] px-4">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<span className="w-3 h-3 bg-green-500 rounded-full" />
|
||||
<span className="text-sm font-bold text-[#00293d] font-fractul">Available.</span>
|
||||
</div>
|
||||
<button className="px-5 py-1.5 bg-[#e58625] text-white text-sm rounded-[15px] hover:bg-[#d47720] transition-colors font-fractul">
|
||||
Connect
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center border border-[#00293d]/10 rounded-[15px] h-[50px] px-4">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<span className="w-3 h-3 bg-white border border-gray-400 rounded-full" />
|
||||
<span className="text-sm font-bold text-[#00293d] font-fractul">Unavailable.</span>
|
||||
</div>
|
||||
<button className="px-5 py-1.5 bg-[#00293d] text-white text-sm rounded-[15px] hover:bg-[#001d2b] transition-colors font-fractul">
|
||||
Connect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
src/components/profile/Tag.tsx
Normal file
22
src/components/profile/Tag.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
'use client';
|
||||
|
||||
interface TagProps {
|
||||
children: React.ReactNode;
|
||||
variant?: 'default' | 'light' | 'orange';
|
||||
}
|
||||
|
||||
export function Tag({ children, variant = 'default' }: TagProps) {
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center justify-center px-3 h-[28px] rounded-[15px] text-[14px] font-medium font-serif ${
|
||||
variant === 'light'
|
||||
? 'border-[0.7px] border-[#00293d] text-[#00293d] bg-transparent'
|
||||
: variant === 'orange'
|
||||
? 'border-[0.7px] border-[#e58625] text-[#e58625] bg-transparent'
|
||||
: 'border-[0.7px] border-[#00293d] text-[#00293d] bg-transparent'
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
34
src/components/profile/TestimonialCard.tsx
Normal file
34
src/components/profile/TestimonialCard.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { StarRating } from './StarRating';
|
||||
|
||||
interface TestimonialCardProps {
|
||||
text: string;
|
||||
author: string;
|
||||
role: string;
|
||||
rating: number;
|
||||
}
|
||||
|
||||
export function TestimonialCard({ text, author, role, rating }: TestimonialCardProps) {
|
||||
return (
|
||||
<div className="p-4 border border-[#00293D]/10 rounded-[15px]">
|
||||
<div className="mb-4">
|
||||
<Image
|
||||
src="/assets/icons/quote-icon.svg"
|
||||
alt="Quote"
|
||||
width={23}
|
||||
height={13}
|
||||
className="mb-3"
|
||||
/>
|
||||
<p className="font-medium text-[#00293D] text-[14px] leading-[17px] font-fractul">I have been working with Lorem .</p>
|
||||
</div>
|
||||
<p className="text-[#00293D] text-[14px] leading-[19px] font-normal font-serif mb-4">{text}</p>
|
||||
<div className="pt-4 border-t border-gray-100 space-y-2">
|
||||
<StarRating rating={rating} />
|
||||
<p className="font-normal text-[#00293D] text-[14px] leading-[17px] font-fractul">{author}</p>
|
||||
<p className="font-fractul font-medium text-[14px] leading-[17px] text-[#00293D]">{role}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
38
src/components/profile/TestimonialsSection.tsx
Normal file
38
src/components/profile/TestimonialsSection.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { TestimonialCard } from './TestimonialCard';
|
||||
|
||||
interface Testimonial {
|
||||
id: number;
|
||||
text: string;
|
||||
author: string;
|
||||
role: string;
|
||||
rating: number;
|
||||
}
|
||||
|
||||
interface TestimonialsSectionProps {
|
||||
testimonials: Testimonial[];
|
||||
}
|
||||
|
||||
export function TestimonialsSection({ testimonials }: TestimonialsSectionProps) {
|
||||
return (
|
||||
<div className="bg-white rounded-[20px] p-8">
|
||||
<h2 className="text-[20px] font-bold text-[#00293D] font-fractul leading-[24px] text-center mb-4">Testimonials</h2>
|
||||
<div className="w-full h-px bg-[#00293D]/20 mb-4" />
|
||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] text-center mb-8">
|
||||
Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.
|
||||
</p>
|
||||
<div className="flex flex-col lg:grid lg:grid-cols-3 gap-6">
|
||||
{testimonials.map((testimonial) => (
|
||||
<TestimonialCard
|
||||
key={testimonial.id}
|
||||
text={testimonial.text}
|
||||
author={testimonial.author}
|
||||
role={testimonial.role}
|
||||
rating={testimonial.rating}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
src/components/profile/index.ts
Normal file
12
src/components/profile/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Shared Profile Components
|
||||
export { StarRating } from './StarRating';
|
||||
export { Tag } from './Tag';
|
||||
export { InfoCard } from './InfoCard';
|
||||
export { SpecializationCard } from './SpecializationCard';
|
||||
export { ProfileCard } from './ProfileCard';
|
||||
export { ExperienceSection } from './ExperienceSection';
|
||||
export { SpecializationSection } from './SpecializationSection';
|
||||
export { TestimonialCard } from './TestimonialCard';
|
||||
export { TestimonialsSection } from './TestimonialsSection';
|
||||
export { StatusButtons } from './StatusButtons';
|
||||
export { ContactInfo } from './ContactInfo';
|
||||
Reference in New Issue
Block a user