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, getCharacterTalents, getCharacterSpecs, getSpellIcons, getActivityPage, } from '@/lib/armory' import { raceName, className, classColor, factionOf, classPower } from '@/lib/wow-data' import { PageShell } from '@/components/PageShell' import { ArmoryPaperdoll, type SheetItem } from '@/components/ArmoryPaperdoll' import { wowheadIcon } from '@/lib/wowhead' import { ArmoryTalents } from '@/components/ArmoryTalents' import { ArmoryActivity } from '@/components/ArmoryActivity' import { WowheadRefresh } from '@/components/WowheadRefresh' export const dynamic = 'force-dynamic' 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, talents, specs, activityData] = await Promise.all([ getCharacterEquipment(id, locale), getCharacterCustomizations(id), getCharacterStats(id), getCharacterProfessions(id, locale), getAchievementPoints(id), getCharacterTalents(id, character.class, locale), getCharacterSpecs(id, character.class, locale), getActivityPage(id, locale, 1, 10, ''), ]) const icons = await getItemIcons(equipment.map((e) => e.entry)) const spellIcons = talents ? await getSpellIcons(talents.trees.flatMap((tr) => tr.talents.map((c) => c.spell))) : {} 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 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}` }, ] : [] 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}> )} {achievementPoints.toLocaleString(locale)}
{specs.primary && (

{t('specialization')}

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

{t('talents')}

)} {activityData.entries.length > 0 && (

{t('recentActivity')}

)}

{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')}

)}
) }