"use client"; import { useState, useEffect, FormEvent } from "react"; import { useAuth } from "@/lib/AuthContext"; import { useRouter } from "next/navigation"; import { Wallet, Zap, Crown, TrendingUp, ArrowUpRight, ArrowDownLeft, Clock, Check, X, Loader2, Sparkles, Info, } from "lucide-react"; import ErrorFeedback from "@/components/ErrorFeedback"; interface CashoutEntry { id: string; amountFlh: number; fiatAmount: number; status: string; createdAt: string; } const TOP_UP_OPTIONS = [ { flh: 500, usd: 0.99, bonus: "", bestValue: false }, { flh: 1000, usd: 0.99, bonus: "", bestValue: true }, { flh: 5000, usd: 4.99, bonus: "+500 bonus", bestValue: false }, { flh: 10000, usd: 9.99, bonus: "+2,000 bonus", bestValue: false }, { flh: 50000, usd: 49.99, bonus: "+15,000 bonus", bestValue: false }, ]; const EARNING_TIPS = [ "Post in the forum — earn 50 FLH per approved post", "List items on Souq marketplace", "Complete daily AI coaching sessions", "Refer friends — earn 200 FLH per referral", "Participate in community challenges", ]; export default function WalletPage() { const { user, token, loading } = useAuth(); const router = useRouter(); const [balance, setBalance] = useState(0); const [cashoutHistory, setCashoutHistory] = useState([]); const [cashoutAmount, setCashoutAmount] = useState(""); const [cashoutLoading, setCashoutLoading] = useState(false); const [cashoutMessage, setCashoutMessage] = useState<{ type: "success" | "error"; text: string; } | null>(null); const [topupLoading, setTopupLoading] = useState(null); const [topupSuccess, setTopupSuccess] = useState(false); useEffect(() => { if (!loading && !token) router.push("/auth"); }, [loading, token, router]); useEffect(() => { if (token) { fetchWallet(); } }, [token]); // Check for ?topup=success from Polar.sh or mock redirect useEffect(() => { const params = new URLSearchParams(window.location.search); if (params.get("topup") === "success") { setTopupSuccess(true); fetchWallet(); // Clean URL param without reload const url = new URL(window.location.href); url.searchParams.delete("topup"); window.history.replaceState({}, "", url.toString()); const timer = setTimeout(() => setTopupSuccess(false), 6000); return () => clearTimeout(timer); } }, []); const fetchWallet = async () => { try { const res = await fetch("/mobile/api/wallet", { headers: { Authorization: `Bearer ${token}` }, }); if (res.ok) { const data = await res.json(); setBalance(data.balance); setCashoutHistory(data.cashoutHistory || []); } } catch { // ignore } }; const handleTopUp = async (amount: number) => { if (!token) return; setTopupLoading(amount); try { const res = await fetch("/mobile/api/wallet/top-up/create-checkout", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ amount }), }); if (!res.ok) { const err = await res.json(); throw new Error(err.error || "Top-up failed"); } const data = await res.json(); if (data.url) { router.push(data.url); } } catch (err: unknown) { const message = err instanceof Error ? err.message : "Top-up failed"; setCashoutMessage({ type: "error", text: message }); setTimeout(() => setCashoutMessage(null), 3000); } finally { setTopupLoading(null); } }; const handleCashout = async (e: FormEvent) => { e.preventDefault(); if (!token) return; const amountNum = parseInt(cashoutAmount, 10); if (isNaN(amountNum) || amountNum < 1000) { setCashoutMessage({ type: "error", text: "Minimum cashout is 1,000 FLH", }); setTimeout(() => setCashoutMessage(null), 3000); return; } if (amountNum > balance) { setCashoutMessage({ type: "error", text: "Insufficient balance", }); setTimeout(() => setCashoutMessage(null), 3000); return; } setCashoutLoading(true); setCashoutMessage(null); try { const res = await fetch("/mobile/api/wallet", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ amountFlh: amountNum }), }); if (!res.ok) { const err = await res.json(); throw new Error(err.error || "Cashout failed"); } const data = await res.json(); setCashoutMessage({ type: "success", text: `Cashout request for ${data.cashout.amountFlh} FLH submitted!`, }); setCashoutAmount(""); await fetchWallet(); setTimeout(() => setCashoutMessage(null), 4000); } catch (err: unknown) { const message = err instanceof Error ? err.message : "Cashout failed"; setCashoutMessage({ type: "error", text: message }); setTimeout(() => setCashoutMessage(null), 3000); } finally { setCashoutLoading(false); } }; const isPremium = user?.isPremium || user?.isPro; if (loading) { return (
); } if (!user) return null; return (

Wallet

{/* Balance Card */}

FLH Balance

{balance.toLocaleString()}

≈ ${(balance / 1000).toFixed(2)} USD

{!isPremium && (

Premium members earn 2x FLH!

)}
{/* Messages */} {cashoutMessage && cashoutMessage.type === "success" && (
{cashoutMessage.text}
)} {cashoutMessage && cashoutMessage.type === "error" && ( setCashoutMessage(null)} context="wallet" /> )} {/* Top-up Success Banner */} {topupSuccess && (
Top-up successful! FLH has been added to your balance.
)} {/* Top-Up Section */}

Top Up FLH

{TOP_UP_OPTIONS.map((opt) => ( ))}
{/* Cashout Form */}

Cash Out FLH

Convert your FLH to USD. Minimum 1,000 FLH (1,000 FLH = $1.00).

setCashoutAmount(e.target.value)} placeholder="Amount FLH" className="w-full bg-[#1a1a24] border border-gray-800/60 rounded-xl px-4 py-4 text-white placeholder-gray-600 focus:outline-none focus:border-[#D4AF37]/50 focus:ring-1 focus:ring-[#D4AF37]/30 transition min-h-[48px]" />
{cashoutAmount && parseInt(cashoutAmount) >= 1000 && (

You will receive ~$ {(parseInt(cashoutAmount) / 1000).toFixed(2)} USD

)}
{/* Transaction History */}

Cashout History

{cashoutHistory.length === 0 ? (

No cashouts yet

Your cashout requests will appear here

) : (
{cashoutHistory.map((entry) => (
{entry.status === "approved" ? ( ) : entry.status === "rejected" ? ( ) : ( )}

{entry.amountFlh.toLocaleString()} FLH

${entry.fiatAmount.toFixed(2)} —{" "} {new Date(entry.createdAt).toLocaleDateString()}

{entry.status}
))}
)}
{/* Earning Tips */}

Earn More FLH

    {EARNING_TIPS.map((tip, i) => (
  • {tip}
  • ))}
); }