'use client' import { useState } from 'react' import { Turnstile } from '@/components/Turnstile' import { CharacterSelect, type CharOption } from '@/components/CharacterSelect' function tradeError(error?: string): string { switch (error) { case 'missingFields': return 'Rellena todos los campos.' case 'invalidAmount': return 'Las cantidades deben ser números enteros entre 1 y 99999.' case 'characterNotFound': case 'characterNotOwned': return 'El personaje seleccionado no es válido.' case 'captcha': return 'Verifica que no eres un robot.' case 'wrongPassword': return 'La contraseña de tu cuenta es incorrecta.' case 'invalidToken': return 'El token de seguridad no es válido.' case 'insufficientPoints': return 'No tienes suficientes PD disponibles para crear este código.' case 'codeNotFound': return 'No existe ningún código con ese ID.' case 'codeUnavailable': return 'El código ya no está disponible (canjeado o expirado).' case 'cannotRedeemOwn': return 'No puedes canjear tu propio código.' case 'characterOnline': return 'El personaje debe estar desconectado para poder cobrar el oro.' case 'insufficientGold': return 'El personaje no tiene suficiente oro.' case 'creatorInsufficient': return 'El creador del código ya no tiene suficientes PD.' case 'locked': return 'Espera unos segundos antes de canjear otro código.' case 'deliveryFailed': return 'No se pudo entregar el oro (servidor de juego no disponible). Inténtalo de nuevo más tarde.' case 'notAuthenticated': return 'Tu sesión ha expirado. Inicia sesión de nuevo.' default: return 'No se pudo completar la operación. Inténtalo de nuevo.' } } function PasswordInput({ id, value, onChange, placeholder, maxLength, }: { id: string value: string onChange: (v: string) => void placeholder: string maxLength: number }) { const [show, setShow] = useState(false) return ( <> onChange(e.target.value)} placeholder={placeholder} />{' '} setShow((s) => !s)} /> ) } /* ---------------------------------- VENTA ---------------------------------- */ function SellForm({ characters, visible }: { characters: CharOption[]; visible: boolean }) { const [points, setPoints] = useState('') const [gold, setGold] = useState('') const [character, setCharacter] = useState('') const [password, setPassword] = useState('') const [token, setToken] = useState('') const [captcha, setCaptcha] = useState('') const [busy, setBusy] = useState(false) const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null) const [created, setCreated] = useState<{ code: string; points: number; gold: number } | null>(null) async function handleSubmit(e: React.FormEvent) { e.preventDefault() if (busy) return setBusy(true) setMessage(null) setCreated(null) try { const res = await fetch('/api/dpoints/trade/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ points: Number(points), gold: Number(gold), character, password, token: token.trim(), turnstile: captcha, }), }) const data: { success?: boolean; error?: string; code?: { code: string; points: number; gold: number } } = await res.json() if (data.success && data.code) { setCreated(data.code) setMessage({ ok: true, text: '¡Código creado!' }) setPoints('') setGold('') setPassword('') setToken('') } else { setMessage({ ok: false, text: tradeError(data.error) }) } } catch { setMessage({ ok: false, text: tradeError() }) } finally { setBusy(false) } } return (
¿Cuánto valdrá el código en PD?
setPoints(e.target.value)} placeholder="Valor del código en PD" required />
¿Cuánto costará el código en oro?
setGold(e.target.value)} placeholder="Valor del código en oro" required />
¿A qué personaje se enviará el oro?

setToken(e.target.value)} placeholder="Token de seguridad" required />

{message && {message.text}} {created && (
Código creado

ID del código: {created.code}

Valor: {created.points} PD por {created.gold} de oro

Envía este ID al comprador. El código caduca en 1 hora.

)}
) } /* ---------------------------------- COMPRA --------------------------------- */ function BuyForm({ characters, visible }: { characters: CharOption[]; visible: boolean }) { const [code, setCode] = useState('') const [character, setCharacter] = useState('') const [password, setPassword] = useState('') const [token, setToken] = useState('') const [captcha, setCaptcha] = useState('') const [busy, setBusy] = useState(false) const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null) const [checkInfo, setCheckInfo] = useState<{ points: number; gold: number } | null>(null) async function handleCheck() { if (busy || !code.trim()) return setBusy(true) setMessage(null) setCheckInfo(null) try { const res = await fetch('/api/dpoints/trade/check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ code: code.trim() }), }) const data: { success?: boolean; error?: string; points?: number; gold?: number } = await res.json() if (data.success && data.points != null && data.gold != null) { setCheckInfo({ points: data.points, gold: data.gold }) setMessage({ ok: true, text: `Este código vale ${data.points} PD y cuesta ${data.gold} de oro.` }) } else { setMessage({ ok: false, text: tradeError(data.error) }) } } catch { setMessage({ ok: false, text: tradeError() }) } finally { setBusy(false) } } async function handleRedeem(e: React.FormEvent) { e.preventDefault() if (busy) return if (!window.confirm('Vas a canjear este código. Esta acción no es reversible. ¿Deseas continuar?')) return setBusy(true) setMessage(null) try { const res = await fetch('/api/dpoints/trade/redeem', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ code: code.trim(), character, password, token: token.trim(), turnstile: captcha }), }) const data: { success?: boolean; error?: string; points?: number; gold?: number } = await res.json() if (data.success) { setMessage({ ok: true, text: `¡Canje correcto! Recibiste ${data.points} PD y se retiraron ${data.gold} de oro de ${character}.` }) setCode('') setPassword('') setToken('') setCheckInfo(null) } else { setMessage({ ok: false, text: tradeError(data.error) }) } } catch { setMessage({ ok: false, text: tradeError() }) } finally { setBusy(false) } } return (
{checkInfo && ( )}
Ingresa el ID del código de PD
setCode(e.target.value)} placeholder="Código de PD" required />

Vale {checkInfo.points} PD · Cuesta {checkInfo.gold} de oro

¿De qué personaje se cobrará el oro?

setToken(e.target.value)} placeholder="Token de seguridad" required />

{message && {message.text}}
) } export function TradePointsForm({ characters }: { characters: CharOption[] }) { // Como el original: al cargar no se muestra ningún formulario; se revela al // pulsar VENTA o COMPRA. const [active, setActive] = useState<'sell' | 'buy' | null>(null) if (characters.length === 0) { return (

No tienes personajes en esta cuenta.
) } return (




) }