import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { Search, ArrowRight, Sparkles, Book, GraduationCap, ArrowUpRight, TextSearch, ScanSearch } from 'lucide-react';
import { searchOrgCourses } from '@services/courses/courses';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import Link from 'next/link';
import { getCourseThumbnailMediaDirectory } 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';
interface Course {
name: string;
description: string;
thumbnail_image: string;
course_uuid: string;
tags?: string[];
authors: Array<{
first_name: string;
last_name: string;
avatar_image: string;
}>;
}
interface SearchBarProps {
orgslug: string;
className?: string;
isMobile?: boolean;
}
const CourseResultsSkeleton = () => (
);
export const SearchBar: React.FC = ({
orgslug,
className = '',
isMobile = false,
}) => {
const org = useOrg() as any;
const [searchQuery, setSearchQuery] = useState('');
const [courses, setCourses] = useState([]);
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 fetchCourses = async () => {
if (debouncedSearch.trim().length === 0) {
setCourses([]);
setIsLoading(false);
return;
}
setIsLoading(true);
try {
const results = await searchOrgCourses(
orgslug,
debouncedSearch,
1,
3,
null,
session?.data?.tokens?.access_token
);
setCourses(results);
} catch (error) {
console.error('Error searching courses:', error);
setCourses([]);
}
setIsLoading(false);
setIsInitialLoad(false);
};
fetchCourses();
}, [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 MemoizedCourseResults = useMemo(() => {
if (!courses.length) return null;
return (
Quick Results
{courses.map((course) => (
{course.thumbnail_image ? (

) : (
)}
{course.name}
Course
{course.description}
))}
);
}, [courses, orgslug, org?.org_uuid]);
const handleSearchChange = useCallback((e: React.ChangeEvent) => {
setSearchQuery(e.target.value);
setShowResults(true);
}, []);
return (
{(!searchQuery.trim() || isInitialLoad) ? (
MemoizedEmptyState
) : (
<>
{MemoizedSearchSuggestions}
{isLoading ? (
) : (
<>
{MemoizedCourseResults}
{(courses.length > 0 || searchQuery.trim()) && (
View all results
)}
>
)}
>
)}
);
};