2025-07-26 00:22:15 -04:00
|
|
|
import { useState } from 'react';
|
2025-07-26 01:12:54 -04:00
|
|
|
import { useRouter } from 'next/router';
|
2025-07-26 00:22:15 -04:00
|
|
|
|
|
|
|
|
export default function CardItem({
|
|
|
|
|
card,
|
|
|
|
|
viewMode = 'grid',
|
|
|
|
|
isSelected = false,
|
|
|
|
|
onToggleSelect,
|
|
|
|
|
onAddToCollection,
|
|
|
|
|
onAddToDeck,
|
|
|
|
|
onToggleFavorite,
|
|
|
|
|
isFavorited = false
|
|
|
|
|
}) {
|
|
|
|
|
const [imageError, setImageError] = useState(false);
|
2025-07-26 01:12:54 -04:00
|
|
|
const router = useRouter();
|
2025-07-26 00:22:15 -04:00
|
|
|
|
|
|
|
|
const handleSelectChange = (e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onToggleSelect(card);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleImageError = () => {
|
|
|
|
|
setImageError(true);
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-26 01:12:54 -04:00
|
|
|
const handleCardClick = (e) => {
|
|
|
|
|
// Don't navigate if clicking on interactive elements
|
|
|
|
|
if (e.target.closest('input, button, a')) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
router.push(`/card/${card.id}`);
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-26 01:01:04 -04:00
|
|
|
// Get rarity-based styling
|
|
|
|
|
const getRarityEffects = (rarity) => {
|
|
|
|
|
const rarityLower = rarity?.toLowerCase() || '';
|
|
|
|
|
|
|
|
|
|
if (rarityLower.includes('mythic') || rarityLower.includes('legendary')) {
|
|
|
|
|
return {
|
|
|
|
|
glow: 'rarity-glow-mythic',
|
|
|
|
|
particles: 'rarity-particles-mythic',
|
|
|
|
|
border: 'border-yellow-400',
|
|
|
|
|
shadow: 'shadow-yellow-400/50'
|
|
|
|
|
};
|
|
|
|
|
} else if (rarityLower.includes('rare') || rarityLower.includes('holo')) {
|
|
|
|
|
return {
|
|
|
|
|
glow: 'rarity-glow-rare',
|
|
|
|
|
particles: 'rarity-particles-rare',
|
|
|
|
|
border: 'border-purple-400',
|
|
|
|
|
shadow: 'shadow-purple-400/50'
|
|
|
|
|
};
|
|
|
|
|
} else if (rarityLower.includes('uncommon')) {
|
|
|
|
|
return {
|
|
|
|
|
glow: 'rarity-glow-uncommon',
|
|
|
|
|
particles: 'rarity-particles-uncommon',
|
|
|
|
|
border: 'border-blue-400',
|
|
|
|
|
shadow: 'shadow-blue-400/30'
|
|
|
|
|
};
|
|
|
|
|
} else if (rarityLower.includes('enchanted') || rarityLower.includes('secret')) {
|
|
|
|
|
return {
|
|
|
|
|
glow: 'rarity-glow-enchanted',
|
|
|
|
|
particles: 'rarity-particles-enchanted',
|
|
|
|
|
border: 'border-pink-400',
|
|
|
|
|
shadow: 'shadow-pink-400/60'
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
glow: '',
|
|
|
|
|
particles: '',
|
|
|
|
|
border: 'border-gray-200',
|
|
|
|
|
shadow: ''
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const rarityEffects = getRarityEffects(card.rarity);
|
|
|
|
|
|
2025-07-26 00:22:15 -04:00
|
|
|
if (viewMode === 'list') {
|
|
|
|
|
return (
|
2025-07-26 01:12:54 -04:00
|
|
|
<div
|
|
|
|
|
className={`flex items-center p-4 border rounded-lg transition-all duration-200 cursor-pointer ${
|
|
|
|
|
isSelected
|
|
|
|
|
? 'border-purple-500 bg-purple-50 shadow-md'
|
|
|
|
|
: 'border-gray-200 hover:border-gray-300 hover:shadow-sm'
|
|
|
|
|
}`}
|
|
|
|
|
onClick={handleCardClick}
|
|
|
|
|
>
|
2025-07-26 00:22:15 -04:00
|
|
|
{/* Selection Checkbox */}
|
|
|
|
|
<div className="flex-shrink-0 mr-4">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={isSelected}
|
|
|
|
|
onChange={handleSelectChange}
|
|
|
|
|
className="w-4 h-4 text-purple-600 bg-gray-100 border-gray-300 rounded focus:ring-purple-500 focus:ring-2"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Card Image */}
|
|
|
|
|
<div className="flex-shrink-0 mr-4">
|
|
|
|
|
<div className="w-16 h-22 bg-gray-200 rounded-lg overflow-hidden">
|
|
|
|
|
{!imageError ? (
|
|
|
|
|
<img
|
|
|
|
|
src={card.image_url}
|
|
|
|
|
alt={card.name}
|
|
|
|
|
className="w-full h-full object-cover"
|
|
|
|
|
onError={handleImageError}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="w-full h-full flex items-center justify-center text-gray-400 text-xs">
|
|
|
|
|
No Image
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Card Info */}
|
|
|
|
|
<div className="flex-1 min-w-0">
|
|
|
|
|
<h3 className="font-semibold text-gray-900 truncate">{card.name}</h3>
|
|
|
|
|
<p className="text-sm text-gray-600">{card.set_name} • {card.rarity}</p>
|
|
|
|
|
<p className="text-sm text-gray-500">{card.card_type}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Game Badge */}
|
|
|
|
|
<div className="flex-shrink-0 mx-4">
|
|
|
|
|
<span className="px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800">
|
|
|
|
|
{card.game}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Price */}
|
|
|
|
|
<div className="flex-shrink-0 mx-4">
|
|
|
|
|
<span className="text-lg font-bold text-green-600">
|
|
|
|
|
${card.market_price}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Actions */}
|
|
|
|
|
<div className="flex-shrink-0 flex items-center space-x-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onToggleFavorite(card);
|
|
|
|
|
}}
|
|
|
|
|
className={`p-2 rounded-lg transition-colors ${
|
|
|
|
|
isFavorited
|
|
|
|
|
? 'text-red-500 hover:text-red-600'
|
|
|
|
|
: 'text-gray-400 hover:text-red-500'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-5 h-5" fill={isFavorited ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onAddToCollection(card);
|
|
|
|
|
}}
|
|
|
|
|
className="p-2 text-blue-600 hover:text-blue-700 hover:bg-blue-50 rounded-lg transition-colors"
|
|
|
|
|
title="Add to Collection"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onAddToDeck(card);
|
|
|
|
|
}}
|
|
|
|
|
className="p-2 text-green-600 hover:text-green-700 hover:bg-green-50 rounded-lg transition-colors"
|
|
|
|
|
title="Add to Deck"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Grid view
|
|
|
|
|
return (
|
2025-07-26 00:44:03 -04:00
|
|
|
<div className={`card-item-container relative group bg-transparent rounded-xl transition-all duration-300 overflow-visible ${
|
|
|
|
|
isSelected ? 'selected scale-105' : ''
|
2025-07-26 00:22:15 -04:00
|
|
|
}`}>
|
2025-07-26 00:44:03 -04:00
|
|
|
{/* Main Card Container */}
|
2025-07-26 01:12:54 -04:00
|
|
|
<div
|
|
|
|
|
className={`relative bg-white rounded-xl border transition-all duration-300 overflow-hidden cursor-pointer ${
|
|
|
|
|
isSelected
|
|
|
|
|
? 'border-purple-500 shadow-xl ring-2 ring-purple-200'
|
|
|
|
|
: `${rarityEffects.border} group-hover:border-gray-300 group-hover:shadow-lg ${rarityEffects.shadow ? `shadow-lg ${rarityEffects.shadow}` : ''}`
|
|
|
|
|
} ${rarityEffects.glow}`}
|
|
|
|
|
onClick={handleCardClick}
|
|
|
|
|
>
|
2025-07-26 01:01:04 -04:00
|
|
|
|
|
|
|
|
{/* Rarity Particles */}
|
|
|
|
|
{rarityEffects.particles && (
|
|
|
|
|
<div className={`absolute inset-0 pointer-events-none ${rarityEffects.particles}`}></div>
|
|
|
|
|
)}
|
2025-07-26 00:44:03 -04:00
|
|
|
{/* Selection Checkbox */}
|
|
|
|
|
<div className="absolute top-3 left-3 z-20">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={isSelected}
|
|
|
|
|
onChange={handleSelectChange}
|
|
|
|
|
className="w-4 h-4 text-purple-600 bg-white border-gray-300 rounded focus:ring-purple-500 focus:ring-2 shadow-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Favorite Button */}
|
|
|
|
|
<div className="absolute top-3 right-3 z-20">
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onToggleFavorite(card);
|
|
|
|
|
}}
|
|
|
|
|
className={`p-2 rounded-full transition-all duration-300 ${
|
|
|
|
|
isFavorited
|
|
|
|
|
? 'text-red-500 bg-white shadow-sm'
|
|
|
|
|
: 'text-gray-400 bg-white/90 hover:text-red-500 hover:bg-white shadow-sm opacity-0 group-hover:opacity-100'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill={isFavorited ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-26 00:22:15 -04:00
|
|
|
|
2025-07-26 00:44:03 -04:00
|
|
|
{/* Card Image */}
|
|
|
|
|
<div className="aspect-[2.5/3.5] bg-gray-200 overflow-hidden">
|
|
|
|
|
{!imageError ? (
|
|
|
|
|
<img
|
|
|
|
|
src={card.image_url}
|
|
|
|
|
alt={card.name}
|
|
|
|
|
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
|
|
|
onError={handleImageError}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="w-full h-full flex items-center justify-center text-gray-400">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<svg className="w-12 h-12 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
|
|
|
</svg>
|
|
|
|
|
<p className="text-xs">No Image</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-07-26 00:22:15 -04:00
|
|
|
</div>
|
|
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Side Hover Panel */}
|
|
|
|
|
<div className={`card-side-panel absolute left-full top-0 ml-2 w-80 h-full transition-all duration-300 ease-out z-30 ${
|
2025-07-26 01:05:26 -04:00
|
|
|
'opacity-0 translate-x-2 pointer-events-none group-hover:opacity-100 group-hover:translate-x-0 group-hover:pointer-events-auto delay-300 group-hover:delay-500'
|
2025-07-26 00:44:03 -04:00
|
|
|
}`}>
|
2025-07-26 01:01:04 -04:00
|
|
|
<div className={`bg-white dark:bg-gray-800 rounded-xl border shadow-2xl h-full overflow-hidden ${
|
|
|
|
|
rarityEffects.border !== 'border-gray-200'
|
|
|
|
|
? `${rarityEffects.border} dark:${rarityEffects.border.replace('border-', 'border-')} ${rarityEffects.glow}`
|
|
|
|
|
: 'border-gray-200 dark:border-gray-700'
|
|
|
|
|
}`}>
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Panel Header */}
|
|
|
|
|
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
|
|
|
|
<div className="flex items-start justify-between mb-2">
|
|
|
|
|
<h3 className="font-bold text-gray-900 dark:text-white text-lg leading-tight pr-2">
|
2025-07-26 00:44:03 -04:00
|
|
|
{card.name}
|
|
|
|
|
</h3>
|
2025-07-26 00:55:54 -04:00
|
|
|
<span className="text-2xl font-bold text-green-600 dark:text-green-400 flex-shrink-0">
|
2025-07-26 00:44:03 -04:00
|
|
|
${card.market_price}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
2025-07-26 00:55:54 -04:00
|
|
|
|
|
|
|
|
{/* TCG-Specific Info */}
|
|
|
|
|
{card.game === 'MTG' && (
|
|
|
|
|
<div className="flex items-center space-x-3 mb-2">
|
|
|
|
|
{card.mana_cost && (
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Cost:</span>
|
|
|
|
|
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded text-sm font-mono">
|
|
|
|
|
{card.mana_cost}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{card.power && card.toughness && (
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">P/T:</span>
|
|
|
|
|
<span className="px-2 py-1 bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 rounded text-sm font-bold">
|
|
|
|
|
{card.power}/{card.toughness}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-07-26 00:44:03 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{card.game === 'Pokemon' && card.power && (
|
|
|
|
|
<div className="flex items-center space-x-3 mb-2">
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">HP:</span>
|
|
|
|
|
<span className="px-2 py-1 bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 rounded text-sm font-bold">
|
|
|
|
|
{card.power}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-07-26 00:44:03 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{card.game === 'Lorcana' && card.cmc && (
|
|
|
|
|
<div className="flex items-center space-x-3 mb-2">
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Cost:</span>
|
|
|
|
|
<span className="px-2 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded text-sm font-bold">
|
|
|
|
|
{card.cmc}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
{card.power && card.toughness && (
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Str/Will:</span>
|
|
|
|
|
<span className="px-2 py-1 bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200 rounded text-sm font-bold">
|
|
|
|
|
{card.power}/{card.toughness}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<span className="px-2 py-1 text-sm font-medium rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
|
2025-07-26 00:44:03 -04:00
|
|
|
{card.game}
|
|
|
|
|
</span>
|
2025-07-26 00:55:54 -04:00
|
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">{card.set_name}</span>
|
2025-07-26 01:01:04 -04:00
|
|
|
{rarityEffects.glow && (
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<div className={`w-2 h-2 rounded-full ${
|
|
|
|
|
card.rarity?.toLowerCase().includes('mythic') || card.rarity?.toLowerCase().includes('legendary')
|
|
|
|
|
? 'bg-yellow-400 shadow-sm shadow-yellow-400/50'
|
|
|
|
|
: card.rarity?.toLowerCase().includes('rare') || card.rarity?.toLowerCase().includes('holo')
|
|
|
|
|
? 'bg-purple-400 shadow-sm shadow-purple-400/50'
|
|
|
|
|
: card.rarity?.toLowerCase().includes('uncommon')
|
|
|
|
|
? 'bg-blue-400 shadow-sm shadow-blue-400/50'
|
|
|
|
|
: card.rarity?.toLowerCase().includes('enchanted') || card.rarity?.toLowerCase().includes('secret')
|
|
|
|
|
? 'bg-pink-400 shadow-sm shadow-pink-400/50'
|
|
|
|
|
: 'bg-gray-400'
|
|
|
|
|
} animate-pulse`}></div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-07-26 00:22:15 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Panel Body */}
|
|
|
|
|
<div className="p-4 flex-1 overflow-y-auto">
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{/* Card Details */}
|
|
|
|
|
<div>
|
|
|
|
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
|
|
|
|
<div>
|
|
|
|
|
<span className="text-sm font-medium text-gray-500 dark:text-gray-400">Rarity</span>
|
|
|
|
|
<p className="text-sm text-gray-900 dark:text-white font-medium">{card.rarity}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<span className="text-sm font-medium text-gray-500 dark:text-gray-400">Type</span>
|
|
|
|
|
<p className="text-sm text-gray-900 dark:text-white font-medium">{card.card_type}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-26 00:22:15 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Oracle Text */}
|
|
|
|
|
{card.oracle_text && (
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<span className="text-sm font-medium text-gray-500 dark:text-gray-400">Description</span>
|
|
|
|
|
<p className="text-sm text-gray-700 dark:text-gray-300 mt-1 leading-relaxed">
|
|
|
|
|
{card.oracle_text}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Colors for MTG */}
|
|
|
|
|
{card.game === 'MTG' && card.colors && card.colors.length > 0 && (
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<span className="text-sm font-medium text-gray-500 dark:text-gray-400">Colors</span>
|
|
|
|
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
|
|
|
{card.colors.map((color, index) => (
|
|
|
|
|
<span key={index} className="px-2 py-1 text-xs rounded-full bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300">
|
|
|
|
|
{color}
|
|
|
|
|
</span>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-07-26 00:44:03 -04:00
|
|
|
</div>
|
2025-07-26 00:55:54 -04:00
|
|
|
</div>
|
2025-07-26 00:22:15 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Panel Footer - Actions */}
|
|
|
|
|
<div className="p-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
{/* Primary Actions */}
|
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onAddToCollection([card]);
|
|
|
|
|
}}
|
|
|
|
|
className="flex items-center justify-center space-x-2 px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors text-sm font-medium"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
<span>Collection</span>
|
|
|
|
|
</button>
|
2025-07-26 00:22:15 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onAddToDeck([card]);
|
|
|
|
|
}}
|
|
|
|
|
className="flex items-center justify-center space-x-2 px-3 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors text-sm font-medium"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
<span>Deck</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-26 00:44:03 -04:00
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
{/* Secondary Actions */}
|
|
|
|
|
<div className="grid grid-cols-4 gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
alert('Mark as owned (coming soon)');
|
|
|
|
|
}}
|
|
|
|
|
className="flex items-center justify-center p-2 bg-purple-100 dark:bg-purple-900 text-purple-700 dark:text-purple-300 rounded-lg hover:bg-purple-200 dark:hover:bg-purple-800 transition-colors"
|
|
|
|
|
title="Mark as Owned"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onToggleFavorite(card);
|
|
|
|
|
}}
|
|
|
|
|
className={`flex items-center justify-center p-2 rounded-lg transition-colors ${
|
|
|
|
|
isFavorited
|
|
|
|
|
? 'bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300 hover:bg-red-200 dark:hover:bg-red-800'
|
|
|
|
|
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
|
|
|
}`}
|
|
|
|
|
title="Toggle Favorite"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill={isFavorited ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
2025-07-26 01:12:54 -04:00
|
|
|
router.push(`/card/${card.id}`);
|
2025-07-26 00:55:54 -04:00
|
|
|
}}
|
|
|
|
|
className="flex items-center justify-center p-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
|
|
|
|
title="View Details"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
navigator.clipboard.writeText(`${card.name} - ${card.set_name}`);
|
|
|
|
|
// Could add a toast notification here
|
|
|
|
|
}}
|
|
|
|
|
className="flex items-center justify-center p-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
|
|
|
|
title="Copy Card Info"
|
|
|
|
|
>
|
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-26 00:44:03 -04:00
|
|
|
</div>
|
2025-07-26 00:22:15 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|