style: clean up code formatting and remove trailing whitespace
refactor: reorganize imports and fix type imports
This commit is contained in:
1 parent
d3704602fd
commit
47c04de6fe
232 files changed
+7140
-4798
No files matched your search
+173
-147
@@ -1,45 +1,39 @@
|
||||
"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, useLocale } from "next-intl";
|
||||
import {
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Sword,
|
||||
Shield,
|
||||
Weight,
|
||||
Heart,
|
||||
Star,
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
Boxes,
|
||||
Clock,
|
||||
Coins,
|
||||
ArrowUp,
|
||||
ArrowDown,
|
||||
Minus,
|
||||
Zap,
|
||||
Flame,
|
||||
Snowflake,
|
||||
Zap as Energy,
|
||||
Skull,
|
||||
Cross,
|
||||
Badge as BadgeIcon,
|
||||
Boxes,
|
||||
Gem,
|
||||
FlaskConical,
|
||||
Wrench,
|
||||
ScrollText,
|
||||
Leaf,
|
||||
Zap as Energy,
|
||||
Flame,
|
||||
FlaskConical,
|
||||
Gem,
|
||||
Heart,
|
||||
Leaf,
|
||||
Minus,
|
||||
ScrollText,
|
||||
Shield,
|
||||
Skull,
|
||||
Snowflake,
|
||||
Star,
|
||||
Sword,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
Weight,
|
||||
Wrench,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
getRarityColor,
|
||||
getRarityBgColor,
|
||||
getRarityGlow,
|
||||
} from "@/lib/currency-utils";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { MagicCard } from "@/components/ui/magic-card";
|
||||
import { PriceDisplay } from "@/components/ui/price-display";
|
||||
import { formatNumber } from "@/lib/utils/formatters";
|
||||
import type { TibiaItem } from "@/types/item";
|
||||
|
||||
interface ItemCardProps {
|
||||
item: TibiaItem;
|
||||
@@ -50,7 +44,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
const t = useTranslations("home.item");
|
||||
const locale = useLocale();
|
||||
const [isFavorited, setIsFavorited] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [_isHovered, setIsHovered] = useState(false);
|
||||
const formatVocation = (v: string | undefined): string | undefined => {
|
||||
if (!v) return undefined;
|
||||
let out = String(v);
|
||||
@@ -64,14 +58,22 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
return out;
|
||||
};
|
||||
|
||||
const ScrollRow = ({ children, ariaLabel }: { children: React.ReactNode; ariaLabel: string }) => {
|
||||
const ScrollRow = ({
|
||||
children,
|
||||
ariaLabel,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
ariaLabel: string;
|
||||
}) => {
|
||||
const scrollerRef = useRef<HTMLDivElement | null>(null);
|
||||
const contentRef = useRef<HTMLDivElement | null>(null);
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(false);
|
||||
const [_canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [_canScrollRight, setCanScrollRight] = useState(false);
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const autoScrollRef = useRef<number | null>(null);
|
||||
const drag = useRef<{ active: boolean; startX: number; startLeft: number }>({ active: false, startX: 0, startLeft: 0 });
|
||||
const drag = useRef<{ active: boolean; startX: number; startLeft: number }>(
|
||||
{ active: false, startX: 0, startLeft: 0 },
|
||||
);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const updateButtons = () => {
|
||||
@@ -114,9 +116,9 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
el.removeEventListener("mouseleave", onLeave);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, []);
|
||||
}, [updateButtons]);
|
||||
|
||||
const scrollBy = (dx: number) => {
|
||||
const _scrollBy = (dx: number) => {
|
||||
const el = scrollerRef.current;
|
||||
if (!el) return;
|
||||
const base = contentRef.current?.offsetWidth || 0;
|
||||
@@ -182,7 +184,9 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
if (!drag.current.active) return;
|
||||
drag.current.active = false;
|
||||
setIsDragging(false);
|
||||
try { el.releasePointerCapture(e.pointerId); } catch {}
|
||||
try {
|
||||
el.releasePointerCapture(e.pointerId);
|
||||
} catch {}
|
||||
};
|
||||
|
||||
el.addEventListener("pointerdown", onPointerDown);
|
||||
@@ -204,7 +208,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
ref={scrollerRef}
|
||||
className={`overflow-x-auto no-scrollbar whitespace-nowrap pr-6 pl-6 select-none ${isDragging ? "cursor-grabbing" : "cursor-grab"}`}
|
||||
style={{ scrollBehavior: "smooth" }}
|
||||
>
|
||||
>
|
||||
<div ref={contentRef} className="inline-flex items-center gap-2">
|
||||
{children}
|
||||
{children}
|
||||
@@ -213,10 +217,10 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const [showAllDroppers, setShowAllDroppers] = useState(false);
|
||||
|
||||
const rarityColors = {
|
||||
const [_showAllDroppers, _setShowAllDroppers] = useState(false);
|
||||
|
||||
const _rarityColors = {
|
||||
common: "bg-gray-500",
|
||||
uncommon: "bg-green-500",
|
||||
rare: "bg-blue-500",
|
||||
@@ -251,14 +255,20 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
(item as any).price.sell !== 0;
|
||||
const priceDifference = hasPrice
|
||||
? (((item as any).price.buy - (item as any).price.sell) /
|
||||
(item as any).price.sell) * 100
|
||||
(item as any).price.sell) *
|
||||
100
|
||||
: 0;
|
||||
const isHotDeal = hasPrice && priceDifference > 20; // Hot deal se diferença > 20%
|
||||
|
||||
// Determinar tendência de preço
|
||||
const getPriceTrend = () => {
|
||||
const ph = (item as any).priceHistory;
|
||||
if (!ph || typeof ph.highest !== "number" || typeof ph.lowest !== "number" || typeof ph.average !== "number") {
|
||||
if (
|
||||
!ph ||
|
||||
typeof ph.highest !== "number" ||
|
||||
typeof ph.lowest !== "number" ||
|
||||
typeof ph.average !== "number"
|
||||
) {
|
||||
return "stable";
|
||||
}
|
||||
if (ph.highest > ph.average * 1.1) return "up";
|
||||
@@ -273,8 +283,8 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
trend === "up"
|
||||
? "text-green-500"
|
||||
: trend === "down"
|
||||
? "text-red-500"
|
||||
: "text-gray-500";
|
||||
? "text-red-500"
|
||||
: "text-gray-500";
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -323,9 +333,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
/>
|
||||
) : (
|
||||
<div className="w-20 h-20 bg-muted/50 rounded-xl flex items-center justify-center text-3xl backdrop-blur-sm">
|
||||
{typeof CategoryIcon === "string" ? (
|
||||
null
|
||||
) : (
|
||||
{typeof CategoryIcon === "string" ? null : (
|
||||
<CategoryIcon className="w-10 h-10" />
|
||||
)}
|
||||
</div>
|
||||
@@ -352,7 +360,8 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
{(() => {
|
||||
const catPt = (item as any).categoryPt as string | undefined;
|
||||
const catEn = (item as any).category as string | undefined;
|
||||
const catRaw = (catPt && String(catPt)) || (catEn && String(catEn)) || "";
|
||||
const catRaw =
|
||||
(catPt && String(catPt)) || (catEn && String(catEn)) || "";
|
||||
const display = catRaw
|
||||
? catRaw.charAt(0).toUpperCase() + catRaw.slice(1)
|
||||
: "";
|
||||
@@ -375,9 +384,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
{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>
|
||||
<span className="font-medium">{item.stats.attack} ATK</span>
|
||||
</div>
|
||||
)}
|
||||
{item.stats.defense && (
|
||||
@@ -391,21 +398,23 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
{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>
|
||||
<span className="font-medium">{item.stats.armor} ARM</span>
|
||||
</div>
|
||||
)}
|
||||
{(item as any).volume && (
|
||||
<div className="flex items-center gap-1.5 text-sm">
|
||||
<Boxes className="h-4 w-4 text-primary" />
|
||||
<span className="font-medium">Volume {(item as any).volume}</span>
|
||||
<span className="font-medium">
|
||||
Volume {(item as any).volume}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{(item as any).bonus_de_skill && (
|
||||
<div className="flex items-center gap-1.5 text-sm">
|
||||
<Zap className="h-4 w-4 text-primary" />
|
||||
<span className="font-medium">Skill +{(item as any).bonus_de_skill}</span>
|
||||
<span className="font-medium">
|
||||
Skill +{(item as any).bonus_de_skill}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -457,84 +466,91 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(item as any).protecao && (() => {
|
||||
const raw = String((item as any).protecao || "");
|
||||
const tokens = raw
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
.filter((s) => !/^nenhum(a)?\.?$/i.test(s.trim()));
|
||||
if (tokens.length === 0) return null;
|
||||
return (
|
||||
<ScrollRow ariaLabel="elemental protections">
|
||||
{tokens.map((raw: string, idx: number) => {
|
||||
const token = raw.trim();
|
||||
const match = token.match(/^([A-Za-zçÇáÁéÉíÍóÓúÚãÃõÕ ]+)(.*)$/);
|
||||
const type = (match?.[1] || token).trim().toLowerCase();
|
||||
const value = (match?.[2] || "").trim();
|
||||
let Icon: any = Shield;
|
||||
let chipBg = "bg-amber-500/10";
|
||||
let chipText = "text-amber-700";
|
||||
switch (type) {
|
||||
case "fire":
|
||||
case "fogo":
|
||||
Icon = Flame;
|
||||
chipBg = "bg-red-500/10";
|
||||
chipText = "text-red-600";
|
||||
break;
|
||||
case "ice":
|
||||
case "gelo":
|
||||
Icon = Snowflake;
|
||||
chipBg = "bg-blue-500/10";
|
||||
chipText = "text-blue-600";
|
||||
break;
|
||||
case "energy":
|
||||
case "energia":
|
||||
Icon = Energy;
|
||||
chipBg = "bg-yellow-500/10";
|
||||
chipText = "text-yellow-600";
|
||||
break;
|
||||
case "earth":
|
||||
case "terra":
|
||||
Icon = Leaf;
|
||||
chipBg = "bg-green-500/10";
|
||||
chipText = "text-green-600";
|
||||
break;
|
||||
case "death":
|
||||
case "morte":
|
||||
Icon = Skull;
|
||||
chipBg = "bg-zinc-500/10";
|
||||
chipText = "text-zinc-600";
|
||||
break;
|
||||
case "holy":
|
||||
case "sagrado":
|
||||
Icon = Cross;
|
||||
chipBg = "bg-amber-500/10";
|
||||
chipText = "text-amber-700";
|
||||
break;
|
||||
case "physical":
|
||||
case "fisico":
|
||||
Icon = Shield;
|
||||
chipBg = "bg-slate-500/10";
|
||||
chipText = "text-slate-600";
|
||||
break;
|
||||
default:
|
||||
Icon = Shield;
|
||||
chipBg = "bg-amber-500/10";
|
||||
chipText = "text-amber-700";
|
||||
}
|
||||
const prettyType = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
const label = `${prettyType.replace("fisico", "Físico")}${value ? " " + value : ""}`;
|
||||
return (
|
||||
<div key={`${type}-${idx}`} className={`flex items-center gap-1 text-xs ${chipBg} ${chipText} px-2 py-1 rounded`}>
|
||||
<Icon className="h-3 w-3" />
|
||||
<span className="whitespace-nowrap">{label}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ScrollRow>
|
||||
);
|
||||
})()}
|
||||
{(item as any).protecao &&
|
||||
(() => {
|
||||
const raw = String((item as any).protecao || "");
|
||||
const tokens = raw
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
.filter((s) => !/^nenhum(a)?\.?$/i.test(s.trim()));
|
||||
if (tokens.length === 0) return null;
|
||||
return (
|
||||
<ScrollRow ariaLabel="elemental protections">
|
||||
{tokens.map((raw: string, idx: number) => {
|
||||
const token = raw.trim();
|
||||
const match = token.match(
|
||||
/^([A-Za-zçÇáÁéÉíÍóÓúÚãÃõÕ ]+)(.*)$/,
|
||||
);
|
||||
const type = (match?.[1] || token).trim().toLowerCase();
|
||||
const value = (match?.[2] || "").trim();
|
||||
let Icon: any = Shield;
|
||||
let chipBg = "bg-amber-500/10";
|
||||
let chipText = "text-amber-700";
|
||||
switch (type) {
|
||||
case "fire":
|
||||
case "fogo":
|
||||
Icon = Flame;
|
||||
chipBg = "bg-red-500/10";
|
||||
chipText = "text-red-600";
|
||||
break;
|
||||
case "ice":
|
||||
case "gelo":
|
||||
Icon = Snowflake;
|
||||
chipBg = "bg-blue-500/10";
|
||||
chipText = "text-blue-600";
|
||||
break;
|
||||
case "energy":
|
||||
case "energia":
|
||||
Icon = Energy;
|
||||
chipBg = "bg-yellow-500/10";
|
||||
chipText = "text-yellow-600";
|
||||
break;
|
||||
case "earth":
|
||||
case "terra":
|
||||
Icon = Leaf;
|
||||
chipBg = "bg-green-500/10";
|
||||
chipText = "text-green-600";
|
||||
break;
|
||||
case "death":
|
||||
case "morte":
|
||||
Icon = Skull;
|
||||
chipBg = "bg-zinc-500/10";
|
||||
chipText = "text-zinc-600";
|
||||
break;
|
||||
case "holy":
|
||||
case "sagrado":
|
||||
Icon = Cross;
|
||||
chipBg = "bg-amber-500/10";
|
||||
chipText = "text-amber-700";
|
||||
break;
|
||||
case "physical":
|
||||
case "fisico":
|
||||
Icon = Shield;
|
||||
chipBg = "bg-slate-500/10";
|
||||
chipText = "text-slate-600";
|
||||
break;
|
||||
default:
|
||||
Icon = Shield;
|
||||
chipBg = "bg-amber-500/10";
|
||||
chipText = "text-amber-700";
|
||||
}
|
||||
const prettyType =
|
||||
type.charAt(0).toUpperCase() + type.slice(1);
|
||||
const label = `${prettyType.replace("fisico", "Físico")}${value ? ` ${value}` : ""}`;
|
||||
return (
|
||||
<div
|
||||
key={`${type}-${idx}`}
|
||||
className={`flex items-center gap-1 text-xs ${chipBg} ${chipText} px-2 py-1 rounded`}
|
||||
>
|
||||
<Icon className="h-3 w-3" />
|
||||
<span className="whitespace-nowrap">{label}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ScrollRow>
|
||||
);
|
||||
})()}
|
||||
|
||||
{item.stats.weight && (
|
||||
<div className="flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
@@ -552,7 +568,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(item as any).dropa_de && (
|
||||
{(item as any).dropa_de &&
|
||||
(() => {
|
||||
const raw = String((item as any).dropa_de || "");
|
||||
const normalized = raw.trim();
|
||||
@@ -588,8 +604,7 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
</ScrollRow>
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
})()}
|
||||
|
||||
{mode === "market" && item.price && (
|
||||
<>
|
||||
@@ -649,7 +664,9 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{t("priceHistory")}
|
||||
</span>
|
||||
<div className={`flex items-center gap-1 text-xs ${trendColor}`}>
|
||||
<div
|
||||
className={`flex items-center gap-1 text-xs ${trendColor}`}
|
||||
>
|
||||
<TrendIcon className="h-3 w-3" />
|
||||
<span className="capitalize">{trend}</span>
|
||||
</div>
|
||||
@@ -661,21 +678,27 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
<TrendingUp className="h-3 w-3" />
|
||||
<span className="font-medium">{t("highest")}</span>
|
||||
</div>
|
||||
<div className="font-bold">{formatNumber(item.priceHistory.highest, locale)} gp</div>
|
||||
<div className="font-bold">
|
||||
{formatNumber(item.priceHistory.highest, locale)} 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">{formatNumber(item.priceHistory.lowest, locale)} gp</div>
|
||||
<div className="font-bold">
|
||||
{formatNumber(item.priceHistory.lowest, locale)} 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">{formatNumber(item.priceHistory.average, locale)} gp</div>
|
||||
<div className="font-bold">
|
||||
{formatNumber(item.priceHistory.average, locale)} gp
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -690,7 +713,10 @@ export function ItemCard({ item, mode = "market" }: ItemCardProps) {
|
||||
>
|
||||
{t("viewDetails")}
|
||||
</Button>
|
||||
<Button className="w-full bg-green-600 hover:bg-green-700 text-white" size="sm">
|
||||
<Button
|
||||
className="w-full bg-green-600 hover:bg-green-700 text-white"
|
||||
size="sm"
|
||||
>
|
||||
{t("buyNow")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user