import { getTranslations, setRequestLocale } from 'next-intl/server' import { redirect, Link } from '@/i18n/navigation' import { getSession } from '@/lib/session' import { getRealmName, realmSlug } from '@/lib/realm' import { getAccountDashboard, getAccountRank } from '@/lib/account' import { reconcileSumUpCheckouts } from '@/lib/sumup' import { getGameAccounts } from '@/lib/auth' import { isAdmin } from '@/lib/admin' import { PageShell } from '@/components/PageShell' import { AccountTools } from '@/components/AccountTools' import { GameAccountSwitcher } from '@/components/GameAccountSwitcher' export const dynamic = 'force-dynamic' // Enmascara el correo (siempre en minúsculas): primeros caracteres + dominio. function maskEmail(e: string): string { if (!e) return '' e = e.toLowerCase() const [local, domain] = e.split('@') if (!domain) return e const visible = local.slice(0, Math.min(6, Math.max(1, local.length - 2))) return `${visible}***@${domain}` } // Nombre visible de la cuenta de juego: «15#1» → «WOW1» (el nº tras # es la // cuenta de juego; con varias serían WOW1, WOW2, WOW3…). function wowAccountName(username: string): string { const i = username.indexOf('#') return i >= 0 ? `WOW${username.slice(i + 1)}` : username } const ICONS = '/ns-themes/ns-ryu/ns-images/ns-icons' export default async function AccountPage({ params }: { params: Promise<{ locale: string }> }) { const { locale } = await params setRequestLocale(locale) const t = await getTranslations('Account') const session = await getSession() if (!session.bnetId) redirect({ href: '/log-in', locale }) if (!session.username) redirect({ href: '/select-account', locale }) // Acredita cualquier pago SumUp pendiente de esta cuenta (SumUp no tiene // webhooks) antes de leer el saldo de PD, para que se muestre actualizado. if (session.accountId) await reconcileSumUpCheckouts({ accountId: session.accountId }).catch(() => {}) const data = await getAccountDashboard(session, locale) const admin = await isAdmin(session) // El rango se basa en el TOTAL DONADO (Stripe + SumUp, sin bajar al gastar); // se usa el máximo con el saldo actual para no rebajar el rango a nadie. const rank = getAccountRank(Math.max(data.donatedPD, data.dp)) // Cuentas de juego de la Battle.net (para el selector si hay más de una). const games = await getGameAccounts(session.bnetId ?? 0) return ( {/* Información: dos fieldsets como en Django */}

{t('infoTitle')}

{t('basicData')}

{t('accountName')}:{' '}

{t('regMail')}: {maskEmail(data.regMail)}

{t('currentMail')}: {maskEmail(data.email)}

{t('joindate')}: {data.joindate}

{t('lastIpWeb')}: {data.lastIp}

{t('lastIpServer')}: {data.lastAttemptIp}

{t('accountState')}
{/* eslint-disable-next-line @next/next/no-img-element */} {`${t('level')}

{t('dp')}: {data.dp} | {t('vp')}: {data.vp}

{t('securityToken')}: {data.securityTokenDate ? t('requested') : t('notRequested')}

{t('twofaLabel')}: {t('twofaOff')}

{t('banned')}: {data.isBanned ? t('yes') : t('no')} ({t('banHistoryLink')})

{t('recruited')}: {data.isRecruited ? t('yes') : t('no')}

{t('recruitedFriends')}: {data.recruitedCount}

{/* Mis personajes: rejilla con char-box como en Django */} {data.characters.length > 0 && (

{t('characters')}

{data.characters.map((c) => (
{/* eslint-disable-next-line @next/next/no-img-element */} {c.name}

{c.name}

{t('level')} {c.level}

{c.zone}

{/* eslint-disable-next-line @next/next/no-img-element */} {c.gold}g{' '} {/* eslint-disable-next-line @next/next/no-img-element */} {c.silver}s{' '} {/* eslint-disable-next-line @next/next/no-img-element */} {c.copper}c

))}
)} {/* Utilidades y Herramientas: paneles plegables como en Django */}

{t('toolsTitle')}

) }