import React, { useState } from "react"; import { http } from "../lib/api"; import { frontendUrl } from "../lib/paths"; import { Check, Zap, Crown, Sparkles, Loader2, RefreshCw, Clock, ShieldCheck, Infinity as InfinityIcon, AlertTriangle, RotateCcw, } from "lucide-react"; import { toast } from "sonner"; const TIER_ICONS = { free: Clock, basic: Sparkles, premium: Zap, ultra: Crown }; const TIER_RANK = { free: 0, basic: 1, premium: 2, ultra: 3 }; export function SubscriptionSection({ license, plans, onRefresh, onReset }) { const [loading, setLoading] = useState(null); const [checking, setChecking] = useState(false); const [productKey, setProductKey] = useState(""); const [redeeming, setRedeeming] = useState(false); const currentTier = license?.tier || "free"; const planList = plans || []; const subscribe = async (tier) => { setLoading(tier); try { const r = await http.post("/license/checkout", { origin_url: frontendUrl("/chat"), tier, }); if (r.data.already_paid) { toast.success(`Abonnement ${r.data.tier} actif`); onRefresh?.(); return; } if (r.data.url) window.location.href = r.data.url; } catch (e) { toast.error(e?.response?.data?.detail || "Erreur Stripe"); } finally { setLoading(null); } }; const redeemKey = async (e) => { e.preventDefault(); const key = productKey.trim(); if (!key) return; setRedeeming(true); try { const r = await http.post("/license/redeem-key", { key }); toast.success(`Licence ${(r.data.tier || "ultra").toUpperCase()} activée`); setProductKey(""); onRefresh?.(); } catch (err) { toast.error(err?.response?.data?.detail || "Clé invalide"); } finally { setRedeeming(false); } }; const checkPayment = async () => { setChecking(true); try { const r = await http.post("/license/claim-payment"); if (r.data.paid) { toast.success(`Abonnement ${r.data.tier} activé`); onRefresh?.(); } else { toast.info(r.data.message || "Paiement en attente"); } } catch (_) { toast.error("Erreur vérification"); } finally { setChecking(false); } }; if (license?.source === "product_key" && license?.active) { return (
{license.tier_name || "Ultra"} · Licence produit

Accès illimité

); } if (license?.is_admin && license?.source === "admin_grant") { return (
Admin
{onReset && ( )}
); } return (
{currentTier !== "free" && license?.active && (
{license.tier_name || currentTier} actif

Modèle : {license.model_label || "—"} {license.valid_until && ( <> · renouvellement {new Date(license.valid_until).toLocaleDateString("fr-FR")} )}

)} {currentTier === "free" && (
Gratuit

{license?.messages_left_today ?? 0} / {license?.messages_per_day ?? 15} messages {license?.model_label && <> · {license.model_label}}

)} {(currentTier === "free" || !license?.active) && (

Clé produit

setProductKey(e.target.value.toUpperCase())} placeholder="EMO-ULTRA-XXXX-XXXX-XXXX" className="w-full px-3 py-2 rounded-lg text-xs font-mono tracking-wide em-input focus:border-purple-500/50" />
)}
{planList.filter((p) => p.id !== "free").map((plan) => { const Icon = TIER_ICONS[plan.id] || Zap; const isCurrent = currentTier === plan.id && license?.active; const canUpgrade = !isCurrent && (TIER_RANK[plan.id] ?? 0) > (TIER_RANK[currentTier] ?? 0); const cardStyle = plan.id === "ultra" ? { bg: "var(--emo-warning-bg)", border: "var(--emo-warning-border)", accent: "var(--emo-admin-text)" } : plan.id === "basic" ? { bg: "var(--emo-accent-soft)", border: "var(--emo-accent-border)", accent: "var(--emo-link)" } : { bg: "var(--emo-accent-soft)", border: "var(--emo-accent-border)", accent: "var(--mode-color)" }; return (
{plan.name}
{plan.price_eur} €/mois
    {plan.features?.slice(0, 4).map((f) => (
  • {f}
  • ))}
{plan.models?.length > 0 && (

IA : {plan.models.slice(0, 3).join(" · ")}

)} {isCurrent ? (
Plan actuel
) : canUpgrade || currentTier === "free" ? ( ) : null}
); })}

Paiement Stripe

); } export default function Paywall({ info, plans, onPaid }) { const [loading, setLoading] = useState(null); const [checking, setChecking] = useState(false); const expired = info?.status === "expired"; const planList = plans || info?.plans || []; const subscribe = async (tier) => { setLoading(tier); try { const r = await http.post("/license/checkout", { origin_url: window.location.origin, tier }); if (r.data.already_paid) { onPaid?.(); return; } if (r.data.url) window.location.href = r.data.url; } catch (_) { setLoading(null); } }; const checkPayment = async () => { setChecking(true); try { const r = await http.post("/license/claim-payment"); if (r.data.paid) onPaid?.(); else { const lic = await http.get("/license/status"); if (lic.data.active && lic.data.tier !== "free") onPaid?.(); else alert(r.data.message || "Paiement en attente."); } } catch (_) { /* ignore */ } finally { setChecking(false); } }; return (

{expired ? "Abonnement expiré" : "Quota du jour atteint"}

{expired ? "Renouvelez votre abonnement." : <>Quota journalier atteint ({info?.messages_per_day || 15} messages).}

{planList.filter((p) => p.id !== "free").map((plan) => ( ))}
); }