'use client' import { useMemo, useState } from 'react' import { CharacterSelect } from '@/components/CharacterSelect' import { Turnstile } from '@/components/Turnstile' const QUEST_DB = 'https://wotlk.novawow.com' export interface QuestCharacter { name: string classCss: string level: number } export interface QuestOptionClient { key: string classCss: string | null label: string minLevel: number hasData: boolean } interface QuestStatus { quest: number state: 'rewarded' | 'inProgress' | 'none' } function trackerError(error?: string, minutes?: number): string { switch (error) { case 'missingFields': return 'Selecciona un personaje y una opción.' case 'characterNotOwned': return 'El personaje seleccionado no es válido.' case 'characterOnline': return 'El personaje debe estar desconectado.' case 'wrongClass': return 'Esa opción no corresponde a la clase del personaje.' case 'levelTooLow': return 'El personaje no cumple el nivel mínimo de esa opción.' case 'invalidQuestId': return 'Introduce un ID de misión válido.' case 'invalidOption': case 'noData': return 'Esa opción no está disponible todavía.' case 'cooldown': return `Debes esperar ${minutes ?? 0} min para repetir esta búsqueda en este personaje.` case 'captcha': return 'Verifica que no eres un robot.' case 'notAuthenticated': return 'Tu sesión ha expirado. Inicia sesión de nuevo.' default: return 'No se pudo completar la búsqueda. Inténtalo de nuevo.' } } export function QuestTrackerForm({ characters, options }: { characters: QuestCharacter[]; options: QuestOptionClient[] }) { const [character, setCharacter] = useState('') const [option, setOption] = useState('') const [questId, setQuestId] = useState('') const [captcha, setCaptcha] = useState('') const [captchaKey, setCaptchaKey] = useState(0) const [busy, setBusy] = useState(false) const [message, setMessage] = useState<{ ok: boolean; text: string } | null>(null) const [result, setResult] = useState<{ label: string; statuses: QuestStatus[] } | null>(null) const selected = characters.find((c) => c.name === character) // Opciones que el personaje elegido puede rastrear (clase + nivel + con datos), // más la búsqueda por misión específica (nivel >= 10). const available = useMemo(() => { if (!selected) return [] as { key: string; label: string }[] const list = options .filter((o) => o.hasData && (o.classCss === null || o.classCss === selected.classCss) && selected.level >= o.minLevel) .map((o) => ({ key: o.key, label: o.label })) if (selected.level >= 10) list.push({ key: 'specific', label: 'Por misión específica' }) return list }, [selected, options]) function onCharacter(v: string) { setCharacter(v) setOption('') setQuestId('') setResult(null) setMessage(null) } async function handleSubmit(e: React.FormEvent) { e.preventDefault() if (busy || !character || !option) return setBusy(true) setMessage(null) setResult(null) try { const res = await fetch('/api/character/quest', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ character, option, questId: option === 'specific' ? Number(questId) : undefined, turnstile: captcha }), }) const data: { success?: boolean; error?: string; minutes?: number; label?: string; statuses?: QuestStatus[] } = await res.json() if (data.success && data.statuses) { setResult({ label: data.label ?? '', statuses: data.statuses }) } else { setMessage({ ok: false, text: trackerError(data.error, data.minutes) }) } } catch { setMessage({ ok: false, text: trackerError() }) } finally { setBusy(false) setCaptcha('') setCaptchaKey((k) => k + 1) } } if (characters.length === 0) { return (

No hay personajes disponibles para esta herramienta
) } const stateText = (s: QuestStatus['state']) => s === 'rewarded' ? { text: 'Completada', cls: 'green-info2' } : s === 'inProgress' ? { text: 'En curso', cls: 'yellow-info' } : { text: 'Sin empezar', cls: 'third-brown' } return (


{option === 'specific' && ( <> setQuestId(e.target.value)} required />
)}
{message && {message.text}} {result && (

{result.label}

{result.statuses.map((s) => { const st = stateText(s.state) return ( ) })}
Misión #{s.quest} {st.text}
)}
) }