import React, { useState, useEffect } from "react"; import { createRoot } from "react-dom/client"; import "./pregunta.css"; // ── Types & constants ───────────────────────────────────────────────────────── type CreditInfo = { token: string; username: string; expiresAt: number; tier: string; questionsLeft: number | null; // null = unlimited }; const STORAGE_KEY = "argumentes_credito"; const TIERS = [ { id: "basico", label: "10 preguntas", sublabel: "30 días", price: "0,99€", maxQuestions: 10 }, { id: "pro", label: "200 preguntas", sublabel: "30 días", price: "9,99€", maxQuestions: 200 }, { id: "ilimitado", label: "Ilimitadas", sublabel: "30 días", price: "19,99€", maxQuestions: null }, ] as const; type TierId = (typeof TIERS)[number]["id"]; // ── Helpers ─────────────────────────────────────────────────────────────────── function loadCredit(): CreditInfo | null { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return null; const c = JSON.parse(raw) as CreditInfo; if (!c.token || !c.expiresAt || c.expiresAt < Date.now()) { localStorage.removeItem(STORAGE_KEY); return null; } return c; } catch { return null; } } function formatDate(ms: number): string { return new Date(ms).toLocaleDateString("es-ES", { day: "numeric", month: "long", year: "numeric", }); } function badgeText(questionsLeft: number | null): string { if (questionsLeft === null) return "Preguntas ilimitadas"; if (questionsLeft === 0) return "Sin preguntas restantes"; return `${questionsLeft} pregunta${questionsLeft !== 1 ? "s" : ""} restante${questionsLeft !== 1 ? "s" : ""}`; } async function submitRedsysForm(data: { tpvUrl: string; merchantParams: string; signature: string; signatureVersion: string; }) { const form = document.createElement("form"); form.method = "POST"; form.action = data.tpvUrl; for (const [name, value] of Object.entries({ Ds_SignatureVersion: data.signatureVersion, Ds_MerchantParameters: data.merchantParams, Ds_Signature: data.signature, })) { const input = document.createElement("input"); input.type = "hidden"; input.name = name; input.value = value; form.appendChild(input); } document.body.appendChild(form); form.submit(); } // ── Footer ──────────────────────────────────────────────────────────────────── function SiteFooter() { return (

IAs compiten respondiendo preguntas absurdas — los jueces votan, tú también puedes.

por{" "} Cloud Host {" "}— La web simplificada, la nube gestionada

); } // ── Main component ──────────────────────────────────────────────────────────── function App() { const params = new URLSearchParams(window.location.search); const creditOkOrder = params.get("credito_ok"); const isKo = params.get("ko") === "1"; // Credit state const [credit, setCredit] = useState(null); const [loaded, setLoaded] = useState(false); // Credit verification (polling after Redsys redirect) const [verifying, setVerifying] = useState(false); const [verifyError, setVerifyError] = useState(false); // Purchase flow const [selectedTier, setSelectedTier] = useState(null); const [username, setUsername] = useState(""); const [buying, setBuying] = useState(false); const [buyError, setBuyError] = useState(null); // Question submission const [text, setText] = useState(""); const [submitting, setSubmitting] = useState(false); const [submitError, setSubmitError] = useState(null); const [sent, setSent] = useState(false); // Load credit from localStorage on mount useEffect(() => { setCredit(loadCredit()); setLoaded(true); }, []); // Poll for credit activation after Redsys redirect useEffect(() => { if (!creditOkOrder || !loaded || credit) return; setVerifying(true); let attempts = 0; const maxAttempts = 15; async function poll() { if (attempts >= maxAttempts) { setVerifying(false); setVerifyError(true); return; } attempts++; try { const res = await fetch( `/api/credito/estado?order=${encodeURIComponent(creditOkOrder!)}`, ); if (res.ok) { const data = (await res.json()) as { found: boolean; status?: string; token?: string; username?: string; expiresAt?: number; tier?: string; questionsLeft?: number | null; }; if (data.found && data.status === "active" && data.token && data.expiresAt) { const newCredit: CreditInfo = { token: data.token, username: data.username ?? "", expiresAt: data.expiresAt, tier: data.tier ?? "", questionsLeft: data.questionsLeft ?? null, }; localStorage.setItem(STORAGE_KEY, JSON.stringify(newCredit)); setCredit(newCredit); setVerifying(false); history.replaceState(null, "", "/pregunta"); return; } } } catch { // retry } setTimeout(poll, 2000); } poll(); }, [creditOkOrder, loaded, credit]); async function handleBuyCredit(e: React.FormEvent) { e.preventDefault(); if (!selectedTier) return; setBuyError(null); setBuying(true); try { const res = await fetch("/api/credito/iniciar", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ tier: selectedTier, username: username.trim() }), }); if (!res.ok) throw new Error(await res.text()); await submitRedsysForm(await res.json()); } catch (err) { setBuyError(err instanceof Error ? err.message : "Error al procesar el pago"); setBuying(false); } } async function handleSubmitQuestion(e: React.FormEvent) { e.preventDefault(); if (!credit) return; setSubmitError(null); setSubmitting(true); try { const res = await fetch("/api/pregunta/enviar", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: text.trim(), token: credit.token }), }); if (!res.ok) { const msg = await res.text(); if (res.status === 401) { localStorage.removeItem(STORAGE_KEY); setCredit(null); throw new Error("Tu acceso ha expirado o se han agotado las preguntas."); } throw new Error(msg || `Error ${res.status}`); } const data = await res.json() as { ok: boolean; questionsLeft: number | null }; // Update questionsLeft in state and localStorage const updated: CreditInfo = { ...credit, questionsLeft: data.questionsLeft }; localStorage.setItem(STORAGE_KEY, JSON.stringify(updated)); setCredit(updated); setText(""); setSent(true); setTimeout(() => setSent(false), 4000); } catch (err) { setSubmitError(err instanceof Error ? err.message : "Error al enviar"); } finally { setSubmitting(false); } } // ── Loading ─────────────────────────────────────────────────────────────── if (!loaded) { return (
); } // ── Payment failed ──────────────────────────────────────────────────────── if (isKo) { return (
argument.es

Pago cancelado

El pago no se completó. Tu acceso no ha sido activado.

Intentar de nuevo
); } // ── Verifying payment ───────────────────────────────────────────────────── if (verifying || (creditOkOrder && !credit)) { return (
argument.es

Verificando tu pago…

{verifyError ? "No se pudo confirmar el pago. Si se completó, espera unos segundos y recarga." : "Esto puede tardar unos segundos."}

{verifyError ? ( Volver ) : (
)}
); } // ── Active credit — question form ───────────────────────────────────────── if (credit) { const exhausted = credit.questionsLeft !== null && credit.questionsLeft <= 0; return (
argument.es
{badgeText(credit.questionsLeft)}

Hola, {credit.username}

Acceso activo hasta el {formatDate(credit.expiresAt)}.{" "} {exhausted ? "Has agotado tus preguntas para este plan." : "Envía todas las preguntas que quieras."}

{sent && (
✓ ¡Pregunta enviada! Se usará en el próximo sorteo.
)} {!exhausted && (