08c36619f0
restore_character_view pasa de redirect+messages a JSON en POST: guid -> SOAP
.char del restore -> {success, message, redirect}. RestoreCharacterForm.tsx
(select de personajes borrados + botón) hace fetch y en éxito redirige a my-account.
La vista pasa character_data por json_script; se conserva la rama sin personajes.
Verificado: check OK, ambas ramas renderizan, redirige a login sin sesión.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import { useState } from 'react'
|
|
|
|
interface DeletedChar {
|
|
guid: number
|
|
name: string
|
|
level: number
|
|
}
|
|
|
|
interface Props {
|
|
url: string
|
|
csrfToken: string
|
|
characters: DeletedChar[]
|
|
}
|
|
|
|
export function RestoreCharacterForm({ url, csrfToken, characters }: Props) {
|
|
const [guid, setGuid] = useState('')
|
|
const [busy, setBusy] = useState(false)
|
|
const [done, setDone] = useState(false)
|
|
const [message, setMessage] = useState<{ ok: boolean; html: string } | null>(null)
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault()
|
|
if (busy || done || !guid) return
|
|
setBusy(true)
|
|
setMessage(null)
|
|
|
|
const body = new URLSearchParams()
|
|
body.set('guid', guid)
|
|
body.set('csrfmiddlewaretoken', csrfToken)
|
|
|
|
try {
|
|
const resp = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-CSRFToken': csrfToken,
|
|
Accept: 'application/json',
|
|
},
|
|
credentials: 'same-origin',
|
|
body: body.toString(),
|
|
})
|
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
|
const data: { success?: boolean; message?: string; redirect?: string } = await resp.json()
|
|
setMessage({ ok: !!data.success, html: data.message || '' })
|
|
if (data.success && data.redirect) {
|
|
setDone(true)
|
|
setTimeout(() => {
|
|
window.location.href = data.redirect as string
|
|
}, 2000)
|
|
} else {
|
|
setBusy(false)
|
|
}
|
|
} catch {
|
|
setMessage({ ok: false, html: '<span class="red-form-response">Error en el servidor. Inténtalo más tarde.</span>' })
|
|
setBusy(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<form onSubmit={handleSubmit} acceptCharset="utf-8">
|
|
<select name="guid" value={guid} onChange={(e) => setGuid(e.target.value)} required>
|
|
<option value="" disabled>Selecciona un personaje</option>
|
|
{characters.map((c) => (
|
|
<option value={c.guid} key={c.guid}>
|
|
{c.name} (Nivel {c.level})
|
|
</option>
|
|
))}
|
|
</select>
|
|
<br />
|
|
<button type="submit" disabled={busy || done || !guid}>
|
|
{done ? 'Hecho' : busy ? 'Restaurando…' : 'Restaurar Personaje'}
|
|
</button>
|
|
</form>
|
|
<div className="alert-message" style={{ display: message ? 'block' : 'none' }}>
|
|
{message && <span dangerouslySetInnerHTML={{ __html: message.html }} />}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|