3 Commits

4 changed files with 153 additions and 65 deletions

View File

@@ -94,6 +94,14 @@ const CardSearch = () => {
return card.image_uris?.normal || card.image_uris?.small || card.card_faces?.[0]?.image_uris?.normal; return card.image_uris?.normal || card.image_uris?.small || card.card_faces?.[0]?.image_uris?.normal;
}; };
// Get card art crop for current face
const getCardArtCrop = (card: Card, faceIndex: number = 0) => {
if (isDoubleFaced(card) && card.card_faces) {
return card.card_faces[faceIndex]?.image_uris?.art_crop || card.card_faces[faceIndex]?.image_uris?.normal;
}
return card.image_uris?.art_crop || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.art_crop;
};
// Add card to collection // Add card to collection
const handleAddCardToCollection = async (cardId: string) => { const handleAddCardToCollection = async (cardId: string) => {
if (!user) { if (!user) {
@@ -617,7 +625,75 @@ const CardSearch = () => {
)} )}
{searchResults && searchResults.length > 0 && ( {searchResults && searchResults.length > 0 && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4"> <>
{/* Mobile: Horizontal list layout */}
<div className="flex flex-col gap-2 sm:hidden">
{searchResults.map((card) => {
const currentFaceIndex = getCurrentFaceIndex(card.id);
const isMultiFaced = isDoubleFaced(card);
const inCollection = userCollection.get(card.id) || 0;
const isAddingThisCard = addingCardId === card.id;
const displayName = isMultiFaced && card.card_faces
? card.card_faces[currentFaceIndex]?.name || card.name
: card.name;
return (
<div key={card.id} className="flex bg-gray-800 rounded-lg overflow-hidden">
{/* Card art crop */}
<div className="relative w-16 h-16 flex-shrink-0">
<img
src={getCardArtCrop(card, currentFaceIndex)}
alt={displayName}
className="w-full h-full object-cover rounded-l-lg"
/>
{isMultiFaced && (
<button
onClick={(e) => {
e.stopPropagation();
toggleCardFace(card.id, card.card_faces!.length);
}}
className="absolute bottom-0.5 right-0.5 bg-purple-600 text-white p-0.5 rounded-full"
>
<RefreshCw size={10} />
</button>
)}
</div>
{/* Info */}
<div className="flex-1 p-2 flex flex-col justify-center min-w-0">
<h3 className="font-bold text-sm truncate">{displayName}</h3>
<div className="flex items-center gap-2 text-xs text-gray-400">
{card.prices?.usd && <span>${card.prices.usd}</span>}
{inCollection > 0 && (
<span className="text-green-400 flex items-center gap-0.5">
<CheckCircle size={10} />
x{inCollection}
</span>
)}
</div>
</div>
{/* Action button */}
<div className="flex items-center p-2">
<button
onClick={() => handleAddCardToCollection(card.id)}
disabled={isAddingThisCard}
className="p-2.5 bg-green-600 active:bg-green-700 disabled:bg-gray-600 rounded-lg"
title="Add to collection"
>
{isAddingThisCard ? (
<Loader2 className="animate-spin" size={18} />
) : (
<PackagePlus size={18} />
)}
</button>
</div>
</div>
);
})}
</div>
{/* Desktop: Grid layout */}
<div className="hidden sm:grid sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3">
{searchResults.map((card) => { {searchResults.map((card) => {
const currentFaceIndex = getCurrentFaceIndex(card.id); const currentFaceIndex = getCurrentFaceIndex(card.id);
const isMultiFaced = isDoubleFaced(card); const isMultiFaced = isDoubleFaced(card);
@@ -652,40 +728,35 @@ const CardSearch = () => {
<RefreshCw size={16} /> <RefreshCw size={16} />
</button> </button>
)} )}
</div>
<div className="p-4">
<div className="flex items-center justify-between mb-2">
<h3 className="font-bold">{displayName}</h3>
{inCollection > 0 && ( {inCollection > 0 && (
<span className="text-xs bg-green-600 px-2 py-0.5 rounded-full flex items-center gap-1"> <span className="absolute top-1 right-1 text-xs bg-green-600 px-2 py-0.5 rounded-full flex items-center gap-1">
<CheckCircle size={12} /> <CheckCircle size={12} />
x{inCollection} x{inCollection}
</span> </span>
)} )}
</div> </div>
<p className="text-gray-400 text-sm mb-3"> <div className="p-3">
<h3 className="font-bold text-sm truncate mb-1">{displayName}</h3>
<p className="text-gray-400 text-xs truncate mb-2">
{isMultiFaced && card.card_faces {isMultiFaced && card.card_faces
? card.card_faces[currentFaceIndex]?.type_line || card.type_line ? card.card_faces[currentFaceIndex]?.type_line || card.type_line
: card.type_line} : card.type_line}
</p> </p>
{card.prices?.usd && ( {card.prices?.usd && (
<div className="text-sm text-gray-400 mb-2">${card.prices.usd}</div> <div className="text-xs text-gray-400 mb-2">${card.prices.usd}</div>
)} )}
<button <button
onClick={() => handleAddCardToCollection(card.id)} onClick={() => handleAddCardToCollection(card.id)}
disabled={isAddingThisCard} disabled={isAddingThisCard}
className="w-full px-4 py-2 bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed rounded-lg flex items-center justify-center gap-2" className="w-full px-3 py-2 bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed rounded-lg flex items-center justify-center gap-2 text-sm"
title="Add to collection" title="Add to collection"
> >
{isAddingThisCard ? ( {isAddingThisCard ? (
<> <Loader2 className="animate-spin" size={16} />
<Loader2 className="animate-spin" size={20} />
Adding...
</>
) : ( ) : (
<> <>
<PackagePlus size={20} /> <PackagePlus size={16} />
Add to Collection Add
</> </>
)} )}
</button> </button>
@@ -694,6 +765,7 @@ const CardSearch = () => {
); );
})} })}
</div> </div>
</>
)} )}
</div> </div>

View File

@@ -228,7 +228,7 @@ export default function Collection() {
<p className="text-sm">Try a different search term</p> <p className="text-sm">Try a different search term</p>
</div> </div>
) : ( ) : (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-7 xl:grid-cols-9 gap-2 sm:gap-3"> <div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 gap-1.5 sm:gap-2">
{filteredCollection.map(({ card, quantity }) => { {filteredCollection.map(({ card, quantity }) => {
const currentFaceIndex = getCurrentFaceIndex(card.id); const currentFaceIndex = getCurrentFaceIndex(card.id);
const isMultiFaced = isDoubleFaced(card); const isMultiFaced = isDoubleFaced(card);

View File

@@ -465,6 +465,7 @@ export default function Community() {
onClose={() => setShowTradeCreator(false)} onClose={() => setShowTradeCreator(false)}
onTradeCreated={() => { onTradeCreated={() => {
setShowTradeCreator(false); setShowTradeCreator(false);
loadTradesData();
toast.success('Trade proposal sent!'); toast.success('Trade proposal sent!');
}} }}
/> />

View File

@@ -5,13 +5,28 @@ export interface User {
themeColor: 'red' | 'green' | 'blue' | 'yellow' | 'grey' | 'purple'; themeColor: 'red' | 'green' | 'blue' | 'yellow' | 'grey' | 'purple';
} }
export interface CardImageUris {
small?: string;
normal?: string;
large?: string;
art_crop?: string;
border_crop?: string;
png?: string;
}
export interface CardFace {
name?: string;
mana_cost?: string;
type_line?: string;
oracle_text?: string;
image_uris?: CardImageUris;
}
export interface Card { export interface Card {
id: string; id: string;
name: string; name: string;
image_uris?: { image_uris?: CardImageUris;
normal: string; card_faces?: CardFace[];
art_crop: string;
};
mana_cost?: string; mana_cost?: string;
type_line?: string; type_line?: string;
oracle_text?: string; oracle_text?: string;