feat: integrate Tailwind CSS with custom theme and animations
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.
This commit is contained in:
1 parent
55c169bd6e
commit
5c1ccbc69a
67 files changed
+8984
-160
No files matched your search
@@ -0,0 +1,354 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user