refactor: Remove deprecated agent dashboard and message components, add FAQ page, and introduce a new generic messaging system.
This commit is contained in:
4
public/assets/icons/calendar-orange-icon.svg
Normal file
4
public/assets/icons/calendar-orange-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="23" height="21" viewBox="0 0 23 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.46581 0.75C6.46581 0.551088 6.37692 0.360322 6.21868 0.21967C6.06045 0.0790177 5.84584 0 5.62206 0C5.39829 0 5.18367 0.0790177 5.02544 0.21967C4.86721 0.360322 4.77831 0.551088 4.77831 0.75V2.33C3.15831 2.445 2.09631 2.727 1.31556 3.422C0.533687 4.116 0.216437 5.061 0.0859375 6.5H22.4082C22.2777 5.06 21.9604 4.116 21.1786 3.422C20.3978 2.727 19.3347 2.445 17.7158 2.329V0.75C17.7158 0.551088 17.6269 0.360322 17.4687 0.21967C17.3104 0.0790177 17.0958 0 16.8721 0C16.6483 0 16.4337 0.0790177 16.2754 0.21967C16.1172 0.360322 16.0283 0.551088 16.0283 0.75V2.263C15.2802 2.25 14.4409 2.25 13.4971 2.25H8.99706C8.05319 2.25 7.21394 2.25 6.46581 2.263V0.75Z" fill="#E58625"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 10.25C0 9.411 1.25729e-08 8.665 0.014625 8H22.4854C22.5 8.665 22.5 9.411 22.5 10.25V12.25C22.5 16.021 22.5 17.907 21.1815 19.078C19.863 20.249 17.7424 20.25 13.5 20.25H9C4.75763 20.25 2.63587 20.25 1.3185 19.078C0.00112492 17.906 0 16.021 0 12.25V10.25ZM16.875 12.25C17.1734 12.25 17.4595 12.1446 17.6705 11.9571C17.8815 11.7696 18 11.5152 18 11.25C18 10.9848 17.8815 10.7304 17.6705 10.5429C17.4595 10.3554 17.1734 10.25 16.875 10.25C16.5766 10.25 16.2905 10.3554 16.0795 10.5429C15.8685 10.7304 15.75 10.9848 15.75 11.25C15.75 11.5152 15.8685 11.7696 16.0795 11.9571C16.2905 12.1446 16.5766 12.25 16.875 12.25ZM16.875 16.25C17.1734 16.25 17.4595 16.1446 17.6705 15.9571C17.8815 15.7696 18 15.5152 18 15.25C18 14.9848 17.8815 14.7304 17.6705 14.5429C17.4595 14.3554 17.1734 14.25 16.875 14.25C16.5766 14.25 16.2905 14.3554 16.0795 14.5429C15.8685 14.7304 15.75 14.9848 15.75 15.25C15.75 15.5152 15.8685 15.7696 16.0795 15.9571C16.2905 16.1446 16.5766 16.25 16.875 16.25ZM12.375 11.25C12.375 11.5152 12.2565 11.7696 12.0455 11.9571C11.8345 12.1446 11.5484 12.25 11.25 12.25C10.9516 12.25 10.6655 12.1446 10.4545 11.9571C10.2435 11.7696 10.125 11.5152 10.125 11.25C10.125 10.9848 10.2435 10.7304 10.4545 10.5429C10.6655 10.3554 10.9516 10.25 11.25 10.25C11.5484 10.25 11.8345 10.3554 12.0455 10.5429C12.2565 10.7304 12.375 10.9848 12.375 11.25ZM12.375 15.25C12.375 15.5152 12.2565 15.7696 12.0455 15.9571C11.8345 16.1446 11.5484 16.25 11.25 16.25C10.9516 16.25 10.6655 16.1446 10.4545 15.9571C10.2435 15.7696 10.125 15.5152 10.125 15.25C10.125 14.9848 10.2435 14.7304 10.4545 14.5429C10.6655 14.3554 10.9516 14.25 11.25 14.25C11.5484 14.25 11.8345 14.3554 12.0455 14.5429C12.2565 14.7304 12.375 14.9848 12.375 15.25ZM5.625 12.25C5.92337 12.25 6.20952 12.1446 6.4205 11.9571C6.63147 11.7696 6.75 11.5152 6.75 11.25C6.75 10.9848 6.63147 10.7304 6.4205 10.5429C6.20952 10.3554 5.92337 10.25 5.625 10.25C5.32663 10.25 5.04048 10.3554 4.8295 10.5429C4.61853 10.7304 4.5 10.9848 4.5 11.25C4.5 11.5152 4.61853 11.7696 4.8295 11.9571C5.04048 12.1446 5.32663 12.25 5.625 12.25ZM5.625 16.25C5.92337 16.25 6.20952 16.1446 6.4205 15.9571C6.63147 15.7696 6.75 15.5152 6.75 15.25C6.75 14.9848 6.63147 14.7304 6.4205 14.5429C6.20952 14.3554 5.92337 14.25 5.625 14.25C5.32663 14.25 5.04048 14.3554 4.8295 14.5429C4.61853 14.7304 4.5 14.9848 4.5 15.25C4.5 15.5152 4.61853 15.7696 4.8295 15.9571C5.04048 16.1446 5.32663 16.25 5.625 16.25Z" fill="#E58625"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.2 KiB |
3
public/assets/icons/caution-icon.svg
Normal file
3
public/assets/icons/caution-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 9V13M12 17H12.01M10.29 3.86L1.82 18C1.64 18.3 1.55 18.64 1.55 19C1.55 19.36 1.64 19.7 1.82 20C2 20.3 2.25 20.55 2.55 20.73C2.85 20.91 3.19 21 3.54 21H20.46C20.81 21 21.15 20.91 21.45 20.73C21.75 20.55 22 20.3 22.18 20C22.36 19.7 22.45 19.36 22.45 19C22.45 18.64 22.36 18.3 22.18 18L13.71 3.86C13.53 3.56 13.28 3.31 12.98 3.13C12.68 2.95 12.34 2.86 12 2.86C11.66 2.86 11.32 2.95 11.02 3.13C10.72 3.31 10.47 3.56 10.29 3.86Z" stroke="#E58625" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 625 B |
3
public/assets/icons/chat-icon.svg
Normal file
3
public/assets/icons/chat-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="#00293D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 443 B |
3
public/assets/icons/reset-icon.svg
Normal file
3
public/assets/icons/reset-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C8.54305 3 5.53671 5.08041 4.10857 8.08274M4.10857 8.08274L4 4M4.10857 8.08274H8" stroke="#E58625" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 365 B |
@@ -1,67 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
'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[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProfileCard({
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
isVerified,
|
|
||||||
title,
|
|
||||||
location,
|
|
||||||
memberSince,
|
|
||||||
bio,
|
|
||||||
expertise,
|
|
||||||
}: 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>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Link href="/agent/edit" 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">
|
|
||||||
<Link
|
|
||||||
href="/agent/message"
|
|
||||||
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>
|
|
||||||
<Link
|
|
||||||
href="/agent/network"
|
|
||||||
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="Requests"
|
|
||||||
width={14}
|
|
||||||
height={13}
|
|
||||||
/>
|
|
||||||
Requests
|
|
||||||
</Link>
|
|
||||||
</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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Image from 'next/image';
|
|
||||||
|
|
||||||
interface ProfileSidebarProps {
|
|
||||||
profileImage: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProfileSidebar({ profileImage, email, phone }: ProfileSidebarProps) {
|
|
||||||
return (
|
|
||||||
<div className="w-full lg:w-[220px] flex-shrink-0 space-y-4 flex flex-col items-center lg:items-start">
|
|
||||||
{/* Profile Image */}
|
|
||||||
<div className="relative w-[200px] h-[200px]">
|
|
||||||
<div className="w-full h-full rounded-[15px] overflow-hidden border-4 border-[#5ba4a4]">
|
|
||||||
<Image
|
|
||||||
src={profileImage}
|
|
||||||
alt="Profile"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* Gradient Overlay - subtle darkening at bottom */}
|
|
||||||
<div className="absolute inset-0 rounded-[15px] bg-gradient-to-t from-black/40 via-transparent to-transparent pointer-events-none" />
|
|
||||||
{/* Edit Icon - Center */}
|
|
||||||
<button className="absolute top-[calc(50%-18px)] left-[calc(50%-18px)] w-9 h-9 bg-white rounded-[10px] shadow-md flex items-center justify-center hover:bg-gray-50 transition-colors">
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/edit-icon.svg"
|
|
||||||
alt="Edit profile"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status Buttons */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<span className="w-2 h-2 bg-green-500 rounded-full" />
|
|
||||||
<span className="text-sm text-[#00293d]">Available.</span>
|
|
||||||
</div>
|
|
||||||
<button className="px-4 py-1.5 bg-[#5ba4a4] text-white text-sm rounded-full hover:bg-[#4a9393] transition-colors">
|
|
||||||
Connect
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<span className="w-2 h-2 bg-white border border-gray-400 rounded-full" />
|
|
||||||
<span className="text-sm text-[#00293d]">Unavailable.</span>
|
|
||||||
</div>
|
|
||||||
<button className="px-4 py-1.5 bg-[#5ba4a4] text-white text-sm rounded-full hover:bg-[#4a9393] transition-colors">
|
|
||||||
Connect
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Contact Info */}
|
|
||||||
<div className="space-y-2 pt-2">
|
|
||||||
<div className="flex items-center gap-2 text-sm">
|
|
||||||
<span className="font-bold text-[#00293d]">Email:</span>
|
|
||||||
<span className="text-[#00293d]">{email}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 text-sm">
|
|
||||||
<span className="font-bold text-[#00293d]">Ph.No:</span>
|
|
||||||
<span className="text-[#00293d]">{phone}</span>
|
|
||||||
<button className="p-1 hover:bg-gray-100 rounded">
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/edit-pencil-icon.svg"
|
|
||||||
alt="Edit phone"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
'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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Dashboard Components
|
|
||||||
export { StarRating } from './StarRating';
|
|
||||||
export { Tag } from './Tag';
|
|
||||||
export { InfoCard } from './InfoCard';
|
|
||||||
export { SpecializationCard } from './SpecializationCard';
|
|
||||||
export { ProfileSidebar } from './ProfileSidebar';
|
|
||||||
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';
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Image from 'next/image';
|
|
||||||
|
|
||||||
interface ConversationItemProps {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
lastMessage: string;
|
|
||||||
date: string;
|
|
||||||
isOnline?: boolean;
|
|
||||||
isSelected?: boolean;
|
|
||||||
onClick?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ConversationItem({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
avatar,
|
|
||||||
lastMessage,
|
|
||||||
date,
|
|
||||||
isOnline = false,
|
|
||||||
isSelected = false,
|
|
||||||
onClick,
|
|
||||||
}: ConversationItemProps) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onClick={onClick}
|
|
||||||
className={`flex items-start gap-3 p-4 border border-[#00293d]/10 rounded-[15px] cursor-pointer transition-colors ${
|
|
||||||
isSelected ? 'bg-[#00293d]/5' : 'hover:bg-[#00293d]/5'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{/* Avatar with online indicator */}
|
|
||||||
<div className="relative flex-shrink-0">
|
|
||||||
<div className="w-[70px] h-[70px] rounded-full overflow-hidden">
|
|
||||||
<Image
|
|
||||||
src={avatar}
|
|
||||||
alt={name}
|
|
||||||
width={70}
|
|
||||||
height={70}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{isOnline && (
|
|
||||||
<div className="absolute bottom-1 right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p className="font-fractul font-medium text-[14px] leading-[17px] text-[#00293D] mb-1">
|
|
||||||
{name}
|
|
||||||
</p>
|
|
||||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] line-clamp-2">
|
|
||||||
{lastMessage}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Date */}
|
|
||||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] flex-shrink-0">
|
|
||||||
{date}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { ConversationItem } from './ConversationItem';
|
|
||||||
|
|
||||||
interface Conversation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
lastMessage: string;
|
|
||||||
date: string;
|
|
||||||
isOnline?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConversationListProps {
|
|
||||||
conversations: Conversation[];
|
|
||||||
selectedId: string | null;
|
|
||||||
onSelect: (id: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ConversationList({
|
|
||||||
conversations,
|
|
||||||
selectedId,
|
|
||||||
onSelect,
|
|
||||||
}: ConversationListProps) {
|
|
||||||
return (
|
|
||||||
<div className="border border-[#00293d]/10 rounded-[15px] bg-white h-full flex flex-col">
|
|
||||||
{/* Scrollable conversation list */}
|
|
||||||
<div className="flex-1 overflow-y-auto p-2 space-y-2">
|
|
||||||
{conversations.map((conversation) => (
|
|
||||||
<ConversationItem
|
|
||||||
key={conversation.id}
|
|
||||||
{...conversation}
|
|
||||||
isSelected={selectedId === conversation.id}
|
|
||||||
onClick={() => onSelect(conversation.id)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export { ConversationItem } from './ConversationItem';
|
|
||||||
export { ConversationList } from './ConversationList';
|
|
||||||
export { ChatHeader } from './ChatHeader';
|
|
||||||
export { MessageInput } from './MessageInput';
|
|
||||||
@@ -1,419 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useRef, useEffect } from 'react';
|
import { MessagingPage } from '@/components/message';
|
||||||
import Image from 'next/image';
|
|
||||||
import { ChatHeader, MessageInput } from './component';
|
|
||||||
|
|
||||||
// Custom scrollbar styles
|
|
||||||
const customScrollbarStyles = `
|
|
||||||
.custom-scrollbar::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba(0, 41, 61, 0.5);
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: rgba(0, 41, 61, 0.7);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Mock data for conversations
|
|
||||||
const conversations = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'Pradeep Ram',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: 'Pradeep',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: 'Gokulraj',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
name: 'Suriya s',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
name: 'Sanjay',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
name: 'Pradeep Ram',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
|
||||||
date: 'Dec 4',
|
|
||||||
isOnline: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '7',
|
|
||||||
name: 'Rahul Kumar',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Thanks for your help with the property listing!',
|
|
||||||
date: 'Dec 3',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '8',
|
|
||||||
name: 'Anita Singh',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Can we schedule a viewing for tomorrow?',
|
|
||||||
date: 'Dec 3',
|
|
||||||
isOnline: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '9',
|
|
||||||
name: 'Vikram Patel',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'The documents have been submitted.',
|
|
||||||
date: 'Dec 2',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '10',
|
|
||||||
name: 'Meera Sharma',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Looking forward to our meeting next week.',
|
|
||||||
date: 'Dec 2',
|
|
||||||
isOnline: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '11',
|
|
||||||
name: 'Arjun Reddy',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'Please send me the property details.',
|
|
||||||
date: 'Dec 1',
|
|
||||||
isOnline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '12',
|
|
||||||
name: 'Sneha Nair',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
lastMessage: 'I have a few questions about the contract.',
|
|
||||||
date: 'Dec 1',
|
|
||||||
isOnline: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Mock data for selected user
|
|
||||||
const selectedUserData = {
|
|
||||||
name: 'Pradeep Ram',
|
|
||||||
role: 'Advisor',
|
|
||||||
lastSeen: '21h Ago',
|
|
||||||
avatar: '/assets/icons/user-placeholder-icon.svg',
|
|
||||||
isOnline: true,
|
|
||||||
pronouns: 'He/Him',
|
|
||||||
expertise: ['Sales', 'Analytics', 'Inspection', 'Residential', 'Commercial'],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mock messages data
|
|
||||||
const messages = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
senderId: '1',
|
|
||||||
text: 'Hi! I saw your profile and I think you might be a great fit for what I\'m looking for.',
|
|
||||||
timestamp: '10:30 AM',
|
|
||||||
isOwn: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
senderId: 'me',
|
|
||||||
text: 'Hello! Thank you for reaching out. I\'d be happy to help. What kind of property are you looking for?',
|
|
||||||
timestamp: '10:32 AM',
|
|
||||||
isOwn: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
senderId: '1',
|
|
||||||
text: 'I\'m looking for a residential property in the downtown area. Preferably a 3-bedroom apartment with modern amenities.',
|
|
||||||
timestamp: '10:35 AM',
|
|
||||||
isOwn: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
senderId: 'me',
|
|
||||||
text: 'That sounds great! I have several listings that might interest you. Do you have a specific budget range in mind?',
|
|
||||||
timestamp: '10:38 AM',
|
|
||||||
isOwn: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
senderId: '1',
|
|
||||||
text: 'My budget is around $500,000 to $700,000. I\'m also interested in properties with good investment potential.',
|
|
||||||
timestamp: '10:40 AM',
|
|
||||||
isOwn: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
senderId: 'me',
|
|
||||||
text: 'Perfect! I have a few properties in that range. Would you like to schedule a viewing this weekend?',
|
|
||||||
timestamp: '10:42 AM',
|
|
||||||
isOwn: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '7',
|
|
||||||
senderId: '1',
|
|
||||||
text: 'Yes, that would be great! Saturday afternoon works best for me.',
|
|
||||||
timestamp: '10:45 AM',
|
|
||||||
isOwn: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '8',
|
|
||||||
senderId: 'me',
|
|
||||||
text: 'Saturday at 2 PM works for me. I\'ll send you the addresses and details of the properties we\'ll be visiting.',
|
|
||||||
timestamp: '10:48 AM',
|
|
||||||
isOwn: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '9',
|
|
||||||
senderId: '1',
|
|
||||||
text: 'Sounds perfect! Looking forward to it. Thank you for your quick response.',
|
|
||||||
timestamp: '10:50 AM',
|
|
||||||
isOwn: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '10',
|
|
||||||
senderId: 'me',
|
|
||||||
text: 'You\'re welcome! See you on Saturday. Feel free to reach out if you have any questions before then.',
|
|
||||||
timestamp: '10:52 AM',
|
|
||||||
isOwn: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function MessagePage() {
|
|
||||||
const [selectedConversation, setSelectedConversation] = useState<string | null>('1');
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
||||||
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const handleSendMessage = (message: string) => {
|
|
||||||
console.log('Sending message:', message);
|
|
||||||
// In production, this would send the message to the API
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
if (messagesContainerRef.current) {
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = messagesContainerRef.current;
|
|
||||||
// Show button if not at bottom (with 100px threshold)
|
|
||||||
setShowScrollButton(scrollHeight - scrollTop - clientHeight > 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
|
||||||
if (messagesContainerRef.current) {
|
|
||||||
messagesContainerRef.current.scrollTo({
|
|
||||||
top: messagesContainerRef.current.scrollHeight,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Scroll to bottom on initial load
|
|
||||||
scrollToBottom();
|
|
||||||
}, [selectedConversation]);
|
|
||||||
|
|
||||||
const filteredConversations = conversations.filter((conv) =>
|
|
||||||
conv.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
|
export default function AgentMessagePage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="max-w-7xl mx-auto px-4 lg:px-8 py-6">
|
||||||
<style>{customScrollbarStyles}</style>
|
<MessagingPage />
|
||||||
<div className="border border-[#00293d]/10 rounded-[20px] bg-white overflow-hidden">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex items-center gap-4 p-4 border-b border-[#00293d]/10">
|
|
||||||
<h1 className="font-fractul font-bold text-[18px] leading-[22px] text-[#00293D]">
|
|
||||||
Messaging
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{/* Search bar */}
|
|
||||||
<div className="flex-1 max-w-[600px]">
|
|
||||||
<div className="flex items-center gap-2 border border-[#00293d]/10 rounded-[15px] px-4 py-2">
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/search-icon.svg"
|
|
||||||
alt="Search"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
|
||||||
placeholder="Search Message"
|
|
||||||
className="flex-1 font-serif font-normal text-[18px] leading-[24px] text-[#00293D] placeholder-[#00293D]/50 focus:outline-none"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Header actions */}
|
|
||||||
<div className="flex items-center gap-2 ml-auto">
|
|
||||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer">
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/three-dots-horizontal-icon.svg"
|
|
||||||
alt="More options"
|
|
||||||
width={24}
|
|
||||||
height={24}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer">
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/compose-icon.svg"
|
|
||||||
alt="Compose"
|
|
||||||
width={24}
|
|
||||||
height={24}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main content */}
|
|
||||||
<div className="flex h-[700px]">
|
|
||||||
{/* Left sidebar - Conversation list */}
|
|
||||||
<div className="w-[380px] border-r border-[#00293d]/10 flex-shrink-0 overflow-hidden flex flex-col">
|
|
||||||
<div className="flex-1 overflow-y-auto p-2 space-y-2 custom-scrollbar">
|
|
||||||
{filteredConversations.map((conversation) => (
|
|
||||||
<div
|
|
||||||
key={conversation.id}
|
|
||||||
onClick={() => setSelectedConversation(conversation.id)}
|
|
||||||
className="relative flex items-start gap-3 p-4 rounded-[15px] cursor-pointer transition-colors hover:bg-[#00293d]/5"
|
|
||||||
style={{ border: '1px solid rgba(0, 41, 61, 0.1)' }}
|
|
||||||
>
|
|
||||||
{/* Active indicator line */}
|
|
||||||
{selectedConversation === conversation.id && (
|
|
||||||
<div className="absolute left-0 top-1/2 -translate-y-1/2 w-[4px] h-[70px] bg-[#E58625] rounded-r-[4px]" />
|
|
||||||
)}
|
|
||||||
{/* Avatar with online indicator */}
|
|
||||||
<div className="relative flex-shrink-0">
|
|
||||||
<div className="w-[70px] h-[70px] rounded-full overflow-hidden border border-[#00293D]/20">
|
|
||||||
<Image
|
|
||||||
src={conversation.avatar}
|
|
||||||
alt={conversation.name}
|
|
||||||
width={70}
|
|
||||||
height={70}
|
|
||||||
className="w-full h-full object-cover rounded-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{conversation.isOnline && (
|
|
||||||
<div className="absolute bottom-1 right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p className="font-fractul font-medium text-[14px] leading-[17px] text-[#00293D] mb-1">
|
|
||||||
{conversation.name}
|
|
||||||
</p>
|
|
||||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] line-clamp-2">
|
|
||||||
{conversation.lastMessage}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Date */}
|
|
||||||
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] flex-shrink-0">
|
|
||||||
{conversation.date}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right panel - Chat area */}
|
|
||||||
<div className="flex-1 flex flex-col p-4">
|
|
||||||
{selectedConversation ? (
|
|
||||||
<>
|
|
||||||
{/* Chat header */}
|
|
||||||
<ChatHeader {...selectedUserData} />
|
|
||||||
|
|
||||||
{/* Messages area wrapper */}
|
|
||||||
<div className="flex-1 relative">
|
|
||||||
<div
|
|
||||||
ref={messagesContainerRef}
|
|
||||||
onScroll={handleScroll}
|
|
||||||
className="absolute inset-0 overflow-y-auto py-4 custom-scrollbar"
|
|
||||||
>
|
|
||||||
<div className="space-y-4 px-2">
|
|
||||||
{messages.map((message) => (
|
|
||||||
<div
|
|
||||||
key={message.id}
|
|
||||||
className={`flex ${message.isOwn ? 'justify-end' : 'justify-start'}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`max-w-[70%] rounded-[15px] px-4 py-3 ${
|
|
||||||
message.isOwn
|
|
||||||
? 'bg-[#e58625] text-[#00293D]'
|
|
||||||
: 'bg-[#00293d]/10 text-[#00293D]'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<p className="font-serif font-normal text-[14px] leading-[19px]">
|
|
||||||
{message.text}
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
className={`font-serif font-normal text-[12px] leading-[16px] mt-1 ${
|
|
||||||
message.isOwn ? 'text-[#00293D]/70' : 'text-[#00293D]/50'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{message.timestamp}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Scroll to bottom button - only shows when scrolled up */}
|
|
||||||
{showScrollButton && (
|
|
||||||
<button
|
|
||||||
onClick={scrollToBottom}
|
|
||||||
className="absolute bottom-4 right-4 w-[50px] h-[50px] bg-[#00293d]/10 rounded-full flex items-center justify-center hover:bg-[#00293d]/20 transition-all cursor-pointer shadow-lg z-10"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src="/assets/icons/arrow-down-icon.svg"
|
|
||||||
alt="Scroll to bottom"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Message input */}
|
|
||||||
<MessageInput onSend={handleSendMessage} />
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center justify-center h-full text-[#00293D]/50 font-serif">
|
|
||||||
Select a conversation to start messaging
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/app/(user)/user/message/page.tsx
Normal file
11
src/app/(user)/user/message/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { MessagingPage } from '@/components/message';
|
||||||
|
|
||||||
|
export default function UserMessagePage() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-7xl mx-auto px-4 lg:px-8 py-6">
|
||||||
|
<MessagingPage />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -129,6 +129,7 @@ export default function AgentProfileView() {
|
|||||||
bio={agentData.bio}
|
bio={agentData.bio}
|
||||||
expertise={agentData.expertise}
|
expertise={agentData.expertise}
|
||||||
showEditButton={false}
|
showEditButton={false}
|
||||||
|
messageHref="/user/message"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Experience Section */}
|
{/* Experience Section */}
|
||||||
|
|||||||
217
src/app/faq/page.tsx
Normal file
217
src/app/faq/page.tsx
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { CommonHeader } from '@/components/layout/CommonHeader';
|
||||||
|
import { Footer } from '@/components/layout/Footer';
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{ id: 'all', label: 'All Questions' },
|
||||||
|
{ id: 'agent', label: 'Agent Resources' },
|
||||||
|
{ id: 'buying', label: 'Buying a Home' },
|
||||||
|
{ id: 'selling', label: 'Selling a Home' },
|
||||||
|
{ id: 'platform', label: 'Platform & Account' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const faqs = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
icon: '/assets/icons/home-icon.svg',
|
||||||
|
question: 'How do I update my listing status?',
|
||||||
|
answer: 'To update your listing status, log in to your Agent Dashboard. Locate the property in your "Active Listings" tab, click the three-dot menu on the right, and select "Update Status." You can change it to Pending, Sold, or Temporarily Off Market. Changes are reflected immediately.',
|
||||||
|
category: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
icon: '/assets/icons/wallet-icon.svg',
|
||||||
|
question: 'What is the pre-approval process?',
|
||||||
|
answer: 'The pre-approval process involves submitting your financial documents to a lender who will review your credit history, income, assets, and debts. Once approved, you\'ll receive a pre-approval letter stating how much you can borrow, which strengthens your position when making offers on properties.',
|
||||||
|
category: 'buying',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
icon: '/assets/icons/calendar-orange-icon.svg',
|
||||||
|
question: 'How do I schedule an open house?',
|
||||||
|
answer: 'To schedule an open house, navigate to your Agent Dashboard and select the property you want to showcase. Click on "Schedule Open House" and choose your preferred date and time. You can add details about the event and it will be automatically listed on the property page for potential buyers to see.',
|
||||||
|
category: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
icon: '/assets/icons/reset-icon.svg',
|
||||||
|
question: 'How do I reset my password?',
|
||||||
|
answer: 'To reset your password, click on the "Login" button and then select "Forgot Password." Enter your registered email address and we\'ll send you a password reset link. Follow the link to create a new password. For security, the link expires after 24 hours.',
|
||||||
|
category: 'platform',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
icon: '/assets/icons/contact-icon.svg',
|
||||||
|
question: 'How do I contact a listing agent directly?',
|
||||||
|
answer: 'You can contact a listing agent by visiting the property page and clicking the "Contact Agent" button. You\'ll be able to send a message directly to the agent through our secure messaging system. Alternatively, you can call the agent using the phone number displayed on their profile.',
|
||||||
|
category: 'buying',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
icon: '/assets/icons/caution-icon.svg',
|
||||||
|
question: 'What if I find inaccurate data on a listing?',
|
||||||
|
answer: 'If you find inaccurate information on a listing, please use the "Report Issue" button on the property page. Describe the inaccuracy and our team will review and correct it within 24-48 hours. You can also contact the listing agent directly to request corrections.',
|
||||||
|
category: 'platform',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function FAQPage() {
|
||||||
|
const [activeCategory, setActiveCategory] = useState('all');
|
||||||
|
const [expandedFaq, setExpandedFaq] = useState<number | null>(1);
|
||||||
|
|
||||||
|
const filteredFaqs = activeCategory === 'all'
|
||||||
|
? faqs
|
||||||
|
: faqs.filter(faq => faq.category === activeCategory);
|
||||||
|
|
||||||
|
const toggleFaq = (id: number) => {
|
||||||
|
setExpandedFaq(expandedFaq === id ? null : id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-white flex flex-col">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 w-full">
|
||||||
|
<CommonHeader />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<main className="flex-1 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8 w-full">
|
||||||
|
<div className="flex flex-col lg:flex-row gap-8">
|
||||||
|
{/* Categories Sidebar */}
|
||||||
|
<div className="lg:w-[280px] flex-shrink-0">
|
||||||
|
<div className="border border-[#00293d]/10 rounded-[15px] overflow-hidden shadow-sm">
|
||||||
|
{/* Sidebar Header */}
|
||||||
|
<div className="p-4 border-b border-[#00293d]/10">
|
||||||
|
<h2 className="font-fractul font-bold text-[20px] text-[#00293d]">
|
||||||
|
Categories
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category Items */}
|
||||||
|
<div className="py-2">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<button
|
||||||
|
key={category.id}
|
||||||
|
onClick={() => setActiveCategory(category.id)}
|
||||||
|
className={`w-full px-6 py-3 flex items-center justify-between text-left transition-colors ${
|
||||||
|
activeCategory === category.id
|
||||||
|
? 'bg-[#e58625] text-white'
|
||||||
|
: 'text-[#00293d] hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className={`${activeCategory === category.id ? 'font-fractul font-semibold' : 'font-serif'} text-[14px]`}>
|
||||||
|
{category.label}
|
||||||
|
</span>
|
||||||
|
{activeCategory === category.id && (
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/chevron-right-icon.svg"
|
||||||
|
alt=""
|
||||||
|
width={12}
|
||||||
|
height={12}
|
||||||
|
className="brightness-0 invert"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FAQ Content */}
|
||||||
|
<div className="flex-1">
|
||||||
|
{/* Title */}
|
||||||
|
<h1 className="font-fractul font-bold text-[24px] text-[#00293d] mb-6">
|
||||||
|
Frequently Asked Questions ?
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* FAQ Accordion */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
{filteredFaqs.map((faq) => (
|
||||||
|
<div
|
||||||
|
key={faq.id}
|
||||||
|
className={`border border-[#00293d]/10 rounded-[15px] overflow-hidden transition-shadow ${
|
||||||
|
expandedFaq === faq.id ? 'shadow-md' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => toggleFaq(faq.id)}
|
||||||
|
className="w-full px-6 py-5 flex items-center gap-4 text-left"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={faq.icon}
|
||||||
|
alt=""
|
||||||
|
width={28}
|
||||||
|
height={28}
|
||||||
|
/>
|
||||||
|
<span className="flex-1 font-fractul font-semibold text-[18px] text-[#00293d]">
|
||||||
|
{faq.question}
|
||||||
|
</span>
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/chevron-down-icon.svg"
|
||||||
|
alt=""
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
className={`transition-transform duration-200 ${
|
||||||
|
expandedFaq === faq.id ? 'rotate-180' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{expandedFaq === faq.id && (
|
||||||
|
<div className="px-6 pb-5">
|
||||||
|
<p className="font-serif text-[16px] text-[#00293d] leading-relaxed pl-11">
|
||||||
|
{faq.answer}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Still Need Help Section */}
|
||||||
|
<div className="mt-8 border border-[#00293d]/10 rounded-[15px] p-8">
|
||||||
|
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-fractul font-bold text-[20px] text-[#00293d] mb-2">
|
||||||
|
Still Need Help ?
|
||||||
|
</h3>
|
||||||
|
<p className="font-serif text-[16px] text-[#00293d]">
|
||||||
|
Our Support Team Is Available<br />
|
||||||
|
Mon-Fri, 9am-6pm IST
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<button className="flex items-center justify-center gap-2 w-[174px] h-[51px] border border-[#00293d] rounded-[7px] font-fractul text-[16px] text-[#00293d] hover:bg-gray-50 transition-colors">
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/chat-icon.svg"
|
||||||
|
alt=""
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
Start Live Chat
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center justify-center gap-2 w-[174px] h-[51px] border border-[#00293d] rounded-[7px] font-fractul text-[16px] text-[#00293d] hover:bg-gray-50 transition-colors">
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/email-icon.svg"
|
||||||
|
alt=""
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
Email Support
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
442
src/components/message/MessagingPage.tsx
Normal file
442
src/components/message/MessagingPage.tsx
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useRef, useEffect } from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { ChatHeader } from './ChatHeader';
|
||||||
|
import { MessageInput } from './MessageInput';
|
||||||
|
|
||||||
|
// Custom scrollbar styles
|
||||||
|
const customScrollbarStyles = `
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 41, 61, 0.5);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(0, 41, 61, 0.7);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Types
|
||||||
|
interface Conversation {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
lastMessage: string;
|
||||||
|
date: string;
|
||||||
|
isOnline: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
id: string;
|
||||||
|
senderId: string;
|
||||||
|
text: string;
|
||||||
|
timestamp: string;
|
||||||
|
isOwn: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectedUser {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
lastSeen: string;
|
||||||
|
avatar: string;
|
||||||
|
isOnline: boolean;
|
||||||
|
pronouns?: string;
|
||||||
|
expertise?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessagingPageProps {
|
||||||
|
conversations?: Conversation[];
|
||||||
|
messages?: Message[];
|
||||||
|
selectedUser?: SelectedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock data for conversations
|
||||||
|
const defaultConversations: Conversation[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'Pradeep Ram',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: 'Pradeep',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
name: 'Gokulraj',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
name: 'Suriya s',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
name: 'Sanjay',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
name: 'Pradeep Ram',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||||
|
date: 'Dec 4',
|
||||||
|
isOnline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
name: 'Rahul Kumar',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Thanks for your help with the property listing!',
|
||||||
|
date: 'Dec 3',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
name: 'Anita Singh',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Can we schedule a viewing for tomorrow?',
|
||||||
|
date: 'Dec 3',
|
||||||
|
isOnline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
name: 'Vikram Patel',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'The documents have been submitted.',
|
||||||
|
date: 'Dec 2',
|
||||||
|
isOnline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
name: 'Meera Sharma',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
lastMessage: 'Looking forward to our meeting next week.',
|
||||||
|
date: 'Dec 2',
|
||||||
|
isOnline: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock data for selected user
|
||||||
|
const defaultSelectedUser: SelectedUser = {
|
||||||
|
name: 'Pradeep Ram',
|
||||||
|
role: 'Advisor',
|
||||||
|
lastSeen: '21h Ago',
|
||||||
|
avatar: '/assets/icons/user-placeholder-icon.svg',
|
||||||
|
isOnline: true,
|
||||||
|
pronouns: 'He/Him',
|
||||||
|
expertise: ['Sales', 'Analytics', 'Inspection', 'Residential', 'Commercial'],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock messages data
|
||||||
|
const defaultMessages: Message[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
senderId: '1',
|
||||||
|
text: 'Hi! I saw your profile and I think you might be a great fit for what I\'m looking for.',
|
||||||
|
timestamp: '10:30 AM',
|
||||||
|
isOwn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
senderId: 'me',
|
||||||
|
text: 'Hello! Thank you for reaching out. I\'d be happy to help. What kind of property are you looking for?',
|
||||||
|
timestamp: '10:32 AM',
|
||||||
|
isOwn: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
senderId: '1',
|
||||||
|
text: 'I\'m looking for a residential property in the downtown area. Preferably a 3-bedroom apartment with modern amenities.',
|
||||||
|
timestamp: '10:35 AM',
|
||||||
|
isOwn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
senderId: 'me',
|
||||||
|
text: 'That sounds great! I have several listings that might interest you. Do you have a specific budget range in mind?',
|
||||||
|
timestamp: '10:38 AM',
|
||||||
|
isOwn: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
senderId: '1',
|
||||||
|
text: 'My budget is around $500,000 to $700,000. I\'m also interested in properties with good investment potential.',
|
||||||
|
timestamp: '10:40 AM',
|
||||||
|
isOwn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
senderId: 'me',
|
||||||
|
text: 'Perfect! I have a few properties in that range. Would you like to schedule a viewing this weekend?',
|
||||||
|
timestamp: '10:42 AM',
|
||||||
|
isOwn: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
senderId: '1',
|
||||||
|
text: 'Yes, that would be great! Saturday afternoon works best for me.',
|
||||||
|
timestamp: '10:45 AM',
|
||||||
|
isOwn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
senderId: 'me',
|
||||||
|
text: 'Saturday at 2 PM works for me. I\'ll send you the addresses and details of the properties we\'ll be visiting.',
|
||||||
|
timestamp: '10:48 AM',
|
||||||
|
isOwn: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
senderId: '1',
|
||||||
|
text: 'Sounds perfect! Looking forward to it. Thank you for your quick response.',
|
||||||
|
timestamp: '10:50 AM',
|
||||||
|
isOwn: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
senderId: 'me',
|
||||||
|
text: 'You\'re welcome! See you on Saturday. Feel free to reach out if you have any questions before then.',
|
||||||
|
timestamp: '10:52 AM',
|
||||||
|
isOwn: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function MessagingPage({
|
||||||
|
conversations = defaultConversations,
|
||||||
|
messages = defaultMessages,
|
||||||
|
selectedUser = defaultSelectedUser,
|
||||||
|
}: MessagingPageProps) {
|
||||||
|
const [selectedConversation, setSelectedConversation] = useState<string | null>('1');
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
||||||
|
const messagesContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const handleSendMessage = (message: string) => {
|
||||||
|
console.log('Sending message:', message);
|
||||||
|
// In production, this would send the message to the API
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (messagesContainerRef.current) {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = messagesContainerRef.current;
|
||||||
|
// Show button if not at bottom (with 100px threshold)
|
||||||
|
setShowScrollButton(scrollHeight - scrollTop - clientHeight > 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
if (messagesContainerRef.current) {
|
||||||
|
messagesContainerRef.current.scrollTo({
|
||||||
|
top: messagesContainerRef.current.scrollHeight,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Scroll to bottom on initial load
|
||||||
|
scrollToBottom();
|
||||||
|
}, [selectedConversation]);
|
||||||
|
|
||||||
|
const filteredConversations = conversations.filter((conv) =>
|
||||||
|
conv.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style>{customScrollbarStyles}</style>
|
||||||
|
<div className="border border-[#00293d]/10 rounded-[20px] bg-white overflow-hidden">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center gap-4 p-4 border-b border-[#00293d]/10">
|
||||||
|
<h1 className="font-fractul font-bold text-[18px] leading-[22px] text-[#00293D]">
|
||||||
|
Messaging
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Search bar */}
|
||||||
|
<div className="flex-1 max-w-[600px]">
|
||||||
|
<div className="flex items-center gap-2 border border-[#00293d]/10 rounded-[15px] px-4 py-2">
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/search-icon.svg"
|
||||||
|
alt="Search"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
placeholder="Search Message"
|
||||||
|
className="flex-1 font-serif font-normal text-[18px] leading-[24px] text-[#00293D] placeholder-[#00293D]/50 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Header actions */}
|
||||||
|
<div className="flex items-center gap-2 ml-auto">
|
||||||
|
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer">
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/three-dots-horizontal-icon.svg"
|
||||||
|
alt="More options"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button className="p-2 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer">
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/compose-icon.svg"
|
||||||
|
alt="Compose"
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div className="flex h-[700px]">
|
||||||
|
{/* Left sidebar - Conversation list */}
|
||||||
|
<div className="w-[380px] border-r border-[#00293d]/10 flex-shrink-0 overflow-hidden flex flex-col">
|
||||||
|
<div className="flex-1 overflow-y-auto p-2 space-y-2 custom-scrollbar">
|
||||||
|
{filteredConversations.map((conversation) => (
|
||||||
|
<div
|
||||||
|
key={conversation.id}
|
||||||
|
onClick={() => setSelectedConversation(conversation.id)}
|
||||||
|
className="relative flex items-start gap-3 p-4 rounded-[15px] cursor-pointer transition-colors hover:bg-[#00293d]/5"
|
||||||
|
style={{ border: '1px solid rgba(0, 41, 61, 0.1)' }}
|
||||||
|
>
|
||||||
|
{/* Active indicator line */}
|
||||||
|
{selectedConversation === conversation.id && (
|
||||||
|
<div className="absolute left-0 top-1/2 -translate-y-1/2 w-[4px] h-[70px] bg-[#E58625] rounded-r-[4px]" />
|
||||||
|
)}
|
||||||
|
{/* Avatar with online indicator */}
|
||||||
|
<div className="relative flex-shrink-0">
|
||||||
|
<div className="w-[70px] h-[70px] rounded-full overflow-hidden border border-[#00293D]/20">
|
||||||
|
<Image
|
||||||
|
src={conversation.avatar}
|
||||||
|
alt={conversation.name}
|
||||||
|
width={70}
|
||||||
|
height={70}
|
||||||
|
className="w-full h-full object-cover rounded-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{conversation.isOnline && (
|
||||||
|
<div className="absolute bottom-1 right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="font-fractul font-medium text-[14px] leading-[17px] text-[#00293D] mb-1">
|
||||||
|
{conversation.name}
|
||||||
|
</p>
|
||||||
|
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] line-clamp-2">
|
||||||
|
{conversation.lastMessage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Date */}
|
||||||
|
<p className="font-serif font-normal text-[14px] leading-[19px] text-[#00293D] flex-shrink-0">
|
||||||
|
{conversation.date}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right panel - Chat area */}
|
||||||
|
<div className="flex-1 flex flex-col p-4">
|
||||||
|
{selectedConversation ? (
|
||||||
|
<>
|
||||||
|
{/* Chat header */}
|
||||||
|
<ChatHeader {...selectedUser} />
|
||||||
|
|
||||||
|
{/* Messages area wrapper */}
|
||||||
|
<div className="flex-1 relative">
|
||||||
|
<div
|
||||||
|
ref={messagesContainerRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
className="absolute inset-0 overflow-y-auto py-4 custom-scrollbar"
|
||||||
|
>
|
||||||
|
<div className="space-y-4 px-2">
|
||||||
|
{messages.map((message) => (
|
||||||
|
<div
|
||||||
|
key={message.id}
|
||||||
|
className={`flex ${message.isOwn ? 'justify-end' : 'justify-start'}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`max-w-[70%] rounded-[15px] px-4 py-3 ${
|
||||||
|
message.isOwn
|
||||||
|
? 'bg-[#e58625] text-[#00293D]'
|
||||||
|
: 'bg-[#00293d]/10 text-[#00293D]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<p className="font-serif font-normal text-[14px] leading-[19px]">
|
||||||
|
{message.text}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
className={`font-serif font-normal text-[12px] leading-[16px] mt-1 ${
|
||||||
|
message.isOwn ? 'text-[#00293D]/70' : 'text-[#00293D]/50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{message.timestamp}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scroll to bottom button - only shows when scrolled up */}
|
||||||
|
{showScrollButton && (
|
||||||
|
<button
|
||||||
|
onClick={scrollToBottom}
|
||||||
|
className="absolute bottom-4 right-4 w-[50px] h-[50px] bg-[#00293d]/10 rounded-full flex items-center justify-center hover:bg-[#00293d]/20 transition-all cursor-pointer shadow-lg z-10"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/assets/icons/arrow-down-icon.svg"
|
||||||
|
alt="Scroll to bottom"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Message input */}
|
||||||
|
<MessageInput onSend={handleSendMessage} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center h-full text-[#00293D]/50 font-serif">
|
||||||
|
Select a conversation to start messaging
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
4
src/components/message/index.ts
Normal file
4
src/components/message/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Shared Message Components
|
||||||
|
export { ChatHeader } from './ChatHeader';
|
||||||
|
export { MessageInput } from './MessageInput';
|
||||||
|
export { MessagingPage } from './MessagingPage';
|
||||||
Reference in New Issue
Block a user