Compare commits
3 Commits
bab6367181
...
304676a06b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
304676a06b | ||
|
|
a7681357b5 | ||
|
|
a0c5143103 |
@@ -94,6 +94,14 @@ const CardSearch = () => {
|
||||
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
|
||||
const handleAddCardToCollection = async (cardId: string) => {
|
||||
if (!user) {
|
||||
@@ -617,83 +625,147 @@ const CardSearch = () => {
|
||||
)}
|
||||
|
||||
{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">
|
||||
{searchResults.map((card) => {
|
||||
const currentFaceIndex = getCurrentFaceIndex(card.id);
|
||||
const isMultiFaced = isDoubleFaced(card);
|
||||
const inCollection = userCollection.get(card.id) || 0;
|
||||
const isAddingThisCard = addingCardId === card.id;
|
||||
<>
|
||||
{/* 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;
|
||||
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) ? (
|
||||
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={getCardImageUri(card, currentFaceIndex)}
|
||||
src={getCardArtCrop(card, currentFaceIndex)}
|
||||
alt={displayName}
|
||||
className="w-full h-auto"
|
||||
className="w-full h-full object-cover rounded-l-lg"
|
||||
/>
|
||||
) : (
|
||||
<MagicCard card={card} />
|
||||
)}
|
||||
{isMultiFaced && (
|
||||
{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={(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"
|
||||
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"
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
{isAddingThisCard ? (
|
||||
<Loader2 className="animate-spin" size={18} />
|
||||
) : (
|
||||
<PackagePlus size={18} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="font-bold">{displayName}</h3>
|
||||
);
|
||||
})}
|
||||
</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="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} />
|
||||
x{inCollection}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm mb-3">
|
||||
{isMultiFaced && card.card_faces
|
||||
? card.card_faces[currentFaceIndex]?.type_line || card.type_line
|
||||
: card.type_line}
|
||||
</p>
|
||||
{card.prices?.usd && (
|
||||
<div className="text-sm text-gray-400 mb-2">${card.prices.usd}</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleAddCardToCollection(card.id)}
|
||||
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"
|
||||
title="Add to collection"
|
||||
>
|
||||
{isAddingThisCard ? (
|
||||
<>
|
||||
<Loader2 className="animate-spin" size={20} />
|
||||
Adding...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PackagePlus size={20} />
|
||||
Add to Collection
|
||||
</>
|
||||
<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>
|
||||
<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>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ export default function Collection() {
|
||||
<p className="text-sm">Try a different search term</p>
|
||||
</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 }) => {
|
||||
const currentFaceIndex = getCurrentFaceIndex(card.id);
|
||||
const isMultiFaced = isDoubleFaced(card);
|
||||
|
||||
@@ -465,6 +465,7 @@ export default function Community() {
|
||||
onClose={() => setShowTradeCreator(false)}
|
||||
onTradeCreated={() => {
|
||||
setShowTradeCreator(false);
|
||||
loadTradesData();
|
||||
toast.success('Trade proposal sent!');
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -5,13 +5,28 @@ export interface User {
|
||||
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 {
|
||||
id: string;
|
||||
name: string;
|
||||
image_uris?: {
|
||||
normal: string;
|
||||
art_crop: string;
|
||||
};
|
||||
image_uris?: CardImageUris;
|
||||
card_faces?: CardFace[];
|
||||
mana_cost?: string;
|
||||
type_line?: string;
|
||||
oracle_text?: string;
|
||||
|
||||
Reference in New Issue
Block a user