fix: Corriger fonctionnalité de recherche globale
- Supprimer appel automatique de onSearch à chaque changement de query - onSearch est maintenant appelé seulement lors de la soumission (Enter ou clic) - Gérer feature flag PLAYLIST_SEARCH désactivé pour éviter erreurs - Améliorer gestion des erreurs dans fetchSuggestions - Corriger style du composant Search dans le Header - Supprimer icône Search dupliquée dans Header
This commit is contained in:
parent
0adcd2cefb
commit
106e80ec6d
3 changed files with 35 additions and 24 deletions
|
|
@ -118,10 +118,8 @@ export function Header(_props: HeaderProps) {
|
|||
|
||||
{/* Global Search integrated */}
|
||||
<div className="flex-1 max-w-lg mx-8 relative hidden md:block">
|
||||
<GlobalSearchBar className="w-full bg-transparent border-none" />
|
||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 flex items-center gap-2 pointer-events-none opacity-50 group-hover:opacity-100 transition-opacity">
|
||||
<Search className="w-4 h-4 text-kodo-steel" />
|
||||
</div>
|
||||
{/* FIX: L'icône Search est déjà dans le composant Search, pas besoin de la dupliquer */}
|
||||
<GlobalSearchBar className="w-full" />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { playlistsApi } from '@/services/api/playlists';
|
|||
import { searchUsers } from '@/features/search/services/searchService';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { isFeatureEnabled } from '@/config/features';
|
||||
|
||||
/**
|
||||
* FE-COMP-008: Global search bar component with autocomplete
|
||||
|
|
@ -38,31 +39,45 @@ export function GlobalSearchBar({
|
|||
|
||||
try {
|
||||
// Fetch suggestions from all sources in parallel
|
||||
const [tracksData, playlistsData, usersData] = await Promise.allSettled(
|
||||
[
|
||||
searchTracks(query, { pagination: { page: 1, limit: 3 } }),
|
||||
// FIX: Gérer les feature flags (PLAYLIST_SEARCH peut être désactivé)
|
||||
const searchPromises: Promise<any>[] = [
|
||||
searchTracks(query, { pagination: { page: 1, limit: 3 } }),
|
||||
searchUsers({ query, page: 1, limit: 3 }),
|
||||
];
|
||||
|
||||
// Ajouter la recherche de playlists seulement si la feature est activée
|
||||
if (isFeatureEnabled('PLAYLIST_SEARCH')) {
|
||||
searchPromises.push(
|
||||
playlistsApi.search({ q: query, page: 1, limit: 3 }),
|
||||
searchUsers({ query, page: 1, limit: 3 }),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
// Si désactivé, retourner une promesse résolue avec un résultat vide
|
||||
searchPromises.push(Promise.resolve({ playlists: [], total: 0 }));
|
||||
}
|
||||
|
||||
const [tracksData, usersData, playlistsData] = await Promise.allSettled(
|
||||
searchPromises,
|
||||
);
|
||||
|
||||
const results: SearchResult[] = [];
|
||||
|
||||
// Add track suggestions
|
||||
// FIX: searchTracks retourne PaginatedResponse<Track> avec propriété 'data'
|
||||
if (tracksData.status === 'fulfilled' && tracksData.value?.data) {
|
||||
tracksData.value.data.forEach((track: any) => {
|
||||
results.push({
|
||||
id: track.id,
|
||||
type: 'track',
|
||||
title: track.title,
|
||||
subtitle: track.artist ? `by ${track.artist}` : undefined,
|
||||
image: track.cover_url,
|
||||
title: track.title || track.name || 'Untitled',
|
||||
subtitle: track.artist || track.artist_name || undefined,
|
||||
image: track.cover_url || track.cover || track.thumbnail_url,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Add playlist suggestions
|
||||
// Add playlist suggestions (seulement si la feature est activée)
|
||||
if (
|
||||
isFeatureEnabled('PLAYLIST_SEARCH') &&
|
||||
playlistsData.status === 'fulfilled' &&
|
||||
playlistsData.value?.playlists
|
||||
) {
|
||||
|
|
@ -70,9 +85,9 @@ export function GlobalSearchBar({
|
|||
results.push({
|
||||
id: playlist.id,
|
||||
type: 'playlist',
|
||||
title: playlist.title,
|
||||
title: playlist.title || 'Untitled Playlist',
|
||||
subtitle: playlist.is_public ? 'Public' : 'Private',
|
||||
image: playlist.cover_url,
|
||||
image: playlist.cover_url || playlist.thumbnail_url,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -102,7 +117,7 @@ export function GlobalSearchBar({
|
|||
[],
|
||||
);
|
||||
|
||||
// Handle search action
|
||||
// Handle search action - navigation vers la page de recherche
|
||||
const handleSearch = useCallback(
|
||||
(query: string) => {
|
||||
if (query.trim()) {
|
||||
|
|
|
|||
|
|
@ -95,12 +95,10 @@ export function Search({
|
|||
loadSuggestions();
|
||||
}, [debouncedQuery, fetchSuggestions, showSuggestions]);
|
||||
|
||||
// Appeler onSearch quand la query débounced change
|
||||
useEffect(() => {
|
||||
if (debouncedQuery.trim()) {
|
||||
onSearch(debouncedQuery);
|
||||
}
|
||||
}, [debouncedQuery, onSearch]);
|
||||
// FIX: Ne pas appeler onSearch automatiquement à chaque changement de query
|
||||
// onSearch sera appelé seulement quand l'utilisateur soumet la recherche (Enter ou clic)
|
||||
// Cela évite de naviguer vers /search à chaque frappe
|
||||
// L'effet précédent qui appelait onSearch à chaque debouncedQuery a été supprimé
|
||||
|
||||
// Gérer le clic en dehors pour fermer le dropdown
|
||||
useEffect(() => {
|
||||
|
|
@ -273,7 +271,7 @@ export function Search({
|
|||
return (
|
||||
<div ref={searchRef} className={cn('relative w-full', className)}>
|
||||
<div className="relative">
|
||||
<SearchIcon className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<SearchIcon className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground pointer-events-none z-10" />
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
|
|
@ -283,7 +281,7 @@ export function Search({
|
|||
onKeyDown={handleInputKeyDown}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-input bg-background px-8 py-2 text-sm',
|
||||
'w-full rounded-md border border-input bg-background pl-10 pr-10 py-2 text-sm',
|
||||
'placeholder:text-muted-foreground',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
|
|
|
|||
Loading…
Reference in a new issue