import { notFound } from 'next/navigation' import { getTranslations, setRequestLocale } from 'next-intl/server' import { Link } from '@/i18n/navigation' import { getCharacter, getCharacterEquipment, getCharacterCustomizations, getCharacterStats, getCharacterProfessions, getItemIcons, getAchievementPoints, getCharacterTalentGroups, getCharacterSpecs, getSpellIcons, getActivityPage, getAchievementOverview, getRaidProgress, getDungeonProgress, getRealmName, getQuestCount, getQuestPage, getQuestStatusCounts, getCharacterCollection, getReputations, getPvpRank, getCharacterArena, getPvpCurrency, getBattlegroundHistory, } from '@/lib/armory' import { raceName, className, classColor, factionOf, classPower, XP_FOR_LEVEL } from '@/lib/wow-data' import { PageShell } from '@/components/PageShell' import { ArmoryPaperdoll, type SheetItem } from '@/components/ArmoryPaperdoll' import { wowheadIcon } from '@/lib/wowhead' import { ArmoryTalentTab } from '@/components/ArmoryTalentTab' import { ArmoryAchievements } from '@/components/ArmoryAchievements' import { ArmoryRaids } from '@/components/ArmoryRaids' import { ArmoryQuests } from '@/components/ArmoryQuests' import { ArmoryTabs, type ArmoryTab } from '@/components/ArmoryTabs' import { ArmoryReputations } from '@/components/ArmoryReputations' import { ArmoryCollection } from '@/components/ArmoryCollection' import { ArmoryPvpRank } from '@/components/ArmoryPvpRank' import { ArmoryBattlegrounds } from '@/components/ArmoryBattlegrounds' export const dynamic = 'force-dynamic' const MAX_LEVEL = 80 export default async function CharacterPage({ params }: { params: Promise<{ locale: string; guid: string }> }) { const { locale, guid } = await params setRequestLocale(locale) const t = await getTranslations('Armory') const id = Number(guid) const character = await getCharacter(id) if (!character) notFound() const [equipment, customizations, stats, professions, achievementPoints, talentGroups, specs, activityData, questCount] = await Promise.all([ getCharacterEquipment(id, locale), getCharacterCustomizations(id), getCharacterStats(id), getCharacterProfessions(id, locale), getAchievementPoints(id), getCharacterTalentGroups(id, character.class, locale), getCharacterSpecs(id, character.class, locale), getActivityPage(id, locale, 1, 10, '', ''), getQuestCount(id), ]) const [questData, mounts, pets, questCounts, reputations, pvpRankData, arena, pvpCurrency, bgHistory, achOverview, raidProgress, dungeonProgress, realmName] = await Promise.all([ getQuestPage(id, locale, 1, 15, '', '', 'all'), getCharacterCollection(id, locale, 'mounts'), getCharacterCollection(id, locale, 'pets'), getQuestStatusCounts(id), getReputations(id, locale), getPvpRank(1, 30, ''), getCharacterArena(id), getPvpCurrency(id), getBattlegroundHistory(id), getAchievementOverview(id, factionOf(character.race), locale), getRaidProgress(id, locale), getDungeonProgress(id, locale), getRealmName(), ]) const qGrand = questCounts.rewarded + questCounts.complete + questCounts.inprogress + questCounts.failed const icons = await getItemIcons(equipment.map((e) => e.entry)) const spellIcons = await getSpellIcons(talentGroups.spellIds) const items: SheetItem[] = equipment.map((e) => ({ slot: e.slot, entry: e.entry, name: e.name, quality: e.quality, itemLevel: e.itemLevel, inventoryType: e.inventoryType, displayId: e.displayId, transmogDisplayId: e.transmogDisplayId, ench: e.ench, gems: e.gems, icon: icons[e.entry] ?? null, })) const gear = equipment.filter((e) => e.slot !== 3 && e.slot !== 18 && e.itemLevel > 0) const avgItemLevel = gear.length ? Math.round(gear.reduce((s, e) => s + e.itemLevel, 0) / gear.length) : 0 const faction = factionOf(character.race) const factionLabel = faction === 'alliance' ? t('alliance') : faction === 'horde' ? t('horde') : '' const power = classPower(character.class, locale) const xpNext = character.level < MAX_LEVEL ? XP_FOR_LEVEL[character.level] ?? 0 : 0 const xpPct = character.level >= MAX_LEVEL ? 100 : xpNext > 0 ? Math.min(100, Math.round((character.xp / xpNext) * 100)) : 0 const statRows = stats ? [ { label: t('strength'), value: `${stats.strength}` }, { label: t('agility'), value: `${stats.agility}` }, { label: t('stamina'), value: `${stats.stamina}` }, { label: t('intellect'), value: `${stats.intellect}` }, { label: t('armor'), value: `${stats.armor}` }, { label: t('attackPower'), value: `${stats.attackPower}` }, { label: t('rangedAttackPower'), value: `${stats.rangedAttackPower}` }, { label: t('spellPower'), value: `${stats.spellPower}` }, { label: t('critChance'), value: `${stats.critPct.toFixed(2)}%` }, { label: t('spellCrit'), value: `${stats.spellCritPct.toFixed(2)}%` }, { label: t('dodge'), value: `${stats.dodgePct.toFixed(2)}%` }, { label: t('parry'), value: `${stats.parryPct.toFixed(2)}%` }, { label: t('block'), value: `${stats.blockPct.toFixed(2)}%` }, { label: t('resilience'), value: `${stats.resilience}` }, ] : [] // ---- Pestaña Equipación: modelo + spec + stats/profesiones/PvP ---- const equipTab = ( <> {specs.primary && (

{t('specialization')}

{specs.primary.name}
{specs.primary.dist.join(' / ')}
{specs.secondary ? ( <>
{specs.secondary.name}
{specs.secondary.dist.join(' / ')}
) : ( N/A )}
)}

{t('stats')}

{stats ? (
{statRows.map((r) => (
{r.label} {r.value}
))}
) : (

{t('statsPending')}

)}

{t('professions')}

{professions.length ? (
{(['primary', 'secondary'] as const).map((kind) => { const list = professions.filter((p) => p.kind === kind) if (!list.length) return null return (

{kind === 'primary' ? t('primaryProfs') : t('secondaryProfs')}

{list.map((p) => (
{p.name} {p.rank ? · {p.rank} : null} {p.value} / {p.max}
))}
) })}
) : (

{t('noProfessions')}

)}
) // ---- Pestaña PvP (sub-pestañas: Estadísticas + Rank mundo) ---- const pvpStatsSub = (
{character.totalKills.toLocaleString(locale)} {t('totalKills')}
{character.todayKills.toLocaleString(locale)} {t('todayKills')}
{character.yesterdayKills.toLocaleString(locale)} {t('yesterdayKills')}
{pvpCurrency.honor.toLocaleString(locale)} {t('honorPoints')}
{pvpCurrency.arena.toLocaleString(locale)} {t('arenaPoints')}
) const arenaSub = (
{arena.map((b) => { const losses = Math.max(0, b.seasonGames - b.seasonWins) const winRate = b.seasonGames ? Math.round((b.seasonWins / b.seasonGames) * 100) : 0 return (
{b.bracket}
0 ? 'rated' : 'none'}`}> {b.seasonGames > 0 ? t('arenaRated') : t('arenaUnrated')}
{b.personalRating}
{t('arenaRating')}
{b.seasonGames > 0 ? (
{t('arenaSeason')} {b.seasonWins} / {losses} ·{' '} {winRate}%
{t('arenaWeek')} {b.weekWins} / {Math.max(0, b.weekGames - b.weekWins)}
MMR {b.mmr}
{b.rank > 0 ? (
{t('arenaRank')} #{b.rank}
) : null}
) : (
{t('arenaNoGames')}
)}
) })}
) const pvpTab = ( ), }, { key: 'pvprank', label: t('pvpRank'), icon: 'fas fa-trophy', content: ( ), }, ]} /> ) const raidsTab = ( ) const dungeonsTab = ( ) // ---- Pestaña Talentos (toggle Primaria/Secundaria + árbol + glifos) ---- const talentsTab = talentGroups.groups.length ? ( ) : (

{t('noTalents')}

) // ---- Pestaña Logros ---- const logrosTab = ( ) const questsTab = (
) const mountsTab = ( ) const petsTab = ( ) const repsTab = ( ) const tabs: ArmoryTab[] = [ { key: 'equip', label: t('tabEquip'), icon: 'fas fa-shield-alt', content: equipTab }, { key: 'talents', label: t('talents'), icon: 'fas fa-tree', count: talentGroups.groups[talentGroups.activeIndex]?.total, content: talentsTab, }, { key: 'achievements', label: t('achievements'), icon: 'fas fa-trophy', count: activityData.total, content: logrosTab }, { key: 'raids', label: t('raids'), icon: 'fas fa-dragon', content: raidsTab }, { key: 'dungeons', label: t('dungeons'), icon: 'fas fa-dungeon', content: dungeonsTab }, { key: 'quests', label: t('quests'), icon: 'fas fa-scroll', count: questCount, content: questsTab }, { key: 'reputations', label: t('reputations'), icon: 'fas fa-handshake', count: reputations.length, content: repsTab }, { key: 'pvp', label: t('pvp'), icon: 'fas fa-khanda', content: pvpTab }, { key: 'mounts', label: t('mounts'), icon: 'fas fa-horse', count: mounts.length, content: mountsTab }, { key: 'pets', label: t('pets'), icon: 'fas fa-paw', count: pets.length, content: petsTab }, ] return (

← {t('backToArmory')}

{character.name} {avgItemLevel > 0 ? ( {' '} ({t('itemLevel')} {avgItemLevel}) ) : null}

{t('level')} {character.level} · {raceName(character.race, locale)} ·{' '} {className(character.class, locale)}
{factionLabel && {factionLabel}} {character.online ? t('online') : t('offline')} {character.guildName && character.guildId && ( <{character.guildName}> )} {realmName && {realmName}} {achievementPoints.toLocaleString(locale)}
{t('level')} {character.level >= MAX_LEVEL ? `${character.level} (MAX)` : character.level}
) }