113 lines
5.2 KiB
TypeScript
113 lines
5.2 KiB
TypeScript
"use client";
|
||
import { useEffect, useState } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
|
||
interface UserInfo {
|
||
id: number;
|
||
email: string;
|
||
discord_id: string | null;
|
||
tier: string;
|
||
expires_at: string | null;
|
||
}
|
||
|
||
export default function DashboardPage() {
|
||
const router = useRouter();
|
||
const [user, setUser] = useState<UserInfo | null>(null);
|
||
const [discordId, setDiscordId] = useState("");
|
||
const [saving, setSaving] = useState(false);
|
||
const [msg, setMsg] = useState("");
|
||
|
||
useEffect(() => {
|
||
const token = localStorage.getItem("arb_token");
|
||
if (!token) { router.push("/login"); return; }
|
||
fetch("/api/user/me", { headers: { Authorization: `Bearer ${token}` } })
|
||
.then(r => { if (!r.ok) { router.push("/login"); return null; } return r.json(); })
|
||
.then(d => { if (d) { setUser(d); setDiscordId(d.discord_id || ""); } });
|
||
}, [router]);
|
||
|
||
const bindDiscord = async () => {
|
||
setSaving(true); setMsg("");
|
||
const token = localStorage.getItem("arb_token");
|
||
const r = await fetch("/api/user/bind-discord", {
|
||
method: "POST",
|
||
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
||
body: JSON.stringify({ discord_id: discordId }),
|
||
});
|
||
const d = await r.json();
|
||
setMsg(r.ok ? "✅ 绑定成功" : d.detail || "绑定失败");
|
||
setSaving(false);
|
||
if (r.ok && user) setUser({ ...user, discord_id: discordId });
|
||
};
|
||
|
||
const logout = () => { localStorage.removeItem("arb_token"); router.push("/"); };
|
||
|
||
if (!user) return <div className="text-slate-400 p-8">加载中...</div>;
|
||
|
||
const tierLabel: Record<string, string> = { free: "免费版", pro: "Pro", premium: "Premium" };
|
||
|
||
return (
|
||
<div className="space-y-6 max-w-2xl">
|
||
<div className="flex items-center justify-between">
|
||
<h1 className="text-2xl font-bold text-slate-100">我的账户</h1>
|
||
<button onClick={logout} className="text-sm text-slate-400 hover:text-slate-200">退出</button>
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6 space-y-3">
|
||
<h2 className="text-slate-200 font-semibold">账户信息</h2>
|
||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||
<div className="text-slate-400">邮箱</div>
|
||
<div className="text-slate-200">{user.email}</div>
|
||
<div className="text-slate-400">订阅等级</div>
|
||
<div className="text-cyan-400 font-medium">{tierLabel[user.tier] || user.tier}</div>
|
||
<div className="text-slate-400">到期时间</div>
|
||
<div className="text-slate-200">{user.expires_at ? new Date(user.expires_at).toLocaleDateString("zh-CN") : "永久免费"}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6 space-y-4">
|
||
<h2 className="text-slate-200 font-semibold">Discord 信号推送</h2>
|
||
<p className="text-slate-400 text-sm">绑定Discord ID后,当套利信号触发时会自动@你</p>
|
||
<div className="flex gap-2">
|
||
<input
|
||
value={discordId} onChange={e => setDiscordId(e.target.value)}
|
||
className="flex-1 bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-slate-100 text-sm focus:outline-none focus:border-cyan-500"
|
||
placeholder="Discord用户ID(18位数字)"
|
||
/>
|
||
<button
|
||
onClick={bindDiscord} disabled={saving || !discordId}
|
||
className="bg-cyan-600 hover:bg-cyan-500 disabled:opacity-50 text-white px-4 py-2 rounded-lg text-sm"
|
||
>
|
||
{saving ? "保存中..." : "绑定"}
|
||
</button>
|
||
</div>
|
||
{msg && <p className={`text-sm ${msg.startsWith("✅") ? "text-emerald-400" : "text-red-400"}`}>{msg}</p>}
|
||
<p className="text-slate-500 text-xs">如何获取Discord ID:设置 → 外观 → 开发者模式 → 右键个人头像 → 复制用户ID</p>
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-slate-700 bg-slate-800/50 p-6">
|
||
<h2 className="text-slate-200 font-semibold mb-3">升级订阅</h2>
|
||
<div className="grid grid-cols-3 gap-3 text-sm">
|
||
{[
|
||
{ tier: "free", label: "免费版", price: "¥0", features: ["实时费率面板"] },
|
||
{ tier: "pro", label: "Pro", price: "¥99/月", features: ["实时费率面板", "信号Discord推送", "历史数据"] },
|
||
{ tier: "premium", label: "Premium", price: "¥299/月", features: ["Pro全部功能", "定制阈值", "优先客服"] },
|
||
].map(p => (
|
||
<div key={p.tier} className={`rounded-lg border p-4 space-y-2 ${user.tier === p.tier ? "border-cyan-500 bg-cyan-950/30" : "border-slate-600"}`}>
|
||
<div className="font-medium text-slate-200">{p.label}</div>
|
||
<div className="text-cyan-400 font-bold">{p.price}</div>
|
||
<ul className="space-y-1">
|
||
{p.features.map(f => <li key={f} className="text-slate-400 text-xs">• {f}</li>)}
|
||
</ul>
|
||
{user.tier !== p.tier && p.tier !== "free" && (
|
||
<button className="w-full mt-2 bg-slate-700 hover:bg-slate-600 text-slate-200 py-1 rounded text-xs">
|
||
升级(即将开放)
|
||
</button>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|