Added Tailwind CSS and tw-animate-css imports to globals.css. Introduced a custom dark variant and an extensive @theme inline block with CSS variables for colors, radii, and animations. Included keyframes for shimmer-slide, spin-around, gradient, shine, and background-position-spin to enable consistent theming and dynamic UI effects across the application.
355 lines
12 KiB
TypeScript
355 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import { TibiaItem } from "@/types/item";
|
|
import { MagicCard } from "@/components/ui/magic-card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { PriceDisplay } from "@/components/ui/price-display";
|
|
import { useTranslations } from "next-intl";
|
|
import {
|
|
TrendingUp,
|
|
TrendingDown,
|
|
Sword,
|
|
Shield,
|
|
Weight,
|
|
Heart,
|
|
Star,
|
|
Clock,
|
|
Coins,
|
|
ArrowUp,
|
|
ArrowDown,
|
|
Minus,
|
|
Zap,
|
|
Flame,
|
|
Snowflake,
|
|
Zap as Energy,
|
|
Skull,
|
|
Cross,
|
|
} from "lucide-react";
|
|
import { useState } from "react";
|
|
import {
|
|
getRarityColor,
|
|
getRarityBgColor,
|
|
getRarityGlow,
|
|
} from "@/lib/currency-utils";
|
|
|
|
interface ItemCardProps {
|
|
item: TibiaItem;
|
|
}
|
|
|
|
export function ItemCard({ item }: ItemCardProps) {
|
|
const t = useTranslations("home.item");
|
|
const [isFavorited, setIsFavorited] = useState(false);
|
|
const [isHovered, setIsHovered] = useState(false);
|
|
|
|
const rarityColors = {
|
|
common: "bg-gray-500",
|
|
uncommon: "bg-green-500",
|
|
rare: "bg-blue-500",
|
|
epic: "bg-purple-500",
|
|
legendary: "bg-orange-500",
|
|
};
|
|
|
|
const categoryIcons = {
|
|
weapon: Sword,
|
|
armor: Shield,
|
|
helmet: Shield,
|
|
legs: Shield,
|
|
boots: Shield,
|
|
shield: Shield,
|
|
amulet: "💎",
|
|
ring: "💍",
|
|
potion: "🧪",
|
|
rune: "📜",
|
|
tool: "🔧",
|
|
ammunition: "🏹",
|
|
other: "📦",
|
|
};
|
|
|
|
const CategoryIcon = categoryIcons[item.category];
|
|
|
|
// Calcular diferença percentual entre compra e venda
|
|
const priceDifference =
|
|
((item.price.buy - item.price.sell) / item.price.sell) * 100;
|
|
const isHotDeal = priceDifference > 20; // Hot deal se diferença > 20%
|
|
|
|
// Determinar tendência de preço
|
|
const getPriceTrend = () => {
|
|
if (item.priceHistory.highest > item.priceHistory.average * 1.1)
|
|
return "up";
|
|
if (item.priceHistory.lowest < item.priceHistory.average * 0.9)
|
|
return "down";
|
|
return "stable";
|
|
};
|
|
|
|
const trend = getPriceTrend();
|
|
const TrendIcon =
|
|
trend === "up" ? ArrowUp : trend === "down" ? ArrowDown : Minus;
|
|
const trendColor =
|
|
trend === "up"
|
|
? "text-green-500"
|
|
: trend === "down"
|
|
? "text-red-500"
|
|
: "text-gray-500";
|
|
|
|
return (
|
|
<div
|
|
className="group relative h-full"
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
>
|
|
<MagicCard className="p-0 overflow-hidden h-full flex flex-col rounded-xl border border-border/50 hover:border-primary/30 transition-all duration-300 hover:scale-[1.02] hover:shadow-xl">
|
|
{/* Header com ícone e badge de raridade */}
|
|
<div className="relative aspect-square bg-linear-to-br from-background to-muted p-6">
|
|
{/* Badge de raridade com glow */}
|
|
<Badge
|
|
className={`${
|
|
rarityColors[item.rarity]
|
|
} text-white absolute top-3 left-3 z-20 shadow-lg ${getRarityGlow(
|
|
item.rarity
|
|
)} ${item.rarity === "legendary" ? "animate-pulse" : ""} ${
|
|
item.rarity === "epic" ? "animate-pulse" : ""
|
|
}`}
|
|
>
|
|
{item.rarity}
|
|
</Badge>
|
|
|
|
{/* Botão de favorito */}
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="absolute top-3 right-3 z-20 h-8 w-8 bg-background/80 backdrop-blur-sm hover:bg-background"
|
|
onClick={() => setIsFavorited(!isFavorited)}
|
|
>
|
|
<Heart
|
|
className={`h-4 w-4 transition-colors ${
|
|
isFavorited
|
|
? "fill-red-500 text-red-500"
|
|
: "text-muted-foreground hover:text-red-500"
|
|
}`}
|
|
/>
|
|
</Button>
|
|
|
|
{/* Hot Deal Badge */}
|
|
{isHotDeal && (
|
|
<Badge className="absolute top-3 left-1/2 transform -translate-x-1/2 z-20 bg-red-500 text-white shadow-lg animate-pulse">
|
|
🔥 Hot Deal
|
|
</Badge>
|
|
)}
|
|
|
|
{/* Ícone do item */}
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-20 h-20 bg-muted/50 rounded-xl flex items-center justify-center text-3xl backdrop-blur-sm">
|
|
{typeof CategoryIcon === "string" ? (
|
|
CategoryIcon
|
|
) : (
|
|
<CategoryIcon className="w-10 h-10" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Conteúdo do card */}
|
|
<div className="p-6 space-y-4 flex-1 flex flex-col">
|
|
{/* Nome do item */}
|
|
<div className="space-y-2">
|
|
<h3 className="font-bold text-xl text-foreground leading-tight">
|
|
{item.name}
|
|
</h3>
|
|
|
|
{/* Timestamp */}
|
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
<Clock className="h-3 w-3" />
|
|
<span>Atualizado há 5 min</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Estatísticas agrupadas */}
|
|
{item.stats && (
|
|
<div className="space-y-2">
|
|
<div className="flex flex-wrap gap-3">
|
|
{item.stats.attack && (
|
|
<div className="flex items-center gap-1.5 text-sm">
|
|
<Sword className="h-4 w-4 text-primary" />
|
|
<span className="font-medium">
|
|
⚔️ {item.stats.attack} ATK
|
|
</span>
|
|
</div>
|
|
)}
|
|
{item.stats.defense && (
|
|
<div className="flex items-center gap-1.5 text-sm">
|
|
<Shield className="h-4 w-4 text-primary" />
|
|
<span className="font-medium">
|
|
🛡️ {item.stats.defense} DEF
|
|
</span>
|
|
</div>
|
|
)}
|
|
{item.stats.armor && (
|
|
<div className="flex items-center gap-1.5 text-sm">
|
|
<Shield className="h-4 w-4 text-primary" />
|
|
<span className="font-medium">
|
|
🛡️ {item.stats.armor} ARM
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Proteções elementais */}
|
|
{item.protections && (
|
|
<div className="flex flex-wrap gap-2">
|
|
{item.protections.fire && (
|
|
<div className="flex items-center gap-1 text-xs bg-red-500/10 text-red-600 px-2 py-1 rounded">
|
|
<Flame className="h-3 w-3" />
|
|
<span>🔥 Fire +{item.protections.fire}%</span>
|
|
</div>
|
|
)}
|
|
{item.protections.ice && (
|
|
<div className="flex items-center gap-1 text-xs bg-blue-500/10 text-blue-600 px-2 py-1 rounded">
|
|
<Snowflake className="h-3 w-3" />
|
|
<span>❄️ Ice +{item.protections.ice}%</span>
|
|
</div>
|
|
)}
|
|
{item.protections.energy && (
|
|
<div className="flex items-center gap-1 text-xs bg-yellow-500/10 text-yellow-600 px-2 py-1 rounded">
|
|
<Energy className="h-3 w-3" />
|
|
<span>⚡ Energy +{item.protections.energy}%</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Requisitos */}
|
|
{item.requirements && (
|
|
<div className="space-y-1">
|
|
{item.requirements.level && (
|
|
<div className="text-xs text-muted-foreground">
|
|
📊 Level {item.requirements.level}+
|
|
</div>
|
|
)}
|
|
{item.requirements.vocation && (
|
|
<div className="text-xs text-muted-foreground">
|
|
👤 Best for {item.requirements.vocation}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{item.stats.weight && (
|
|
<div className="flex items-center gap-1.5 text-sm text-muted-foreground">
|
|
<Weight className="h-4 w-4" />
|
|
<span>⚖️ {item.stats.weight} oz</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Divisor visual */}
|
|
<div className="border-t border-border/50" />
|
|
|
|
{/* Preços com ícones e formatação melhorada */}
|
|
<div className="space-y-3">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-1">
|
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
<Coins className="h-3 w-3" />
|
|
<span>{t("buyPrice")}</span>
|
|
</div>
|
|
<PriceDisplay
|
|
value={item.price.buy}
|
|
className="text-green-600 dark:text-green-400"
|
|
size="md"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
<Coins className="h-3 w-3" />
|
|
<span>{t("sellPrice")}</span>
|
|
</div>
|
|
<PriceDisplay
|
|
value={item.price.sell}
|
|
className="text-red-600 dark:text-red-400"
|
|
size="md"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Diferença percentual */}
|
|
<div className="text-center">
|
|
<span
|
|
className={`text-sm font-medium ${
|
|
priceDifference > 0 ? "text-green-600" : "text-red-600"
|
|
}`}
|
|
>
|
|
{priceDifference > 0 ? "+" : ""}
|
|
{priceDifference.toFixed(1)}% diferença
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Divisor visual */}
|
|
<div className="border-t border-border/50" />
|
|
|
|
{/* Histórico de preço com sparkline */}
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium text-muted-foreground">
|
|
{t("priceHistory")}
|
|
</span>
|
|
<div className={`flex items-center gap-1 text-xs ${trendColor}`}>
|
|
<TrendIcon className="h-3 w-3" />
|
|
<span className="capitalize">{trend}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-3 gap-2 text-xs">
|
|
<div className="text-center space-y-1">
|
|
<div className="flex items-center justify-center gap-1 text-green-600">
|
|
<TrendingUp className="h-3 w-3" />
|
|
<span className="font-medium">{t("highest")}</span>
|
|
</div>
|
|
<div className="font-bold">
|
|
🪙 {item.priceHistory.highest.toLocaleString()} gp
|
|
</div>
|
|
</div>
|
|
<div className="text-center space-y-1">
|
|
<div className="flex items-center justify-center gap-1 text-red-600">
|
|
<TrendingDown className="h-3 w-3" />
|
|
<span className="font-medium">{t("lowest")}</span>
|
|
</div>
|
|
<div className="font-bold">
|
|
🪙 {item.priceHistory.lowest.toLocaleString()} gp
|
|
</div>
|
|
</div>
|
|
<div className="text-center space-y-1">
|
|
<div className="flex items-center justify-center gap-1 text-muted-foreground">
|
|
<Minus className="h-3 w-3" />
|
|
<span className="font-medium">{t("average")}</span>
|
|
</div>
|
|
<div className="font-bold">
|
|
🪙 {item.priceHistory.average.toLocaleString()} gp
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Botões de ação */}
|
|
<div className="pt-4 space-y-2">
|
|
<Button
|
|
className="w-full group-hover:bg-primary group-hover:text-primary-foreground transition-colors"
|
|
variant="outline"
|
|
>
|
|
{t("viewDetails")}
|
|
</Button>
|
|
<Button
|
|
className="w-full bg-green-600 hover:bg-green-700 text-white"
|
|
size="sm"
|
|
>
|
|
{t("buyNow")}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</MagicCard>
|
|
</div>
|
|
);
|
|
}
|