beba7301a1
- 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>
104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
'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>
|
||
)
|
||
}
|