style: clean up code formatting and remove trailing whitespace

refactor: reorganize imports and fix type imports
This commit is contained in:
Evandro Fonseca Junior committed 2025-11-11 03:08:36 -03:00
1 parent d3704602fd
commit 47c04de6fe
232 files changed
+7140 -4798

No files matched your search

+197 -58
View File
@@ -1,19 +1,34 @@
"use client";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { RefreshCw, Plus, Pencil } from "lucide-react";
import Image from "next/image";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { ItemCreationModal, type ItemFormPayload } from "@/components/admin/items/ItemCreationModal";
import { Pencil, Plus, RefreshCw } from "lucide-react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
ItemCreationModal,
type ItemFormPayload,
} from "@/components/admin/items/ItemCreationModal";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export interface AdminItemRecord {
item_id: number;
@@ -50,12 +65,20 @@ interface AdminItemManagerProps {
locale: string;
}
export function AdminItemManager({ initialData, locale }: AdminItemManagerProps) {
export function AdminItemManager({
initialData,
locale,
}: AdminItemManagerProps) {
const router = useRouter();
const [data, setData] = useState<AdminItemsResponse>(initialData);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [apiStatus, setApiStatus] = useState<{ code: number | null; ok: boolean | null; durationMs: number | null; checkedAt: string | null }>({ code: null, ok: null, durationMs: null, checkedAt: null });
const [apiStatus, setApiStatus] = useState<{
code: number | null;
ok: boolean | null;
durationMs: number | null;
checkedAt: string | null;
}>({ code: null, ok: null, durationMs: null, checkedAt: null });
const [serverFilter, setServerFilter] = useState<string>("all");
const [search, setSearch] = useState<string>("");
@@ -65,10 +88,12 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
const pageSize = initialData.pageSize ?? 50;
const [modalOpen, setModalOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState<AdminItemRecord | null>(null);
const [selectedItem, setSelectedItem] = useState<AdminItemRecord | null>(
null,
);
const [isSubmitting, setIsSubmitting] = useState(false);
const [activeServers, setActiveServers] = useState<string[]>([]);
const [serversLoading, setServersLoading] = useState<boolean>(false);
const [_serversLoading, setServersLoading] = useState<boolean>(false);
const searchDebounce = useRef<number | undefined>(undefined);
@@ -96,13 +121,18 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
cache: "no-store",
});
if (!res.ok) {
console.warn("[AdminItemManager] falha ao buscar servidores ativos:", res.status);
console.warn(
"[AdminItemManager] falha ao buscar servidores ativos:",
res.status,
);
return;
}
const json = await res.json();
const names: string[] = Array.isArray(json?.servers)
? json.servers
.map((s: any) => (typeof s?.serverName === "string" ? s.serverName : null))
.map((s: any) =>
typeof s?.serverName === "string" ? s.serverName : null,
)
.filter((n: string | null): n is string => Boolean(n))
: [];
if (!cancelled) {
@@ -147,8 +177,19 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
signal: controller.signal,
});
const durationMs = Math.round(performance.now() - start);
setApiStatus({ code: res.status, ok: res.ok, durationMs, checkedAt: new Date().toISOString() });
console.log("Status:", res.status, res.statusText, "Duration:", `${durationMs}ms`);
setApiStatus({
code: res.status,
ok: res.ok,
durationMs,
checkedAt: new Date().toISOString(),
});
console.log(
"Status:",
res.status,
res.statusText,
"Duration:",
`${durationMs}ms`,
);
console.log("Headers:", {
"content-type": res.headers.get("content-type"),
"x-powered-by": res.headers.get("x-powered-by"),
@@ -165,10 +206,12 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
res.status === 401
? "Sessão expirada ou não autenticado. Faça login e tente novamente."
: res.status === 403
? "Acesso restrito ao administrador."
: "Não foi possível carregar os itens.";
? "Acesso restrito ao administrador."
: "Não foi possível carregar os itens.";
const apiMsg = errorPayload?.error || errorPayload?.message || null;
const composed = apiMsg ? `${baseMsg} (${apiMsg})` : `${baseMsg} (HTTP ${res.status})`;
const composed = apiMsg
? `${baseMsg} (${apiMsg})`
: `${baseMsg} (HTTP ${res.status})`;
console.error("Erro na API:", composed, "Payload:", errorPayload);
throw new Error(composed);
}
@@ -179,23 +222,42 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
throw new Error("Resposta inválida da API de itens.");
}
setData(json);
console.log("Itens carregados:", { total: json.total, page: json.page, pageSize: json.pageSize });
console.log("Itens carregados:", {
total: json.total,
page: json.page,
pageSize: json.pageSize,
});
} catch (err: any) {
const durationMs = Math.round(performance.now() - start);
setApiStatus((prev) => ({ code: prev.code, ok: false, durationMs, checkedAt: new Date().toISOString() }));
setApiStatus((prev) => ({
code: prev.code,
ok: false,
durationMs,
checkedAt: new Date().toISOString(),
}));
if (err?.name === "AbortError") {
console.error("Requisição abortada por timeout");
setError("Tempo de resposta excedido. Tente novamente.");
} else {
console.error("Falha ao carregar itens:", err);
setError(err instanceof Error ? err.message : "Falha ao carregar itens.");
setError(
err instanceof Error ? err.message : "Falha ao carregar itens.",
);
}
} finally {
console.groupEnd();
window.clearTimeout(timeoutId);
setLoading(false);
}
}, [search, serverFilter, categoryFilter, rarityFilter, page, pageSize]);
}, [
search,
serverFilter,
categoryFilter,
rarityFilter,
page,
pageSize,
isAdminItemsResponse,
]);
useEffect(() => {
if (searchDebounce.current !== undefined) {
@@ -219,7 +281,9 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
set.add(item.rarity);
}
});
return Array.from(set).sort((a, b) => a.localeCompare(b, "pt-BR", { sensitivity: "base" }));
return Array.from(set).sort((a, b) =>
a.localeCompare(b, "pt-BR", { sensitivity: "base" }),
);
}, [data.items]);
const openEditModal = (item: AdminItemRecord) => {
@@ -257,8 +321,12 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
});
if (!res.ok) {
const error = await res.json().catch(() => ({ error: "Erro desconhecido" }));
throw new Error(error?.error || error?.message || "Falha ao salvar item");
const error = await res
.json()
.catch(() => ({ error: "Erro desconhecido" }));
throw new Error(
error?.error || error?.message || "Falha ao salvar item",
);
}
await fetchItems();
@@ -281,12 +349,18 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
<div className="space-y-1">
<CardTitle>Todos os itens cadastrados</CardTitle>
<CardDescription>
Visualize e edite os registros unificados importados do database do Tibia Wiki.
Itens específicos de servidores podem ser marcados através das tags.
Visualize e edite os registros unificados importados do database
do Tibia Wiki. Itens específicos de servidores podem ser marcados
através das tags.
</CardDescription>
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" onClick={fetchItems} disabled={loading}>
<Button
variant="outline"
size="sm"
onClick={fetchItems}
disabled={loading}
>
<RefreshCw className="mr-2 h-4 w-4" /> Atualizar
</Button>
<Button
@@ -309,7 +383,13 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
value={search}
onChange={(event) => setSearch(event.target.value)}
/>
<Select value={categoryFilter} onValueChange={(value) => { setCategoryFilter(value); setPage(1); }}>
<Select
value={categoryFilter}
onValueChange={(value) => {
setCategoryFilter(value);
setPage(1);
}}
>
<SelectTrigger>
<SelectValue placeholder="Categoria" />
</SelectTrigger>
@@ -322,7 +402,13 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
))}
</SelectContent>
</Select>
<Select value={rarityFilter} onValueChange={(value) => { setRarityFilter(value); setPage(1); }}>
<Select
value={rarityFilter}
onValueChange={(value) => {
setRarityFilter(value);
setPage(1);
}}
>
<SelectTrigger>
<SelectValue placeholder="Raridade" />
</SelectTrigger>
@@ -340,7 +426,9 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
{apiStatus.code !== null && (
<Badge variant="outline" className="text-xs">
API: {apiStatus.code} {apiStatus.ok ? "OK" : "Erro"}
{apiStatus.durationMs !== null ? ` • ${apiStatus.durationMs}ms` : ""}
{apiStatus.durationMs !== null
? ` • ${apiStatus.durationMs}ms`
: ""}
</Badge>
)}
</div>
@@ -350,7 +438,9 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
{error && (
<Card className="border-destructive/40 bg-destructive/10">
<CardContent className="py-3 text-sm text-destructive">{error}</CardContent>
<CardContent className="py-3 text-sm text-destructive">
{error}
</CardContent>
</Card>
)}
@@ -382,26 +472,48 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
<table className="w-full table-auto border-collapse min-w-[960px]">
<thead>
<tr className="bg-muted/70 text-muted-foreground text-xs uppercase tracking-wide">
<th className="border border-border px-3 py-2 text-left">Imagem</th>
<th className="border border-border px-3 py-2 text-left">Nome</th>
<th className="border border-border px-3 py-2 text-left">Categoria</th>
<th className="border border-border px-3 py-2 text-left">Raridade</th>
<th className="border border-border px-3 py-2 text-left">Origem</th>
<th className="border border-border px-3 py-2 text-left">Servidores</th>
<th className="border border-border px-3 py-2 text-left">Atualizado em</th>
<th className="border border-border px-3 py-2 text-left">Ações</th>
<th className="border border-border px-3 py-2 text-left">
Imagem
</th>
<th className="border border-border px-3 py-2 text-left">
Nome
</th>
<th className="border border-border px-3 py-2 text-left">
Categoria
</th>
<th className="border border-border px-3 py-2 text-left">
Raridade
</th>
<th className="border border-border px-3 py-2 text-left">
Origem
</th>
<th className="border border-border px-3 py-2 text-left">
Servidores
</th>
<th className="border border-border px-3 py-2 text-left">
Atualizado em
</th>
<th className="border border-border px-3 py-2 text-left">
Ações
</th>
</tr>
</thead>
<tbody>
{itemsForDisplay.length === 0 ? (
<tr>
<td colSpan={8} className="border border-border px-3 py-6 text-center text-sm text-muted-foreground">
<td
colSpan={8}
className="border border-border px-3 py-6 text-center text-sm text-muted-foreground"
>
Nenhum item encontrado com os filtros atuais.
</td>
</tr>
) : (
itemsForDisplay.map((item) => (
<tr key={item.item_id} className="odd:bg-background even:bg-muted/40 hover:bg-primary/5 transition-colors">
<tr
key={item.item_id}
className="odd:bg-background even:bg-muted/40 hover:bg-primary/5 transition-colors"
>
<td className="border border-border px-3 py-2 align-middle">
{item.image_url ? (
<div className="relative h-12 w-12 overflow-hidden rounded border">
@@ -413,7 +525,9 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
/>
</div>
) : (
<span className="text-xs text-muted-foreground">Sem imagem</span>
<span className="text-xs text-muted-foreground">
Sem imagem
</span>
)}
</td>
<td className="border border-border px-3 py-2 align-top">
@@ -422,14 +536,21 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
#{item.item_id}
</div>
</td>
<td className="border border-border px-3 py-2 align-top text-sm">{item.category}</td>
<td className="border border-border px-3 py-2 align-top text-sm">
{item.category}
</td>
<td className="border border-border px-3 py-2 align-top text-sm">
{item.rarity ? (
<Badge variant="outline" className="uppercase text-[10px]">
<Badge
variant="outline"
className="uppercase text-[10px]"
>
{item.rarity}
</Badge>
) : (
<span className="text-muted-foreground text-xs">-</span>
<span className="text-muted-foreground text-xs">
-
</span>
)}
</td>
<td className="border border-border px-3 py-2 align-top text-[11px] text-muted-foreground">
@@ -440,17 +561,27 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
{item.servers.length > 0 ? (
<div className="flex flex-wrap gap-1">
{item.servers.map((serverTag) => (
<Badge key={`${item.item_id}-${serverTag}`} variant="secondary" className="text-[10px] uppercase">
<Badge
key={`${item.item_id}-${serverTag}`}
variant="secondary"
className="text-[10px] uppercase"
>
{serverTag}
</Badge>
))}
</div>
) : (
<span className="text-muted-foreground text-xs">Global</span>
<span className="text-muted-foreground text-xs">
Global
</span>
)}
</td>
<td className="border border-border px-3 py-2 align-top text-xs text-muted-foreground">
{format(new Date(item.updated_at), "dd/MM/yyyy HH:mm", { locale: ptBR })}
{format(
new Date(item.updated_at),
"dd/MM/yyyy HH:mm",
{ locale: ptBR },
)}
</td>
<td className="border border-border px-3 py-2 align-top">
<Button
@@ -471,7 +602,8 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
</div>
<div className="flex items-center justify-between text-xs text-muted-foreground">
<span>
Página {page} • Total de {Math.max(1, Math.ceil(data.total / pageSize))}
Página {page} • Total de{" "}
{Math.max(1, Math.ceil(data.total / pageSize))}
</span>
<div className="flex items-center gap-2">
<Button
@@ -485,8 +617,16 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
<Button
variant="outline"
size="sm"
onClick={() => setPage((prev) => (prev >= Math.ceil(data.total / pageSize) ? prev : prev + 1))}
disabled={page >= Math.ceil(data.total / pageSize) || loading}
onClick={() =>
setPage((prev) =>
prev >= Math.ceil(data.total / pageSize)
? prev
: prev + 1,
)
}
disabled={
page >= Math.ceil(data.total / pageSize) || loading
}
>
Próxima
</Button>
@@ -510,4 +650,3 @@ export function AdminItemManager({ initialData, locale }: AdminItemManagerProps)
</div>
);
}