diff --git a/src/app/dashboard/cms/page.tsx b/src/app/dashboard/cms/page.tsx index 934da11..29b9aee 100644 --- a/src/app/dashboard/cms/page.tsx +++ b/src/app/dashboard/cms/page.tsx @@ -1,9 +1,10 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useEffect, useState, useCallback, useRef } from 'react'; import { useRouter } from 'next/navigation'; import { cmsService, + usersService, getErrorMessage, CmsContentRecord, HeroContent, @@ -14,6 +15,7 @@ import { TestimonialsContent, TestimonialItem, StatItem, + User, } from '@/services'; // --------------------------------------------------------------------------- @@ -103,6 +105,14 @@ export default function CmsPage() { const [modalError, setModalError] = useState(''); const [professionalsTab, setProfessionalsTab] = useState<'agents' | 'lenders'>('agents'); + // Agent search + const [agentSearchQuery, setAgentSearchQuery] = useState(''); + const [agentSearchResults, setAgentSearchResults] = useState([]); + const [isSearchingAgents, setIsSearchingAgents] = useState(false); + const [showAgentDropdown, setShowAgentDropdown] = useState(false); + const agentSearchTimeout = useRef(null); + const agentSearchRef = useRef(null); + // Fetch all CMS records for "landing" page const fetchRecords = useCallback(async () => { setIsLoading(true); @@ -133,6 +143,57 @@ export default function CmsPage() { } }, [notification]); + // Close agent search dropdown on outside click + useEffect(() => { + function handleClickOutside(e: MouseEvent) { + if (agentSearchRef.current && !agentSearchRef.current.contains(e.target as Node)) { + setShowAgentDropdown(false); + } + } + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // Agent search with debounce + function handleAgentSearch(query: string) { + setAgentSearchQuery(query); + if (agentSearchTimeout.current) clearTimeout(agentSearchTimeout.current); + if (query.length < 2) { + setAgentSearchResults([]); + setShowAgentDropdown(false); + return; + } + setIsSearchingAgents(true); + setShowAgentDropdown(true); + agentSearchTimeout.current = setTimeout(async () => { + try { + const res = await usersService.getUsers({ role: 'AGENT', search: query, limit: 10 }); + setAgentSearchResults(res.users); + } catch { + setAgentSearchResults([]); + } finally { + setIsSearchingAgents(false); + } + }, 300); + } + + function selectAgent(user: User, onChange: (items: ProfessionalItem[]) => void, currentItems: ProfessionalItem[]) { + if (currentItems.length >= 3) return; + const profile = user.profile; + const agent = user.agentProfile; + const name = profile ? `${profile.firstName} ${profile.lastName}`.trim() : ''; + const subtitle = agent?.headline || agent?.agentType?.name || ''; + const location = profile ? [profile.city, profile.state].filter(Boolean).join(', ') : ''; + const experience = agent?.yearsOfExperience ? `${agent.yearsOfExperience}+ years in real estate.` : ''; + const imageUrl = profile?.avatar || ''; + + const newItem: ProfessionalItem = { name, subtitle, location, experience, expertise: [], imageUrl }; + onChange([...currentItems, newItem]); + setAgentSearchQuery(''); + setAgentSearchResults([]); + setShowAgentDropdown(false); + } + // Helpers function recordFor(sectionKey: string): CmsContentRecord | undefined { return records.find((r) => r.sectionKey === sectionKey); @@ -144,6 +205,9 @@ export default function CmsPage() { setEditContent(existing ? JSON.parse(JSON.stringify(existing.content)) : defaultContentFor(sectionKey)); setModalError(''); setProfessionalsTab('agents'); + setAgentSearchQuery(''); + setAgentSearchResults([]); + setShowAgentDropdown(false); setEditingSection(sectionKey); } @@ -270,15 +334,82 @@ export default function CmsPage() { onChange(items.filter((_, i) => i !== index)); } function addItem() { + if (items.length >= 3) return; onChange([...items, emptyProfessionalItem()]); } + const atMax = items.length >= 3; + return (
+ {/* Agent search */} + {!atMax &&
+ +
+ handleAgentSearch(e.target.value)} + onFocus={() => { if (agentSearchResults.length > 0) setShowAgentDropdown(true); }} + placeholder="Search agents by name or email..." + /> + {isSearchingAgents && ( +
+
+
+ )} +
+ {showAgentDropdown && agentSearchQuery.length >= 2 && ( +
+ {isSearchingAgents && agentSearchResults.length === 0 ? ( +
Searching...
+ ) : agentSearchResults.length === 0 ? ( +
No agents found
+ ) : ( + agentSearchResults.map((user) => ( + + )) + )} +
+ )} +
} +
- - + +
+ {atMax &&

Maximum of 3 {label.toLowerCase()} reached.

} {items.length === 0 &&

No {label.toLowerCase()} added yet.

}
{items.map((item, idx) => (