init: 初始化 AssetX 项目仓库

包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、
antdesign(管理后台)、landingpage(营销落地页)、
数据库 SQL 和配置文件。
This commit is contained in:
2026-03-27 11:26:43 +00:00
commit 2ee4553b71
634 changed files with 988255 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
"use client";
import { useState, useEffect } from "react";
import { toast } from "sonner";
import { useAccount } from "wagmi";
import EarnOpportunityCard from "./EarnOpportunityCard";
import ActivityHistory from "./ActivityHistory";
import TopPerformers from "./TopPerformers";
import ReferralCodeCard from "./ReferralCodeCard";
import BindInviteCard from "./BindInviteCard";
import TeamTVLCard from "./TeamTVLCard";
import DepositCard from "./DepositCard";
import { fetchTeamTVL, bindInviteCode, registerWallet, type TeamTVLData } from "@/lib/api/points";
import { useApp } from "@/contexts/AppContext";
export default function PointsCards() {
const { t } = useApp();
const { address } = useAccount();
const [mounted, setMounted] = useState(false);
const [teamTVL, setTeamTVL] = useState<TeamTVLData | null>(null);
const [loading, setLoading] = useState(true);
const [inviteCode, setInviteCode] = useState('');
const [inviteUsedCount, setInviteUsedCount] = useState(0);
const [inviteLoading, setInviteLoading] = useState(false);
useEffect(() => { setMounted(true); }, []);
// Register wallet and load user data when address connects
useEffect(() => {
if (address) {
registerAndLoad(address);
}
}, [address]);
async function registerAndLoad(walletAddress: string) {
setInviteLoading(true);
// Register wallet (creates user if not exists), get invite code
const userData = await registerWallet(walletAddress);
if (userData) {
setInviteCode(userData.inviteCode);
setInviteUsedCount(userData.usedCount);
}
setInviteLoading(false);
// Load team TVL for this wallet
setLoading(true);
const teamData = await fetchTeamTVL(walletAddress);
setTeamTVL(teamData);
setLoading(false);
}
const handleCopy = () => {
if (inviteCode) {
navigator.clipboard.writeText(inviteCode);
toast.success(t("points.copiedToClipboard"));
}
};
const handleShare = async () => {
if (!inviteCode) return;
const shareUrl = `${window.location.origin}?ref=${inviteCode}`;
if (navigator.share) {
try {
await navigator.share({ url: shareUrl });
} catch {
// user cancelled, do nothing
}
} else {
navigator.clipboard.writeText(shareUrl);
toast.success(t("points.shareLinkCopied"));
}
};
const handleApply = async (code: string) => {
if (!code) {
toast.error(t("points.pleaseEnterInviteCode"));
return;
}
// TODO: Get signature from wallet
const signature = "0x..."; // Placeholder
const result = await bindInviteCode(code, signature, address);
if (result.success) {
toast.success(result.message || t("points.inviteCodeBoundSuccess"));
if (address) registerAndLoad(address); // Reload data
} else {
toast.error(result.error || t("points.failedToBindInviteCode"));
}
};
return (
<div className="flex flex-col gap-6">
{/* First Row - Cards 1, 2, 3 */}
<div className="flex flex-col md:flex-row gap-6 animate-fade-in" style={{ animationDelay: '0.1s' }}>
<ReferralCodeCard
code={inviteCode || t("points.loading")}
usedCount={inviteUsedCount}
loading={!mounted || inviteLoading || !address}
onCopy={handleCopy}
onShare={handleShare}
/>
<BindInviteCard
onApply={handleApply}
/>
<TeamTVLCard
currentTVL={teamTVL?.currentTVL || "$0"}
targetTVL={teamTVL?.targetTVL || "$10M"}
progressPercent={teamTVL?.progressPercent || 0}
members={teamTVL?.totalMembers || 0}
roles={teamTVL?.roles || []}
loading={loading}
/>
</div>
{/* Second Row - Card 4 */}
<div className="animate-fade-in" style={{ animationDelay: '0.2s' }}>
<DepositCard
title="Deposit USDC to ALP"
subtitle="Native Staking"
badge="UP TO 3X"
lockPeriod="30 DAYS"
progressPercent={65}
pointsBoost="+10% POINTS"
onDeposit={() => {}}
/>
</div>
{/* Third Row - Earn Opportunities */}
<div className="flex flex-col md:flex-row gap-6 animate-fade-in" style={{ animationDelay: '0.3s' }}>
<EarnOpportunityCard
pointsLabel="7X Points"
title="Pendle YT"
subtitle="Yield Trading optimization"
metricLabel="EST. APY"
metricValue="50%-300%"
buttonText="ZAP IN"
onButtonClick={() => {}}
/>
<EarnOpportunityCard
pointsLabel="4X Points"
title="Curve LP"
subtitle="Liquidity Pool provision"
metricLabel="CURRENT APY"
metricValue="15%-35%"
buttonText="Add Liquidity"
onButtonClick={() => {}}
/>
<EarnOpportunityCard
pointsLabel="2X-8X Points"
title="Morpho"
subtitle="Lending Loop strategy"
metricLabel="MAX LTV"
metricValue="85%"
buttonText="Start LOOP"
onButtonClick={() => {}}
/>
</div>
{/* Fourth Row - Activity History and Top Performers */}
<div className="flex flex-col md:flex-row gap-6 animate-fade-in" style={{ animationDelay: '0.4s' }}>
<div className="flex-[2]">
<ActivityHistory />
</div>
<div className="flex-1">
<TopPerformers />
</div>
</div>
</div>
);
}