Armería: PvP campos de batalla, logros por categoría, bandas/mazmorras y página de hermandad
- PvP: pestaña Campos de batalla (pvpstats) con resultado, stats por partida y fecha/hora. - Logros: rejilla por categoría con anillos de progreso + detalle por categoría (datos de Achievement.db2 + Achievement_Category.db2, iconos vía wowhead). - Bandas: progreso por dificultad (10/25/heroico) con jefes reales de DungeonEncounter.db2 y tooltip de jefes; agrupado por expansión (Clásico/TBC/WotLK). - Mazmorras: mismo sistema, Normal + Heroico, Clásico/TBC/WotLK. - Página de hermandad estilo armería: cabecera (emblema, puntos = unión de logros de miembros sin duplicar, nº miembros, fundada), pestañas Hermandad/Logros, roster con iconos de clase/raza, columna Función (rol por spec), filtros clase/función, orden por rango/puntos/nivel de objeto/nivel/nombre, paginación. - Barra lateral: mensaje diario con login (iron-session, gated por pertenencia), logros recientes y desgloses de clases y funciones. - Cabecera de personaje: enlace a la hermandad + nombre del reino. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { WowheadRefresh } from './WowheadRefresh'
|
||||
import { wowheadIcon, wowheadUrl, wowheadData } from '@/lib/wowhead'
|
||||
|
||||
export type CollectionEntry = { id: number; name: string; icon: string }
|
||||
|
||||
/** Colección (monturas/mascotas) con búsqueda en tiempo real y paginación en cliente. */
|
||||
export function ArmoryCollection({
|
||||
items,
|
||||
locale,
|
||||
pageSize,
|
||||
labels,
|
||||
}: {
|
||||
items: CollectionEntry[]
|
||||
locale: string
|
||||
pageSize: number
|
||||
labels: { title: string; search: string; empty: string }
|
||||
}) {
|
||||
const [q, setQ] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const query = q.trim().toLowerCase()
|
||||
const filtered = query ? items.filter((i) => i.name.toLowerCase().includes(query)) : items
|
||||
const pages = Math.max(1, Math.ceil(filtered.length / pageSize))
|
||||
const p = Math.min(page, pages)
|
||||
const pageItems = filtered.slice((p - 1) * pageSize, (p - 1) * pageSize + pageSize)
|
||||
|
||||
const win = 2
|
||||
const from = Math.max(1, p - win)
|
||||
const to = Math.min(pages, p + win)
|
||||
const nums: number[] = []
|
||||
for (let i = from; i <= to; i++) nums.push(i)
|
||||
|
||||
return (
|
||||
<div className="armory-panel">
|
||||
<WowheadRefresh dep={`${p}-${q}-${pageItems.length}`} />
|
||||
<div className="armory-collection-head">{labels.title}</div>
|
||||
<input
|
||||
className="armory-ach-search"
|
||||
value={q}
|
||||
onChange={(e) => {
|
||||
setQ(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
placeholder={labels.search}
|
||||
/>
|
||||
{filtered.length === 0 ? (
|
||||
<p className="armory-hint">{labels.empty}</p>
|
||||
) : (
|
||||
<div className="armory-collection">
|
||||
{pageItems.map((c) => (
|
||||
<a
|
||||
className="armory-coll-card"
|
||||
key={c.id}
|
||||
href={wowheadUrl('spell', c.id, locale)}
|
||||
data-wowhead={wowheadData('spell', c.id, locale)}
|
||||
data-wh-icon-size="false"
|
||||
data-wh-rename-link="false"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="armory-coll-icon" style={{ backgroundImage: `url(${wowheadIcon(c.icon, 'small')})` }} />
|
||||
<span className="armory-coll-name">{c.name}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pages > 1 && (
|
||||
<div className="armory-pager">
|
||||
<button onClick={() => setPage(p - 1)} disabled={p <= 1} type="button" aria-label="Anterior">
|
||||
‹
|
||||
</button>
|
||||
{from > 1 && (
|
||||
<>
|
||||
<button onClick={() => setPage(1)} type="button">
|
||||
1
|
||||
</button>
|
||||
{from > 2 && <span className="armory-pager-dots">…</span>}
|
||||
</>
|
||||
)}
|
||||
{nums.map((n) => (
|
||||
<button key={n} className={n === p ? 'active' : ''} onClick={() => setPage(n)} type="button">
|
||||
{n}
|
||||
</button>
|
||||
))}
|
||||
{to < pages && (
|
||||
<>
|
||||
{to < pages - 1 && <span className="armory-pager-dots">…</span>}
|
||||
<button onClick={() => setPage(pages)} type="button">
|
||||
{pages}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<button onClick={() => setPage(p + 1)} disabled={p >= pages} type="button" aria-label="Siguiente">
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user