import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { Search, ArrowRight, Sparkles, Book, GraduationCap, ArrowUpRight, TextSearch, ScanSearch, Users } from 'lucide-react'; import { searchOrgContent } from '@services/search/search'; import { useLHSession } from '@components/Contexts/LHSessionContext'; import Link from 'next/link'; import { getCourseThumbnailMediaDirectory, getUserAvatarMediaDirectory } from '@services/media/media'; import { useDebounce } from '@/hooks/useDebounce'; import { useOrg } from '@components/Contexts/OrgContext'; import { getUriWithOrg } from '@services/config/config'; import { removeCoursePrefix } from '../Thumbnails/CourseThumbnail'; import UserAvatar from '../UserAvatar'; interface User { username: string; first_name: string; last_name: string; email: string; avatar_image: string; bio: string; details: Record; profile: Record; id: number; user_uuid: string; } interface Author { user: User; authorship: string; authorship_status: string; creation_date: string; update_date: string; } interface Course { name: string; description: string; about: string; learnings: string; tags: string; thumbnail_image: string; public: boolean; open_to_contributors: boolean; id: number; org_id: number; authors: Author[]; course_uuid: string; creation_date: string; update_date: string; } interface Collection { name: string; public: boolean; description: string; id: number; courses: string[]; collection_uuid: string; creation_date: string; update_date: string; } interface SearchResults { courses: Course[]; collections: Collection[]; users: User[]; } interface SearchBarProps { orgslug: string; className?: string; isMobile?: boolean; showSearchSuggestions?: boolean; } const CourseResultsSkeleton = () => (
{[1, 2].map((i) => (
))}
); export const SearchBar: React.FC = ({ orgslug, className = '', isMobile = false, showSearchSuggestions = false, }) => { const org = useOrg() as any; const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState({ courses: [], collections: [], users: [] }); const [isLoading, setIsLoading] = useState(false); const [showResults, setShowResults] = useState(false); const searchRef = useRef(null); const session = useLHSession() as any; const [isInitialLoad, setIsInitialLoad] = useState(true); // Debounce the search query value const debouncedSearch = useDebounce(searchQuery, 300); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (searchRef.current && !searchRef.current.contains(event.target as Node)) { setShowResults(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); useEffect(() => { const fetchResults = async () => { if (debouncedSearch.trim().length === 0) { setSearchResults({ courses: [], collections: [], users: [] }); setIsLoading(false); return; } setIsLoading(true); try { const response = await searchOrgContent( orgslug, debouncedSearch, 1, 3, null, session?.data?.tokens?.access_token ); console.log('Search API Response:', response); // Debug log // Type assertion and safe access const typedResponse = response.data as any; // Ensure we have the correct structure and handle potential undefined values const processedResults: SearchResults = { courses: Array.isArray(typedResponse?.courses) ? typedResponse.courses : [], collections: Array.isArray(typedResponse?.collections) ? typedResponse.collections : [], users: Array.isArray(typedResponse?.users) ? typedResponse.users : [] }; console.log('Processed Results:', processedResults); // Debug log setSearchResults(processedResults); } catch (error) { console.error('Error searching content:', error); setSearchResults({ courses: [], collections: [], users: [] }); } setIsLoading(false); setIsInitialLoad(false); }; fetchResults(); }, [debouncedSearch, orgslug, session?.data?.tokens?.access_token]); const MemoizedEmptyState = useMemo(() => { if (!searchQuery.trim()) { return (

Discover Your Next Learning Journey

Start typing to search through available content

); } return null; }, [searchQuery]); const searchTerms = useMemo(() => [ { term: searchQuery, type: 'exact', icon: }, { term: `${searchQuery} courses`, type: 'courses', icon: }, { term: `${searchQuery} collections`, type: 'collections', icon: }, ], [searchQuery]); const MemoizedSearchSuggestions = useMemo(() => { if (searchQuery.trim()) { return (
Search suggestions
{searchTerms.map(({ term, type, icon }) => (
{icon} {term}
))}
); } return null; }, [searchQuery, searchTerms, orgslug]); const MemoizedQuickResults = useMemo(() => { const hasResults = searchResults.courses.length > 0 || searchResults.collections.length > 0 || searchResults.users.length > 0; if (!hasResults) return null; return (
Quick Results
{/* Courses Section */} {searchResults.courses.length > 0 && (
Courses
{searchResults.courses.map((course) => (
{course.thumbnail_image ? ( {course.name} ) : (
)}

{course.name}

Course

{course.description}

))}
)} {/* Collections Section */} {searchResults.collections.length > 0 && (
Collections
{searchResults.collections.map((collection) => (

{collection.name}

Collection

{collection.description}

))}
)} {/* Users Section */} {searchResults.users.length > 0 && (
Users
{searchResults.users.map((user) => (

{user.first_name} {user.last_name}

User

@{user.username}

))}
)}
); }, [searchResults, orgslug, org?.org_uuid]); const handleSearchChange = useCallback((e: React.ChangeEvent) => { setSearchQuery(e.target.value); setShowResults(true); }, []); return (
setShowResults(true)} placeholder="Search courses, users, collections..." className="w-full h-9 pl-11 pr-4 rounded-xl nice-shadow bg-white focus:outline-none focus:ring-1 focus:ring-black/5 focus:border-black/20 text-sm placeholder:text-black/40 transition-all" />
{(!searchQuery.trim() || isInitialLoad) ? ( MemoizedEmptyState ) : ( <> {showSearchSuggestions && MemoizedSearchSuggestions} {isLoading ? ( ) : ( <> {MemoizedQuickResults} {((searchResults.courses.length > 0 || searchResults.collections.length > 0 || searchResults.users.length > 0) || searchQuery.trim()) && ( View all results )} )} )}
); };