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,118 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export type ReputationEntry = {
|
||||
faction: number
|
||||
name: string
|
||||
standing: number
|
||||
rank: string
|
||||
rankIndex: number
|
||||
color: string
|
||||
cur: number
|
||||
max: number
|
||||
}
|
||||
|
||||
/** Pestaña de Reputaciones: búsqueda en tiempo real + paginación + barra de rango/progreso. */
|
||||
export function ArmoryReputations({
|
||||
reputations,
|
||||
pageSize,
|
||||
labels,
|
||||
}: {
|
||||
reputations: ReputationEntry[]
|
||||
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 ? reputations.filter((r) => r.name.toLowerCase().includes(query)) : reputations
|
||||
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 armory-reps-panel">
|
||||
<div className="armory-quest-title">
|
||||
<i className="fas fa-handshake" /> {labels.title}{' '}
|
||||
<span className="armory-quest-title-count">({reputations.length})</span>
|
||||
</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-reps">
|
||||
{pageItems.map((r) => (
|
||||
<div className="armory-rep" key={r.faction}>
|
||||
<div className="armory-rep-head">
|
||||
<span className="armory-rep-name">{r.name}</span>
|
||||
<span className="armory-rep-standing">
|
||||
<span className="armory-rep-rank" style={{ color: r.color }}>
|
||||
{r.rank}
|
||||
</span>
|
||||
{r.rankIndex < 7 ? (
|
||||
<span className="armory-rep-nums">
|
||||
{' '}
|
||||
{r.cur.toLocaleString()} / {r.max.toLocaleString()}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
<div className="armory-rep-bar">
|
||||
<span style={{ width: `${Math.round((r.cur / Math.max(1, r.max)) * 100)}%`, background: r.color }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</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