diff --git a/src/app/search/SearchClient.tsx b/src/app/search/SearchClient.tsx new file mode 100644 index 0000000..98542a1 --- /dev/null +++ b/src/app/search/SearchClient.tsx @@ -0,0 +1,88 @@ +'use client'; + +import { useState, useEffect, FormEvent } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; +import { Movie, TVShow, moviesAPI, tvAPI } from '@/lib/api'; +import MovieCard from '@/components/MovieCard'; + +export default function SearchClient() { + const router = useRouter(); + const searchParams = useSearchParams(); + const [query, setQuery] = useState(searchParams.get('q') || ''); + const [results, setResults] = useState<(Movie | TVShow)[]>([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + const currentQuery = searchParams.get('q'); + if (currentQuery) { + setLoading(true); + Promise.all([ + moviesAPI.searchMovies(currentQuery), + tvAPI.searchShows(currentQuery), + ]) + .then(([movieResults, tvResults]) => { + const combined = [ + ...(movieResults.data.results || []), + ...(tvResults.data.results || []), + ]; + setResults(combined.sort((a, b) => b.vote_count - a.vote_count)); + }) + .catch((error) => { + console.error('Search failed:', error); + setResults([]); + }) + .finally(() => setLoading(false)); + } else { + setResults([]); + } + }, [searchParams]); + + const handleSearch = (e: FormEvent) => { + e.preventDefault(); + router.push(`/search?q=${encodeURIComponent(query)}`); + }; + + return ( +
+ {/* Search form */} +
+ setQuery(e.target.value)} + placeholder="Поиск фильмов и сериалов..." + className="flex-1 rounded-l-md border border-transparent bg-card px-4 py-2 focus:outline-none focus:ring-2 focus:ring-accent" + /> + +
+ + {searchParams.get('q') && ( +

+ Результаты поиска для: "{searchParams.get('q')}" +

+ )} + + {loading ? ( +
Загрузка...
+ ) : results.length > 0 ? ( +
+ {results.map((item) => ( + + ))} +
+ ) : ( + searchParams.get('q') && ( +
Ничего не найдено.
+ ) + )} +
+ ); +} \ No newline at end of file diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index 30a4938..3ea446e 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -1,64 +1,20 @@ 'use client'; -// Страница использует useSearchParams, поэтому отключаем статическую генерацию -export const dynamic = 'force-dynamic'; - -import { useState, useEffect, FormEvent } from 'react'; -import { useSearchParams, useRouter } from 'next/navigation'; -import { Movie, TVShow, moviesAPI, tvAPI } from '@/lib/api'; -import MovieCard from '@/components/MovieCard'; -import { Search } from 'lucide-react'; - -export default function SearchPage() { - const router = useRouter(); - const searchParams = useSearchParams(); - const [query, setQuery] = useState(searchParams.get('q') || ''); - const [results, setResults] = useState<(Movie | TVShow)[]>([]); - const [loading, setLoading] = useState(false); - - useEffect(() => { - const currentQuery = searchParams.get('q'); - if (currentQuery) { - setLoading(true); - Promise.all([ - moviesAPI.searchMovies(currentQuery), - tvAPI.searchShows(currentQuery) - ]).then(([movieResults, tvResults]) => { - const combined = [...(movieResults.data.results || []), ...(tvResults.data.results || [])]; - setResults(combined.sort((a, b) => b.vote_count - a.vote_count)); - }).catch(error => { - console.error('Search failed:', error); - setResults([]); - }).finally(() => setLoading(false)); - } else { - setResults([]); - } - }, [searchParams]); - - const handleSearch = (e: FormEvent) => { - e.preventDefault(); - router.push(`/search?q=${encodeURIComponent(query)}`); - }; +import { Suspense } from 'react'; +import SearchClient from './SearchClient'; +function LoadingSpinner() { return ( -
- {searchParams.get('q') && ( -

- Результаты поиска для: "{searchParams.get('q')}" -

- )} - - {loading ? ( -
Загрузка...
- ) : results.length > 0 ? ( -
- {results.map(item => ( - - ))} -
- ) : ( - searchParams.get('q') &&
Ничего не найдено.
- )} +
+
); +} + +export default function SearchPage() { + return ( + }> + + + ); } \ No newline at end of file