'use client'; import React, { useState, useEffect } from 'react'; import type { SearchIndex } from '../types/api'; import { API_PATHS } from '../types/api'; interface SearchResultItem { id: string; type: 'song' | 'album' | 'artist'; title: string; subtitle?: string; image?: string; duration?: number; year?: number; metadata?: Record; } interface SearchResultsProps { query: string; onResultClick?: (item: SearchResultItem) => void; onPlaySong?: (songId: string) => void; onAddToQueue?: (songId: string) => void; onAddToPlaylist?: (songId: string) => void; maxResults?: number; showFilters?: boolean; className?: string; } export default function SearchResults({ query, onResultClick, onPlaySong, onAddToQueue, onAddToPlaylist, maxResults = 50, showFilters = true, className = '', }: SearchResultsProps) { const [results, setResults] = useState<{ songs: SearchResultItem[]; albums: SearchResultItem[]; artists: SearchResultItem[]; }>({ songs: [], albums: [], artists: [], }); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState<'all' | 'songs' | 'albums' | 'artists'>('all'); const [sortBy, setSortBy] = useState<'relevance' | 'newest' | 'oldest' | 'name'>('relevance'); // Fetch search results useEffect(() => { if (!query.trim()) { setResults({ songs: [], albums: [], artists: [] }); return; } const fetchResults = async () => { setIsLoading(true); setError(null); try { const response = await fetch( `${API_PATHS.SEARCH}?q=${encodeURIComponent(query)}&limit=${maxResults}&sort=${sortBy}`, { headers: { 'Content-Type': 'application/json', }, } ); if (!response.ok) { throw new Error('Failed to fetch search results'); } const data = await response.json(); // Transform API response to our format // This would depend on the actual API response structure const transformedResults = { songs: (data.songs || []).map((item: any) => ({ id: item.id, type: 'song' as const, title: item.title, subtitle: item.artist, image: item.coverArt, duration: item.duration, year: item.year, metadata: item.metadata, })), albums: (data.albums || []).map((item: any) => ({ id: item.id, type: 'album' as const, title: item.title, subtitle: item.artist, image: item.coverArt, year: item.year, metadata: item.metadata, })), artists: (data.artists || []).map((item: any) => ({ id: item.id, type: 'artist' as const, title: item.name, subtitle: `${item.albumCount || 0} albums`, image: item.image, metadata: item.metadata, })), }; setResults(transformedResults); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } finally { setIsLoading(false); } }; fetchResults(); }, [query, maxResults, sortBy]); // Get filtered results based on active tab const getFilteredResults = () => { switch (activeTab) { case 'songs': return results.songs; case 'albums': return results.albums; case 'artists': return results.artists; default: return [...results.songs, ...results.albums, ...results.artists]; } }; // Format duration helper const formatDuration = (seconds?: number): string => { if (!seconds) return ''; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; }; // Handle result click const handleResultClick = (item: SearchResultItem) => { onResultClick?.(item); }; // Handle play song const handlePlaySong = (e: React.MouseEvent, songId: string) => { e.stopPropagation(); onPlaySong?.(songId); }; // Handle add to queue const handleAddToQueue = (e: React.MouseEvent, songId: string) => { e.stopPropagation(); onAddToQueue?.(songId); }; // Handle add to playlist const handleAddToPlaylist = (e: React.MouseEvent, songId: string) => { e.stopPropagation(); onAddToPlaylist?.(songId); }; // Render song item const renderSongItem = (item: SearchResultItem) => (
handleResultClick(item)} className="group flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors" > {/* Album art */}
{item.image ? ( {item.title} ) : (
)} {/* Play button overlay */}
{/* Song info */}

{item.title}

{item.subtitle}

{/* Duration */}
{formatDuration(item.duration)}
{/* Actions */}
); // Render album item const renderAlbumItem = (item: SearchResultItem) => (
handleResultClick(item)} className="group cursor-pointer" >
{item.image ? ( {item.title} ) : (
)} {/* Play button overlay */}

{item.title}

{item.subtitle}

{item.year && (

{item.year}

)}
); // Render artist item const renderArtistItem = (item: SearchResultItem) => (
handleResultClick(item)} className="group cursor-pointer text-center" >
{item.image ? ( {item.title} ) : (
)}

{item.title}

{item.subtitle}

); const filteredResults = getFilteredResults(); const allResultsCount = results.songs.length + results.albums.length + results.artists.length; if (!query.trim()) { return (

Search for music

Find your favorite songs, artists, and albums

); } return (
{/* Filters and controls */} {showFilters && (
{/* Tabs */}
{/* Sort */}

{isLoading ? 'Searching...' : `Found ${filteredResults.length} result${filteredResults.length !== 1 ? 's' : ''}`}

)} {/* Loading state */} {isLoading && (
)} {/* Error state */} {error && (

Error loading results

{error}

)} {/* Results */} {!isLoading && !error && filteredResults.length === 0 && (

No results found

Try adjusting your search or filters

)} {!isLoading && !error && filteredResults.length > 0 && (
{activeTab === 'all' && ( <> {/* Songs section */} {results.songs.length > 0 && (

Songs

{results.songs.slice(0, 5).map(renderSongItem)} {results.songs.length > 5 && ( )}
)} {/* Albums section */} {results.albums.length > 0 && (

Albums

{results.albums.slice(0, 5).map(renderAlbumItem)} {results.albums.length > 5 && (
)}
)} {/* Artists section */} {results.artists.length > 0 && (

Artists

{results.artists.slice(0, 6).map(renderArtistItem)} {results.artists.length > 6 && (
)}
)} )} {activeTab === 'songs' && (
{results.songs.map(renderSongItem)}
)} {activeTab === 'albums' && (
{results.albums.map(renderAlbumItem)}
)} {activeTab === 'artists' && (
{results.artists.map(renderArtistItem)}
)}
)}
); }