'use client'; import Link from 'next/link'; import Image from 'next/image'; import { useState, useEffect, useRef } from 'react'; import { useRouter } from 'next/navigation'; import { fetchSearchSuggestions, fetchMostPopular, fetchTopAiring, fetchRecentEpisodes, fetchMostFavorite, fetchTopUpcoming } from '@/lib/api'; export default function Navbar() { const [isMenuOpen, setIsMenuOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [searchSuggestions, setSearchSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const [isScrolled, setIsScrolled] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isRandomLoading, setIsRandomLoading] = useState(false); const suggestionRef = useRef(null); const searchInputRef = useRef(null); const router = useRouter(); // Track scroll position useEffect(() => { const handleScroll = () => { if (window.scrollY > 10) { setIsScrolled(true); } else { setIsScrolled(false); } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); // Update suggestions when search query changes useEffect(() => { const updateSuggestions = async () => { // Only search if we have at least 2 characters if (searchQuery.trim().length >= 2) { setIsLoading(true); setShowSuggestions(true); // Always show the suggestions container when typing try { console.log(`Fetching suggestions for: ${searchQuery}`); const apiSuggestions = await fetchSearchSuggestions(searchQuery); console.log('API returned:', apiSuggestions); if (Array.isArray(apiSuggestions) && apiSuggestions.length > 0) { // Take top 5 results setSearchSuggestions(apiSuggestions.slice(0, 5)); } else { // Create a generic suggestion based on the search query setSearchSuggestions([{ id: searchQuery.toLowerCase().replace(/\s+/g, '-'), title: `Search for "${searchQuery}"`, type: "SEARCH", image: null }]); } } catch (error) { console.error('Error in search component:', error); // Create a generic suggestion setSearchSuggestions([{ id: searchQuery.toLowerCase().replace(/\s+/g, '-'), title: `Search for "${searchQuery}"`, type: "SEARCH", image: null }]); } finally { setIsLoading(false); } } else { setSearchSuggestions([]); setShowSuggestions(false); } }; const debounceTimer = setTimeout(() => { updateSuggestions(); }, 300); // 300ms debounce time return () => clearTimeout(debounceTimer); }, [searchQuery]); // Close suggestions when clicking outside useEffect(() => { const handleClickOutside = (event) => { if ( suggestionRef.current && !suggestionRef.current.contains(event.target) && !searchInputRef.current?.contains(event.target) ) { setShowSuggestions(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); const handleSearch = (e) => { e.preventDefault(); // Navigate to search page regardless if search is empty or not router.push(searchQuery.trim() ? `/search?q=${encodeURIComponent(searchQuery)}` : '/search'); setSearchQuery(''); setShowSuggestions(false); setIsMenuOpen(false); }; // Handle suggestion item click const handleAnimeClick = (id) => { router.push(`/anime/${id}`); setSearchQuery(''); setShowSuggestions(false); setIsMenuOpen(false); }; // Handle search by query click const handleSearchByQueryClick = () => { router.push(`/search?q=${encodeURIComponent(searchQuery)}`); setSearchQuery(''); setShowSuggestions(false); setIsMenuOpen(false); }; // Helper function to render clear button const renderClearButton = () => { if (searchQuery) { return ( ); } return null; }; // Function to handle input focus const handleInputFocus = () => { if (searchQuery.trim().length >= 2) { setShowSuggestions(true); } }; // Function to handle random anime click const handleRandomAnimeClick = async () => { setIsRandomLoading(true); try { // Randomly select a category to fetch from const categories = [ { name: 'Most Popular', fetch: fetchMostPopular }, { name: 'Top Airing', fetch: fetchTopAiring }, { name: 'Recent Episodes', fetch: fetchRecentEpisodes }, { name: 'Most Favorite', fetch: fetchMostFavorite }, { name: 'Top Upcoming', fetch: fetchTopUpcoming } ]; // Select a random category const randomCategoryIndex = Math.floor(Math.random() * categories.length); const selectedCategory = categories[randomCategoryIndex]; console.log(`Fetching random anime from: ${selectedCategory.name}`); // Fetch anime from the selected category - use a random page number to get more variety const randomPage = Math.floor(Math.random() * 5) + 1; // Random page between 1-5 const animeList = await selectedCategory.fetch(randomPage); if (animeList && animeList.results && animeList.results.length > 0) { // Skip the first few results as they tend to be more popular const skipCount = Math.min(5, Math.floor(animeList.results.length / 3)); let availableAnime = animeList.results.slice(skipCount); if (availableAnime.length === 0) { // If we've filtered out everything, use the original list availableAnime = animeList.results; } // Get a random index const randomAnimeIndex = Math.floor(Math.random() * availableAnime.length); // Get the random anime ID const randomAnimeId = availableAnime[randomAnimeIndex].id; console.log(`Selected random anime: ${availableAnime[randomAnimeIndex].title} (ID: ${randomAnimeId})`); // Navigate to the anime page router.push(`/anime/${randomAnimeId}`); } else { console.error('No anime found to select randomly from'); // Fallback to most popular if the chosen category fails, but use a higher page number const fallbackPage = Math.floor(Math.random() * 5) + 2; // Pages 2-6 for more obscure options const fallbackList = await fetchMostPopular(fallbackPage); if (fallbackList && fallbackList.results && fallbackList.results.length > 0) { const randomIndex = Math.floor(Math.random() * fallbackList.results.length); const randomAnimeId = fallbackList.results[randomIndex].id; router.push(`/anime/${randomAnimeId}`); } } } catch (error) { console.error('Error fetching random anime:', error); } finally { setIsRandomLoading(false); } }; return ( ); }