Files
pagodera/components/offer-card.tsx
T
Evandro Fonseca Junior 5c1ccbc69a 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.
2025-10-23 19:57:09 -03:00

247 lines
8.0 KiB
TypeScript

"use client";
import { MarketOffer } from "@/types/item";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
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 {
Clock,
User,
Coins,
TrendingUp,
TrendingDown,
Star,
Heart,
Shield,
Sword,
} from "lucide-react";
import { useState } from "react";
import { getRarityColor, getRarityBgColor } from "@/lib/currency-utils";
interface OfferCardProps {
offer: MarketOffer;
}
export function OfferCard({ offer }: OfferCardProps) {
const t = useTranslations("home.stats");
const tButtons = useTranslations("home.buttons");
const [isFavorited, setIsFavorited] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const formatTimeAgo = (timestamp: Date) => {
const now = new Date();
const diff = now.getTime() - timestamp.getTime();
const minutes = Math.floor(diff / (1000 * 60));
if (minutes < 1) return "Agora";
if (minutes < 60) return `há ${minutes} ${t("minutes")}`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `há ${hours} ${t("hours")}`;
const days = Math.floor(hours / 24);
return `há ${days} dias`;
};
const getInitials = (name: string) => {
return name
.split(" ")
.map((word) => word[0])
.join("")
.toUpperCase()
.slice(0, 2);
};
const getCategoryIcon = (category: string) => {
switch (category) {
case "weapon":
return <Sword className="w-6 h-6" />;
case "armor":
return <Shield className="w-6 h-6" />;
default:
return "📦";
}
};
const getRarityColor = (rarity: string) => {
switch (rarity) {
case "common":
return "bg-gray-500";
case "uncommon":
return "bg-green-500";
case "rare":
return "bg-blue-500";
case "epic":
return "bg-purple-500";
case "legendary":
return "bg-orange-500";
default:
return "bg-gray-500";
}
};
const isHotDeal = offer.price < offer.item.priceHistory.average * 0.8;
const isNewOffer =
new Date().getTime() - offer.timestamp.getTime() < 1000 * 60 * 30; // 30 min
return (
<div
className="group relative flex items-center justify-between p-6 border border-border/50 rounded-xl hover:border-primary/30 hover:shadow-lg transition-all duration-300 hover:scale-[1.01] bg-background/50 backdrop-blur-sm"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{/* Badges de status */}
<div className="absolute top-3 left-3 flex gap-2">
{isNewOffer && (
<Badge className="bg-green-500 text-white text-xs animate-pulse">
⭐ {t("status.new")}
</Badge>
)}
{isHotDeal && (
<Badge className="bg-red-500 text-white text-xs animate-bounce">
🔥 {t("status.hotDeal")}
</Badge>
)}
</div>
{/* Botão de favorito */}
<Button
variant="ghost"
size="icon"
className="absolute top-3 right-3 h-8 w-8 bg-background/80 backdrop-blur-sm hover:bg-background opacity-0 group-hover:opacity-100 transition-opacity"
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>
<div className="flex items-center gap-6 flex-1">
{/* Ícone do item com raridade */}
<div className="relative">
<div className="w-16 h-16 bg-muted/50 rounded-xl flex items-center justify-center text-2xl backdrop-blur-sm">
{getCategoryIcon(offer.item.category)}
</div>
<Badge
className={`${getRarityColor(
offer.item.rarity
)} text-white absolute -top-2 -right-2 text-xs shadow-lg`}
>
{offer.item.rarity}
</Badge>
</div>
{/* Informações do item */}
<div className="flex-1 space-y-2">
<div className="space-y-1">
<h3 className="font-bold text-lg text-foreground">
{offer.item.name}
</h3>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<div className="flex items-center gap-1">
<Avatar className="w-5 h-5">
<AvatarFallback className="text-xs">
{getInitials(offer.seller)}
</AvatarFallback>
</Avatar>
<span className="font-medium">{offer.seller}</span>
</div>
<div className="flex items-center gap-1">
<Clock className="w-4 h-4" />
<span>{formatTimeAgo(offer.timestamp)}</span>
</div>
</div>
</div>
{/* Estatísticas do item */}
{offer.item.stats && (
<div className="flex flex-wrap gap-3 text-sm">
{offer.item.stats.attack && (
<div className="flex items-center gap-1 text-muted-foreground">
<Sword className="h-3 w-3" />
<span>{offer.item.stats.attack} ATK</span>
</div>
)}
{offer.item.stats.defense && (
<div className="flex items-center gap-1 text-muted-foreground">
<Shield className="h-3 w-3" />
<span>{offer.item.stats.defense} DEF</span>
</div>
)}
{offer.item.stats.armor && (
<div className="flex items-center gap-1 text-muted-foreground">
<Shield className="h-3 w-3" />
<span>{offer.item.stats.armor} ARM</span>
</div>
)}
</div>
)}
</div>
{/* Preço e ações */}
<div className="flex items-center gap-6">
<div className="text-right space-y-2">
<div className="space-y-1">
<div className="flex items-center gap-1 text-sm text-muted-foreground">
<Coins className="h-4 w-4" />
<span>Preço</span>
</div>
<PriceDisplay
value={offer.price}
size="lg"
className="text-foreground"
/>
<p className="text-sm text-muted-foreground">
{offer.item.stackable
? `Stack: ${offer.quantity}x`
: `Qtd: ${offer.quantity}`}
{offer.item.stackable && offer.quantity > 1 && (
<span className="block text-xs">
{Math.round(offer.price / offer.quantity).toLocaleString()}{" "}
gp cada
</span>
)}
</p>
</div>
<Badge
variant={offer.type === "buy" ? "default" : "secondary"}
className={`${
offer.type === "buy"
? "bg-green-500 hover:bg-green-600"
: "bg-blue-500 hover:bg-blue-600"
} text-white transition-colors`}
>
{offer.type === "buy" ? "Comprando" : "Vendendo"}
</Badge>
</div>
<div className="space-y-2">
<Button
size="sm"
className={`w-full transition-all duration-200 ${
offer.type === "buy"
? "bg-green-600 hover:bg-green-700"
: "bg-blue-600 hover:bg-blue-700"
} text-white`}
>
{tButtons("negotiate")}
</Button>
<Button
variant="outline"
size="sm"
className="w-full opacity-0 group-hover:opacity-100 transition-opacity"
>
{tButtons("viewProfile")}
</Button>
</div>
</div>
</div>
</div>
);
}