diff --git a/apps/web/src/pages/search-page.tsx b/apps/web/src/pages/search-page.tsx
new file mode 100644
index 0000000..ea9b250
--- /dev/null
+++ b/apps/web/src/pages/search-page.tsx
@@ -0,0 +1,165 @@
+import { useQuery } from '@tanstack/react-query'
+import { Search } from 'lucide-react'
+import { FormEvent, useState } from 'react'
+import { Link, useNavigate, useSearchParams } from 'react-router-dom'
+import { FavoriteToggle } from '@/components/favorite-toggle'
+import { coverArtUrl, fetchFavorites, searchLibrary } from '@/lib/api'
+import { usePlayerStore } from '@/stores/player-store'
+
+export function SearchPage() {
+ const navigate = useNavigate()
+ const [params] = useSearchParams()
+ const initialQuery = params.get('q') ?? ''
+ const [query, setQuery] = useState(initialQuery)
+ const setQueue = usePlayerStore((state) => state.setQueue)
+ const playTrack = usePlayerStore((state) => state.playTrack)
+
+ const searchQuery = useQuery({
+ queryKey: ['search-page', initialQuery],
+ queryFn: () => searchLibrary(initialQuery),
+ enabled: initialQuery.trim().length > 0,
+ })
+ const favoritesQuery = useQuery({
+ queryKey: ['favorites'],
+ queryFn: fetchFavorites,
+ })
+
+ const results = searchQuery.data
+ const favoriteTrackIds = new Set((favoritesQuery.data?.tracks ?? []).map((item) => item.id))
+ const favoriteAlbumIds = new Set((favoritesQuery.data?.albums ?? []).map((item) => item.id))
+ const favoriteArtistIds = new Set((favoritesQuery.data?.artists ?? []).map((item) => item.id))
+
+ function onSubmit(event: FormEvent) {
+ event.preventDefault()
+ navigate(`/search?q=${encodeURIComponent(query.trim())}`)
+ }
+
+ return (
+
+
+
+ {!initialQuery ? (
+
+ Введи название трека, альбома или исполнителя.
+
+ ) : null}
+
+ {results?.artists.length ? (
+
+ Исполнители
+
+ {results.artists.map((artist) => (
+
+
+
+ {artist.coverArtId ?
})
: null}
+
+
+
{artist.name}
+
{artist.albumCount} альбомов
+
+
+
+
+ ))}
+
+
+ ) : null}
+
+ {results?.albums.length ? (
+
+ Альбомы
+
+ {results.albums.map((album) => (
+
+
+ {album.coverArtId ?
: null}
+
+
+
+
+ {album.title}
+
+
{album.artistName}
+
+
+
+
+ ))}
+
+
+ ) : null}
+
+ {results?.tracks.length ? (
+
+ Треки
+
+
+
#
+
Название
+
Альбом
+
◷
+
♡
+
+ {results.tracks.map((track, index) => (
+
+ ))}
+
+
+ ) : null}
+
+ {initialQuery && !searchQuery.isLoading && !results?.artists.length && !results?.albums.length && !results?.tracks.length ? (
+
+ Ничего не найдено по запросу "{initialQuery}".
+
+ ) : null}
+
+ )
+}
+
+function formatDuration(durationSeconds: number) {
+ const minutes = Math.floor(durationSeconds / 60)
+ const seconds = durationSeconds % 60
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`
+}
diff --git a/apps/web/src/pages/tracks-page.tsx b/apps/web/src/pages/tracks-page.tsx
index 37ddd15..65b45cc 100644
--- a/apps/web/src/pages/tracks-page.tsx
+++ b/apps/web/src/pages/tracks-page.tsx
@@ -2,9 +2,11 @@ import { useQuery } from '@tanstack/react-query'
import { Search } from 'lucide-react'
import { FavoriteToggle } from '@/components/favorite-toggle'
import { coverArtUrl, fetchFavorites, fetchTracks } from '@/lib/api'
+import { useNavigate } from 'react-router-dom'
import { usePlayerStore } from '@/stores/player-store'
export function TracksPage() {
+ const navigate = useNavigate()
const setQueue = usePlayerStore((state) => state.setQueue)
const playTrack = usePlayerStore((state) => state.playTrack)
const tracksQuery = useQuery({
@@ -21,7 +23,7 @@ export function TracksPage() {
return (
-
+
navigate('/search')} />
#
@@ -74,10 +76,10 @@ export function TracksPage() {
)
}
-function HeaderSearch() {
+function HeaderSearch({ onClick }: { onClick: () => void }) {
return (
-