Fix CardDatabaseBrowser issues - prevent drawer closing, add event handling, and improve debugging

This commit is contained in:
Randall Stillwell 2025-07-22 20:31:36 -05:00
parent d9e7d856f0
commit 0c65919b02
2 changed files with 100 additions and 11 deletions

View file

@ -41,23 +41,36 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
setIsSearching(true); setIsSearching(true);
try { try {
const cards = await cardDataService.searchCards(debouncedSearch, selectedGame || undefined); const cards = await cardDataService.searchCards(debouncedSearch, selectedGame || undefined);
console.log(`Found ${cards.length} cards for search "${debouncedSearch}" in game "${selectedGame}"`);
return cards; return cards;
} catch (error) {
console.error('Error searching external cards:', error);
return [];
} finally { } finally {
setIsSearching(false); setIsSearching(false);
} }
}, },
enabled: !!debouncedSearch.trim() && isOpen, enabled: !!debouncedSearch.trim() && isOpen,
staleTime: 5 * 60 * 1000, // 5 minutes staleTime: 5 * 60 * 1000, // 5 minutes
retry: 2,
}); });
// Get random cards for discovery // Get random cards for discovery
const { data: randomCards = [] } = useQuery({ const { data: randomCards = [] } = useQuery({
queryKey: ['random-cards', selectedGame], queryKey: ['random-cards', selectedGame],
queryFn: async () => { queryFn: async () => {
return await cardDataService.getRandomCards(12, selectedGame || undefined); try {
const cards = await cardDataService.getRandomCards(20, selectedGame || undefined);
console.log(`Found ${cards.length} random cards for game "${selectedGame}"`);
return cards;
} catch (error) {
console.error('Error fetching random cards:', error);
return [];
}
}, },
enabled: isOpen && !debouncedSearch.trim(), enabled: isOpen && !debouncedSearch.trim(),
staleTime: 10 * 60 * 1000, // 10 minutes staleTime: 10 * 60 * 1000, // 10 minutes
retry: 2,
}); });
// Add card to database mutation // Add card to database mutation
@ -141,11 +154,26 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
const displayCards = debouncedSearch.trim() ? externalCards : randomCards; const displayCards = debouncedSearch.trim() ? externalCards : randomCards;
const isLoading = isSearchingExternal || isSearching; const isLoading = isSearchingExternal || isSearching;
// Debug logging
console.log('CardDatabaseBrowser state:', {
isOpen,
searchTerm,
debouncedSearch,
selectedGame,
externalCards: externalCards.length,
randomCards: randomCards.length,
displayCards: displayCards.length,
isLoading
});
return ( return (
<> <>
<div className="fixed inset-0 z-50 bg-black/50" onClick={onClose}> <div className="fixed inset-0 z-50 bg-black/50" onClick={onClose}>
<div className="fixed bottom-0 left-0 right-0 bg-white dark:bg-surface-900 rounded-t-3xl h-[90vh] overflow-hidden animate-slide-up"> <div
className="fixed bottom-0 left-0 right-0 bg-white dark:bg-surface-900 rounded-t-3xl h-[90vh] overflow-hidden animate-slide-up"
onClick={(e) => e.stopPropagation()}
>
<div className="p-4 pb-0"> <div className="p-4 pb-0">
<div className="w-12 h-1.5 bg-surface-300 dark:bg-surface-600 rounded-full mx-auto mb-4"></div> <div className="w-12 h-1.5 bg-surface-300 dark:bg-surface-600 rounded-full mx-auto mb-4"></div>
@ -155,7 +183,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
Card Database Browser Card Database Browser
</h2> </h2>
<button <button
onClick={onClose} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onClose();
}}
className="p-2 rounded-xl text-surface-400 hover:text-surface-600 dark:hover:text-surface-300 transition-colors" className="p-2 rounded-xl text-surface-400 hover:text-surface-600 dark:hover:text-surface-300 transition-colors"
> >
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -175,6 +208,8 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
type="text" type="text"
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
onKeyDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
placeholder="Search cards from external databases..." placeholder="Search cards from external databases..."
className="w-full pl-10 pr-4 py-3 bg-surface-50 dark:bg-surface-700 border border-surface-300 dark:border-surface-600 rounded-xl text-surface-900 dark:text-white placeholder-surface-500 dark:placeholder-surface-400 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors" className="w-full pl-10 pr-4 py-3 bg-surface-50 dark:bg-surface-700 border border-surface-300 dark:border-surface-600 rounded-xl text-surface-900 dark:text-white placeholder-surface-500 dark:placeholder-surface-400 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors"
/> />
@ -183,7 +218,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
{/* Game Filter */} {/* Game Filter */}
<div className="flex space-x-2 mb-4 overflow-x-auto pb-2"> <div className="flex space-x-2 mb-4 overflow-x-auto pb-2">
<button <button
onClick={() => setSelectedGame('')} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setSelectedGame('');
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${ className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
!selectedGame !selectedGame
? 'bg-primary-500 text-white' ? 'bg-primary-500 text-white'
@ -193,7 +233,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
All Games All Games
</button> </button>
<button <button
onClick={() => setSelectedGame('MTG')} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setSelectedGame('MTG');
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${ className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
selectedGame === 'MTG' selectedGame === 'MTG'
? 'bg-orange-500 text-white' ? 'bg-orange-500 text-white'
@ -203,7 +248,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
Magic: The Gathering Magic: The Gathering
</button> </button>
<button <button
onClick={() => setSelectedGame('POKEMON')} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setSelectedGame('POKEMON');
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${ className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
selectedGame === 'POKEMON' selectedGame === 'POKEMON'
? 'bg-yellow-500 text-white' ? 'bg-yellow-500 text-white'
@ -213,7 +263,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
Pokémon Pokémon
</button> </button>
<button <button
onClick={() => setSelectedGame('YUGIOH')} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setSelectedGame('YUGIOH');
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${ className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
selectedGame === 'YUGIOH' selectedGame === 'YUGIOH'
? 'bg-blue-500 text-white' ? 'bg-blue-500 text-white'
@ -223,7 +278,12 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
Yu-Gi-Oh! Yu-Gi-Oh!
</button> </button>
<button <button
onClick={() => setSelectedGame('LORCANA')} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setSelectedGame('LORCANA');
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${ className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors whitespace-nowrap ${
selectedGame === 'LORCANA' selectedGame === 'LORCANA'
? 'bg-purple-500 text-white' ? 'bg-purple-500 text-white'
@ -309,13 +369,23 @@ const CardDatabaseBrowser: React.FC<CardDatabaseBrowserProps> = ({
{/* Actions */} {/* Actions */}
<div className="flex flex-col space-y-2"> <div className="flex flex-col space-y-2">
<button <button
onClick={() => handleCardClick(card)} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleCardClick(card);
}}
className="px-3 py-1 bg-primary-500 hover:bg-primary-600 text-white text-xs font-medium rounded-lg transition-colors" className="px-3 py-1 bg-primary-500 hover:bg-primary-600 text-white text-xs font-medium rounded-lg transition-colors"
> >
Add to Collection Add to Collection
</button> </button>
<button <button
onClick={() => handleAddToDatabase(card)} type="button"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleAddToDatabase(card);
}}
disabled={addToDatabaseMutation.isPending} disabled={addToDatabaseMutation.isPending}
className="px-3 py-1 bg-surface-100 dark:bg-surface-700 hover:bg-surface-200 dark:hover:bg-surface-600 text-surface-700 dark:text-surface-300 text-xs font-medium rounded-lg transition-colors disabled:opacity-50" className="px-3 py-1 bg-surface-100 dark:bg-surface-700 hover:bg-surface-200 dark:hover:bg-surface-600 text-surface-700 dark:text-surface-300 text-xs font-medium rounded-lg transition-colors disabled:opacity-50"
> >

View file

@ -430,24 +430,34 @@ export const cardDataService = {
async searchCards(query: string, game?: string): Promise<Card[]> { async searchCards(query: string, game?: string): Promise<Card[]> {
const results: Card[] = []; const results: Card[] = [];
console.log(`🔍 Unified search: "${query}" for game: "${game}"`);
try { try {
if (!game || game === 'MTG') { if (!game || game === 'MTG') {
console.log('🔍 Searching MTG...');
const mtgCards = await mtgService.searchCards(query); const mtgCards = await mtgService.searchCards(query);
console.log(`✅ Found ${mtgCards.length} MTG cards`);
results.push(...mtgCards); results.push(...mtgCards);
} }
if (!game || game === 'POKEMON') { if (!game || game === 'POKEMON') {
console.log('🔍 Searching Pokémon...');
const pokemonCards = await pokemonService.searchCards(query); const pokemonCards = await pokemonService.searchCards(query);
console.log(`✅ Found ${pokemonCards.length} Pokémon cards`);
results.push(...pokemonCards); results.push(...pokemonCards);
} }
if (!game || game === 'YUGIOH') { if (!game || game === 'YUGIOH') {
console.log('🔍 Searching Yu-Gi-Oh!...');
const yugiohCards = await yugiohService.searchCards(query); const yugiohCards = await yugiohService.searchCards(query);
console.log(`✅ Found ${yugiohCards.length} Yu-Gi-Oh! cards`);
results.push(...yugiohCards); results.push(...yugiohCards);
} }
if (!game || game === 'LORCANA') { if (!game || game === 'LORCANA') {
console.log('🔍 Searching Lorcana...');
const lorcanaCards = await lorcanaService.searchCards(query); const lorcanaCards = await lorcanaService.searchCards(query);
console.log(`✅ Found ${lorcanaCards.length} Lorcana cards`);
results.push(...lorcanaCards); results.push(...lorcanaCards);
} }
@ -456,6 +466,7 @@ export const cardDataService = {
index === self.findIndex(c => c.name === card.name && c.game === card.game) index === self.findIndex(c => c.name === card.name && c.game === card.game)
); );
console.log(`🎯 Total results: ${results.length}, Unique: ${uniqueCards.length}`);
return uniqueCards; return uniqueCards;
} catch (error) { } catch (error) {
console.error('Error in unified card search:', error); console.error('Error in unified card search:', error);
@ -504,21 +515,29 @@ export const cardDataService = {
async getRandomCards(count = 20, game?: string): Promise<Card[]> { async getRandomCards(count = 20, game?: string): Promise<Card[]> {
const results: Card[] = []; const results: Card[] = [];
console.log(`🎲 Getting ${count} random cards for game: "${game}"`);
try { try {
if (!game || game === 'MTG') { if (!game || game === 'MTG') {
console.log('🎲 Getting random MTG cards...');
const mtgCards = await mtgService.getRandomCards(Math.ceil(count / 4)); const mtgCards = await mtgService.getRandomCards(Math.ceil(count / 4));
console.log(`✅ Found ${mtgCards.length} random MTG cards`);
results.push(...mtgCards); results.push(...mtgCards);
} }
if (!game || game === 'LORCANA') { if (!game || game === 'LORCANA') {
console.log('🎲 Getting random Lorcana cards...');
const lorcanaCards = await lorcanaService.getRandomCards(Math.ceil(count / 4)); const lorcanaCards = await lorcanaService.getRandomCards(Math.ceil(count / 4));
console.log(`✅ Found ${lorcanaCards.length} random Lorcana cards`);
results.push(...lorcanaCards); results.push(...lorcanaCards);
} }
// Note: Pokémon and Yu-Gi-Oh! APIs don't have random card endpoints // Note: Pokémon and Yu-Gi-Oh! APIs don't have random card endpoints
// You could implement random selection from popular cards lists // You could implement random selection from popular cards lists
return results.slice(0, count); const finalResults = results.slice(0, count);
console.log(`🎯 Returning ${finalResults.length} random cards`);
return finalResults;
} catch (error) { } catch (error) {
console.error('Error getting random cards:', error); console.error('Error getting random cards:', error);
return []; return [];