Refactor CardSearch component for improved mobile and desktop layouts
This commit is contained in:
@@ -617,79 +617,147 @@ const CardSearch = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{searchResults && searchResults.length > 0 && (
|
{searchResults && searchResults.length > 0 && (
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-2 sm:gap-3">
|
<>
|
||||||
{searchResults.map((card) => {
|
{/* Mobile: Horizontal list layout */}
|
||||||
const currentFaceIndex = getCurrentFaceIndex(card.id);
|
<div className="flex flex-col gap-2 sm:hidden">
|
||||||
const isMultiFaced = isDoubleFaced(card);
|
{searchResults.map((card) => {
|
||||||
const inCollection = userCollection.get(card.id) || 0;
|
const currentFaceIndex = getCurrentFaceIndex(card.id);
|
||||||
const isAddingThisCard = addingCardId === card.id;
|
const isMultiFaced = isDoubleFaced(card);
|
||||||
|
const inCollection = userCollection.get(card.id) || 0;
|
||||||
|
const isAddingThisCard = addingCardId === card.id;
|
||||||
|
|
||||||
const displayName = isMultiFaced && card.card_faces
|
const displayName = isMultiFaced && card.card_faces
|
||||||
? card.card_faces[currentFaceIndex]?.name || card.name
|
? card.card_faces[currentFaceIndex]?.name || card.name
|
||||||
: card.name;
|
: card.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={card.id} className="bg-gray-800 rounded-lg overflow-hidden hover:ring-2 hover:ring-blue-500 transition-all">
|
<div key={card.id} className="flex bg-gray-800 rounded-lg overflow-hidden">
|
||||||
<div className="relative">
|
{/* Card image */}
|
||||||
{getCardImageUri(card, currentFaceIndex) ? (
|
<div className="relative w-16 flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
src={getCardImageUri(card, currentFaceIndex)}
|
src={getCardImageUri(card, currentFaceIndex)}
|
||||||
alt={displayName}
|
alt={displayName}
|
||||||
className="w-full h-auto"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
{isMultiFaced && (
|
||||||
<MagicCard card={card} />
|
<button
|
||||||
)}
|
onClick={(e) => {
|
||||||
{isMultiFaced && (
|
e.stopPropagation();
|
||||||
<button
|
toggleCardFace(card.id, card.card_faces!.length);
|
||||||
onClick={(e) => {
|
}}
|
||||||
e.stopPropagation();
|
className="absolute bottom-0.5 right-0.5 bg-purple-600 text-white p-0.5 rounded-full"
|
||||||
toggleCardFace(card.id, card.card_faces!.length);
|
>
|
||||||
}}
|
<RefreshCw size={10} />
|
||||||
className="absolute bottom-1 right-1 bg-purple-600 hover:bg-purple-700 text-white p-1.5 sm:p-2 rounded-full shadow-lg transition-all"
|
</button>
|
||||||
title="Flip card"
|
|
||||||
>
|
|
||||||
<RefreshCw size={12} className="sm:w-4 sm:h-4" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{inCollection > 0 && (
|
|
||||||
<span className="absolute top-1 right-1 text-[10px] sm:text-xs bg-green-600 px-1.5 py-0.5 rounded-full flex items-center gap-0.5">
|
|
||||||
<CheckCircle size={10} className="hidden sm:block" />
|
|
||||||
x{inCollection}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="p-2 sm:p-3">
|
|
||||||
<h3 className="font-bold text-xs sm:text-sm truncate mb-1">{displayName}</h3>
|
|
||||||
<p className="text-gray-400 text-[10px] sm:text-xs truncate mb-2 hidden sm:block">
|
|
||||||
{isMultiFaced && card.card_faces
|
|
||||||
? card.card_faces[currentFaceIndex]?.type_line || card.type_line
|
|
||||||
: card.type_line}
|
|
||||||
</p>
|
|
||||||
{card.prices?.usd && (
|
|
||||||
<div className="text-[10px] sm:text-xs text-gray-400 mb-1.5">${card.prices.usd}</div>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => handleAddCardToCollection(card.id)}
|
|
||||||
disabled={isAddingThisCard}
|
|
||||||
className="w-full px-2 py-1.5 sm:px-3 sm:py-2 bg-green-600 hover:bg-green-700 active:bg-green-800 disabled:bg-gray-600 disabled:cursor-not-allowed rounded-lg flex items-center justify-center gap-1 text-xs sm:text-sm"
|
|
||||||
title="Add to collection"
|
|
||||||
>
|
|
||||||
{isAddingThisCard ? (
|
|
||||||
<Loader2 className="animate-spin" size={14} />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<PackagePlus size={14} className="sm:w-4 sm:h-4" />
|
|
||||||
<span className="hidden sm:inline">Add</span>
|
|
||||||
<span className="sm:hidden">+</span>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</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>
|
||||||
</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) => {
|
||||||
|
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="bg-gray-800 rounded-lg overflow-hidden hover:ring-2 hover:ring-blue-500 transition-all">
|
||||||
|
<div className="relative">
|
||||||
|
{getCardImageUri(card, currentFaceIndex) ? (
|
||||||
|
<img
|
||||||
|
src={getCardImageUri(card, currentFaceIndex)}
|
||||||
|
alt={displayName}
|
||||||
|
className="w-full h-auto"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MagicCard card={card} />
|
||||||
|
)}
|
||||||
|
{isMultiFaced && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleCardFace(card.id, card.card_faces!.length);
|
||||||
|
}}
|
||||||
|
className="absolute bottom-2 right-2 bg-purple-600 hover:bg-purple-700 text-white p-2 rounded-full shadow-lg transition-all"
|
||||||
|
title="Flip card"
|
||||||
|
>
|
||||||
|
<RefreshCw size={16} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{inCollection > 0 && (
|
||||||
|
<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} />
|
||||||
|
x{inCollection}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<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
|
||||||
|
? card.card_faces[currentFaceIndex]?.type_line || card.type_line
|
||||||
|
: card.type_line}
|
||||||
|
</p>
|
||||||
|
{card.prices?.usd && (
|
||||||
|
<div className="text-xs text-gray-400 mb-2">${card.prices.usd}</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => handleAddCardToCollection(card.id)}
|
||||||
|
disabled={isAddingThisCard}
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
{isAddingThisCard ? (
|
||||||
|
<Loader2 className="animate-spin" size={16} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PackagePlus size={16} />
|
||||||
|
Add
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user