[ISSUE-10] Add card collection integration to deck manager
Implemented comprehensive collection management features: Backend: - Created collectionService with 9 API functions - Added useCollection React hook for state management - Implemented batch processing for performance - Added full authentication and authorization Frontend: - Enhanced DeckManager with collection status indicators - Added "Add All Missing Cards" bulk operation button - Added individual "Add Card" buttons for missing cards - Implemented loading states and error handling - Added responsive design with visual badges Features: - Visual indicators (yellow for missing, green for owned) - Bulk add all missing cards functionality - Individual card addition with quantity tracking - Real-time collection synchronization - Success/error notifications Tests: Build passing (5.98s), linting passing, TypeScript passing Resolves: #ISSUE-10 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
233
src/hooks/useCollection.ts
Normal file
233
src/hooks/useCollection.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import {
|
||||
getUserCollection,
|
||||
getCardInCollection,
|
||||
checkCardsOwnership,
|
||||
getDeckCardOwnership,
|
||||
getMissingCardsFromDeck,
|
||||
addCardToCollection,
|
||||
addCardsToCollectionBulk,
|
||||
addMissingDeckCardsToCollection,
|
||||
removeCardFromCollection,
|
||||
CollectionCard,
|
||||
CardOwnershipInfo,
|
||||
MissingCardInfo,
|
||||
} from '../services/collectionService';
|
||||
|
||||
/**
|
||||
* Custom React hook for managing card collections
|
||||
* Provides state management and loading/error handling for collection operations
|
||||
*/
|
||||
export const useCollection = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
/**
|
||||
* Clear any existing error
|
||||
*/
|
||||
const clearError = useCallback(() => {
|
||||
setError(null);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Get user's entire collection
|
||||
*/
|
||||
const getCollection = useCallback(async (): Promise<CollectionCard[] | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const collection = await getUserCollection();
|
||||
return collection;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch collection';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Check if a single card is in the collection
|
||||
*/
|
||||
const checkCardOwnership = useCallback(async (cardId: string): Promise<CollectionCard | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const card = await getCardInCollection(cardId);
|
||||
return card;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to check card ownership';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Check ownership for multiple cards
|
||||
*/
|
||||
const checkMultipleCardsOwnership = useCallback(
|
||||
async (cardIds: string[]): Promise<Map<string, number> | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const ownershipMap = await checkCardsOwnership(cardIds);
|
||||
return ownershipMap;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to check cards ownership';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Get detailed ownership info for all cards in a deck
|
||||
*/
|
||||
const getDeckOwnership = useCallback(
|
||||
async (deckId: string): Promise<CardOwnershipInfo[] | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const ownershipInfo = await getDeckCardOwnership(deckId);
|
||||
return ownershipInfo;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to get deck ownership info';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Get list of missing cards from a deck
|
||||
*/
|
||||
const getMissingCards = useCallback(
|
||||
async (deckId: string): Promise<MissingCardInfo[] | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const missingCards = await getMissingCardsFromDeck(deckId);
|
||||
return missingCards;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to get missing cards';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Add a single card to collection
|
||||
*/
|
||||
const addCard = useCallback(
|
||||
async (cardId: string, quantity: number = 1): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await addCardToCollection(cardId, quantity);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to add card to collection';
|
||||
setError(errorMessage);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Add multiple cards to collection in bulk
|
||||
*/
|
||||
const addCardsBulk = useCallback(
|
||||
async (
|
||||
cards: Array<{ card_id: string; quantity: number }>
|
||||
): Promise<Array<{ card_id: string; success: boolean; error?: string }> | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const results = await addCardsToCollectionBulk(cards);
|
||||
return results;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to add cards to collection';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Add all missing cards from a deck to collection
|
||||
*/
|
||||
const addMissingDeckCards = useCallback(
|
||||
async (
|
||||
deckId: string
|
||||
): Promise<Array<{ card_id: string; success: boolean; error?: string }> | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const results = await addMissingDeckCardsToCollection(deckId);
|
||||
return results;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to add missing cards';
|
||||
setError(errorMessage);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove a card from collection
|
||||
*/
|
||||
const removeCard = useCallback(
|
||||
async (cardId: string, quantity?: number): Promise<boolean> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await removeCardFromCollection(cardId, quantity);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to remove card from collection';
|
||||
setError(errorMessage);
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
clearError,
|
||||
getCollection,
|
||||
checkCardOwnership,
|
||||
checkMultipleCardsOwnership,
|
||||
getDeckOwnership,
|
||||
getMissingCards,
|
||||
addCard,
|
||||
addCardsBulk,
|
||||
addMissingDeckCards,
|
||||
removeCard,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user